javascript php geolocation timezone gis
base de datos de ciudades

javascript - Determine la zona horaria a partir de la latitud/longitud sin usar servicios web como Geonames.org



php geolocation (16)

¿hay alguna posibilidad de determinar la zona horaria del punto (lat / lon) sin usar servicios web? Geonames.org no es lo suficientemente estable como para que lo use :( Necesito que esto funcione en PHP.

Gracias


¿Cuán exactos deben ser tus resultados? Si una estimación aproximada es suficiente, calcule la compensación usted mismo:

offset = direction * longitude * 24 / 360

donde la dirección es 1 para el este, -1 para el oeste, y la longitud está en (-180,180)



Desafortunadamente, las zonas horarias no son lo suficientemente regulares para algunas funciones simples. Ver el mapa en Wikipedia - Zona horaria

Sin embargo, se puede calcular una aproximación muy aproximada: 1 hora de diferencia corresponde a 15 grados de longitud (360/24).


Descargué datos que coinciden con los códigos postales de 5 dígitos en las zonas horarias, y los datos que determinan el desplazamiento UTC para cada zona horaria durante los períodos de horario de verano y fuera del horario de verano.

Luego simplifiqué los datos para considerar solo los primeros tres dígitos del código postal, ya que todos los códigos postales que comparten los primeros tres dígitos están muy cerca el uno del otro; los tres dígitos identifican un centro de distribución de correo único.

El archivo Json resultante requiere que usted decida si está o no sujeto a DST actualmente, y probablemente tenga alguna imprecisión aquí y allá. Pero es una solución local bastante buena que es muy compacta y fácil de consultar.

Aquí está: https://gist.github.com/benjiwheeler/8aced8dac396c2191cf0


He escrito una pequeña clase de Java para hacer esto. Podría traducirse fácilmente a PHP. La base de datos está integrada en el código en sí. Tiene una precisión de 22 km.

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

Todo el código es básicamente algo como esto:

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

... así que supongo que una traducción a PHP sería fácil.


Me encontré con este problema mientras trabajaba en otro proyecto y lo estudié a fondo. Encontré que faltan todas las soluciones existentes de las principales maneras.

Descargar los datos de GeoNames y usar algún índice espacial para buscar el punto más cercano es definitivamente una opción, y dará el resultado correcto la mayor parte del tiempo, pero puede fallar fácilmente si un punto de consulta está en el lado equivocado de un tiempo. borde de zona desde el punto más cercano en la base de datos.

Un método más preciso es usar un mapa digital de las zonas horarias y escribir código para encontrar el polígono en ese mapa que contiene un punto de consulta determinado. Afortunadamente, hay un excelente mapa de las zonas horarias del mundo disponible en http://efele.net/maps/tz/world/ . Para escribir un motor de consulta eficiente, debe:

  • Analice el formato de archivo de forma ESRI en una representación interna útil.
  • Escriba un código de punto en un polígono para probar si un punto de consulta dado se encuentra en un polígono determinado.
  • Escribe un índice espacial eficiente sobre los datos del polígono para que no necesites verificar cada polígono para encontrar el que lo contiene.
  • Manejar consultas que no están contenidas en ningún polígono (por ejemplo, en el océano). En tales casos, debe "ajustarse" al polígono más cercano hasta cierta distancia, y volver a la zona horaria "natural" (la única determinada por la longitud) en el océano abierto. Para hacer esto, necesitará un código para calcular la distancia entre un punto de consulta y un segmento de línea de un polígono (esto no es trivial ya que la latitud y la longitud son un sistema de coordenadas no euclidiano), y su índice espacial deberá ser capaz de devolver polígonos cercanos, no solo polígonos potencialmente conteniendo.

Cada uno de ellos es digno de su propia página de pregunta / respuesta de desbordamiento de pila.

Después de concluir que ninguna de las soluciones existentes satisfacía mis necesidades, escribí mi propia solución y la hice disponible aquí:

http://askgeo.com

AskGeo utiliza un mapa digital y tiene un índice espacial altamente optimizado que permite ejecutar más de 10.000 consultas por segundo en mi computadora en un solo hilo. Y es seguro para subprocesos, por lo que es posible un rendimiento aún mayor. Este es un código serio, y nos tomó mucho tiempo desarrollarlo, por lo que lo estamos ofreciendo bajo una licencia comercial.

Está escrito en Java, por lo que usarlo en PHP implicaría el uso de:

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

También estamos abiertos a portarlo por una recompensa. Para obtener más información sobre los precios y la documentación detallada, consulte http://askgeo.com .

Espero que esto sea útil. Ciertamente fue útil para el proyecto en el que estaba trabajando.


No estoy seguro de si esto es útil o no, pero construí una base de datos de formas de zona horaria (solo para América del Norte), que es minuciosamente precisa y actual no solo para las fronteras, sino también para la observancia del horario de verano. También formas para excepciones no oficiales. Para que pueda consultar el conjunto de formas para una ubicación determinada, puede devolver varias formas que se apliquen a esa ubicación, y elija la correcta para la época del año.

Puede ver una imagen de las formas en http://OnTimeZone.com/OnTimeZone_shapes.gif . Las formas azules están alrededor de las áreas que no observan el horario de verano, las formas magentas las que sí observan el horario de verano y las formas verdes neón (pequeñas y difíciles de ver en ese nivel de zoom) son para áreas con desviación no oficial de la zona horaria oficial. Mucho más detalles sobre eso disponible en el sitio OnTimeZone.com.

Los datos disponibles para descargar en OnTimeZone.com son gratuitos para uso no comercial. La información de forma, que no está disponible para descargar, está disponible para licencia comercial.


Otra solución es importar una tabla de ciudades con zonas horarias y luego usar la fórmula Haversine para encontrar la ciudad más cercana en esa tabla, relevante para sus coordenadas. He publicado una descripción completa aquí: http://sylnsr.blogspot.com/2012/12/find-nearest-location-by-latitude-and.html

Para un ejemplo de carga de datos en MySQL, he publicado un ejemplo aquí (con fuentes para descargar un pequeño volcado de datos): http://sylnsr.blogspot.com/2012/12/load-timezone-data-by-city-and-country.html

Tenga en cuenta que la precisión de la búsqueda se basará en cuán completos son sus datos de búsqueda.

Créditos y referencias: MySQL Great Circle Distance (fórmula Haversine)


Para áreas en tierra, hay algunos mapas shapefile que se han hecho para las zonas horarias de la base de datos tz (Olson). No se actualizan con la misma frecuencia que la base de datos tz, pero es un excelente punto de partida y parece ser muy preciso para la mayoría de los propósitos.


Puede usar la aplicación 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" }



Qué tal esto ?

// 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'') ;



Tuve este problema hace un tiempo e hice exactamente lo que Adam sugirió:

  • Descargue la base de datos de ciudades de geonames.org
  • convertirlo en una lista compacta lat / lon -> timezone
  • utilice una implementación de R-Tree para buscar eficientemente la ciudad más cercana (o más bien, su zona horaria) a una coordenada dada

IIRC tardó menos de 1 segundo en poblar el R-Tree, y luego pudo realizar miles de búsquedas por segundo (ambos en una PC de 5 años).


Yo uso geocoder.ca

Ingrese cualquier ubicación en América del Norte, geocodificaciones de salida, códigos de área y zona horaria en json o jsonp.

Por ejemplo: http://geocoder.ca/1600%20pennsylvania%20avenue,Washington,DC

Código de área: (202) Zona horaria: 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"}


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); } }); } }); }