$this->sType = $sType;
}
- /**
- * Check if this might be a full address search.
- *
- * @return bool True if the search contains name, address and housenumber.
- */
- public function looksLikeFullAddress()
- {
- return (!empty($this->aName))
- && (!empty($this->aAddress) || $this->sCountryCode)
- && preg_match('/[0-9]+/', $this->sHouseNumber);
- }
-
/**
* Check if any operator is set.
*
'Name terms (stop words)' => $this->aNameNonSearch,
'Address terms' => $this->aAddress,
'Address terms (stop words)' => $this->aAddressNonSearch,
- 'Address terms (full words)' => $this->aFullNameAddress,
+ 'Address terms (full words)' => $this->aFullNameAddress ?? '',
'Special search' => $this->iOperator,
'Class' => $this->sClass,
'Type' => $this->sType,
public function dumpAsHtmlTableRow(&$aWordIDs)
{
$kf = function ($k) use (&$aWordIDs) {
- return $aWordIDs[$k];
+ return $aWordIDs[$k] ?? '['.$k.']';
};
echo '<tr>';
return '';
}
-
-function osmLink($aFeature, $sRefText = false)
-{
- $sOSMType = formatOSMType($aFeature['osm_type'], false);
- if ($sOSMType) {
- return '<a href="//www.openstreetmap.org/'.$sOSMType.'/'.$aFeature['osm_id'].'">'.$sOSMType.' '.($sRefText?$sRefText:$aFeature['osm_id']).'</a>';
- }
- return '';
-}
-
-function wikipediaLink($aFeature)
-{
- if ($aFeature['wikipedia']) {
- list($sLanguage, $sArticle) = explode(':', $aFeature['wikipedia']);
- return '<a href="https://'.$sLanguage.'.wikipedia.org/wiki/'.urlencode($sArticle).'" target="_blank">'.$aFeature['wikipedia'].'</a>';
- }
- return '';
-}
-
-function detailsLink($aFeature, $sTitle = false, $sExtraProperties = false)
-{
- if (!$aFeature['place_id']) return '';
-
- $sHtml = '<a ';
- if ($sExtraProperties) {
- $sHtml .= $sExtraProperties.' ';
- }
-
- $sHtml .= 'href="details.php?place_id='.$aFeature['place_id'].'">'.($sTitle?$sTitle:$aFeature['place_id']).'</a>';
-
- return $sHtml;
-}
-
-function detailsPermaLink($aFeature, $sRefText = false, $sExtraProperties = false)
-{
- $sOSMType = formatOSMType($aFeature['osm_type'], false);
-
- if ($sOSMType) {
- $sHtml = '<a ';
- if ($sExtraProperties) {
- $sHtml .= $sExtraProperties.' ';
- }
- $sHtml .= 'href="details.php?osmtype='.$aFeature['osm_type']
- .'&osmid='.$aFeature['osm_id'].'&class='.$aFeature['class'].'">';
-
- if ($sRefText) {
- $sHtml .= $sRefText.'</a>';
- } else {
- $sHtml .= $sOSMType.' '.$aFeature['osm_id'].'</a>';
- }
-
- return $sHtml;
- }
- return detailsLink($aFeature, $sRefText, $sExtraProperties);
-}
--- /dev/null
+@APIDB
+Feature: Localization of search results
+
+ Scenario: default language
+ When sending details query for R1155955
+ Then results contain
+ | ID | localname |
+ | 0 | Liechtenstein |
+
+ Scenario: accept-language first
+ When sending details query for R1155955
+ | accept-language |
+ | zh,de |
+ Then results contain
+ | ID | localname |
+ | 0 | 列支敦士登 |
+
+ Scenario: accept-language missing
+ When sending details query for R1155955
+ | accept-language |
+ | xx,fr,en,de |
+ Then results contain
+ | ID | localname |
+ | 0 | Liechtenstein |
+
+ Scenario: http accept language header first
+ Given the HTTP header
+ | accept-language |
+ | fo;q=0.8,en-ca;q=0.5,en;q=0.3 |
+ When sending details query for R1155955
+ Then results contain
+ | ID | localname |
+ | 0 | Liktinstein |
+
+ Scenario: http accept language header and accept-language
+ Given the HTTP header
+ | accept-language |
+ | fr-ca,fr;q=0.8,en-ca;q=0.5,en;q=0.3 |
+ When sending details query for R1155955
+ | accept-language |
+ | fo,en |
+ Then results contain
+ | ID | localname |
+ | 0 | Liktinstein |
+
+ Scenario: http accept language header fallback
+ Given the HTTP header
+ | accept-language |
+ | fo-ca,en-ca;q=0.5 |
+ When sending details query for R1155955
+ Then results contain
+ | ID | localname |
+ | 0 | Liktinstein |
+
+ Scenario: http accept language header fallback (upper case)
+ Given the HTTP header
+ | accept-language |
+ | fo-FR;q=0.8,en-ca;q=0.5 |
+ When sending details query for R1155955
+ Then results contain
+ | ID | localname |
+ | 0 | Liktinstein |
And result has attributes geometry
And result has not attributes keywords,address,linked_places,parentof
- Scenario: JSON Details with keywords
+ Scenario: JSON Details with pretty printing
When sending json details query for W297699560
- | keywords |
- | 1 |
+ | pretty |
+ | 1 |
Then the result is valid json
- And result has attributes keywords
+ And result has attributes geometry
+ And result has not attributes keywords,address,linked_places,parentof
- Scenario: JSON Details with addressdetails
+ Scenario: JSON Details with addressdetails
When sending json details query for W297699560
| addressdetails |
| 1 |
Then the result is valid json
And result has attributes hierarchy
- Scenario: JSON Details with linkedplaces
- When sending json details query for R123924
- | linkedplaces |
- | 1 |
+ Scenario: JSON Details with grouped hierarchy
+ When sending json details query for W297699560
+ | hierarchy | group_hierarchy |
+ | 1 | 1 |
Then the result is valid json
+ And result has attributes hierarchy
- Scenario Outline: HTML Details with keywords
+ Scenario Outline: JSON Details with keywords
When sending json details query for <osmid>
| keywords |
| 1 |
Then the result is valid json
+ And result has attributes keywords
+
+ Examples:
+ | osmid |
+ | W297699560 |
+ | W243055645 |
+ | W243055716 |
+ | W43327921 |
+
+ # ticket #1343
+ Scenario: Details of a country with keywords
+ When sending details query for R1155955
+ | keywords |
+ | 1 |
+ Then the result is valid json
+
+ Scenario Outline: JSON details with full geometry
+ When sending json details query for <osmid>
+ | polygon_geojson |
+ | 1 |
+ Then the result is valid json
+ And result has attributes geometry
Examples:
| osmid |
| W297699560 |
| W243055645 |
| W243055716 |
+ | W43327921 |
Feature: Object details
Check details page for correctness
+ Scenario: Details by place ID
+ When sending details query for 107077
+ Then the result is valid json
+ And results contain
+ | place_id |
+ | 107077 |
+
Scenario Outline: Details via OSM id
- When sending <format> details query for <object>
- Then the result is valid <format>
+ When sending details query for <type><id>
+ Then the result is valid json
+ And results contain
+ | osm_type | osm_id |
+ | <type> | <id> |
Examples:
- | format | object |
- | json | 107077 |
- | json | N5484325405 |
- | json | W43327921 |
- | json | R123924 |
+ | type | id |
+ | N | 5484325405 |
+ | W | 43327921 |
+ | R | 123924 |
+
+ Scenario: Details for interpolation way just return the dependent street
+ When sending details query for W1
+ Then the result is valid json
+ And results contain
+ | category |
+ | highway |
+
+ Scenario Outline: Details for different class types for the same OSM id
+ When sending details query for N300209696:<class>
+ Then the result is valid json
+ And results contain
+ | osm_type | osm_id | category |
+ | N | 300209696 | <class> |
+
+ Examples:
+ | class |
+ | tourism |
+ | natural |
+ | mountain_pass |
Scenario Outline: Details via unknown OSM id
- When sending <format> details query for <object>
+ When sending details query for <object>
Then a HTTP 400 is returned
Examples:
- | format | object |
- | json | 1 |
- | json | R1 |
-
- Scenario: Details with keywords
- When sending details query for W43327921
- | keywords |
- | 1 |
- Then the result is valid json
+ | object |
+ | 1 |
+ | R1 |
+ | N300209696:highway |
- # ticket #1343
- Scenario: Details of a country with keywords
- When sending details query for R1155955
- | keywords |
- | 1 |
- Then the result is valid json
| -45.3,; |
| gkjd,50 |
+ Scenario Outline: Zoom levels between 4 and 18 are allowed
+ When sending reverse coordinates 47.14122383,9.52169581334
+ | zoom |
+ | <zoom> |
+ Then exactly 1 result is returned
+ And result addresses contain
+ | country_code |
+ | li |
+
+ Examples:
+ | zoom |
+ | 4 |
+ | 5 |
+ | 6 |
+ | 7 |
+ | 8 |
+ | 9 |
+ | 10 |
+ | 11 |
+ | 12 |
+ | 13 |
+ | 14 |
+ | 15 |
+ | 16 |
+ | 17 |
+ | 18 |
+
+ Scenario: Non-numerical zoom levels return an error
+ When sending reverse coordinates 47.14122383,9.52169581334
+ | zoom |
+ | adfe |
+ Then a HTTP 400 is returned
+
Scenario Outline: Reverse Geocoding with extratags
When sending <format> reverse coordinates 47.1395013150811,9.522098077031046
| extratags |
| 48.966 | 8.448.2 |
| Nan | 8.448 |
| 48.966 | Nan |
+
+ Scenario: Reverse Debug output returns no errors
+ When sending debug reverse coordinates 47.11,9.57
+ Then a HTTP 200 is returned
| class | type |
| amenity | restaurant |
+ Scenario: Search with specific amenity also work in country
+ When sending json search query "restaurants in liechtenstein" with address
+ Then result addresses contain
+ | country |
+ | Liechtenstein |
+ And results contain
+ | class | type |
+ | amenity | restaurant |
+
Scenario: Search with key-value amenity
When sending json search query "[club=scout] Vaduz"
Then results contain
| class | type |
| leisure | firepit |
+ Scenario Outline: Key/value search near given coordinate can be restricted to country
+ When sending json search query "[natural=peak] 47.06512,9.53965" with address
+ | countrycodes |
+ | <cc> |
+ Then result addresses contain
+ | country_code |
+ | <cc> |
+
+ Examples:
+ | cc |
+ | li |
+ | ch |
+
Scenario: Name search near given coordinate
When sending json search query "sporry" with address
Then result addresses contain
| li |
Then exactly 0 results are returned
+ Scenario: Country searches only return results for the given country
+ When sending search query "Ans Trail" with address
+ | countrycodes |
+ | li |
+ Then result addresses contain
+ | country_code |
+ | li |
+
# https://trac.openstreetmap.org/ticket/5094
Scenario: housenumbers are ordered by complete match first
When sending json search query "Austrasse 11, Vaduz" with address
Then result addresses contain
| ID | town |
| 0 | Vaduz |
+
+ Scenario: Search can handle complex query word sets
+ When sending search query "aussenstelle universitat lichtenstein wachterhaus aussenstelle universitat lichtenstein wachterhaus aussenstelle universitat lichtenstein wachterhaus aussenstelle universitat lichtenstein wachterhaus"
+ Then a HTTP 200 is returned
Examples:
| parameter | value |
- | addressdetails | 1 |
| addressdetails | 0 |
- | polygon_text | 1 |
| polygon_text | 0 |
- | polygon_kml | 1 |
| polygon_kml | 0 |
- | polygon_geojson | 1 |
| polygon_geojson | 0 |
- | polygon_svg | 1 |
| polygon_svg | 0 |
| accept-language | de,en |
| countrycodes | li |
| limit | 1000 |
| dedupe | 1 |
| dedupe | 0 |
- | extratags | 1 |
| extratags | 0 |
- | namedetails | 1 |
| namedetails | 0 |
Scenario: Search with invalid output format
| 234 |
Then the result is valid xml
- Scenario: Empty JSON search
- When sending json search query "YHlERzzx"
+ Scenario Outline: Empty search
+ When sending <format> search query "YHlERzzx"
Then exactly 0 results are returned
- Scenario: Empty JSONv2 search
- When sending jsonv2 search query "Flubb XdfESSaZx"
- Then exactly 0 results are returned
+ Examples:
+ | format |
+ | json |
+ | jsonv2 |
+ | geojson |
+ | geocodejson |
Scenario: Search for non-existing coordinates
When sending json search query "-21.0,-33.0"
Then result header contains
| attr | value |
| more_url | .*&countrycodes=pl%2Cbo&.* |
+
+ Scenario Outline: Search debug output does not return errors
+ When sending debug search query "<query>"
+ Then a HTTP 200 is returned
+
+ Examples:
+ | query |
+ | Liechtenstein |
+ | Triesen |
+ | Pfarrkirche |
+ | Landstr 27 Steinort, Triesenberg, 9495 |
+ | 9497 |
+ | restaurant in triesen |
self.result = []
self.header = dict()
- if errorcode == 200:
+ if errorcode == 200 and fmt != 'debug':
getattr(self, '_parse_' + fmt)()
def _parse_json(self):
from utils import run_script
from http_responses import GenericResponse, SearchResponse, ReverseResponse, StatusResponse
from check_functions import Bbox
+from table_compare import NominatimID
LOG = logging.getLogger(__name__)
context.response = SearchResponse(outp, 'json')
def send_api_query(endpoint, params, fmt, context):
- if fmt is not None:
+ if fmt is not None and fmt.strip() != 'debug':
params['format'] = fmt.strip()
if context.table:
if context.table.headings[0] == 'param':
params['q'] = query
if addr is not None:
params['addressdetails'] = '1'
+ if fmt and fmt.strip() == 'debug':
+ params['debug'] = '1'
outp, status = send_api_query('search', params, fmt, context)
params['lat'] = lat
if lon is not None:
params['lon'] = lon
+ if fmt and fmt.strip() == 'debug':
+ params['debug'] = '1'
outp, status = send_api_query('reverse', params, fmt, context)
def website_details_request(context, fmt, query):
params = {}
if query[0] in 'NWR':
- params['osmtype'] = query[0]
- params['osmid'] = query[1:]
+ nid = NominatimID(query)
+ params['osmtype'] = nid.typ
+ params['osmid'] = nid.oid
+ if nid.cls:
+ params['class'] = nid.cls
else:
params['place_id'] = query
outp, status = send_api_query('details', params, fmt, context)
@then(u'a HTTP (?P<status>\d+) is returned')
def check_http_return_status(context, status):
- assert context.response.errorcode == int(status)
+ assert context.response.errorcode == int(status), \
+ "Return HTTP status is {}.".format(context.response.errorcode)
@then(u'the page contents equals "(?P<text>.+)"')
def check_page_content_equals(context, text):
def check_header_no_attr(context, neg, attrs):
for attr in attrs.split(','):
if neg:
- assert attr not in context.response.header
+ assert attr not in context.response.header, \
+ "Unexpected attribute {}. Full response:\n{}".format(
+ attr, json.dumps(context.response.header, sort_keys=True, indent=2))
else:
- assert attr in context.response.header
+ assert attr in context.response.header, \
+ "No attribute {}. Full response:\n{}".format(
+ attr, json.dumps(context.response.header, sort_keys=True, indent=2))
@then(u'results contain')
def step_impl(context):
for i in idx:
for attr in attrs.split(','):
if neg:
- assert attr not in context.response.result[i]
+ assert attr not in context.response.result[i],\
+ "Unexpected attribute {}. Full response:\n{}".format(
+ attr, json.dumps(context.response.result[i], sort_keys=True, indent=2))
else:
- assert attr in context.response.result[i]
+ assert attr in context.response.result[i], \
+ "No attribute {}. Full response:\n{}".format(
+ attr, json.dumps(context.response.result[i], sort_keys=True, indent=2))
@then(u'result addresses contain')
def step_impl(context):
+++ /dev/null
-<?php
-
-namespace Nominatim;
-
-require_once(CONST_LibDir.'/output.php');
-
-class OutputTest extends \PHPUnit\Framework\TestCase
-{
- public function testDetailsPermaLinkNode()
- {
- $aFeature = array('osm_type' => 'N', 'osm_id'=> 38274, 'class' => 'place');
- $this->assertSame(
- detailsPermaLink($aFeature),
- '<a href="details.php?osmtype=N&osmid=38274&class=place">node 38274</a>'
- );
- }
-
- public function testDetailsPermaLinkWay()
- {
- $aFeature = array('osm_type' => 'W', 'osm_id'=> 65, 'class' => 'highway');
- $this->assertSame(
- detailsPermaLink($aFeature),
- '<a href="details.php?osmtype=W&osmid=65&class=highway">way 65</a>'
- );
- }
-
- public function testDetailsPermaLinkRelation()
- {
- $aFeature = array('osm_type' => 'R', 'osm_id'=> 9908, 'class' => 'waterway');
- $this->assertSame(
- detailsPermaLink($aFeature),
- '<a href="details.php?osmtype=R&osmid=9908&class=waterway">relation 9908</a>'
- );
- }
-
- public function testDetailsPermaLinkTiger()
- {
- $aFeature = array('osm_type' => 'T', 'osm_id'=> 2, 'place_id' => 334);
- $this->assertSame(
- detailsPermaLink($aFeature, 'foo'),
- '<a href="details.php?place_id=334">foo</a>'
- );
- }
-
- public function testDetailsPermaLinkInterpolation()
- {
- $aFeature = array('osm_type' => 'I', 'osm_id'=> 400, 'place_id' => 3);
- $this->assertSame(
- detailsPermaLink($aFeature, 'foo'),
- '<a href="details.php?place_id=3">foo</a>'
- );
- }
-
- public function testDetailsPermaLinkWithExtraPropertiesNode()
- {
- $aFeature = array('osm_type' => 'N', 'osm_id'=> 2, 'class' => 'amenity');
- $this->assertSame(
- detailsPermaLink($aFeature, 'something', 'class="xtype"'),
- '<a class="xtype" href="details.php?osmtype=N&osmid=2&class=amenity">something</a>'
- );
- }
-
- public function testDetailsPermaLinkWithExtraPropertiesTiger()
- {
- $aFeature = array('osm_type' => 'T', 'osm_id'=> 5, 'place_id' => 46);
- $this->assertSame(
- detailsPermaLink($aFeature, 'something', 'class="xtype"'),
- '<a class="xtype" href="details.php?place_id=46">something</a>'
- );
- }
-}