03
08/2016
获得特定城市的边界
网上有通过百度地图API获得特定城市的边界的程序,如:http://www.cnblogs.com/hzb2010/archive/2012/06/10/2544096.html能够批量下载县市级以上的城市边界;http://www.cnblogs.com/i-gps/archive/2012/05/18/2507941.html和http://www.cnblogs.com/i-gps/archive/2012/07/17/2595733.html以网页的形式下载区域的边界数据。以上两种办法使用的都是百度地图的数据源,区域的边界可能与真实的行政边界不一样。如:贵阳市南明区,其获得的边界如图1所示,但真实的南明区的范围如图2所示。

图1 已有方法获得的南明区边界

图2 贵阳市南明区的真实边界
图2是从openstreetmap(https://www.openstreetmap.org/relation/2783470#map=11/26.5409/106.7765)中的查询结果。通过该网站,查询指定的区域,在Chrome开发者工具的network下,过滤“full”关键字,就能够获得边界数据了。下面是解析边界数据的代码:
static void ParseOpenStreetMap()
{
string content = "";
using (StreamReader sr = new StreamReader(@"下载下来的文件"))
{
content = sr.ReadToEnd();
}
XmlDocument doc = new XmlDocument();
doc.LoadXml(content);
Dictionary<string, List<double>> idToLatLng = new Dictionary<string, List<double>>();
XmlNodeList dataNode = doc.SelectNodes("/osm/node");
foreach (XmlNode node in dataNode)
{
string id = node.Attributes["id"].Value;
double lat = double.Parse(node.Attributes["lat"].Value);
double lng = double.Parse(node.Attributes["lon"].Value);
idToLatLng[id] = new List<double>() { lat, lng };
}
dataNode = doc.SelectNodes("/osm/relation/member");
List<string> wayOrder = new List<string>();
foreach (XmlNode node in dataNode)
{
wayOrder.Add(node.Attributes["ref"].Value);
}
List<List<double>> latLngList = new List<List<double>>();
HashSet<string> nodeIdSet = new HashSet<string>();
int count = 0;
foreach (var wayId in wayOrder)
{
dataNode = doc.SelectNodes("/osm/way[@id=" + wayId + "]/nd");
List<List<double>> latLngListItem = new List<List<double>>();
foreach (XmlNode node in dataNode)
{
string id = node.Attributes["ref"].Value;
if (!nodeIdSet.Contains(id))
{
nodeIdSet.Add(id);
}
else
{
Console.WriteLine("dumplicated id in way/nd: " + id);
}
if (idToLatLng.ContainsKey(id))
{
latLngListItem.Add(idToLatLng[id]);
}
else
{
Console.WriteLine("no id: " + id);
}
}
if (count == 1) //第一个放下来的可能是反的
{
if (GetDistance(latLngList.Last()[0], latLngList.Last()[1], latLngListItem.First()[0], latLngListItem.First()[1]) > 1
&&
GetDistance(latLngList.Last()[0], latLngList.Last()[1], latLngListItem.Last()[0], latLngListItem.Last()[1]) > 1)
{
latLngList.Reverse();
}
}
if (latLngList.Count == 0 || GetDistance(latLngList.Last()[0], latLngList.Last()[1], latLngListItem.First()[0], latLngListItem.First()[1]) <= 1)
{
latLngList.AddRange(latLngListItem);
}
else
{
latLngListItem.Reverse();
latLngList.AddRange(latLngListItem);
}
count++;
}
using (StreamWriter sw = new StreamWriter(@"解析后的输出文件"))
{
foreach (var l in latLngList)
{
sw.Write("[" + l[0] + "," + l[1] + "],");
}
}
Console.WriteLine(latLngList.Count);
}
private const double EARTH_RADIUS = 6378.137; //地球半径
private static double rad(double d)
{
return d * Math.PI / 180.0;
}
public static double GetDistance(double lat1, double lng1, double lat2, double lng2)
{
double radLat1 = rad(lat1);
double radLat2 = rad(lat2);
double a = radLat1 - radLat2;
double b = rad(lng1) - rad(lng2);
double s = 2 * Math.Asin(Math.Sqrt(Math.Pow(Math.Sin(a / 2), 2) +
Math.Cos(radLat1) * Math.Cos(radLat2) * Math.Pow(Math.Sin(b / 2), 2)));
s = s * EARTH_RADIUS;
s = Math.Round(s * 10000) / 10000;
return s; //单位:千米
}
0 条评论