前言
圖資的費用相當的昂貴,台灣大部分可靠的圖資來源
就我所知有Google、Map8
Map8也是有一些額外需要處理判斷的
一般情況如果只需要Zip還需要透過圖資的話,是相當的浪費成本,可以大大節省費用
事先準備資料
取得資料
取得台灣各縣市的經緯度資料
我國各鄉(鎮、市、區)行政區域界線圖資
https://data.gov.tw/dataset/7441
下載的資料檔案轉出可以使用的json檔
.dbf, .prj, .shp, .shx 檔案一同轉出geojson
https://products.aspose.app/gis/conversion/shapefile-to-json
整理資料
將各縣市區域的最大範圍先存起來,方便之後快速篩選符合的名單
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| ini_set('memory_limit','2048M'); $file = Storage::get('TOWN_MOI_1100415.geojson'); $datas = json_decode($file, true); $infos = []; foreach ($datas['features'] as $index => $data) { $infos[] = [ 'property' => $data['properties'], 'maxRange' => $this->getMaxRange($data['geometry']['coordinates']), ]; $path = 'area/' . $data['properties']['COUNTYCODE'] . '/' . $data['properties']['TOWNCODE']. '.json';
if (!is_float($data['geometry']['coordinates'][0][0][0])) { foreach ($data['geometry']['coordinates'] as $index2 => $area) { $data['geometry']['coordinates'][$index2] = $data['geometry']['coordinates'][$index2][0]; } }
Storage::disk('public')->put($path, json_encode($data['geometry']['coordinates'])); };
Storage::disk('public')->put('area/max_range.json', json_encode($infos));
|
實作方法
利用射線法來判斷「點」是否在多邊形內,
穿出範圍要是經過為「奇數」為內部
方法與邏輯(減少判斷Loading)
- 取得各行政區的最大與最小經緯度 max_lat, max_lng, min_lat, min_lng,將符合的區域列入候補名單中
- 假設查詢的點剛好在線的「起始」、「終點」其中一點上,則判斷為圈內
- 假設查詢的點(Y)與該線的(Y)一樣,判斷為擦邊不算經過的點之一
- 判斷查詢的點(Y)是否在比對的線的(Y)範圍內
- 計算左射線與比對線是否有香蕉 (count+1)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| foreach ($qualifierAreas as $area) { $filePath = 'area/' . $area['property']['COUNTYCODE'] . '/' . $area['property']['TOWNCODE'] . '.json'; $blocks = json_decode(Storage::disk('public')->get($filePath), true); Log::info('符合名單', [$area['property']]); //一個行政區有可能多個圈(ex:澎湖) foreach ($blocks as $block) { $startPoint = $block[0]; $count = 0; $inside = true; for ($i = 1 ; $i < count($block) ; $i++) { $endPoint = $block[$i]; // 在點頂上 if (($point[0] == $startPoint[0] && $point[1] == $startPoint[1]) || ($point[0] == $endPoint[0] && $point[1] == $endPoint[1])) { $inside = true; }
// 查詢的點(Y)與該線的(Y)一樣,判斷為擦邊不算經過的點之一 if ($point[1] == min($startPoint[1], $endPoint[1])) { $startPoint = $endPoint;
continue; }
// 判斷水平射線是否Y在兩點範圍內 $maxY = max($startPoint[1], $endPoint[1]); $minY = min($startPoint[1], $endPoint[1]); if ($point[1] <= $minY || $point[1] >= $maxY) { $startPoint = $endPoint;
continue; }
//計算左射線與線兩條是否相交 $x = $endPoint[0] - ($endPoint[1] - $point[1]) * ($endPoint[0] - $startPoint[0]) / ($endPoint[1] - $startPoint[1]);
if ($x < $point[0]) { Log::info('第' . ($count+1) .'個香蕉點:', [$startPoint, $endPoint, [$x, $point[1]]]); $count += 1; } else if ($x > $point[0]) { Log::info('香蕉點在右側:', [$startPoint, $endPoint, [$x, $point[1]]]); } else { Log::info('點在多邊形上'); $inside = false; }
$startPoint = $endPoint; }
if ($count % 2 == 0 || !$inside) { Log::info('在多邊形外'); continue; }
Log::info('在多邊形內------------------------', [$area]); $costTime = microtime(true) - $startTime; Log::info('結束總耗費' . $costTime);
return $area; } }
|
DEMO網址
https://tool.tyeydy.com/area-by-lat-lng
參考網站:
http://wyj-learning.blogspot.com/2018/07/python-100.html
https://www.itread01.com/content/1546013522.html
如果這一篇文章有幫助到你的話,
請幫忙點選廣告,都是友善方式不強迫點擊