根据纬度/长度坐标,我们怎样才能找到城市/国家?

例如,如果我们有一组坐标

"latitude": 48.858844300000001,
"longitude": 2.2943506,

我们怎样才能知道这个城市/国家?

252982 次浏览

这真的取决于你有什么样的技术限制。

一种方法是建立一个空间数据库,列出你感兴趣的国家和城市的轮廓。我的意思是说,国家和城市都是储存为空间类型的多边形。您的坐标集可以转换为空间类型点,并根据多边形进行查询,以获得该点所在的国家/城市名称。

下面是一些支持空间类型的数据库: SQL Server 2008MySQL后地理信息系统-postgreSQL 和 神使的扩展。

如果你想使用一个服务,而不是有自己的数据库,你可以使用雅虎的 地球行星。对于服务方法,您可能希望查看 Gis.stackexchange.com上的 这个答案,其中包括解决您的问题的服务的可用性。

免费的 谷歌地理编码 API通过 HTTP REST API 提供此服务。注意,API 是 使用及收费有限,但是您可以支付无限制的访问。

尝试使用这个链接查看输出示例(这是在 json 中,输出也可以在 XML 中使用)

Https://maps.googleapis.com/maps/api/geocode/json?latlng=40.714224,-73.961452&sensor=true

另一个选择:

  • http://download.geonames.org/export/dump/下载城市数据库
  • 将每个城市作为 lat/long-> City 映射添加到 R-Tree 等空间索引(一些 DBs 也具有这种功能)
  • 使用最近邻搜索查找任何给定点的最近城市

优点:

  • 不依赖于可用的外部服务器
  • 非常快(每秒可轻松执行数千次查找)

缺点:

  • 不会自动更新
  • 如果您想区分最近的城市在几十英里之外的情况,则需要额外的代码
  • 可能会在两极和国际日期线附近产生奇怪的结果(尽管这些地方没有任何城市)

请检查下面的答案。它为我工作

if(navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(position){


initialize(position.coords.latitude,position.coords.longitude);
});
}


function initialize(lat,lng) {
//directionsDisplay = new google.maps.DirectionsRenderer(rendererOptions);
//directionsService = new google.maps.DirectionsService();
var latlng = new google.maps.LatLng(lat, lng);


//alert(latlng);
getLocation(latlng);
}


function getLocation(latlng){


var geocoder = new google.maps.Geocoder();
geocoder.geocode({'latLng': latlng}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
if (results[0]) {
var loc = getCountry(results);
alert("location is::"+loc);
}
}
});


}


function getCountry(results)
{
for (var i = 0; i < results[0].address_components.length; i++)
{
var shortname = results[0].address_components[i].short_name;
var longname = results[0].address_components[i].long_name;
var type = results[0].address_components[i].types;
if (type.indexOf("country") != -1)
{
if (!isNullOrWhitespace(shortname))
{
return shortname;
}
else
{
return longname;
}
}
}


}


function isNullOrWhitespace(text) {
if (text == null) {
return true;
}
return text.replace(/\s/gi, '').length < 1;
}

我花了大约30分钟的时间试图找到一个如何在 Javascript 中实现这一点的代码示例。你发布的问题我找不到一个快速明确的答案。所以... 我自己做了。希望人们可以使用它,而不必深入研究 API 或盯着他们不知道如何阅读的代码。哈,如果没有别的,我可以参考这篇文章为我自己的东西。.很好的问题,感谢论坛的讨论!

这就是利用 Google API。

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?key=<YOURGOOGLEKEY>&sensor=false&v=3&libraries=geometry"></script>

.

//CHECK IF BROWSER HAS HTML5 GEO LOCATION
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function (position) {


//GET USER CURRENT LOCATION
var locCurrent = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);


//CHECK IF THE USERS GEOLOCATION IS IN AUSTRALIA
var geocoder = new google.maps.Geocoder();
geocoder.geocode({ 'latLng': locCurrent }, function (results, status) {
var locItemCount = results.length;
var locCountryNameCount = locItemCount - 1;
var locCountryName = results[locCountryNameCount].formatted_address;


if (locCountryName == "Australia") {
//SET COOKIE FOR GIVING
jQuery.cookie('locCountry', locCountryName, { expires: 30, path: '/' });
}
});
}
}

