def map_parallel(self, latitude): p = SphericalPoint(0, latitude) radius = p.distance(self.projection.parallel_circle_center) center = self.projection.parallel_circle_center c = Circle(center, radius) c = self.map_circle(c) if self.bordered: crossings = self.circle_intersect_borders(c) if crossings: parallels = [] for i in range(len(crossings)): a1, c1, b1 = crossings[i - 1] a2, c2, b2 = crossings[i] if a1 > a2: a2 += 360.0 aavg = math.radians(0.5 * (a1 + a2)) pavg = c.center + Point(c.radius * math.cos(aavg), c.radius * math.sin(aavg)) if self.inside_maparea(pavg): arc = Arc(c.center, c.radius, a1, a2) p = self.gridline_factory.parallel(latitude, arc) p.border1 = b1 p.tickangle1 = a1 + 90 if self.gridline_factory.rotate_parallel_labels: if latitude > 0: p.labelangle1 = a1 + 90 else: p.labelangle1 = a1 - 90 else: p.labelangle1 = 0 p.border2 = b2 p.tickangle2 = a2 + 90 if self.gridline_factory.rotate_parallel_labels: if latitude > 0: p.labelangle2 = a2 + 90 else: p.labelangle2 = a2 - 90 else: p.labelangle2 = 0 parallels.append(p) else: if self.inside_maparea(c.center): p = self.gridline_factory.parallel(latitude, c) parallels = [p] else: parallels = [] else: if self.projection.reference_latitude > 0: start_angle = self.map_meridian( self.min_longitude).meridian.angle + 180 stop_angle = self.map_meridian( self.max_longitude).meridian.angle + 180 else: start_angle = self.map_meridian( self.max_longitude).meridian.angle stop_angle = self.map_meridian( self.min_longitude).meridian.angle a = Arc(c.center, c.radius, start_angle, stop_angle) p = self.gridline_factory.parallel(latitude, a) if self.projection.reference_latitude > 0: p.border1 = 'right' else: p.border1 = 'left' p.tickangle1 = start_angle + 90 if self.gridline_factory.rotate_parallel_labels: if self.projection.reference_latitude > 0: p.labelangle1 = start_angle + 90 else: p.labelangle1 = start_angle - 90 else: p.labelangle1 = 0 if self.projection.reference_latitude > 0: p.border2 = 'left' else: p.border2 = 'right' p.tickangle2 = stop_angle - 90 if self.gridline_factory.rotate_parallel_labels: if self.projection.reference_latitude > 0: p.labelangle2 = stop_angle + 90 else: p.labelangle2 = stop_angle - 90 else: p.labelangle2 = 0 parallels = [p] return parallels
class AzimuthalEquidistantProjection(object): def __init__(self, north=True, reference_longitude=0, reference_scale=45, celestial=False): """ :param north: whether to plot the north pole :param reference_longitude: the longitude that points to the right :param reference_scale: degrees of latitude per unit distance :param celestial: longitude increases clockwise around north pole """ self.origin = SphericalPoint(0, 0) self._north = north self.reference_longitude = reference_longitude self.reference_scale = reference_scale self._celestial = celestial if self._north: if self.reference_scale >= 90: raise ProjectionError( "Invalid reference scale {} for north pole".format( self.reference_scale)) self.origin_latitude = 90 else: self.reference_scale = -self.reference_scale if self.reference_scale <= -90: raise ProjectionError( "Invalid reference scale {} for south pole".format( self.reference_scale)) self.origin_latitude = -90 self.reverse_polar_direction = not xor(self._north, self._celestial) def __call__(self, point, inverse=False): if inverse: return self.inverse_project(point) return self.project(point) @property def celestial(self): return self._celestial @celestial.setter def celestial(self, celestial): self._celestial = celestial self.reverse_polar_direction = not xor(self._north, self._celestial) @property def north(self): return self._north @north.setter def north(self, north): self.north = north self.reverse_polar_direction = not xor(self._north, self._celestial) def project(self, spherical_point): rho = (self.origin_latitude - spherical_point.latitude) / float( self.reference_scale) if self.reverse_polar_direction: theta = -self.reduce_longitude( spherical_point.longitude) + 90 + self.reference_longitude else: theta = self.reduce_longitude( spherical_point.longitude) + 90 - self.reference_longitude return Point(rho * math.sin(math.radians(theta)), -rho * math.cos(math.radians(theta))) def inverse_project(self, point): rho = self.origin.distance(point) theta = ensure_angle_range(math.degrees(math.atan2(point.y, point.x))) if self.reverse_polar_direction: longitude = ensure_angle_range(-theta + self.reference_longitude) else: longitude = ensure_angle_range(theta + self.reference_longitude) return SphericalPoint( longitude, -rho * self.reference_scale + self.origin_latitude) def reduce_longitude(self, longitude): return ensure_angle_range(longitude, self.reference_longitude)