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', suffix='.osm', 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','--osm2pgsql-cache', '300'])
158 proc = subprocess.Popen(cmd, cwd=world.config.source_dir,
159 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
160 (outp, outerr) = proc.communicate()
161 assert (proc.returncode == 0), "OSM data import failed:\n%s\n%s\n" % (outp, outerr)
163 ### reintroduce the triggers/indexes we've lost by having osm2pgsql set up place again
164 cur = world.conn.cursor()
165 cur.execute("""CREATE TRIGGER place_before_delete BEFORE DELETE ON place
166 FOR EACH ROW EXECUTE PROCEDURE place_delete()""")
167 cur.execute("""CREATE TRIGGER place_before_insert BEFORE INSERT ON place
168 FOR EACH ROW EXECUTE PROCEDURE place_insert()""")
169 cur.execute("""CREATE UNIQUE INDEX idx_place_osm_unique on place using btree(osm_id,osm_type,class,type)""")
176 actiontypes = { 'C' : 'create', 'M' : 'modify', 'D' : 'delete' }
178 @step(u'updating osm data')
179 def osm2pgsql_update_place(step):
180 """Creates an osc file from the previously defined data and imports it
183 world.run_nominatim_script('setup', 'create-functions', 'create-partition-functions')
184 cur = world.conn.cursor()
185 cur.execute("""insert into placex (osm_type, osm_id, class, type, name, admin_level,
186 housenumber, street, addr_place, isin, postcode, country_code, extratags,
187 geometry) select * from place""")
189 world.run_nominatim_script('setup', 'index', 'index-noanalyse')
190 world.run_nominatim_script('setup', 'create-functions', 'create-partition-functions', 'enable-diff-updates')
192 with tempfile.NamedTemporaryFile(dir='/tmp', suffix='.osc', delete=False) as fd:
194 fd.write("<?xml version='1.0' encoding='UTF-8'?>\n")
195 fd.write('<osmChange version="0.6" generator="Osmosis 0.43.1">\n')
197 for obj in world.osm2pgsql:
198 fd.write('<%s>\n' % (actiontypes[obj['action']], ))
199 write_osm_obj(fd, obj)
200 fd.write('</%s>\n' % (actiontypes[obj['action']], ))
202 fd.write('</osmChange>\n')
204 logger.debug( "Filename: %s" % fname)
206 cmd = [os.path.join(world.config.source_dir, 'utils', 'update.php')]
207 cmd.extend(['--import-diff', fname])
208 proc = subprocess.Popen(cmd, cwd=world.config.source_dir,
209 stdout=subprocess.PIPE, stderr=subprocess.PIPE)
210 (outp, outerr) = proc.communicate()
211 assert (proc.returncode == 0), "OSM data update failed:\n%s\n%s\n" % (outp, outerr)