files: ./Nominatim/coverage*.xml
directory: ./
name: codecov-umbrella
- fail_ci_if_error: true
+ fail_ci_if_error: false
path_to_write_report: ./coverage/codecov_report.txt
verbose: true
nominatim special-phrases --import-from-csv <csv file>
```
-Note that the 2 previous import commands will update the phrases from your database.
+Note that the two previous import commands will update the phrases from your database.
This means that if you import some phrases from a csv file, only the phrases
present in the csv file will be kept into the database. All other phrases will
be removed.
### Configuration setup in `.env`
-The Nominatim server can be customized via an `.env` configuration file in the
+The Nominatim server can be customized via an `.env` configuration file in the
project directory. This is a file in [dotenv](https://github.com/theskumar/python-dotenv)
format which looks the same as variable settings in a standard shell environment.
You can also set the same configuration via environment variables. All
variables.
There are lots of configuration settings you can tweak. Have a look
-at `settings/env.default` for a full list. Most should have a sensible default.
+at `Nominatim/settings/env.default` for a full list. Most should have a sensible default.
#### Flatnode files
[Geofabrik](https://download.geofabrik.de).
Download the data to import. Then issue the following command
-from the **build directory** to start the import:
+from the **project directory** to start the import:
```sh
nominatim import --osm-file <data file> 2>&1 | tee setup.log
```
+The **project directory** is the one that you have set up at the beginning.
+See [creating the project directory](Import#creating-the-project-directory).
+
### Notes on full planet imports
Even on a perfectly configured machine
./utils/setup.php --setup-website
```
+### Update SQL code
+
+To update the SQL code to the leatest version run:
+
+```
+./utils/setup.php --create-functions --enable-diff-updates --create-partition-functions
+```
+
## 3.4.0 -> 3.5.0
### New Wikipedia/Wikidata importance tables
}
)));
}
+
+ public static function joinIdsByTableMinRank($aResults, $iTable, $iMinAddressRank)
+ {
+ return join(',', array_keys(array_filter(
+ $aResults,
+ function ($aValue) use ($iTable, $iMinAddressRank) {
+ return $aValue->iTable == $iTable && $aValue->iAddressRank >= $iMinAddressRank;
+ }
+ )));
+ }
+
+ public static function joinIdsByTableMaxRank($aResults, $iTable, $iMaxAddressRank)
+ {
+ return join(',', array_keys(array_filter(
+ $aResults,
+ function ($aValue) use ($iTable, $iMaxAddressRank) {
+ return $aValue->iTable == $iTable && $aValue->iAddressRank <= $iMaxAddressRank;
+ }
+ )));
+ }
+
public static function sqlHouseNumberTable($aResults, $iTable)
{
$sHousenumbers = '';
// Now search for housenumber, if housenumber provided. Can be zero.
if (($this->sHouseNumber || $this->sHouseNumber === '0') && !empty($aResults)) {
+ $aHnResults = $this->queryHouseNumber($oDB, $aResults);
+
// Downgrade the rank of the street results, they are missing
- // the housenumber.
+ // the housenumber. Also drop POI places (rank 30) here, they
+ // cannot be a parent place and therefore must not be shown
+ // as a result for a search with a missing housenumber.
foreach ($aResults as $oRes) {
- if ($oRes->iAddressRank >= 26) {
- $oRes->iResultRank++;
- } else {
- $oRes->iResultRank += 2;
+ if ($oRes->iAddressRank < 28) {
+ if ($oRes->iAddressRank >= 26) {
+ $oRes->iResultRank++;
+ } else {
+ $oRes->iResultRank += 2;
+ }
+ $aHnResults[$oRes->iId] = $oRes;
}
}
- $aHnResults = $this->queryHouseNumber($oDB, $aResults);
-
- if (!empty($aHnResults)) {
- foreach ($aHnResults as $oRes) {
- $aResults[$oRes->iId] = $oRes;
- }
- }
+ $aResults = $aHnResults;
}
// finally get POIs if requested
private function queryHouseNumber(&$oDB, $aRoadPlaceIDs)
{
$aResults = array();
- $sPlaceIDs = Result::joinIdsByTable($aRoadPlaceIDs, Result::TABLE_PLACEX);
+ $sRoadPlaceIDs = Result::joinIdsByTableMaxRank(
+ $aRoadPlaceIDs,
+ Result::TABLE_PLACEX,
+ 27
+ );
+ $sPOIPlaceIDs = Result::joinIdsByTableMinRank(
+ $aRoadPlaceIDs,
+ Result::TABLE_PLACEX,
+ 30
+ );
- if (!$sPlaceIDs) {
+ $aIDCondition = array();
+ if ($sRoadPlaceIDs) {
+ $aIDCondition[] = 'parent_place_id in ('.$sRoadPlaceIDs.')';
+ }
+ if ($sPOIPlaceIDs) {
+ $aIDCondition[] = 'place_id in ('.$sPOIPlaceIDs.')';
+ }
+
+ if (empty($aIDCondition)) {
return $aResults;
}
$sHouseNumberRegex = '\\\\m'.$this->sHouseNumber.'\\\\M';
- $sSQL = 'SELECT place_id FROM placex ';
- $sSQL .= 'WHERE parent_place_id in ('.$sPlaceIDs.')';
- $sSQL .= " AND housenumber ~* E'".$sHouseNumberRegex."'";
+ $sSQL = 'SELECT place_id FROM placex WHERE';
+ $sSQL .= " housenumber ~* E'".$sHouseNumberRegex."'";
+ $sSQL .= ' AND ('.join(' OR ', $aIDCondition).')';
$sSQL .= $this->oContext->excludeSQL(' AND place_id');
Debug::printSQL($sSQL);
$bIsIntHouseNumber= (bool) preg_match('/[0-9]+/', $this->sHouseNumber);
$iHousenumber = intval($this->sHouseNumber);
- if ($bIsIntHouseNumber && empty($aResults)) {
+ if ($bIsIntHouseNumber && $sRoadPlaceIDs && empty($aResults)) {
// if nothing found, search in the interpolation line table
$sSQL = 'SELECT distinct place_id FROM location_property_osmline';
$sSQL .= ' WHERE startnumber is not NULL';
- $sSQL .= ' AND parent_place_id in ('.$sPlaceIDs.') AND (';
+ $sSQL .= ' AND parent_place_id in ('.$sRoadPlaceIDs.') AND (';
if ($iHousenumber % 2 == 0) {
// If housenumber is even, look for housenumber in streets
// with interpolationtype even or all.
}
// If nothing found then search in Tiger data (location_property_tiger)
- if (CONST_Use_US_Tiger_Data && $bIsIntHouseNumber && empty($aResults)) {
+ if (CONST_Use_US_Tiger_Data && $sRoadPlaceIDs && $bIsIntHouseNumber && empty($aResults)) {
$sSQL = 'SELECT place_id FROM location_property_tiger';
- $sSQL .= ' WHERE parent_place_id in ('.$sPlaceIDs.') and (';
+ $sSQL .= ' WHERE parent_place_id in ('.$sRoadPlaceIDs.') and (';
if ($iHousenumber % 2 == 0) {
$sSQL .= "interpolationtype='even'";
} else {
+++ /dev/null
-<?php
-@define('CONST_LibDir', dirname(dirname(__FILE__)));
-
-require_once(CONST_LibDir.'/init-cmd.php');
-require_once(CONST_LibDir.'/ParameterParser.php');
-ini_set('memory_limit', '800M');
-
-$aCMDOptions
-= array(
- 'Query database from command line. Returns search result as JSON.',
- array('help', 'h', 0, 1, 0, 0, false, 'Show Help'),
- array('quiet', 'q', 0, 1, 0, 0, 'bool', 'Quiet output'),
- array('verbose', 'v', 0, 1, 0, 0, 'bool', 'Verbose output'),
-
- array('search', '', 0, 1, 1, 1, 'string', 'Search for given term or coordinate'),
- array('country', '', 0, 1, 1, 1, 'string', 'Structured search: country'),
- array('state', '', 0, 1, 1, 1, 'string', 'Structured search: state'),
- array('county', '', 0, 1, 1, 1, 'string', 'Structured search: county'),
- array('city', '', 0, 1, 1, 1, 'string', 'Structured search: city'),
- array('street', '', 0, 1, 1, 1, 'string', 'Structured search: street'),
- array('amenity', '', 0, 1, 1, 1, 'string', 'Structured search: amenity'),
- array('postalcode', '', 0, 1, 1, 1, 'string', 'Structured search: postal code'),
-
- array('accept-language', '', 0, 1, 1, 1, 'string', 'Preferred language order for showing search results'),
- array('bounded', '', 0, 1, 0, 0, 'bool', 'Restrict results to given viewbox'),
- array('nodedupe', '', 0, 1, 0, 0, 'bool', 'Do not remove duplicate results'),
- array('limit', '', 0, 1, 1, 1, 'int', 'Maximum number of results returned (default: 10)'),
- array('exclude_place_ids', '', 0, 1, 1, 1, 'string', 'Comma-separated list of place ids to exclude from results'),
- array('featureType', '', 0, 1, 1, 1, 'string', 'Restrict results to certain features (country, state,city,settlement)'),
- array('countrycodes', '', 0, 1, 1, 1, 'string', 'Comma-separated list of countries to restrict search to'),
- array('viewbox', '', 0, 1, 1, 1, 'string', 'Prefer results in given view box'),
-
- array('project-dir', '', 0, 1, 1, 1, 'realpath', 'Base directory of the Nominatim installation (default: .)'),
- );
-getCmdOpt($_SERVER['argv'], $aCMDOptions, $aCMDResult, true, true);
-
-loadSettings($aCMDResult['project-dir'] ?? getcwd());
-
-@define('CONST_Database_DSN', getSetting('DATABASE_DSN'));
-@define('CONST_Default_Language', getSetting('DEFAULT_LANGUAGE', false));
-@define('CONST_Log_DB', getSettingBool('LOG_DB'));
-@define('CONST_Log_File', getSetting('LOG_FILE', false));
-@define('CONST_NoAccessControl', getSettingBool('CORS_NOACCESSCONTROL'));
-@define('CONST_Places_Max_ID_count', getSetting('LOOKUP_MAX_COUNT'));
-@define('CONST_PolygonOutput_MaximumTypes', getSetting('POLYGON_OUTPUT_MAX_TYPES'));
-@define('CONST_Search_BatchMode', getSettingBool('SEARCH_BATCH_MODE'));
-@define('CONST_Search_NameOnlySearchFrequencyThreshold', getSetting('SEARCH_NAME_ONLY_THRESHOLD'));
-@define('CONST_Use_US_Tiger_Data', getSettingBool('USE_US_TIGER_DATA'));
-@define('CONST_MapIcon_URL', getSetting('MAPICON_URL', false));
-@define('CONST_TokenizerDir', CONST_InstallDir.'/tokenizer');
-
-require_once(CONST_LibDir.'/Geocode.php');
-
-$oDB = new Nominatim\DB;
-$oDB->connect();
-
-if (isset($aCMDResult['nodedupe'])) $aCMDResult['dedupe'] = 'false';
-
-$oParams = new Nominatim\ParameterParser($aCMDResult);
-
-$aSearchParams = array(
- 'search',
- 'amenity',
- 'street',
- 'city',
- 'county',
- 'state',
- 'country',
- 'postalcode'
- );
-
-if (!$oParams->hasSetAny($aSearchParams)) {
- showUsage($aCMDOptions, true);
- return 1;
-}
-
-$oGeocode = new Nominatim\Geocode($oDB);
-
-$oGeocode->setLanguagePreference($oParams->getPreferredLanguages(false));
-$oGeocode->loadParamArray($oParams);
-
-if ($oParams->getBool('search')) {
- $oGeocode->setQuery($aCMDResult['search']);
-} else {
- $oGeocode->setQueryFromParams($oParams);
-}
-
-$aSearchResults = $oGeocode->lookup();
-
-echo json_encode($aSearchResults, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)."\n";
-- Get the existing place_id
select * from placex where osm_type = NEW.osm_type and osm_id = NEW.osm_id and class = NEW.class and type = NEW.type INTO existingplacex;
+ -- Pure postcodes are never queried from placex so we don't add them.
+ -- location_postcodes is filled from the place table directly.
+ IF NEW.class = 'place' AND NEW.type = 'postcode' THEN
+ -- Remove old placex entry.
+ DELETE FROM placex where osm_type = NEW.osm_type and osm_id = NEW.osm_id;
+ RETURN NEW;
+ END IF;
+
-- Handle a place changing type by removing the old data
-- My generated 'place' types are causing havok because they overlap with real keys
-- TODO: move them to their own special purpose key/class to avoid collisions
where osm_type = NEW.osm_type and osm_id = NEW.osm_id and class = NEW.class and type = NEW.type;
- IF NEW.class in ('place','boundary') AND NEW.type in ('postcode','postal_code') THEN
+ IF NEW.class = 'boundary' AND NEW.type = 'postal_code' THEN
IF NEW.address is NULL OR NOT NEW.address ? 'postcode' THEN
-- postcode was deleted, no longer retain in placex
DELETE FROM placex where place_id = existingplacex.place_id;
if args.postcodes:
- LOG.warning("Update postcodes centroid")
- tokenizer = self._get_tokenizer(args.config)
- postcodes.update_postcodes(args.config.get_libpq_dsn(),
- args.project_dir, tokenizer)
- indexer = Indexer(args.config.get_libpq_dsn(), tokenizer,
- args.threads or 1)
- indexer.index_postcodes()
+ if postcodes.can_compute(args.config.get_libpq_dsn()):
+ LOG.warning("Update postcodes centroid")
+ tokenizer = self._get_tokenizer(args.config)
+ postcodes.update_postcodes(args.config.get_libpq_dsn(),
+ args.project_dir, tokenizer)
+ indexer = Indexer(args.config.get_libpq_dsn(), tokenizer,
+ args.threads or 1)
+ indexer.index_postcodes()
+ else:
+ LOG.error("The place table doesn\'t exist. " \
+ "Postcode updates on a frozen database is not possible.")
if args.word_counts:
LOG.warning('Recompute frequency of full-word search terms')
if state is not replication.UpdateState.NO_CHANGES:
status.log_status(conn, start, 'import')
batchdate, _, _ = status.get_status(conn)
+ conn.commit()
if state is not replication.UpdateState.NO_CHANGES and args.do_index:
index_start = dt.datetime.now(dt.timezone.utc)
with connect(args.config.get_libpq_dsn()) as conn:
status.set_indexed(conn, True)
status.log_status(conn, index_start, 'index')
+ conn.commit()
else:
index_start = None
@staticmethod
def run(args): # pylint: disable=too-many-statements
- from ..tools import database_import
- from ..tools import refresh
+ from ..tools import database_import, refresh, postcodes, freeze
from ..indexer.indexer import Indexer
- from ..tools import postcodes
from ..tokenizer import factory as tokenizer_factory
if args.osm_file and not Path(args.osm_file).is_file():
LOG.warning('Create search index for default country names.')
database_import.create_country_names(conn, tokenizer,
args.config.LANGUAGES)
+ conn.commit()
+ if args.no_updates:
+ freeze.drop_update_tables(conn)
tokenizer.finalize_import(args.config)
+
webdir = args.project_dir / 'website'
LOG.warning('Setup website at %s', webdir)
with connect(args.config.get_libpq_dsn()) as conn:
"""
# First, find the node with the highest ID in the database
with conn.cursor() as cur:
- osmid = cur.scalar("SELECT max(osm_id) FROM place WHERE osm_type='N'")
+ if conn.table_exists('place'):
+ osmid = cur.scalar("SELECT max(osm_id) FROM place WHERE osm_type='N'")
+ else:
+ osmid = cur.scalar("SELECT max(osm_id) FROM placex WHERE osm_type='N'")
if osmid is None:
LOG.fatal("No data found in the database.")
cur,
""" INSERT INTO word (word_id, word_token, word, class, type,
search_name_count, operator)
- (SELECT nextval('seq_word'), make_standard_name(name), name,
+ (SELECT nextval('seq_word'), ' ' || make_standard_name(name), name,
class, type, 0,
CASE WHEN op in ('in', 'near') THEN op ELSE null END
FROM (VALUES %s) as v(name, class, type, op))""",
cur.execute(
"""INSERT INTO word (word_id, word_token, country_code)
(SELECT nextval('seq_word'), lookup_token, %s
- FROM (SELECT ' ' || make_standard_name(n) as lookup_token
+ FROM (SELECT DISTINCT ' ' || make_standard_name(n) as lookup_token
FROM unnest(%s)n) y
WHERE NOT EXISTS(SELECT * FROM word
WHERE word_token = lookup_token and country_code = %s))
conn.perform("""INSERT INTO placex ({0})
SELECT {0} FROM place
WHERE osm_id % {1} = {2}
- AND NOT (class='place' and type='houses')
+ AND NOT (class='place' and (type='houses' or type='postcode'))
AND ST_IsValid(geometry)
""".format(_COPY_COLUMNS, place_threads, imod))
sel.register(conn, selectors.EVENT_READ, conn)
# Recompute the list of valid postcodes from placex.
with conn.cursor(name="placex_postcodes") as cur:
- cur.execute("""SELECT country_code, pc, ST_X(centroid), ST_Y(centroid)
- FROM (
- SELECT country_code,
- token_normalized_postcode(address->'postcode') as pc,
- ST_Centroid(ST_Collect(ST_Centroid(geometry))) as centroid
- FROM placex
- WHERE address ? 'postcode' and geometry IS NOT null
- and country_code is not null
- GROUP BY country_code, pc) xx
- WHERE pc is not null
- ORDER BY country_code, pc""")
+ cur.execute("""
+ SELECT cc as country_code, pc, ST_X(centroid), ST_Y(centroid)
+ FROM (SELECT
+ COALESCE(plx.country_code, get_country_code(ST_Centroid(pl.geometry))) as cc,
+ token_normalized_postcode(pl.address->'postcode') as pc,
+ ST_Centroid(ST_Collect(COALESCE(plx.centroid, ST_Centroid(pl.geometry)))) as centroid
+ FROM place AS pl LEFT OUTER JOIN placex AS plx ON pl.osm_id = plx.osm_id AND pl.osm_type = plx.osm_type
+ WHERE pl.address ? 'postcode' AND pl.geometry IS NOT null
+ GROUP BY cc, pc) xx
+ WHERE pc IS NOT null AND cc IS NOT null
+ ORDER BY country_code, pc""")
collector = None
conn.commit()
analyzer.update_postcodes_from_db()
+
+def can_compute(dsn):
+ """
+ Check that the place table exists so that
+ postcodes can be computed.
+ """
+ with connect(dsn) as conn:
+ return conn.table_exists('place')
Then place_addressline doesn't contain
| object | address |
| N1 | N2 |
- When searching for "Square"
+ When sending search query "Square"
Then results contain
- | osm_type | osm_id | name |
- | N | 1 | Square, East Farm |
+ | osm | display_name |
+ | N1 | Square, East Farm |
Scenario: given two place nodes, the closer one wins for the address
Given the grid
And place_addressline doesn't contain
| object | address |
| W1 | R1 |
- When searching for "Bolder"
+ When sending search query "Bolder"
Then results contain
- | osm_type | osm_id | name |
- | N | 1 | Bolder, Wonderway, Left |
+ | osm | display_name |
+ | N1 | Bolder, Wonderway, Left |
Scenario: addr:* tags do not produce addresslines when the parent has the address part
Given the grid
And place_addressline doesn't contain
| object | address |
| N1 | R1 |
- When searching for "Bolder"
+ When sending search query "Bolder"
Then results contain
- | osm_type | osm_id | name |
- | N | 1 | Bolder, Wonderway, Outer |
+ | osm | display_name |
+ | N1 | Bolder, Wonderway, Outer |
Scenario: addr:* tags on outside do not produce addresslines when the parent has the address part
Given the grid
And place_addressline doesn't contain
| object | address |
| N1 | R1 |
- When searching for "Bolder"
+ When sending search query "Bolder"
Then results contain
- | osm_type | osm_id | name |
- | N | 1 | Bolder, Wonderway, Left |
+ | osm | display_name |
+ | N1 | Bolder, Wonderway, Left |
Scenario: POIs can correct address parts on the fly
Given the grid
| object | address |
| N1 | R1 |
| N2 | R2 |
- When searching for "Bolder"
+ When sending search query "Bolder"
Then results contain
- | osm_type | osm_id | name |
- | N | 1 | Bolder, Wonderway, Left |
- When searching for "Leftside"
+ | osm | display_name |
+ | N1 | Bolder, Wonderway, Left |
+ When sending search query "Leftside"
Then results contain
- | osm_type | osm_id | name |
- | N | 2 | Leftside, Wonderway, Right |
+ | osm | display_name |
+ | N2 | Leftside, Wonderway, Right |
| osm | class | type | name | geometry |
| N1 | place | town | Wenig | country:de |
When importing
- When searching for "Wenig, Loudou"
+ When sending search query "Wenig, Loudou"
Then results contain
- | osm_type | osm_id | name |
- | N | 1 | Wenig, Deutschland |
- When searching for "Wenig"
+ | osm | display_name |
+ | N1 | Wenig, Deutschland |
+ When sending search query "Wenig"
| accept-language |
| xy,en |
Then results contain
- | osm_type | osm_id | name |
- | N | 1 | Wenig, Loudou |
+ | osm | display_name |
+ | N1 | Wenig, Loudou |
Scenario: OSM country relations outside expected boundaries are ignored
Given the places
| osm | class | type | admin | name+name:xy | country | geometry |
| osm | class | type | name | geometry |
| N1 | place | town | Wenig | country:de |
When importing
- When searching for "Wenig"
+ When sending search query "Wenig"
| accept-language |
| xy,en |
Then results contain
- | osm_type | osm_id | name |
- | N | 1 | Wenig, Germany |
+ | osm | display_name |
+ | N1 | Wenig, Germany |
Scenario: Pre-defined country names are used
Given the places
| osm | class | type | name | geometry |
| N1 | place | town | Ingb | country:ch |
When importing
- And searching for "Ingb"
+ And sending search query "Ingb"
| accept-language |
| en,de |
Then results contain
- | osm_type | osm_id | name |
- | N | 1 | Ingb, Switzerland |
+ | osm | display_name |
+ | N1 | Ingb, Switzerland |
Then W11 expands to interpolation
| parent_place_id | start | end |
| W3 | 12 | 16 |
- When searching for "16 Cloud Street"
+ When sending search query "16 Cloud Street"
Then results contain
| ID | osm_type | osm_id |
| 0 | N | 4 |
- When searching for "14 Cloud Street"
+ When sending search query "14 Cloud Street"
Then results contain
| ID | osm_type | osm_id |
| 0 | W | 11 |
Then W11 expands to interpolation
| parent_place_id | start | end |
| W3 | 12 | 16 |
- When searching for "16 Cloud Street"
+ When sending search query "16 Cloud Street"
Then results contain
| ID | osm_type | osm_id |
| 0 | N | 4 |
- When searching for "14 Cloud Street"
+ When sending search query "14 Cloud Street"
Then results contain
| ID | osm_type | osm_id |
| 0 | W | 11 |
| W2 | R13 |
| R13 | - |
| R23 | - |
- When searching for "rhein"
+ When sending search query "rhein"
Then results contain
| osm_type |
| R |
| object | linked_place_id |
| W1 | - |
| R1 | - |
- When searching for "rhein"
+ When sending search query "rhein"
Then results contain
| ID | osm_type |
| 0 | R |
| object | linked_place_id |
| W1 | - |
| W2 | R1 |
- When searching for "rhein2"
+ When sending search query "rhein2"
Then results contain
| osm_type |
| W |
And placex contains
| object | rank_address |
| R13 | 16 |
- When searching for ""
+ When sending search query ""
| city |
| Berlin |
Then results contain
| ID | osm_type | osm_id |
| 0 | R | 13 |
- When searching for ""
+ When sending search query ""
| state |
| Berlin |
Then results contain
And placex contains
| object | rank_address |
| R13 | 8 |
- When searching for ""
+ When sending search query ""
| state |
| Berlin |
Then results contain
| ID | osm_type | osm_id |
| 0 | R | 13 |
- When searching for ""
+ When sending search query ""
| city |
| Berlin |
Then results contain
| object | parent_place_id |
| N1 | W1 |
| N2 | W1 |
- When searching for "4 galoo"
+ When sending search query "4 galoo"
Then results contain
- | ID | osm_type | osm_id | langaddress |
+ | ID | osm_type | osm_id | display_name |
| 0 | N | 1 | 4, galoo, 12345 |
- When searching for "5 galoo"
+ When sending search query "5 galoo"
Then results contain
- | ID | osm_type | osm_id | langaddress |
+ | ID | osm_type | osm_id | display_name |
| 0 | N | 2 | 5, galoo, 99999 |
Scenario: Address without tags, closest street
| country | postcode | geometry |
| gb | EH4 7EA | country:gb |
| gb | E4 7EA | country:gb |
- When searching for "EH4 7EA"
+ When sending search query "EH4 7EA"
Then results contain
- | type | placename |
+ | type | display_name |
| postcode | EH4 7EA |
- When searching for "E4 7EA"
+ When sending search query "E4 7EA"
Then results contain
- | type | placename |
+ | type | display_name |
| postcode | E4 7EA |
Then search_name contains
| object | nameaddress_vector |
| N1 | #Rose Street, Walltown |
- When searching for "23 Rose Street, Walltown"
+ When sending search query "23 Rose Street, Walltown"
Then results contain
- | osm_type | osm_id | name |
- | N | 1 | 23, Rose Street |
- When searching for "Walltown, Rose Street 23"
+ | osm | display_name |
+ | N1 | 23, Rose Street |
+ When sending search query "Walltown, Rose Street 23"
Then results contain
- | osm_type | osm_id | name |
- | N | 1 | 23, Rose Street |
- When searching for "Rose Street 23, Walltown"
+ | osm | display_name |
+ | N1 | 23, Rose Street |
+ When sending search query "Rose Street 23, Walltown"
Then results contain
- | osm_type | osm_id | name |
- | N | 1 | 23, Rose Street |
+ | osm | display_name |
+ | N1 | 23, Rose Street |
Scenario: Searching for unknown addr: tags also works for multiple words
Given the scene roads-with-pois
Then search_name contains
| object | nameaddress_vector |
| N1 | #Rose Street, rose, Little, Big, Town |
- When searching for "23 Rose Street, Little Big Town"
+ When sending search query "23 Rose Street, Little Big Town"
Then results contain
- | osm_type | osm_id | name |
- | N | 1 | 23, Rose Street |
- When searching for "Rose Street 23, Little Big Town"
+ | osm | display_name |
+ | N1 | 23, Rose Street |
+ When sending search query "Rose Street 23, Little Big Town"
Then results contain
- | osm_type | osm_id | name |
- | N | 1 | 23, Rose Street |
- When searching for "Little big Town, Rose Street 23"
+ | osm | display_name |
+ | N1 | 23, Rose Street |
+ When sending search query "Little big Town, Rose Street 23"
Then results contain
- | osm_type | osm_id | name |
- | N | 1 | 23, Rose Street |
+ | osm | display_name |
+ | N1 | 23, Rose Street |
Scenario: Unnamed POI has no search entry when it has known addr: tags
Given the scene roads-with-pois
| W1 | highway | residential | Rose Street | Walltown | :w-north |
When importing
Then search_name has no entry for N1
- When searching for "23 Rose Street, Walltown"
+ When sending search query "23 Rose Street, Walltown"
Then results contain
- | osm_type | osm_id | name |
- | N | 1 | 23, Rose Street |
+ | osm | display_name |
+ | N1 | 23, Rose Street |
Scenario: Unnamed POI must have a house number to get a search entry
Given the scene roads-with-pois
Then search_name contains
| object | name_vector | nameaddress_vector |
| N1 | #Walltown | Strange, Town |
- When searching for "23 Rose Street"
+ When sending search query "23 Rose Street"
Then exactly 1 results are returned
And results contain
- | osm_type | osm_id | name |
- | W | 1 | Rose Street, Strange Town |
- When searching for "23 Walltown, Strange Town"
+ | osm | display_name |
+ | W1 | Rose Street, Strange Town |
+ When sending search query "23 Walltown, Strange Town"
Then results contain
- | osm_type | osm_id | name |
- | N | 1 | 23, Walltown, Strange Town |
- When searching for "Walltown 23, Strange Town"
+ | osm | display_name |
+ | N1 | 23, Walltown, Strange Town |
+ When sending search query "Walltown 23, Strange Town"
Then results contain
- | osm_type | osm_id | name |
- | N | 1 | 23, Walltown, Strange Town |
- When searching for "Strange Town, Walltown 23"
+ | osm | display_name |
+ | N1 | 23, Walltown, Strange Town |
+ When sending search query "Strange Town, Walltown 23"
Then results contain
- | osm_type | osm_id | name |
- | N | 1 | 23, Walltown, Strange Town |
+ | osm | display_name |
+ | N1 | 23, Walltown, Strange Town |
Scenario: Named POIs can be searched by housenumber when unknown addr:place is present
Given the scene roads-with-pois
Then search_name contains
| object | name_vector | nameaddress_vector |
| N1 | #Walltown, #Blue house | Walltown, Strange, Town |
- When searching for "23 Walltown, Strange Town"
+ When sending search query "23 Walltown, Strange Town"
Then results contain
- | osm_type | osm_id | name |
- | N | 1 | Blue house, 23, Walltown, Strange Town |
- When searching for "Walltown 23, Strange Town"
+ | osm | display_name |
+ | N1 | Blue house, 23, Walltown, Strange Town |
+ When sending search query "Walltown 23, Strange Town"
Then results contain
- | osm_type | osm_id | name |
- | N | 1 | Blue house, 23, Walltown, Strange Town |
- When searching for "Strange Town, Walltown 23"
+ | osm | display_name |
+ | N1 | Blue house, 23, Walltown, Strange Town |
+ When sending search query "Strange Town, Walltown 23"
Then results contain
- | osm_type | osm_id | name |
- | N | 1 | Blue house, 23, Walltown, Strange Town |
- When searching for "Strange Town, Walltown 23, Blue house"
+ | osm | display_name |
+ | N1 | Blue house, 23, Walltown, Strange Town |
+ When sending search query "Strange Town, Walltown 23, Blue house"
Then results contain
- | osm_type | osm_id | name |
- | N | 1 | Blue house, 23, Walltown, Strange Town |
- When searching for "Strange Town, Walltown, Blue house"
+ | osm | display_name |
+ | N1 | Blue house, 23, Walltown, Strange Town |
+ When sending search query "Strange Town, Walltown, Blue house"
Then results contain
- | osm_type | osm_id | name |
- | N | 1 | Blue house, 23, Walltown, Strange Town |
+ | osm | display_name |
+ | N1 | Blue house, 23, Walltown, Strange Town |
Scenario: Named POIs can be found when unknown multi-word addr:place is present
Given the scene roads-with-pois
Then search_name contains
| object | name_vector | nameaddress_vector |
| N1 | #Moon sun, #Blue house | Moon, Sun, Strange, Town |
- When searching for "23 Moon Sun, Strange Town"
+ When sending search query "23 Moon Sun, Strange Town"
Then results contain
- | osm_type | osm_id | name |
- | N | 1 | Blue house, 23, Moon sun, Strange Town |
- When searching for "Blue house, Moon Sun, Strange Town"
+ | osm | display_name |
+ | N1 | Blue house, 23, Moon sun, Strange Town |
+ When sending search query "Blue house, Moon Sun, Strange Town"
Then results contain
- | osm_type | osm_id | name |
- | N | 1 | Blue house, 23, Moon sun, Strange Town |
+ | osm | display_name |
+ | N1 | Blue house, 23, Moon sun, Strange Town |
Scenario: Unnamed POIs doesn't inherit parent name when addr:place is present only in parent address
Given the scene roads-with-pois
Then search_name contains
| object | name_vector | nameaddress_vector |
| N1 | #Walltown | Strange, Town |
- When searching for "23 Rose Street, Walltown"
+ When sending search query "23 Rose Street, Walltown"
Then exactly 1 result is returned
And results contain
- | osm_type | osm_id | name |
- | W | 1 | Rose Street, Strange Town |
- When searching for "23 Walltown"
+ | osm | display_name |
+ | W1 | Rose Street, Strange Town |
+ When sending search query "23 Walltown"
Then exactly 1 result is returned
And results contain
- | osm_type | osm_id | name |
- | N | 1 | 23, Walltown, Strange Town |
+ | osm | display_name |
+ | N1 | 23, Walltown, Strange Town |
Scenario: Unnamed POIs does inherit parent name when unknown addr:place and addr:street is present
Given the scene roads-with-pois
| W1 | highway | residential | Rose Street | :w-north |
When importing
Then search_name has no entry for N1
- When searching for "23 Rose Street"
+ When sending search query "23 Rose Street"
Then results contain
- | osm_type | osm_id | name |
- | N | 1 | 23, Rose Street |
- When searching for "23 Lily Street"
+ | osm | display_name |
+ | N1 | 23, Rose Street |
+ When sending search query "23 Lily Street"
Then exactly 0 results are returned
Scenario: An unknown addr:street is ignored
| W1 | highway | residential | Rose Street | :w-north |
When importing
Then search_name has no entry for N1
- When searching for "23 Rose Street"
+ When sending search query "23 Rose Street"
Then results contain
- | osm_type | osm_id | name |
- | N | 1 | 23, Rose Street |
- When searching for "23 Lily Street"
+ | osm | display_name |
+ | N1 | 23, Rose Street |
+ When sending search query "23 Lily Street"
Then exactly 0 results are returned
Scenario: Named POIs get unknown address tags added in the search_name table
Then search_name contains
| object | name_vector | nameaddress_vector |
| N1 | #Green Moss | #Rose Street, Walltown |
- When searching for "Green Moss, Rose Street, Walltown"
+ When sending search query "Green Moss, Rose Street, Walltown"
Then results contain
- | osm_type | osm_id | name |
- | N | 1 | Green Moss, 26, Rose Street |
- When searching for "Green Moss, 26, Rose Street, Walltown"
+ | osm | display_name |
+ | N1 | Green Moss, 26, Rose Street |
+ When sending search query "Green Moss, 26, Rose Street, Walltown"
Then results contain
- | osm_type | osm_id | name |
- | N | 1 | Green Moss, 26, Rose Street |
- When searching for "26, Rose Street, Walltown"
+ | osm | display_name |
+ | N1 | Green Moss, 26, Rose Street |
+ When sending search query "26, Rose Street, Walltown"
Then results contain
- | osm_type | osm_id | name |
- | N | 1 | Green Moss, 26, Rose Street |
- When searching for "Rose Street 26, Walltown"
+ | osm | display_name |
+ | N1 | Green Moss, 26, Rose Street |
+ When sending search query "Rose Street 26, Walltown"
Then results contain
- | osm_type | osm_id | name |
- | N | 1 | Green Moss, 26, Rose Street |
- When searching for "Walltown, Rose Street 26"
+ | osm | display_name |
+ | N1 | Green Moss, 26, Rose Street |
+ When sending search query "Walltown, Rose Street 26"
Then results contain
- | osm_type | osm_id | name |
- | N | 1 | Green Moss, 26, Rose Street |
+ | osm | display_name |
+ | N1 | Green Moss, 26, Rose Street |
Scenario: Named POI doesn't inherit parent name when addr:place is present only in parent address
Given the scene roads-with-pois
Then search_name contains
| object | name_vector | nameaddress_vector |
| N1 | #Green Moss | Walltown |
- When searching for "Green Moss, Rose Street, Walltown"
+ When sending search query "Green Moss, Rose Street, Walltown"
Then exactly 0 result is returned
- When searching for "Green Moss, Walltown"
+ When sending search query "Green Moss, Walltown"
Then results contain
- | osm_type | osm_id | name |
- | N | 1 | Green Moss, Walltown, Strange Town |
+ | osm | display_name |
+ | N1 | Green Moss, Walltown, Strange Town |
Scenario: Named POIs inherit address from parent
Given the scene roads-with-pois
Then placex contains
| object | class | type | name+name |
| N1 | place | locality | FooBar |
- When searching for "FooBar"
+ When sending search query "FooBar"
Then results contain
- | ID | osm_type | osm_id |
- | 0 | N | 1 |
- When searching for "foobar"
+ | ID | osm |
+ | 0 | N1 |
+ When sending search query "foobar"
Then results contain
- | ID | osm_type | osm_id |
- | 0 | N | 1 |
- When searching for "fOObar"
+ | ID | osm |
+ | 0 | N1 |
+ When sending search query "fOObar"
Then results contain
- | ID | osm_type | osm_id |
- | 0 | N | 1 |
- When searching for "FOOBAR"
+ | ID | osm |
+ | 0 | N1 |
+ When sending search query "FOOBAR"
Then results contain
- | ID | osm_type | osm_id |
- | 0 | N | 1 |
+ | ID | osm |
+ | 0 | N1 |
Scenario: Multiple spaces in name
Given the places
| osm | class | type | name |
| N1 | place | locality | one two three |
When importing
- When searching for "one two three"
+ When sending search query "one two three"
Then results contain
- | ID | osm_type | osm_id |
- | 0 | N | 1 |
- When searching for "one two three"
+ | ID | osm |
+ | 0 | N1 |
+ When sending search query "one two three"
Then results contain
- | ID | osm_type | osm_id |
- | 0 | N | 1 |
- When searching for "one two three"
+ | ID | osm |
+ | 0 | N1 |
+ When sending search query "one two three"
Then results contain
- | ID | osm_type | osm_id |
- | 0 | N | 1 |
- When searching for " one two three"
+ | ID | osm |
+ | 0 | N1 |
+ When sending search query " one two three"
Then results contain
- | ID | osm_type | osm_id |
- | 0 | N | 1 |
+ | ID | osm |
+ | 0 | N1 |
Scenario: Special characters in name
Given the places
| N4 | place | locality | space |
| N5 | place | locality | mountain |
When importing
- When searching for "Jim-Knopf-Str"
+ When sending search query "Jim-Knopf-Str"
Then results contain
- | ID | osm_type | osm_id |
- | 0 | N | 1 |
- When searching for "Jim Knopf-Str"
+ | ID | osm |
+ | 0 | N1 |
+ When sending search query "Jim Knopf-Str"
Then results contain
- | ID | osm_type | osm_id |
- | 0 | N | 1 |
- When searching for "Jim Knopf Str"
+ | ID | osm |
+ | 0 | N1 |
+ When sending search query "Jim Knopf Str"
Then results contain
- | ID | osm_type | osm_id |
- | 0 | N | 1 |
- When searching for "Jim/Knopf-Str"
+ | ID | osm |
+ | 0 | N1 |
+ When sending search query "Jim/Knopf-Str"
Then results contain
- | ID | osm_type | osm_id |
- | 0 | N | 1 |
- When searching for "Jim-Knopfstr"
+ | ID | osm |
+ | 0 | N1 |
+ When sending search query "Jim-Knopfstr"
Then results contain
- | ID | osm_type | osm_id |
- | 0 | N | 1 |
- When searching for "Smith/Weston"
+ | ID | osm |
+ | 0 | N1 |
+ When sending search query "Smith/Weston"
Then results contain
- | ID | osm_type | osm_id |
- | 0 | N | 2 |
- When searching for "Smith Weston"
+ | ID | osm |
+ | 0 | N2 |
+ When sending search query "Smith Weston"
Then results contain
- | ID | osm_type | osm_id |
- | 0 | N | 2 |
- When searching for "Smith-Weston"
+ | ID | osm |
+ | 0 | N2 |
+ When sending search query "Smith-Weston"
Then results contain
- | ID | osm_type | osm_id |
- | 0 | N | 2 |
- When searching for "space mountain"
+ | ID | osm |
+ | 0 | N2 |
+ When sending search query "space mountain"
Then results contain
- | ID | osm_type | osm_id |
- | 0 | N | 3 |
- When searching for "space-mountain"
+ | ID | osm |
+ | 0 | N3 |
+ When sending search query "space-mountain"
Then results contain
- | ID | osm_type | osm_id |
- | 0 | N | 3 |
- When searching for "space/mountain"
+ | ID | osm |
+ | 0 | N3 |
+ When sending search query "space/mountain"
Then results contain
- | ID | osm_type | osm_id |
- | 0 | N | 3 |
- When searching for "space\mountain"
+ | ID | osm |
+ | 0 | N3 |
+ When sending search query "space\mountain"
Then results contain
- | ID | osm_type | osm_id |
- | 0 | N | 3 |
- When searching for "space(mountain)"
+ | ID | osm |
+ | 0 | N3 |
+ When sending search query "space(mountain)"
Then results contain
- | ID | osm_type | osm_id |
- | 0 | N | 3 |
+ | ID | osm |
+ | 0 | N3 |
Scenario: Landuse with name are found
Given the places
| R1 | natural | meadow | landuse1 | (0 0, 1 0, 1 1, 0 1, 0 0) |
| R2 | landuse | industrial | landuse2 | (0 0, -1 0, -1 -1, 0 -1, 0 0) |
When importing
- When searching for "landuse1"
+ When sending search query "landuse1"
Then results contain
- | ID | osm_type | osm_id |
- | 0 | R | 1 |
- When searching for "landuse2"
+ | ID | osm |
+ | 0 | R1 |
+ When sending search query "landuse2"
Then results contain
- | ID | osm_type | osm_id |
- | 0 | R | 2 |
+ | ID | osm |
+ | 0 | R2 |
Scenario: Postcode boundaries without ref
Given the places
| osm | class | type | postcode | geometry |
| R1 | boundary | postal_code | 12345 | (0 0, 1 0, 1 1, 0 1, 0 0) |
When importing
- When searching for "12345"
+ When sending search query "12345"
Then results contain
- | ID | osm_type | osm_id |
- | 0 | R | 1 |
+ | ID | osm |
+ | 0 | R1 |
Scenario: Unprintable characters in postcodes are ignored
Given the named places
| osm | class | type | address |
| N234 | amenity | prison | 'postcode' : u'1234\u200e' |
When importing
- And searching for "1234"
- Then results contain
- | ID | osm_type |
- | 0 | P |
+ And sending search query "1234"
+ Then result 0 has not attributes osm_type
Scenario Outline: Housenumbers with special characters are found
Given the grid
| osm | class | type | housenr | geometry |
| N1 | building | yes | <nr> | 9 |
When importing
- And searching for "Main St <nr>"
+ And sending search query "Main St <nr>"
Then results contain
- | osm_type | osm_id | name |
- | N | 1 | <nr>, Main St |
+ | osm | display_name |
+ | N1 | <nr>, Main St |
Examples:
| nr |
| osm | class | type | housenr | geometry |
| N1 | building | yes | <nr-list> | 9 |
When importing
- And searching for "Main St <nr>"
+ And sending search query "Main St <nr>"
Then results contain
- | osm_type | osm_id | name |
- | N | 1 | <nr-list>, Main St |
+ | osm | display_name |
+ | N1 | <nr-list>, Main St |
Examples:
| nr-list | nr |
| osm | class | type | name+name | geometry |
| N1 | place | village | Foo | 10.0 -10.0 |
When importing
- And searching for "Foo"
+ And sending search query "Foo"
Then results contain
- | ID | osm | class | type | centroid |
- | 0 | N1 | place | village | 10 -10 |
+ | ID | osm | category | type | centroid |
+ | 0 | N1 | place | village | 10 -10 |
Scenario: Updating postcode in postcode boundaries without ref
Given the places
| osm | class | type | postcode | geometry |
| R1 | boundary | postal_code | 12345 | poly-area:1.0 |
When importing
- And searching for "12345"
+ And sending search query "12345"
Then results contain
- | ID | osm_type | osm_id |
- | 0 | R | 1 |
+ | ID | osm |
+ | 0 | R1 |
When updating places
| osm | class | type | postcode | geometry |
| R1 | boundary | postal_code | 54321 | poly-area:1.0 |
- And searching for "12345"
+ And sending search query "12345"
+ Then result 0 has not attributes osm_type
+ When sending search query "54321"
Then results contain
- | osm_type |
- | P |
- When searching for "54321"
- Then results contain
- | ID | osm_type | osm_id |
- | 0 | R | 1 |
+ | ID | osm |
+ | 0 | R1 |
# github #1763
Scenario: Correct translation of highways under construction
Then result addresses contain
| amenity | road |
| Bean | The build |
+
+ Scenario: when missing housenumbers in search don't return a POI
+ Given the places
+ | osm | class | type | name |
+ | N3 | amenity | restaurant | Wood Street |
+ And the places
+ | osm | class | type | name | housenr |
+ | N20 | amenity | restaurant | Red Way | 34 |
+ When importing
+ And sending search query "Wood Street 45"
+ Then exactly 0 results are returned
+ When sending search query "Red Way 34"
+ Then results contain
+ | osm |
+ | N20 |
+
+ Scenario: when the housenumber is missing the stret is still returned
+ Given the grid
+ | 1 | | 2 |
+ Given the places
+ | osm | class | type | name | geometry |
+ | W1 | highway | residential | Wood Street | 1, 2 |
+ When importing
+ And sending search query "Wood Street"
+ Then results contain
+ | osm |
+ | W1 |
| osm | class | type | name | admin | geometry |
| R1 | boundary | administrative | foo | 8 | poly-area:0.1 |
When importing
- And searching for "foo" with dups
+ And sending search query "foo"
+ | dups |
+ | 1 |
Then results contain
| osm_type |
| R |
Then placex contains
| object | linked_place_id |
| N1 | - |
- When searching for "foo" with dups
+ When sending search query "foo"
+ | dups |
+ | 1 |
Then results contain
| osm_type |
| N |
| osm | class | type | name | admin | geometry |
| R1 | boundary | administrative | foo | 8 | poly-area:0.1 |
When importing
- And searching for "foo" with dups
+ And sending search query "foo"
+ | dups |
+ | 1 |
Then results contain
| osm_type |
| R |
Then placex contains
| object | linked_place_id |
| N1 | - |
- When searching for "foo" with dups
+ When sending search query "foo"
+ | dups |
+ | 1 |
Then results contain
| osm_type |
| N |
| osm | class | type | name | geometry |
| N1 | place | city | foo | 0 0 |
When importing
- And searching for "foo" with dups
+ And sending search query "foo"
+ | dups |
+ | 1 |
Then results contain
| osm_type |
| N |
Then placex contains
| object | linked_place_id |
| N1 | R1 |
- When searching for "foo" with dups
+ When sending search query "foo"
+ | dups |
+ | 1 |
Then results contain
| osm_type |
| R |
| osm | class | type | name | admin | geometry |
| R1 | boundary | administrative | foobar | 8 | poly-area:0.1 |
When importing
- And searching for "foo" with dups
+ And sending search query "foo"
+ | dups |
+ | 1 |
Then results contain
| osm_type |
| N |
Then placex contains
| object | linked_place_id |
| N1 | R1 |
- When searching for "foo" with dups
+ When sending search query "foo"
+ | dups |
+ | 1 |
Then results contain
| osm_type |
| R |
| osm | class | type | postcode | geometry |
| R1 | boundary | postal_code | 12345 | poly-area:0.5 |
When importing
- And searching for "12345"
+ And sending search query "12345"
Then results contain
- | ID | osm_type | osm_id |
- | 0 | R | 1 |
+ | ID | osm |
+ | 0 | R1 |
When updating places
| osm | class | type | geometry |
| R1 | boundary | postal_code | poly-area:0.5 |
from check_functions import Almost
-OSM_TYPE = {'N' : 'node', 'W' : 'way', 'R' : 'relation'}
+OSM_TYPE = {'N' : 'node', 'W' : 'way', 'R' : 'relation',
+ 'n' : 'node', 'w' : 'way', 'r' : 'relation',
+ 'node' : 'n', 'way' : 'w', 'relation' : 'r'}
def _geojson_result_to_json_result(geojson_result):
result = geojson_result['properties']
assert self.result[i]['osm_type'] in (OSM_TYPE[value[0]], value[0]), \
BadRowValueAssert(self, i, 'osm_type', value)
self.assert_field(i, 'osm_id', value[1:])
+ elif name == 'osm_type':
+ assert self.result[i]['osm_type'] in (OSM_TYPE[value[0]], value[0]), \
+ BadRowValueAssert(self, i, 'osm_type', value)
elif name == 'centroid':
lon, lat = value.split(' ')
self.assert_field(i, 'lat', float(lat))
raise Exception("unknown operator '%s'" % operator)
-@when(u'searching for "(?P<query>.*)"(?P<dups> with dups)?')
-def query_cmd(context, query, dups):
- """ Query directly via PHP script.
- """
- cmd = ['/usr/bin/env', 'php']
- cmd.append(context.nominatim.src_dir / 'lib-php' / 'admin' / 'query.php')
- if query:
- cmd.extend(['--search', query])
- # add more parameters in table form
- if context.table:
- for h in context.table.headings:
- value = context.table[0][h].strip()
- if value:
- cmd.extend(('--' + h, value))
-
- if dups:
- cmd.extend(('--dedupe', '0'))
-
- outp, err = run_script(cmd, cwd=context.nominatim.website_dir.name,
- env=context.nominatim.test_env)
-
- context.response = SearchResponse(outp, 'json')
-
def send_api_query(endpoint, params, fmt, context):
if fmt is not None and fmt.strip() != 'debug':
params['format'] = fmt.strip()
self.conn.commit()
+ def add_country(self, country_code, word_token):
+ with self.conn.cursor() as cur:
+ cur.execute("INSERT INTO word (word_token, country_code) VALUES(%s, %s)",
+ (word_token, country_code))
+ self.conn.commit()
+
+
def add_postcode(self, word_token, postcode):
with self.conn.cursor() as cur:
cur.execute("""INSERT INTO word (word_token, word, class, type)
with self.conn.cursor() as cur:
cur.execute("""SELECT word_token, word, class, type, operator
FROM word WHERE class != 'place'""")
- return set((tuple(row) for row in cur))
+ result = set((tuple(row) for row in cur))
+ assert len(result) == cur.rowcount, "Word table has duplicates."
+ return result
+
+
+ def get_country(self):
+ with self.conn.cursor() as cur:
+ cur.execute("""SELECT country_code, word_token
+ FROM word WHERE country_code is not null""")
+ result = set((tuple(row) for row in cur))
+ assert len(result) == cur.rowcount, "Word table has duplicates."
+ return result
def get_postcodes(self):
assert func_mock.called == 1
- def test_refresh_postcodes(self, mock_func_factory):
+ def test_refresh_postcodes(self, mock_func_factory, place_table):
func_mock = mock_func_factory(nominatim.tools.postcodes, 'update_postcodes')
idx_mock = mock_func_factory(nominatim.indexer.indexer.Indexer, 'index_postcodes')
@pytest.fixture
def make_standard_name(temp_db_cursor):
temp_db_cursor.execute("""CREATE OR REPLACE FUNCTION make_standard_name(name TEXT)
- RETURNS TEXT AS $$ SELECT ' ' || name; $$ LANGUAGE SQL""")
+ RETURNS TEXT AS $$ SELECT '#' || lower(name) || '#'; $$ LANGUAGE SQL""")
@pytest.fixture
analyzer.update_special_phrases([
("König bei", "amenity", "royal", "near"),
("Könige", "amenity", "royal", "-"),
+ ("könige", "amenity", "royal", "-"),
("strasse", "highway", "primary", "in")
], True)
assert word_table.get_special() \
- == set(((' könig bei', 'könig bei', 'amenity', 'royal', 'near'),
- (' könige', 'könige', 'amenity', 'royal', None),
- (' strasse', 'strasse', 'highway', 'primary', 'in')))
+ == set(((' #könig bei#', 'könig bei', 'amenity', 'royal', 'near'),
+ (' #könige#', 'könige', 'amenity', 'royal', None),
+ (' #strasse#', 'strasse', 'highway', 'primary', 'in')))
def test_update_special_phrase_delete_all(analyzer, word_table, make_standard_name):
- word_table.add_special(' foo', 'foo', 'amenity', 'prison', 'in')
- word_table.add_special(' bar', 'bar', 'highway', 'road', None)
+ word_table.add_special(' #foo#', 'foo', 'amenity', 'prison', 'in')
+ word_table.add_special(' #bar#', 'bar', 'highway', 'road', None)
assert word_table.count_special() == 2
def test_update_special_phrases_no_replace(analyzer, word_table, make_standard_name):
- word_table.add_special(' foo', 'foo', 'amenity', 'prison', 'in')
- word_table.add_special(' bar', 'bar', 'highway', 'road', None)
+ word_table.add_special(' #foo#', 'foo', 'amenity', 'prison', 'in')
+ word_table.add_special(' #bar#', 'bar', 'highway', 'road', None)
assert word_table.count_special() == 2
def test_update_special_phrase_modify(analyzer, word_table, make_standard_name):
- word_table.add_special(' foo', 'foo', 'amenity', 'prison', 'in')
- word_table.add_special(' bar', 'bar', 'highway', 'road', None)
+ word_table.add_special(' #foo#', 'foo', 'amenity', 'prison', 'in')
+ word_table.add_special(' #bar#', 'bar', 'highway', 'road', None)
assert word_table.count_special() == 2
], True)
assert word_table.get_special() \
- == set(((' prison', 'prison', 'amenity', 'prison', 'in'),
- (' bar', 'bar', 'highway', 'road', None),
- (' garden', 'garden', 'leisure', 'garden', 'near')))
+ == set(((' #prison#', 'prison', 'amenity', 'prison', 'in'),
+ (' #bar#', 'bar', 'highway', 'road', None),
+ (' #garden#', 'garden', 'leisure', 'garden', 'near')))
+
+
+def test_add_country_names(analyzer, word_table, make_standard_name):
+ analyzer.add_country_names('de', ['Germany', 'Deutschland', 'germany'])
+
+ assert word_table.get_country() \
+ == {('de', ' #germany#'),
+ ('de', ' #deutschland#')}
+
+
+def test_add_more_country_names(analyzer, word_table, make_standard_name):
+ word_table.add_country('fr', ' #france#')
+ word_table.add_country('it', ' #italy#')
+ word_table.add_country('it', ' #itala#')
+
+ analyzer.add_country_names('it', ['Italy', 'IT'])
+
+ assert word_table.get_country() \
+ == {('fr', ' #france#'),
+ ('it', ' #italy#'),
+ ('it', ' #itala#'),
+ ('it', ' #it#')}
def test_process_place_names(analyzer, make_keywords):
geometry GEOMETRY(Geometry, 4326))""")
cur.execute("""CREATE OR REPLACE FUNCTION token_normalized_postcode(postcode TEXT)
RETURNS TEXT AS $$ BEGIN RETURN postcode; END; $$ LANGUAGE plpgsql;
+
+ CREATE OR REPLACE FUNCTION get_country_code(place geometry)
+ RETURNS TEXT AS $$ BEGIN
+ RETURN null;
+ END; $$ LANGUAGE plpgsql;
""")
conn.commit()
return MockPostcodeTable(temp_db_conn)
-def test_postcodes_empty(dsn, postcode_table, tmp_path, tokenizer):
+def test_postcodes_empty(dsn, postcode_table, place_table,
+ tmp_path, tokenizer):
postcodes.update_postcodes(dsn, tmp_path, tokenizer)
assert not postcode_table.row_set
-def test_postcodes_add_new(dsn, placex_table, postcode_table, tmp_path, tokenizer):
- placex_table.add(country='xx', geom='POINT(10 12)',
- address=dict(postcode='9486'))
+def test_postcodes_add_new(dsn, postcode_table, tmp_path,
+ insert_implicit_postcode, tokenizer):
+ insert_implicit_postcode(1, 'xx', 'POINT(10 12)', dict(postcode='9486'))
postcode_table.add('yy', '9486', 99, 34)
postcodes.update_postcodes(dsn, tmp_path, tokenizer)
assert postcode_table.row_set == {('xx', '9486', 10, 12), }
-def test_postcodes_replace_coordinates(dsn, placex_table, postcode_table,
- tmp_path, tokenizer):
- placex_table.add(country='xx', geom='POINT(10 12)',
- address=dict(postcode='AB 4511'))
+def test_postcodes_replace_coordinates(dsn, postcode_table, tmp_path,
+ insert_implicit_postcode, tokenizer):
+ insert_implicit_postcode(1, 'xx', 'POINT(10 12)', dict(postcode='AB 4511'))
postcode_table.add('xx', 'AB 4511', 99, 34)
postcodes.update_postcodes(dsn, tmp_path, tokenizer)
assert postcode_table.row_set == {('xx', 'AB 4511', 10, 12)}
-def test_postcodes_replace_coordinates_close(dsn, placex_table, postcode_table,
- tmp_path, tokenizer):
- placex_table.add(country='xx', geom='POINT(10 12)',
- address=dict(postcode='AB 4511'))
+def test_postcodes_replace_coordinates_close(dsn, postcode_table, tmp_path,
+ insert_implicit_postcode, tokenizer):
+ insert_implicit_postcode(1, 'xx', 'POINT(10 12)', dict(postcode='AB 4511'))
postcode_table.add('xx', 'AB 4511', 10, 11.99999)
postcodes.update_postcodes(dsn, tmp_path, tokenizer)
assert postcode_table.row_set == {('xx', 'AB 4511', 10, 11.99999)}
-def test_postcodes_remove(dsn, placex_table, postcode_table, tmp_path, tokenizer):
- placex_table.add(country='xx', geom='POINT(10 12)',
- address=dict(postcode='AB 4511'))
+def test_postcodes_remove(dsn, postcode_table, tmp_path,
+ insert_implicit_postcode, tokenizer):
+ insert_implicit_postcode(1, 'xx', 'POINT(10 12)', dict(postcode='AB 4511'))
postcode_table.add('xx', 'badname', 10, 12)
postcodes.update_postcodes(dsn, tmp_path, tokenizer)
assert postcode_table.row_set == {('xx', 'AB 4511', 10, 12)}
-def test_postcodes_ignore_empty_country(dsn, placex_table, postcode_table, tmp_path, tokenizer):
- placex_table.add(country=None, geom='POINT(10 12)',
- address=dict(postcode='AB 4511'))
-
+def test_postcodes_ignore_empty_country(dsn, postcode_table, tmp_path,
+ insert_implicit_postcode, tokenizer):
+ insert_implicit_postcode(1, None, 'POINT(10 12)', dict(postcode='AB 4511'))
postcodes.update_postcodes(dsn, tmp_path, tokenizer)
-
assert not postcode_table.row_set
-def test_postcodes_remove_all(dsn, postcode_table, tmp_path, tokenizer):
+def test_postcodes_remove_all(dsn, postcode_table, place_table,
+ tmp_path, tokenizer):
postcode_table.add('ch', '5613', 10, 12)
-
postcodes.update_postcodes(dsn, tmp_path, tokenizer)
assert not postcode_table.row_set
-def test_postcodes_multi_country(dsn, placex_table, postcode_table, tmp_path, tokenizer):
- placex_table.add(country='de', geom='POINT(10 12)',
- address=dict(postcode='54451'))
- placex_table.add(country='cc', geom='POINT(100 56)',
- address=dict(postcode='DD23 T'))
- placex_table.add(country='de', geom='POINT(10.3 11.0)',
- address=dict(postcode='54452'))
- placex_table.add(country='cc', geom='POINT(10.3 11.0)',
- address=dict(postcode='54452'))
+def test_postcodes_multi_country(dsn, postcode_table, tmp_path,
+ insert_implicit_postcode, tokenizer):
+ insert_implicit_postcode(1, 'de', 'POINT(10 12)', dict(postcode='54451'))
+ insert_implicit_postcode(2, 'cc', 'POINT(100 56)', dict(postcode='DD23 T'))
+ insert_implicit_postcode(3, 'de', 'POINT(10.3 11.0)', dict(postcode='54452'))
+ insert_implicit_postcode(4, 'cc', 'POINT(10.3 11.0)', dict(postcode='54452'))
postcodes.update_postcodes(dsn, tmp_path, tokenizer)
@pytest.mark.parametrize("gzipped", [True, False])
-def test_postcodes_extern(dsn, placex_table, postcode_table, tmp_path,
- tokenizer, gzipped):
- placex_table.add(country='xx', geom='POINT(10 12)',
- address=dict(postcode='AB 4511'))
+def test_postcodes_extern(dsn, postcode_table, tmp_path,
+ insert_implicit_postcode, tokenizer, gzipped):
+ insert_implicit_postcode(1, 'xx', 'POINT(10 12)', dict(postcode='AB 4511'))
extfile = tmp_path / 'xx_postcodes.csv'
extfile.write_text("postcode,lat,lon\nAB 4511,-4,-1\nCD 4511,-5, -10")
('xx', 'CD 4511', -10, -5)}
-def test_postcodes_extern_bad_column(dsn, placex_table, postcode_table,
- tmp_path, tokenizer):
- placex_table.add(country='xx', geom='POINT(10 12)',
- address=dict(postcode='AB 4511'))
+def test_postcodes_extern_bad_column(dsn, postcode_table, tmp_path,
+ insert_implicit_postcode, tokenizer):
+ insert_implicit_postcode(1, 'xx', 'POINT(10 12)', dict(postcode='AB 4511'))
extfile = tmp_path / 'xx_postcodes.csv'
extfile.write_text("postode,lat,lon\nAB 4511,-4,-1\nCD 4511,-5, -10")
assert postcode_table.row_set == {('xx', 'AB 4511', 10, 12)}
-def test_postcodes_extern_bad_number(dsn, placex_table, postcode_table,
- tmp_path, tokenizer):
- placex_table.add(country='xx', geom='POINT(10 12)',
- address=dict(postcode='AB 4511'))
+def test_postcodes_extern_bad_number(dsn, insert_implicit_postcode,
+ postcode_table, tmp_path, tokenizer):
+ insert_implicit_postcode(1, 'xx', 'POINT(10 12)', dict(postcode='AB 4511'))
extfile = tmp_path / 'xx_postcodes.csv'
extfile.write_text("postcode,lat,lon\nXX 4511,-4,NaN\nCD 4511,-5, -10\n34,200,0")
assert postcode_table.row_set == {('xx', 'AB 4511', 10, 12),
('xx', 'CD 4511', -10, -5)}
+
+def test_can_compute(dsn, table_factory):
+ assert not postcodes.can_compute(dsn)
+ table_factory('place')
+ assert postcodes.can_compute(dsn)
+
+def test_no_placex_entry(dsn, tmp_path, temp_db_cursor, place_row, postcode_table, tokenizer):
+ #Rewrite the get_country_code function to verify its execution.
+ temp_db_cursor.execute("""
+ CREATE OR REPLACE FUNCTION get_country_code(place geometry)
+ RETURNS TEXT AS $$ BEGIN
+ RETURN 'fr';
+ END; $$ LANGUAGE plpgsql;
+ """)
+ place_row(geom='SRID=4326;POINT(10 12)', address=dict(postcode='AB 4511'))
+ postcodes.update_postcodes(dsn, tmp_path, tokenizer)
+
+ assert postcode_table.row_set == {('fr', 'AB 4511', 10, 12)}
+
+@pytest.fixture
+def insert_implicit_postcode(placex_table, place_row):
+ """
+ Inserts data into the placex and place table
+ which can then be used to compute one postcode.
+ """
+ def _insert_implicit_postcode(osm_id, country, geometry, address):
+ placex_table.add(osm_id=osm_id, country=country, geom=geometry)
+ place_row(osm_id=osm_id, geom='SRID=4326;'+geometry, address=address)
+
+ return _insert_implicit_postcode
EOF_NGINX_CONF
#DOCS:```
+# If you have some errors, make sure that php7.4-fpm.sock is well under
+# /var/run/ and not under /var/run/php. Otherwise change the Nginx configuration
+# to /var/run/php/php7.4-fpm.sock.
#
# Enable the configuration and restart Nginx
#