web-dev-qa-db-fra.com

Déterminez le fuseau horaire à partir de la latitude/longitude sans utiliser de services Web tels que Geonames.org

est-il possible de déterminer le fuseau horaire du point (lat/lon) sans utiliser les services Web? Geonames.org n'est pas assez stable pour que je puisse utiliser :( J'ai besoin de ça pour fonctionner en PHP.

Merci

49
Jacek Francuz

J'ai eu ce problème il y a quelque temps et j'ai fait exactement ce que Adam a suggéré:

  • Télécharger la base de données des villes de geonames.org
  • convertissez-le en une liste compacte lat/lon -> timezone
  • utiliser un R-Tree implementation pour rechercher efficacement la ville la plus proche (ou plutôt son fuseau horaire) avec une coordonnée donnée

IIRC a mis moins de 1 seconde pour peupler le R-Tree et il pouvait ensuite effectuer des milliers de recherches à la seconde (les deux sur un PC âgé de 5 ans).

54

Quelle est la précision de vos résultats? Si une estimation approximative est suffisante, calculez vous-même le décalage:

offset = direction * longitude * 24 / 360

où direction est 1 pour l'est, -1 pour l'ouest et la longitude est en (-180,180)

12

J'ai rencontré ce problème alors que je travaillais sur un autre projet et l'ai examiné de manière approfondie. J'ai trouvé que toutes les solutions existantes manquaient de façon majeure. 

Télécharger les données GeoNames et utiliser un index spatial pour rechercher le point le plus proche est certainement une option. Le résultat obtenu est souvent correct, mais il peut facilement échouer si un point de requête se trouve du mauvais côté d'une heure. bordure de zone à partir du point le plus proche dans la base de données.

