Skip to content

Commit 13a5df0

Browse files
Famlamfrodrigo
authored andcommitted
Add analyser to detect cyclic relations (#1705)
1 parent 2030ec9 commit 13a5df0

File tree

3 files changed

+239
-0
lines changed

3 files changed

+239
-0
lines changed
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
#!/usr/bin/env python
2+
#-*- coding: utf-8 -*-
3+
4+
###########################################################################
5+
## ##
6+
## Copyrights Osmose Project 2022 ##
7+
## ##
8+
## This program is free software: you can redistribute it and/or modify ##
9+
## it under the terms of the GNU General Public License as published by ##
10+
## the Free Software Foundation, either version 3 of the License, or ##
11+
## (at your option) any later version. ##
12+
## ##
13+
## This program is distributed in the hope that it will be useful, ##
14+
## but WITHOUT ANY WARRANTY; without even the implied warranty of ##
15+
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ##
16+
## GNU General Public License for more details. ##
17+
## ##
18+
## You should have received a copy of the GNU General Public License ##
19+
## along with this program. If not, see <http://www.gnu.org/licenses/>. ##
20+
## ##
21+
###########################################################################
22+
23+
from modules.OsmoseTranslation import T_
24+
from .Analyser_Osmosis import Analyser_Osmosis
25+
26+
sql10 = """
27+
WITH RECURSIVE rcte AS (
28+
-- initial 'seed' relation
29+
SELECT DISTINCT ON (relation_id)
30+
relation_id AS start_id,
31+
member_id,
32+
1 as nloops,
33+
CONCAT('r', relation_id) AS path
34+
FROM
35+
relation_members
36+
WHERE
37+
member_type = 'R' AND
38+
relation_locate(relation_id) IS NOT NULL -- can't display relations without way/node members
39+
40+
UNION ALL
41+
42+
-- loop children until no 'relation'-type members or relation back to start
43+
SELECT
44+
rcte.start_id,
45+
child.member_id,
46+
nloops + 1 AS nloops,
47+
CONCAT(rcte.path, ' > r', child.relation_id) AS path
48+
FROM
49+
rcte
50+
JOIN relation_members AS child ON
51+
child.relation_id = rcte.member_id AND
52+
child.member_type = 'R' AND
53+
child.relation_id != rcte.start_id AND
54+
nloops < 10 -- avoid getting stuck in strange constructions
55+
)
56+
SELECT DISTINCT ON (start_id)
57+
start_id,
58+
ST_AsText(relation_locate(start_id)),
59+
path
60+
FROM
61+
rcte
62+
WHERE
63+
start_id=member_id
64+
ORDER BY
65+
start_id,
66+
LENGTH(path) -- to get the shortest path selected by the DISTINCT ON call
67+
"""
68+
69+
class Analyser_Osmosis_Relation_Cyclic(Analyser_Osmosis):
70+
71+
def __init__(self, config, logger = None):
72+
Analyser_Osmosis.__init__(self, config, logger)
73+
self.classs[2] = self.def_class(item = 1160, level = 2, tags = ['relation', 'fix:chair'],
74+
title = T_('Cyclic relation'),
75+
detail = T_(
76+
'''A relation whose members (eventually) refer back to itself is rarely correct.'''))
77+
78+
def analyser_osmosis_common(self):
79+
self.run(sql10, lambda res: {
80+
"class": 2,
81+
"data": [self.relation, self.positionAsText],
82+
"text": {"en": res[2] + " > r" + str(res[0])}
83+
})
84+
85+
86+
87+
###########################################################################
88+
89+
from .Analyser_Osmosis import TestAnalyserOsmosis
90+
91+
class Test(TestAnalyserOsmosis):
92+
@classmethod
93+
def setup_class(cls):
94+
from modules import config
95+
TestAnalyserOsmosis.setup_class()
96+
cls.analyser_conf = cls.load_osm("tests/osmosis_relation_cyclic.osm",
97+
config.dir_tmp + "/tests/osmosis_relation_cyclic.test.xml", {})
98+
99+
def test_classes(self):
100+
with Analyser_Osmosis_Relation_Cyclic(self.analyser_conf, self.logger) as a:
101+
a.analyser()
102+
103+
self.root_err = self.load_errors()
104+
self.check_err(cl="2", elems=[("relation", "10005")])
105+
self.check_err(cl="2", elems=[("relation", "10006")])
106+
self.check_err(cl="2", elems=[("relation", "10007")])
107+
self.check_err(cl="2", elems=[("relation", "10008")])
108+
self.check_err(cl="2", elems=[("relation", "10009")])
109+
self.check_err(cl="2", elems=[("relation", "10010")])
110+
self.check_err(cl="2", elems=[("relation", "10011")])
111+
self.check_err(cl="2", elems=[("relation", "10012")])
112+
self.check_num_err(8)

osmose_config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ def __init__(self, country, polygon_id=None, analyser_options=None, download_url
147147
self.analyser["osmosis_orphan_nodes_cluster"] = "xxx"
148148
self.analyser["osmosis_powerline"] = "xxx"
149149
self.analyser["osmosis_double_tagging"] = "xxx"
150+
self.analyser["osmosis_relation_cyclic"] = "xxx"
150151
self.analyser["osmosis_relation_associatedStreet"] = "xxx"
151152
self.analyser["osmosis_highway_link"] = "xxx"
152153
self.analyser["osmosis_highway_broken_level_continuity"] = "xxx"

tests/osmosis_relation_cyclic.osm

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
<?xml version='1.0' encoding='UTF-8'?>
2+
<osm version='0.6' generator='JOSM'>
3+
<node id='1' timestamp='2014-03-31T22:00:00Z' version='1' lat='51.83216791492' lon='5.84570362807' />
4+
<node id='2' timestamp='2014-03-31T22:00:00Z' version='1' lat='51.83044698127' lon='5.84750025864' />
5+
<node id='3' timestamp='2014-03-31T22:00:00Z' version='1' lat='51.83111315693' lon='5.84947655227' />
6+
<node id='4' timestamp='2014-03-31T22:00:00Z' version='1' lat='51.83089964362' lon='5.8476057774' />
7+
<node id='5' timestamp='2014-03-31T22:00:00Z' version='1' lat='51.83077473584' lon='5.84792018775' />
8+
<node id='6' timestamp='2014-03-31T22:00:00Z' version='1' lat='51.83110782248' lon='5.84814476657' />
9+
<node id='7' timestamp='2014-03-31T22:00:00Z' version='1' lat='51.83198216319' lon='5.84500066308'>
10+
<tag k='highway' v='bus_stop' />
11+
</node>
12+
<node id='8' timestamp='2014-03-31T22:00:00Z' version='1' lat='51.83080249315' lon='5.85000877079'>
13+
<tag k='highway' v='bus_stop' />
14+
</node>
15+
<way id='100' timestamp='2014-03-31T22:00:00Z' version='1'>
16+
<nd ref='1' />
17+
<nd ref='2' />
18+
<tag k='highway' v='residential' />
19+
</way>
20+
<way id='101' timestamp='2014-03-31T22:00:00Z' version='1'>
21+
<nd ref='2' />
22+
<nd ref='3' />
23+
<tag k='highway' v='residential' />
24+
</way>
25+
<way id='102' timestamp='2014-03-31T22:00:00Z' version='1'>
26+
<nd ref='4' />
27+
<nd ref='5' />
28+
<nd ref='6' />
29+
<nd ref='4' />
30+
<tag k='building' v='house' />
31+
</way>
32+
<relation id='10000' timestamp='2014-03-31T22:00:00Z' version='1'>
33+
<member type='way' ref='100' role='from' />
34+
<member type='node' ref='2' role='via' />
35+
<member type='way' ref='101' role='to' />
36+
<tag k='restriction' v='no_left_turn' />
37+
<tag k='type' v='restriction' />
38+
</relation>
39+
<relation id='10001' timestamp='2014-03-31T22:00:00Z' version='1'>
40+
<member type='way' ref='100' role='street' />
41+
<member type='way' ref='101' role='street' />
42+
<member type='way' ref='102' role='house' />
43+
<tag k='name' v='Mystreet' />
44+
<tag k='type' v='associatedStreet' />
45+
</relation>
46+
<relation id='10002' timestamp='2014-03-31T22:00:00Z' version='1'>
47+
<member type='node' ref='7' role='platform' />
48+
<member type='node' ref='8' role='platform' />
49+
<member type='way' ref='100' role='' />
50+
<member type='way' ref='101' role='' />
51+
<tag k='name' v='Bus to destination' />
52+
<tag k='public_transport:version' v='2' />
53+
<tag k='route' v='bus' />
54+
<tag k='type' v='route' />
55+
</relation>
56+
<relation id='10003' timestamp='2014-03-31T22:00:00Z' version='1'>
57+
<member type='relation' ref='10002' role='' />
58+
<tag k='name' v='Bus to destination' />
59+
<tag k='route_master' v='bus' />
60+
<tag k='type' v='route_master' />
61+
</relation>
62+
<relation id='10004' timestamp='2014-03-31T22:00:00Z' version='1'>
63+
<member type='relation' ref='10003' role='' />
64+
<tag k='network' v='Bus network MyTown' />
65+
<tag k='type' v='network' />
66+
</relation>
67+
<relation id='10005' timestamp='2014-03-31T22:00:00Z' version='1'>
68+
<member type='way' ref='100' role='' />
69+
<member type='way' ref='101' role='' />
70+
<member type='relation' ref='10006' role='' />
71+
<member type='relation' ref='10003' role='' />
72+
<tag k='name' v='Bad relation simple 1' />
73+
<tag k='public_transport:version' v='2' />
74+
<tag k='route' v='monorail' />
75+
<tag k='type' v='route' />
76+
</relation>
77+
<relation id='10006' timestamp='2014-03-31T22:00:00Z' version='1'>
78+
<member type='way' ref='101' role='' />
79+
<member type='way' ref='100' role='' />
80+
<member type='relation' ref='10005' role='' />
81+
<tag k='name' v='Bad relation simple 2' />
82+
<tag k='public_transport:version' v='2' />
83+
<tag k='route' v='monorail' />
84+
<tag k='type' v='route' />
85+
</relation>
86+
<relation id='10007' timestamp='2014-03-31T22:00:00Z' version='1'>
87+
<member type='way' ref='100' role='' />
88+
<member type='way' ref='101' role='' />
89+
<member type='relation' ref='10008' role='' />
90+
<member type='relation' ref='10009' role='' />
91+
<tag k='name' v='Bad complicated 1' />
92+
<tag k='type' v='network' />
93+
</relation>
94+
<relation id='10008' timestamp='2014-03-31T22:00:00Z' version='1'>
95+
<member type='way' ref='100' role='' />
96+
<member type='way' ref='101' role='' />
97+
<member type='relation' ref='10007' role='' />
98+
<tag k='name' v='Bad complicated 2' />
99+
<tag k='type' v='network' />
100+
</relation>
101+
<relation id='10009' timestamp='2014-03-31T22:00:00Z' version='1'>
102+
<member type='way' ref='100' role='' />
103+
<member type='way' ref='101' role='' />
104+
<member type='relation' ref='10007' role='' />
105+
<tag k='name' v='Bad complicated 3' />
106+
<tag k='type' v='network' />
107+
</relation>
108+
<relation id='10010' timestamp='2014-03-31T22:00:00Z' version='1'>
109+
<member type='way' ref='100' role='' />
110+
<member type='relation' ref='10011' role='' />
111+
<tag k='name' v='Bad Triple 1' />
112+
<tag k='type' v='network' />
113+
</relation>
114+
<relation id='10011' timestamp='2014-03-31T22:00:00Z' version='1'>
115+
<member type='way' ref='100' role='' />
116+
<member type='relation' ref='10012' role='' />
117+
<tag k='name' v='Bad Triple 2' />
118+
<tag k='type' v='network' />
119+
</relation>
120+
<relation id='10012' timestamp='2014-03-31T22:00:00Z' version='1'>
121+
<member type='way' ref='100' role='' />
122+
<member type='relation' ref='10010' role='' />
123+
<tag k='name' v='Bad Triple 3' />
124+
<tag k='type' v='network' />
125+
</relation>
126+
</osm>

0 commit comments

Comments
 (0)