DayDreamInGIS 逆地理编码工具(根据经纬度获取位置描述)插件源码解析

本工具调用高德地图逆地理编码api,根据高的地图逆地理编码api,实现根据经纬度获取位置描述。

总体设计逻辑,窗体采用WPF,通过属性的方式传递交互对象,核心处理逻辑写到button的执行逻辑中。

1.页面

页面XAML:

<Window x:Class="DayDreamInGISTool.GeoCoding.InverseGCWPF"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" Title="逆地理编码" ResizeMode="NoResize" WindowStartupLocation="CenterScreen"
             Width="500" Height="480">
    <Grid Margin="5">
        <Grid.RowDefinitions>
            <RowDefinition Height="40"></RowDefinition>
            <RowDefinition Height="40"></RowDefinition>
            <RowDefinition Height="40"></RowDefinition>
            <RowDefinition Height="40"></RowDefinition>
            <RowDefinition Height="40"></RowDefinition>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
            <RowDefinition Height="40"></RowDefinition>
        </Grid.RowDefinitions>
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="100"></ColumnDefinition>
                <ColumnDefinition></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <Label Content="高德地图Token" VerticalAlignment="Center"></Label>
            <TextBox Grid.Column="1" VerticalAlignment="Center" Name="txtGDToken" Height="22" Text="b1670be70e419c2a112957742e55a756"></TextBox>
        </Grid>
        <!--第二行-->
        <Grid Grid.Row="1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="100"></ColumnDefinition>
                <ColumnDefinition></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <Label Content="图层" VerticalAlignment="Center" HorizontalAlignment="Right"></Label>
            <ComboBox Grid.Column="1" Name="cmbLayer" VerticalAlignment="Center" Height="23" SelectionChanged="cmbLayer_SelectionChanged"></ComboBox>
        </Grid>
        <!--第三行-->
        <Grid Grid.Row="2">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="100"></ColumnDefinition>
                <ColumnDefinition></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <Label Content="经度" VerticalAlignment="Center" HorizontalAlignment="Right"></Label>
            <ComboBox Grid.Column="1" Name="cmbLongtitude" VerticalAlignment="Center" Height="23"></ComboBox>
        </Grid>
        <!--第四行-->
        <Grid Grid.Row="3">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="100"></ColumnDefinition>
                <ColumnDefinition></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <Label Content="纬度" VerticalAlignment="Center" HorizontalAlignment="Right"></Label>
            <ComboBox Grid.Column="1" VerticalAlignment="Center" Name="cmbLatitude" Height="23"></ComboBox>
        </Grid>
        <!--第五行-->
        <Grid Grid.Row="4">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="100"></ColumnDefinition>
                <ColumnDefinition></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <Label Content="位置" VerticalAlignment="Center" HorizontalAlignment="Right"></Label>
            <ComboBox Grid.Column="1" VerticalAlignment="Center" Name="cmbLocation" Height="23"></ComboBox>
        </Grid>

        <Grid Grid.Row="5">
            <GroupBox Header="地址结构">
                <Grid Margin="45 5">
                    <Grid.RowDefinitions>
                        <RowDefinition></RowDefinition>
                        <RowDefinition></RowDefinition>
                    </Grid.RowDefinitions>
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition></ColumnDefinition>
                            <ColumnDefinition></ColumnDefinition>
                            <ColumnDefinition></ColumnDefinition>
                            <ColumnDefinition></ColumnDefinition>
                        </Grid.ColumnDefinitions>
                        <CheckBox Grid.Column="0" Name="chkPro" IsChecked="False" VerticalAlignment="Center" Checked="chkPro_Checked">省</CheckBox>
                        <CheckBox Grid.Column="1" Name="chkCity" IsChecked="False" VerticalAlignment="Center" Checked="chkCity_Checked">市</CheckBox>
                        <CheckBox Grid.Column="2" Name="chkCounty" IsChecked="False" VerticalAlignment="Center" Checked="chkCounty_Checked">县/区</CheckBox>
                        <CheckBox Grid.Column="3" Name="chkTown" IsChecked="False" VerticalAlignment="Center" Checked="chkTown_Checked">街道/乡镇</CheckBox>
                    </Grid>
                    <Grid Grid.Row="1">
                        <Label>示例</Label>
                        <TextBox Name="txtExample" VerticalAlignment="Center" HorizontalAlignment="Right" IsReadOnly="True">
                            北京市朝阳区望京街道方恒国际中心B座方恒国际
                        </TextBox>
                    </Grid>
                    
                </Grid>
            </GroupBox>
        </Grid>
        
        <!--第六行 说明-->
        <Grid Grid.Row="6">
            <GroupBox Header="说明">
                <Grid Margin="8">
                    <TextBlock TextWrapping="Wrap" LineHeight="20">
                        <Run>本插件使用高德地图逆地理编码服务获取位置描述,经纬度需为wgs84或者cgcs2000的经纬度,如果token不能使用,请去高德图管网申请token</Run>
                        <LineBreak></LineBreak>
                        <Hyperlink  Click="Hyperlink_Click" NavigateUri="https://console.amap.com/dev/key/app"> https://console.amap.com/dev/key/app</Hyperlink>
                    </TextBlock>
                    
                </Grid>
            </GroupBox>
        </Grid>
        
        <!--第七行-->
        <Grid Grid.Row="7">
            <Grid.ColumnDefinitions>
                <ColumnDefinition></ColumnDefinition>
                <ColumnDefinition></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <Button Content="确定" Name="btnOK" Width="80" Height="30" VerticalAlignment="Center" HorizontalAlignment="Center" Click="btnOK_Click"></Button>
            <Button Content="取消" Name="btnCancel" Width="80" Height="30" Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Center" Click="btnCancel_Click"></Button>
        </Grid>
    </Grid>
