예제 #1
0
 def make_tiles(self):
 #====================
     log('Tiling {}...'.format(self.__id))
     source_kind = self.__raster_layer.source_kind
     if source_kind == 'image':
         tile_extractor = ImageTiler(self.__raster_layer, self.__tile_set)
     elif source_kind == 'pdf':
         tile_extractor = PDFTiler(self.__raster_layer, self.__tile_set)
     elif source_kind == 'svg':
         if self.__raster_layer.local_world_to_base is None:
             tile_extractor = SVGTiler(self.__raster_layer, self.__tile_set)
         else:
             tile_extractor = SVGImageTiler(self.__raster_layer, self.__tile_set)
     else:
         raise TypeError('Unsupported kind of background tile source: {}'.format(source_kind))
     return self.__make_zoomed_tiles(tile_extractor)
예제 #2
0
    def __finish_make(self):
        #=======================
        # We are finished with the knowledge base
        settings['KNOWLEDGE_STORE'].close()

        # Show what the map is about
        if self.__flatmap.models is not None:
            log('Generated map: {} for {}'.format(self.id,
                                                  self.__flatmap.models))
        else:
            log('Generated map: {}'.format(self.id))
        ## FIX v's errorCheck
        for filename in self.__geojson_files:
            if settings.get('saveGeoJSON', False):
                print(filename)
            else:
                os.remove(filename)
예제 #3
0
 def process(self):
 #=================
     for n in range(len(self.__slides)):
         slide = self.__slides[n]
         slide_number = n + 1
         slide_layer = PowerpointSlide(self, slide, slide_number)
         log('Slide {}, {}'.format(slide_number, slide_layer.id))
         if settings.get('saveDrawML'):
             xml = open(os.path.join(self.flatmap.map_directory,
                                     '{}.xml'.format(slide_layer.id)), 'w')
             xml.write(slide.element.xml)
             xml.close()
         slide_layer.process()
         for error in self.errors:
             log.warn(error)
         else:
             self.add_layer(slide_layer)
예제 #4
0
 def __make_zoomed_tiles(self, tile_extractor):
 #=============================================
     mbtiles = MBTiles(self.__database_path, True, True)
     mbtiles.add_metadata(id=self.__id)
     zoom = self.__max_zoom
     log('Tiling zoom level {} for {}'.format(zoom, self.__id))
     progress_bar = ProgressBar(total=len(self.__tile_set),
         unit='tiles', ncols=40,
         bar_format='{l_bar}{bar}| {n_fmt}/{total_fmt}')
     for tile in self.__tile_set:
         tile_image = tile_extractor.get_tile(tile)
         alpha_image = add_alpha(tile_image)
         if not_empty(alpha_image):
             mbtiles.save_tile_as_png(zoom, tile.x, tile.y, alpha_image)
         progress_bar.update(1)
     progress_bar.close()
     self.__make_overview_tiles(mbtiles, zoom, self.__tile_set.start_coords,
                                               self.__tile_set.end_coords)
     mbtiles.close(compress=True)
예제 #5
0
 def __output_geojson(self):
     #==========================
     log('Outputting GeoJson features...')
     for layer in self.__flatmap.layers:
         if layer.exported:
             log('Layer: {}'.format(layer.id))
             geojson_output = GeoJSONOutput(layer, self.__flatmap.area,
                                            self.__map_dir)
             saved_layer = geojson_output.save(
                 layer.features, settings.get('saveGeoJSON', False))
             for (layer_name, filename) in saved_layer.items():
                 self.__geojson_files.append(filename)
                 self.__tippe_inputs.append({
                     'file':
                     filename,
                     'layer':
                     layer_name,
                     'description':
                     '{} -- {}'.format(layer.description, layer_name)
                 })
             self.__flatmap.update_annotations(layer.annotations)
