1 # SPDX-License-Identifier: GPL-2.0-only
 
   3 # This file is part of Nominatim. (https://nominatim.org)
 
   5 # Copyright (C) 2022 by the Nominatim developer community.
 
   6 # For a full list of authors see the git log.
 
  10 from pathlib import Path
 
  12 from nominatim.tools.exec_utils import run_osm2pgsql
 
  13 from nominatim.tools.replication import run_osm2pgsql_updates
 
  15 from geometry_alias import ALIASES
 
  17 def get_osm2pgsql_options(nominatim_env, fname, append):
 
  18     return dict(import_file=fname,
 
  19                 osm2pgsql=str(nominatim_env.build_dir / 'osm2pgsql' / 'osm2pgsql'),
 
  21                 osm2pgsql_style=str(nominatim_env.get_test_config().get_import_style_file()),
 
  22                 osm2pgsql_style_path=nominatim_env.get_test_config().config_dir,
 
  24                 dsn=nominatim_env.get_libpq_dsn(),
 
  26                 tablespaces=dict(slim_data='', slim_index='',
 
  27                                  main_data='', main_index=''),
 
  32 def write_opl_file(opl, grid):
 
  33     """ Create a temporary OSM file from OPL and return the file name. It is
 
  34         the responsibility of the caller to delete the file again.
 
  36         Node with missing coordinates, can retrieve their coordinates from
 
  37         a supplied grid. Failing that a random coordinate is assigned.
 
  39     with tempfile.NamedTemporaryFile(suffix='.opl', delete=False) as fd:
 
  40         for line in opl.splitlines():
 
  41             if line.startswith('n') and line.find(' x') < 0:
 
  42                 coord = grid.grid_node(int(line[1:].split(' ')[0]))
 
  44                     coord = (random.random() * 360 - 180,
 
  45                              random.random() * 180 - 90)
 
  46                 line += " x%f y%f" % coord
 
  47             fd.write(line.encode('utf-8'))
 
  52 @given(u'the ([0-9.]+ )?grid(?: with origin (?P<origin>.*))?')
 
  53 def define_node_grid(context, grid_step, origin):
 
  55     Define a grid of node positions.
 
  56     Use a table to define the grid. The nodes must be integer ids. Optionally
 
  57     you can give the grid distance. The default is 0.00001 degrees.
 
  59     if grid_step is not None:
 
  60         grid_step = float(grid_step.strip())
 
  67             coords = origin.split(',')
 
  69                 raise RuntimeError('Grid origin expects orgin with x,y coordinates.')
 
  70             origin = (float(coords[0]), float(coords[1]))
 
  71         elif origin in ALIASES:
 
  72             origin = ALIASES[origin]
 
  74             raise RuntimeError('Grid origin must be either coordinate or alias.')
 
  78     context.osm.set_grid([context.table.headings] + [list(h) for h in context.table],
 
  82 @when(u'loading osm data')
 
  83 def load_osm_file(context):
 
  85     Load the given data into a freshly created test data using osm2pgsql.
 
  86     No further indexing is done.
 
  88     The data is expected as attached text in OPL format.
 
  90     # create an OSM file and import it
 
  91     fname = write_opl_file(context.text, context.osm)
 
  93         run_osm2pgsql(get_osm2pgsql_options(context.nominatim, fname, append=False))
 
  97     ### reintroduce the triggers/indexes we've lost by having osm2pgsql set up place again
 
  98     cur = context.db.cursor()
 
  99     cur.execute("""CREATE TRIGGER place_before_delete BEFORE DELETE ON place
 
 100                     FOR EACH ROW EXECUTE PROCEDURE place_delete()""")
 
 101     cur.execute("""CREATE TRIGGER place_before_insert BEFORE INSERT ON place
 
 102                    FOR EACH ROW EXECUTE PROCEDURE place_insert()""")
 
 103     cur.execute("""CREATE UNIQUE INDEX idx_place_osm_unique on place using btree(osm_id,osm_type,class,type)""")
 
 107 @when(u'updating osm data')
 
 108 def update_from_osm_file(context):
 
 110     Update a database previously populated with 'loading osm data'.
 
 111     Needs to run indexing on the existing data first to yield the correct result.
 
 113     The data is expected as attached text in OPL format.
 
 115     context.nominatim.copy_from_place(context.db)
 
 116     context.nominatim.run_nominatim('index')
 
 117     context.nominatim.run_nominatim('refresh', '--functions')
 
 119     # create an OSM file and import it
 
 120     fname = write_opl_file(context.text, context.osm)
 
 122         run_osm2pgsql_updates(context.db,
 
 123                               get_osm2pgsql_options(context.nominatim, fname, append=True))
 
 128 def index_database(context):
 
 130     Run the Nominatim indexing step. This will process data previously
 
 131     loaded with 'updating osm data'
 
 133     context.nominatim.run_nominatim('index')