Skip to content

Commit

Permalink
Merge pull request #1446 from tilezen/zerebubuth/1142-buildings-clipping
Browse files Browse the repository at this point in the history
Clip buildings to tiles.
  • Loading branch information
zerebubuth authored Dec 14, 2017
2 parents a9ae281 + ff2a866 commit 2bed573
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 14 deletions.
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

0 comments on commit 2bed573

Please sign in to comment.