Jef
Jef

Reputation: 811

Finding nearby large city with geonames

I have a simple php function which get's the closest nearby city from a given latitude and longitude:

function findCity($lat, $lng, $username) {
        $json_url = "http://api.geonames.org/findNearbyPlaceNameJSON?lat=" . $lat . "&lng=" . $lng . "&username=" . $username;
        $json = file_get_contents($json_url);
        $json = str_replace('},

            ]', "}

            ]", $json);
        $data = json_decode($json);

        echo "<pre>";
    print_r($data);
    echo "</pre>";
    } 

This method returns the following with lat: 51.992 and long: 4.89

stdClass Object
(
    [geonames] => Array
        (
            [0] => stdClass Object
                (
                    [countryName] => Netherlands
                    [adminCode1] => 11
                    [fclName] => city, village,...
                    [countryCode] => NL
                    [lng] => 4.876389
                    [fcodeName] => populated place
                    [distance] => 1.42349
                    [toponymName] => Langerak
                    [fcl] => P
                    [name] => Langerak
                    [fcode] => PPL
                    [geonameId] => 2751924
                    [lat] => 51.931667
                    [adminName1] => South Holland
                    [population] => 0
                )

        )

)

This returns the closest city, but I am looking for something like this. Where only the closest large city is returned. Is this possible? Or are there other alternatives to solve this. I've read about the Google Geocoding API, but we can't use it since we aren't using a Google map to show the results. (Note: the Geocoding API may only be used in conjunction with a Google map; geocoding results without displaying them on a map is prohibited. Source)

I know this isn't an actual programmer problem, but since the geonames forums are not really active, I figured I would post it here.

Upvotes: 0

Views: 2299

Answers (2)

Jef
Jef

Reputation: 811

For those struggling with the same problem, I made some Java code which scrapes continents, countries, cities, lat and lon coordinates from a website. The code isn't beautiful because I made it in a rush, but it does what it's supposed to do:

