def rightlegend(figure, chart_number, min_longitude, max_longitude, min_latitude, max_latitude): # Lower legend box p1 = figure.llcorner p2 = figure.llcorner + Point(LEGEND_WIDTH, LEGEND_HEIGHT) l = DrawingArea(p1, p2, p1, box=False) figure.add(l) l.draw_bounding_box(0.4) # Long/lat ranges l.draw_line(Line(Point(138, 1.5), Point(138, LEGEND_HEIGHT - 1.5))) minha = HourAngle() minha.from_degrees(min_longitude) maxha = HourAngle() maxha.from_degrees(max_longitude) longlabel = "\\condensed\\textbf{{{:02}\\raisebox{{0.35em}}{{\\footnotesize h}}{:02}\\raisebox{{0.35em}}{{\\footnotesize m}} to {:02}\\raisebox{{0.35em}}{{\\small h}}{:02}\\raisebox{{0.35em}}{{\\small m}}}}".format( minha.hours, minha.minutes, maxha.hours, maxha.minutes) l.draw_label(Label(Point(158, 6.75), longlabel, 90, "LARGE")) latlabel = "\\condensed\\textbf{{" if min_latitude >= 0: latlabel += "+{:02}".format(min_latitude) else: latlabel += "--{:02}".format(abs(min_latitude)) latlabel += "\\textdegree{{}} to " if max_latitude >= 0: latlabel += "+{:02}".format(max_latitude) else: latlabel += "--{:02}".format(abs(max_latitude)) latlabel += "\\textdegree}}" l.draw_label(Label(Point(158, 1), latlabel, 90, "LARGE")) # Constellations l.draw_line(Line(Point(178, 1.5), Point(178, LEGEND_HEIGHT - 1.5))) constellations = constellations_in_area(min_longitude, max_longitude, min_latitude, max_latitude, nsamples=10000) l.draw_label( Label(Point(187.5, 5.0), "\\condensed\\textbf{{{}}}".format(constellations[0].upper()), 90, "LARGE")) if len(constellations) > 1: l.draw_label( Label( Point(187.5, 1.5), "\\condensed\\textbf{{{}}}".format( ", ".join(constellations[1:4]).upper()), 90, "small")) # Chart number p1 = figure.urcorner + Point(-EDGE_MARGIN, TOP_MARGIN - 17) p2 = p1 + Point(2 * EDGE_MARGIN, 17) l = DrawingArea(p1, p2, box=False) figure.add(l) l.draw_label( Label(Point(EDGE_MARGIN, -1.5), "\\textbf{{{}}}".format(chart_number), 90, "huge"))
def legend(figure, chart_number): l = DrawingArea(f.llcorner + Point(264, 0), f.urcorner, f.llcorner + Point(264, 0)) figure.add(l) l.draw_label(Label(Point(8, 189), "Epoch", 90, "tiny")) l.draw_label(Label(Point(8, 185), "\\textbf{2000.0}", 90, "normalsize")) l.draw_line(Line(Point(2, 183.5), Point(14, 183.5))) l.draw_line(Line(Point(2, 15), Point(14, 15))) l.draw_label(Label(Point(8, 11), "Chart number", 90, "tiny")) l.draw_label( Label(Point(8, 2), "\\textbf{{{}}}".format(chart_number), 90, "Huge"))
def __init__(self, tikz, p1, p2, origin=None, boxed=True, box_linewidth=0.5): # Make sure the p1 is lower left and p2 is upper right self.p1 = Point(min(p1.x, p2.x), min(p1.y, p2.y)) self.p2 = Point(max(p1.x, p2.x), max(p1.y, p2.y)) if origin is None: self.set_origin(0.5 * (p1 + p2)) else: self.set_origin(origin) # Calculate the picture coordinates of the corners self.llcorner = Point(self.minx, self.miny) self.lrcorner = Point(self.maxx, self.miny) self.urcorner = Point(self.maxx, self.maxy) self.ulcorner = Point(self.minx, self.maxy) # Construct the borders of the picture self.bottom_border = Line(self.llcorner, self.lrcorner) self.right_border = Line(self.lrcorner, self.urcorner) self.top_border = Line(self.urcorner, self.ulcorner) self.left_border = Line(self.ulcorner, self.llcorner) self.borderdict = { "left": self.left_border, "top": self.top_border, "right": self.right_border, "bottom": self.bottom_border, } self.clipper = Clipper(self.borderdict) self.boxed = boxed self.box_linewidth = box_linewidth self.width = p2.x - p1.x self.height = p2.y - p1.y self.texstring = "" self.pen_style = None # self._dotted = False # self._dashed = False self.linewidth = 0.5 self.color = "black" self.opened = False self.closed = False tikz.add(self)
def equatorial_meridian(self, tick_interval, frame): if tick_interval is not None: for i in range(int(360 / int(tick_interval))): l = i * tick_interval sp = SkyCoordDeg(l, 0, frame=frame).icrs if self.clip_at_border: p = self.projection.project(sp) if not self.clipper.point_inside(p): continue else: if not self.inside_coordinate_range(sp): continue p = self.projection.project(sp) v = self.projection.project( SkyCoordDeg(l + 1, 0, frame=frame).icrs) - p v = v.rotate(90) / v.norm tp1 = p + 0.5 * v tp2 = p - 0.5 * v lp = p + 0.4 * v tick = Line(tp1, tp2) label = Label( lp, text=f"\\textit{{{l}\\textdegree}}", fontsize="miniscule", angle=tick.angle - 90, position="below", fill="white", ) yield EquatorialMeridian(l, tick, label)
def draw(self): center = 0.5 * LEGEND_WIDTH self.draw_label( Label(Point(center, 189), text="Epoch", fontsize="tiny")) self.draw_label( Label(Point(center, 185), text="2000.0", fontsize="normalsize", bold=True)) self.draw_line(Line(Point(2, 183.5), Point(LEGEND_WIDTH - 2, 183.5))) self.draw_line(Line(Point(2, 15), Point(LEGEND_WIDTH - 2, 15))) self.draw_label( Label(Point(center, 11), text="Chart number", fontsize="tiny")) self.draw_label( Label(Point(center, 2), f"{self.chart_number}", bold=True, fontsize="Huge"))
def __init__( self, coordinate_grid_config, projection, borderdict, clip_at_border, clipper, longitude_range, latitude_range, latitude_range_func=None, ): self.config = coordinate_grid_config self.projection = projection self.borderdict = borderdict self.clip_at_border = clip_at_border self.clipper = clipper self.min_longitude, self.max_longitude = longitude_range self.min_latitude, self.max_latitude = latitude_range self.latitude_range_func = latitude_range_func longlatborders = { "left": Line( Point(self.min_longitude, self.min_latitude), Point(self.min_longitude, self.max_latitude), ), "right": Line( Point(self.max_longitude, self.min_latitude), Point(self.max_longitude, self.max_latitude), ), "bottom": Line( Point(self.min_longitude, self.min_latitude), Point(self.max_longitude, self.min_latitude), ), "top": Line( Point(self.min_longitude, self.max_latitude), Point(self.max_longitude, self.max_latitude), ), } self.longlat_clipper = Clipper(longlatborders)
def pole_markers(self, frame): for latitude in [-90, 90]: sp = SkyCoordDeg(0, latitude, frame=frame).icrs p = self.projection.project(sp) if self.config.rotate_poles: delta1 = (self.projection.project( SkyCoordDeg(sp.ra.degree + 1, sp.dec.degree)) - p) delta2 = (self.projection.project( SkyCoordDeg(sp.ra.degree, sp.dec.degree + 1)) - p) else: delta1 = Point(1, 0) delta2 = Point(0, 1) delta1 *= self.config.pole_marker_size / delta1.norm delta2 *= self.config.pole_marker_size / delta2.norm if self.clipper.point_inside(p): yield Line(p + delta1, p - delta1) yield Line(p + delta2, p - delta2)
def map_meridian(self, longitude): p1 = SphericalPoint(longitude, self.min_latitude) p2 = SphericalPoint(longitude, self.max_latitude) l = self.map_line(Line(p1, p2)) m = self.gridline_factory.meridian(longitude, l) m.border1 = "bottom" m.border2 = "top" m.tickangle1 = 270 m.tickangle2 = 90 return m
def polar_tick(self): if not self.config.polar_tick: return None latitude = 90 delta = Point(0, 1) if self.config.center_latitude < 0: delta = Point(0, -1) latitude *= -1 p1 = self.projection.project(SkyCoordDeg(0, latitude)) p2 = p1 + self.config.marked_ticksize * delta return Line(p1, p2)
def map_parallel(self, latitude): p1 = SphericalPoint(self.min_longitude, latitude) p2 = SphericalPoint(self.max_longitude, latitude) l = self.map_line(Line(p1, p2)) if l.p1.x > l.p2.x: l.p1, l.p2 = l.p2, l.p1 p = self.gridline_factory.parallel(latitude, l) p.border1 = "left" p.border2 = "right" p.tickangle1 = 180 p.tickangle2 = 0 return p
def map_meridian(self, longitude, longitude_offsets={}): offset = 0 for l in sorted(longitude_offsets.keys(), reverse=True): if longitude % l == 0: offset = longitude_offsets[l] break if self.projection.reference_latitude > 0: p1 = SphericalPoint(longitude, self.min_latitude) p2 = SphericalPoint(longitude, self.max_latitude - offset) else: p1 = SphericalPoint(longitude, self.min_latitude + offset) p2 = SphericalPoint(longitude, self.max_latitude) l = self.map_line(Line(p1, p2)) if self.bordered: intersections = self.line_intersect_borders(l) if not intersections: return None if len(intersections) == 1: if self.inside_maparea(l.p1): p2, border2 = intersections[0] p1, border1 = l.p1, None else: p1, border1 = intersections[0] p2, border2 = l.p2, None else: p1, border1 = intersections[0] p2, border2 = intersections[1] if p1.y < p2.y: p1, p2 = p2, p1 border1, border2 = border2, border1 l.p1 = p1 l.p2 = p2 else: border1 = "bottom" border2 = "top" m = self.gridline_factory.meridian(longitude, l) m.border1 = border1 m.border2 = border2 m.tickangle1 = l.angle + 180 m.tickangle2 = l.angle if self.gridline_factory.rotate_meridian_labels: m.labelangle1 = l.angle - 90 m.labelangle2 = l.angle - 90 return m
def tick(self, point, angle, border): if self.coordinate % self.config.marked_tick_interval == 0: ticklength = self.config.marked_ticksize elif self.coordinate % self.config.tick_interval == 0: ticklength = self.config.unmarked_ticksize else: return None if ticklength == 0: return None delta = self.tickdelta(angle, border) if delta.norm > 4: return None p1 = point p2 = point + ticklength * delta return Line(p1, p2)
def draw_internal_parallel_ticks(self, longitude, angle=90, labels=False, label_angle=None): reference_meridian = self.map_meridian(longitude) tickangle = reference_meridian.meridian.angle + angle start_latitude = self.gridline_factory.parallel_tick_interval * math.ceil( self.min_latitude / self.gridline_factory.parallel_tick_interval) for latitude in numpy.arange( start_latitude, self.max_latitude, self.gridline_factory.parallel_tick_interval): if latitude in [-90, 90]: continue p1 = self.projection(SphericalPoint(longitude, latitude)) if not self.inside_maparea(p1): continue delta = Point(1, 0).rotate(tickangle) if latitude % self.gridline_factory.parallel_line_interval == 0: ticksize = 0 elif latitude % self.gridline_factory.parallel_marked_tick_interval == 0: ticksize = self.gridline_factory.marked_ticksize else: ticksize = self.gridline_factory.unmarked_ticksize if ticksize > 0: p2 = p1 + ticksize * delta self.draw_line( Line(p1, p2), linewidth=self.gridline_factory.gridline_thickness) if labels and latitude % self.gridline_factory.parallel_line_interval == 0: p3 = p1 + 0.75 * delta text = "{}\\textdegree".format(int(abs(latitude))) if abs < 0: text = "--" + text else: text = "+" + text if label_angle is None: label_angle = tickangle l = Label(p3, text, label_angle, "tiny", angle=0, fill="white") self.draw_label(l)
def map_meridian(self, longitude, longitude_offsets={}): """Returns the exact line to draw for a meridian""" offset = 0 for l in sorted(longitude_offsets.keys(), reverse=True): if longitude % l == 0: offset = longitude_offsets[l] break if self.projection.north: p1 = SphericalPoint(longitude, self.projection.origin_latitude - offset) else: p1 = SphericalPoint(longitude, self.projection.origin_latitude + offset) if self.projection.north: p2 = SphericalPoint(longitude, self.min_latitude) else: p2 = SphericalPoint(longitude, self.max_latitude) l = self.map_line(Line(p1, p2)) if self.bordered: p2, border2 = self.line_intersect_borders(l)[0] l.p2 = p2 else: border2 = "bottom" m = self.gridline_factory.meridian(longitude, l) m.border2 = border2 if xor(self.projection.north, not self.projection.celestial): m.tickangle2 = -longitude + self.projection.reference_longitude else: m.tickangle2 = longitude - self.projection.reference_longitude if self.gridline_factory.rotate_meridian_labels: m.labelpos2 = 270 m.labelangle2 = l.angle + 90 else: pass return m
def draw_coordinate_system(self, transformation, linewidth=0.3, dashed='dashed', tickinterval=None, poles=False): points = [] for longitude in numpy.arange(0, 360.1, 0.251): p = transformation(SphericalPoint(longitude, 0)) points.append( SphericalPoint(self.projection.reduce_longitude(p.longitude), p.latitude)) points_to_draw = [] for i in range(len(points)): prev_p = points[i - 1] p = points[i] if i + 1 == len(points): next_p = points[0] else: next_p = points[i + 1] if self.bordered: prev_p = self.map_point(prev_p) p = self.map_point(p) next_p = self.map_point(next_p) if self.inside_maparea(prev_p) or self.inside_maparea( p) or self.inside_maparea(next_p): points_to_draw.append(p) else: prevpinside = self.inside_coordinate_range(prev_p) pinside = self.inside_coordinate_range(p) nextpinside = self.inside_coordinate_range(next_p) if prevpinside or pinside or nextpinside: points_to_draw.append(self.map_point(p)) points_to_draw = sorted(points_to_draw, key=attrgetter('x')) if points_to_draw: self.draw_polygon(points_to_draw, linewidth=linewidth, dashed=dashed) # Ticks if tickinterval is not None: for i in range(360 / int(tickinterval)): l = i * tickinterval p = transformation(SphericalPoint(l, 0)) p.longitude = self.projection.reduce_longitude(p.longitude) if self.bordered: p = self.map_point(p) if not self.inside_maparea(p): continue else: if not self.inside_coordinate_range(p): continue p = self.map_point(p) v = self.map_point(transformation(SphericalPoint(l + 1, 0))) - p v = v.rotate(90) / v.norm tp1 = p + 0.5 * v tp2 = p - 0.5 * v lp = p + 0.8 * v tick = Line(tp1, tp2) self.draw_line(tick, linewidth=linewidth) self.draw_label( Label(lp, "\\textit{{{}\\textdegree}}".format(l), 270, "miniscule", angle=tick.angle - 90, fill="white")) # Poles if poles: p = transformation(SphericalPoint(0, 90)) np = self.map_point(p) if self.gridline_factory.rotate_poles: delta1 = self.map_point(p + SphericalPoint(1, 0)) - np delta2 = self.map_point(p + SphericalPoint(0, 1)) - np else: delta1 = Point(1, 0) delta2 = Point(0, 1) delta1 *= self.gridline_factory.pole_marker_size / delta1.norm delta2 *= self.gridline_factory.pole_marker_size / delta2.norm if self.inside_maparea(np): bl1 = Line(np + 1.25 * delta1, np - 1.25 * delta1) l1 = Line(np + delta1, np - delta1) bl2 = Line(np + 1.25 * delta2, np - 1.25 * delta2) l2 = Line(np + delta2, np - delta2) self.draw_line(bl1, linewidth=3 * linewidth, color="white") self.draw_line(bl2, linewidth=3 * linewidth, color="white") self.draw_line( l1, linewidth=self.gridline_factory.gridline_thickness) self.draw_line( l2, linewidth=self.gridline_factory.gridline_thickness) p = transformation(SphericalPoint(0, -90)) sp = self.map_point(p) if self.gridline_factory.rotate_poles: delta1 = self.map_point(p + SphericalPoint(1, 0)) - sp delta2 = self.map_point(p + SphericalPoint(0, 1)) - sp else: delta1 = Point(1, 0) delta2 = Point(0, 1) delta1 *= self.gridline_factory.pole_marker_size / delta1.norm delta2 *= self.gridline_factory.pole_marker_size / delta2.norm if self.inside_maparea(sp): bl1 = Line(sp + 1.25 * delta1, sp - 1.25 * delta1) l1 = Line(sp + delta1, sp - delta1) bl2 = Line(sp + 1.25 * delta2, sp - 1.25 * delta2) l2 = Line(sp + delta2, sp - delta2) self.draw_line(bl1, linewidth=3 * linewidth, color="white") self.draw_line(bl2, linewidth=3 * linewidth, color="white") self.draw_line( l1, linewidth=self.gridline_factory.gridline_thickness) self.draw_line( l2, linewidth=self.gridline_factory.gridline_thickness)
m.parallel_ticks['left'] = False m.parallel_ticks['right'] = False m.parallel_ticks['bottom'] = False m.parallel_ticks['top'] = True m.rotate_parallel_labels = False # Draw the grid m.draw_meridians(origin_offsets=CONIC_MERIDIAN_OFFSETS) m.draw_parallels() # Draw the +90 degrees parallel tick p1 = Point(0, 0.5 * (MAP_URCORNER.y - MAP_LLCORNER.y - 2 * MAP_VMARGIN)) p2 = p1 + Point(0, m.gridline_factory.marked_ticksize) p3 = p1 + Point(0, m.gridline_factory.label_distance) m.draw_line(Line(p1, p2), linewidth=m.gridline_factory.gridline_thickness) m.draw_label(Label(p3, "+90\\textdegree", 90, "tiny")) with m.clip(m.clipping_path): m.draw_constellations(dashed=CONSTELLATION_DASH_PATTERN) m.draw_ecliptic(tickinterval=5, dashed=ECLIPTIC_DASH_PATTERN) m.draw_galactic(tickinterval=5, dashed=GALACTIC_DASH_PATTERN) # Legend legend(f, chart_number) f.render(os.path.join(OUTPUT_FOLDER, "{:02}.pdf".format(chart_number)), open=False) # North conic maps for i in range(6):
def parallel(self, latitude, min_longitude, max_longitude): p1 = self.project(SkyCoordDeg(min_longitude - 0.1, latitude)) p2 = self.project(SkyCoordDeg(max_longitude + 0.1, latitude)) return Line(p1, p2)
def meridian(self, longitude, min_latitude, max_latitude): p1 = self.project(SkyCoordDeg(longitude, min_latitude)) p2 = self.project(SkyCoordDeg(longitude, max_latitude)) return Line(p1, p2)
def __init__(self, p1, p2, origin=None, hmargin=0, vmargin=0, box=True): if origin is None: origin = 0.5 * (p1 + p2) DrawingArea.__init__(self, p1, p2, origin, box) # Total map self.map_hmargin = hmargin self.map_vmargin = vmargin # Central map area self.map_width = float(self.width - 2 * self.map_hmargin) self.map_height = float(self.height - 2 * self.map_vmargin) self.map_minx = self.p1.x + self.map_hmargin - self.origin.x self.map_maxx = self.map_minx + self.map_width self.map_miny = self.p1.y + self.map_vmargin - self.origin.y self.map_maxy = self.map_miny + self.map_height self.map_llcorner = Point(self.map_minx, self.map_miny) self.map_lrcorner = Point(self.map_maxx, self.map_miny) self.map_urcorner = Point(self.map_maxx, self.map_maxy) self.map_ulcorner = Point(self.map_minx, self.map_maxy) self.map_bottom_border = Line(self.map_llcorner, self.map_lrcorner) self.map_right_border = Line(self.map_lrcorner, self.map_urcorner) self.map_top_border = Line(self.map_urcorner, self.map_ulcorner) self.map_left_border = Line(self.map_ulcorner, self.map_llcorner) self.map_box = Rectangle(self.map_llcorner, self.map_urcorner) self.projection = UnitProjection() # Longitude/latitude ranges self.min_longitude = None self.max_longitude = None self.min_latitude = None self.max_latitude = None self.bordered = True # Longitude/latitude grid self.gridline_factory = GridLineFactory() self.gridline_factory.marked_ticksize = 1.0 self.gridline_factory.unmarked_ticksize = 0.5 self.gridline_factory.meridian_line_interval = 15 # 1 hour self.gridline_factory.meridian_marked_tick_interval = 5 # 20 minutes self.gridline_factory.meridian_tick_interval = 1.25 # 5 minutes self.gridline_factory.parallel_line_interval = 10 self.gridline_factory.parallel_marked_tick_interval = 10 self.gridline_factory.parallel_tick_interval = 1 self.parallel_ticks = { 'left': True, 'right': True, 'bottom': False, 'top': False } self.meridian_ticks = { 'left': False, 'right': False, 'bottom': True, 'top': True }
def map_line(self, line): p1 = self.map_point(line.p1) p2 = self.map_point(line.p2) return Line(p1, p2)