判定座標是否在國內,有下列幾種方法可選:
- 通過網絡地圖接口查詢
- 根據國家行政邊界判定
- 根據國家行政邊界近似判定
方法1,通過網絡地圖接口查詢
網絡接口:http://maps.google.com/maps/api/geocode/xml?latlng=xxx,xxx&sensor=false,其中latlng參數填寫的是座標的經緯度參數。
例如進行如下調用:http://maps.google.com/maps/api/geocode/xml?latlng=39.829632,117.059326&sensor=false
返回結果如下:
<?xml version="1.0" encoding="UTF-8"?>
<GeocodeResponse>
<status>OK</status>
<result>
<type>route</type>
<formatted_address>274 Provincial Rd, Sanhe, Langfang, Hebei, China</formatted_address>
<address_component>
<long_name>274 Provincial Rd</long_name>
<short_name>S274</short_name>
<type>route</type>
</address_component>
<address_component>
<long_name>Sanhe</long_name>
<short_name>Sanhe</short_name>
<type>sublocality</type>
<type>political</type>
</address_component>
<address_component>
<long_name>Langfang</long_name>
<short_name>Langfang</short_name>
<type>locality</type>
<type>political</type>
</address_component>
<address_component>
<long_name>Hebei</long_name>
<short_name>Hebei</short_name>
<type>administrative_area_level_1</type>
<type>political</type>
</address_component>
<address_component>
<long_name>China</long_name>
<short_name>CN</short_name>
<type>country</type>
<type>political</type>
</address_component>
<geometry>
<location>
<lat>39.8483120</lat>
<lng>117.0741900</lng>
</location>
<location_type>APPROXIMATE</location_type>
<viewport>
<southwest>
<lat>39.8292258</lat>
<lng>117.0728091</lng>
</southwest>
<northeast>
<lat>39.8674069</lat>
<lng>117.0757736</lng>
</northeast>
</viewport>
<bounds>
<southwest>
<lat>39.8292258</lat>
<lng>117.0728091</lng>
</southwest>
<northeast>
<lat>39.8674069</lat>
<lng>117.0757736</lng>
</northeast>
</bounds>
</geometry>
</result>
<result>
<type>sublocality</type>
<type>political</type>
<formatted_address>Sanhe, Langfang, Hebei, China</formatted_address>
<address_component>
<long_name>Sanhe</long_name>
<short_name>Sanhe</short_name>
<type>sublocality</type>
<type>political</type>
</address_component>
<address_component>
<long_name>Langfang</long_name>
<short_name>Langfang</short_name>
<type>locality</type>
<type>political</type>
</address_component>
<address_component>
<long_name>Hebei</long_name>
<short_name>Hebei</short_name>
<type>administrative_area_level_1</type>
<type>political</type>
</address_component>
<address_component>
<long_name>China</long_name>
<short_name>CN</short_name>
<type>country</type>
<type>political</type>
</address_component>
<geometry>
<location>
<lat>39.9827180</lat>
<lng>117.0782950</lng>
</location>
<location_type>APPROXIMATE</location_type>
<viewport>
<southwest>
<lat>39.8081545</lat>
<lng>116.7572022</lng>
</southwest>
<northeast>
<lat>40.0850111</lat>
<lng>117.2599867</lng>
</northeast>
</viewport>
<bounds>
<southwest>
<lat>39.8081545</lat>
<lng>116.7572022</lng>
</southwest>
<northeast>
<lat>40.0850111</lat>
<lng>117.2599867</lng>
</northeast>
</bounds>
</geometry>
</result>
<result>
<type>locality</type>
<type>political</type>
<formatted_address>Langfang, Hebei, China</formatted_address>
<address_component>
<long_name>Langfang</long_name>
<short_name>Langfang</short_name>
<type>locality</type>
<type>political</type>
</address_component>
<address_component>
<long_name>Hebei</long_name>
<short_name>Hebei</short_name>
<type>administrative_area_level_1</type>
<type>political</type>
</address_component>
<address_component>
<long_name>China</long_name>
<short_name>CN</short_name>
<type>country</type>
<type>political</type>
</address_component>
<geometry>
<location>
<lat>39.5380470</lat>
<lng>116.6837520</lng>
</location>
<location_type>APPROXIMATE</location_type>
<viewport>
<southwest>
<lat>39.4355307</lat>
<lng>116.5841675</lng>
</southwest>
<northeast>
<lat>39.6029123</lat>
<lng>116.8444061</lng>
</northeast>
</viewport>
<bounds>
<southwest>
<lat>38.4703170</lat>
<lng>116.1166769</lng>
</southwest>
<northeast>
<lat>40.0850111</lat>
<lng>117.2599867</lng>
</northeast>
</bounds>
</geometry>
</result>
<result>
<type>administrative_area_level_1</type>
<type>political</type>
<formatted_address>Hebei, China</formatted_address>
<address_component>
<long_name>Hebei</long_name>
<short_name>Hebei</short_name>
<type>administrative_area_level_1</type>
<type>political</type>
</address_component>
<address_component>
<long_name>China</long_name>
<short_name>CN</short_name>
<type>country</type>
<type>political</type>
</address_component>
<geometry>
<location>
<lat>38.0370570</lat>
<lng>114.4686650</lng>
</location>
<location_type>APPROXIMATE</location_type>
<viewport>
<southwest>
<lat>36.0482067</lat>
<lng>113.4653687</lng>
</southwest>
<northeast>
<lat>42.6197178</lat>
<lng>119.8542560</lng>
</northeast>
</viewport>
<bounds>
<southwest>
<lat>36.0482067</lat>
<lng>113.4653687</lng>
</southwest>
<northeast>
<lat>42.6197178</lat>
<lng>119.8542560</lng>
</northeast>
</bounds>
</geometry>
</result>
<result>
<type>country</type>
<type>political</type>
<formatted_address>China</formatted_address>
<address_component>
<long_name>China</long_name>
<short_name>CN</short_name>
<type>country</type>
<type>political</type>
</address_component>
<geometry>
<location>
<lat>35.8616600</lat>
<lng>104.1953970</lng>
</location>
<location_type>APPROXIMATE</location_type>
<viewport>
<southwest>
<lat>18.1535216</lat>
<lng>73.4994137</lng>
</southwest>
<northeast>
<lat>53.5609740</lat>
<lng>134.7728100</lng>
</northeast>
</viewport>
<bounds>
<southwest>
<lat>18.1535216</lat>
<lng>73.4994137</lng>
</southwest>
<northeast>
<lat>53.5609740</lat>
<lng>134.7728100</lng>
</northeast>
</bounds>
</geometry>
</result>
</GeocodeResponse>
查詢結果各字段的含義可以參考Google Map API的Reverse Geocoding (Address Lookup)章節。簡化後可以直接判斷返回的字符串中是否存在“<short_name>CN</short_name>”作爲判定所查詢座標是否在中國的依據。
方法2,根據國家行政邊界判定
由於國家行政邊界是由多個簡單多邊形組成的。對多個多邊形進行命中測試 (Hit Testing),只要其中一個多邊形測試成功,則表示整個命中測試成功。但多邊形的命中測試有些麻煩,更簡單的方法是判別點是否在多邊形內(Point in polygon(PIP))。這些算法中簡單高效的莫過於是光線投射算法 (Ray casting algorithm)。
光線投射算法的要義是:在二維空間的任意一點射出一條射線,射線穿過多邊形邊界奇數次則這個點在多邊形內,否則在多邊形外。此方法適用於複雜多邊形。實現此算法往往會限制射線的方向是向右的,這樣的好處是簡化了許多計算。此算法的一個實現參考1987年Paul Bourke教授所寫“Determining if a point lies on the interior of a polygon”的Solution 1。
在 Windows Phone 7 中的實現,首先需要獲取到國家行政邊界的WGS84座標數據。Alexey Strakh(鏈接有惡意軟件警告)所開發的gMaps應用中包含了此邊界數據。因此,自己可以把數據摳出來。摳出的數據由四部分組成我國的行政邊界:大陸、臺灣省、海南省、崇明島。此數據相對來說比較準確,但是邊界附近誤差較大。每次判別座標是否在此區域內的計算次數較多,大約需要幾百次的幾何計算。
此方法的實現代碼如下:
#region polygon
List<Point[]> polygon = new List<Point[]>();
private void InitPolygon()
{
polygon.Add(new Point[] //Mainland
{
new Point(27.32083, 88.91693),
new Point(27.54243, 88.76464),
new Point(28.00805, 88.83575),
new Point(28.1168, 88.62435),
new Point(27.86605, 88.14279),
new Point(27.82305, 87.19275),
new Point(28.11166, 86.69527),
new Point(27.90888, 86.45137),
new Point(28.15805, 86.19769),
new Point(27.88625, 86.0054),
new Point(28.27916, 85.72137),
new Point(28.30666, 85.11095),
new Point(28.59104, 85.19518),
new Point(28.54444, 84.84665),
new Point(28.73402, 84.48623),
new Point(29.26097, 84.11651),
new Point(29.18902, 83.5479),
new Point(29.63166, 83.19109),
new Point(30.06923, 82.17525),
new Point(30.33444, 82.11123),
new Point(30.385, 81.42623),
new Point(30.01194, 81.23221),
new Point(30.20435, 81.02536),
new Point(30.57552, 80.207),
new Point(30.73374, 80.25423),
new Point(30.96583, 79.86304),
new Point(30.95708, 79.55429),
new Point(31.43729, 79.08082),
new Point(31.30895, 78.76825),
new Point(31.96847, 78.77075),
new Point(32.24304, 78.47594),
new Point(32.5561, 78.40595),
new Point(32.63902, 78.74623),
new Point(32.35083, 78.9711),
new Point(32.75666, 79.52874),
new Point(33.09944, 79.37511),
new Point(33.42863, 78.93623),
new Point(33.52041, 78.81387),
new Point(34.06833, 78.73581),
new Point(34.35001, 78.98535),
new Point(34.6118, 78.33707),
new Point(35.28069, 78.02305),
new Point(35.49902, 78.0718),
new Point(35.50133, 77.82393),
new Point(35.6125, 76.89526),
new Point(35.90665, 76.55304),
new Point(35.81458, 76.18061),
new Point(36.07082, 75.92887),
new Point(36.23751, 76.04166),
new Point(36.66343, 75.85984),
new Point(36.73169, 75.45179),
new Point(36.91156, 75.39902),
new Point(36.99719, 75.14787),
new Point(37.02782, 74.56543),
new Point(37.17, 74.39089),
new Point(37.23733, 74.91574),
new Point(37.40659, 75.18748),
new Point(37.65243, 74.9036),
new Point(38.47256, 74.85442),
new Point(38.67438, 74.35471),
new Point(38.61271, 73.81401),
new Point(38.88653, 73.70818),
new Point(38.97256, 73.85235),
new Point(39.23569, 73.62005),
new Point(39.45483, 73.65569),
new Point(39.59965, 73.95471),
new Point(39.76896, 73.8429),
new Point(40.04202, 73.99096),
new Point(40.32792, 74.88089),
new Point(40.51723, 74.8588),
new Point(40.45042, 75.23394),
new Point(40.64452, 75.58284),
new Point(40.298, 75.70374),
new Point(40.35324, 76.3344),
new Point(41.01258, 76.87067),
new Point(41.04079, 78.08083),
new Point(41.39286, 78.39554),
new Point(42.03954, 80.24513),
new Point(42.19622, 80.23402),
new Point(42.63245, 80.15804),
new Point(42.81565, 80.25796),
new Point(42.88545, 80.57226),
new Point(43.02906, 80.38405),
new Point(43.1683, 80.81526),
new Point(44.11378, 80.36887),
new Point(44.6358, 80.38499),
new Point(44.73408, 80.51589),
new Point(44.90282, 79.87106),
new Point(45.3497, 81.67928),
new Point(45.15748, 81.94803),
new Point(45.13303, 82.56638),
new Point(45.43581, 82.64624),
new Point(45.5831, 82.32179),
new Point(47.20061, 83.03443),
new Point(46.97332, 83.93026),
new Point(46.99361, 84.67804),
new Point(46.8277, 84.80318),
new Point(47.0591, 85.52257),
new Point(47.26221, 85.70139),
new Point(47.93721, 85.53707),
new Point(48.39333, 85.76596),
new Point(48.54277, 86.59791),
new Point(49.1102, 86.87602),
new Point(49.09262, 87.34821),
new Point(49.17295, 87.8407),
new Point(48.98304, 87.89291),
new Point(48.88103, 87.7611),
new Point(48.73499, 88.05942),
new Point(48.56541, 87.99194),
new Point(48.40582, 88.51679),
new Point(48.21193, 88.61179),
new Point(47.99374, 89.08514),
new Point(47.88791, 90.07096),
new Point(46.95221, 90.9136),
new Point(46.57735, 91.07027),
new Point(46.29694, 90.92151),
new Point(46.01735, 91.02651),
new Point(45.57972, 90.68193),
new Point(45.25305, 90.89694),
new Point(45.07729, 91.56088),
new Point(44.95721, 93.5547),
new Point(44.35499, 94.71735),
new Point(44.29416, 95.41061),
new Point(44.01937, 95.34109),
new Point(43.99311, 95.53339),
new Point(43.28388, 95.87901),
new Point(42.73499, 96.38206),
new Point(42.79583, 97.1654),
new Point(42.57194, 99.51012),
new Point(42.67707, 100.8425),
new Point(42.50972, 101.8147),
new Point(42.23333, 102.0772),
new Point(41.88721, 103.4164),
new Point(41.87721, 104.5267),
new Point(41.67068, 104.5237),
new Point(41.58666, 105.0065),
new Point(42.46624, 107.4758),
new Point(42.42999, 109.3107),
new Point(42.64576, 110.1064),
new Point(43.31694, 110.9897),
new Point(43.69221, 111.9583),
new Point(44.37527, 111.4214),
new Point(45.04944, 111.873),
new Point(45.08055, 112.4272),
new Point(44.8461, 112.853),
new Point(44.74527, 113.638),
new Point(45.38943, 114.5453),
new Point(45.4586, 115.7019),
new Point(45.72193, 116.2104),
new Point(46.29583, 116.5855),
new Point(46.41888, 117.3755),
new Point(46.57069, 117.425),
new Point(46.53645, 117.8455),
new Point(46.73638, 118.3147),
new Point(46.59895, 119.7068),
new Point(46.71513, 119.9315),
new Point(46.90221, 119.9225),
new Point(47.66499, 119.125),
new Point(47.99475, 118.5393),
new Point(48.01125, 117.8046),
new Point(47.65741, 117.3827),
new Point(47.88805, 116.8747),
new Point(47.87819, 116.2624),
new Point(47.69186, 115.9231),
new Point(47.91749, 115.5944),
new Point(48.14353, 115.5491),
new Point(48.25249, 115.8358),
new Point(48.52055, 115.8111),
new Point(49.83047, 116.7114),
new Point(49.52058, 117.8747),
new Point(49.92263, 118.5746),
new Point(50.09631, 119.321),
new Point(50.33028, 119.36),
new Point(50.39027, 119.1386),
new Point(51.62083, 120.0641),
new Point(52.115, 120.7767),
new Point(52.34423, 120.6259),
new Point(52.54267, 120.7122),
new Point(52.58805, 120.0819),
new Point(52.76819, 120.0314),
new Point(53.26374, 120.8307),
new Point(53.54361, 123.6147),
new Point(53.18832, 124.4933),
new Point(53.05027, 125.62),
new Point(52.8752, 125.6573),
new Point(52.75722, 126.0968),
new Point(52.5761, 125.9943),
new Point(52.12694, 126.555),
new Point(51.99437, 126.4412),
new Point(51.38138, 126.9139),
new Point(51.26555, 126.8176),
new Point(51.31923, 126.9689),
new Point(51.05825, 126.9331),
new Point(50.74138, 127.2919),
new Point(50.31472, 127.334),
new Point(50.20856, 127.5861),
new Point(49.80588, 127.515),
new Point(49.58665, 127.838),
new Point(49.58443, 128.7119),
new Point(49.34676, 129.1118),
new Point(49.4158, 129.4902),
new Point(48.86464, 130.2246),
new Point(48.86041, 130.674),
new Point(48.60576, 130.5236),
new Point(48.3268, 130.824),
new Point(48.10839, 130.6598),
new Point(47.68721, 130.9922),
new Point(47.71027, 132.5211),
new Point(48.09888, 133.0827),
new Point(48.06888, 133.4843),
new Point(48.39112, 134.4153),
new Point(48.26713, 134.7408),
new Point(47.99207, 134.5576),
new Point(47.70027, 134.7608),
new Point(47.32333, 134.1825),
new Point(46.64017, 133.9977),
new Point(46.47888, 133.8472),
new Point(46.25363, 133.9016),
new Point(45.82347, 133.4761),
new Point(45.62458, 133.4702),
new Point(45.45083, 133.1491),
new Point(45.05694, 133.0253),
new Point(45.34582, 131.8684),
new Point(44.97388, 131.4691),
new Point(44.83649, 130.953),
new Point(44.05193, 131.298),
new Point(43.53624, 131.1912),
new Point(43.38958, 131.3104),
new Point(42.91645, 131.1285),
new Point(42.74485, 130.4327),
new Point(42.42186, 130.6044),
new Point(42.71416, 130.2468),
new Point(42.88794, 130.2514),
new Point(43.00457, 129.9046),
new Point(42.43582, 129.6955),
new Point(42.44624, 129.3493),
new Point(42.02736, 128.9269),
new Point(42.00124, 128.0566),
new Point(41.58284, 128.3002),
new Point(41.38124, 128.1529),
new Point(41.47249, 127.2708),
new Point(41.79222, 126.9047),
new Point(41.61176, 126.5661),
new Point(40.89694, 126.0118),
new Point(40.47037, 124.8851),
new Point(40.09362, 124.3736),
new Point(39.82777, 124.128),
new Point(39.8143, 123.2422),
new Point(39.67388, 123.2167),
new Point(38.99638, 121.648),
new Point(38.8611, 121.6982),
new Point(38.71909, 121.1873),
new Point(38.91221, 121.0887),
new Point(39.09013, 121.6794),
new Point(39.2186, 121.5994),
new Point(39.35166, 121.7511),
new Point(39.52847, 121.2283),
new Point(39.62322, 121.533),
new Point(39.81138, 121.4683),
new Point(40.00305, 121.881),
new Point(40.50562, 122.2987),
new Point(40.73874, 122.0521),
new Point(40.92194, 121.1775),
new Point(40.1961, 120.4468),
new Point(39.87242, 119.5264),
new Point(39.15693, 118.9715),
new Point(39.04083, 118.3273),
new Point(39.19846, 117.889),
new Point(38.67555, 117.5364),
new Point(38.38666, 117.6722),
new Point(38.16721, 118.0281),
new Point(38.1529, 118.8378),
new Point(37.87832, 119.0355),
new Point(37.30054, 118.9566),
new Point(37.14361, 119.2328),
new Point(37.15138, 119.7672),
new Point(37.35228, 119.8529),
new Point(37.83499, 120.7371),
new Point(37.42458, 121.58),
new Point(37.55256, 122.1282),
new Point(37.41833, 122.1814),
new Point(37.39624, 122.5586),
new Point(37.20999, 122.5972),
new Point(37.02583, 122.4005),
new Point(37.01978, 122.5392),
new Point(36.89361, 122.5047),
new Point(36.84298, 122.1923),
new Point(37.00027, 121.9566),
new Point(36.75889, 121.5944),
new Point(36.61666, 120.7764),
new Point(36.52638, 120.96),
new Point(36.37582, 120.8753),
new Point(36.42277, 120.7062),
new Point(36.14075, 120.6956),
new Point(36.0419, 120.3436),
new Point(36.26345, 120.3078),
new Point(36.19998, 120.0889),
new Point(35.95943, 120.2378),
new Point(35.57893, 119.6475),
new Point(34.88499, 119.1761),
new Point(34.31145, 120.2487),
new Point(32.97499, 120.8858),
new Point(32.63889, 120.8375),
new Point(32.42958, 121.3348),
new Point(32.11333, 121.4412),
new Point(32.02166, 121.7066),
new Point(31.67833, 121.8275),
new Point(31.86639, 120.9444),
new Point(32.09361, 120.6019),
new Point(31.94555, 120.099),
new Point(32.30638, 119.8267),
new Point(32.26277, 119.6317),
new Point(31.90388, 120.1364),
new Point(31.98833, 120.7026),
new Point(31.81944, 120.7196),
new Point(31.30889, 121.6681),
new Point(30.97986, 121.8828),
new Point(30.85305, 121.8469),
new Point(30.56889, 120.9915),
new Point(30.33555, 120.8144),
new Point(30.39298, 120.4586),
new Point(30.19694, 120.15),
new Point(30.31027, 120.5082),
new Point(30.06465, 120.7916),
new Point(30.30458, 121.2808),
new Point(29.96305, 121.6778),
new Point(29.88211, 122.1196),
new Point(29.51167, 121.4483),
new Point(29.58916, 121.9744),
new Point(29.19527, 121.9336),
new Point(29.18388, 121.8119),
new Point(29.37236, 121.7969),
new Point(29.19729, 121.7444),
new Point(29.29111, 121.5611),
new Point(29.1634, 121.4135),
new Point(29.02194, 121.6914),
new Point(28.9359, 121.4908),
new Point(28.72798, 121.6113),
new Point(28.84215, 121.1464),
new Point(28.66993, 121.4844),
new Point(28.34722, 121.6417),
new Point(28.13889, 121.3419),
new Point(28.38277, 121.1651),
new Point(27.98222, 120.9353),
new Point(28.07944, 120.5908),
new Point(27.87229, 120.84),
new Point(27.59319, 120.5812),
new Point(27.45083, 120.6655),
new Point(27.20777, 120.5075),
new Point(27.28278, 120.1896),
new Point(27.14764, 120.4211),
new Point(26.89805, 120.0332),
new Point(26.64465, 120.128),
new Point(26.51778, 119.8603),
new Point(26.78823, 120.0733),
new Point(26.64888, 119.8668),
new Point(26.79611, 119.7879),
new Point(26.75625, 119.5503),
new Point(26.44222, 119.8204),
new Point(26.47388, 119.5775),
new Point(26.33861, 119.658),
new Point(26.36777, 119.9489),
new Point(25.99694, 119.4253),
new Point(26.14041, 119.0975),
new Point(25.93788, 119.354),
new Point(25.99069, 119.7058),
new Point(25.67996, 119.5807),
new Point(25.68222, 119.4522),
new Point(25.35333, 119.6454),
new Point(25.60649, 119.3149),
new Point(25.42097, 119.1053),
new Point(25.25319, 119.3526),
new Point(25.17208, 119.2726),
new Point(25.2426, 118.8749),
new Point(24.97194, 118.9866),
new Point(24.88291, 118.5729),
new Point(24.75673, 118.7631),
new Point(24.52861, 118.5953),
new Point(24.53638, 118.2397),
new Point(24.68194, 118.1688),
new Point(24.44024, 118.0199),
new Point(24.46019, 117.7947),
new Point(24.25875, 118.1237),
new Point(23.62437, 117.1957),
new Point(23.65919, 116.9179),
new Point(23.355, 116.7603),
new Point(23.42024, 116.5322),
new Point(23.23666, 116.7871),
new Point(23.21083, 116.5139),
new Point(22.93902, 116.4817),
new Point(22.73916, 115.7978),
new Point(22.88416, 115.6403),
new Point(22.65889, 115.5367),
new Point(22.80833, 115.1614),
new Point(22.70277, 114.8889),
new Point(22.53305, 114.8722),
new Point(22.64027, 114.718),
new Point(22.81402, 114.7782),
new Point(22.69972, 114.5208),
new Point(22.50423, 114.6136),
new Point(22.55004, 114.2223),
new Point(22.42993, 114.3885),
new Point(22.26056, 114.2961),
new Point(22.36736, 113.9056),
new Point(22.50874, 114.0337),
new Point(22.47444, 113.8608),
new Point(22.83458, 113.606),
new Point(23.05027, 113.5253),
new Point(23.11724, 113.8219),
new Point(23.05083, 113.4793),
new Point(22.87986, 113.3629),
new Point(22.54944, 113.5648),
new Point(22.18701, 113.5527),
new Point(22.56701, 113.1687),
new Point(22.17965, 113.3868),
new Point(22.04069, 113.2226),
new Point(22.20485, 113.0848),
new Point(21.8693, 112.94),
new Point(21.96472, 112.824),
new Point(21.70139, 112.2819),
new Point(21.91611, 111.8921),
new Point(21.75139, 111.9669),
new Point(21.77819, 111.6762),
new Point(21.61264, 111.7832),
new Point(21.5268, 111.644),
new Point(21.52528, 111.0285),
new Point(21.21138, 110.5328),
new Point(21.37322, 110.3944),
new Point(20.84381, 110.1594),
new Point(20.84083, 110.3755),
new Point(20.64, 110.3239),
new Point(20.48618, 110.5274),
new Point(20.24611, 110.2789),
new Point(20.2336, 109.9244),
new Point(20.4318, 110.0069),
new Point(20.92416, 109.6629),
new Point(21.44694, 109.9411),
new Point(21.50569, 109.6605),
new Point(21.72333, 109.5733),
new Point(21.49499, 109.5344),
new Point(21.39666, 109.1428),
new Point(21.58305, 109.1375),
new Point(21.61611, 108.911),
new Point(21.79889, 108.8702),
new Point(21.59888, 108.7403),
new Point(21.93562, 108.4692),
new Point(21.59014, 108.5125),
new Point(21.68999, 108.3336),
new Point(21.51444, 108.2447),
new Point(21.54241, 107.99),
new Point(21.66694, 107.7831),
new Point(21.60526, 107.3627),
new Point(22.03083, 106.6933),
new Point(22.45682, 106.5517),
new Point(22.76389, 106.7875),
new Point(22.86694, 106.7029),
new Point(22.91253, 105.8771),
new Point(23.32416, 105.3587),
new Point(23.18027, 104.9075),
new Point(22.81805, 104.7319),
new Point(22.6875, 104.3747),
new Point(22.79812, 104.1113),
new Point(22.50387, 103.9687),
new Point(22.78287, 103.6538),
new Point(22.58436, 103.5224),
new Point(22.79451, 103.3337),
new Point(22.43652, 103.0304),
new Point(22.77187, 102.4744),
new Point(22.39629, 102.1407),
new Point(22.49777, 101.7415),
new Point(22.20916, 101.5744),
new Point(21.83444, 101.7653),
new Point(21.14451, 101.786),
new Point(21.17687, 101.2919),
new Point(21.57264, 101.1482),
new Point(21.76903, 101.099),
new Point(21.47694, 100.6397),
new Point(21.43546, 100.2057),
new Point(21.72555, 99.97763),
new Point(22.05018, 99.95741),
new Point(22.15592, 99.16785),
new Point(22.93659, 99.56484),
new Point(23.08204, 99.5113),
new Point(23.18916, 98.92747),
new Point(23.97076, 98.67991),
new Point(24.16007, 98.89073),
new Point(23.92999, 97.54762),
new Point(24.26055, 97.7593),
new Point(24.47666, 97.54305),
new Point(24.73992, 97.55255),
new Point(25.61527, 98.19109),
new Point(25.56944, 98.36137),
new Point(25.85597, 98.7104),
new Point(26.12527, 98.56944),
new Point(26.18472, 98.73109),
new Point(26.79166, 98.77777),
new Point(27.52972, 98.69699),
new Point(27.6725, 98.45888),
new Point(27.54014, 98.31992),
new Point(28.14889, 98.14499),
new Point(28.54652, 97.55887),
new Point(28.22277, 97.34888),
new Point(28.46749, 96.65387),
new Point(28.35111, 96.40193),
new Point(28.525, 96.34027),
new Point(28.79569, 96.61373),
new Point(29.05666, 96.47083),
new Point(28.90138, 96.17532),
new Point(29.05972, 96.14888),
new Point(29.25757, 96.39172),
new Point(29.46444, 96.08315),
new Point(29.03527, 95.38777),
new Point(29.33346, 94.64751),
new Point(29.07348, 94.23456),
new Point(28.6692, 93.96172),
new Point(28.61876, 93.35194),
new Point(28.3193, 93.22205),
new Point(28.1419, 92.71044),
new Point(27.86194, 92.54498),
new Point(27.76472, 91.65776),
new Point(27.945, 91.66277),
new Point(28.08111, 91.30138),
new Point(27.96999, 91.08693),
new Point(28.07958, 90.3765),
new Point(28.24257, 90.38898),
new Point(28.32369, 89.99819),
new Point(28.05777, 89.48749),
new Point(27.32083, 88.91693)
});
polygon.Add(new Point[] //Taiwan
{
new Point(25.13474, 121.4441),
new Point(25.28361, 121.5632),
new Point(25.00722, 122.0004),
new Point(24.85028, 121.8182),
new Point(24.47638, 121.8397),
new Point(23.0875, 121.3556),
new Point(21.92791, 120.7196),
new Point(22.31277, 120.6103),
new Point(22.54044, 120.3071),
new Point(23.04437, 120.0539),
new Point(23.61708, 120.1112),
new Point(25.00166, 121.0017),
new Point(25.13474, 121.4441)
});
polygon.Add(new Point[] //Hainan
{
new Point(19.52888, 110.855),
new Point(19.16761, 110.4832),
new Point(18.80083, 110.5255),
new Point(18.3852, 110.0503),
new Point(18.39152, 109.7594),
new Point(18.19777, 109.7036),
new Point(18.50562, 108.6871),
new Point(19.28028, 108.6283),
new Point(19.76, 109.2939),
new Point(19.7236, 109.1653),
new Point(19.89972, 109.2572),
new Point(19.82861, 109.4658),
new Point(19.99389, 109.6108),
new Point(20.13361, 110.6655),
new Point(19.97861, 110.9425),
new Point(19.63829, 111.0215),
new Point(19.52888, 110.855)
});
polygon.Add(new Point[] //Chongming
{
new Point(31.80054, 121.2039),
new Point(31.49972, 121.8736),
new Point(31.53111, 121.5464),
new Point(31.80054, 121.2039)
});
}
private bool IsInsideChina(GeoCoordinate positionToCheck)
{
if (positionToCheck == null || positionToCheck.IsUnknown)
return false;
return polygon.Any(s => IsInsidePolygon(s, positionToCheck.Latitude, positionToCheck.Longitude));
}
private bool IsInsidePolygon(Point[] poly, double x, double y)
{
var index = 0;
var inside = false;
var prePoint = poly[0];
for (index = 1; index < poly.Length; index++)
{
var nextPoint = poly[index];
if (y > Math.Min(prePoint.Y, nextPoint.Y)
&& y <= Math.Max(prePoint.Y, nextPoint.Y)
&& x <= Math.Max(prePoint.X, nextPoint.X)
&& prePoint.Y != nextPoint.Y)
{
double xinters = (y - prePoint.Y) * (nextPoint.X - prePoint.X) / (nextPoint.Y - prePoint.Y) + prePoint.X;
if (prePoint.X == nextPoint.X || x <= xinters)
inside ^= true;
}
prePoint = nextPoint;
}
return inside;
}
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
InitPolygon();
foreach (var points in polygon)
{
var poly = new MapPolyline();
poly.Locations = new LocationCollection();
for (int i = 0; i < points.Length; i++)
{
poly.Locations.Add(new GeoCoordinate(points[i].X, points[i].Y));
}
poly.Stroke = new SolidColorBrush(Colors.Red);
myMap.Children.Add(poly);
}
myMap.SetView(new GeoCoordinate(39.4916649164344, 108.745576123357), 3.7090892722116307);
}
#endregion
方法3,根據國家行政邊界近似判定
有時候需要一種快速的方法判定,這種快速判定的方法是從Nokia Maps中挖掘出來的。基本思路是:把整個行政區域劃分爲幾個小的矩形,然後再排除掉一些矩形區域。只要一個點在限定的區域內,並且不在排除的區域內,則判定成功,否則失敗。下圖中藍色區域爲限定區域,紅色區域爲排除區域。
此方法的實現代碼如下:
#region rectangle
private class Rectangle
{
public double West;
public double North;
public double East;
public double South;
public Rectangle(double latitude1, double longitude1, double latitude2, double longitude2)
{
this.West = Math.Min(longitude1, longitude2);
this.North = Math.Max(latitude1, latitude2);
this.East = Math.Max(longitude1, longitude2);
this.South = Math.Min(latitude1, latitude2);
}
}
private static Rectangle[] region = new Rectangle[]
{
new Rectangle(49.220400, 079.446200, 42.889900, 096.330000),
new Rectangle(54.141500, 109.687200, 39.374200, 135.000200),
new Rectangle(42.889900, 073.124600, 29.529700, 124.143255),
new Rectangle(29.529700, 082.968400, 26.718600, 097.035200),
new Rectangle(29.529700, 097.025300, 20.414096, 124.367395),
new Rectangle(20.414096, 107.975793, 17.871542, 111.744104),
};
private static Rectangle[] exclude = new Rectangle[]
{
new Rectangle(25.398623, 119.921265, 21.785006, 122.497559),
new Rectangle(22.284000, 101.865200, 20.098800, 106.665000),
new Rectangle(21.542200, 106.452500, 20.487800, 108.051000),
new Rectangle(55.817500, 109.032300, 50.325700, 119.127000),
new Rectangle(55.817500, 127.456800, 49.557400, 137.022700),
new Rectangle(44.892200, 131.266200, 42.569200, 137.022700),
};
public static bool IsInsideChina(GeoCoordinate pos)
{
for (int i = 0; i < region.Length; i++)
{
if (InRectangle(region[i], pos))
{
for (int j = 0; j < exclude.Length; j++)
{
if (InRectangle(exclude[j], pos))
{
return false;
}
}
return true;
}
}
return false;
}
private static bool InRectangle(Rectangle rect, GeoCoordinate pos)
{
return rect.West <= pos.Longitude && rect.East >= pos.Longitude && rect.North >= pos.Latitude && rect.South <= pos.Latitude;
}
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
DrawRect(region, new SolidColorBrush(Colors.Blue));
DrawRect(exclude, new SolidColorBrush(Colors.Red));
myMap.SetView(new GeoCoordinate(39.4916649164344, 108.745576123357), 3.7090892722116307);
}
private void DrawRect(Rectangle[] rects, SolidColorBrush brush)
{
foreach (var rect in rects)
{
var poly = new MapPolyline();
poly.Locations = new LocationCollection();
poly.Locations.Add(new GeoCoordinate(rect.North, rect.West));
poly.Locations.Add(new GeoCoordinate(rect.North, rect.East));
poly.Locations.Add(new GeoCoordinate(rect.South, rect.East));
poly.Locations.Add(new GeoCoordinate(rect.South, rect.West));
poly.Locations.Add(new GeoCoordinate(rect.North, rect.West));
poly.Stroke = brush;
myMap.Children.Add(poly);
}
}
#endregion
這幾種方法的比較
優 | 劣 | 備註 | |
方法1 通過網絡地圖接口查詢 |
簡單、準確 信息量大 |
速度慢 依賴網絡 |
可用Google、Baidu地圖接口 |
方法2 根據國家行政邊界判定 |
準確 | 代碼相對較多 判別計算次數較多 陸地邊界數據誤差較大 |
方法和數據從gMaps中挖掘 |
方法3 根據國家行政邊界近似判定 |
快速 相對準確 |
邊界誤差較大 | 方法和數據從Nokia Maps中挖掘 |
PS:你可能會問,在方法3中爲什麼把臺灣給排除掉了,這是因爲所謂的座標偏移對臺灣地區不適用。對於香港澳門地區,視情況需要單獨判定。