你可以使用谷歌地理编码 API

Bellow 是返回地址、城市、州和国家的 php 函数

public function get_location($latitude='', $longitude='')
{
$geolocation = $latitude.','.$longitude;
$request = 'http://maps.googleapis.com/maps/api/geocode/json?latlng='.$geolocation.'&sensor=false';
$file_contents = file_get_contents($request);
$json_decode = json_decode($file_contents);
if(isset($json_decode->results[0])) {
$response = array();
foreach($json_decode->results[0]->address_components as $addressComponet) {
if(in_array('political', $addressComponet->types)) {
$response[] = $addressComponet->long_name;
}
}


if(isset($response[0])){ $first  =  $response[0];  } else { $first  = 'null'; }
if(isset($response[1])){ $second =  $response[1];  } else { $second = 'null'; }
if(isset($response[2])){ $third  =  $response[2];  } else { $third  = 'null'; }
if(isset($response[3])){ $fourth =  $response[3];  } else { $fourth = 'null'; }
if(isset($response[4])){ $fifth  =  $response[4];  } else { $fifth  = 'null'; }




$loc['address']=''; $loc['city']=''; $loc['state']=''; $loc['country']='';
if( $first != 'null' && $second != 'null' && $third != 'null' && $fourth != 'null' && $fifth != 'null' ) {
$loc['address'] = $first;
$loc['city'] = $second;
$loc['state'] = $fourth;
$loc['country'] = $fifth;
}
else if ( $first != 'null' && $second != 'null' && $third != 'null' && $fourth != 'null' && $fifth == 'null'  ) {
$loc['address'] = $first;
$loc['city'] = $second;
$loc['state'] = $third;
$loc['country'] = $fourth;
}
else if ( $first != 'null' && $second != 'null' && $third != 'null' && $fourth == 'null' && $fifth == 'null' ) {
$loc['city'] = $first;
$loc['state'] = $second;
$loc['country'] = $third;
}
else if ( $first != 'null' && $second != 'null' && $third == 'null' && $fourth == 'null' && $fifth == 'null'  ) {
$loc['state'] = $first;
$loc['country'] = $second;
}
else if ( $first != 'null' && $second == 'null' && $third == 'null' && $fourth == 'null' && $fifth == 'null'  ) {
$loc['country'] = $first;
}
}
return $loc;
}

一个开放源码的替代品是来自开放街道地图的提名。 您所需要做的就是在 URL 中设置变量,然后它返回该位置的城市/国家。请点击以下链接查看官方文档: < a href = “ http://wiki.openstreetmap.org/wiki/Nominatim # RT _ Geocoding”rel = “ noReferrer”> Nominatim

你需要 geopy

pip install geopy

然后:

from geopy.geocoders import Nominatim
geolocator = Nominatim()
location = geolocator.reverse("48.8588443, 2.2943506")


print(location.address)

获取更多信息:

print (location.raw)


{'place_id': '24066644', 'osm_id': '2387784956', 'lat': '41.442115', 'lon': '-8.2939909', 'boundingbox': ['41.442015', '41.442215', '-8.2940909', '-8.2938909'], 'address': {'country': 'Portugal', 'suburb': 'Oliveira do Castelo', 'house_number': '99', 'city_district': 'Oliveira do Castelo', 'country_code': 'pt', 'city': 'Oliveira, São Paio e São Sebastião', 'state': 'Norte', 'state_district': 'Ave', 'pedestrian': 'Rua Doutor Avelino Germano', 'postcode': '4800-443', 'county': 'Guimarães'}, 'osm_type': 'node', 'display_name': '99, Rua Doutor Avelino Germano, Oliveira do Castelo, Oliveira, São Paio e São Sebastião, Guimarães, Braga, Ave, Norte, 4800-443, Portugal', 'licence': 'Data © OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright'}

如果你正在使用 Google 的 Places API,这是你如何使用 Javascript 从 place 对象获取国家和城市信息的方法:

