根据纬度/经度确定时区,而不使用像Geonames.org这样的networking服务
有没有可能确定点(纬度/经度)的时区而不使用web服务? Geonames.org不够稳定,我使用:(我需要这个工作在PHP中。
谢谢
我曾经有过这个问题,并且做了adam的build议:
- 从geonames.org下载城市数据库
- 将其转换为紧凑的经纬度 – >时区列表
- 使用R-Tree实现来有效地查找最近的城市(或者说,它的时区)到给定的坐标
IIRC花费了不到1秒的时间来填充R-Tree,然后每秒可以执行数千次查询(在5年前的PC上)。
我在处理另一个项目时遇到了这个问题,并深入研究了这个问题。 我发现所有现有的解决scheme都缺乏重要的方法。
下载GeoNames数据并使用一些空间索引来查找最近的点肯定是一个选项,它会在很长一段时间内产生正确的结果,但是如果查询点位于一个错误的一侧数据库中距离最近点的区域边界。
更准确的方法是使用时区的数字地图,并编写代码来查找包含给定查询点的地图中的多边形。 值得庆幸的是,在http://efele.net/maps/tz/world/上有世界各地时区的优秀地图。; 要编写一个高效的查询引擎,您需要:
- 将ESRI shapefile格式parsing为有用的内部表示。
- 写入多边形代码来testing给定的查询点是否在给定的多边形中。
- 在多边形数据之上写一个有效的空间索引,这样就不需要检查每个多边形来find包含的多边形。
- 处理任何多边形不包含的查询(例如,在海洋中)。 在这种情况下,您应该将最近的多边形“alignment”到一定距离,然后恢复到公海的“自然”时区(仅由经度确定的时区)。 要做到这一点,你将需要代码来计算一个查询点和一个多边形的线段之间的距离(这是非平凡的,因为经度和纬度是一个非欧几里得坐标系统),你的空间索引将需要能够返回附近的多边形,而不只是可能包含多边形。
这些都是值得自己堆栈溢出问题/答案页。
在总结出现有解决scheme中没有一个满足我的需求之后,我写下了自己的解决scheme并在此处提供:
AskGeo使用数字地图,并且具有高度优化的空间索引,允许在单个线程中在我的计算机上每秒运行超过10,000个查询。 而且是线程安全的,所以更高的吞吐量当然是可能的。 这是一个严重的代码,我们花了很长时间来开发,所以我们提供了一个商业许可。
它是用Java编写的,所以在PHP中使用它将涉及到使用:
http://php-java-bridge.sourceforge.net/doc/how_it_works.php
我们也开放将它移植到一个赏金。 有关定价的详细信息和详细的文档,请参阅http://askgeo.com 。
我希望这是有用的。 这对我正在进行的项目当然是有用的。
你的结果究竟有多确切? 如果粗略的估计是足够的,自己计算抵消:
offset = direction * longitude * 24 / 360
东方向为1,西方为-1,经度为(-180,180)
我知道这是旧的,但我花了一些时间寻找这个答案。 find一些非常有用的 Google通过long / lat进行时区查找。 每天限额2500(或商业用户100,000)。
如果您知道时区的多边形,您应该可以查看给定的纬度/经度是否在其中。
世界时区数据库
纬度/经度多边形数据
对于陆地上的地区,有一些shapefile地图是针对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。 数据库embedded在代码本身中。 准确到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的翻译将是容易的。
不幸的是,时区对于一些简单的function是不够规律的。 查看维基百科地图- 时区
然而,可以计算出一些非常粗略的近似值:1小时的差值对应于15度的经度(360/24)。
另一个解决scheme是导入一个带有时区的城市表格,然后使用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大圆距离(Haversine公式)
您可以使用时区边界,如下所示:
不知道这是否有用,但我build立了一个时区形状的数据库(仅适用于北美地区),这个数据库精确准确,不仅适用于边界,而且适用于夏令时。 也为非正式的例外。 因此,您可以查询给定位置的一组形状,可以返回适用于该位置的多个形状,并为一年的时间select正确的形状。
你可以在http://OnTimeZone.com/OnTimeZone_shapes.gif看到形状的图像。; 蓝色的形状是在不遵守夏令时的地方,那些确实遵守夏令时的洋红色形状,以及霓虹绿色的形状(在这个缩放水平上看起来小而难以看见)是与官方时区有非正式偏差的地区。 有关OnTimeZone.com网站上的更多详细信息。
可在OnTimeZone.com上下载的数据是免费用于非商业用途的。 无法下载的形状数据可用于商业许可。
我将与5位数的邮政编码相匹配的数据下载到时区,以及在DST和非DST期间确定每个时区UTC时差的数据。
然后,我简化了数据,只考虑邮政编码的前三位数字,因为所有共享前三位的邮政编码都非常接近; 三位数字标识一个独特的邮件分配中心。
由此产生的Json文件确实要求您决定是否您目前正在使用DST,并且可能在这里和那里有一些不准确的地方。 但这是一个非常好的本地解决scheme,它非常紧凑,易于查询。
这里是: https : //gist.github.com/benjiwheeler/8aced8dac396c2191cf0
我使用geocoder.ca
在北美input任意位置,在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"}
您可以使用Google时区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" }
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); } }); } }); }