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

Add 'shield_text' property on roads #966

Merged
merged 10 commits into from
Aug 12, 2016
31 changes: 20 additions & 11 deletions data/functions.sql
Original file line number Diff line number Diff line change
Expand Up @@ -83,24 +83,33 @@ BEGIN
END;
$$ LANGUAGE plpgsql IMMUTABLE;

-- mz_get_rel_network returns a network tag, or NULL, for a
-- given way ID.
-- mz_get_rel_networks returns a list of triples of route type,
-- network and ref tags, or NULL, for a given way ID.
--
-- it does this by joining onto the relations slim table, so it
-- won't work if you dropped the slim tables, or didn't use slim
-- mode in osm2pgsql.
--
CREATE OR REPLACE FUNCTION mz_get_rel_network(
CREATE OR REPLACE FUNCTION mz_get_rel_networks(
way_id bigint)
RETURNS text AS $$
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it easy to have this return back an array, hstore, or json with the values instead of a delimited string? This is just meant for the python transform to consume and process right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, yes! Good point! I had forgotten that was JSON and thought it had to be stringly to fit in an hstore. Fixed in 861ac6d.

BEGIN
RETURN mz_first_dedup(ARRAY(
SELECT mz_rel_get_tag(tags, 'network')
FROM planet_osm_rels
WHERE parts && ARRAY[way_id]
AND parts[way_off+1:rel_off] && ARRAY[way_id]));
END;
$$ LANGUAGE plpgsql STABLE;
SELECT
string_agg(unnested, '|')
FROM (
SELECT
unnest(tags) AS unnested
FROM (
SELECT
hstore(tags)->ARRAY['route','network','ref'] AS tags
FROM
planet_osm_rels
WHERE
parts && ARRAY[way_id] AND
parts[way_off+1:rel_off] && ARRAY[way_id] AND
hstore(tags) ?& ARRAY['route','network','ref']
) inner1
) inner2;
$$ LANGUAGE sql STABLE;

-- adds the prefix onto every key in an hstore value
CREATE OR REPLACE FUNCTION mz_hstore_add_prefix(
Expand Down
2 changes: 2 additions & 0 deletions data/migrations/v1.0.0-cleanup.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- drop no longer used single-network version of this function
DROP FUNCTION mz_get_rel_network(bigint);
5 changes: 3 additions & 2 deletions docs/layers.md
Original file line number Diff line number Diff line change
Expand Up @@ -837,7 +837,7 @@ To improve performance, some road segments are merged at low and mid-zooms. To f
* `source`: `openstreetmap` or `naturalearthdata.com`
* `kind`: one of High Road's values for `highway`, `major_road`, `minor_road`, `rail`, `path`, `ferry`, `piste`, `aerialway`, `aeroway`, `racetrack`, `portage_way` if `whitewater=portage_way`; or Natural Earth's `featurecla` value. You'll want to look at other tags like `highway` and `railway` for raw OpenStreetMap values. At low zooms, Natural Earth `featurecla` kinds of `Road` and `Ferry` are used. Look to `type` for more fidelity.
* `landuse_kind`: See description above, values match values in the `landuse` layer.
* `ref`: Used for road shields. Related, see `symbol` for pistes.
* `ref`: Commonly-used reference for roads, for example "I 90" for Interstate 90. To use with shields, see the common optional properties `network` and `shield_text`. Related, see `symbol` for pistes.
* `sort_key`: a suggestion for which order to draw features. The value is an integer where smaller numbers suggest that features should be "behind" features with larger numbers. At zooms >= 15, the `sort_key` is adjusted to realistically model bridge, tunnel, and layer ordering.

#### Road properties (common optional):
Expand All @@ -857,13 +857,14 @@ To improve performance, some road segments are merged at low and mid-zooms. To f
* `is_tunnel`: `true` if the road is part of a tunnel. The property will not be present if the road is not part of a tunnel.
* `leisure`: See kind list below.
* `man_made`: See kind list below.
* `network`: eg: `US:I` for the United States Interstate network, useful for shields and road selections.
* `network`: eg: `US:I` for the United States Interstate network, useful for shields and road selections. This only contains _road_ network types. Please see `bicycle_network` and `walking_network` for bicycle and walking networks, respectively.
* `oneway_bicycle`: `oneway:bicycle` tag from feature
* `oneway`: `yes` or `no`
* `piste_type`: See kind list below.
* `railway`: the original OSM railway tag value
* `segregated`: Set to `true` when a path allows both pedestrian and bicycle traffic, but when pedestrian traffic is segregated from bicycle traffic.
* `service`: See value list below, provided for `railway` and `highway=service` roads.
* `shield_text`: Contains text to display on a shield. For example, I 90 would have a `network` of `US:I` and a `shield_text` of `90`. The `ref`, `I 90`, is less useful for shield display without further processing.
* `type`: Natural Earth roads and ferry
* `walking_network`: Present if the feature is part of a hiking network. If so, the value will be one of `iwn` for International Walking Network, `nwn` for National Walking Network, `rwn` for Regional Walking Network, `lwn` for Local Walking Network.
* `kind_detail`: normalized values describing the kind value, see below.
Expand Down
19 changes: 19 additions & 0 deletions integration-test/192-shield-text-ref.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# US 101, "James Lick Freeway"
# http://www.openstreetmap.org/way/27183379
# http://www.openstreetmap.org/relation/108619
assert_has_feature(
16, 10484, 25334, 'roads',
{ 'kind': 'highway', 'network': 'US:US', 'id': 27183379,
'shield_text': '101' })

# I-77, I-81, US-11 & US-52 all in one road West Virginia.
#
# http://www.openstreetmap.org/way/51388984
# http://www.openstreetmap.org/relation/2309416
# http://www.openstreetmap.org/relation/2301037
# http://www.openstreetmap.org/relation/2297359
# http://www.openstreetmap.org/relation/1027748
assert_has_feature(
16, 18022, 25522, 'roads',
{ 'kind': 'highway', 'network': 'US:I', 'id': 51388984,
'shield_text': '77' })
6 changes: 3 additions & 3 deletions integration-test/647-cycle-route.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@
# http://www.openstreetmap.org/relation/32386
assert_has_feature(
16, 10487, 25327, 'roads',
{ 'kind': 'major_road', 'cycleway': 'lane', 'network': 'lcn', 'bicycle_network': 'lcn' })
{ 'kind': 'major_road', 'cycleway': 'lane', 'bicycle_network': 'lcn' })

# Way: King Street (8920394) http://www.openstreetmap.org/way/8920394
assert_has_feature(
16, 10487, 25329, 'roads',
{ 'kind': 'major_road', 'cycleway_left': 'lane', 'network': 'lcn', 'bicycle_network': 'lcn'})
{ 'kind': 'major_road', 'cycleway_left': 'lane', 'bicycle_network': 'lcn'})

