Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clip buildings to tiles. #1446

Merged
merged 5 commits into from
Dec 14, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/layers.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,8 @@ Label position points may also have `closed` or `historical` kind_detail values

Values for `kind_detail` are sourced from OpenStreetMap's `building` tag for building footprints and from `building:part` tag for building parts.

Note that building geometries, like most geometries in Tilezen tiles, are clipped to the bounds of the tile, even if the building extends beyond the tile. This means that it might be necessary to assemble geometry from several neighbouring tiles to recreate the full building. Some buildings are exceptionally large and span many tiles, so this can be tricky.

#### Building properties (common):

* `name`
Expand Down
72 changes: 72 additions & 0 deletions integration-test/1226-landuse-kind-buildings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
from . import FixtureTest


class LanduseKindBuildings(FixtureTest):
def test_lax_airport_terminals(self):
self.load_fixtures([
# Terminal 2, which had landuse_kind set correctly at z14
'https://www.openstreetmap.org/way/413226245',

# Tom Bradley International Terminal was missing landuse_kind
'https://www.openstreetmap.org/relation/6168071',

# the aerodrome landuse polygon
'https://www.openstreetmap.org/way/131190417',
])

# the tile which contains Terminal 2 should set the landuse_kind
# correctly.
self.assert_has_feature(
14, 2803, 6547, 'buildings',
{'id': 413226245, 'kind': 'building', 'landuse_kind': 'aerodrome'})

# the four tiles containing bits of Tom Bradley International should
# do so also.
for x in (2802, 2803):
for y in (6547, 6548):
self.assert_has_feature(
14, x, y, 'buildings',
{'id': -6168071, 'kind': 'building',
'landuse_kind': 'aerodrome'})

def test_lax_united_airlines_building(self):
self.load_fixtures([
# United Airlines building spans two tiles at z16, and neither(?)
# of them were getting a landuse_kind assigned.
'https://www.openstreetmap.org/relation/6168075',

# the aerodrome landuse polygon
'https://www.openstreetmap.org/way/131190417',
])

for x in (11209, 11210):
self.assert_has_feature(
16, x, 26192, 'buildings',
{'id': -6168075, 'kind': 'building',
'landuse_kind': 'aerodrome'})

def test_building_part(self):
import dsl
from tilequeue.tile import coord_to_bounds
from shapely.geometry import box
from ModestMaps.Core import Coordinate

z, x, y = (16, 0, 0)
bounds = coord_to_bounds(Coordinate(zoom=z, column=x, row=y))
shape = box(*bounds)

self.generate_fixtures(
dsl.way(1, shape,
{'landuse': 'park', 'source': 'openstreetmap.org'}),
dsl.way(2, shape,
{'building': 'yes', 'source': 'openstreetmap.org'}),
dsl.way(3, shape,
{'building:part': 'yes', 'source': 'openstreetmap.org'}),
)

self.assert_has_feature(
z, x, y, 'buildings',
{'id': 2, 'kind': 'building', 'landuse_kind': 'park'})
self.assert_has_feature(
z, x, y, 'buildings',
{'id': 3, 'kind': 'building_part', 'landuse_kind': 'park'})
35 changes: 21 additions & 14 deletions integration-test/197-clip-buildings.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,46 @@
class ClipBuildings(FixtureTest):

def test_high_line(self):
from ModestMaps.Core import Coordinate
from shapely.geometry import box
from shapely.geometry import shape
from tilequeue.tile import coord_to_mercator_bounds

# this is mid way along the High Line in NYC, which is a huge long
# "building". we should be clipping it to the bounds of the tile.
#
# NOTE: we _don't_ clip the fixture, that has to happen in the
# query.
#
# NOTE: https://github.com/tilezen/vector-datasource/issues/1142
# we want to clip all buildings to the bounding box of the tile, so
# that there are no overlaps.
self.load_fixtures([
'http://www.openstreetmap.org/relation/7141751',
])
with self.features_in_tile_layer(16, 19295, 24631, 'buildings') \
as buildings:
# max width and height in mercator meters as the size of the
# above tile
max_w = 611.5
max_h = 611.5
coord = Coordinate(zoom=16, column=19295, row=24631)
with self.features_in_tile_layer(
coord.zoom, coord.column, coord.row, 'buildings') as buildings:
# tile bounds as a box
tile_bounds = coord_to_mercator_bounds(coord)
bbox = box(*tile_bounds)

# need to check that we at least saw the high line
saw_the_high_line = False

for building in buildings:
bounds = shape(building['geometry']).bounds
w = bounds[2] - bounds[0]
h = bounds[3] - bounds[1]
building_bounds = shape(building['geometry']).bounds
building_box = box(*building_bounds)

if building['properties']['id'] == -7141751:
saw_the_high_line = True

self.assertFalse(
w > max_w or h > max_h,
'feature %r is %rx%r, larger than the allowed '
'%rx%r.' % (building['properties']['id'], w, h,
max_w, max_h))
self.assertTrue(
building_box.within(bbox),
'feature %r extends outside of the bounds of the '
'tile (%r not within %r).' %
(building['properties']['id'], building_bounds,
tile_bounds))

self.assertTrue(
saw_the_high_line,
Expand Down
7 changes: 7 additions & 0 deletions integration-test/dsl.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ def point(id, position, tags):
return Feature(id, Point(x, y), tags)


# utility wrapper to generate a Feature from a lat/lon shape.
def way(id, shape, tags):
from shapely.ops import transform
merc_shape = transform(reproject_lnglat_to_mercator, shape)
return Feature(id, merc_shape, tags)


# the fixture code expects "raw" relations as if they come straight from
# osm2pgsql. the structure is a little cumbersome, so this utility function
# constructs it from a more readable function call.
Expand Down