function getCityAndCountry(location) {
var components = {};
for(var i = 0; i < location.address_components.length; i++) {
components[location.address_components[i].types[0]] = location.address_components[i].long_name;
}


if(!components['country']) {
console.warn('Couldn\'t extract country');
return false;
}


if(components['locality']) {
return [components['locality'], components['country']];
} else if(components['administrative_area_level_1']) {
return [components['administrative_area_level_1'], components['country']];
} else {
console.warn('Couldn\'t extract city');
return false;
}
}

我对 只提一些使用了 地理编码器,这是一个很好的 Python 库,支持多个提供者,包括 Google、 Geonames 和 OpenStreetMaps。我试过使用 GeoPy 库,它经常出现超时。为 GeoNames 开发自己的代码并不是对时间的最佳利用,最终可能会得到不稳定的代码。地理编码器是非常简单的使用在我的经验,并有足够好的 文件。下面是一些按经纬度查找城市或按城市名查找纬度/经度的示例代码。

import geocoder


g = geocoder.osm([53.5343609, -113.5065084], method='reverse')
print g.json['city'] # Prints Edmonton


g = geocoder.osm('Edmonton, Canada')
print g.json['lat'], g.json['lng'] # Prints 53.5343609, -113.5065084

我知道这个问题很老了,但是我一直在研究同样的问题,我发现了一个非常有效和方便的软件包 reverse_geocoder,它是由 Ajay Thampi 构建的。 代码是可用的 给你。它基于 K-D 树的并行实现,对于大量的积分来说非常有效(我只花了几秒钟就得到了100,000分)。

它基于 这个数据库,已经由@turgos 突出显示。

如果你的任务是快速找到国家和城市的坐标列表,这是一个伟大的工具。

Loc2country 是一个基于 Golang 的工具,它返回给定位置坐标(lat/lon)的 ISO alpha-3国家代码。它的反应只有几微秒。它使用一个地理散列到国家地图。

地理哈希数据是使用 盗龙生成的。

我们使用地质散列在第6级的这个工具,即,箱子的大小1.2公里 x 600米。

尽量减少库的数量。

在他们的网站上获取使用 API 的密钥,然后在 http 请求中得到结果:

curl -i -H "key: YOUR_KEY" -X GET https://api.latlong.dev/lookup?lat=38.7447913&long=-9.1625173

更新: 我的解决方案不够准确,有时它返回的坐标国家不正确,就在边界附近,或者它不会返回任何国家,例如,当坐标是在海边。最后,我选择了付费的地图反座标化 API。对 URL https://api.mapbox.com/geocoding/v5/mapbox.places/<longitude>,<latitude>.json?access_token=<access token>的请求返回具有位置数据的 geojson-地名、地区、国家。


原答案 :

https://www.naturalearthdata.com/downloads/下载国家(我建议使用1:10 m 以获得更好的精度) ,从中生成 GeoJSON,并使用一些算法来检测给定的坐标是否位于国家多边形内。

