def df_geom_collection(): from shapely.geometry import Point, LineString, Polygon, GeometryCollection df = geopandas.GeoDataFrame( { "geometry": [ GeometryCollection([ Polygon([(0, 0), (1, 1), (0, 1)]), LineString([(0, 0), (1, 1)]), Point(0, 0), ]) ] }, crs="epsg:4326", ) return df
def test_aggregate_polygon_result_nan_values(tmp_path): timeseries = { "2019-10-15T08:15:45Z": [[1, 2, 3], [4, np.nan, 6]], "2019-11-11T01:11:11Z": [[7, 8, 9], [np.nan, np.nan, np.nan]], } regions = GeometryCollection( [Polygon([(0, 0), (5, 1), (1, 4)]), Polygon([(6, 1), (1, 7), (9, 9)])]) result = AggregatePolygonResult(timeseries, regions=regions) filename = result.to_netcdf(tmp_path / 'timeseries_xarray_nodata.nc') timeseries_ds = xr.open_dataset(filename) assert_array_equal(timeseries_ds.band_0.sel(feature=0).data, [1, 7]) assert_array_equal(timeseries_ds.band_2.sel(feature=0).data, [3, 9]) assert_array_equal(timeseries_ds.band_2.sel(feature=1).data, [6, np.nan])
def geom(self, union=False): """ Converts the Segments to a shapely geometry. """ lines = [ LineString([(x0, y0), (x1, y1)]) for (x0, y0, x1, y1) in self.array([0, 1, 2, 3]) ] nlines = len(lines) if not nlines: geom = GeometryCollection() elif nlines == 1: geom = lines[0] else: geom = MultiLineString(lines) return unary_union(geom) if union else geom
def test_aggregate_polygon_result_nan_values(): timeseries = { "2019-10-15T08:15:45Z": [[1, 2, 3], [4, np.nan, 6]], "2019-11-11T01:11:11Z": [[7, 8, 9], [np.nan, np.nan, np.nan]], } regions = GeometryCollection( [Polygon([(0, 0), (5, 1), (1, 4)]), Polygon([(6, 1), (1, 7), (9, 9)])]) result = AggregatePolygonResult(timeseries, regions=regions) result.set_format("covjson") data = json_normalize(result.prepare_for_json()) assert data["ranges"]["band0"]["values"] == [1, 4, 7, None] assert data["ranges"]["band1"]["values"] == [2, None, 8, None] assert data["ranges"]["band2"]["values"] == [3, 6, 9, None]
def test_clip(geojson): """Clip an array with a vector.""" with mapchete.open(geojson.path) as mp: tile = next(mp.get_process_tiles(zoom=4)) tile_process = MapcheteProcess(tile, params=mp.config.params_at_zoom(4)) with tile_process.open("file1") as vector_file: test_array = ma.masked_array(np.ones(tile_process.tile.shape)) clipped = tile_process.clip(test_array, vector_file.read()) # default params assert isinstance(clipped, ma.masked_array) assert clipped.mask.any() assert not clipped.mask.all() # inverted clip clipped_inverted = tile_process.clip(test_array, vector_file.read(), inverted=True) assert isinstance(clipped_inverted, ma.masked_array) assert clipped_inverted.mask.any() assert not clipped_inverted.mask.all() # compare results assert (clipped + clipped_inverted).mask.all() # using empty Geometries geoms = [dict(geometry=Point())] clipped = tile_process.clip(test_array, geoms) assert clipped.mask.all() # using empty Geometries inverted clipped = tile_process.clip(test_array, geoms, inverted=True) assert not clipped.mask.any() # using Point Geometries geoms = [dict(geometry=tile.bbox.centroid)] clipped = tile_process.clip(test_array, geoms) assert clipped.mask.all() # using Geometry Collections geoms = [ dict(geometry=GeometryCollection( [tile.bbox.centroid, tile.bbox])) ] clipped = tile_process.clip(test_array, geoms) assert not clipped.mask.any() # using 3D array test_array = ma.masked_array( np.ones((1, ) + tile_process.tile.shape)) clipped = tile_process.clip(test_array, vector_file.read()) assert isinstance(clipped, ma.masked_array) assert clipped.mask.any() assert not clipped.mask.all()
def test_aggregate_polygon_result_basic(tmp_path): timeseries = { "2019-11-11T01:11:11Z": [[7, 8, 9], [10, 11, 12]], "2019-10-15T08:15:45Z": [[1, 2, 3], [4, 5, 6]], } regions = GeometryCollection( [Polygon([(0, 0), (5, 1), (1, 4)]), Polygon([(6, 1), (1, 7), (9, 9)])]) metadata = CollectionMetadata({ "cube:dimensions": { "x": { "type": "spatial" }, "b": { "type": "bands", "values": ["red", "green", "blue"] } } }) result = AggregatePolygonResult(timeseries, regions=regions, metadata=metadata) result.set_format("netcdf") assets = result.write_assets(tmp_path) theAsset = assets.popitem()[1] filename = theAsset['href'] assert 'application/x-netcdf' == theAsset['type'] assert ["red", "green", "blue"] == [b['name'] for b in theAsset['bands']] timeseries_ds = xr.open_dataset(filename) print(timeseries_ds) assert_array_equal( timeseries_ds.band_0.coords['t'].data, np.asarray([ np.datetime64('2019-10-15T08:15:45'), np.datetime64('2019-11-11T01:11:11') ])) timeseries_ds.band_0.sel(feature=1) timeseries_ds.band_0.sel(t='2019-10-16') print(timeseries_ds) assert_array_equal( 4, timeseries_ds.band_0.sel(feature=1).sel(t="2019-10-15T08:15:45Z").data)
def readGeoJSON(path): """ Read geojson """ if not os.path.isfile(path): raise Exception("File {0} does not exist".format( os.path.abspath(path))) with open(path) as f: collection = json.load(f) schema = collection.get("schema", {}) features = collection["features"] geometry = GeometryCollection( [shape(feature["geometry"]) for feature in features]) properties = [feature['properties'] for feature in features] # Save properties return GIS(geometry, properties, schema)
def read_geojson(self, filename: str) -> Tuple[str, dict]: try: with open(filename) as geojson: features = json.load(geojson)['features'] geometry = GeometryCollection([ shape(feature['geometry']) if feature['geometry']['type'].lower() == 'point' else shape( feature['geometry']).buffer(0) for feature in features ]) properties = features[0]['properties'] return str(geometry[0]), properties except IndexError: raise ValueError(f"Cannot find polygon in {filename}") return None
def test_projection(): given = GeometryCollection([ MultiPoint([(1, 2), (3, 4), (5, 6), (7, 8)]), MultiLineString([[(20, 30), (40, 50), (60, 70)]]), MultiPolygon([[[(1.1, 1.2), (1.3, 1.4), (1.5, 1.6), (1.7, 1.8), (1.1, 1.2)], []]]), ]) result = project(given, WGS84, MOLLWEIDE).wkt assert 'MULTIPOINT' in result assert 'GEOMETRYCOLLECTION' in result result = project(given).wkt assert 'MULTIPOINT' in result assert 'GEOMETRYCOLLECTION' in result expected = 'GEOMETRYCOLLECTION (MULTIPOINT (1 2, 3 4, 5 6, 7 8), MULTILINESTRING ((20 30, 40 50, 60 70)), MULTIPOLYGON (((1.1 1.2, 1.3 1.4, 1.5 1.6, 1.7 1.8, 1.1 1.2))))' assert project(given, WGS84, WGS84).wkt == expected
def route_stops_inside(self, path_to_shape, format='geojson'): """Count the number of stops a given route has inside a geographical boundary or shape. :param path_to_shape: A path to the file containing the shapes. This file must contain unprojected geospatial information in WGS:84 format. :type path_to_shape: str :param format: The format of the geospatial file. **Note:** currently, only the default `geojson` is supported. :type format: str, optional :return: A :py:mod:`pandas.DataFrame` object listing each route and the number of stops served by that route that fall within the provided boundary. """ from shapely.geometry import Point, shape, GeometryCollection count = 0 # For starters, let's load a bounding box and check how many stops are in the point if format == 'geojson': with open(path_to_shape) as f: features = json.load(f)["features"] boundary = GeometryCollection([ shape(feature["geometry"]).buffer(0) for feature in features ]) routes = [] counts = [] for idx, route in self.routes.iterrows(): # Get all the stops on trips for that route. stops = self.stop_times[self.stop_times.trip_id.isin( self.trips[self.trips.route_id == route.route_id].trip_id)].stop_id.unique() # NOTE: buffer(0) is a trick for fixing scenarios where polygons have overlapping coordinates count = 0 for idx, stop in self.stops[self.stops.stop_id.isin( stops)].iterrows(): if Point(stop.stop_lon, stop.stop_lat).within(boundary): count += 1 routes.append(route.route_id) counts.append(count) stop_count = pd.DataFrame(counts, index=routes) return stop_count
def merge_multilinestring(geom): """Merge a MultiLineString to LineString""" try: if geom.geom_type == "MultiLineString": geom_inb = linemerge(geom) if geom_inb.is_ring: return geom # In case of linestring merge issues, we could add this to the script again # from centerline.main import Centerline # if geom_inb.geom_type == 'MultiLineString': # return linemerge(Centerline(geom.buffer(0.5))) else: return geom_inb else: return geom except: return GeometryCollection()
def centroid(geojson: GeoJSON): # handle multiple features geojson if geojson.features and not geojson.geometry: features = geojson.features else: features = [geojson] geo_col = GeometryCollection( [shape(feature.geometry) for feature in features]) centroids = [{ 'x': geom.centroid.x, 'y': geom.centroid.y } for geom in geo_col.geoms] if geojson.features and not geojson.geometry: return centroids else: return centroids[0]
def test_keep_geom_type_error(): gcol = GeoSeries( GeometryCollection( [ Polygon([(1, 1), (3, 1), (3, 3), (1, 3)]), LineString([(3, 3), (5, 3), (5, 5), (3, 5)]), ] ) ) dfcol = GeoDataFrame({"col1": [2], "geometry": gcol}) polys1 = GeoSeries( [ Polygon([(1, 1), (3, 1), (3, 3), (1, 3)]), Polygon([(3, 3), (5, 3), (5, 5), (3, 5)]), ] ) df1 = GeoDataFrame({"col1": [1, 2], "geometry": polys1}) with pytest.raises(TypeError): overlay(dfcol, df1, keep_geom_type=True)
def get_clip_vertex_list(geojson): """ Take a geojson object and return a list of geometry vertices that ee can use as an argument to get thumbs """ tmp_poly = [] s = GeometryCollection([ shape(feature["geometry"]).buffer(0) for feature in geojson.get('features') ]) simple = s[0].simplify(tolerance=0.01, preserve_topology=True) try: for x, y in zip(simple.exterior.coords.xy[0], simple.exterior.coords.xy[1]): tmp_poly.append([x, y]) except: for x, y in zip(simple[0].exterior.coords.xy[0], simple[0].exterior.coords.xy[1]): tmp_poly.append([x, y]) return tmp_poly
def clip_geometry_to_srs_bounds(geometry, pyramid, multipart=False): """ Clip input geometry to SRS bounds of given TilePyramid. If geometry passes the antimeridian, it will be split up in a multipart geometry and shifted to within the SRS boundaries. Note: geometry SRS must be the TilePyramid SRS! - geometry: any shapely geometry - pyramid: a TilePyramid object - multipart: return list of geometries instead of a GeometryCollection """ if not geometry.is_valid: raise ValueError("invalid geometry given") pyramid_bbox = box(*pyramid.bounds) # Special case for global tile pyramids if geometry extends over tile # pyramid boundaries (such as the antimeridian). if pyramid.is_global and not geometry.within(pyramid_bbox): inside_geom = geometry.intersection(pyramid_bbox) outside_geom = geometry.difference(pyramid_bbox) # shift outside geometry so it lies within SRS bounds if isinstance(outside_geom, Polygon): outside_geom = [outside_geom] all_geoms = [inside_geom] for geom in outside_geom: geom_bounds = Bounds(*geom.bounds) if geom_bounds.left < pyramid.left: geom = translate(geom, xoff=2 * pyramid.right) elif geom_bounds.right > pyramid.right: geom = translate(geom, xoff=-2 * pyramid.right) all_geoms.append(geom) if multipart: return all_geoms else: return GeometryCollection(all_geoms) else: if multipart: return [geometry] else: return geometry
def get_total_basin(catchmentIdentifier): """Use local catchment identifier to get local upstream basin geometry from NLDI""" print('getting upstream basin...') # request upstream basin payload = {'f': 'json', 'simplified': 'false'} # request upstream basin from NLDI using comid of catchment point is in r = requests.get(NLDI_URL + catchmentIdentifier + '/basin', params=payload) resp = r.json() # convert geojson to ogr geom features = resp['features'] totalBasinGeom = GeometryCollection( [shape(feature["geometry"]).buffer(0) for feature in features]) print('finished getting upstream basin') return totalBasinGeom
def geometrycollection(): return GeometryCollection(geoms=[ wkt_loads(wkt_point_a), MultiPoint(points=[ wkt_loads(wkt_point_a), wkt_loads(wkt_point_b) ]), wkt_loads(wkt_linestring_a), MultiLineString(lines=[ wkt_loads(wkt_linestring_a), wkt_loads(wkt_linestring_b) ]), wkt_loads(wkt_polygon_simple_a), wkt_loads(wkt_polygon_with_holes), MultiPolygon(polygons=[ wkt_loads(wkt_polygon_simple_a), wkt_loads(wkt_polygon_simple_b) ]), wkt_loads(wkt_multipolygon_with_holes) ])
def state_polygon(state): """ Return a shapely polygon of US state boundaries. GeoJSON Data: https://raw.githubusercontent.com/johan/world.geo.json Helpful tip: https://medium.com/@pramukta/recipe-importing-geojson-into-shapely-da1edf79f41d Parameters ---------- state : str Abbreviated state """ URL = ( "https://raw.githubusercontent.com/johan/world.geo.json/master/countries/USA/%s.geo.json" % state.upper()) f = requests.get(URL) features = f.json()["features"] poly = GeometryCollection( [shape(feature["geometry"]).buffer(0) for feature in features]) return poly
def test_aggregate_polygon_result_basic(tmp_path): timeseries = { "2019-10-15T08:15:45Z": [[1, 2, 3], [4, 5, 6]], "2019-11-11T01:11:11Z": [[7, 8, 9], [10, 11, 12]], } regions = GeometryCollection([ Polygon([(0, 0), (5, 1), (1, 4)]), Polygon([(6, 1), (1, 7), (9, 9)]) ]) result = AggregatePolygonResult(timeseries, regions=regions) result.set_format("netcdf") filename = result.to_netcdf(tmp_path / 'timeseries_xarray.nc') timeseries_ds = xr.open_dataset(filename) print(timeseries_ds) assert_array_equal(timeseries_ds.band_0.coords['time'].data, np.asarray([ np.datetime64('2019-10-15T08:15:45'),np.datetime64('2019-11-11T01:11:11')])) timeseries_ds.band_0.sel(feature=1) timeseries_ds.band_0.sel( time='2019-10-16') print(timeseries_ds)
def shp2box(shape, scale, margin=0.05): if isinstance(shape, gpd.GeoDataFrame): shape = shape['geometry'] shapes = shape.values kmargin = margin / (1 - 2 * margin) geoms = list(shape.values) bounds = GeometryCollection(geoms).bounds l, t, r, b = bounds w, h = r - l, b - t if isinstance(scale, tuple): ox, oy = (l + r) / 2, (t + b) / 2 W, H = np.array(scale) * (1 - margin * 2) scale = max(w / W, h / H) if w / h > W / H: h = w / W * H else: w = h / H * W l, t, r, b = (ox - w / 2, oy - h / 2, ox + w / 2, oy + h / 2) offsetx, offsety = l - w * kmargin, b + h * kmargin shp = np.array((h, w)) * (1 + (kmargin * 2)) / scale shp = (tuple(shp.round().astype(np.int))) m = np.array([offsetx, scale, 0, offsety, 0, -scale]).reshape((2, 3)) return (shp, shape.crs, m)
def test_transform_geometries(): assert tuple(transform_geometries([ Point(1, 2), ], flip_xy)[0].coords[0]) == (2, 1) assert tuple( transform_geometries([ LineString([(0, 1), (1, 2)]), ], flip_xy)[0].coords[0]) == (1, 0) assert tuple( transform_geometries([ Polygon([(0, 1), (1, 2), (2, 3)]), ], flip_xy)[0].exterior.coords[0]) == (1, 0) assert tuple( transform_geometries([ GeometryCollection([ Point(1, 2), ]), ], flip_xy)[0].geoms[0].coords[0]) == (2, 1)
def collection_update_extents(collection): bounds = GeometryCollection( [shape(s.geometry) for s in collection.get_all_items()]).bounds collection.extent.spatial = SpatialExtent(bounds) dates = { i.datetime for i in collection.get_all_items() if isinstance(i.datetime, datetime) } if len(dates) == 1: collection.extent.temporal = TemporalExtent([(next(iter(dates)), None) ]) elif len(dates) > 1: collection.extent.temporal = TemporalExtent([(min(dates), max(dates))]) else: print("WARN: {} has no TemporalExtent. Dates: {}".format( collection.id, dates)) collection.extent.temporal = TemporalExtent([ (datetime(1900, 1, 1, 0, 0, 0), None) ])
def geom(self, union=False): """ Converts the Points to a shapely geometry. Parameters ---------- union: boolean (default=False) Whether to compute a union between the geometries Returns ------- A shapely geometry """ points = [Point(x, y) for (x, y) in self.array([0, 1])] npoints = len(points) if not npoints: geom = GeometryCollection() elif len(points) == 1: geom = points[0] else: geom = MultiPoint(points) return unary_union(geom) if union else geom
def geom(self, union=False): """ Converts the Contours to a shapely geometry. Parameters ---------- union: boolean (default=False) Whether to compute a union between the geometries Returns ------- A shapely geometry """ geoms = expand_geoms([g['geometry'] for g in path_to_geom_dicts(self)]) ngeoms = len(geoms) if not ngeoms: geom = GeometryCollection() elif ngeoms == 1: geom = geoms[0] else: geom = MultiLineString(geoms) return unary_union(geom) if union else geom
def geom(self, union=False): """ Converts the Rectangles to a shapely geometry. Parameters ---------- union: boolean (default=False) Whether to compute a union between the geometries Returns ------- A shapely geometry """ boxes = [box(*g) for g in self.array([0, 1, 2, 3])] nboxes = len(boxes) if not nboxes: geom = GeometryCollection() elif nboxes == 1: geom = boxes[0] else: geom = MultiPolygon(boxes) return unary_union(geom) if union else geom
def geom_transformation(transformer, geom, params): LOGGER.debug(geom.type) if geom.type == 'GeometryCollection': collection = [ geom_transformation(transformer, _geom, params) for _geom in geom.geoms ] geom_output = GeometryCollection(collection) elif not geom.type.startswith('Multi'): geom_output = singlepart_geom_transformation( transformer, geom, params) elif geom.type.startswith('Multi'): parts = [ geom_transformation(transformer, part, params) for part in geom ] if parts[0].type == 'Point': geom_output = MultiPoint(parts) elif parts[0].type == 'LineString': geom_output = MultiLineString(parts) elif parts[0].type == 'Polygon': geom_output = MultiPolygon(parts) return geom_output
def test_aggregate_polygon_result_empty_ts_data(): timeseries = { "2019-01-01T12:34:56Z": [[1, 2], [3, 4]], "2019-02-01T12:34:56Z": [[5, 6], []], "2019-03-01T12:34:56Z": [[], []], "2019-04-01T12:34:56Z": [[], [7, 8]], "2019-05-01T12:34:56Z": [[9, 10], [11, 12]], } regions = GeometryCollection( [Polygon([(0, 0), (5, 1), (1, 4)]), Polygon([(6, 1), (1, 7), (9, 9)])]) result = AggregatePolygonResult(timeseries, regions=regions) result.set_format("covjson") data = json_normalize(result.prepare_for_json()) assert data["domain"]["axes"]["t"]["values"] == [ "2019-01-01T12:34:56Z", "2019-02-01T12:34:56Z", "2019-04-01T12:34:56Z", "2019-05-01T12:34:56Z", ] assert data["ranges"] == { "band0": { "type": "NdArray", "dataType": "float", "axisNames": ["t", "composite"], "shape": [4, 2], "values": [1, 3, 5, None, None, 7, 9, 11], }, "band1": { "type": "NdArray", "dataType": "float", "axisNames": ["t", "composite"], "shape": [4, 2], "values": [2, 4, 6, None, None, 8, 10, 12], }, }
def _parse_geom(self, geometry): geom_type = self._geometry_type.upper() geom = None # Check for LV95 geometry for element in geometry: tag = get_tag(element) if tag == self.TAG_POINT_LV95: geom = self._parse_point(element, 2056) elif tag == self.TAG_LINE_LV95: geom = self._parse_line(element, 2056) elif tag == self.TAG_AREA_LV95: geom = self._parse_area(element, 2056) # Check for LV03 geometry as fallback if geom is None: for element in geometry: tag = get_tag(element) if tag == self.TAG_POINT_LV03: geom = self._parse_point(element, 21781) elif tag == self.TAG_LINE_LV03: geom = self._parse_line(element, 21781) elif tag == self.TAG_AREA_LV03: geom = self._parse_area(element, 21781) # Wrap in collection if necessary if geom is not None: if geom_type == 'MULTIPOINT': geom = MultiPoint([geom]) elif geom_type == 'MULTILINESTRING': geom = MultiLineString([geom]) elif geom_type == 'MULTIPOLYGON': geom = MultiPolygon([geom]) elif geom_type == 'GEOMETRYCOLLECTION': geom = GeometryCollection([geom]) # Return geometry or None return None if geom is None else from_shape(geom, srid=2056)
def _clip_bounds(bbox, filename): """Clip input fiona-compatible vector file to input bounding box. :param bbox: Tuple of (xmin,ymin,xmax,ymax) desired clipping bounds. :param filename: Input name of file containing vector data in a format compatible with fiona. :returns: Shapely Geometry object (Polygon or MultiPolygon). """ f = fiona.open(filename, 'r') shapes = list(f.items(bbox=bbox)) xmin, ymin, xmax, ymax = bbox newshapes = [] bboxpoly = sPolygon([(xmin, ymax), (xmax, ymax), (xmax, ymin), (xmin, ymin), (xmin, ymax)]) for tshape in shapes: myshape = sShape(tshape[1]['geometry']) intshape = myshape.intersection(bboxpoly) newshapes.append(intshape) newshapes.append(myshape) gc = GeometryCollection(newshapes) f.close() return gc
def test_keep_geom_type_geometry_collection2(): polys1 = [ box(0, 0, 1, 1), box(1, 1, 3, 3).union(box(1, 3, 5, 5)), ] polys2 = [ box(0, 0, 1, 1), box(3, 1, 4, 2).union(box(4, 1, 5, 4)), ] df1 = GeoDataFrame({"left": [0, 1], "geometry": polys1}) df2 = GeoDataFrame({"right": [0, 1], "geometry": polys2}) result1 = overlay(df1, df2, keep_geom_type=True) expected1 = GeoDataFrame( { "left": [0, 1], "right": [0, 1], "geometry": [box(0, 0, 1, 1), box(4, 3, 5, 4)], } ) assert_geodataframe_equal(result1, expected1) result1 = overlay(df1, df2, keep_geom_type=False) expected1 = GeoDataFrame( { "left": [0, 1, 1], "right": [0, 0, 1], "geometry": [ box(0, 0, 1, 1), Point(1, 1), GeometryCollection([box(4, 3, 5, 4), LineString([(3, 1), (3, 2)])]), ], } ) assert_geodataframe_equal(result1, expected1)