Пример #1
0
def jinja_filter_bbox_padded_intersection(bounds,
                                          geometry_col_name,
                                          pad_factor=1.1,
                                          srid=3857):
    padded_bounds = calculate_padded_bounds(pad_factor, bounds)
    return jinja_filter_bbox_intersection(padded_bounds.bounds,
                                          geometry_col_name, srid)
Пример #2
0
    def _parse_row(self, zoom, unpadded_bounds, bbox, source, fid, shape,
                   props, layer_min_zooms):
        # reject any feature which doesn't intersect the given bounds
        if bbox.disjoint(shape):
            return None

        # place for assembing the read row as if from postgres
        read_row = {}
        generate_label_placement = False

        # add names into whichever of the pois, landuse or buildings
        # layers has claimed this feature.
        names = {}
        for k in name_keys(props):
            names[k] = props[k]
        named_layer = self._named_layer(layer_min_zooms)

        for layer_name, min_zoom in layer_min_zooms.items():
            # we need to keep fractional zooms, e.g: 4.999 should appear
            # in tiles at zoom level 4, but not 3. also, tiles at zooms
            # past the max zoom should be clamped to the max zoom.
            tile_zoom = min(self.tile_pyramid.max_z, floor(min_zoom))
            if tile_zoom > zoom:
                continue

            layer_props = layer_properties(
                fid, shape, props, layer_name, zoom, self.osm)
            layer_props['min_zoom'] = min_zoom

            if names and named_layer == layer_name:
                layer_props.update(names)

            read_row['__' + layer_name + '_properties__'] = layer_props

            # if the feature exists in any label placement layer, then we
            # should consider generating a centroid
            label_layers = self.label_placement_layers.get(
                shape_type_lookup(shape), {})
            if layer_name in label_layers:
                generate_label_placement = True

        # nasty hack: in the SQL, we don't query the lines table for
        # boundaries layer features - instead we take the rings (both outer
        # and inner) of the polygon features in the polygons table - which are
        # also called the "boundary" of the polygon. the hack below replicates
        # the process we have in the SQL query.
        #
        # note: we shoe-horn buffered land into boundaries, so we have to
        # disable this special processing for those features.
        if read_row and '__boundaries_properties__' in read_row and \
           read_row['__boundaries_properties__'].get('kind') != 'maritime':
            if shape.geom_type in ('LineString', 'MultiLineString'):
                read_row.pop('__boundaries_properties__')

            elif shape.geom_type in ('Polygon', 'MultiPolygon'):
                # make sure boundary rings are oriented in the correct
                # direction; anti-clockwise for outers and clockwise for
                # inners, which means the interior should be on the left.
                boundaries_shape = _orient(shape).boundary

                # make sure it's only lines, post-intersection. a polygon-line
                # intersection can return points as well as lines. however,
                # these would not only be useless for labelling boundaries, but
                # also trip up any later processing which was expecting only
                # lines.
                clip_shape = _lines_only(boundaries_shape.intersection(bbox))
                read_row['__boundaries_geometry__'] = bytes(clip_shape.wkb)

                # we don't want area on boundaries
                read_row['__boundaries_properties__'].pop('area', None)

        if read_row:
            read_row['__id__'] = fid

            # if this is a water layer feature, then clip to an expanded
            # bounding box to avoid tile-edge artefacts.
            clip_box = bbox
            if layer_name == 'water':
                pad_factor = 1.1
                clip_box = calculate_padded_bounds(
                    pad_factor, unpadded_bounds)
            # don't need to clip if geom is fully within the clipping box
            if box(*shape.bounds).within(clip_box):
                clip_shape = shape
            else:
                clip_shape = clip_box.intersection(shape)
            read_row['__geometry__'] = bytes(clip_shape.wkb)

            if generate_label_placement:
                read_row['__label__'] = bytes(
                    shape.representative_point().wkb)

            if source:
                read_row['__properties__'] = {'source': source.value}

        return read_row