예제 #6
0
    def __make_vector_tiles(self, compressed=True):
        #==============================================
        # Generate Mapbox vector tiles
        if len(self.__tippe_inputs) == 0:
            raise ValueError('No selectable layers found...')

        log('Running tippecanoe...')
        tippe_command = [
            'tippecanoe',
            '--force',
            '--projection=EPSG:4326',
            '--buffer=100',
            '--minimum-zoom={}'.format(self.__zoom[0]),
            '--maximum-zoom={}'.format(self.__zoom[1]),
            '--no-tile-size-limit',
            '--output={}'.format(self.__mbtiles_file),
        ]
        if not compressed:
            tippe_command.append('--no-tile-compression')
        if settings.get('quiet', False):
            tippe_command.append('--quiet')
        tippe_command += list([
            "-L{}".format(json.dumps(input)) for input in self.__tippe_inputs
        ])

        if settings.get('showTippe', False):
            print('  \\\n    '.join(tippe_command))
        subprocess.run(tippe_command)

        # `tippecanoe` uses the bounding box containing all features as the
        # map bounds, which is not the same as the extracted bounds, so update
        # the map's metadata
        tile_db = MBTiles(self.__mbtiles_file)
        tile_db.add_metadata(compressed=compressed)
        tile_db.update_metadata(
            center=','.join([str(x) for x in self.__centre]),
            bounds=','.join([str(x) for x in self.__extent]))
        tile_db.execute("COMMIT")
        tile_db.close()
        self.__upload_files.append('index.mbtiles')
예제 #7
0
    def __init__(self, options):
        # ``silent`` implies not ``verbose``
        if options.get('silent', False):
            options['verbose'] = False

        # Setup logging

        log_file = options.get('logFile')
        if log_file is None:
            log_path = options.get('logPath')
            if log_path is not None:
                log_file = os.path.join(log_path, '{}.log'.format(os.getpid()))
        configure_logging(log_file,
                          verbose=options.get('verbose', False),
                          silent=options.get('silent', False),
                          debug=options.get('debug', False))
        log('Mapmaker {}'.format(__version__))

        # Default base output directory to ``./flatmaps``.
        if 'output' not in options:
            options['output'] = './flatmaps'

        # Check zoom settings are valid
        min_zoom = options.get('minZoom', 2)
        max_zoom = options.get('maxZoom', 10)
        initial_zoom = options.get('initialZoom', 4)
        if min_zoom < 0 or min_zoom > max_zoom:
            raise ValueError(
                'Min zoom must be between 0 and {}'.format(max_zoom))
        if max_zoom < min_zoom or max_zoom > 15:
            raise ValueError(
                'Max zoom must be between {} and 15'.format(min_zoom))
        if initial_zoom < min_zoom or initial_zoom > max_zoom:
            raise ValueError('Initial zoom must be between {} and {}'.format(
                min_zoom, max_zoom))
        self.__zoom = (min_zoom, max_zoom, initial_zoom)

        # Save options into global ``settings`` dict
        settings.update(options)

        # Check we have been given a map source and get our manifest
        if 'source' in options:
            self.__manifest = Manifest(options['source'],
                                       options.get('singleSvg', False))
        else:
            raise ValueError('No source manifest specified')
        self.__id = options.get('id', self.__manifest.id)
        if self.__id is None:
            raise ValueError('No id given for map')
        log('Making map: {}'.format(self.__id))

        # Make sure our output directories exist
        map_base = options.get('output')
        if not os.path.exists(map_base):
            os.makedirs(map_base)
        self.__map_dir = os.path.join(map_base, self.__id)
        if options.get('clean', False):
            shutil.rmtree(self.__map_dir, True)
        if not os.path.exists(self.__map_dir):
            os.makedirs(self.__map_dir)

        # The vector tiles' database that is created by `tippecanoe`
        self.__mbtiles_file = os.path.join(self.__map_dir, 'index.mbtiles')
        self.__geojson_files = []
        self.__tippe_inputs = []

        # Raster tile layers
        self.__raster_layers = []

        # Our source of knowledge, updated with information
        # about maps we've made, held in a global place

        settings['KNOWLEDGE_STORE'] = KnowledgeStore(map_base)

        # The map we are making
        self.__flatmap = FlatMap(self.__manifest, self)
