diff --git a/csvkit/utilities/csvjson.py b/csvkit/utilities/csvjson.py index 9b97efb8a..92d0a1da4 100644 --- a/csvkit/utilities/csvjson.py +++ b/csvkit/utilities/csvjson.py @@ -92,11 +92,40 @@ def dump_json(data, newline=False): ) features = [] + + global min_lon, min_lat, max_lon, max_lat + min_lon = None min_lat = None max_lon = None max_lat = None + def update_boundary_lat(lat): + global min_lat, max_lat + + if min_lat is None or lat < min_lat: + min_lat = lat + if max_lat is None or lat > max_lat: + max_lat = lat + + def update_boundary_lon(lon): + global min_lon, max_lon + + if min_lon is None or lon < min_lon: + min_lon = lon + if max_lon is None or lon > max_lon: + max_lon = lon + + def update_boundary_coords(coords): + if (isinstance(coords, list) and len(coords) == 2 and + isinstance(coords[0], (float, int)) and + isinstance(coords[1], (float, int))): + update_boundary_lon(coords[0]) + update_boundary_lat(coords[1]) + else: + for coord in coords: + update_boundary_coords(coord) + lat_column = match_column_identifier(table.column_names, self.args.lat, self.args.zero_based) lon_column = match_column_identifier(table.column_names, self.args.lon, self.args.zero_based) @@ -110,39 +139,48 @@ def dump_json(data, newline=False): feature['type'] = 'Feature' properties = OrderedDict() geoid = None + geo_type = 'Point' lat = None lon = None + coords = None for i, c in enumerate(row): if i == lat_column: + if c is None: + continue try: lat = float(c) except ValueError: lat = None - if min_lat is None or lat < min_lat: - min_lat = lat - if max_lat is None or lat > max_lat: - max_lat = lat + update_boundary_lat(lat) elif i == lon_column: + if c is None: + continue try: lon = float(c) except ValueError: lon = None - if min_lon is None or lon < min_lon: - min_lon = lon - if max_lon is None or lon > max_lon: - max_lon = lon + update_boundary_lon(lon) elif i == id_column: geoid = c + elif table.column_names[i] == 'type': + geo_type = c + elif table.column_names[i] == 'geojson': + geojson = json.loads(c) + coords = geojson['coordinates'] + update_boundary_coords(coords) elif c is not None: properties[table.column_names[i]] = c if id_column is not None: feature['id'] = geoid + if lon and lat: + coords = [lon, lat] + feature['geometry'] = OrderedDict([ - ('type', 'Point'), - ('coordinates', [lon, lat]) + ('type', geo_type), + ('coordinates', coords) ]) feature['properties'] = properties features.append(feature) diff --git a/tests/test_utilities/test_csvjson.py b/tests/test_utilities/test_csvjson.py index 177c1df18..f981997ce 100644 --- a/tests/test_utilities/test_csvjson.py +++ b/tests/test_utilities/test_csvjson.py @@ -62,7 +62,7 @@ def test_duplicate_keys(self): self.assertRaisesRegex(ValueError, 'Value True is not unique in the key column.', utility.run) output_file.close() - def test_geojson(self): + def test_geojson_point(self): geojson = json.loads(self.get_output(['--lat', 'latitude', '--lon', 'longitude', 'examples/test_geo.csv'])) self.assertEqual(geojson['type'], 'FeatureCollection') @@ -83,6 +83,36 @@ def test_geojson(self): self.assertTrue(isinstance(geometry['coordinates'][0], float)) self.assertTrue(isinstance(geometry['coordinates'][1], float)) + def test_geojson_shape(self): + geojson = json.loads(self.get_output(['--lat', 'latitude', '--lon', 'longitude', 'examples/test_geojson.csv'])) + + self.assertEqual(geojson['type'], 'FeatureCollection') + self.assertFalse('crs' in geojson) + self.assertEqual(geojson['bbox'], [100.0, 0.0, 105.0, 1.0]) + self.assertEqual(len(geojson['features']), 3) + + for feature in geojson['features']: + self.assertEqual(feature['type'], 'Feature') + self.assertFalse('id' in feature) + self.assertIn('properties', feature) + self.assertIsInstance(feature['properties'], dict) + self.assertIn('prop0', feature['properties'].keys()) + + geometry = feature['geometry'] + + self.assertIn('coordinates', geometry) + self.assertIsNotNone(geometry['coordinates']) + + self.assertEqual(geojson['features'][0]['geometry']['type'], 'Point') + self.assertEqual(geojson['features'][1]['geometry']['type'], 'LineString') + self.assertEqual(geojson['features'][2]['geometry']['type'], 'Polygon') + + self.assertEqual(geojson['features'][0]['geometry']['coordinates'], [102.0, 0.5]) + self.assertEqual(geojson['features'][1]['geometry']['coordinates'], + [[102.0, 0.0], [103.0, 1.0], [104.0, 0.0], [105.0, 1.0]]) + self.assertEqual(geojson['features'][2]['geometry']['coordinates'], + [[[100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0]]]) + def test_geojson_with_id(self): geojson = json.loads(self.get_output(['--lat', 'latitude', '--lon', 'longitude', '-k', 'slug', 'examples/test_geo.csv']))