本工具调用高德地图逆地理编码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 CastprogressDialog2.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=allstring 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)将结果写入要素