예제 #8
0
    def geometry(self) -> [GeometricShape]:
        """
        Returns:
            A list of geometric objects. This are LineStrings describing paths
            between nodes and possibly additional features (e.g. way markers)
            of the paths.
        """
        display_bezier_points = True  #False     ### To come from settings...
        if self.__path_layout == 'automatic':
            log("Automated pathway layout. Path ID: ", self.__path_id)
            evaluate_settings = self.__sheath.settings()
            # TODO: use evenly-distributed offsets for the final product.
            number_of_neurons = len(evaluate_settings['derivatives'])
            # locations = [0.01 + x*(0.99-0.01)/number_of_neurons for x in range(number_of_neurons)]
            location = 0.5
            geometry = []
            for scaffold, path_id, derivative in zip(
                    evaluate_settings['scaffolds'],
                    evaluate_settings['path_ids'],
                    evaluate_settings['derivatives']):
                scaffold.generate()
                connectivity = Connectivity(path_id, scaffold, derivative,
                                            location)
                auto_beziers = connectivity.get_neuron_line_beziers()
                path = BezierPath.fromSegments(auto_beziers)
                geometry.append(
                    GeometricShape(
                        shapely.geometry.LineString(bezier_sample(path))))
            end_nodes = set(self.__source_nodes)
            end_nodes.update(self.__target_nodes)
            for node in end_nodes:
                for edge in self.__graph.edges(node, data=True):
                    if edge[2].get('type') == 'terminal':
                        line = self.__line_from_edge(edge)
                        if line is not None:
                            geometry.append(GeometricShape(line))
            if display_bezier_points:
                for beziers in self.__sheath.path_beziers.values():
                    for bezier in beziers:
                        bz_pts = tuple([p.x, p.y] for p in bezier.points)
                        for pt in [bz_pts[0], bz_pts[3]]:
                            geometry.append(
                                GeometricShape(GeometricShape.circle(pt), {
                                    'type': 'bezier',
                                    'kind': 'bezier-end'
                                }))
                        for pt in bz_pts[1:3]:
                            geometry.append(
                                GeometricShape(GeometricShape.circle(pt), {
                                    'type': 'bezier',
                                    'kind': 'bezier-control'
                                }))
                        geometry.append(
                            GeometricShape(GeometricShape.line(*bz_pts[0:2]),
                                           {'type': 'bezier'}))
                        geometry.append(
                            GeometricShape(GeometricShape.line(*bz_pts[2:4]),
                                           {'type': 'bezier'}))

            return geometry

        # Fallback is centreline layout
        geometry = []
        for edge in self.__graph.edges.data():
            nerve = edge[2].get('nerve')
            properties = {'nerve': nerve} if nerve is not None else None
            bezier = edge[2].get('geometry')
            if self.__path_layout != 'linear' and bezier is not None:
                geometry.append(
                    GeometricShape(
                        shapely.geometry.LineString(bezier_sample(bezier)),
                        properties))
            else:
                line = self.__line_from_edge(edge)
                if line is not None:
                    geometry.append(GeometricShape(line, properties))
        return geometry