</Window>

页面逻辑代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.Geodatabase;
using System.CodeDom;
using System.Diagnostics;

namespace DayDreamInGISTool.GeoCoding
{
    /// <summary>
    /// InverseGCWPF.xaml 的交互逻辑
    /// </summary>
    public partial class InverseGCWPF : Window
    {
        private IFeatureLayer pftlyr = null;

        public IFeatureLayer Pftlyr
        {
            get { return pftlyr; }
            set { pftlyr = value; }
        }
        private string xfdnm;

        public string Xfdnm
        {
            get { return xfdnm; }
            set { xfdnm = value; }
        }
        private string yfdnm;

        public string Yfdnm
        {
            get { return yfdnm; }
            set { yfdnm = value; }
        }
        private string locationfdnm;

        public string Locationfdnm
        {
            get { return locationfdnm; }
            set { locationfdnm = value; }
        }
        private string gdtoken;

        public string Gdtoken
        {
            get { return gdtoken; }
            set { gdtoken = value; }
        }

        private IMap pMap = null;

        string toolname = "DDGInverGeocoding";
        string keyname = "gdtoken";
        string tokenInReg = "";

        public InverseGCWPF()
        {
            InitializeComponent();
            pMap = ArcMap.Document.FocusMap;
            GISCommonHelper.CartoLyrHelper.setFeatureLyrCombox(ref cmbLayer, pMap, esriGeometryType.esriGeometryAny);

            try
            {
                object tt = GISCommonHelper.esriSystemHelper.getValueFromReg2(toolname, keyname);
                if (tt == null)
                {

                }
                else
                {
                    tokenInReg = tt.ToString();
                    this.txtGDToken.Text = tt.ToString();
                }
            }
            catch (Exception)
            {

                throw;
            }

            setExample();
        }

        private bool isPro;

        public bool IsPro
        {
            get { return isPro; }
            set { isPro = value; }
        }
        private bool isCity;

        public bool IsCity
        {
            get { return isCity; }
            set { isCity = value; }
        }
        private bool isCounty;

        public bool IsCounty
        {
            get { return isCounty; }
            set { isCounty = value; }
        }
        private bool isTown;

        public bool IsTown
        {
            get { return isTown; }
            set { isTown = value; }
        }