Пример #3
0
    def __call__(self, zoom, unpadded_bounds):
        read_rows = []
        bbox = box(*unpadded_bounds)

        for (fid, shape, props) in self.rows:
            # reject any feature which doesn't intersect the given bounds
            if bbox.disjoint(shape):
                continue

            # copy props so that any updates to it don't affect the original
            # data.
            props = props.copy()

            # TODO: there must be some better way of doing this?
            rels = props.pop('__relations__', [])
            ways = props.pop('__ways__', [])

            # place for assembing the read row as if from postgres
            read_row = {}

            # whether to generate a label placement centroid
            generate_label_placement = False

            # whether to clip to a padded box
            has_water_layer = False

            # tracking which layers claim this feature, as this is important to
            # figure out which layer will be assigned the name.
            claims_feature_at_some_zoom = set()

            for layer_name, info in self.layers.items():
                if not info.allows_shape_type(shape):
                    continue

                source_value = props.get('source')
                source = lookup_source(source_value)

                # this is a bit of a hack to ensure that custom source values,
                # as used in the tests, get passed though the fixture data
                # fetcher intact.
                if source is None and source_value is not None:
                    source = Source(source_value, source_value)

                meta = Metadata(source, ways, rels)
                min_zoom = info.min_zoom_fn(shape, props, fid, meta)

                # reject features which don't match in this layer
                if min_zoom is None:
                    continue

                # make a note that this feature is claimed at some zoom by this
                # layer, which is important for name processing.
                claims_feature_at_some_zoom.add(layer_name)

                # reject anything which isn't in the current zoom range
                # note that this is (zoom+1) because things with a min_zoom of
                # (e.g) 14.999 should still be in the zoom 14 tile.
                #
                # also, if zoom >= 16, we should include all features, even
                # those with min_zoom > zoom.
                if zoom < 16 and (zoom + 1) <= min_zoom:
                    continue

                # UGLY HACK: match the query for "max zoom" for NE places.
                # this removes larger cities at low zooms, and smaller cities
                # as the zoom increases and as the OSM cities start to "fade
                # in".
                if source and source.name == 'ne':
                    pop_max = int(props.get('pop_max', '0'))
                    remove = ((zoom >= 8 and zoom < 10 and pop_max > 50000) or
                              (zoom >= 10 and zoom < 11 and pop_max > 20000) or
                              (zoom >= 11 and pop_max > 5000))
                    if remove:
                        continue

                # if the feature exists in any label placement layer, then we
                # should consider generating a centroid
                label_layers = self.label_placement_layers.get(
                    shape_type_lookup(shape), {})
                if layer_name in label_layers:
                    generate_label_placement = True

                layer_props = layer_properties(
                    fid, shape, props, layer_name, zoom, self.osm)

                if source:
                    layer_props['source'] = source.value

                layer_props['min_zoom'] = min_zoom
                props_name = '__%s_properties__' % layer_name
                read_row[props_name] = layer_props
                if layer_name == 'water':
                    has_water_layer = True

            # if at least one min_zoom / properties match
            if read_row:
                clip_box = bbox
                if has_water_layer:
                    pad_factor = 1.1
                    clip_box = calculate_padded_bounds(
                        pad_factor, unpadded_bounds)
                clip_shape = clip_box.intersection(shape)

                # add back name into whichever of the pois, landuse or
                # buildings layers has claimed this feature.
                names = {}
                for k in name_keys(props):
                    names[k] = props[k]
                if names:
                    for layer_name in ('pois', 'landuse', 'buildings'):
                        if layer_name in claims_feature_at_some_zoom:
                            props_name = '__%s_properties__' % layer_name
                            if props_name in read_row:
                                read_row[props_name].update(names)
                            # break regardless of whether or not we managed to
                            # update the row - sometimes a feature is claimed
                            # in one layer at a min_zoom higher than another
                            # layer's min_zoom. so the feature is visible
                            # before it gets labelled.
                            break

                read_row['__id__'] = fid
                read_row['__geometry__'] = bytes(clip_shape.wkb)
                if generate_label_placement:
                    read_row['__label__'] = bytes(
                        shape.representative_point().wkb)
                read_rows.append(read_row)

        return read_rows