Une méthode plus précise consiste à utiliser une carte numérique des fuseaux horaires et à écrire du code pour trouver le polygone dans cette carte qui contient un point de requête donné. Heureusement, il existe une excellente carte des fuseaux horaires du monde disponible à l’adresse http://efele.net/maps/tz/world/ . Pour écrire un moteur de requête efficace, vous devez:

  • Analyser le format de fichier de forme ESRI en une représentation interne utile.
  • Écrivez un code de point dans un polygone pour vérifier si un point de requête donné se trouve dans un polygone donné.
  • Ecrivez un index spatial efficace au-dessus des données de polygone afin que vous n'ayez pas besoin de vérifier chaque polygone pour trouver celui qui le contient.
  • Traitez les requêtes qui ne sont contenues dans aucun polygone (par exemple, dans l'océan). Dans ce cas, vous devez "aligner" sur le polygone le plus proche jusqu'à une certaine distance et revenir au fuseau horaire "naturel" (celui déterminé par la longitude uniquement) en pleine mer. Pour ce faire, vous aurez besoin d'un code pour calculer la distance entre un point de requête et un segment de droite d'un polygone (ce n'est pas trivial, car la latitude et la longitude sont un système de coordonnées non euclidien) capable de retourner des polygones proches, pas seulement potentiellement contenant des polygones.

Chacun de ceux-ci est digne de sa propre page de questions/réponses Stack Overflow.

Après avoir conclu qu'aucune des solutions existantes ne répondait à mes besoins, j'ai écrit ma propre solution et l'ai mise à disposition ici:

http://askgeo.com

AskGeo utilise une carte numérique et dispose d'un index spatial hautement optimisé qui permet d'exécuter plus de 10 000 requêtes par seconde sur mon ordinateur en un seul fil. Et comme il est thread-safe, un débit encore plus élevé est certainement possible. Il s’agit d’un code sérieux dont le développement a pris beaucoup de temps. Nous l’offrons donc sous licence commerciale. 

Il est écrit en Java, son utilisation dans PHP impliquerait donc:

http://php-Java-bridge.sourceforge.net/doc/how_it_works.php

Nous sommes également disposés à le porter contre une prime. Pour plus de détails sur les prix et pour une documentation détaillée, voir http://askgeo.com .

J'espère que c'est utile. Cela a certainement été utile pour le projet sur lequel je travaillais.

10
James D

Je sais que c'est vieux, mais j'ai passé du temps à chercher cette réponse. Trouvé quelque chose de très utile. Google fait des recherches de fuseau horaire par long/lat. Limite de 2 500 par jour (ou 100 000 pour les utilisateurs professionnels).

https://developers.google.com/maps/documentation/timezone/

8
Jeffrey Vdovjak

Vous devriez pouvoir, si vous connaissez le polygone du fuseau horaire, voir si un lat/lon donné se trouve à l'intérieur.

Base de données du fuseau horaire mondialenter image description here

Données de polygone de latitude/longitudeenter image description here

5
daniellmb

Pour les zones terrestres, il existe des cartes de fichier de formes qui ont été créées pour les fuseaux horaires de la base de données tz (Olson). Elles ne sont pas mises à jour aussi régulièrement que la base de données tz elle-même, mais c'est un excellent point de départ et semble être très précis dans la plupart des cas.

5
Tim Parenti

Que dis-tu de ça ?

// ben@jp

function get_nearest_timezone($cur_lat, $cur_long, $country_code = '') {
    $timezone_ids = ($country_code) ? DateTimeZone::listIdentifiers(DateTimeZone::PER_COUNTRY, $country_code)
                                    : DateTimeZone::listIdentifiers();

    if($timezone_ids && is_array($timezone_ids) && isset($timezone_ids[0])) {

        $time_zone = '';
        $tz_distance = 0;

        //only one identifier?
        if (count($timezone_ids) == 1) {
            $time_zone = $timezone_ids[0];
        } else {

            foreach($timezone_ids as $timezone_id) {
                $timezone = new DateTimeZone($timezone_id);
                $location = $timezone->getLocation();
                $tz_lat   = $location['latitude'];
                $tz_long  = $location['longitude'];

                $theta    = $cur_long - $tz_long;
                $distance = (sin(deg2rad($cur_lat)) * sin(deg2rad($tz_lat))) 
                + (cos(deg2rad($cur_lat)) * cos(deg2rad($tz_lat)) * cos(deg2rad($theta)));
                $distance = acos($distance);
                $distance = abs(rad2deg($distance));
                // echo '<br />'.$timezone_id.' '.$distance; 

                if (!$time_zone || $tz_distance > $distance) {
                    $time_zone   = $timezone_id;
                    $tz_distance = $distance;
                } 

            }
        }
        return  $time_zone;
    }
    return 'none?';
}
//timezone for one NY co-ordinate
echo get_nearest_timezone(40.772222,-74.164581) ;
// more faster and accurate if you can pass the country code 
echo get_nearest_timezone(40.772222, -74.164581, 'US') ;
3
j-bin

J'ai écrit une petite classe Java pour le faire. Il pourrait être facilement traduit en PHP. La base de données est intégrée au code même. C'est précis à 22 km.

https://sites.google.com/a/edval.biz/www/mapping-lat-lng-s-to-timezones

Le code entier est fondamentalement comme ça:

         if (lng < -139.5) {
          if (lat < 68.5) {
           if (lng < -140.5) {
            return 371;
           } else {
            return 325;
           }

... donc je suppose qu'une traduction en PHP serait facile.

2
Tim Cooper

Une autre solution consiste à importer un tableau des villes avec des fuseaux horaires, puis à utiliser la formule de Haversine pour trouver la ville la plus proche dans ce tableau, en fonction de vos coordonnées ..__ J'ai posté une description complète ici: http: // sylnsr .blogspot.com/2012/12/trouver-le-lieu-le-plus-proche-par-latitude-et.html

Pour un exemple de chargement des données dans MySQL, j'ai posté un exemple ici (avec les sources permettant de télécharger un petit vidage de données): http://sylnsr.blogspot.com/2012/12/load-timezone-data-by -city-and-country.html

Notez que l'exactitude de la recherche dépendra de la précision de vos données de recherche.

Crédits et références: MySQL Great Circle Distance (formule de Haversine)

1
MikeM

Malheureusement, les fuseaux horaires ne sont pas assez réguliers pour une fonction simple. Voir la carte dans Wikipedia - Fuseau horaire

Cependant, une approximation très grossière peut être calculée: une différence d'une heure correspond à une longitude de 15 degrés (360/24).

1
MillKa

Je ne suis pas sûr que cela soit utile ou non, mais j'ai construit une base de données de formes de fuseaux horaires (pour l'Amérique du Nord uniquement), extrêmement précise et à jour, non seulement pour les frontières, mais également pour l'observation de l'heure avancée. Formes également pour les exceptions non officielles. Ainsi, vous pouvez interroger l'ensemble des formes pour un emplacement donné, puis renvoyer plusieurs formes s'appliquant à cet emplacement et choisir celle qui convient pour la période de l'année.

Vous pouvez voir une image des formes sur http://OnTimeZone.com/OnTimeZone_shapes.gif . Les formes bleues entourent les zones qui n'observent pas l'heure d'été, celles de Magenta, celles respectant l'heure d'été, et les formes vertes fluo (petites et difficiles à voir à ce niveau de zoom), pour les zones non conformes à l'heure officielle. Beaucoup plus de détails sur ceux disponibles sur le site OnTimeZone.com.

Les données disponibles au téléchargement sur OnTimeZone.com sont gratuites pour un usage non commercial. Les données de forme, qui ne sont pas disponibles pour le téléchargement, sont disponibles pour une licence commerciale.

1
Steve

Vous pouvez utiliser les limites de fuseau horaire, fournies ici:

http://www.opensource.Apple.com/source/TimeZoneData/

1
Mikhail Krasnorutsky

J'ai téléchargé des données qui correspondent à des codes postaux à 5 chiffres dans des fuseaux horaires et des données qui déterminent le décalage UTC pour chaque fuseau horaire pendant les périodes d'heure d'été et les heures d'été.

Ensuite, j'ai simplifié les données pour ne prendre en compte que les trois premiers chiffres du code postal, car tous les codes géographiques qui partagent les trois premiers chiffres sont très proches les uns des autres. les trois chiffres identifient un centre de distribution de courrier unique.

Le fichier Json résultant vous oblige à décider si vous êtes ou non soumis à l'heure d'été, et il comporte probablement une certaine imprécision ici et là. Mais c’est une très bonne solution locale, très compacte et simple à interroger.

Le voici: https://Gist.github.com/benjiwheeler/8aced8dac396c2191cf0

0
Ben Wheeler
You can get the timezone based on the location in javascript.

function initAutocomplete() {
        // Create the autocomplete object, restricting the search to geographical
        // location types.
        autocomplete = new google.maps.places.Autocomplete(
                /** @type {!HTMLInputElement} */(document.getElementById('Location')),
                {types: ['geocode']});

        // When the user selects an address from the dropdown, populate the address
        // fields in the form.
        autocomplete.addListener('place_changed', fillInAddress);
    }

    function fillInAddress() {
        // Get the place details from the autocomplete object.
        var place = autocomplete.getPlace();

        fnGettimezone($('#Location').serialize());
        for (var component in componentForm) {
            document.getElementById(component).value = '';
            document.getElementById(component).disabled = false;
        }

        // Get each component of the address from the place details
        // and fill the corresponding field on the form.
        for (var i = 0; i < place.address_components.length; i++) {
            var addressType = place.address_components[i].types[0];
            if (componentForm[addressType]) {
                var val = place.address_components[i][componentForm[addressType]];
                document.getElementById(addressType).value = val;
            }
        }

    }


function fnGettimezone(location) {
    $.ajax({
            url: "http://maps.googleapis.com/maps/api/geocode/json?address=" + location + "&sensor=false",
            dataType: 'json',
            success: function (result) {
                 console.log(result);
                // result is already a parsed javascript object that you could manipulate directly here
                var myObject = JSON.parse(JSON.stringify(result));
                lat = myObject.results[0].geometry.location.lat;
                lng = myObject.results[0].geometry.location.lng;
                console.log(myObject);

                $.ajax({
                    url: "https://maps.googleapis.com/maps/api/timezone/json?location=" + lat + "," + lng + "&timestamp=1331161200&sensor=false",
                    dataType: 'json',
                    success: function (result) {
                        // result is already a parsed javascript object that you could manipulate directly here
                        var myObject = JSON.parse(JSON.stringify(result));
                        $('#timezone').val(myObject.timeZoneName);

                    }
                });

            }
        });
    }
0
Ummar farook P

Ce projet crée un fichier de formes à partir de données provenant d'OpenStreetMap et est lié à plusieurs bibliothèques qui le gèrent. Pour PHP, vous pouvez consulter https://github.com/minube/geo-timezone

0
vwvw

J'utilise geocoder.ca

Entrez n'importe quel emplacement en Amérique du Nord, géocodes en sortie, codes de zone et fuseau horaire dans json ou jsonp.

Par exemple: http://geocoder.ca/1600%20pennsylvania%20avenue,Washington,DC

Indicatif régional: (202) Fuseau horaire: America/New_York

Json: 

{"standard":{"staddress":"Pennsylvania Ave","stnumber":"1600","prov":"DC","city":"WASHINGTON","postal":"20011","confidence":"0.8"},"longt":"-76.972948","TimeZone":"America/New_York","AreaCode":"202","latt":"38.874533"}
0
Ervin Ruci

Vous pouvez utiliser Google Timezone api. https://maps.googleapis.com/maps/api/timezone/json?location=39.6034810,-119.6822510&timestamp=1331161200&key=YOUR_API_KEY

{
   "dstOffset" : 0,
   "rawOffset" : -28800,
   "status" : "OK",
   "timeZoneId" : "America/Los_Angeles",
   "timeZoneName" : "Pacific Standard Time"
}
0
Sheena Singla