public class Main {

private static String title;
private static String land;
private static Document docu;
private static String continent = "Africa";

public static void main(String[] args) throws IOException {
    String url = "http://www.timegenie.com/latitude_and_longitude/";

    Document doc = Jsoup.connect(url).get();
    Elements links = doc.select("a[href]");

    //print("\nLinks: (%d)", links.size());
    for (Element link : links) {
        if (link.attr("abs:href").contains("country_coordinates")) {
            try {
                docu = Jsoup.connect(link.attr("abs:href")).get();
                title = docu.title();
                land = (trim(link.text(), 50));

                if (land.equals("Algeria")) {
                    continent = "Africa";
                } else if (land.equals("Antarctica")) {
                    continent = "Antarctica";
                } else if (land.equals("Afghanistan")) {
                    continent = "Asia";
                } else if (land.equals("Bouvet Island")) {
                    continent = "Antartica";
                } else if (land.equals("Anguilla")) {
                    continent = "North America";
                } else if (land.equals("Belize")) {
                    continent = "North America";
                } else if (land.equals("Armenia")) {
                    continent = "Asia";
                } else if (land.equals("Åland Islands") || land.equals("Aland Islands")) {
                    continent = "Europe";
                } else if (land.equals("Bassas da India")) {
                    continent = "Africa";
                } else if (land.equals("Akrotiri")) {
                    continent = "Asia";
                } else if (land.equals("Bermuda")) {
                    continent = "North America";
                } else if (land.equals("Clipperton Island")) {
                    continent = "North America";
                } else if (land.equals("Argentina")) {
                    continent = "South America";
                } else if (land.equals("American Samoa")) {
                    continent = "Oceania";
                }

                Element table = docu.select("table.times").get(0);
                Elements trs = table.select("tr");
                Iterator trIter = trs.iterator();
                boolean firstRow = true;
                while (trIter.hasNext()) {

                    Element tr = (Element) trIter.next();
                    if (firstRow) {
                        firstRow = false;
                        continue;
                    }
                    Elements tds = tr.select("td");
                    Iterator tdIter = tds.iterator();
                    int tdCount = 1;
                    String city = null;
                    String longgr = null;
                    String latgr= null;

                    while (tdIter.hasNext()) {

                        Element td = (Element) tdIter.next();
                        switch (tdCount++) {
                            case 1:
                                city = td.select("a").text();
                                break;
                            case 4:
                                latgr= td.text();
                                break;
                            case 6:
                                longgr = td.text();
                                break;
                        }
                    }

                    System.out.println(continent + "|" + land + "|" + city + "|" + latgr+ "|" + longgr+ "|");
                }
            } catch (Exception ex) {
                Elements links2 = docu.select("a[href]");

                for (Element link2 : links2) {
                    if (link2.attr("abs:href").contains("state_coordinates")) {
                        try {
                            try {
                                docu = Jsoup.connect(link2.attr("abs:href")).get();
                                title = docu.title();

                                Element table = docu.select("table.times").get(0);
                                Elements trs = table.select("tr");
                                Iterator trIter = trs.iterator();
                                boolean firstRow = true;
                                while (trIter.hasNext()) {

                                    Element tr = (Element) trIter.next();
                                    if (firstRow) {
                                        firstRow = false;
                                        continue;
                                    }
                                    Elements tds = tr.select("td");
                                    Iterator tdIter = tds.iterator();
                                    int tdCount = 1;
                                    String city = null;
                                    String longgr = null;
                                    String latgr= null;


                                    while (tdIter.hasNext()) {

                                        Element td = (Element) tdIter.next();
                                        switch (tdCount++) {
                                            case 1:
                                                city = td.select("a").text();
                                                break;
                                            case 4:
                                                latgr= td.text();
                                                break;
                                            case 6:
                                                longgr= td.text();
                                                break;
                                        }
                                    }

                                    System.out.println(continent + "|" + land + "|" + city + "|" + latgr+ "|" + longgr+ "|");
                                }
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        } catch (Exception x) {
                            x.printStackTrace();
                        }
                    }
                }
            }
        }
    }
}

private static void print(String msg, Object... args) {
    System.out.println(String.format(msg, args));
}

private static String trim(String s, int width) {
    if (s.length() > width) {
        return s.substring(0, width - 1) + ".";
    } else {
        return s;
    }
}
}

I've used the Jsoup library.

It will take a while (around 3 minutes) to run this code, it will return a lot of lines (pasted in word: 146 pages) formatted like: continent|country|city|lat|long| so it will be easy to insert them in a database.

Cheers

Upvotes: 0

Bass Jobsen
Bass Jobsen

Reputation: 49044

You need a list of the biggest cities. I didn't find an api call on geonames (maybe try freebase api for sorting by city relevance). Because the example list you show is short an static you could hard code it? If so you could use something shown below:

/*
* Haversine formula
* from: http://rosettacode.org/wiki/Haversine_formula#PHP
*/ 

class POI {
    private $latitude;
    private $longitude;
    public function __construct($latitude, $longitude) {
        $this->latitude = deg2rad($latitude);
        $this->longitude = deg2rad($longitude);
    }
    public function getLatitude() {return $this->latitude;}
    public function getLongitude(){return $this->longitude;}
    public function getDistanceInMetersTo(POI $other) {
        $radiusOfEarth = 6371000;// Earth's radius in meters.
        $diffLatitude = $other->getLatitude() - $this->latitude;
        $diffLongitude = $other->getLongitude() - $this->longitude;
        $a = sin($diffLatitude / 2) * sin($diffLatitude / 2) +
            cos($this->latitude) * cos($other->getLatitude()) *
            sin($diffLongitude / 2) * sin($diffLongitude / 2);
        $c = 2 * asin(sqrt($a));
        $distance = $radiusOfEarth * $c;
        return $distance;
    }
}


class bigcity
{
    public $name;
    public $lat;
    public $long;

    function __construct($name,$lat,$long)
    {
        $this->name=$name;
        $this->lat=$lat;
        $this->long=$long;
    }       
}   

function getbigcities()
{
    $bigcities = array();
    $bigcities[] = new bigcity('Amsterdam',52.374 ,4.89);
    $bigcities[] = new bigcity('Eindhoven',51.441 ,5.478);
    $bigcities[] = new bigcity('Groningen',53.219 ,6.567);
    return $bigcities;
}   


function findCity($lat, $lng)
{
    $userinput = new POI($lat,$lng);
    $bigcities = getbigcities();
    $distance = 1000000000;
    $result = '';
    foreach ($bigcities as $bigcity)
    {

        $delta = $userinput->getDistanceInMetersTo(new POI($bigcity->lat,$bigcity->long));
        if($delta<$distance)
        {
            $result = $bigcity->name;
            $distance = $delta;
        }   
    }   

    return ($result);
}   

echo findcity(51.556,5.091); //tilburg
echo findcity(52.55,6.15); //leeuwaarden
echo findcity(52.091,5.122); //utrecht
exit;

Upvotes: 1

Related Questions