I'm currently importing Europe maps because I need a reverse geocoder. While playing with the OSM website I encountered 2 issues: 1. When centering the map to: http://www.openstreetmap.org/search?query=42.70988%2C23.29388 it displays the wrong address. It prefers the 173 Видин (173 Vidin) instead бул.Сливница (boulevard Slivnica). Looking the source code it filters the nearest one while 173 Vidin is not (I tested with a lat, lon exactly on the Slivnica main street). 2. When centering the map to: http://www.openstreetmap.org/search?query=42.39311%2C23.63431 it shows not the village Ново Село (Novo selo) but Ихтиман (Ihtiman) which is far away from that point. I understand that the reverse geocoder goes the hierarchy up while looking up the parents of the road found, but still this is not optimal. Google reverser somehow shows the nearest city/town/village. https://www.google.com/maps/place/42%C2%B023%2737.5%22N+23%C2%B038%2703.3%22E/@42.393736,23.634251,14z/data=!3m1!4b1!4m2!3m1!1s0x0:0x0

Similar issues to be found when asking for reverse on a highway between two cities and when in the middle of the highway there is another town near or on the road.

Is there a way to fix this? I believe this would be a nice feature to have.

Or somehow as a mapper I may split the road into sub part and attach that part to the city of interest?

I was unable to find the way how to define a residential area as a village or town or city and attach the roads to it. I saw that some smaller towns and villages have a residential polygon surrounding but it is not named, nor belongs to a relation. Nevertheless, nominatim somehow knows for their streets that they are in that town. Well, I'd like to be able to do something about that but I do not how. The mapping tutorials I read are not sufficient for fixing the above problems. Any help is highly appreciated.

Regards Boris

asked 23 Mar '14, 14:01

bkamenov's gravatar image

bkamenov
1223
accept rate: 0%

edited 23 Mar '14, 20:04


Me again,