        private void cmbLayer_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (cmbLayer.SelectedIndex != -1)
            {
                pftlyr = cmbLayer.SelectedValue as IFeatureLayer;

                GISCommonHelper.CartoFieldHelper.setFieldCombox(ref cmbLongtitude, pftlyr.FeatureClass.Fields, false, 
                    esriFieldType.esriFieldTypeDouble, esriFieldType.esriFieldTypeSingle);
                GISCommonHelper.CartoFieldHelper.setFieldCombox(ref cmbLatitude, pftlyr.FeatureClass.Fields, false,
                    esriFieldType.esriFieldTypeDouble, esriFieldType.esriFieldTypeSingle);
                GISCommonHelper.CartoFieldHelper.setFieldCombox(ref cmbLocation, pftlyr.FeatureClass.Fields,false,esriFieldType.esriFieldTypeString);

                GISCommonHelper.CartoFieldHelper.setDftField(ref cmbLongtitude, p =>
                {
                    if (p.alias_name.Contains("经度") || p.name.Contains("经度") || p.alias_name.Contains("Lng") 
                    || p.name.Contains("Lng") || p.alias_name.Contains("Longtitude") || p.name.Contains("Longtitude")
                    || p.name.ToLower()=="x" || p.alias_name.ToLower()=="x")
                    {
                        return true;
                    }

                    return false;
                });

                GISCommonHelper.CartoFieldHelper.setDftField(ref cmbLatitude, p =>
                {
                    if (p.alias_name.Contains("纬度") || p.name.Contains("纬度") || p.alias_name.Contains("Lat") 
                    || p.name.Contains("Lat") || p.alias_name.Contains("Latitude") || p.name.Contains("Latitude")
                    || p.name.ToLower()=="y" || p.alias_name.ToLower()=="y")
                    {
                        return true;
                    }

                    return false;
                });

                GISCommonHelper.CartoFieldHelper.setDftField(ref cmbLocation, p =>
                {
                    if (p.alias_name.Contains("位置") || p.name=="位置" || p.name.ToLower().Contains("location"))
                    {
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                });
            }
        }

        private void btnCancel_Click(object sender, RoutedEventArgs e)
        {
            this.DialogResult = false;
        }

        // 北京市朝阳区望京街道方恒国际中心B座方恒国际
        string pro = "浙江省";
        string city = "杭州市";
        string county = "上城区";
        string town = "四季青街道";
        string building = "钱江新城";

        private void btnOK_Click(object sender, RoutedEventArgs e)
        {
            if (pftlyr == null)
            {
                MessageBox.Show("请选择图层");
                return;
            }

            if (cmbLatitude.SelectedIndex == -1)
            {
                MessageBox.Show("请配置纬度字段");
                return;
            }
            else
            {
                yfdnm = cmbLatitude.SelectedValue.ToString();
            }

            if (cmbLongtitude.SelectedIndex == -1)
            {
                MessageBox.Show("请配置经度字段");
                return;
            }
            else
            {
                xfdnm = cmbLongtitude.SelectedValue.ToString();
            }

            if (cmbLocation.SelectedIndex == -1)
            {
                MessageBox.Show("请配置位置字段");
                return;
            }
            else
            {
                locationfdnm = cmbLocation.SelectedValue.ToString();
            }

            if (string.IsNullOrEmpty(txtGDToken.Text))
            {
                MessageBox.Show("高德地图token不能为空");
            }
            else 
            {
                gdtoken = txtGDToken.Text;
                if(gdtoken== tokenInReg)
                {

                }
                else
                {
                    //写入注册表
                    GISCommonHelper.esriSystemHelper.setValueToReg2(toolname, new KeyValuePair<string, object>(keyname, gdtoken));
                }
            }

            this.DialogResult = true;
        }

        private void Hyperlink_Click(object sender, RoutedEventArgs e) 
        {
            System.Windows.Documents.Hyperlink link = sender as System.Windows.Documents.Hyperlink;
            Process.Start(new ProcessStartInfo(link.NavigateUri.AbsoluteUri));
        }

