def test_wrap_dateline(): sinus_crs = geometry.CRS("""PROJCS["unnamed", GEOGCS["Unknown datum based upon the custom spheroid", DATUM["Not specified (based on custom spheroid)", SPHEROID["Custom spheroid",6371007.181,0]], PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433]], PROJECTION["Sinusoidal"], PARAMETER["longitude_of_center",0], PARAMETER["false_easting",0], PARAMETER["false_northing",0], UNIT["Meter",1]]""") albers_crs = geometry.CRS('EPSG:3577') geog_crs = geometry.CRS('EPSG:4326') wrap = geometry.polygon([(12231455.716333, -5559752.598333), (12231455.716333, -4447802.078667), (13343406.236, -4447802.078667), (13343406.236, -5559752.598333), (12231455.716333, -5559752.598333)], crs=sinus_crs) wrapped = wrap.to_crs(geog_crs) assert wrapped.type == 'Polygon' wrapped = wrap.to_crs(geog_crs, wrapdateline=True) # assert wrapped.type == 'MultiPolygon' TODO: these cases are quite hard to implement. # hopefully GDAL's CutGeometryOnDateLineAndAddToMulti will be available through py API at some point wrap = geometry.polygon([(13343406.236, -5559752.598333), (13343406.236, -4447802.078667), (14455356.755667, -4447802.078667), (14455356.755667, -5559752.598333), (13343406.236, -5559752.598333)], crs=sinus_crs) wrapped = wrap.to_crs(geog_crs) assert wrapped.type == 'Polygon' wrapped = wrap.to_crs(geog_crs, wrapdateline=True) # assert wrapped.type == 'MultiPolygon' TODO: same as above wrap = geometry.polygon([(14455356.755667, -5559752.598333), (14455356.755667, -4447802.078667), (15567307.275333, -4447802.078667), (15567307.275333, -5559752.598333), (14455356.755667, -5559752.598333)], crs=sinus_crs) wrapped = wrap.to_crs(geog_crs) assert wrapped.type == 'Polygon' wrapped = wrap.to_crs(geog_crs, wrapdateline=True) # assert wrapped.type == 'MultiPolygon' TODO: same as above wrap = geometry.polygon([(3658653.1976781483, -4995675.379595791), (4025493.916030875, -3947239.249752495), (4912789.243100313, -4297237.125269571), (4465089.861944263, -5313778.16975072), (3658653.1976781483, -4995675.379595791)], crs=albers_crs) wrapped = wrap.to_crs(geog_crs) assert wrapped.type == 'Polygon' assert wrapped.intersects(geometry.line([(0, -90), (0, 90)], crs=geog_crs)) wrapped = wrap.to_crs(geog_crs, wrapdateline=True) assert wrapped.type == 'MultiPolygon' assert not wrapped.intersects( geometry.line([(0, -90), (0, 90)], crs=geog_crs))
def test_common_crs(): assert common_crs([]) is None assert common_crs([geometry.point(0, 0, epsg4326), geometry.line([(0, 0), (1, 1)], epsg4326)]) is epsg4326 with pytest.raises(CRSMismatchError): common_crs([geometry.point(0, 0, epsg4326), geometry.line([(0, 0), (1, 1)], epsg3857)])
def test_wrap_dateline_utm(): poly = geometry.box(618300, -1876800, 849000, -1642500, 'EPSG:32660') wrapped = poly.to_crs(epsg4326) assert wrapped.type == 'Polygon' assert wrapped.intersects(geometry.line([(0, -90), (0, 90)], crs=epsg4326)) wrapped = poly.to_crs(epsg4326, wrapdateline=True) assert wrapped.type == 'MultiPolygon' assert not wrapped.intersects( geometry.line([(0, -90), (0, 90)], crs=epsg4326))
def test_geom_split(): box = geometry.box(0, 0, 10, 30, epsg4326) line = geometry.line([(5, 0), (5, 30)], epsg4326) bb = list(box.split(line)) assert len(bb) == 2 assert box.contains(bb[0] | bb[1]) assert (box ^ (bb[0] | bb[1])).is_empty with pytest.raises(CRSMismatchError): list(box.split(geometry.line([(5, 0), (5, 30)], epsg3857)))
def test_wrap_dateline(): albers_crs = epsg3577 geog_crs = epsg4326 wrap = geometry.polygon([(3658653.1976781483, -4995675.379595791), (4025493.916030875, -3947239.249752495), (4912789.243100313, -4297237.125269571), (4465089.861944263, -5313778.16975072), (3658653.1976781483, -4995675.379595791)], crs=albers_crs) wrapped = wrap.to_crs(geog_crs) assert wrapped.type == 'Polygon' assert wrapped.intersects(geometry.line([(0, -90), (0, 90)], crs=geog_crs)) wrapped = wrap.to_crs(geog_crs, wrapdateline=True) assert wrapped.type == 'MultiPolygon' assert not wrapped.intersects(geometry.line([(0, -90), (0, 90)], crs=geog_crs))
def test_multigeom(): p1, p2 = (0, 0), (1, 2) p3, p4 = (3, 4), (5, 6) b1 = geometry.box(*p1, *p2, epsg4326) b2 = geometry.box(*p3, *p4, epsg4326) bb = multigeom([b1, b2]) assert bb.type == 'MultiPolygon' assert bb.crs is b1.crs assert len(list(bb)) == 2 g1 = geometry.line([p1, p2], None) g2 = geometry.line([p3, p4], None) gg = multigeom(iter([g1, g2, g1])) assert gg.type == 'MultiLineString' assert gg.crs is g1.crs assert len(list(gg)) == 3 g1 = geometry.point(*p1, epsg3857) g2 = geometry.point(*p2, epsg3857) g3 = geometry.point(*p3, epsg3857) gg = multigeom(iter([g1, g2, g3])) assert gg.type == 'MultiPoint' assert gg.crs is g1.crs assert len(list(gg)) == 3 assert list(gg)[0] == g1 assert list(gg)[1] == g2 assert list(gg)[2] == g3 # can't mix types with pytest.raises(ValueError): multigeom([geometry.line([p1, p2], None), geometry.point(*p1, None)]) # can't mix CRSs with pytest.raises(CRSMismatchError): multigeom([ geometry.line([p1, p2], epsg4326), geometry.line([p3, p4], epsg3857) ]) # only some types are supported on input with pytest.raises(ValueError): multigeom([gg])
def test_wrap_dateline_sinusoidal(pts): sinus_crs = geometry.CRS("""PROJCS["unnamed", GEOGCS["Unknown datum based upon the custom spheroid", DATUM["Not specified (based on custom spheroid)", SPHEROID["Custom spheroid",6371007.181,0]], PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433]], PROJECTION["Sinusoidal"], PARAMETER["longitude_of_center",0], PARAMETER["false_easting",0], PARAMETER["false_northing",0], UNIT["Meter",1]]""") wrap = geometry.polygon(pts, crs=sinus_crs) wrapped = wrap.to_crs(epsg4326) assert wrapped.type == 'Polygon' wrapped = wrap.to_crs(epsg4326, wrapdateline=True) assert wrapped.type == 'MultiPolygon' assert not wrapped.intersects(geometry.line([(0, -90), (0, 90)], crs=epsg4326))
def test_ops(): box1 = geometry.box(10, 10, 30, 30, crs=epsg4326) box2 = geometry.box(20, 10, 40, 30, crs=epsg4326) box3 = geometry.box(20, 10, 40, 30, crs=epsg4326) box4 = geometry.box(40, 10, 60, 30, crs=epsg4326) no_box = None assert box1 != box2 assert box2 == box3 assert box3 != no_box union1 = box1.union(box2) assert union1.area == 600.0 inter1 = box1.intersection(box2) assert bool(inter1) assert inter1.area == 200.0 inter2 = box1.intersection(box4) assert not bool(inter2) assert inter2.is_empty # assert not inter2.is_valid TODO: what's going on here? diff1 = box1.difference(box2) assert diff1.area == 200.0 symdiff1 = box1.symmetric_difference(box2) assert symdiff1.area == 400.0 # test segmented line = geometry.line([(0, 0), (0, 5), (10, 5)], epsg4326) line2 = line.segmented(2) assert line.crs is line2.crs assert line.length == line2.length assert len(line.coords) < len(line2.coords) # test interpolate pt = line.interpolate(1) assert pt.crs is line.crs assert pt.coords[0] == (0, 1) assert pt.interpolate(3) is None
def vector_to_crs(point, vector, original_crs, destination_crs): """ Transform a vector (in the tangent space of a particular point) to a new CRS Expects point and vector to each be a 2-tuple in the original CRS. Returns a pair of 2-tuples (transformed point and vector). Order of coordinates is specified by the CRS (or the OGR library). """ # pylint: disable=zip-builtin-not-iterating # theoretically should use infinitesimal displacement # i.e. jacobian of the transformation # but here just use a finite displatement (for convenience of implementation) original_line = line([point, tuple(map(sum, zip(point, vector)))], crs=original_crs) transformed_line = original_line.to_crs(destination_crs) transformed_point, transformed_end = transformed_line.points # take difference (i.e. remove origin offset) transformed_vector = tuple(map(lambda x: x[1] - x[0], zip(*transformed_line.points))) return transformed_point, transformed_vector
def line(params, fp=None): """Handles line-based algorithms e.g. ndvi_transect and dispatches the right function. :param Dictionary params: dictionary with xmin,xmax,ymin,ymax,type :param file object params: optional file object to save plots are other bulky files :return: raw HTTP response (json or image/*) NOTE: line can potentially be any multipoint LINESTRING but we start with xmin,xmax,ymin,ymax """ from datacube.utils import geometry if not ('xmin' in params) or not ('xmax' in params) \ or not ('ymin' in params) or not ('ymax' in params): return error("Line requires xmin,xmax,ymin,ymax") xmin = float(params["xmin"]) xmax = float(params["xmax"]) ymin = float(params["ymin"]) ymax = float(params["ymax"]) # NOTE: you can use a line with multiple points here line = geometry.line([(xmin, ymin), (xmax, ymax)], 'EPSG:4326') if (line.type != 'LineString'): return error("ndvi_transect: line not LINESTRING") query = {'geopolygon': line} if ('time_begin' in params) and ('time_end' in params): if (not isdate(params['time_begin'])) or (not isdate( params['time_end'])): return error("Invalid time specified") query['time'] = (params['time_begin'], params['time_end']) if params['type'] == 'ndvi_transect': if not fp: return error("ndvi_transet needs a pre-allocated file") return ndvi_transect(query, fp) else: return error("Supported line-processing types: ndvi_transect")
def test_ops(): box1 = geometry.box(10, 10, 30, 30, crs=epsg4326) box2 = geometry.box(20, 10, 40, 30, crs=epsg4326) box3 = geometry.box(20, 10, 40, 30, crs=epsg4326) box4 = geometry.box(40, 10, 60, 30, crs=epsg4326) no_box = None assert box1 != box2 assert box2 == box3 assert box3 != no_box union1 = box1.union(box2) assert union1.area == 600.0 with pytest.raises(geometry.CRSMismatchError): box1.union(box2.to_crs(epsg3857)) inter1 = box1.intersection(box2) assert bool(inter1) assert inter1.area == 200.0 inter2 = box1.intersection(box4) assert not bool(inter2) assert inter2.is_empty # assert not inter2.is_valid TODO: what's going on here? diff1 = box1.difference(box2) assert diff1.area == 200.0 symdiff1 = box1.symmetric_difference(box2) assert symdiff1.area == 400.0 # test segmented line = geometry.line([(0, 0), (0, 5), (10, 5)], epsg4326) line2 = line.segmented(2) assert line.crs is line2.crs assert line.length == line2.length assert len(line.coords) < len(line2.coords) poly = geometry.polygon([(0, 0), (0, 5), (10, 5)], epsg4326) poly2 = poly.segmented(2) assert poly.crs is poly2.crs assert poly.length == poly2.length assert poly.area == poly2.area assert len(poly.geom.exterior.coords) < len(poly2.geom.exterior.coords) poly2 = poly.exterior.segmented(2) assert poly.crs is poly2.crs assert poly.length == poly2.length assert len(poly.geom.exterior.coords) < len(poly2.geom.coords) # test interpolate pt = line.interpolate(1) assert pt.crs is line.crs assert pt.coords[0] == (0, 1) assert isinstance(pt.coords, list) with pytest.raises(TypeError): pt.interpolate(3) # test array interface assert line.__array_interface__ is not None assert np.array(line).shape == (3, 2) # test simplify poly = geometry.polygon([(0, 0), (0, 5), (10, 5)], epsg4326) assert poly.simplify(100) == poly # test iteration poly_2_parts = geometry.Geometry( { "type": "MultiPolygon", "coordinates": [[[[102.0, 2.0], [103.0, 2.0], [103.0, 3.0], [102.0, 3.0], [102.0, 2.0]]], [[[100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0]], [[100.2, 0.2], [100.8, 0.2], [100.8, 0.8], [100.2, 0.8], [100.2, 0.2]]]] }, 'EPSG:4326') pp = list(poly_2_parts) assert len(pp) == 2 assert all(p.crs == poly_2_parts.crs for p in pp) # test transform assert geometry.point( 0, 0, epsg4326).transform(lambda x, y: (x + 1, y + 2)) == geometry.point( 1, 2, epsg4326) # test sides box = geometry.box(1, 2, 11, 22, epsg4326) ll = list(geometry.sides(box)) assert all(l.crs is epsg4326 for l in ll) assert len(ll) == 4 assert ll[0] == geometry.line([(1, 2), (1, 22)], epsg4326) assert ll[1] == geometry.line([(1, 22), (11, 22)], epsg4326) assert ll[2] == geometry.line([(11, 22), (11, 2)], epsg4326) assert ll[3] == geometry.line([(11, 2), (1, 2)], epsg4326)
def to_crs(self, new_crs): grid = self.layer.grids[new_crs] skip_x_xform = False skip_y_xform = False if self.crs != new_crs: if not self.subsetted.x and not self.subsetted.y: # Neither axis subsetted self.min.x = self.layer.ranges["bboxes"][new_crs]["left"] self.max.x = self.layer.ranges["bboxes"][new_crs]["right"] self.min.y = self.layer.ranges["bboxes"][new_crs]["bottom"] self.max.y = self.layer.ranges["bboxes"][new_crs]["top"] self.crs = new_crs elif not self.subsetted.x or not self.subsetted.y: # One axis subsetted if self.subsetted.x: self.min.y = self.layer.ranges["bboxes"][self.crs]["bottom"] self.max.y = self.layer.ranges["bboxes"][self.crs]["top"] skip_y_xform = True if self.subsetted.y: self.min.x = self.layer.ranges["bboxes"][self.crs]["left"] self.max.x = self.layer.ranges["bboxes"][self.crs]["right"] skip_x_xform = True else: # Both axes subsetted pass if self.crs != new_crs: is_point = False # Prepare geometry for transformation old_crs_obj = self.cfg.crs(self.crs) if self.is_slice("x") and self.is_slice("y"): geom = geometry.point(self.min.x, self.min.y, old_crs_obj) is_point = True elif self.is_slice("x") or self.is_slice("y"): geom = geometry.line( ( (self.min.x, self.min.y), (self.max.x, self.max.y) ), old_crs_obj) else: geom = geometry.polygon( ( (self.min.x, self.min.y), (self.min.x, self.max.y), (self.max.x, self.max.y), (self.max.x, self.min.y), (self.min.x, self.min.y), ), old_crs_obj ) new_crs_obj = self.cfg.crs(new_crs) grid = self.layer.grids[new_crs] if is_point: prj_pt = geom.to_crs(new_crs_obj) x, y = prj_pt.coords[0] self.min.set(x, y) self.max.set(x + grid["resolution"][0], y + grid["resolution"][1]) self.size.set(1, 1) else: proj_geom = geom.to_crs(new_crs_obj) bbox = proj_geom.boundingbox if skip_x_xform: self.min.x = self.layer.ranges["bboxes"][new_crs]["left"] self.max.x = self.layer.ranges["bboxes"][new_crs]["right"] else: self.min.x = bbox.left self.max.x = bbox.right if skip_y_xform: self.min.y = self.layer.ranges["bboxes"][new_crs]["bottom"] self.max.y = self.layer.ranges["bboxes"][new_crs]["top"] else: self.min.y = bbox.bottom self.max.y = bbox.top self.quantise_to_resolution(grid) self.crs = new_crs else: self.quantise_to_resolution(grid)