获得特定城市的边界

    网上有通过百度地图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 条评论

    发表评论

    电子邮件地址不会被公开。 必填项已用 * 标注