Пример #4
0
    def __call__(self, zoom, unpadded_bounds):
        read_rows = []
        bbox = box(*unpadded_bounds)

        for (fid, shape, props) in self.rows:
            # reject any feature which doesn't intersect the given bounds
            if bbox.disjoint(shape):
                continue

            # copy props so that any updates to it don't affect the original
            # data.
            props = props.copy()

            # TODO: there must be some better way of doing this?
            rels = props.pop('__relations__', [])
            ways = props.pop('__ways__', [])

            # place for assembing the read row as if from postgres
            read_row = {}

            # whether to generate a label placement centroid
            generate_label_placement = False

            # whether to clip to a padded box
            has_water_layer = False

            # tracking which layers claim this feature, as this is important to
            # figure out which layer will be assigned the name.
            claims_feature_at_some_zoom = set()

            for layer_name, info in self.layers.items():
                if not info.allows_shape_type(shape):
                    continue

                source_value = props.get('source')
                source = lookup_source(source_value)

                # this is a bit of a hack to ensure that custom source values,
                # as used in the tests, get passed though the fixture data
                # fetcher intact.
                if source is None and source_value is not None:
                    source = Source(source_value, source_value)

                meta = Metadata(source, ways, rels)
                min_zoom = info.min_zoom_fn(shape, props, fid, meta)

                # reject features which don't match in this layer
                if min_zoom is None:
                    continue

                # make a note that this feature is claimed at some zoom by this
                # layer, which is important for name processing.
                claims_feature_at_some_zoom.add(layer_name)

                # reject anything which isn't in the current zoom range
                # note that this is (zoom+1) because things with a min_zoom of
                # (e.g) 14.999 should still be in the zoom 14 tile.
                #
                # also, if zoom >= 16, we should include all features, even
                # those with min_zoom > zoom.
                if zoom < 16 and (zoom + 1) <= min_zoom:
                    continue

                # UGLY HACK: match the query for "max zoom" for NE places.
                # this removes larger cities at low zooms, and smaller cities
                # as the zoom increases and as the OSM cities start to "fade
                # in".
                if source and source.name == 'ne':
                    pop_max = int(props.get('pop_max', '0'))
                    remove = ((zoom >= 8 and zoom < 10 and pop_max > 50000)
                              or (zoom >= 10 and zoom < 11 and pop_max > 20000)
                              or (zoom >= 11 and pop_max > 5000))
                    if remove:
                        continue

                # if the feature exists in any label placement layer, then we
                # should consider generating a centroid
                label_layers = self.label_placement_layers.get(
                    shape_type_lookup(shape), {})
                if layer_name in label_layers:
                    generate_label_placement = True

                layer_props = layer_properties(fid, shape, props, layer_name,
                                               zoom, self.osm)

                if source:
                    layer_props['source'] = source.value

                layer_props['min_zoom'] = min_zoom
                props_name = '__%s_properties__' % layer_name
                read_row[props_name] = layer_props
                if layer_name == 'water':
                    has_water_layer = True

            # if at least one min_zoom / properties match
            if read_row:
                clip_box = bbox
                if has_water_layer:
                    pad_factor = 1.1
                    clip_box = calculate_padded_bounds(pad_factor,
                                                       unpadded_bounds)
                clip_shape = clip_box.intersection(shape)

                # add back name into whichever of the pois, landuse or
                # buildings layers has claimed this feature.
                names = {}
                for k in name_keys(props):
                    names[k] = props[k]
                if names:
                    for layer_name in ('pois', 'landuse', 'buildings'):
                        if layer_name in claims_feature_at_some_zoom:
                            props_name = '__%s_properties__' % layer_name
                            if props_name in read_row:
                                read_row[props_name].update(names)
                            # break regardless of whether or not we managed to
                            # update the row - sometimes a feature is claimed
                            # in one layer at a min_zoom higher than another
                            # layer's min_zoom. so the feature is visible
                            # before it gets labelled.
                            break

                read_row['__id__'] = fid
                read_row['__geometry__'] = bytes(clip_shape.wkb)
                if generate_label_placement:
                    read_row['__label__'] = bytes(
                        shape.representative_point().wkb)
                read_rows.append(read_row)

        return read_rows
Пример #5
0
def jinja_filter_bbox_padded_intersection(
        bounds, geometry_col_name, pad_factor=1.1, srid=3857):
    padded_bounds = calculate_padded_bounds(pad_factor, bounds)
    return jinja_filter_bbox_intersection(
        padded_bounds.bounds, geometry_col_name, srid)
