1 """ Steps for setting up a test database for osm2pgsql import.
3 Note that osm2pgsql features need a database and therefore need
7 from nose.tools import *
16 logger = logging.getLogger(__name__)
19 def osm2pgsql_setup_test(scenario):
22 @step(u'the osm nodes:')
23 def osm2pgsql_import_nodes(step):
24 """ Define a list of OSM nodes to be imported, given as a table.
25 Each line describes one node with all its attributes.
26 'id' is mendatory, all other fields are filled with random values
27 when not given. If 'tags' is missing an empty tag list is assumed.
28 For updates, a mandatory 'action' column needs to contain 'A' (add),
29 'M' (modify), 'D' (delete).
31 for line in step.hashes:
32 node = { 'type' : 'N', 'version' : '1', 'timestamp': "2012-05-01T15:06:20Z",
33 'changeset' : "11470653", 'uid' : "122294", 'user' : "foo"
36 node['id'] = int(node['id'])
37 if 'geometry' in node:
38 lat, lon = node['geometry'].split(' ')
39 node['lat'] = float(lat)
40 node['lon'] = float(lon)
42 node['lon'] = random.random()*360 - 180
43 node['lat'] = random.random()*180 - 90
45 node['tags'] = world.make_hash(line['tags'])
49 world.osm2pgsql.append(node)
52 @step(u'the osm ways:')
53 def osm2pgsql_import_ways(step):
54 """ Define a list of OSM ways to be imported.
56 for line in step.hashes:
57 way = { 'type' : 'W', 'version' : '1', 'timestamp': "2012-05-01T15:06:20Z",
58 'changeset' : "11470653", 'uid' : "122294", 'user' : "foo"
62 way['id'] = int(way['id'])
64 way['tags'] = world.make_hash(line['tags'])
67 way['nodes'] = way['nodes'].strip().split()
69 world.osm2pgsql.append(way)
71 membertype = { 'N' : 'node', 'W' : 'way', 'R' : 'relation' }
73 @step(u'the osm relations:')
74 def osm2pgsql_import_rels(step):
75 """ Define a list of OSM relation to be imported.
77 for line in step.hashes:
78 rel = { 'type' : 'R', 'version' : '1', 'timestamp': "2012-05-01T15:06:20Z",
79 'changeset' : "11470653", 'uid' : "122294", 'user' : "foo"
83 rel['id'] = int(rel['id'])
85 rel['tags'] = world.make_hash(line['tags'])
89 if rel['members'].strip():
90 for mem in line['members'].split(','):
91 memparts = mem.strip().split(':', 2)
92 memid = memparts[0].upper()
93 members.append((membertype[memid[0]],
95 memparts[1] if len(memparts) == 2 else ''
97 rel['members'] = members
99 world.osm2pgsql.append(rel)
103 def _sort_xml_entries(x, y):
104 if x['type'] == y['type']:
105 return cmp(x['id'], y['id'])
107 return cmp('NWR'.find(x['type']), 'NWR'.find(y['type']))
109 def write_osm_obj(fd, obj):
110 if obj['type'] == 'N':
111 fd.write('<node id="%(id)d" lat="%(lat).8f" lon="%(lon).8f" version="%(version)s" timestamp="%(timestamp)s" changeset="%(changeset)s" uid="%(uid)s" user="%(user)s"'% obj)
112 if obj['tags'] is None:
116 for k,v in obj['tags'].iteritems():
117 fd.write(' <tag k="%s" v="%s"/>\n' % (k, v))
118 fd.write('</node>\n')
119 elif obj['type'] == 'W':
120 fd.write('<way id="%(id)d" version="%(version)s" changeset="%(changeset)s" timestamp="%(timestamp)s" user="%(user)s" uid="%(uid)s">\n' % obj)
121 for nd in obj['nodes']:
122 fd.write('<nd ref="%s" />\n' % (nd,))
123 for k,v in obj['tags'].iteritems():
124 fd.write(' <tag k="%s" v="%s"/>\n' % (k, v))
126 elif obj['type'] == 'R':
127 fd.write('<relation id="%(id)d" version="%(version)s" changeset="%(changeset)s" timestamp="%(timestamp)s" user="%(user)s" uid="%(uid)s">\n' % obj)
128 for mem in obj['members']:
129 fd.write(' <member type="%s" ref="%s" role="%s"/>\n' % mem)
130 for k,v in obj['tags'].iteritems():
131 fd.write(' <tag k="%s" v="%s"/>\n' % (k, v))
132 fd.write('</relation>\n')
134 @step(u'loading osm data')
135 def osm2pgsql_load_place(step):
136 """Imports the previously defined OSM data into a fresh copy of a
137 Nominatim test database.
140 world.osm2pgsql.sort(cmp=_sort_xml_entries)
142 # create a OSM file in /tmp
143 with tempfile.NamedTemporaryFile(dir='/tmp', delete=False) as fd:
145 fd.write("<?xml version='1.0' encoding='UTF-8'?>\n")
146 fd.write('<osm version="0.6" generator="test-nominatim" timestamp="2014-08-26T20:22:02Z">\n')
147 fd.write('\t<bounds minlat="43.72335" minlon="7.409205" maxlat="43.75169" maxlon="7.448637"/>\n')
149 for obj in world.osm2pgsql:
150 write_osm_obj(fd, obj)
154 logger.debug( "Filename: %s" % fname)
156 cmd = [os.path.join(world.config.source_dir, 'utils', 'setup.php')]
157 cmd.extend(['--osm-file', fname, '--import-data'])
158 proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
159 (outp, outerr) = proc.communicate()
160 assert (proc.returncode == 0), "OSM data import failed:\n%s\n%s\n" % (outp, outerr)
162 ### reintroduce the triggers/indexes we've lost by having osm2pgsql set up place again
163 cur = world.conn.cursor()
164 cur.execute("""CREATE TRIGGER place_before_delete BEFORE DELETE ON place
165 FOR EACH ROW EXECUTE PROCEDURE place_delete()""")
166 cur.execute("""CREATE TRIGGER place_before_insert BEFORE INSERT ON place
167 FOR EACH ROW EXECUTE PROCEDURE place_insert()""")
168 cur.execute("""CREATE UNIQUE INDEX idx_place_osm_unique on place using btree(osm_id,osm_type,class,type)""")
175 actiontypes = { 'C' : 'create', 'M' : 'modify', 'D' : 'delete' }
177 @step(u'updating osm data')
178 def osm2pgsql_update_place(step):
179 """Creates an osc file from the previously defined data and imports it
182 world.run_nominatim_script('setup', 'create-functions', 'create-partition-functions')
183 cur = world.conn.cursor()
184 cur.execute("""insert into placex (osm_type, osm_id, class, type, name, admin_level,
185 housenumber, street, addr_place, isin, postcode, country_code, extratags,
186 geometry) select * from place""")
188 world.run_nominatim_script('setup', 'index', 'index-noanalyse')
189 world.run_nominatim_script('setup', 'create-functions', 'create-partition-functions', 'enable-diff-updates')
191 with tempfile.NamedTemporaryFile(dir='/tmp', delete=False) as fd:
193 fd.write("<?xml version='1.0' encoding='UTF-8'?>\n")
194 fd.write('<osmChange version="0.6" generator="Osmosis 0.43.1">\n')
196 for obj in world.osm2pgsql:
197 fd.write('<%s>\n' % (actiontypes[obj['action']], ))
198 write_osm_obj(fd, obj)
199 fd.write('</%s>\n' % (actiontypes[obj['action']], ))
201 fd.write('</osmChange>\n')
203 logger.debug( "Filename: %s" % fname)
205 cmd = [os.path.join(world.config.source_dir, 'utils', 'update.php')]
206 cmd.extend(['--import-diff', fname])
207 proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
208 (outp, outerr) = proc.communicate()
209 assert (proc.returncode == 0), "OSM data update failed:\n%s\n%s\n" % (outp, outerr)