I have investigated deeply through the source of the reverse geocoder. Unfortunately, it is wrong. I have rewritten the core code and now I get results even better than google does. But my code is almost 3 times as slower than the original one. If I could get faulty results in 36 ms for DB only Bulgaria map only (my Europe map import has failed, I'll try again later), now for 97 ms I get perfect results.

I'll paste my changes if someone needs them later, but my algorithm works as follows:

for the GPS point I find nearest city/town/village place, then a second query searches for the nearest road in range of 200 meters near my point. Finally, I search with a third query the nearest feature with rank > 28 which are houses and buildings but the trick is to search for nearest one in range of 40 meters and the parent should be the road found in the previous query. In that way I resolve all issues I had.

Would be nice if someone could optimize the queries a little. Here is my code (please note that I ignore the zoom level factor):

In lib/ReverseGeocode.php I replaced the lookup() method body with:

function lookup() { $sPointSQL = 'ST_SetSRID(ST_Point('.$this->fLon.','.$this->fLat.'),4326)'; $iMaxRank = $this->iMaxRank;

        // Find the nearest city/town/village
        $fSearchDiam = 0.3;
        $iPlaceID = null;
        $fMaxAreaDistance = 1;
        while(!$iPlaceID && $fSearchDiam < $fMaxAreaDistance)
        {
            $fSearchDiam = $fSearchDiam * 2;

            $sSQL = 'select place_id from placex';
            $sSQL .= ' WHERE ST_DWithin('.$sPointSQL.', geometry, '.$fSearchDiam.')';
            $sSQL .= ' and rank_search <= 22';
            $sSQL .= ' and (name is not null)';
            $sSQL .= ' and class = \'place\'';
            $sSQL .= ' and indexed_status = 0 ';
            $sSQL .= ' and (ST_GeometryType(geometry) not in (\'ST_Polygon\',\'ST_MultiPolygon\') ';
            $sSQL .= ' OR ST_DWithin('.$sPointSQL.', centroid, '.$fSearchDiam.'))';
            $sSQL .= ' ORDER BY ST_distance('.$sPointSQL.', geometry) ASC limit 1';

            $aPlace = $this->oDB->getRow($sSQL);
            if (PEAR::IsError($aPlace))
            {
                failInternalError("Could not determine closest city/town/village.", $sSQL, $aPlace);
            }
            $iPlaceID = $aPlace['place_id'];
        }

        $oPlaceLookup = new PlaceLookup($this->oDB);
        $oPlaceLookup->setLanguagePreference($this->aLangPrefOrder);
        $oPlaceLookup->setIncludeAddressDetails(true);
        $oPlaceLookup->setPlaceId($iPlaceID);

        $oAddressInfo = $oPlaceLookup->lookup();

        //Find nearest house/street/road within 200 m
        $fSearchDiam = 0.002;

        $sSQL = 'select place_id,parent_place_id,rank_search from placex';
        $sSQL .= ' WHERE ST_DWithin('.$sPointSQL.', geometry, '.$fSearchDiam.')';
        $sSQL .= ' and rank_search != 28 and rank_search >= 23';
        $sSQL .= ' and (name is not null or housenumber is not null)';
        $sSQL .= ' and class not in (\'waterway\',\'railway\',\'tunnel\',\'bridge\')';
        $sSQL .= ' and indexed_status = 0 ';
        $sSQL .= ' and (ST_GeometryType(geometry) not in (\'ST_Polygon\',\'ST_MultiPolygon\') ';
        $sSQL .= ' OR ST_DWithin('.$sPointSQL.', centroid, '.$fSearchDiam.'))';
        $sSQL .= ' ORDER BY ST_distance('.$sPointSQL.', geometry) ASC limit 1';

        $aPlace = $this->oDB->getRow($sSQL);
        if(PEAR::IsError($aPlace))
        {
            failInternalError("Could not determine closest place.", $sSQL, $aPlace);
        }
        $iPlaceID = $aPlace['place_id'];
        $iParentPlaceID = $aPlace['parent_place_id'];

        if($iPlaceID)
        {
            if($aPlace['rank_search'] > 28)
            {
                if($iParentPlaceID)
                    $iPlaceID = $iParentPlaceID;
            }

            //Find nearest house number within 40 meters or building which is child of the road found
            $fSearchDiam = 0.0004;

            $sSQL = 'select place_id, parent_place_id from placex';
            $sSQL .= ' WHERE ST_DWithin('.$sPointSQL.', geometry, '.$fSearchDiam.')';
            $sSQL .= ' and rank_search > 28';
            $sSQL .= ' and (name is not null or housenumber is not null)';
            $sSQL .= ' and class not in (\'waterway\',\'railway\',\'tunnel\',\'bridge\')';
            $sSQL .= ' and indexed_status = 0 ';
            $sSQL .= ' and parent_place_id='.$iPlaceID;
            $sSQL .= ' and (ST_GeometryType(geometry) not in (\'ST_Polygon\',\'ST_MultiPolygon\') ';
            $sSQL .= ' OR ST_DWithin('.$sPointSQL.', centroid, '.$fSearchDiam.'))';
            $sSQL .= ' ORDER BY ST_distance('.$sPointSQL.', geometry) ASC limit 1';

            $aPlace = $this->oDB->getRow($sSQL);
            if(PEAR::IsError($aPlace))
            {
                failInternalError("Could not determine closest place.", $sSQL, $aPlace);
            }
            if($aPlace['place_id'])
                $iPlaceID = $aPlace['place_id'];

            $oPlaceLookup->setPlaceId($iPlaceID);

            $oStreetInfo = $oPlaceLookup->lookup();

            if(!isset($oStreetInfo["error"]) && isset($oStreetInfo["aAddress"]) && isset($oStreetInfo["aAddress"]["road"]))
            {
                $oAddressInfo["aAddress"]["road"] = $oStreetInfo["aAddress"]["road"];

                if(isset($oStreetInfo["aAddress"]["house_number"]))
                    $oAddressInfo["aAddress"]["house_number"] = $oStreetInfo["aAddress"]["house_number"];
                else if($oStreetInfo["aAddress"]["building"])
                    $oAddressInfo["aAddress"]["house_number"] = $oStreetInfo["aAddress"]["building"];

                if(isset($oStreetInfo["aAddress"]["suburb"]))
                    $oAddressInfo["aAddress"]["suburb"] = $oStreetInfo["aAddress"]["suburb"];

                if(isset($oStreetInfo["aAddress"]["postcode"]))
                    $oAddressInfo["aAddress"]["postcode"] = $oStreetInfo["aAddress"]["postcode"];
            }

        }

        return $oAddressInfo;
    }

In my own reverse.php I use the reverse place to build an address as follows:

$oReverseGeocode = new ReverseGeocode($oDB); $oReverseGeocode->setLanguagePreference($aLangPrefOrder); $oReverseGeocode->setIncludeAddressDetails(true); $oReverseGeocode->setLatLon($request[$i]['lat'], $request[$i]['lon']); $aPlace = $oReverseGeocode->lookup();

    $addr = "";
    if(!isset($aPlace["error"]) && isset($aPlace["aAddress"]) && isset($aPlace["aAddress"]["country"]))
    {
        //Road
        if(isset($aPlace["aAddress"]["road"]))
        {
            $addr .= $aPlace["aAddress"]["road"];

            if(isset($aPlace["aAddress"]["house_number"])) //House number
                $addr .= " " . $aPlace["aAddress"]["house_number"];
            else if(isset($aPlace["aAddress"]["suburb"]))//Try providing a suburb
                $addr .= ", " . $aPlace["aAddress"]["suburb"];

            $addr .= ", ";

        }

        //Post code
        if(isset($aPlace["aAddress"]["postcode"]))
            $addr .= $aPlace["aAddress"]["postcode"] . ", ";

        //Village, town or city
        if(isset($aPlace["aAddress"]["vilage"]) || isset($aPlace["aAddress"]["town"]) || isset($aPlace["aAddress"]["city"]))
        {
            if(isset($aPlace["aAddress"]["vilage"]))
                $addr .= $aPlace["aAddress"]["vilage"] . ", ";

            if(isset($aPlace["aAddress"]["town"]))
                $addr .= $aPlace["aAddress"]["town"] . ", ";

            if(isset($aPlace["aAddress"]["city"]))
                $addr .= $aPlace["aAddress"]["city"] . ", ";
        }
        else if(isset($aPlace["aAddress"]["county"])) //County
            $addr .= $aPlace["aAddress"]["county"] . ", ";
        else if(isset($aPlace["aAddress"]["state"])) //State
            $addr .= $aPlace["aAddress"]["state"] . ", ";

        //Country
        $addr .= $aPlace["aAddress"]["country"];
    }

}