Пример #6
0
    def _parse_row(self, zoom, unpadded_bounds, bbox, source, fid, shape,
                   props, layer_min_zooms):
        # reject any feature which doesn't intersect the given bounds
        if bbox.disjoint(shape):
            return None

        # place for assembing the read row as if from postgres
        read_row = {}
        generate_label_placement = False

        # add names into whichever of the pois, landuse or buildings
        # layers has claimed this feature.
        names = {}
        for k in name_keys(props):
            names[k] = props[k]
        named_layer = self._named_layer(layer_min_zooms)

        for layer_name, min_zoom in layer_min_zooms.items():
            # we need to keep fractional zooms, e.g: 4.999 should appear
            # in tiles at zoom level 4, but not 3. also, tiles at zooms
            # past the max zoom should be clamped to the max zoom.
            tile_zoom = min(self.tile_pyramid.max_z, floor(min_zoom))
            if tile_zoom > zoom:
                continue

            layer_props = layer_properties(
                fid, shape, props, layer_name, zoom, self.osm)
            layer_props['min_zoom'] = min_zoom

            if names and named_layer == layer_name:
                layer_props.update(names)

            read_row['__' + layer_name + '_properties__'] = layer_props

            # if the feature exists in any label placement layer, then we
            # should consider generating a centroid
            label_layers = self.label_placement_layers.get(
                shape_type_lookup(shape), {})
            if layer_name in label_layers:
                generate_label_placement = True

        # nasty hack: in the SQL, we don't query the lines table for
        # boundaries layer features - instead we take the rings (both outer
        # and inner) of the polygon features in the polygons table - which are
        # also called the "boundary" of the polygon. the hack below replicates
        # the process we have in the SQL query.
        #
        # note: we shoe-horn buffered land into boundaries, so we have to
        # disable this special processing for those features.
        if read_row and '__boundaries_properties__' in read_row and \
           read_row['__boundaries_properties__'].get('kind') != 'maritime':
            if shape.geom_type in ('Polygon', 'MultiPolygon'):
                # make sure boundary rings are oriented in the correct
                # direction; anti-clockwise for outers and clockwise for
                # inners, which means the interior should be on the left.
                boundaries_shape = _orient(shape).boundary

                # make sure it's only lines, post-intersection. a polygon-line
                # intersection can return points as well as lines. however,
                # these would not only be useless for labelling boundaries, but
                # also trip up any later processing which was expecting only
                # lines.
                clip_shape = _lines_only(boundaries_shape.intersection(bbox))
                read_row['__boundaries_geometry__'] = bytes(clip_shape.wkb)

                boundary_props = read_row['__boundaries_properties__']
                # we don't want area on boundaries
                boundary_props.pop('area', None)
                boundary_props.pop('way_area', None)

                # set a flag to indicate that we transformed this from a
                # polygon to a boundary.
                boundary_props['mz_boundary_from_polygon'] = True

        if read_row:
            read_row['__id__'] = fid

            # if this is a water layer feature, then clip to an expanded
            # bounding box to avoid tile-edge artefacts.
            clip_box = bbox
            if layer_name == 'water':
                pad_factor = 1.1
                clip_box = calculate_padded_bounds(
                    pad_factor, unpadded_bounds)
            # don't need to clip if geom is fully within the clipping box
            if box(*shape.bounds).within(clip_box):
                clip_shape = shape
            else:
                clip_shape = clip_box.intersection(shape)
            read_row['__geometry__'] = bytes(clip_shape.wkb)

            if generate_label_placement:
                read_row['__label__'] = bytes(
                    shape.representative_point().wkb)

            if source:
                read_row['__properties__'] = {'source': source.value}

        return read_row
Пример #7
0
    def _parse_row(self, zoom, unpadded_bounds, bbox, source, fid, shape,
                   props, layer_min_zooms):
        # reject any feature which doesn't intersect the given bounds
        if bbox.disjoint(shape):
            return None

        # place for assembing the read row as if from postgres
        read_row = {}
        generate_label_placement = False

        # add names into whichever of the pois, landuse or buildings
        # layers has claimed this feature.
        names = {}
        for k in name_keys(props):
            names[k] = props[k]
        named_layer = self._named_layer(layer_min_zooms)

        for layer_name, min_zoom in layer_min_zooms.items():
            # we need to keep fractional zooms, e.g: 4.999 should appear
            # in tiles at zoom level 4, but not 3. also, tiles at zooms
            # past the max zoom should be clamped to the max zoom.
            tile_zoom = min(self.tile_pyramid.max_z, floor(min_zoom))
            if tile_zoom > zoom:
                continue

            layer_props = layer_properties(
                fid, shape, props, layer_name, zoom, self.osm)
            layer_props['min_zoom'] = min_zoom

            if names and named_layer == layer_name:
                layer_props.update(names)

            read_row['__' + layer_name + '_properties__'] = layer_props

            # if the feature exists in any label placement layer, then we
            # should consider generating a centroid
            label_layers = self.label_placement_layers.get(
                shape_type_lookup(shape), {})
            if layer_name in label_layers:
                generate_label_placement = True

        if read_row:
            read_row['__id__'] = fid

            # if this is a water layer feature, then clip to an expanded
            # bounding box to avoid tile-edge artefacts.
            clip_box = bbox
            if layer_name == 'water':
                pad_factor = 1.1
                clip_box = calculate_padded_bounds(
                    pad_factor, unpadded_bounds)
            # don't need to clip if geom is fully within the clipping box
            if box(*shape.bounds).within(clip_box):
                clip_shape = shape
            else:
                clip_shape = clip_box.intersection(shape)
            read_row['__geometry__'] = bytes(clip_shape.wkb)

            if generate_label_placement:
                read_row['__label__'] = bytes(
                    shape.representative_point().wkb)

            if source:
                read_row['__properties__'] = {'source': source.value}

        return read_row