예제 #9
0
    def __save_metadata(self):
        #=========================
        log('Creating index and style files...')
        tile_db = MBTiles(self.__mbtiles_file)

        # Save the URL of the map's manifest
        tile_db.add_metadata(source=self.__manifest.url)

        # What the map models
        if self.__models is not None:
            tile_db.add_metadata(describes=self.__models)
        # Save layer details in metadata
        tile_db.add_metadata(layers=json.dumps(self.__layer_metadata()))
        # Save pathway details in metadata
        tile_db.add_metadata(
            pathways=json.dumps(self.__map_properties.resolved_pathways))
        # Save annotations in metadata
        tile_db.add_metadata(annotations=json.dumps(self.__annotations))
        # Save command used to run mapmaker
        tile_db.add_metadata(created_by=self.__creator)
        # Save the maps creation time
        tile_db.add_metadata(created=datetime.datetime.utcnow().isoformat())
        # Commit updates to the database
        tile_db.execute("COMMIT")

        #*        ## TODO: set ``layer.properties`` for annotations...
        #*        ##update_RDF(options['map_base'], options['map_id'], source, annotations)

        # Get list of all image sources from all layers
        raster_layers = []
        for layer in self.__layer_dict.values():
            raster_layers.extend(layer.raster_layers)

        map_index = {
            'id': self.__id,
            'source': self.__manifest.url,
            'min-zoom': self.__zoom[0],
            'max-zoom': self.__zoom[1],
            'bounds': self.__extent,
            'version': FLATMAP_VERSION,
            'image_layer': len(raster_layers) > 0  ## For compatibility
        }
        if self.__models is not None:
            map_index['describes'] = self.__models
        # Create `index.json` for building a map in the viewer
        with open(os.path.join(self.__map_dir, 'index.json'),
                  'w') as output_file:
            json.dump(map_index, output_file)

        # Create style file
        metadata = tile_db.metadata()
        style_dict = MapStyle.style(raster_layers, metadata, self.__zoom)
        with open(os.path.join(self.__map_dir, 'style.json'),
                  'w') as output_file:
            json.dump(style_dict, output_file)

        # Create TileJSON file
        json_source = tile_json(self.__id, self.__zoom, self.__extent)
        with open(os.path.join(self.__map_dir, 'tilejson.json'),
                  'w') as output_file:
            json.dump(json_source, output_file)

        tile_db.close()
        self.__upload_files.extend(
            ['index.json', 'style.json', 'tilejson.json'])
예제 #10
0
    def __init__(self, options):
        # ``silent`` implies ``quiet``
        if options.get('silent', False):
            options['quiet'] = True

        # Setup logging
        configure_logging(options.get('logFile'),
                          quiet=options.get('quiet', False),
                          silent=options.get('silent', False))

        # Check we have been given a map source
        if 'source' in options:
            self.__manifest = Manifest(options['source'])
        else:
            raise ValueError('No map source specified')

        # Default base output directory to ``./flatmaps``.
        if 'output' not in options:
            options['output'] = './flatmaps'

        # Check zoom settings are valid
        min_zoom = options.get('minZoom', 2)
        max_zoom = options.get('maxZoom', 10)
        initial_zoom = options.get('initialZoom', 4)
        if min_zoom < 0 or min_zoom > max_zoom:
            raise ValueError(
                'Min zoom must be between 0 and {}'.format(max_zoom))
        if max_zoom < min_zoom or max_zoom > 15:
            raise ValueError(
                'Max zoom must be between {} and 15'.format(min_zoom))
        if initial_zoom < min_zoom or initial_zoom > max_zoom:
            raise ValueError('Initial zoom must be between {} and {}'.format(
                min_zoom, max_zoom))
        self.__zoom = (min_zoom, max_zoom, initial_zoom)

        # Save options into global ``settings`` dict
        settings.update(options)

        log('Mapmaker {}'.format(__version__))

        self.__id = options.get('id', self.__manifest.id)
        if self.__id is None:
            raise ValueError('No `id` given for map')
        log('Making map: {}'.format(self.id))

        self.__models = self.__manifest.models

        # Make sure our output directories exist
        map_base = options.get('output')
        if not os.path.exists(map_base):
            os.makedirs(map_base)
        self.__map_dir = os.path.join(map_base, self.__id)
        if options.get('clean', False):
            shutil.rmtree(self.__map_dir, True)
        if not os.path.exists(self.__map_dir):
            os.makedirs(self.__map_dir)

        # The vector tiles' database that is created by `tippecanoe`
        self.__mbtiles_file = os.path.join(self.__map_dir, 'index.mbtiles')

        self.__geojson_files = []
        self.__tippe_inputs = []
        self.__upload_files = []

        # Properties about map features
        self.__map_properties = JsonProperties(self.__manifest)

        self.__layer_dict = OrderedDict()
        self.__visible_layer_count = 0

        self.__annotations = {}
        self.__creator = 'mapmaker'  ## FIX, add version info creator

        self.__map_area = None
        self.__extent = None
        self.__centre = None

        self.__last_feature_id = 0
        self.__class_to_feature = defaultdict(list)
        self.__id_to_feature = {}