# Way: King Street (397270776) http://www.openstreetmap.org/way/397270776
assert_has_feature(
16, 10487, 25329, 'roads',
{ 'kind': 'major_road', 'cycleway_right': 'lane', 'network': 'lcn', 'bicycle_network': 'lcn'})
{ 'kind': 'major_road', 'cycleway_right': 'lane', 'bicycle_network': 'lcn'})

# Way: Clara-Immerwahr-Straße (287167007) http://www.openstreetmap.org/way/287167007
assert_has_feature(
Expand Down
5 changes: 3 additions & 2 deletions queries.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ layers:
- vectordatasource.transform.normalize_aerialways
- vectordatasource.transform.normalize_cycleway
- vectordatasource.transform.add_is_bicycle_related
- vectordatasource.transform.choose_most_important_network
- vectordatasource.transform.road_trim_properties
- vectordatasource.transform.remove_feature_id
- vectordatasource.transform.tags_remove
Expand Down Expand Up @@ -567,7 +568,7 @@ post_process:
source_layer: roads
start_zoom: 0
end_zoom: 14
properties: [name, ref, network]
properties: [name, ref, network, shield_text]
where: >-
(kind == 'rail' and zoom < 15) or
(kind == 'minor_road' and zoom < 14) or
Expand All @@ -580,7 +581,7 @@ post_process:
source_layer: roads
start_zoom: 7
end_zoom: 10
properties: [name, network]
properties: [name, network, shield_text]
where: >-
kind == 'major_road'
# this is a patch to get rid of name, but keep ref & network, for highways
Expand Down
65 changes: 65 additions & 0 deletions vectordatasource/transform.py
Original file line number Diff line number Diff line change
Expand Up @@ -3676,3 +3676,68 @@ def normalize_operator_values(shape, properties, fid, zoom):
return (shape, properties, fid)

return (shape, properties, fid)


def network_importance(route_type, network, ref):
"""
Returns an integer representing the numeric importance of the network,
where lower numbers are more important.

This is to handle roads which are part of many networks, and ensuring
that the most important one is displayed. For example, in the USA many
roads can be part of both interstate (US:I) and "US" (US:US) highways,
and possibly state ones as well (e.g: US:NY:xxx). In addition, there
are international conventions around the use of "CC:national" and
"CC:regional:*" where "CC" is an ISO 2-letter country code.

Here we treat national-level roads as more important than regional or
lower, and assume that the deeper the network is in the hierarchy, the
less important the road. Roads with lower "ref" numbers are considered
more important than higher "ref" numbers, if they are part of the same
network.
"""

if network == 'US:I' or ':national' in network:
network_code = 1
elif network == 'US:US' or ':regional' in network:
network_code = 2
else:
network_code = len(network.split(':')) + 3

try:
ref = max(int(ref), 0)
except ValueError:
ref = 0

return network_code * 10000 + min(ref, 9999)


def choose_most_important_network(shape, properties, fid, zoom):
"""
Use the `network_importance` function to select any road networks from
`mz_networks` and take the most important one.
"""

networks = properties.pop('mz_networks', None)

if networks is not None:
networks = networks.split('|')

# take the list and make triples out of it
itr = iter(networks)
triples = zip(itr, itr, itr)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! ⚡


def is_road_network(t):
return t[0] == 'road'

triples = filter(is_road_network, triples)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see you were in a functional mood ;) If this function is just used in this one location, list comprehension instead?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that would be more readable... but what would it look like? Can I do [t for t in triples where t[0] == 'road']?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, fixed in 99ea802. And 🍄 level up for me, learned new Python syntax 😉

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoops, yea I missed that it's if instead of where in your comment, but you got it.


if len(triples) > 0:
def network_key(t):
return network_importance(*t)

route_type, network, ref = sorted(triples, key=network_key)[0]
properties['network'] = network
properties['shield_text'] = ref

return (shape, properties, fid)
2 changes: 1 addition & 1 deletion yaml/roads.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ global:
CASE WHEN tags->'crossing' <> 'no' THEN tags->'crossing' END
sidewalk: {col: tags->sidewalk}
- &osm_network_from_relation
network: {expr: "mz_get_rel_network(osm_id)"}
mz_networks: {expr: "mz_get_rel_networks(osm_id)"}
- &osm_network_from_tags
network: {col: tags->network}
- &osm_piste_properties
Expand Down