Skip to content

Commit

Permalink
csvjson: Support types other than Point
Browse files Browse the repository at this point in the history
Parses and removes 'geojson' from the properties,
putting the coordinate into the GeoJSON feature geometry.

Fixes #866
  • Loading branch information
jayvdb committed Jul 26, 2017
1 parent f63a654 commit 8e53286
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 11 deletions.
58 changes: 48 additions & 10 deletions csvkit/utilities/csvjson.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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 not c:
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 not c:
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)
Expand Down
32 changes: 31 additions & 1 deletion tests/test_utilities/test_csvjson.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand All @@ -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']))

Expand Down

0 comments on commit 8e53286

Please sign in to comment.