def _create_grid(self, canvas): """ Create a new Grid object for the given MapCanvas. Args: canvas (MapCanvas): Map Canvas (see _create_map_canvas). Return a new Grid object. """ # Prepare the grid SHP map_grid = Grid(canvas.get_actual_bounding_box(), canvas.get_actual_scale(), self.rc.i18n.isrtl()) grid_shape = map_grid.generate_shape_file( os.path.join(self.tmpdir, 'grid.shp')) # Add the grid SHP to the map canvas.add_shape_file(grid_shape, self.rc.stylesheet.grid_line_color, self.rc.stylesheet.grid_line_alpha, self.rc.stylesheet.grid_line_width) return map_grid
def __init__(self, db, rc, tmpdir, dpi, file_prefix): Renderer.__init__(self, db, rc, tmpdir, dpi) self._grid_legend_margin_pt = \ min(Renderer.GRID_LEGEND_MARGIN_RATIO * self.paper_width_pt, Renderer.GRID_LEGEND_MARGIN_RATIO * self.paper_height_pt) # Compute the usable area per page self._usable_area_width_pt = (self.paper_width_pt - (2 * Renderer.PRINT_SAFE_MARGIN_PT)) self._usable_area_height_pt = (self.paper_height_pt - (2 * Renderer.PRINT_SAFE_MARGIN_PT)) scale_denom = Renderer.DEFAULT_SCALE # the mapnik scale depends on the latitude. However we are # always using Mapnik conversion functions (lat,lon <-> # mercator_meters) so we don't need to take into account # latitude in following computations # by convention, mapnik uses 90 ppi whereas cairo uses 72 ppi scale_denom *= float(72) / 90 GRAYED_MARGIN_MM = 10 OVERLAP_MARGIN_MM = 20 # Debug: show original bounding box as JS code # print self.rc.bounding_box.as_javascript("original", "#00ff00") # Convert the original Bounding box into Mercator meters self._proj = mapnik.Projection(coords._MAPNIK_PROJECTION) orig_envelope = self._project_envelope(self.rc.bounding_box) # Extend the bounding box to take into account the lost outter # margin off_x = orig_envelope.minx - (GRAYED_MARGIN_MM * scale_denom) / 1000 off_y = orig_envelope.miny - (GRAYED_MARGIN_MM * scale_denom) / 1000 width = orig_envelope.width() + (2 * GRAYED_MARGIN_MM * scale_denom) / 1000 height = orig_envelope.height() + (2 * GRAYED_MARGIN_MM * scale_denom) / 1000 # Calculate the total width and height of paper needed to # render the geographical area at the current scale. total_width_pt = commons.convert_mm_to_pt(float(width) * 1000 / scale_denom) total_height_pt = commons.convert_mm_to_pt(float(height) * 1000 / scale_denom) self.grayed_margin_pt = commons.convert_mm_to_pt(GRAYED_MARGIN_MM) overlap_margin_pt = commons.convert_mm_to_pt(OVERLAP_MARGIN_MM) # Calculate the number of pages needed in both directions if total_width_pt < self._usable_area_width_pt: nb_pages_width = 1 else: nb_pages_width = \ (float(total_width_pt - self._usable_area_width_pt) / \ (self._usable_area_width_pt - overlap_margin_pt)) + 1 if total_height_pt < self._usable_area_height_pt: nb_pages_height = 1 else: nb_pages_height = \ (float(total_height_pt - self._usable_area_height_pt) / \ (self._usable_area_height_pt - overlap_margin_pt)) + 1 # Round up the number of pages needed so that we have integer # number of pages self.nb_pages_width = int(math.ceil(nb_pages_width)) self.nb_pages_height = int(math.ceil(nb_pages_height)) # Calculate the entire paper area available total_width_pt_after_extension = self._usable_area_width_pt + \ (self._usable_area_width_pt - overlap_margin_pt) * (self.nb_pages_width - 1) total_height_pt_after_extension = self._usable_area_height_pt + \ (self._usable_area_height_pt - overlap_margin_pt) * (self.nb_pages_height - 1) # Convert this paper area available in the number of Mercator # meters that can be rendered on the map total_width_merc = \ commons.convert_pt_to_mm(total_width_pt_after_extension) * scale_denom / 1000 total_height_merc = \ commons.convert_pt_to_mm(total_height_pt_after_extension) * scale_denom / 1000 # Extend the geographical boundaries so that we completely # fill the available paper size. We are careful to extend the # boundaries evenly on all directions (so the center of the # previous boundaries remain the same as the new one) off_x -= (total_width_merc - width) / 2 width = total_width_merc off_y -= (total_height_merc - height) / 2 height = total_height_merc # Calculate what is the final global bounding box that we will render envelope = mapnik.Box2d(off_x, off_y, off_x + width, off_y + height) self._geo_bbox = self._inverse_envelope(envelope) # Debug: show transformed bounding box as JS code # print self._geo_bbox.as_javascript("extended", "#0f0f0f") # Convert the usable area on each sheet of paper into the # amount of Mercator meters we can render in this area. usable_area_merc_m_width = commons.convert_pt_to_mm(self._usable_area_width_pt) * scale_denom / 1000 usable_area_merc_m_height = commons.convert_pt_to_mm(self._usable_area_height_pt) * scale_denom / 1000 grayed_margin_merc_m = (GRAYED_MARGIN_MM * scale_denom) / 1000 overlap_margin_merc_m = (OVERLAP_MARGIN_MM * scale_denom) / 1000 # Calculate all the bounding boxes that correspond to the # geographical area that will be rendered on each sheet of # paper. area_polygon = shapely.wkt.loads(self.rc.polygon_wkt) bboxes = [] self.page_disposition, map_number = {}, 0 for j in reversed(range(0, self.nb_pages_height)): col = self.nb_pages_height - j - 1 self.page_disposition[col] = [] for i in range(0, self.nb_pages_width): cur_x = off_x + i * (usable_area_merc_m_width - overlap_margin_merc_m) cur_y = off_y + j * (usable_area_merc_m_height - overlap_margin_merc_m) envelope = mapnik.Box2d(cur_x, cur_y, cur_x+usable_area_merc_m_width, cur_y+usable_area_merc_m_height) envelope_inner = mapnik.Box2d(cur_x + grayed_margin_merc_m, cur_y + grayed_margin_merc_m, cur_x + usable_area_merc_m_width - grayed_margin_merc_m, cur_y + usable_area_merc_m_height - grayed_margin_merc_m) inner_bb = self._inverse_envelope(envelope_inner) if not area_polygon.disjoint(shapely.wkt.loads( inner_bb.as_wkt())): self.page_disposition[col].append(map_number) map_number += 1 bboxes.append((self._inverse_envelope(envelope), inner_bb)) else: self.page_disposition[col].append(None) # Debug: show per-page bounding boxes as JS code # for i, (bb, bb_inner) in enumerate(bboxes): # print bb.as_javascript(name="p%d" % i) self.pages = [] # Create an overview map overview_bb = self._geo_bbox.create_expanded(0.001, 0.001) # Create the overview grid self.overview_grid = OverviewGrid(overview_bb, [bb_inner for bb, bb_inner in bboxes], self.rc.i18n.isrtl()) grid_shape = self.overview_grid.generate_shape_file( os.path.join(self.tmpdir, 'grid_overview.shp')) # Create a canvas for the overview page self.overview_canvas = MapCanvas(self.rc.stylesheet, overview_bb, self._usable_area_width_pt, self._usable_area_height_pt, dpi, extend_bbox_to_ratio=True) # Create the gray shape around the overview map exterior = shapely.wkt.loads(self.overview_canvas.get_actual_bounding_box()\ .as_wkt()) interior = shapely.wkt.loads(self.rc.polygon_wkt) shade_wkt = exterior.difference(interior).wkt shade = maplib.shapes.PolyShapeFile(self.rc.bounding_box, os.path.join(self.tmpdir, 'shape_overview.shp'), 'shade-overview') shade.add_shade_from_wkt(shade_wkt) self.overview_canvas.add_shape_file(shade) self.overview_canvas.add_shape_file(grid_shape, self.rc.stylesheet.grid_line_color, 1, self.rc.stylesheet.grid_line_width) self.overview_canvas.render() # Create the map canvas for each page indexes = [] for i, (bb, bb_inner) in enumerate(bboxes): # Create the gray shape around the map exterior = shapely.wkt.loads(bb.as_wkt()) interior = shapely.wkt.loads(bb_inner.as_wkt()) shade_wkt = exterior.difference(interior).wkt shade = maplib.shapes.PolyShapeFile( bb, os.path.join(self.tmpdir, 'shade%d.shp' % i), 'shade%d' % i) shade.add_shade_from_wkt(shade_wkt) # Create the contour shade # Area to keep visible interior_contour = shapely.wkt.loads(self.rc.polygon_wkt) # Determine the shade WKT shade_contour_wkt = interior.difference(interior_contour).wkt # Prepare the shade SHP shade_contour = maplib.shapes.PolyShapeFile(bb, os.path.join(self.tmpdir, 'shade_contour%d.shp' % i), 'shade_contour%d' % i) shade_contour.add_shade_from_wkt(shade_contour_wkt) # Create one canvas for the current page map_canvas = MapCanvas(self.rc.stylesheet, bb, self._usable_area_width_pt, self._usable_area_height_pt, dpi, extend_bbox_to_ratio=False) # Create the grid map_grid = Grid(bb_inner, map_canvas.get_actual_scale(), self.rc.i18n.isrtl()) grid_shape = map_grid.generate_shape_file( os.path.join(self.tmpdir, 'grid%d.shp' % i)) map_canvas.add_shape_file(shade) map_canvas.add_shape_file(shade_contour, self.rc.stylesheet.shade_color_2, self.rc.stylesheet.shade_alpha_2) map_canvas.add_shape_file(grid_shape, self.rc.stylesheet.grid_line_color, self.rc.stylesheet.grid_line_alpha, self.rc.stylesheet.grid_line_width) map_canvas.render() self.pages.append((map_canvas, map_grid)) # Create the index for the current page inside_contour_wkt = interior_contour.intersection(interior).wkt index = StreetIndex(self.db, inside_contour_wkt, self.rc.i18n, page_number=(i + 4)) index.apply_grid(map_grid) indexes.append(index) # Merge all indexes self.index_categories = self._merge_page_indexes(indexes) # Prepare the small map for the front page self._front_page_map = self._prepare_front_page_map(dpi)