From: Sarah Hoffmann Date: Wed, 9 Mar 2016 22:28:33 +0000 (+0100) Subject: Merge remote-tracking branch 'upstream/master' X-Git-Tag: deploy~431 X-Git-Url: https://git.openstreetmap.org./nominatim.git/commitdiff_plain/fa7730d4af7c330d5443c6ae32f5ab73c48bf7a3?hp=21c590416854b1245866b96772fde61050c0e01a Merge remote-tracking branch 'upstream/master' --- diff --git a/ChangeLog b/ChangeLog index d6002d43..87275f2a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,12 @@ - * reverse geocoding looking includes looking up Tiger data +2.5 + * reverse geocoding includes looking up housenumbers from Tiger data + * added parameter to return simplified geometries + * new lookup call for getting address information for OSM objects + * new namedetails and extratags parameters that expose the name and extratags + fields of the placex table + * mobile website + * reverse web view 2.4 diff --git a/VAGRANT.md b/VAGRANT.md index 3a8e479b..e6a0fbb3 100644 --- a/VAGRANT.md +++ b/VAGRANT.md @@ -30,11 +30,11 @@ is. 1. Start the virtual machine - vagrant up + vagrant up ubuntu 2. Log into the virtual machine - vagrant ssh + vagrant ssh ubuntu 3. Import a small country (Monaco) @@ -54,7 +54,7 @@ is. To repeat an import you'd need to delete the database first - dropdb --username postgres -if-exists nominatim + dropdb -if-exists nominatim @@ -131,9 +131,11 @@ bug fixes) get added since those usually only get applied to new/changed data. Also this document skips the optional Wikipedia data import which affects ranking of search results. See [Nominatim installation](http://wiki.openstreetmap.org/wiki/Nominatim/Installation) for details. -##### Why Ubuntu, can I test CentOS/CoreOS/FreeBSD? +##### Why Ubuntu and CentOS, can I test CentOS/CoreOS/FreeBSD? -In general Nominatim will run in all these environment. The installation steps +There is a Vagrant script for CentOS available. Simply start your box +with `vagrant up centos` and then log in with `vagrant ssh centos`. +In general Nominatim will also run in the other environments. The installation steps are slightly different, e.g. the name of the package manager, Apache2 package name, location of files. We chose Ubuntu because that is closest to the nominatim.openstreetmap.org production environment. diff --git a/Vagrantfile b/Vagrantfile index 843dd668..30bc9375 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -2,30 +2,28 @@ # vi: set ft=ruby : Vagrant.configure("2") do |config| - - # Ubuntu LTS 14.04 - config.vm.box = "ubuntu/trusty64" - # Apache webserver config.vm.network "forwarded_port", guest: 8089, host: 8089 - # If true, then any SSH connections made will enable agent forwarding. config.ssh.forward_agent = true config.vm.synced_folder ".", "/home/vagrant/Nominatim" - - - # provision using a simple shell script - config.vm.provision :shell, :path => "vagrant-provision.sh" - + config.vm.define "ubuntu" do |sub| + sub.vm.box = "ubuntu/trusty64" + sub.vm.provision :shell, :path => "vagrant/ubuntu-trusty-provision.sh" + end + config.vm.define "centos" do |sub| + sub.vm.box = "bento/centos-7.2" + sub.vm.provision :shell, :path => "vagrant/centos-7-provision.sh" + end # configure shared package cache if possible - if Vagrant.has_plugin?("vagrant-cachier") - config.cache.enable :apt - config.cache.scope = :box - end + #if Vagrant.has_plugin?("vagrant-cachier") + # config.cache.enable :apt + # config.cache.scope = :box + #end config.vm.provider "virtualbox" do |vb| diff --git a/lib/Geocode.php b/lib/Geocode.php index afeaf687..48055ec6 100644 --- a/lib/Geocode.php +++ b/lib/Geocode.php @@ -1,4 +1,6 @@ + Calculate all searches using aValidTokens i.e. + 'Wodsworth Road, Sheffield' => - Phrase Wordset - 0 0 (wodsworth road) - 0 1 (wodsworth)(road) - 1 0 (sheffield) + Phrase Wordset + 0 0 (wodsworth road) + 0 1 (wodsworth)(road) + 1 0 (sheffield) - Score how good the search is so they can be ordered + Score how good the search is so they can be ordered */ foreach($aPhrases as $iPhrase => $sPhrase) { @@ -757,32 +759,32 @@ /* Perform the actual query lookup. Returns an ordered list of results, each with the following fields: - osm_type: type of corresponding OSM object + osm_type: type of corresponding OSM object N - node W - way R - relation P - postcode (internally computed) - osm_id: id of corresponding OSM object - class: general object class (corresponds to tag key of primary OSM tag) - type: subclass of object (corresponds to tag value of primary OSM tag) - admin_level: see http://wiki.openstreetmap.org/wiki/Admin_level - rank_search: rank in search hierarchy + osm_id: id of corresponding OSM object + class: general object class (corresponds to tag key of primary OSM tag) + type: subclass of object (corresponds to tag value of primary OSM tag) + admin_level: see http://wiki.openstreetmap.org/wiki/Admin_level + rank_search: rank in search hierarchy (see also http://wiki.openstreetmap.org/wiki/Nominatim/Development_overview#Country_to_street_level) - rank_address: rank in address hierarchy (determines orer in address) - place_id: internal key (may differ between different instances) - country_code: ISO country code - langaddress: localized full address - placename: localized name of object - ref: content of ref tag (if available) - lon: longitude - lat: latitude - importance: importance of place based on Wikipedia link count - addressimportance: cumulated importance of address elements - extra_place: type of place (for admin boundaries, if there is a place tag) - aBoundingBox: bounding Box - label: short description of the object class/type (English only) - name: full name (currently the same as langaddress) - foundorder: secondary ordering for places with same importance + rank_address: rank in address hierarchy (determines orer in address) + place_id: internal key (may differ between different instances) + country_code: ISO country code + langaddress: localized full address + placename: localized name of object + ref: content of ref tag (if available) + lon: longitude + lat: latitude + importance: importance of place based on Wikipedia link count + addressimportance: cumulated importance of address elements + extra_place: type of place (for admin boundaries, if there is a place tag) + aBoundingBox: bounding Box + label: short description of the object class/type (English only) + name: full name (currently the same as langaddress) + foundorder: secondary ordering for places with same importance */ function lookup() { @@ -864,9 +866,23 @@ { // Start with a blank search $aSearches = array( - array('iSearchRank' => 0, 'iNamePhrase' => -1, 'sCountryCode' => false, 'aName'=>array(), 'aAddress'=>array(), 'aFullNameAddress'=>array(), - 'aNameNonSearch'=>array(), 'aAddressNonSearch'=>array(), - 'sOperator'=>'', 'aFeatureName' => array(), 'sClass'=>'', 'sType'=>'', 'sHouseNumber'=>'', 'fLat'=>'', 'fLon'=>'', 'fRadius'=>'') + array('iSearchRank' => 0, + 'iNamePhrase' => -1, + 'sCountryCode' => false, + 'aName' => array(), + 'aAddress' => array(), + 'aFullNameAddress' => array(), + 'aNameNonSearch' => array(), + 'aAddressNonSearch' => array(), + 'sOperator' => '', + 'aFeatureName' => array(), + 'sClass' => '', + 'sType' => '', + 'sHouseNumber' => '', + 'fLat' => '', + 'fLon' => '', + 'fRadius' => '' + ) ); // Do we have a radius search? @@ -1196,8 +1212,8 @@ { $sSQL = "select place_id from placex where calculated_country_code='".$aSearch['sCountryCode']."' and rank_search = 4"; if ($sCountryCodesSQL) $sSQL .= " and calculated_country_code in ($sCountryCodesSQL)"; - if ($bBoundingBoxSearch) - $sSQL .= " and _st_intersects($this->sViewboxSmallSQL, geometry)"; + if ($bBoundingBoxSearch) + $sSQL .= " and _st_intersects($this->sViewboxSmallSQL, geometry)"; $sSQL .= " order by st_area(geometry) desc limit 1"; if (CONST_Debug) var_dump($sSQL); $aPlaceIDs = $this->oDB->getCol($sSQL); @@ -1620,113 +1636,23 @@ if (!preg_match('/\pL/', $sWord)) unset($aRecheckWords[$i]); } - if (CONST_Debug) { echo 'Recheck words:<\i>'; var_dump($aRecheckWords); } + if (CONST_Debug) { echo 'Recheck words:<\i>'; var_dump($aRecheckWords); } foreach($aSearchResults as $iResNum => $aResult) { // Default - $fDiameter = 0.0001; - - if (isset($aClassType[$aResult['class'].':'.$aResult['type'].':'.$aResult['admin_level']]['defdiameter']) - && $aClassType[$aResult['class'].':'.$aResult['type'].':'.$aResult['admin_level']]['defdiameter']) - { - $fDiameter = $aClassType[$aResult['class'].':'.$aResult['type'].':'.$aResult['admin_level']]['defdiameter']; - } - elseif (isset($aClassType[$aResult['class'].':'.$aResult['type']]['defdiameter']) - && $aClassType[$aResult['class'].':'.$aResult['type']]['defdiameter']) - { - $fDiameter = $aClassType[$aResult['class'].':'.$aResult['type']]['defdiameter']; - } - $fRadius = $fDiameter / 2; - - if (CONST_Search_AreaPolygons) - { - // Get the bounding box and outline polygon - $sSQL = "select place_id,0 as numfeatures,st_area(geometry) as area,"; - $sSQL .= "ST_Y(centroid) as centrelat,ST_X(centroid) as centrelon,"; - $sSQL .= "ST_YMin(geometry) as minlat,ST_YMax(geometry) as maxlat,"; - $sSQL .= "ST_XMin(geometry) as minlon,ST_XMax(geometry) as maxlon"; - if ($this->bIncludePolygonAsGeoJSON) $sSQL .= ",ST_AsGeoJSON(geometry) as asgeojson"; - if ($this->bIncludePolygonAsKML) $sSQL .= ",ST_AsKML(geometry) as askml"; - if ($this->bIncludePolygonAsSVG) $sSQL .= ",ST_AsSVG(geometry) as assvg"; - if ($this->bIncludePolygonAsText || $this->bIncludePolygonAsPoints) $sSQL .= ",ST_AsText(geometry) as astext"; - $sFrom = " from placex where place_id = ".$aResult['place_id']; - if ($this->fPolygonSimplificationThreshold > 0) - { - $sSQL .= " from (select place_id,centroid,ST_SimplifyPreserveTopology(geometry,".$this->fPolygonSimplificationThreshold.") as geometry".$sFrom.") as plx"; - } - else - { - $sSQL .= $sFrom; - } + $fDiameter = getResultDiameter($aResult); - $aPointPolygon = $this->oDB->getRow($sSQL); - if (PEAR::IsError($aPointPolygon)) - { - failInternalError("Could not get outline.", $sSQL, $aPointPolygon); - } + $oPlaceLookup = new PlaceLookup($this->oDB); + $oPlaceLookup->setIncludePolygonAsPoints($this->bIncludePolygonAsPoints); + $oPlaceLookup->setIncludePolygonAsText($this->bIncludePolygonAsText); + $oPlaceLookup->setIncludePolygonAsGeoJSON($this->bIncludePolygonAsGeoJSON); + $oPlaceLookup->setIncludePolygonAsKML($this->bIncludePolygonAsKML); + $oPlaceLookup->setIncludePolygonAsSVG($this->bIncludePolygonAsSVG); + $oPlaceLookup->setPolygonSimplificationThreshold($this->fPolygonSimplificationThreshold); - if ($aPointPolygon['place_id']) - { - if ($this->bIncludePolygonAsGeoJSON) $aResult['asgeojson'] = $aPointPolygon['asgeojson']; - if ($this->bIncludePolygonAsKML) $aResult['askml'] = $aPointPolygon['askml']; - if ($this->bIncludePolygonAsSVG) $aResult['assvg'] = $aPointPolygon['assvg']; - if ($this->bIncludePolygonAsText) $aResult['astext'] = $aPointPolygon['astext']; - - if ($aPointPolygon['centrelon'] !== null && $aPointPolygon['centrelat'] !== null ) - { - $aResult['lat'] = $aPointPolygon['centrelat']; - $aResult['lon'] = $aPointPolygon['centrelon']; - } - - if ($this->bIncludePolygonAsPoints) - { - // Translate geometry string to point array - if (preg_match('#POLYGON\\(\\(([- 0-9.,]+)#',$aPointPolygon['astext'],$aMatch)) - { - preg_match_all('/(-?[0-9.]+) (-?[0-9.]+)/',$aMatch[1],$aPolyPoints,PREG_SET_ORDER); - } - /* - elseif (preg_match('#MULTIPOLYGON\\(\\(\\(([- 0-9.,]+)#',$aPointPolygon['astext'],$aMatch)) - { - preg_match_all('/(-?[0-9.]+) (-?[0-9.]+)/',$aMatch[1],$aPolyPoints,PREG_SET_ORDER); - } - */ - elseif (preg_match('#POINT\\((-?[0-9.]+) (-?[0-9.]+)\\)#',$aPointPolygon['astext'],$aMatch)) - { - $iSteps = max(8, min(100, ($fRadius * 40000)^2)); - $fStepSize = (2*pi())/$iSteps; - $aPolyPoints = array(); - for($f = 0; $f < 2*pi(); $f += $fStepSize) - { - $aPolyPoints[] = array('',$aMatch[1]+($fRadius*sin($f)),$aMatch[2]+($fRadius*cos($f))); - } - } - } - - // Output data suitable for display (points and a bounding box) - if ($this->bIncludePolygonAsPoints && isset($aPolyPoints)) - { - $aResult['aPolyPoints'] = array(); - foreach($aPolyPoints as $aPoint) - { - $aResult['aPolyPoints'][] = array($aPoint[1], $aPoint[2]); - } - } - - if (abs($aPointPolygon['minlat'] - $aPointPolygon['maxlat']) < 0.0000001) - { - $aPointPolygon['minlat'] = $aPointPolygon['minlat'] - $fRadius; - $aPointPolygon['maxlat'] = $aPointPolygon['maxlat'] + $fRadius; - } - if (abs($aPointPolygon['minlon'] - $aPointPolygon['maxlon']) < 0.0000001) - { - $aPointPolygon['minlon'] = $aPointPolygon['minlon'] - $fRadius; - $aPointPolygon['maxlon'] = $aPointPolygon['maxlon'] + $fRadius; - } - $aResult['aBoundingBox'] = array((string)$aPointPolygon['minlat'],(string)$aPointPolygon['maxlat'],(string)$aPointPolygon['minlon'],(string)$aPointPolygon['maxlon']); - } - } + $aOutlineResult = $oPlaceLookup->getOutlines($aResult['place_id'], $aResult['lon'], $aResult['lat'], $fDiameter/2); + $aResult = array_merge($aResult, $aOutlineResult); if ($aResult['extra_place'] == 'city') { @@ -1735,32 +1661,6 @@ $aResult['rank_search'] = 16; } - if (!isset($aResult['aBoundingBox'])) - { - $iSteps = max(8,min(100,$fRadius * 3.14 * 100000)); - $fStepSize = (2*pi())/$iSteps; - $aPointPolygon['minlat'] = $aResult['lat'] - $fRadius; - $aPointPolygon['maxlat'] = $aResult['lat'] + $fRadius; - $aPointPolygon['minlon'] = $aResult['lon'] - $fRadius; - $aPointPolygon['maxlon'] = $aResult['lon'] + $fRadius; - - // Output data suitable for display (points and a bounding box) - if ($this->bIncludePolygonAsPoints) - { - $aPolyPoints = array(); - for($f = 0; $f < 2*pi(); $f += $fStepSize) - { - $aPolyPoints[] = array('',$aResult['lon']+($fRadius*sin($f)),$aResult['lat']+($fRadius*cos($f))); - } - $aResult['aPolyPoints'] = array(); - foreach($aPolyPoints as $aPoint) - { - $aResult['aPolyPoints'][] = array($aPoint[1], $aPoint[2]); - } - } - $aResult['aBoundingBox'] = array((string)$aPointPolygon['minlat'],(string)$aPointPolygon['maxlat'],(string)$aPointPolygon['minlon'],(string)$aPointPolygon['maxlon']); - } - // Is there an icon set for this type of result? if (isset($aClassType[$aResult['class'].':'.$aResult['type']]['icon']) && $aClassType[$aResult['class'].':'.$aResult['type']]['icon']) diff --git a/lib/PlaceLookup.php b/lib/PlaceLookup.php index c5129fee..04e45d5a 100644 --- a/lib/PlaceLookup.php +++ b/lib/PlaceLookup.php @@ -15,6 +15,14 @@ protected $bNameDetails = false; + protected $bIncludePolygonAsPoints = false; + protected $bIncludePolygonAsText = false; + protected $bIncludePolygonAsGeoJSON = false; + protected $bIncludePolygonAsKML = false; + protected $bIncludePolygonAsSVG = false; + protected $fPolygonSimplificationThreshold = 0.0; + + function PlaceLookup(&$oDB) { $this->oDB =& $oDB; @@ -46,6 +54,48 @@ } } + + function setIncludePolygonAsPoints($b = true) + { + $this->bIncludePolygonAsPoints = $b; + } + + function getIncludePolygonAsPoints() + { + return $this->bIncludePolygonAsPoints; + } + + function setIncludePolygonAsText($b = true) + { + $this->bIncludePolygonAsText = $b; + } + + function getIncludePolygonAsText() + { + return $this->bIncludePolygonAsText; + } + + function setIncludePolygonAsGeoJSON($b = true) + { + $this->bIncludePolygonAsGeoJSON = $b; + } + + function setIncludePolygonAsKML($b = true) + { + $this->bIncludePolygonAsKML = $b; + } + + function setIncludePolygonAsSVG($b = true) + { + $this->bIncludePolygonAsSVG = $b; + } + + function setPolygonSimplificationThreshold($f) + { + $this->fPolygonSimplificationThreshold = $f; + } + + function setPlaceID($iPlaceID) { $this->iPlaceID = $iPlaceID; @@ -219,5 +269,110 @@ return $aAddress; } + + + // returns an array which will contain the keys + // aBoundingBox + // and may also contain one or more of the keys + // asgeojson + // askml + // assvg + // astext + // lat + // lon + function getOutlines($iPlaceID, $fLon=null, $fLat=null, $fRadius=null) + { + + $aOutlineResult = array(); + if (!$iPlaceID) return $aOutlineResult; + + if (CONST_Search_AreaPolygons) + { + // Get the bounding box and outline polygon + $sSQL = "select place_id,0 as numfeatures,st_area(geometry) as area,"; + $sSQL .= "ST_Y(centroid) as centrelat,ST_X(centroid) as centrelon,"; + $sSQL .= "ST_YMin(geometry) as minlat,ST_YMax(geometry) as maxlat,"; + $sSQL .= "ST_XMin(geometry) as minlon,ST_XMax(geometry) as maxlon"; + if ($this->bIncludePolygonAsGeoJSON) $sSQL .= ",ST_AsGeoJSON(geometry) as asgeojson"; + if ($this->bIncludePolygonAsKML) $sSQL .= ",ST_AsKML(geometry) as askml"; + if ($this->bIncludePolygonAsSVG) $sSQL .= ",ST_AsSVG(geometry) as assvg"; + if ($this->bIncludePolygonAsText || $this->bIncludePolygonAsPoints) $sSQL .= ",ST_AsText(geometry) as astext"; + $sFrom = " from placex where place_id = ".$iPlaceID; + if ($this->fPolygonSimplificationThreshold > 0) + { + $sSQL .= " from (select place_id,centroid,ST_SimplifyPreserveTopology(geometry,".$this->fPolygonSimplificationThreshold.") as geometry".$sFrom.") as plx"; + } + else + { + $sSQL .= $sFrom; + } + + $aPointPolygon = $this->oDB->getRow($sSQL); + if (PEAR::IsError($aPointPolygon)) + { + echo var_dump($aPointPolygon); + failInternalError("Could not get outline.", $sSQL, $aPointPolygon); + } + + if ($aPointPolygon['place_id']) + { + if ($aPointPolygon['centrelon'] !== null && $aPointPolygon['centrelat'] !== null ) + { + $aOutlineResult['lat'] = $aPointPolygon['centrelat']; + $aOutlineResult['lon'] = $aPointPolygon['centrelon']; + } + + if ($this->bIncludePolygonAsGeoJSON) $aOutlineResult['asgeojson'] = $aPointPolygon['asgeojson']; + if ($this->bIncludePolygonAsKML) $aOutlineResult['askml'] = $aPointPolygon['askml']; + if ($this->bIncludePolygonAsSVG) $aOutlineResult['assvg'] = $aPointPolygon['assvg']; + if ($this->bIncludePolygonAsText) $aOutlineResult['astext'] = $aPointPolygon['astext']; + if ($this->bIncludePolygonAsPoints) $aOutlineResult['aPolyPoints'] = geometryText2Points($aPointPolygon['astext'], $fRadius); + + + if (abs($aPointPolygon['minlat'] - $aPointPolygon['maxlat']) < 0.0000001) + { + $aPointPolygon['minlat'] = $aPointPolygon['minlat'] - $fRadius; + $aPointPolygon['maxlat'] = $aPointPolygon['maxlat'] + $fRadius; + } + if (abs($aPointPolygon['minlon'] - $aPointPolygon['maxlon']) < 0.0000001) + { + $aPointPolygon['minlon'] = $aPointPolygon['minlon'] - $fRadius; + $aPointPolygon['maxlon'] = $aPointPolygon['maxlon'] + $fRadius; + } + + $aOutlineResult['aBoundingBox'] = array( + (string)$aPointPolygon['minlat'], + (string)$aPointPolygon['maxlat'], + (string)$aPointPolygon['minlon'], + (string)$aPointPolygon['maxlon'] + ); + } + } // CONST_Search_AreaPolygons + + // as a fallback we generate a bounding box without knowing the size of the geometry + if ( (!isset($aOutlineResult['aBoundingBox'])) && isset($fLon) ) + { + + if ($this->bIncludePolygonAsPoints) + { + $sGeometryText = 'POINT('.$fLon.','.$fLat.')'; + $aOutlineResult['aPolyPoints'] = geometryText2Points($sGeometryText, $fRadius); + } + + $aBounds = array(); + $aBounds['minlat'] = $fLat - $fRadius; + $aBounds['maxlat'] = $fLat + $fRadius; + $aBounds['minlon'] = $fLon - $fRadius; + $aBounds['maxlon'] = $fLon + $fRadius; + + $aOutlineResult['aBoundingBox'] = array( + (string)$aBounds['minlat'], + (string)$aBounds['maxlat'], + (string)$aBounds['minlon'], + (string)$aBounds['maxlon'] + ); + } + return $aOutlineResult; + } } ?> diff --git a/lib/ReverseGeocode.php b/lib/ReverseGeocode.php index e40ce6cc..d9652764 100644 --- a/lib/ReverseGeocode.php +++ b/lib/ReverseGeocode.php @@ -9,6 +9,14 @@ protected $aLangPrefOrder = array(); + protected $bIncludePolygonAsPoints = false; + protected $bIncludePolygonAsText = false; + protected $bIncludePolygonAsGeoJSON = false; + protected $bIncludePolygonAsKML = false; + protected $bIncludePolygonAsSVG = false; + protected $fPolygonSimplificationThreshold = 0.0; + + function ReverseGeocode(&$oDB) { $this->oDB =& $oDB; @@ -58,6 +66,48 @@ $this->iMaxRank = (isset($iZoom) && isset($aZoomRank[$iZoom]))?$aZoomRank[$iZoom]:28; } + function setIncludePolygonAsPoints($b = true) + { + $this->bIncludePolygonAsPoints = $b; + } + + function getIncludePolygonAsPoints() + { + return $this->bIncludePolygonAsPoints; + } + + function setIncludePolygonAsText($b = true) + { + $this->bIncludePolygonAsText = $b; + } + + function getIncludePolygonAsText() + { + return $this->bIncludePolygonAsText; + } + + function setIncludePolygonAsGeoJSON($b = true) + { + $this->bIncludePolygonAsGeoJSON = $b; + } + + function setIncludePolygonAsKML($b = true) + { + $this->bIncludePolygonAsKML = $b; + } + + function setIncludePolygonAsSVG($b = true) + { + $this->bIncludePolygonAsSVG = $b; + } + + function setPolygonSimplificationThreshold($f) + { + $this->fPolygonSimplificationThreshold = $f; + } + + // returns { place_id =>, type => '(osm|tiger)' } + // fails if no place was found function lookup() { $sPointSQL = 'ST_SetSRID(ST_Point('.$this->fLon.','.$this->fLat.'),4326)'; @@ -86,7 +136,8 @@ if ($fSearchDiam > 0.008 && $iMaxRank > 22) $iMaxRank = 22; if ($fSearchDiam > 0.001 && $iMaxRank > 26) $iMaxRank = 26; - $sSQL = 'select place_id,parent_place_id,rank_search,calculated_country_code from placex'; + $sSQL = 'select place_id,parent_place_id,rank_search,calculated_country_code'; + $sSQL .= ' FROM placex'; $sSQL .= ' WHERE ST_DWithin('.$sPointSQL.', geometry, '.$fSearchDiam.')'; $sSQL .= ' and rank_search != 28 and rank_search >= '.$iMaxRank; $sSQL .= ' and (name is not null or housenumber is not null)'; @@ -152,7 +203,11 @@ { $iPlaceID = $iParentPlaceID; } - $sSQL = "select address_place_id from place_addressline where place_id = $iPlaceID order by abs(cached_rank_address - $iMaxRank) asc,cached_rank_address desc,isaddress desc,distance desc limit 1"; + $sSQL = 'select address_place_id'; + $sSQL .= ' FROM place_addressline'; + $sSQL .= " WHERE place_id = $iPlaceID"; + $sSQL .= " ORDER BY abs(cached_rank_address - $iMaxRank) asc,cached_rank_address desc,isaddress desc,distance desc"; + $sSQL .= ' LIMIT 1'; $iPlaceID = $this->oDB->getOne($sSQL); if (PEAR::IsError($iPlaceID)) { @@ -165,7 +220,8 @@ } return array('place_id' => $iPlaceID, - 'type' => $bPlaceIsTiger ? 'tiger' : 'osm'); + 'type' => $bPlaceIsTiger ? 'tiger' : 'osm'); } + } ?> diff --git a/lib/init-website.php b/lib/init-website.php index 013aee4b..03e26965 100644 --- a/lib/init-website.php +++ b/lib/init-website.php @@ -13,4 +13,4 @@ } if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') exit; - header('Content-type: text/html; charset=utf-8'); + header('Content-type: text/html; charset=utf-8'); diff --git a/lib/leakybucket.php b/lib/leakybucket.php index 6d4e8f29..47e84477 100644 --- a/lib/leakybucket.php +++ b/lib/leakybucket.php @@ -7,39 +7,39 @@ if (!CONST_ConnectionBucket_MemcacheServerAddress) return null; if (!isset($m)) { - $m = new Memcached(); - $m->addServer(CONST_ConnectionBucket_MemcacheServerAddress, CONST_ConnectionBucket_MemcacheServerPort); + $m = new Memcached(); + $m->addServer(CONST_ConnectionBucket_MemcacheServerAddress, CONST_ConnectionBucket_MemcacheServerPort); } return $m; } function doBucket($asKey, $iRequestCost, $iLeakPerSecond, $iThreshold) { - $m = getBucketMemcache(); + $m = getBucketMemcache(); if (!$m) return 0; $iMaxVal = 0; - $t = time(); + $t = time(); foreach($asKey as $sKey) { - $aCurrentBlock = $m->get($sKey); - if (!$aCurrentBlock) + $aCurrentBlock = $m->get($sKey); + if (!$aCurrentBlock) { - $aCurrentBlock = array($iRequestCost, $t, false); - } + $aCurrentBlock = array($iRequestCost, $t, false); + } else { // add RequestCost // remove leak * the time since the last request - $aCurrentBlock[0] += $iRequestCost - ($t - $aCurrentBlock[1])*$iLeakPerSecond; - $aCurrentBlock[1] = $t; + $aCurrentBlock[0] += $iRequestCost - ($t - $aCurrentBlock[1])*$iLeakPerSecond; + $aCurrentBlock[1] = $t; } - if ($aCurrentBlock[0] <= 0) + if ($aCurrentBlock[0] <= 0) { - $m->delete($sKey); - } + $m->delete($sKey); + } else { // If we have hit the threshold stop and record this to the block list @@ -77,7 +77,7 @@ } } // Only keep in memcache until the time it would have expired (to avoid clutering memcache) - $m->set($sKey, $aCurrentBlock, $t + 1 + $aCurrentBlock[0]/$iLeakPerSecond); + $m->set($sKey, $aCurrentBlock, $t + 1 + $aCurrentBlock[0]/$iLeakPerSecond); } // Bucket result in the largest bucket we find @@ -85,16 +85,16 @@ } return $iMaxVal; - } + } function isBucketSleeping($asKey) { - $m = getBucketMemcache(); + $m = getBucketMemcache(); if (!$m) return false; foreach($asKey as $sKey) { - $aCurrentBlock = $m->get($sKey); + $aCurrentBlock = $m->get($sKey); if ($aCurrentBlock[2]) return true; } return false; @@ -102,15 +102,15 @@ function setBucketSleeping($asKey, $bVal) { - $m = getBucketMemcache(); + $m = getBucketMemcache(); if (!$m) return false; $iMaxVal = 0; - $t = time(); + $t = time(); foreach($asKey as $sKey) { - $aCurrentBlock = $m->get($sKey); + $aCurrentBlock = $m->get($sKey); $aCurrentBlock[2] = $bVal; $m->set($sKey, $aCurrentBlock, $t + 1 + $aCurrentBlock[0]/CONST_ConnectionBucket_LeakRate); } @@ -137,9 +137,9 @@ function getBucketBlocks() { - $m = getBucketMemcache(); + $m = getBucketMemcache(); if (!$m) return null; - $t = time(); + $t = time(); $aBlockedList = $m->get('blockedList', null, $hCasToken); if (!$aBlockedList) $aBlockedList = array(); foreach($aBlockedList as $sKey => $aDetails) @@ -161,7 +161,7 @@ function clearBucketBlocks() { - $m = getBucketMemcache(); + $m = getBucketMemcache(); if (!$m) return false; $m->delete('blockedList'); return true; diff --git a/lib/lib.php b/lib/lib.php index d1afedbd..e657daf4 100644 --- a/lib/lib.php +++ b/lib/lib.php @@ -101,7 +101,7 @@ function bySearchRank($a, $b) { if ($a['iSearchRank'] == $b['iSearchRank']) - return strlen($a['sOperator']) + strlen($a['sHouseNumber']) - strlen($b['sOperator']) - strlen($b['sHouseNumber']); + return strlen($a['sOperator']) + strlen($a['sHouseNumber']) - strlen($b['sOperator']) - strlen($b['sHouseNumber']); return ($a['iSearchRank'] < $b['iSearchRank']?-1:1); } @@ -673,6 +673,31 @@ return $aOrders; } + function getResultDiameter($aResult) + { + $aClassType = getClassTypes(); + + $fDiameter = 0.0001; + + if (isset($aResult['class']) + && isset($aResult['type']) + && isset($aResult['admin_level']) + && isset($aClassType[$aResult['class'].':'.$aResult['type'].':'.$aResult['admin_level']]['defdiameter']) + && $aClassType[$aResult['class'].':'.$aResult['type'].':'.$aResult['admin_level']]['defdiameter']) + { + $fDiameter = $aClassType[$aResult['class'].':'.$aResult['type'].':'.$aResult['admin_level']]['defdiameter']; + } + elseif (isset($aResult['class']) + && isset($aResult['type']) + && isset($aClassType[$aResult['class'].':'.$aResult['type']]['defdiameter']) + && $aClassType[$aResult['class'].':'.$aResult['type']]['defdiameter']) + { + $fDiameter = $aClassType[$aResult['class'].':'.$aResult['type']]['defdiameter']; + } + + return $fDiameter; + } + function javascript_renderData($xVal, $iOptions = 0) { @@ -1022,3 +1047,49 @@ return array('lat' => $fQueryLat, 'lon' => $fQueryLon, 'query' => $sQuery); } + + + function geometryText2Points($geometry_as_text, $fRadius) + { + $aPolyPoints = NULL; + if (preg_match('#POLYGON\\(\\(([- 0-9.,]+)#', $geometry_as_text, $aMatch)) + { + preg_match_all('/(-?[0-9.]+) (-?[0-9.]+)/', $aMatch[1], $aPolyPoints, PREG_SET_ORDER); + } + elseif (preg_match('#LINESTRING\\(([- 0-9.,]+)#', $geometry_as_text, $aMatch)) + { + preg_match_all('/(-?[0-9.]+) (-?[0-9.]+)/', $aMatch[1], $aPolyPoints, PREG_SET_ORDER); + } +/* elseif (preg_match('#MULTIPOLYGON\\(\\(\\(([- 0-9.,]+)#', $geometry_as_text, $aMatch)) + { + preg_match_all('/(-?[0-9.]+) (-?[0-9.]+)/', $aMatch[1], $aPolyPoints, PREG_SET_ORDER); + }*/ + elseif (preg_match('#POINT\\((-?[0-9.]+) (-?[0-9.]+)\\)#', $geometry_as_text, $aMatch)) + { + $aPolyPoints = createPointsAroundCenter($aMatch[1], $aMatch[2], $fRadius); + } + + if (isset($aPolyPoints)) + { + $aResultPoints = array(); + foreach($aPolyPoints as $aPoint) + { + $aResultPoints[] = array($aPoint[1], $aPoint[2]); + } + return $aResultPoints; + } + + return; + } + + function createPointsAroundCenter($fLon, $fLat, $fRadius) + { + $iSteps = max(8, min(100, ($fRadius * 40000)^2)); + $fStepSize = (2*pi())/$iSteps; + $aPolyPoints = array(); + for($f = 0; $f < 2*pi(); $f += $fStepSize) + { + $aPolyPoints[] = array('', $fLon+($fRadius*sin($f)), $fLat+($fRadius*cos($f)) ); + } + return $aPolyPoints; + } diff --git a/lib/template/address-html.php b/lib/template/address-html.php index f8d425e2..b943c530 100644 --- a/lib/template/address-html.php +++ b/lib/template/address-html.php @@ -1,106 +1,106 @@ - - + + - - - - - -
+ + + + + +
- '; - ?> -
+ '; + ?> +
- + -
-
-
-
+
+
+
+
- + @@ -108,22 +108,22 @@ - - + echo 'var nominatim_results = ' . json_encode([$aPlace], JSON_PRETTY_PRINT) . ';'; + ?> + + diff --git a/lib/template/address-json.php b/lib/template/address-json.php index ff245e14..6fd101bb 100644 --- a/lib/template/address-json.php +++ b/lib/template/address-json.php @@ -13,17 +13,49 @@ if (isset($aPlace['place_id'])) $aFilteredPlaces['place_id'] = $aPlace['place_id']; $aFilteredPlaces['licence'] = "Data © OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright"; $sOSMType = ($aPlace['osm_type'] == 'N'?'node':($aPlace['osm_type'] == 'W'?'way':($aPlace['osm_type'] == 'R'?'relation':''))); - if ($sOSMType) - { - $aFilteredPlaces['osm_type'] = $sOSMType; - $aFilteredPlaces['osm_id'] = $aPlace['osm_id']; - } - if (isset($aPlace['lat'])) $aFilteredPlaces['lat'] = $aPlace['lat']; - if (isset($aPlace['lon'])) $aFilteredPlaces['lon'] = $aPlace['lon']; + if ($sOSMType) + { + $aFilteredPlaces['osm_type'] = $sOSMType; + $aFilteredPlaces['osm_id'] = $aPlace['osm_id']; + } + if (isset($aPlace['lat'])) $aFilteredPlaces['lat'] = $aPlace['lat']; + if (isset($aPlace['lon'])) $aFilteredPlaces['lon'] = $aPlace['lon']; $aFilteredPlaces['display_name'] = $aPlace['langaddress']; if (isset($aPlace['aAddress'])) $aFilteredPlaces['address'] = $aPlace['aAddress']; if (isset($aPlace['sExtraTags'])) $aFilteredPlaces['extratags'] = $aPlace['sExtraTags']; if (isset($aPlace['sNameDetails'])) $aFilteredPlaces['namedetails'] = $aPlace['sNameDetails']; + + if (isset($aPlace['aBoundingBox'])) + { + $aFilteredPlaces['boundingbox'] = $aPlace['aBoundingBox']; + + if (isset($aPlace['aPolyPoints']) && $bAsPoints) + { + $aFilteredPlaces['polygonpoints'] = $aPlace['aPolyPoints']; + } + } + + if (isset($aPlace['asgeojson'])) + { + $aFilteredPlaces['geojson'] = json_decode($aPlace['asgeojson']); + } + + if (isset($aPlace['assvg'])) + { + $aFilteredPlaces['svg'] = $aPlace['assvg']; + } + + if (isset($aPlace['astext'])) + { + $aFilteredPlaces['geotext'] = $aPlace['astext']; + } + + if (isset($aPlace['askml'])) + { + $aFilteredPlaces['geokml'] = $aPlace['askml']; + } + + } javascript_renderData($aFilteredPlaces); diff --git a/lib/template/address-jsonv2.php b/lib/template/address-jsonv2.php index a8a05dd4..89403875 100644 --- a/lib/template/address-jsonv2.php +++ b/lib/template/address-jsonv2.php @@ -13,11 +13,11 @@ if ($aPlace['place_id']) $aFilteredPlaces['place_id'] = $aPlace['place_id']; $aFilteredPlaces['licence'] = "Data © OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright"; $sOSMType = ($aPlace['osm_type'] == 'N'?'node':($aPlace['osm_type'] == 'W'?'way':($aPlace['osm_type'] == 'R'?'relation':''))); - if ($sOSMType) - { - $aFilteredPlaces['osm_type'] = $sOSMType; - $aFilteredPlaces['osm_id'] = $aPlace['osm_id']; - } + if ($sOSMType) + { + $aFilteredPlaces['osm_type'] = $sOSMType; + $aFilteredPlaces['osm_id'] = $aPlace['osm_id']; + } if (isset($aPlace['lat'])) $aFilteredPlaces['lat'] = $aPlace['lat']; if (isset($aPlace['lon'])) $aFilteredPlaces['lon'] = $aPlace['lon']; @@ -36,6 +36,37 @@ if (isset($aPlace['aAddress'])) $aFilteredPlaces['address'] = $aPlace['aAddress']; if (isset($aPlace['sExtraTags'])) $aFilteredPlaces['extratags'] = $aPlace['sExtraTags']; if (isset($aPlace['sNameDetails'])) $aFilteredPlaces['namedetails'] = $aPlace['sNameDetails']; + + if (isset($aPlace['aBoundingBox'])) + { + $aFilteredPlaces['boundingbox'] = $aPlace['aBoundingBox']; + + if (isset($aPlace['aPolyPoints']) && $bAsPoints) + { + $aFilteredPlaces['polygonpoints'] = $aPlace['aPolyPoints']; + } + } + + if (isset($aPlace['asgeojson'])) + { + $aFilteredPlaces['geojson'] = json_decode($aPlace['asgeojson']); + } + + if (isset($aPlace['assvg'])) + { + $aFilteredPlaces['svg'] = $aPlace['assvg']; + } + + if (isset($aPlace['astext'])) + { + $aFilteredPlaces['geotext'] = $aPlace['astext']; + } + + if (isset($aPlace['askml'])) + { + $aFilteredPlaces['geokml'] = $aPlace['askml']; + } + } javascript_renderData($aFilteredPlaces); diff --git a/lib/template/address-xml.php b/lib/template/address-xml.php index 39d9a147..109e66a1 100644 --- a/lib/template/address-xml.php +++ b/lib/template/address-xml.php @@ -27,6 +27,40 @@ if ($aPlace['ref']) echo ' ref="'.htmlspecialchars($aPlace['ref']).'"'; if (isset($aPlace['lat'])) echo ' lat="'.htmlspecialchars($aPlace['lat']).'"'; if (isset($aPlace['lon'])) echo ' lon="'.htmlspecialchars($aPlace['lon']).'"'; + if (isset($aPlace['aBoundingBox'])) + { + echo ' boundingbox="'; + echo join(',', $aPlace['aBoundingBox']); + echo '"'; + + if ($bAsPoints && isset($aPlace['aPolyPoints'])) + { + echo ' polygonpoints=\''; + echo json_encode($aPlace['aPolyPoints']); + echo '\''; + } + } + + if (isset($aPlace['asgeojson'])) + { + echo ' geojson=\''; + echo $aPlace['asgeojson']; + echo '\''; + } + + if (isset($aPlace['assvg'])) + { + echo ' geosvg=\''; + echo $aPlace['assvg']; + echo '\''; + } + + if (isset($aPlace['astext'])) + { + echo ' geotext=\''; + echo $aPlace['astext']; + echo '\''; + } echo ">".htmlspecialchars($aPlace['langaddress']).""; if (isset($aPlace['aAddress'])) @@ -63,6 +97,14 @@ } echo ""; } + + if (isset($aPlace['askml'])) + { + echo "\n"; + echo $aPlace['askml']; + echo ""; + } + } echo ""; diff --git a/lib/template/details-html.php b/lib/template/details-html.php index 6845cbb2..3e9e39d7 100644 --- a/lib/template/details-html.php +++ b/lib/template/details-html.php @@ -113,6 +113,19 @@ echo "\n"; } + function _one_keyword_row($keyword_token,$word_id){ + echo "\n"; + echo ''; + // mark partial tokens (those starting with a space) with a star for readability + echo ($keyword_token[0]==' '?'*':''); + echo $keyword_token; + if (isset($word_id)) + { + echo 'word id: '.$word_id; + } + echo "\n"; + } + ?> @@ -209,7 +222,7 @@ headline('Name Keywords'); foreach($aPlaceSearchNameKeywords as $aRow) { - echo '
'.$aRow['word_token']."
\n"; + _one_keyword_row($aRow['word_token'], $aRow['word_id']); } } @@ -218,7 +231,7 @@ headline('Address Keywords'); foreach($aPlaceSearchAddressKeywords as $aRow) { - echo '
'.($aRow['word_token'][0]==' '?'*':'').$aRow['word_token'].'('.$aRow['word_id'].')'."
\n"; + _one_keyword_row($aRow['word_token'], $aRow['word_id']); } } @@ -264,21 +277,21 @@ diff --git a/lib/template/includes/html-footer.php b/lib/template/includes/html-footer.php index 3083c11b..3d73b1e4 100644 --- a/lib/template/includes/html-footer.php +++ b/lib/template/includes/html-footer.php @@ -1,10 +1,10 @@
-

- Addresses and postcodes are approximate -

- +

+ Addresses and postcodes are approximate +

+
diff --git a/lib/template/includes/html-top-navigation.php b/lib/template/includes/html-top-navigation.php index e0b367b4..d72fcb7c 100644 --- a/lib/template/includes/html-top-navigation.php +++ b/lib/template/includes/html-top-navigation.php @@ -1,49 +1,49 @@ -
-
- -
- - Data last updated: -
- - -
-
-
- - -
-
-
-
+
+
+ +
+ + Data last updated: +
+ + +
+
+
+ + +
+
+
+
- + diff --git a/lib/template/search-batch-json.php b/lib/template/search-batch-json.php index ef2c143a..edfe3871 100644 --- a/lib/template/search-batch-json.php +++ b/lib/template/search-batch-json.php @@ -11,8 +11,8 @@ foreach($aSearchResults as $iResNum => $aPointDetails) { $aPlace = array( - 'place_id'=>$aPointDetails['place_id'], - ); + 'place_id'=>$aPointDetails['place_id'], + ); $sOSMType = ($aPointDetails['osm_type'] == 'N'?'node':($aPointDetails['osm_type'] == 'W'?'way':($aPointDetails['osm_type'] == 'R'?'relation':''))); if ($sOSMType) @@ -21,8 +21,8 @@ $aPlace['osm_id'] = $aPointDetails['osm_id']; } - if (isset($aPointDetails['aBoundingBox'])) - { + if (isset($aPointDetails['aBoundingBox'])) + { $aPlace['boundingbox'] = array( $aPointDetails['aBoundingBox'][0], $aPointDetails['aBoundingBox'][1], @@ -33,7 +33,7 @@ { $aPlace['polygonpoints'] = $aPointDetails['aPolyPoints']; } - } + } if (isset($aPointDetails['zoom'])) { @@ -58,27 +58,27 @@ if (isset($aPointDetails['address']) && sizeof($aPointDetails['address'])>0) { $aPlace['address'] = $aPointDetails['address']; - } + } - if (isset($aPointDetails['asgeojson'])) - { + if (isset($aPointDetails['asgeojson'])) + { $aPlace['geojson'] = json_decode($aPointDetails['asgeojson']); - } + } - if (isset($aPointDetails['assvg'])) - { + if (isset($aPointDetails['assvg'])) + { $aPlace['svg'] = $aPointDetails['assvg']; - } + } - if (isset($aPointDetails['astext'])) - { - $aPlace['geotext'] = $aPointDetails['astext']; - } + if (isset($aPointDetails['astext'])) + { + $aPlace['geotext'] = $aPointDetails['astext']; + } - if (isset($aPointDetails['askml'])) - { - $aPlace['geokml'] = $aPointDetails['askml']; - } + if (isset($aPointDetails['askml'])) + { + $aPlace['geokml'] = $aPointDetails['askml']; + } $aFilteredPlaces[] = $aPlace; } diff --git a/lib/template/search-json.php b/lib/template/search-json.php index 3dcaabdb..fc50ee0e 100644 --- a/lib/template/search-json.php +++ b/lib/template/search-json.php @@ -16,19 +16,15 @@ $aPlace['osm_id'] = $aPointDetails['osm_id']; } - if (isset($aPointDetails['aBoundingBox'])) - { - $aPlace['boundingbox'] = array( - $aPointDetails['aBoundingBox'][0], - $aPointDetails['aBoundingBox'][1], - $aPointDetails['aBoundingBox'][2], - $aPointDetails['aBoundingBox'][3]); + if (isset($aPointDetails['aBoundingBox'])) + { + $aPlace['boundingbox'] = $aPointDetails['aBoundingBox']; if (isset($aPointDetails['aPolyPoints']) && $bShowPolygons) { $aPlace['polygonpoints'] = $aPointDetails['aPolyPoints']; } - } + } if (isset($aPointDetails['zoom'])) { @@ -52,22 +48,22 @@ if (isset($aPointDetails['address'])) { $aPlace['address'] = $aPointDetails['address']; - } - - if (isset($aPointDetails['asgeojson'])) - { - $aPlace['geojson'] = json_decode($aPointDetails['asgeojson']); - } - - if (isset($aPointDetails['assvg'])) - { - $aPlace['svg'] = $aPointDetails['assvg']; - } - - if (isset($aPointDetails['astext'])) - { - $aPlace['geotext'] = $aPointDetails['astext']; - } + } + + if (isset($aPointDetails['asgeojson'])) + { + $aPlace['geojson'] = json_decode($aPointDetails['asgeojson']); + } + + if (isset($aPointDetails['assvg'])) + { + $aPlace['svg'] = $aPointDetails['assvg']; + } + + if (isset($aPointDetails['astext'])) + { + $aPlace['geotext'] = $aPointDetails['astext']; + } if (isset($aPointDetails['askml'])) { diff --git a/lib/template/search-jsonv2.php b/lib/template/search-jsonv2.php index e9747720..773cfd96 100644 --- a/lib/template/search-jsonv2.php +++ b/lib/template/search-jsonv2.php @@ -14,19 +14,15 @@ $aPlace['osm_id'] = $aPointDetails['osm_id']; } - if (isset($aPointDetails['aBoundingBox'])) - { - $aPlace['boundingbox'] = array( - $aPointDetails['aBoundingBox'][0], - $aPointDetails['aBoundingBox'][1], - $aPointDetails['aBoundingBox'][2], - $aPointDetails['aBoundingBox'][3]); + if (isset($aPointDetails['aBoundingBox'])) + { + $aPlace['boundingbox'] = $aPointDetails['aBoundingBox']; if (isset($aPointDetails['aPolyPoints']) && $bShowPolygons) { $aPlace['polygonpoints'] = $aPointDetails['aPolyPoints']; } - } + } if (isset($aPointDetails['zoom'])) { @@ -51,27 +47,27 @@ if (isset($aPointDetails['address']) && sizeof($aPointDetails['address'])>0) { $aPlace['address'] = $aPointDetails['address']; - } + } - if (isset($aPointDetails['asgeojson'])) - { + if (isset($aPointDetails['asgeojson'])) + { $aPlace['geojson'] = json_decode($aPointDetails['asgeojson']); - } + } - if (isset($aPointDetails['assvg'])) - { + if (isset($aPointDetails['assvg'])) + { $aPlace['svg'] = $aPointDetails['assvg']; - } + } - if (isset($aPointDetails['astext'])) - { - $aPlace['geotext'] = $aPointDetails['astext']; - } + if (isset($aPointDetails['astext'])) + { + $aPlace['geotext'] = $aPointDetails['astext']; + } - if (isset($aPointDetails['askml'])) - { - $aPlace['geokml'] = $aPointDetails['askml']; - } + if (isset($aPointDetails['askml'])) + { + $aPlace['geokml'] = $aPointDetails['askml']; + } if (isset($aPointDetails['sExtraTags'])) $aPlace['extratags'] = $aPointDetails['sExtraTags']; if (isset($aPointDetails['sNameDetails'])) $aPlace['namedetails'] = $aPointDetails['sNameDetails']; diff --git a/lib/template/search-xml.php b/lib/template/search-xml.php index b61ff22f..6a382ebf 100644 --- a/lib/template/search-xml.php +++ b/lib/template/search-xml.php @@ -36,10 +36,7 @@ if (isset($aResult['aBoundingBox'])) { echo ' boundingbox="'; - echo $aResult['aBoundingBox'][0]; - echo ','.$aResult['aBoundingBox'][1]; - echo ','.$aResult['aBoundingBox'][2]; - echo ','.$aResult['aBoundingBox'][3]; + echo join(',',$aResult['aBoundingBox']); echo '"'; if ($bShowPolygons && isset($aResult['aPolyPoints'])) diff --git a/settings/phrase_settings.php b/settings/phrase_settings.php index 4456316e..2e199ada 100644 --- a/settings/phrase_settings.php +++ b/settings/phrase_settings.php @@ -38,14 +38,14 @@ $aLanguageIn = array( # class/type combinations to exclude $aTagsBlacklist = array( - 'boundary' => array('administrative'), - 'place' => array('house', 'houses'), + 'boundary' => array('administrative'), + 'place' => array('house', 'houses'), ); # If a class is in the white list then all types will # be ignored except the ones given in the list. # Also use this list to exclude an entire class from # special phrases. $aTagsWhitelist = array( - 'highway' => array('bus_stop', 'rest_area', 'raceway'), - 'building' => array(), + 'highway' => array('bus_stop', 'rest_area', 'raceway'), + 'building' => array(), ); diff --git a/tests-php/Nominatim/NominatimTest.php b/tests-php/Nominatim/NominatimTest.php index 4c5638b1..d50a8da4 100644 --- a/tests-php/Nominatim/NominatimTest.php +++ b/tests-php/Nominatim/NominatimTest.php @@ -12,6 +12,51 @@ class NominatimTest extends \PHPUnit_Framework_TestCase } + public function test_getClassTypesWithImportance() + { + $aClasses = getClassTypesWithImportance(); + + $this->assertGreaterThan( + 200, + count($aClasses) + ); + + $this->assertEquals( + array( + 'label' => "Country", + 'frequency' => 0, + 'icon' => "poi_boundary_administrative", + 'defzoom' => 6, + 'defdiameter' => 15, + 'importance' => 3 + ), + $aClasses['place:country'] + ); + } + + + public function test_getResultDiameter() + { + $aResult = array(); + $this->assertEquals( + 0.0001, + getResultDiameter($aResult) + ); + + $aResult = array('class' => 'place', 'type' => 'country'); + $this->assertEquals( + 15, + getResultDiameter($aResult) + ); + + $aResult = array('class' => 'boundary', 'type' => 'administrative', 'admin_level' => 6); + $this->assertEquals( + 0.32, + getResultDiameter($aResult) + ); + } + + public function test_addQuotes() { // FIXME: not quoting existing quote signs is probably a bug @@ -142,10 +187,74 @@ class NominatimTest extends \PHPUnit_Framework_TestCase 65536, count( getWordSets(array_fill( 0, 18, 'a'),0) ) ); + } + // you might say we're creating a circle + public function test_createPointsAroundCenter() + { + $aPoints = createPointsAroundCenter(0, 0, 2); + $this->assertEquals( + 101, + count($aPoints) + ); + $this->assertEquals( + array( + ['', 0, 2], + ['', 0.12558103905863, 1.9960534568565], + ['', 0.25066646712861, 1.984229402629] + ), + array_splice($aPoints, 0, 3) + ); } + public function test_geometryText2Points() + { + $fRadius = 1; + // invalid value + $this->assertEquals( + NULL, + geometryText2Points('', $fRadius) + ); + + // POINT + $aPoints = geometryText2Points('POINT(10 20)', $fRadius); + $this->assertEquals( + 101, + count($aPoints) + ); + $this->assertEquals( + array( + [10, 21], + [10.062790519529, 20.998026728428], + [10.125333233564, 20.992114701314] + ), + array_splice($aPoints, 0,3) + ); + + // POLYGON + $this->assertEquals( + array( + ['30', '10'], + ['40', '40'], + ['20', '40'], + ['10', '20'], + ['30', '10'] + ), + geometryText2Points('POLYGON((30 10, 40 40, 20 40, 10 20, 30 10))', $fRadius) + ); + + // MULTIPOLYGON + $this->assertEquals( + array( + ['30', '20'], // first polygon only + ['45', '40'], + ['10', '40'], + ['30', '20'], + ), + geometryText2Points('MULTIPOLYGON(((30 20, 45 40, 10 40, 30 20)),((15 5, 40 10, 10 20, 5 10, 15 5)))', $fRadius) + ); + } } diff --git a/tests/features/api/reverse.feature b/tests/features/api/reverse.feature index fa636acf..39519177 100644 --- a/tests/features/api/reverse.feature +++ b/tests/features/api/reverse.feature @@ -11,6 +11,15 @@ Feature: Reverse geocoding | ID | country | 0 | Deutschland + + Scenario: Boundingbox is returned + Given the request parameters + | format | zoom + | xml | 4 + When looking up coordinates 53.9788769,13.0830313 + And results contain valid boundingboxes + + @Tiger Scenario: TIGER house number Given the request parameters @@ -61,3 +70,75 @@ Feature: Reverse geocoding | xml | json | jsonv2 + + + Scenario Outline: Reverse Geocoding contains TEXT geometry + Given the request parameters + | polygon_text + | 1 + When looking up coordinates 48.86093,2.2978 + Then result 0 has attributes + + Examples: + | format | response_attribute + | xml | geotext + | json | geotext + | jsonv2 | geotext + + Scenario Outline: Reverse Geocoding contains polygon-as-points geometry + Given the request parameters + | polygon + | 1 + When looking up coordinates 48.86093,2.2978 + Then result 0 has not attributes + + Examples: + | format | response_attribute + | xml | polygonpoints + | json | polygonpoints + | jsonv2 | polygonpoints + + + + Scenario Outline: Reverse Geocoding contains SVG geometry + Given the request parameters + | polygon_svg + | 1 + When looking up coordinates 48.86093,2.2978 + Then result 0 has attributes + + Examples: + | format | response_attribute + | xml | geosvg + | json | svg + | jsonv2 | svg + + + Scenario Outline: Reverse Geocoding contains KML geometry + Given the request parameters + | polygon_kml + | 1 + When looking up coordinates 48.86093,2.2978 + Then result 0 has attributes + + Examples: + | format | response_attribute + | xml | geokml + | json | geokml + | jsonv2 | geokml + + + Scenario Outline: Reverse Geocoding contains GEOJSON geometry + Given the request parameters + | polygon_geojson + | 1 + When looking up coordinates 48.86093,2.2978 + Then result 0 has attributes + + Examples: + | format | response_attribute + | xml | geojson + | json | geojson + | jsonv2 | geojson + + diff --git a/tests/features/api/reverse_simple.feature b/tests/features/api/reverse_simple.feature index 8621ec65..5d79fd4b 100644 --- a/tests/features/api/reverse_simple.feature +++ b/tests/features/api/reverse_simple.feature @@ -17,6 +17,49 @@ Feature: Simple Reverse Tests | -79.34 | 23.5 | 0.23 | -178.555 + Scenario Outline: Testing different parameters + Given the request parameters + | + | + When sending search query "Manchester" + Then the result is valid html + Given the request parameters + | + | + When sending html search query "Manchester" + Then the result is valid html + Given the request parameters + | + | + When sending xml search query "Manchester" + Then the result is valid xml + Given the request parameters + | + | + When sending json search query "Manchester" + Then the result is valid json + Given the request parameters + | + | + When sending jsonv2 search query "Manchester" + Then the result is valid json + + Examples: + | parameter | value + | polygon | 1 + | polygon | 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 + + + + Scenario Outline: Wrapping of legal jsonp requests Given the request parameters | json_callback diff --git a/tests/features/api/search_params.feature b/tests/features/api/search_params.feature index a4885a15..34e43328 100644 --- a/tests/features/api/search_params.feature +++ b/tests/features/api/search_params.feature @@ -8,6 +8,7 @@ Feature: Search queries And result 0 has attributes lat,lon,display_name And result 0 has attributes class,type,importance,icon And result 0 has not attributes address + And results contain valid boundingboxes Scenario: Simple JSON search When sending json search query "Vaduz" @@ -15,6 +16,7 @@ Feature: Search queries And result 0 has attributes osm_type,osm_id,boundingbox And result 0 has attributes lat,lon,display_name,importance And result 0 has not attributes address + And results contain valid boundingboxes Scenario: JSON search with addressdetails When sending json search query "Montevideo" with address @@ -230,3 +232,73 @@ Feature: Search queries | xml | json | jsonv2 + + + Scenario Outline: Search result with contains TEXT geometry + Given the request parameters + | polygon_text + | 1 + When sending search query "switzerland" + Then result 0 has attributes + + Examples: + | format | response_attribute + | xml | geotext + | json | geotext + | jsonv2 | geotext + + Scenario Outline: Search result contains polygon-as-points geometry + Given the request parameters + | polygon + | 1 + When sending search query "switzerland" + Then result 0 has attributes + + Examples: + | format | response_attribute + | xml | polygonpoints + | json | polygonpoints + | jsonv2 | polygonpoints + + + + Scenario Outline: Search result contains SVG geometry + Given the request parameters + | polygon_svg + | 1 + When sending search query "switzerland" + Then result 0 has attributes + + Examples: + | format | response_attribute + | xml | geosvg + | json | svg + | jsonv2 | svg + + + Scenario Outline: Search result contains KML geometry + Given the request parameters + | polygon_kml + | 1 + When sending search query "switzerland" + Then result 0 has attributes + + Examples: + | format | response_attribute + | xml | geokml + | json | geokml + | jsonv2 | geokml + + + Scenario Outline: Search result contains GEOJSON geometry + Given the request parameters + | polygon_geojson + | 1 + When sending search query "switzerland" + Then result 0 has attributes + + Examples: + | format | response_attribute + | xml | geojson + | json | geojson + | jsonv2 | geojson diff --git a/tests/steps/api_result.py b/tests/steps/api_result.py index 4910c157..2644d4a2 100644 --- a/tests/steps/api_result.py +++ b/tests/steps/api_result.py @@ -98,6 +98,8 @@ def _parse_xml(): attrs = dict(tag.attributes.items()) assert_in('desc', attrs) world.results[0]['namedetails'][attrs['desc']] = tag.firstChild.nodeValue.strip() + elif node.nodeName == "geokml": + world.results[0]['geokml'] = node elif node.nodeName == "#text": pass else: @@ -209,6 +211,15 @@ def api_result_contains(step): m = re.match("%s$" % (v,), curres[k]) assert_is_not_none(m, msg="field %s does not match: %s$ != %s." % (k, v, curres[k])) +@step(u'results contain valid boundingboxes$') +def api_result_address_contains(step): + step.given('the result is valid') + for curres in world.results: + bb = curres['boundingbox'] + if world.response_format == 'json': + bb = ','.join(bb) + m = re.match('^(-?\d+\.\d+),(-?\d+\.\d+),(-?\d+\.\d+),(-?\d+\.\d+)$', bb) + assert_is_not_none(m, msg="invalid boundingbox: %s." % (curres['boundingbox'])) @step(u'result addresses contain$') def api_result_address_contains(step): diff --git a/vagrant/centos-7-provision.sh b/vagrant/centos-7-provision.sh new file mode 100644 index 00000000..93139a23 --- /dev/null +++ b/vagrant/centos-7-provision.sh @@ -0,0 +1,107 @@ +#!/bin/bash + +# This script sets up a Nominatim installation on a CentOS 7 box. +# +# For more detailed CentOS installation instructions see also +# http://wiki.openstreetmap.org/wiki/Nominatim/Installation_on_CentOS + +## Part 1: System preparation + +## During 'vagrant provision' this script runs as root and the current +## directory is '/root' +USERNAME=vagrant + +yum update -y +yum install -y epel-release + +yum install -y postgresql-server postgresql-contrib postgresql-devel postgis postgis-utils \ + make automake gcc gcc-c++ libtool policycoreutils-python \ + php-pgsql php php-pear php-pear-DB libpqxx-devel proj-epsg \ + bzip2-devel proj-devel geos-devel libxml2-devel boost-devel \ + expat-devel zlib-devel + +# Create a cluster and start up postgresql. +postgresql-setup initdb +systemctl enable postgresql +systemctl start postgresql + +# We leave postgresql in its default configuration here. This is only +# suitable for small extracts. + +# Create the necessary postgres users. +sudo -u postgres createuser -s vagrant +sudo -u postgres createuser apache + +# Create the website directory. +mkdir -m 755 /var/www/html/nominatim +chown vagrant /var/www/html/nominatim + +# Set up the necessary rights on SELinux. +semanage fcontext -a -t httpd_sys_content_t "/home/vagrant/Nominatim/(website|lib|settings)(/.*)?" +semanage fcontext -a -t lib_t "/home/vagrant/Nominatim/module/nominatim.so" +semanage port -a -t http_port_t -p tcp 8089 +restorecon -R -v /home/vagrant/Nominatim + +# Configure apache site. +echo ' +Listen 8089 + + # DirectoryIndex index.html + # ErrorDocument 403 /index.html + + DocumentRoot "/var/www/html/" + + + Options FollowSymLinks MultiViews + AddType text/html .php + + +' | sudo tee /etc/httpd/conf.d/nominatim.conf > /dev/null + +# Restart apache to enable the site configuration. +systemctl enable httpd +systemctl restart httpd + +## Part 2: Nominatim installaion + +# now ideally login as $USERNAME and continue +cd /home/$USERNAME + +# If the Nominatim source is not being shared with the host, check out source. +if [ ! -d "Nominatim" ]; then + yum install -y git + sudo -u $USERNAME git clone --recursive https://github.com/twain47/Nominatim.git +fi + +# Configure and compile the source. +cd Nominatim +sudo -u $USERNAME ./autogen.sh +sudo -u $USERNAME ./configure +sudo -u $USERNAME make + +# Make sure that postgres has access to the nominatim library. +chmod +x /home/$USERNAME +chmod +x ./ +chmod +x ./module + +# Create customized settings suitable for this VM installation. +LOCALSETTINGS_FILE='settings/local.php' +if [[ -e "$LOCALSETTINGS_FILE" ]]; then + echo "$LOCALSETTINGS_FILE already exist, writing to settings/local-vagrant.php instead." + LOCALSETTINGS_FILE='settings/local-vagrant.php' +fi + +IP=localhost +echo " $LOCALSETTINGS_FILE + +# Install the web interface. +sudo -u $USERNAME ./utils/setup.php --create-website /var/www/html/nominatim diff --git a/vagrant-provision.sh b/vagrant/ubuntu-trusty-provision.sh similarity index 81% rename from vagrant-provision.sh rename to vagrant/ubuntu-trusty-provision.sh index 1a770226..84e98e23 100755 --- a/vagrant-provision.sh +++ b/vagrant/ubuntu-trusty-provision.sh @@ -1,5 +1,11 @@ #!/bin/bash +# This script sets up a Nominatim installation on a Ubuntu box. +# +# For more detailed installation instructions see also +# http://wiki.openstreetmap.org/wiki/Nominatim/Installation + +## Part 1: System preparation ## During 'vagrant provision' this script runs as root and the current ## directory is '/root' @@ -75,20 +81,22 @@ echo "date.timezone = 'Etc/UTC'" | sudo tee /etc/php5/cli/conf.d/99-timezone.ini ### sudo apt-get install -y libgeos-c1 libgeos++-dev libxml2-dev +## Part 2: Nominatim installaion + # now ideally login as $USERNAME and continue -su $USERNAME -l -cd /home/vagrant +cd /home/$USERNAME +# If the Nominatim source is not being shared with the host, check out source. if [ ! -d "Nominatim" ]; then sudo apt-get install -y git - git clone --recursive https://github.com/twain47/Nominatim.git + sudo -u $USERNAME git clone --recursive https://github.com/twain47/Nominatim.git fi cd Nominatim -./autogen.sh -./configure -make +sudo -u $USERNAME ./autogen.sh +sudo -u $USERNAME ./configure +sudo -u $USERNAME make chmod +x ./ chmod +x ./module @@ -139,12 +147,12 @@ Listen 8089 ' | sudo tee /etc/apache2/sites-enabled/nominatim.conf > /dev/null -sudo apache2ctl graceful +apache2ctl graceful -sudo mkdir -m 755 /var/www/nominatim -sudo chown $USERNAME /var/www/nominatim -./utils/setup.php --threads 1 --create-website /var/www/nominatim +mkdir -m 755 /var/www/nominatim +chown $USERNAME /var/www/nominatim +sudo -u $USERNAME ./utils/setup.php --create-website /var/www/nominatim # if you get 'permission denied for relation word', then try @@ -155,12 +163,12 @@ sudo chown $USERNAME /var/www/nominatim ## Test suite (Python) ## https://github.com/twain47/Nominatim/tree/master/tests ## -sudo apt-get install -y python-dev python-pip python-Levenshtein python-shapely \ +apt-get install -y python-dev python-pip python-Levenshtein python-shapely \ python-psycopg2 tidy python-nose python-tidylib -sudo pip install lettuce==0.2.18 six==1.7 haversine +pip install lettuce==0.2.18 six==1.7 haversine ## Test suite (PHP) ## https://github.com/twain47/Nominatim/tree/master/tests-php -sudo apt-get install -y phpunit +apt-get install -y phpunit diff --git a/website/deletable.php b/website/deletable.php index 4e2e623c..5d6ee15d 100755 --- a/website/deletable.php +++ b/website/deletable.php @@ -1,6 +1,6 @@ - - - - Nominatim Deleted Data - - + + + + Nominatim Deleted Data + + @@ -79,16 +79,16 @@ table td { { switch($sCol) { - case 'osm_id': - $sOSMType = ($aRow['osm_type'] == 'N'?'node':($aRow['osm_type'] == 'W'?'way':($aRow['osm_type'] == 'R'?'relation':''))); - echo ''.$sVal.''; - break; - case 'place_id': - echo ''.$sVal.''; - break; - default: - echo "".($sVal?$sVal:' ').""; - break; + case 'osm_id': + $sOSMType = ($aRow['osm_type'] == 'N'?'node':($aRow['osm_type'] == 'W'?'way':($aRow['osm_type'] == 'R'?'relation':''))); + echo ''.$sVal.''; + break; + case 'place_id': + echo ''.$sVal.''; + break; + default: + echo "".($sVal?$sVal:' ').""; + break; } } echo ""; diff --git a/website/details.php b/website/details.php index ad753a82..3a0fee82 100755 --- a/website/details.php +++ b/website/details.php @@ -159,7 +159,12 @@ logEnd($oDB, $hLog, 1); - $sTileURL = CONST_Map_Tile_URL; - $sTileAttribution = CONST_Map_Tile_Attribution; + if ($sOutputFormat=='html') + { + $sDataDate = $oDB->getOne("select TO_CHAR(lastimportdate - '2 minutes'::interval,'YYYY/MM/DD HH24:MI')||' GMT' from import_status limit 1"); + $sTileURL = CONST_Map_Tile_URL; + $sTileAttribution = CONST_Map_Tile_Attribution; + } + include(CONST_BasePath.'/lib/template/details-'.$sOutputFormat.'.php'); diff --git a/website/hierarchy.php b/website/hierarchy.php index d99040a2..de83ca44 100755 --- a/website/hierarchy.php +++ b/website/hierarchy.php @@ -78,7 +78,7 @@ $aBreadcrums[] = array('placeId'=>$aPlace['place_id'], 'osmType'=>$aPlace['osm_type'], 'osmId'=>$aPlace['osm_id'], 'localName'=>$aPlace['localname']); $sPlaceUrl = 'hierarchy.php?place_id='.$aPlace['place_id']; $sOSMType = ($aPlace['osm_type'] == 'N'?'node':($aPlace['osm_type'] == 'W'?'way':($aPlace['osm_type'] == 'R'?'relation':''))); - $sOSMUrl = 'http://www.openstreetmap.org/browse/'.$sOSMType.'/'.$aPlace['osm_id']; + $sOSMUrl = 'http://www.openstreetmap.org/browse/'.$sOSMType.'/'.$aPlace['osm_id']; if ($sOutputFormat == 'html') if ($i) echo " > "; if ($sOutputFormat == 'html') echo ''.$aPlace['localname'].' (osm)'; } @@ -102,52 +102,52 @@ $sSQL .= " order by rank_address asc,rank_search asc,localname,class, type,housenumber"; $aParentOfLines = $oDB->getAll($sSQL); - if (sizeof($aParentOfLines)) - { - echo '

Parent Of:

'; + if (sizeof($aParentOfLines)) + { + echo '

Parent Of:

'; $aClassType = getClassTypesWithImportance(); - $aGroupedAddressLines = array(); - foreach($aParentOfLines as $aAddressLine) - { + $aGroupedAddressLines = array(); + foreach($aParentOfLines as $aAddressLine) + { if (isset($aClassType[$aAddressLine['class'].':'.$aAddressLine['type'].':'.$aAddressLine['admin_level']]['label']) - && $aClassType[$aAddressLine['class'].':'.$aAddressLine['type'].':'.$aAddressLine['admin_level']]['label']) - { - $aAddressLine['label'] = $aClassType[$aAddressLine['class'].':'.$aAddressLine['type'].':'.$aAddressLine['admin_level']]['label']; - } + && $aClassType[$aAddressLine['class'].':'.$aAddressLine['type'].':'.$aAddressLine['admin_level']]['label']) + { + $aAddressLine['label'] = $aClassType[$aAddressLine['class'].':'.$aAddressLine['type'].':'.$aAddressLine['admin_level']]['label']; + } elseif (isset($aClassType[$aAddressLine['class'].':'.$aAddressLine['type']]['label']) - && $aClassType[$aAddressLine['class'].':'.$aAddressLine['type']]['label']) - { - $aAddressLine['label'] = $aClassType[$aAddressLine['class'].':'.$aAddressLine['type']]['label']; - } + && $aClassType[$aAddressLine['class'].':'.$aAddressLine['type']]['label']) + { + $aAddressLine['label'] = $aClassType[$aAddressLine['class'].':'.$aAddressLine['type']]['label']; + } else $aAddressLine['label'] = ucwords($aAddressLine['type']); - if (!isset($aGroupedAddressLines[$aAddressLine['label']])) $aGroupedAddressLines[$aAddressLine['label']] = array(); - $aGroupedAddressLines[$aAddressLine['label']][] = $aAddressLine; - } - foreach($aGroupedAddressLines as $sGroupHeading => $aParentOfLines) - { - echo "

$sGroupHeading

"; - foreach($aParentOfLines as $aAddressLine) - { - $aAddressLine['localname'] = $aAddressLine['localname']?$aAddressLine['localname']:$aAddressLine['housenumber']; - $sOSMType = ($aAddressLine['osm_type'] == 'N'?'node':($aAddressLine['osm_type'] == 'W'?'way':($aAddressLine['osm_type'] == 'R'?'relation':''))); - - echo '
'; - echo ''.(trim($aAddressLine['localname'])?$aAddressLine['localname']:'No Name').''; - echo ' ('; - echo ''.($aAddressLine['isarea']=='t'?'Polygon':'Point').''; - if ($sOSMType) echo ', '.$sOSMType.' '.$aAddressLine['osm_id'].''; - echo ', GOTO'; - echo ', '.$aAddressLine['area']; - echo ')'; - echo '
'; - } - } - if (sizeof($aParentOfLines) >= 500) { - echo '

There are more child objects which are not shown.

'; - } - echo ''; - } + if (!isset($aGroupedAddressLines[$aAddressLine['label']])) $aGroupedAddressLines[$aAddressLine['label']] = array(); + $aGroupedAddressLines[$aAddressLine['label']][] = $aAddressLine; + } + foreach($aGroupedAddressLines as $sGroupHeading => $aParentOfLines) + { + echo "

$sGroupHeading

"; + foreach($aParentOfLines as $aAddressLine) + { + $aAddressLine['localname'] = $aAddressLine['localname']?$aAddressLine['localname']:$aAddressLine['housenumber']; + $sOSMType = ($aAddressLine['osm_type'] == 'N'?'node':($aAddressLine['osm_type'] == 'W'?'way':($aAddressLine['osm_type'] == 'R'?'relation':''))); + + echo '
'; + echo ''.(trim($aAddressLine['localname'])?$aAddressLine['localname']:'No Name').''; + echo ' ('; + echo ''.($aAddressLine['isarea']=='t'?'Polygon':'Point').''; + if ($sOSMType) echo ', '.$sOSMType.' '.$aAddressLine['osm_id'].''; + echo ', GOTO'; + echo ', '.$aAddressLine['area']; + echo ')'; + echo '
'; + } + } + if (sizeof($aParentOfLines) >= 500) { + echo '

There are more child objects which are not shown.

'; + } + echo ''; + } exit; diff --git a/website/js/nominatim-ui.js b/website/js/nominatim-ui.js index be108074..3e0aabd2 100644 --- a/website/js/nominatim-ui.js +++ b/website/js/nominatim-ui.js @@ -117,7 +117,7 @@ jQuery(document).on('ready', function(){ var bounds = [[result.aBoundingBox[0]*1,result.aBoundingBox[2]*1], [result.aBoundingBox[1]*1,result.aBoundingBox[3]*1]]; map.fitBounds(bounds); - if (result.astext && result.astext.match(/POLY/) ){ + if (result.astext && result.astext.match(/(POLY)|(LINE)/) ){ var layer = omnivore.wkt.parse(result.astext); layerGroup.addLayer(layer); } diff --git a/website/polygons.php b/website/polygons.php index 5a5be9f0..423374c7 100755 --- a/website/polygons.php +++ b/website/polygons.php @@ -39,12 +39,12 @@ - - - - Nominatim Broken Polygon Data - - + + + + Nominatim Broken Polygon Data + + diff --git a/website/reverse.php b/website/reverse.php index d1603fa1..1f6e0aad 100755 --- a/website/reverse.php +++ b/website/reverse.php @@ -6,6 +6,35 @@ require_once(CONST_BasePath.'/lib/PlaceLookup.php'); require_once(CONST_BasePath.'/lib/ReverseGeocode.php'); + $bAsPoints = false; + $bAsGeoJSON = (boolean)isset($_GET['polygon_geojson']) && $_GET['polygon_geojson']; + $bAsKML = (boolean)isset($_GET['polygon_kml']) && $_GET['polygon_kml']; + $bAsSVG = (boolean)isset($_GET['polygon_svg']) && $_GET['polygon_svg']; + $bAsText = (boolean)isset($_GET['polygon_text']) && $_GET['polygon_text']; + if ( ( ($bAsGeoJSON?1:0) + + ($bAsKML?1:0) + + ($bAsSVG?1:0) + + ($bAsText?1:0) + + ($bAsPoints?1:0) + ) > CONST_PolygonOutput_MaximumTypes) + { + if (CONST_PolygonOutput_MaximumTypes) + { + userError("Select only ".CONST_PolygonOutput_MaximumTypes." polgyon output option"); + } + else + { + userError("Polygon output is disabled"); + } + exit; + } + + + // Polygon simplification threshold (optional) + $fThreshold = 0.0; + if (isset($_GET['polygon_threshold'])) $fThreshold = (float)$_GET['polygon_threshold']; + + $oDB =& getDB(); ini_set('memory_limit', '200M'); @@ -51,6 +80,18 @@ $oPlaceLookup->setIncludeNameDetails(getParamBool('namedetails', false)); $aPlace = $oPlaceLookup->lookupPlace($aLookup); + + $oPlaceLookup->setIncludePolygonAsPoints($bAsPoints); + $oPlaceLookup->setIncludePolygonAsText($bAsText); + $oPlaceLookup->setIncludePolygonAsGeoJSON($bAsGeoJSON); + $oPlaceLookup->setIncludePolygonAsKML($bAsKML); + $oPlaceLookup->setIncludePolygonAsSVG($bAsSVG); + $oPlaceLookup->setPolygonSimplificationThreshold($fThreshold); + + $fRadius = $fDiameter = getResultDiameter($aPlace); + $aOutlineResult = $oPlaceLookup->getOutlines($aPlace['place_id'],$aPlace['lon'],$aPlace['lat'],$fRadius); + + $aPlace = array_merge($aPlace, $aOutlineResult); } else { @@ -65,6 +106,10 @@ exit; } - $sTileURL = CONST_Map_Tile_URL; - $sTileAttribution = CONST_Map_Tile_Attribution; + if ($sOutputFormat=='html') + { + $sDataDate = $oDB->getOne("select TO_CHAR(lastimportdate - '2 minutes'::interval,'YYYY/MM/DD HH24:MI')||' GMT' from import_status limit 1"); + $sTileURL = CONST_Map_Tile_URL; + $sTileAttribution = CONST_Map_Tile_Attribution; + } include(CONST_BasePath.'/lib/template/address-'.$sOutputFormat.'.php'); diff --git a/website/search.php b/website/search.php index f2c327c0..c216c884 100755 --- a/website/search.php +++ b/website/search.php @@ -13,8 +13,6 @@ $fLat = CONST_Default_Lat; $fLon = CONST_Default_Lon; $iZoom = CONST_Default_Zoom; - $sTileURL = CONST_Map_Tile_URL; - $sTileAttribution = CONST_Map_Tile_Attribution; $oGeocode =& new Geocode($oDB); @@ -99,17 +97,17 @@ { if (!(isset($_GET['q']) && $_GET['q']) && isset($_SERVER['PATH_INFO']) && $_SERVER['PATH_INFO'][0] == '/') { - $sQuery = substr(rawurldecode($_SERVER['PATH_INFO']), 1); + $sQuery = substr(rawurldecode($_SERVER['PATH_INFO']), 1); - // reverse order of '/' separated string - $aPhrases = explode('/', $sQuery); - $aPhrases = array_reverse($aPhrases); - $sQuery = join(', ',$aPhrases); - $oGeocode->setQuery($sQuery); + // reverse order of '/' separated string + $aPhrases = explode('/', $sQuery); + $aPhrases = array_reverse($aPhrases); + $sQuery = join(', ',$aPhrases); + $oGeocode->setQuery($sQuery); } else { - $oGeocode->setQueryFromParams($_GET); + $oGeocode->setQueryFromParams($_GET); } } @@ -118,8 +116,12 @@ $aSearchResults = $oGeocode->lookup(); if ($aSearchResults === false) $aSearchResults = array(); - $sDataDate = $oDB->getOne("select TO_CHAR(lastimportdate - '2 minutes'::interval,'YYYY/MM/DD HH24:MI')||' GMT' from import_status limit 1"); - + if ($sOutputFormat=='html') + { + $sDataDate = $oDB->getOne("select TO_CHAR(lastimportdate - '2 minutes'::interval,'YYYY/MM/DD HH24:MI')||' GMT' from import_status limit 1"); + $sTileURL = CONST_Map_Tile_URL; + $sTileAttribution = CONST_Map_Tile_Attribution; + } logEnd($oDB, $hLog, sizeof($aSearchResults)); $bAsText = $oGeocode->getIncludePolygonAsText();