我使用这些步骤来生成 GeoJSON 文件:

  1. 安装水蟒: https://www.anaconda.com/products/distribution
  2. 安装 gdal: conda install -c conda-forge gdal(使用升级的管理权限,更多关于 https://anaconda.org/conda-forge/gdal的信息)
  3. https://www.naturalearthdata.com/http//www.naturalearthdata.com/download/10m/cultural/ne_10m_admin_0_countries.zip下载1:10 m 国家,解压缩。
  4. 环境变量: abc 0
  5. 运行命令 C:\ProgramData\Anaconda3\Library\bin\ogr2ogr.exe -f GeoJSON -t_srs crs:84 data.geo.json ne_10m_admin_0_countries.shp

这将生成大约24MB 的 data.geo.json。您也可以下载它 给你

C # :

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;


namespace SmartGuide.Core.Services.CountryLocators
{
public static class CountryLocator
{
private static readonly Lazy<List<CountryPolygons>> _countryPolygonsByCountryName = new(() =>
{
var dataGeoJsonFileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "data.geo.json");
var stream = new FileStream(dataGeoJsonFileName, FileMode.Open, FileAccess.Read);
var geoJson = _Deserialize<Root>(stream);
var countryPolygonsByCountryName = geoJson.Features.Select(
feature => new CountryPolygons
{
CountryName = feature.Properties.Name,
Polygons =
feature.Geometry.Type switch
{
"Polygon" => new List<List<GpsCoordinate>>(
new[]
{
feature.Geometry.Coordinates[0]
.Select(x => new GpsCoordinate(
Convert.ToDouble(x[1]),
Convert.ToDouble(x[0])
)
).ToList()
}
),
"MultiPolygon" => feature.Geometry.Coordinates.Select(
polygon => polygon[0].Select(x =>
new GpsCoordinate(
Convert.ToDouble(((JArray) x)[1]),
Convert.ToDouble(((JArray) x)[0])
)
).ToList()
)
.ToList(),
_ => throw new NotImplementedException($"Unknown geometry type {feature.Geometry.Type}")
}
}
).ToList();
return countryPolygonsByCountryName;
});


public static string GetCountryName(GpsCoordinate coordinate)
{
var country = _countryPolygonsByCountryName.Value.FirstOrDefault(country =>
country.Polygons.Any(polygon => _IsPointInPolygon(polygon, coordinate)));
return country?.CountryName;
}


// taken from https://stackoverflow.com/a/7739297/379279
private static bool _IsPointInPolygon(IReadOnlyList<GpsCoordinate> polygon, GpsCoordinate point)
{
int i, j;
bool c = false;
for (i = 0, j = polygon.Count - 1; i < polygon.Count; j = i++)
{
if ((((polygon[i].Latitude <= point.Latitude) && (point.Latitude < polygon[j].Latitude))
|| ((polygon[j].Latitude <= point.Latitude) && (point.Latitude < polygon[i].Latitude)))
&& (point.Longitude < (polygon[j].Longitude - polygon[i].Longitude) * (point.Latitude - polygon[i].Latitude)
/ (polygon[j].Latitude - polygon[i].Latitude) + polygon[i].Longitude))
{


c = !c;
}
}


return c;
}


private class CountryPolygons
{
public string CountryName { get; set; }
public List<List<GpsCoordinate>> Polygons { get; set; }
}


public static TResult _Deserialize<TResult>(Stream stream)
{
var serializer = new JsonSerializer();


using var sr = new StreamReader(stream);
using var jsonTextReader = new JsonTextReader(sr);
return serializer.Deserialize<TResult>(jsonTextReader);
}


public readonly struct GpsCoordinate
{
public GpsCoordinate(
double latitude,
double longitude
)
{
Latitude = latitude;
Longitude = longitude;
}


public double Latitude { get; }
public double Longitude { get; }
}
}
}


// Generated by https://json2csharp.com/ (with Use Pascal Case) from data.geo.json
public class Feature
{
public string Type { get; set; }
public string Id { get; set; }
public Properties Properties { get; set; }
public Geometry Geometry { get; set; }
}


public class Geometry
{
public string Type { get; set; }
public List<List<List<object>>> Coordinates { get; set; }
}


public class Properties
{
public string Name { get; set; }
}


public class Root
{
public string Type { get; set; }
public List<Feature> Features { get; set; }
}

测试:

    [TestFixture]
public class when_locating_country
{
[TestCase(49.2231391, 17.8545076, "Czechia", TestName = "1 Vizovice, Czech Republic")]
[TestCase(2.9263126, -75.2891733, "Colombia", TestName = "2 Neiva, Colombia")]
[TestCase(12, -70, "Venezuela", TestName = "3 Paraguana, Venezuela")]
[TestCase(-5.0721976, 39.0993457, "Tanzania", TestName = "4 Tanga, Tanzania")]
[TestCase(42.9830241, 47.5048716, "Russia", TestName = "5 Makhachkala, Russia")]
public void country_is_located_correctly(double latitude, double longitude, string expectedCountryName)
{
var countryName = CountryLocator.GetCountryName(new CountryLocator.GpsCoordinate(latitude, longitude));


countryName.ShouldBe(expectedCountryName);
}
}

JS: 您可以使用 https://github.com/vkurchatkin/which-country并用生成的 https://github.com/vkurchatkin/which-country/blob/master/lib/data.geo.json代替不那么精确的 https://github.com/vkurchatkin/which-country/blob/master/lib/data.geo.json。但我没有测试。