        private void setExample()
        {
            string res = "";
            if (isPro)
            {
                res += pro;
            }

            if (isCity)
            {
                res+= city;
            }

            if (isCounty)
            {
                res += county;
            }

            if(isTown) 
            {
                res += town;
            }

            res += building;
            txtExample.Text = res;
        }

        private void chkPro_Checked(object sender, RoutedEventArgs e)
        {
            isPro = chkPro.IsChecked.Value;
            setExample();
        }

        private void chkCity_Checked(object sender, RoutedEventArgs e)
        {
            isCity= chkCity.IsChecked.Value;
            setExample();
        }

        private void chkCounty_Checked(object sender, RoutedEventArgs e)
        {
            isCounty= chkCounty.IsChecked.Value;
            setExample();
        }

        private void chkTown_Checked(object sender, RoutedEventArgs e)
        {
            isTown= chkTown.IsChecked.Value;
            setExample();
        }
    }
}

2.代码逻辑

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.Carto;
using System.Windows;
using GISCommonHelper;
using DayDreamInGISTool.Assister;
using Newtonsoft.Json.Linq;

namespace DayDreamInGISTool.GeoCoding
{
    /// <summary>
    /// 高德地图逆地理编码
    /// 地址结构点选  OO
    /// 处理消息/状态写入 OO
    /// 进度条 OO
    /// token限制 默认token限速
    /// </summary>
    public class btnInverseGeocoding : ESRI.ArcGIS.Desktop.AddIns.Button
    {
        public btnInverseGeocoding()
        {
        }

        ESRI.ArcGIS.esriSystem.ITrackCancel trackCancel = null;
        ESRI.ArcGIS.esriSystem.IStepProgressor stepProgressor = null;
        ESRI.ArcGIS.Framework.IProgressDialog2 progressDialog2 = null;

        InverseGCWPF iwp = null;
        string tsttoken = "";
        bool isTest = false;
        
