def __init__(self, srs_code): """ Create a new SRS with the given `srs_code` code. """ self.srs_code = srs_code init = _SRS.proj_init.get(srs_code, None) if init is not None: self.proj = init() else: epsg_num = get_epsg_num(srs_code) self.proj = Proj(init='epsg:%d' % epsg_num)
class _SRS(object): # http://trac.openlayers.org/wiki/SphericalMercator proj_init = { 'EPSG:4326': lambda: Proj('+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs +over'), 'CRS:84': lambda: Proj('+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs +over'), } for _epsg in WEBMERCATOR_EPSG: proj_init[_epsg] = lambda: Proj( '+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 ' '+lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m ' '+nadgrids=@null +no_defs +over') """ This class represents a Spatial Reference System. """ def __init__(self, srs_code): """ Create a new SRS with the given `srs_code` code. """ self.srs_code = srs_code init = _SRS.proj_init.get(srs_code, None) if init is not None: self.proj = init() else: epsg_num = get_epsg_num(srs_code) self.proj = Proj(init='epsg:%d' % epsg_num) def transform_to(self, other_srs, points): """ :type points: ``(x, y)`` or ``[(x1, y1), (x2, y2), …]`` >>> srs1 = SRS(4326) >>> srs2 = SRS(900913) >>> [str(round(x, 5)) for x in srs1.transform_to(srs2, (8.22, 53.15))] ['915046.21432', '7010792.20171'] >>> srs1.transform_to(srs1, (8.25, 53.5)) (8.25, 53.5) >>> [(str(round(x, 5)), str(round(y, 5))) for x, y in ... srs1.transform_to(srs2, [(8.2, 53.1), (8.22, 53.15), (8.3, 53.2)])] ... #doctest: +NORMALIZE_WHITESPACE [('912819.8245', '7001516.67745'), ('915046.21432', '7010792.20171'), ('923951.77358', '7020078.53264')] """ if self == other_srs: return points if isinstance(points[0], (int, float)) and 2 >= len(points) <= 3: return transform(self.proj, other_srs.proj, *points) x = [p[0] for p in points] y = [p[1] for p in points] transf_pts = transform(self.proj, other_srs.proj, x, y) return izip(transf_pts[0], transf_pts[1]) def transform_bbox_to(self, other_srs, bbox, with_points=16): """ :param with_points: the number of points to use for the transformation. A bbox transformation with only two or four points may cut off some parts due to distortions. >>> ['%.3f' % x for x in ... SRS(4326).transform_bbox_to(SRS(900913), (-180.0, -90.0, 180.0, 90.0))] ['-20037508.343', '-147730762.670', '20037508.343', '147730758.195'] >>> ['%.5f' % x for x in ... SRS(4326).transform_bbox_to(SRS(900913), (8.2, 53.1, 8.3, 53.2))] ['912819.82450', '7001516.67745', '923951.77358', '7020078.53264'] >>> SRS(4326).transform_bbox_to(SRS(4326), (8.25, 53.0, 8.5, 53.75)) (8.25, 53.0, 8.5, 53.75) """ if self == other_srs: return bbox bbox = self.align_bbox(bbox) points = generate_envelope_points(bbox, with_points) transf_pts = self.transform_to(other_srs, points) result = calculate_bbox(transf_pts) log_proj.debug('transformed from %r to %r (%s -> %s)' % (self, other_srs, bbox, result)) return result def align_bbox(self, bbox): """ Align bbox to reasonable values to prevent errors in transformations. E.g. transformations from EPSG:4326 with lat=90 or -90 will fail, so we subtract a tiny delta. At the moment only EPSG:4326 bbox will be modifyed. >>> bbox = SRS(4326).align_bbox((-180, -90, 180, 90)) >>> -90 < bbox[1] < -89.99999998 True >>> 90 > bbox[3] > 89.99999998 True """ # TODO should not be needed anymore since we transform with +over # still a few tests depend on the rounding behavior of this if self.srs_code == 'EPSG:4326': delta = 0.00000001 (minx, miny, maxx, maxy) = bbox if abs(miny - -90.0) < 1e-6: miny = -90.0 + delta if abs(maxy - 90.0) < 1e-6: maxy = 90.0 - delta bbox = minx, miny, maxx, maxy return bbox @property def is_latlong(self): """ >>> SRS(4326).is_latlong True >>> SRS(31466).is_latlong False """ return self.proj.is_latlong() @property def is_axis_order_ne(self): """ Returns `True` if the axis order is North, then East (i.e. y/x or lat/lon). >>> SRS(4326).is_axis_order_ne True >>> SRS('CRS:84').is_axis_order_ne False >>> SRS(31468).is_axis_order_ne True >>> SRS(31463).is_axis_order_ne False >>> SRS(25831).is_axis_order_ne False """ if self.srs_code in base_config().srs.axis_order_ne: return True if self.srs_code in base_config().srs.axis_order_en: return False if self.is_latlong: return True return False @property def is_axis_order_en(self): """ Returns `True` if the axis order is East then North (i.e. x/y or lon/lat). """ return not self.is_axis_order_ne def __eq__(self, other): """ >>> SRS(4326) == SRS("EpsG:4326") True >>> SRS(4326) == SRS("4326") True >>> SRS(4326) == SRS(3857) False """ if isinstance(other, _SRS): return self.proj.srs == other.proj.srs else: return NotImplemented def __ne__(self, other): """ >>> SRS(3857) != SRS(3857) False >>> SRS(4326) != SRS(900913) True """ equal_result = self.__eq__(other) if equal_result is NotImplemented: return NotImplemented else: return not equal_result def __str__(self): #pylint: disable-msg=E1101 return "SRS %s ('%s')" % (self.srs_code, self.proj.srs) def __repr__(self): """ >>> repr(SRS(4326)) "SRS('EPSG:4326')" """ return "SRS('%s')" % (self.srs_code, ) def __hash__(self): return hash(self.srs_code)
class _SRS(object): # http://trac.openlayers.org/wiki/SphericalMercator proj_init = { 'EPSG:4326': lambda: Proj('+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs +over'), 'CRS:84': lambda: Proj('+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs +over'), } for _epsg in WEBMERCATOR_EPSG: proj_init[_epsg] = lambda: Proj( '+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 ' '+lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m ' '+nadgrids=@null +no_defs +over') """ This class represents a Spatial Reference System. """ def __init__(self, srs_code): """ Create a new SRS with the given `srs_code` code. """ self.srs_code = srs_code init = _SRS.proj_init.get(srs_code, None) if init is not None: self.proj = init() else: epsg_num = get_epsg_num(srs_code) self.proj = Proj(init='epsg:%d' % epsg_num) def transform_to(self, other_srs, points): """ :type points: ``(x, y)`` or ``[(x1, y1), (x2, y2), …]`` >>> srs1 = SRS(4326) >>> srs2 = SRS(900913) >>> [str(round(x, 5)) for x in srs1.transform_to(srs2, (8.22, 53.15))] ['915046.21432', '7010792.20171'] >>> srs1.transform_to(srs1, (8.25, 53.5)) (8.25, 53.5) >>> [(str(round(x, 5)), str(round(y, 5))) for x, y in ... srs1.transform_to(srs2, [(8.2, 53.1), (8.22, 53.15), (8.3, 53.2)])] ... #doctest: +NORMALIZE_WHITESPACE [('912819.8245', '7001516.67745'), ('915046.21432', '7010792.20171'), ('923951.77358', '7020078.53264')] """ if self == other_srs: return points if isinstance(points[0], (int, float)) and 2 >= len(points) <= 3: return transform(self.proj, other_srs.proj, *points) x = [p[0] for p in points] y = [p[1] for p in points] transf_pts = transform(self.proj, other_srs.proj, x, y) return izip(transf_pts[0], transf_pts[1]) def transform_bbox_to(self, other_srs, bbox, with_points=16): """ :param with_points: the number of points to use for the transformation. A bbox transformation with only two or four points may cut off some parts due to distortions. >>> ['%.3f' % x for x in ... SRS(4326).transform_bbox_to(SRS(900913), (-180.0, -90.0, 180.0, 90.0))] ['-20037508.343', '-147730762.670', '20037508.343', '147730758.195'] >>> ['%.5f' % x for x in ... SRS(4326).transform_bbox_to(SRS(900913), (8.2, 53.1, 8.3, 53.2))] ['912819.82450', '7001516.67745', '923951.77358', '7020078.53264'] >>> SRS(4326).transform_bbox_to(SRS(4326), (8.25, 53.0, 8.5, 53.75)) (8.25, 53.0, 8.5, 53.75) """ if self == other_srs: return bbox bbox = self.align_bbox(bbox) points = generate_envelope_points(bbox, with_points) transf_pts = self.transform_to(other_srs, points) result = calculate_bbox(transf_pts) log_proj.debug('transformed from %r to %r (%s -> %s)' % (self, other_srs, bbox, result)) return result def align_bbox(self, bbox): """ Align bbox to reasonable values to prevent errors in transformations. E.g. transformations from EPSG:4326 with lat=90 or -90 will fail, so we subtract a tiny delta. At the moment only EPSG:4326 bbox will be modifyed. >>> bbox = SRS(4326).align_bbox((-180, -90, 180, 90)) >>> -90 < bbox[1] < -89.99999998 True >>> 90 > bbox[3] > 89.99999998 True """ # TODO should not be needed anymore since we transform with +over # still a few tests depend on the rounding behavior of this if self.srs_code == 'EPSG:4326': delta = 0.00000001 (minx, miny, maxx, maxy) = bbox if abs(miny - -90.0) < 1e-6: miny = -90.0 + delta if abs(maxy - 90.0) < 1e-6: maxy = 90.0 - delta bbox = minx, miny, maxx, maxy return bbox @property def is_latlong(self): """ >>> SRS(4326).is_latlong True >>> SRS(31466).is_latlong False """ return self.proj.is_latlong() @property def is_axis_order_ne(self): """ Returns `True` if the axis order is North, then East (i.e. y/x or lat/lon). >>> SRS(4326).is_axis_order_ne True >>> SRS('CRS:84').is_axis_order_ne False >>> SRS(31468).is_axis_order_ne True >>> SRS(31463).is_axis_order_ne False >>> SRS(25831).is_axis_order_ne False """ if self.srs_code in base_config().srs.axis_order_ne: return True if self.srs_code in base_config().srs.axis_order_en: return False if self.is_latlong: return True return False @property def is_axis_order_en(self): """ Returns `True` if the axis order is East then North (i.e. x/y or lon/lat). """ return not self.is_axis_order_ne def __eq__(self, other): """ >>> SRS(4326) == SRS("EpsG:4326") True >>> SRS(4326) == SRS("4326") True >>> SRS(4326) == SRS(900913) False >>> SRS(3857) == SRS(900913) True >>> SRS(900913) == SRS(3857) True """ if isinstance(other, _SRS): if (self.srs_code in WEBMERCATOR_EPSG and other.srs_code in WEBMERCATOR_EPSG): return True return self.proj.srs == other.proj.srs else: return NotImplemented def __ne__(self, other): """ >>> SRS(900913) != SRS(900913) False >>> SRS(4326) != SRS(900913) True """ equal_result = self.__eq__(other) if equal_result is NotImplemented: return NotImplemented else: return not equal_result def __str__(self): #pylint: disable-msg=E1101 return "SRS %s ('%s')" % (self.srs_code, self.proj.srs) def __repr__(self): """ >>> repr(SRS(4326)) "SRS('EPSG:4326')" """ return "SRS('%s')" % (self.srs_code,) def __hash__(self): return hash(self.srs_code)