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