From 7d5717e4f2942d27c2f8c5e305ad8be3ae5a859c Mon Sep 17 00:00:00 2001 From: Norman Fomferra Date: Tue, 25 Sep 2018 23:48:34 +0200 Subject: [PATCH] closes #768 --- cate/webapi/geojson.py | 32 +++++++++++++++++++++++++++++--- cate/webapi/rest.py | 20 ++++++++++---------- test/webapi/test_geojson.py | 8 ++++---- 3 files changed, 43 insertions(+), 17 deletions(-) diff --git a/cate/webapi/geojson.py b/cate/webapi/geojson.py index 381e361dc..a5479bb1d 100644 --- a/cate/webapi/geojson.py +++ b/cate/webapi/geojson.py @@ -266,6 +266,7 @@ def write_feature_collection(feature_collection: Union[fiona.Collection, Iterabl io.flush() num_features_written = 0 + feature_index = 0 for feature in feature_collection: feature_ok = _transform_feature(feature, max_num_display_geometry_points, @@ -277,10 +278,16 @@ def write_feature_collection(feature_collection: Union[fiona.Collection, Iterabl io.flush() if res_id is not None: feature['_resId'] = res_id + feature['_idx'] = feature_index + if 'id' not in feature: + feature['id'] = feature_index # Note: io.write(json.dumps(feature)) is 3x faster than json.dump(feature, fp=io) - io.write(json.dumps(feature)) + json_text = json.dumps(feature) + io.write(json_text) num_features_written += 1 + feature_index += 1 + io.write('\n]}\n') io.flush() @@ -291,9 +298,9 @@ def write_feature(feature: Feature, io, crs=None, res_id: int = None, + feature_index: int = -1, max_num_display_geometry_points: int = 100, conservation_ratio: float = 1.0): - source_prj = target_prj = None if crs: source_prj = pyproj.Proj(crs) @@ -306,11 +313,17 @@ def write_feature(feature: Feature, if feature_ok: if res_id is not None: feature['_resId'] = res_id + feature['_idx'] = feature_index + if 'id' not in feature: + feature['id'] = feature_index # Note: io.write(json.dumps(feature)) is 3x faster than json.dump(feature, fp=io) - io.write(json.dumps(feature)) + json_text = json.dumps(feature, cls=SeriesJSONEncoder) + io.write(json_text) io.flush() + + def _transform_feature(feature: Feature, max_num_display_geometry_points: int, conservation_ratio: float, @@ -517,3 +530,16 @@ def _update_point(self, point): self._push(new_point) return new_point return point + + +class SeriesJSONEncoder(json.JSONEncoder): + def default(self, obj): + if hasattr(obj, 'dtype'): + if np.issubdtype(obj.dtype, np.integer): + return int(obj) + elif np.issubdtype(obj.dtype, np.floating): + return float(obj) + else: + return str(obj) + + return json.JSONEncoder.default(self, obj) \ No newline at end of file diff --git a/cate/webapi/rest.py b/cate/webapi/rest.py index ae563aac5..0d65ae2a7 100644 --- a/cate/webapi/rest.py +++ b/cate/webapi/rest.py @@ -369,16 +369,13 @@ def get(self, base_dir, res_id, feature_index): elif isinstance(resource, gpd.GeoDataFrame): if not self._check_feature_index(feature_index, len(resource)): return - # TODO (nf,mz): review & test following code, - # it is inspired by geopandas.GeoDataFrame.iterfeatures() impl. - row = resource[feature_index] + row = resource.iloc[feature_index] geometry = None properties = row.to_dict() if 'geometry' in properties: geometry = row['geometry'].__geo_interface__ del properties['geometry'] feature = { - 'id': str(row.index), 'type': 'Feature', 'properties': properties, 'geometry': geometry @@ -398,15 +395,18 @@ def get(self, base_dir, res_id, feature_index): conservation_ratio = _level_to_conservation_ratio(level, _NUM_GEOM_SIMP_LEVELS) def job(): - write_feature(feature, - self, - crs=crs, - res_id=res_id, - conservation_ratio=conservation_ratio) + try: + write_feature(feature, + self, + crs=crs, + res_id=res_id, + feature_index=feature_index, + conservation_ratio=conservation_ratio) + except BaseException as e: + self.write_status_error(exc_info=sys.exc_info()) self.finish() if TRACE_PERF: print('ResFeatureHandler: streaming done at ', datetime.datetime.now()) - self.finish() yield [THREAD_POOL.submit(job)] except Exception: diff --git a/test/webapi/test_geojson.py b/test/webapi/test_geojson.py index 7a1370e41..04850e13a 100644 --- a/test/webapi/test_geojson.py +++ b/test/webapi/test_geojson.py @@ -123,10 +123,10 @@ class Collection(list): '{"type": "FeatureCollection", "features": [\n' '{"type": "Feature", "geometry": {"type": "Polygon", ' '"coordinates": [[[12.0, 53.0], [13.0, 54.0], [13.0, 56.0], [12.0, 53.0]]]}, ' - '"properties": {"id": "1", "a": 3, "b": true}},\n' + '"properties": {"id": "1", "a": 3, "b": true}, "_idx": 0, "id": 0},\n' '{"type": "Feature", "geometry": {"type": "Polygon", ' '"coordinates": [[[12.0, 73.0], [13.0, 74.0], [13.0, 76.0], [12.0, 73.0]]]}, ' - '"properties": {"id": "2", "a": 9, "b": false}}\n' + '"properties": {"id": "2", "a": 9, "b": false}, "_idx": 1, "id": 1}\n' ']}\n') def test_polygon_with_simp(self): @@ -163,10 +163,10 @@ class Collection(list): '{"type": "FeatureCollection", "features": [\n' '{"type": "Feature", "geometry": {"type": "Polygon", ' '"coordinates": [[[12.0, 53.0], [13.0, 54.0], [13.0, 56.0], [12.0, 53.0]]]}, ' - '"properties": {"id": "1", "a": 3, "b": true}, "_simp": 1},\n' + '"properties": {"id": "1", "a": 3, "b": true}, "_simp": 1, "_idx": 0, "id": 0},\n' '{"type": "Feature", "geometry": {"type": "Polygon", ' '"coordinates": [[[12.0, 73.0], [13.0, 74.0], [13.0, 76.0], [12.0, 73.0]]]}, ' - '"properties": {"id": "2", "a": 9, "b": false}, "_simp": 1}\n' + '"properties": {"id": "2", "a": 9, "b": false}, "_simp": 1, "_idx": 1, "id": 1}\n' ']}\n') def test_countries_with_simp(self):