从纬度/经度确定时区,而不使用像Geonames.org这样的网络服务

问题描述 投票:49回答:17

有没有可能在不使用webservices的情况下确定point(lat / lon)的时区? Geonames.org对我来说不够稳定:(我需要这个在PHP中工作。

谢谢

javascript php geolocation timezone gis
17个回答
54
投票

我有一段时间没遇到这个问题,并且确实完成了亚当建议:

  • 从geonames.org下载database of cities
  • 将其转换为紧凑的lat / lon - >时区列表
  • 使用R-Tree实现有效地查找最近的城市(或更确切地说,它的时区)到给定的坐标

IIRC花了不到1秒的时间来填充R-Tree,然后它可以每秒执行数千次查找(都是在一台5岁的PC上)。


1
投票

另一种解决方案是导入具有时区的城市表,然后使用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 formula)


1
投票

您可以使用此处提供的时区边界:

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


1
投票

不确定这是否有用,但我建立了一个时区形状的数据库(仅适用于北美),这是一个非常准确,当前不仅适用于边界,而且适用于夏令时的遵守。也是非官方例外的形状。因此,您可以查询给定位置的形状集,可以返回适用于该位置的多个形状,并选择一年中的正确时间。

你可以在http://OnTimeZone.com/OnTimeZone_shapes.gif看到形状的图像。蓝色形状在不遵守夏令时的区域周围,洋红色塑造那些观察夏令时的区域,而霓虹绿色形状(小而难以看到的缩放级别)适用于与官方时区非正式偏离的区域。关于OnTimeZone.com网站上提供的更多细节。

可在OnTimeZone.com上下载的数据可免费用于非商业用途。形状数据无法下载,可用于商业许可。


0
投票

我下载了与5位数邮政编码匹配的数据到时区,以及确定DST和非DST期间每个时区的UTC偏移量的数据。

然后我将数据简化为仅考虑邮政编码的前三位数字,因为所有共享前三位数的邮政编码彼此非常接近;这三个数字标识一个唯一的邮件分发中心。

生成的Json文件确实要求您决定当前是否受DST约束,并且它可能存在一些不准确的地方。但它是一个非常好的本地解决方案,非常紧凑,易于查询。

这是:https://gist.github.com/benjiwheeler/8aced8dac396c2191cf0


0
投票

我用geocoder.ca

在json或jsonp中输入北美的任何位置,输出地理编码,区域代码和时区。

例如:http://geocoder.ca/1600%20pennsylvania%20avenue,Washington,DC

区号:(202)时区: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
投票

您可以使用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
投票
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
投票

This project根据OpenStreetMap中的数据构建shapefile,并链接到处理它的多个库。对于PHP,你可以看看https://github.com/minube/geo-timezone


12
投票

您的结果有多准确?如果粗略估计足够,请自行计算偏移量:

offset = direction * longitude * 24 / 360

东方向为1,西方方向为-1,经度方向为(-180,180)


10
投票

我在处理另一个项目时遇到了这个问题,并深入研究了它。我发现所有现有的解决方案都缺乏主要方面。

下载GeoNames数据并使用一些空间索引来查找最近的点肯定是一个选项,它会在很多时候产生正确的结果,但如果查询点位于错误的一侧,它很容易失败区域边界距离数据库中最近的点。

更准确的方法是使用时区的数字地图并编写代码以在该地图中查找包含给定查询点的多边形。值得庆幸的是,http://efele.net/maps/tz/world/(未保持anymore)提供了世界时区的绝佳地图。要编写有效的查询引擎,您需要:

  • 将ESRI shapefile格式解析为有用的内部表示。
  • 写入多边形点代码以测试给定查询点是否在给定多边形中。
  • 在多边形数据之上写一个有效的空间索引,这样您就不需要检查每个多边形以找到包含多边形的多边形。
  • 处理任何多边形未包含的查询(例如,在海洋中)。在这种情况下,您应该“捕捉”最近的多边形直到一定距离,然后在开阔的海洋中恢复到“自然”时区(仅由经度确定的时区)。为此,您需要使用代码来计算查询点与多边形线段之间的距离(这是非平凡的,因为纬度和经度是非欧几里德坐标系),您的空间索引需要是能够返回附近的多边形,而不仅仅是可能包含多边形。

每个都值得他们自己的Stack Overflow问题/答案页面。

在断定现有解决方案都没有满足我的需求后,我编写了自己的解决方案,并在此处提供:

http://askgeo.com

AskGeo使用数字地图并具有高度优化的空间索引,允许在一个线程中在我的计算机上每秒运行超过10,000个查询。它是线程安全的,因此甚至可以实现更高的吞吐量。这是一段严肃的代码,我们花了很长时间来开发,所以我们在商业许可下提供它。

它是用Java编写的,因此在PHP中使用它将涉及使用:

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

我们也愿意将它移植到赏金中。有关定价的详细信息以及详细文档,请参阅http://askgeo.com

我希望这很有用。这当然对我正在进行的项目很有用。


8
投票

我知道这已经过时了,但我花了一些时间寻找这个答案。找到了非常有用的东西Google通过long / lat执行时区查找。 No free tier anymore: - /

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


5
投票

如果您知道时区的多边形以查看给定的纬度/经度是否在其中,您应该能够。

World Time Zone Database

Latitude/Longitude Polygon Data


5
投票

对于陆地上的区域,有一些shapefile maps是为tz(Olson)数据库的时区制作的。它们没有像tz数据库那样经常更新,但它是一个很好的起点,对于大多数用途来说似乎非常准确。


3
投票

这个怎么样 ?

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

2
投票

我写了一个小的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
投票

不幸的是,时区对于某些简单的功能来说不够规则。查看Wikipedia - Time Zone的地图

但是,可以计算一些非常粗略的近似值:1小时差异对应于15度经度(360/24)。

© www.soinside.com 2019 - 2024. All rights reserved.