        protected override void OnClick()
        {
            try
            {
                iwp = new InverseGCWPF();
                if (iwp.ShowDialog().Value)
                {
                    if(iwp.Gdtoken== tsttoken)
                    {
                        MessageBox.Show("测试token,每次处理不超过30个要素");
                        isTest = true;
                    }
                    

                    trackCancel = new ESRI.ArcGIS.Display.CancelTrackerClass();
                    ESRI.ArcGIS.Framework.IProgressDialogFactory progressDialogFactory = new ESRI.ArcGIS.Framework.ProgressDialogFactoryClass();
                    stepProgressor = progressDialogFactory.Create(trackCancel, ArcMap.Application.hWnd);
                    ESRI.ArcGIS.Framework.IProgressDialog2 progressDialog2 = (ESRI.ArcGIS.Framework.IProgressDialog2)stepProgressor; // Explict Cast
                    progressDialog2.CancelEnabled = true;
                    progressDialog2.Description = "逆地理编码中...";
                    progressDialog2.Title = "逆地理编码";
                    progressDialog2.Animation = ESRI.ArcGIS.Framework.esriProgressAnimationTypes.esriProgressGlobe;
                    stepProgressor.Show();
                    execute();

                    //完成
                    trackCancel = null;
                    stepProgressor = null;
                    progressDialog2.HideDialog();
                    progressDialog2 = null;
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("发生未知异常:"+ex.Message);
            }
        }

        string statusFd = "status";
        string infoFd = "info";

        private void execute()
        {
            //创建可能的消息状态字段
            try
            {
                GISCommonHelper.FieldHelper.AddStringField(iwp.Pftlyr.FeatureClass, new KeyValuePair<string, int>(statusFd, 10), 
                    new KeyValuePair<string, int>(infoFd, 70));
            }
            catch (Exception)
            {
                MessageBox.Show("创建标识字段出错");
            }

            stepProgressor.MinRange = 0;
            int featurecount = iwp.Pftlyr.FeatureClass.FeatureCount(null);
            if (isTest)
            {
                stepProgressor.MaxRange = featurecount > 30 ? 30 : featurecount;  //测试token,一次请求只能30
            }
            else
            {
                stepProgressor.MaxRange = featurecount;
            }
            stepProgressor.StepValue = 1;
            int n = 0;
            IFeatureCursor pftcursor = iwp.Pftlyr.FeatureClass.Search(null, false);
            IFeature pFeature= pftcursor.NextFeature();
            while(pFeature!=null )
            {
                if (isTest)
                {
                    if (n > 30)
                    {
                        break;
                    }
                }

                stepProgressor.Message = "正在处理要素,OId:" + pFeature.OID;
                System.Diagnostics.Debug.WriteLine("正在处理:"+pFeature.OID);
                double lng= (double)pFeature.getval(iwp.Xfdnm);
                double lat = (double)pFeature.getval(iwp.Yfdnm);
                RequestRes rr = getLocationStr(lng, lat);
                if(pFeature.Fields.FindField(statusFd)!=-1)
                    pFeature.setval(statusFd, rr.status);
                if (rr.status == "0")
                {
                    if(pFeature.Fields.FindField(infoFd)!=-1)
                        pFeature.setval(infoFd, rr.info);
                }
                else if (rr.status == "1")
                {
                    pFeature.setval(iwp.Locationfdnm, rr.address);
                }

                stepProgressor.Step();

                pFeature.Store();
                n++;
                pFeature = pftcursor.NextFeature();
            }
        }

        string bsurl = "https://restapi.amap.com/v3/geocode/regeo?parameters";

        private RequestRes getLocationStr(double lng,double lat)
        {
            RequestRes rr = new RequestRes();
            //https://restapi.amap.com/v3/geocode/regeo?output=xml&location=116.310003,39.991957&key=<用户的key>&radius=1000&extensions=all
            string location = "";
            string url = string.Format("https://restapi.amap.com/v3/geocode/regeo?location={0},{1}&key={2}&radius=1000&extensions=all",lng,lat,iwp.Gdtoken);
            string json = WebRequestAssist.GetRequestStr(url);
            var jRoot= JObject.Parse(json);
            int status= jRoot.Value<int>("status");
            rr.status = status.ToString();
            if (status == 1)
            {
                // 1 请求成功
                var regeocodeObj = jRoot.GetValue("regeocode") as JObject;
                string formatted_address = regeocodeObj.Value<string>("formatted_address");
                //北京市朝阳区望京街道方恒国际中心B座方恒国际
                //移除前缀
                var addressComponentObj = regeocodeObj.GetValue("addressComponent");
                string province = addressComponentObj.Value<string>("province");
                string district = addressComponentObj.Value<string>("district");
                string city = addressComponentObj.Value<string>("city");
                string township = addressComponentObj.Value<string>("township");
                rr.address = formatted_address;
                //return formatted_address;

                if (!iwp.IsPro)
                {
                    formatted_address=formatted_address.Replace(province, "");
                }

                if(!iwp.IsCity) 
                {
                    if (!string.IsNullOrEmpty(city))
                    {
                        formatted_address = formatted_address.Replace(city, "");
                    }
                }

                if (!iwp.IsCounty)
                {
                    formatted_address = formatted_address.Replace(district,""); 
                }

                if (!iwp.IsTown)
                {
                    formatted_address = formatted_address.Replace(township, "");
                }
                rr.address = formatted_address;
            }
            else 
            {
                //0 请求失败
                string info = jRoot.Value<string>("info");  //错误消息
                rr.info = info;
            }

            return rr;
        }

        protected override void OnUpdate()
        {
        }

        struct RequestRes
        {
            public string status { get; set; }
            public string info { get; set; }
            public string address { get; set; }
        }
    }
}

大体解析:(1)获取要素的经度、纬度字段值

(2)调用高德地图逆地理编码API,获取位置描述

(3)通过Newtonsoft.JSON解析返回的json对象,并按照要求,剔除省、市等前缀

(4)将结果写入要素

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值