Определите часовой пояс по широте / долготе без использования веб-сервисов, таких как Geonames.org
Есть ли возможность определить часовой пояс точки (широта / долгота) без использования веб-сервисов? Geonames.org не достаточно стабилен для меня, чтобы использовать:(Мне нужно это для работы в PHP.
Спасибо
16 ответов
Я имел эту проблему некоторое время назад и сделал именно то, что предложил Адам:
- Скачать базу данных городов с geonames.org
- преобразовать его в компактный лат / лон -> список часовых поясов
- использовать реализацию R-Tree для эффективного поиска ближайшего города (точнее его часового пояса) по заданной координате
IIRC потребовалось менее 1 секунды, чтобы заполнить R-Tree, и затем он мог выполнять тысячи поисков в секунду (оба на 5-летнем ПК).
Насколько точными должны быть ваши результаты? Если приблизительной оценки достаточно, рассчитайте смещение самостоятельно:
offset = direction * longitude * 24 / 360
где направление равно 1 для востока, -1 для запада и долготы (-180,180)
Я столкнулся с этой проблемой, работая над другим проектом, и очень глубоко изучил ее. Я обнаружил, что во всех существующих решениях отсутствуют основные пути.
Загрузка данных GeoNames и использование некоторого пространственного индекса для поиска ближайшей точки - это определенно вариант, и он будет часто давать правильный результат, но он может легко потерпеть неудачу, если точка запроса находится не на той стороне времени граница зоны от ближайшей точки в базе данных.
Более точный метод состоит в том, чтобы использовать цифровую карту часовых поясов и написать код, чтобы найти многоугольник в той карте, которая содержит заданную точку запроса. К счастью, на http://efele.net/maps/tz/world/ есть отличная карта часовых поясов мира. Чтобы написать эффективный механизм запросов, вам необходимо:
- Разобрать формат шейп-файла ESRI в полезное внутреннее представление.
- Напишите код точки в многоугольнике, чтобы проверить, находится ли заданная точка запроса в заданном многоугольнике.
- Напишите эффективный пространственный индекс поверх данных многоугольника, чтобы вам не нужно было проверять каждый многоугольник, чтобы найти содержащий его.
- Обработка запросов, которые не содержатся ни в одном многоугольнике (например, в океане). В таких случаях вы должны "привязаться" к ближайшему полигону на определенное расстояние и вернуться к "естественному" часовому поясу (который определяется только долготой) в открытом океане. Для этого вам понадобится код для вычисления расстояния между точкой запроса и отрезком линии многоугольника (это нетривиально, поскольку широта и долгота неевклидовой системы координат), и ваш пространственный индекс должен быть возможность вернуть соседние полигоны, а не только потенциально содержащие полигоны.
Каждый из них достоин своей страницы вопросов / ответов о переполнении стека.
После того, как я пришел к выводу, что ни одно из существующих решений не отвечает моим потребностям, я написал собственное решение и сделал его доступным здесь:
AskGeo использует цифровую карту и имеет высокооптимизированный пространственный индекс, который позволяет выполнять более 10000 запросов в секунду на моем компьютере в одном потоке. И это потокобезопасно, так что, безусловно, возможна даже более высокая пропускная способность Это серьезный кусок кода, и его разработка заняла много времени, поэтому мы предлагаем его по коммерческой лицензии.
Он написан на Java, поэтому его использование в PHP предполагает использование:
http://php-java-bridge.sourceforge.net/doc/how_it_works.php
Мы также открыты для портирования за вознаграждение. Для получения подробной информации о ценах и подробной документации см. http://askgeo.com/.
Я надеюсь, что это полезно. Это, безусловно, было полезно для проекта, над которым я работал.
Я знаю, что это старо, но я потратил некоторое время на поиски этого ответа. Нашел что-то очень полезное. Google выполняет поиск часовых поясов по long / lat. Ограничение 2500 в день (или 100 000 для бизнес-пользователей).
Для областей на суше есть несколько карт шейп-файлов, которые были созданы для часовых поясов базы данных tz (Olson). Они обновляются не так регулярно, как сама база данных tz, но это отличная отправная точка и, как представляется, очень точная для большинства целей.
Вы должны быть в состоянии, если вы знаете многоугольник часового пояса, посмотреть, находится ли данный широта / долгота внутри него.
Как насчет этого?
// 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') ;
Я написал небольшой класс Java для этого. Это может быть легко переведено на PHP. База данных встроена в сам код. Это с точностью до 22 км.
https://sites.google.com/a/edval.biz/www/mapping-lat-lng-s-to-timezones
Весь код в основном такой:
if (lng < -139.5) {
if (lat < 68.5) {
if (lng < -140.5) {
return 371;
} else {
return 325;
}
... поэтому я предполагаю, что перевод на PHP будет легким.
Вы можете использовать границы часового пояса, предоставленные здесь:
К сожалению, часовые пояса не достаточно регулярны для некоторых простых функций. Посмотреть карту в Википедии - Часовой пояс
Тем не менее, можно рассчитать очень грубое приближение: разница в 1 час соответствует 15 градусам долготы (360 / 24).
Не уверен, полезно это или нет, но я создал базу данных форм часовых поясов (только для Северной Америки), которая кропотливо точна и актуальна не только для границ, но и для соблюдения летнего времени. Также формы для неофициальных исключений. Таким образом, вы можете запросить набор фигур для данного местоположения, можете вернуть несколько фигур, которые применяются к этому местоположению, и выбрать правильную для времени года.
Вы можете увидеть изображение фигур на http://OnTimeZone.com/OnTimeZone_shapes.gif. Синие фигуры находятся вокруг областей, в которых не наблюдается переход на летнее время, пурпурные фигуры - в тех, которые выполняют переход на летнее время, а неоновые зеленые фигуры (маленькие и трудно различимые при таком уровне масштабирования) - для областей с неофициальным отклонением от официального часового пояса. Много более подробной информации об этом доступно на сайте OnTimeZone.com.
Данные, доступные для скачивания на OnTimeZone.com, бесплатны для некоммерческого использования. Данные формы, которые недоступны для загрузки, доступны для коммерческой лицензии.
Другое решение - импортировать таблицу городов с часовыми поясами, а затем использовать формулу Haversine, чтобы найти ближайший город в этой таблице, соответствующий вашим координатам. Я разместил полное описание здесь: http://sylnsr.blogspot.com/2012/12/find-nearest-location-by-latitude-and.html
Для примера загрузки данных в MySQL я разместил пример здесь (с источниками для загрузки небольшого дампа данных): http://sylnsr.blogspot.com/2012/12/load-timezone-data-by-city-and-country.html
Обратите внимание, что точность поиска будет зависеть от того, насколько полны ваши поисковые данные.
Кредиты и ссылки: MySQL Great Circle Distance (формула Haversine)
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 + "×tamp=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);
}
});
}
});
}
Я использую http://geocoder.ca/
Введите любое местоположение в Северной Америке, вывод геокодов, кодов городов и часовых поясов в формате json или jsonp.
Например: http://geocoder.ca/1600%20pennsylvania%20avenue,Washington,DC
Код города: (202) Часовой пояс: Америка / Нью-Йорк
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"}
Этот проект создает шейп-файл из данных OpenStreetMap и связывает его с несколькими библиотеками, которые его обрабатывают. Для PHP вы можете посмотреть на https://github.com/minube/geo-timezone
Я загрузил данные, которые соответствуют 5-значным почтовым индексам часовых поясов, и данные, которые определяют смещение UTC для каждого часового пояса в течение летнего и не летнего периодов.
Затем я упростил данные, чтобы рассмотреть только первые три цифры почтового индекса, поскольку все почтовые коды, которые разделяют первые три цифры, очень близки друг к другу; три цифры обозначают уникальный центр распространения почты.
Полученный файл Json требует, чтобы вы решили, подпадаете ли вы под DST в настоящее время, и, вероятно, здесь и там есть некоторая неточность. Но это довольно хорошее локальное решение, которое очень компактно и просто для запроса.
Вот оно: https://gist.github.com/benjiwheeler/8aced8dac396c2191cf0
Вы можете использовать Google Timezone API. https://maps.googleapis.com/maps/api/timezone/json?location=39.6034810,-119.6822510×tamp=1331161200&key=YOUR_API_KEY
{
"dstOffset" : 0,
"rawOffset" : -28800,
"status" : "OK",
"timeZoneId" : "America/Los_Angeles",
"timeZoneName" : "Pacific Standard Time"
}