Now $addr will have your desired address.

Hope someone could optimize that.

permanent link

answered 24 Mar '14, 19:31

bkamenov's gravatar image

bkamenov
1223
accept rate: 0%

edited 24 Mar '14, 22:38

I'm trying to use your code, but obtain the next one:

<reversegeocode timestamp="Wed, 04 Jun 14 14:50:24 +0000" attribution="Data © OpenStreetMap contributors, ODbL 1.0. &lt;a href=" http:="" www.openstreetmap.org="" copyright""="">http://www.openstreetmap.org/copyright" querystring="format=xml&lat=50.90921&lon=4.46187"> <error>Unable to geocode</error> </reversegeocode>

Did you change anything in PlaceLookup.php? Did you make change only in ReverseGeocode.php and reverse.php?

I imagine that I need to make a change in /lib/template/address.php , could you tell me what I'm doing wrong?

Thanks in advance!

(04 Jun '14, 16:06) soyeya
2

This is the wrong place for discussing code. Please use https://github.com/twain47/Nominatim/ for asking question and suggesting changes.

(04 Jun '14, 16:19) scai ♦
Your answer
toggle preview

Follow this question

By Email:

Once you sign in you will be able to subscribe for any updates here

By RSS:

Answers

Answers and Comments

Markdown Basics

  • *italic* or _italic_
  • **bold** or __bold__
  • link:[text](http://url.com/ "title")
  • image?![alt text](/path/img.jpg "title")
  • numbered list: 1. Foo 2. Bar
  • to add a line break simply add two spaces to where you would like the new line to be.
  • basic HTML tags are also supported

Question tags:

×546
×72
×31
×6

question asked: 23 Mar '14, 14:01

question was seen: 5,174 times

last updated: 04 Jun '14, 16:20

powered by OSQA