今までの手順で作成した白地図は県の輪郭線が途切れていました。そこで輪郭線が途切れない様に処理を変更しました。市区町村の重なりを求め削除し、そこから都道府県の輪郭線を作るという流れは同じですが、市区町村の重なりを作った時に内側に一つ座標を短くしています。それにより都道府県の輪郭が切れずに求まります。
プログラムの全文は次の通りです。下に部分ごとの解説を書いてあります。いままでのプログラムはfunctionを使わずに書いてて見通しがわるかったので機能ごとにfunctionにまとめてあります。
makePrefBoundNoJoint.php
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 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 |
<?php //国土数値情報行政区域から県境だけのデータを作る。 //各県の境界線と海岸線を切れ目なしで作る。 //市区町村の重なりを調べる。端っこを1つオフセットする。最後に引き算で輪郭を求める。 ini_set('memory_limit', '1280M'); mb_language("Japanese");//文字コードの設定 mb_internal_encoding("UTF-8"); $loadXML[1] = "./国土数値情報行政区域/N03-170101_01_GML/N03-17_01_170101.xml"; $saveFile[1] ="hokkaido"; $loadXML[2] = "./国土数値情報行政区域/N03-170101_02_GML/N03-17_02_170101.xml"; $saveFile[2] ="aomori"; $loadXML[3] = "./国土数値情報行政区域/N03-170101_03_GML/N03-17_03_170101.xml"; $saveFile[3] ="iwate"; $loadXML[4] = "./国土数値情報行政区域/N03-170101_04_GML/N03-17_04_170101.xml"; $saveFile[4] ="miyagi"; $loadXML[5] = "./国土数値情報行政区域/N03-170101_05_GML/N03-17_05_170101.xml"; $saveFile[5] ="akita"; $loadXML[6] = "./国土数値情報行政区域/N03-170101_06_GML/N03-17_06_170101.xml"; $saveFile[6] ="yamagata"; $loadXML[7] = "./国土数値情報行政区域/N03-170101_07_GML/N03-17_07_170101.xml"; $saveFile[7] ="fukushima"; $loadXML[8] = "./国土数値情報行政区域/N03-170101_08_GML/N03-17_08_170101.xml"; $saveFile[8] ="ibaraki"; $loadXML[9] = "./国土数値情報行政区域/N03-170101_09_GML/N03-17_09_170101.xml"; $saveFile[9] ="tochigi"; $loadXML[10] = "./国土数値情報行政区域/N03-170101_10_GML/N03-17_10_170101.xml"; $saveFile[10] ="gunnma"; $loadXML[11] = "./国土数値情報行政区域/N03-170101_11_GML/N03-17_11_170101.xml"; $saveFile[11] ="saitama"; $loadXML[12] = "./国土数値情報行政区域/N03-170101_12_GML/N03-17_12_170101.xml"; $saveFile[12] ="chiba"; $loadXML[13] = "./国土数値情報行政区域/N03-170101_13_GML/N03-17_13_170101.xml"; $saveFile[13] ="tokyo"; $loadXML[14] = "./国土数値情報行政区域/N03-170101_14_GML/N03-17_14_170101.xml"; $saveFile[14] ="kanagawa"; $loadXML[15] = "./国土数値情報行政区域/N03-170101_15_GML/N03-17_15_170101.xml"; $saveFile[15] ="niigata"; $loadXML[16] = "./国土数値情報行政区域/N03-170101_16_GML/N03-17_16_170101.xml"; $saveFile[16] ="toyama"; $loadXML[17] = "./国土数値情報行政区域/N03-170101_17_GML/N03-17_17_170101.xml"; $saveFile[17] ="isihikawa"; $loadXML[18] = "./国土数値情報行政区域/N03-170101_18_GML/N03-17_18_170101.xml"; $saveFile[18] ="fukui"; $loadXML[19] = "./国土数値情報行政区域/N03-170101_19_GML/N03-17_19_170101.xml"; $saveFile[19] ="yamanashi"; $loadXML[20] = "./国土数値情報行政区域/N03-170101_20_GML/N03-17_20_170101.xml"; $saveFile[20] ="nagano"; $loadXML[21] = "./国土数値情報行政区域/N03-170101_21_GML/N03-17_21_170101.xml"; $saveFile[21] ="gifu"; $loadXML[22] = "./国土数値情報行政区域/N03-170101_22_GML/N03-17_22_170101.xml"; $saveFile[22] ="sizuoka"; $loadXML[23] = "./国土数値情報行政区域/N03-170101_23_GML/N03-17_23_170101.xml"; $saveFile[23] ="aiti"; $loadXML[24] = "./国土数値情報行政区域/N03-170101_24_GML/N03-17_24_170101.xml"; $saveFile[24] ="mie"; $loadXML[25] = "./国土数値情報行政区域/N03-170101_25_GML/N03-17_25_170101.xml"; $saveFile[25] ="shiga"; $loadXML[26] = "./国土数値情報行政区域/N03-170101_26_GML/N03-17_26_170101.xml"; $saveFile[26] ="kyoto"; $loadXML[27] = "./国土数値情報行政区域/N03-170101_27_GML/N03-17_27_170101.xml"; $saveFile[27] ="osaka"; $loadXML[28] = "./国土数値情報行政区域/N03-170101_28_GML/N03-17_28_170101.xml"; $saveFile[28] ="hyougo"; $loadXML[29] = "./国土数値情報行政区域/N03-170101_29_GML/N03-17_29_170101.xml"; $saveFile[29] ="nara"; $loadXML[30] = "./国土数値情報行政区域/N03-170101_30_GML/N03-17_30_170101.xml"; $saveFile[30] ="wakayama"; $loadXML[31] = "./国土数値情報行政区域/N03-170101_31_GML/N03-17_31_170101.xml"; $saveFile[31] ="tottori"; $loadXML[32] = "./国土数値情報行政区域/N03-170101_32_GML/N03-17_32_170101.xml"; $saveFile[32] ="shimane"; $loadXML[33] = "./国土数値情報行政区域/N03-170101_33_GML/N03-17_33_170101.xml"; $saveFile[33] ="okayama"; $loadXML[34] = "./国土数値情報行政区域/N03-170101_34_GML/N03-17_34_170101.xml"; $saveFile[34] ="hiroshima"; $loadXML[35] = "./国土数値情報行政区域/N03-170101_35_GML/N03-17_35_170101.xml"; $saveFile[35] ="yamaguchi"; $loadXML[36] = "./国土数値情報行政区域/N03-170101_36_GML/N03-17_36_170101.xml"; $saveFile[36] ="tokushima"; $loadXML[37] = "./国土数値情報行政区域/N03-170101_37_GML/N03-17_37_170101.xml"; $saveFile[37] ="kagawa"; $loadXML[38] = "./国土数値情報行政区域/N03-170101_38_GML/N03-17_38_170101.xml"; $saveFile[38] ="ehime"; $loadXML[39] = "./国土数値情報行政区域/N03-170101_39_GML/N03-17_39_170101.xml"; $saveFile[39] ="kouchi"; $loadXML[40] = "./国土数値情報行政区域/N03-170101_40_GML/N03-17_40_170101.xml"; $saveFile[40] ="fukuoka"; $loadXML[41] = "./国土数値情報行政区域/N03-170101_41_GML/N03-17_41_170101.xml"; $saveFile[41] ="saga"; $loadXML[42] = "./国土数値情報行政区域/N03-170101_42_GML/N03-17_42_170101.xml"; $saveFile[42] ="nagasaki"; $loadXML[43] = "./国土数値情報行政区域/N03-170101_43_GML/N03-17_43_170101.xml"; $saveFile[43] ="kumamoto"; $loadXML[44] = "./国土数値情報行政区域/N03-170101_44_GML/N03-17_44_170101.xml"; $saveFile[44] ="ooita"; $loadXML[45] = "./国土数値情報行政区域/N03-170101_45_GML/N03-17_45_170101.xml"; $saveFile[45] ="miyazaki"; $loadXML[46] = "./国土数値情報行政区域/N03-170101_46_GML/N03-17_46_170101.xml"; $saveFile[46] ="kagoshima"; $loadXML[47] = "./国土数値情報行政区域/N03-170101_47_GML/N03-17_47_170101.xml"; $saveFile[47] ="okinawa"; $startKenNo = 1; $endKenNo = 47; //県ごとに処理を繰り返す for ($i=$startKenNo; $i<$endKenNo+1; $i++){ $startTime = microtime(true); $coordinate = readXMLfromFile($loadXML[$i]);//XMLから読み込み $max = getMaxPointDistance($coordinate);//座標データで一番はなれているところの二乗を求める。 $ave = getAvePointDistance($coordinate);//平均を求める $cityBorder = checkBorder($coordinate);//市区町村の境界線を求める。 $cityBorder = separateBorder($cityBorder,$max);//線が飛んでいるところを別の図形にする。 $cityBorder = arrayCleanup($cityBorder);//配列をきれいにする。 $cityBorder = cityBoarderEdge($cityBorder);//ここで端の線を1つ短くする処理をする。両側を短くする。 $cityBorder = arrayCleanup($cityBorder);//配列をきれいにする。 $prefBorder = getPrefBorder($coordinate,$cityBorder);//すべてのデータcoordinateと市区町村の境界線から県境をもとめる。 $prefBorder = separateBorder($prefBorder,$max);//線が飛んでいる部分を処理する。 $prefBorder = arrayCleanup($prefBorder);//配列をきれいにする。 $prefBorder = deleteShortLine($prefBorder);//点が3つ以下の部分を削除 $prefBorder = deleteEndLine($prefBorder,$ave);//図形の端っこの点が離れている場合には削除する。 $saveSVG = "./PrefSVG/" .$saveFile[$i] .".svg"; $saveXML = "./PrefXML/" .$saveFile[$i] .".xml"; drawSVG($saveSVG,$prefBorder); saveXMLdata($saveXML,$prefBorder); $endTime = microtime(true); $exeTime = $endTime - $startTime; print(" [$saveFile[$i]]exe Time=".$exeTime . "\n"); } //=====================県ごとの座標と座標の距離をはかり最大値を返す。 function getMaxPointDistance($coordinate){ $min = 10.00; $max = 0.0; $figMax = count($coordinate); for ($i=0 ;$i<$figMax ; $i++){ $jmax = count($coordinate[$i]); $j = 0; $latilongArray = explode(" ",$coordinate[$i][$j]);//空白区切りでデータを取り出す $ox = floatval($latilongArray[0]); $oy = floatval($latilongArray[1]); for ($j=1 ;$j<$jmax ; $j++){ $latilongArray = explode(" ",$coordinate[$i][$j]);//空白区切りでデータを取り出す $x = floatval($latilongArray[0]); $y = floatval($latilongArray[1]); $dx = $ox - $x; $dy = $oy - $y; $s = $dx * $dx + $dy * $dy;//距離の二乗 この値で判定する。 $ox = $x; $oy = $y; if ($min>$s){ $min = $s; } if ($max<$s){ $max = $s; } }// for $j }// for $i} return $max; } //=====================県ごとの座標と座標の距離をはかり平均値を返す。 function getAvePointDistance($coordinate){ $total = 0.0; $averageArray = array(); $figMax = count($coordinate); for ($i=0 ;$i<$figMax ; $i++){ $count = 0; $total = 0; $jmax = count($coordinate[$i]); $j = 0; $latilongArray = explode(" ",$coordinate[$i][$j]);//空白区切りでデータを取り出す $ox = floatval($latilongArray[0]); $oy = floatval($latilongArray[1]); for ($j=1 ;$j<$jmax ; $j++){ $latilongArray = explode(" ",$coordinate[$i][$j]);//空白区切りでデータを取り出す $x = floatval($latilongArray[0]); $y = floatval($latilongArray[1]); $dx = $ox - $x; $dy = $oy - $y; $s = $dx * $dx + $dy * $dy;//距離の二乗 この値で判定する。 $ox = $x; $oy = $y; $count++; $total = $total + $s; }// for $j $averageArray[$i] = $total / $count; }// for $i} $total = array_sum($averageArray); $ave = $total / count($averageArray); return $ave; } //======================市区町村の橋を処理する。 function cityBoarderEdge($inputArray){ $iMax = count($inputArray); for ($i=0 ;$i<$iMax ; $i++){ $jMax = count($inputArray[$i]); if($jMax !=0){ $k = 0; unset($inputArray[$i][$k]); $inputArray[$i] = array_values($inputArray[$i]);//削除した分をつめる $k = count($inputArray[$i])-1; unset($inputArray[$i][$k]); $inputArray[$i] = array_values($inputArray[$i]); }//if } return $inputArray; } //=======================市区町村の境界線を求める。 function checkBorder($coordinate){ //かさなっている部分、市区町村の境界線を$innerBorderに入れる。 $figMax = count($coordinate); $c = 0; for ($fig1=0; $fig1<$figMax; $fig1++){ echo(" step1 (" . $fig1 . "/" . $figMax . ") \r"); for ($fig2=$fig1+1; $fig2<$figMax; $fig2++){ $overlap = array_intersect($coordinate[$fig1],$coordinate[$fig2]);//重なった値を返す if (count($overlap) !== 0){ $innerBorder[$c] = array_values($overlap); $c++; }//if }//fig2 }//fig1 //値が一つしかない点のデータの時には削除する。 $iMax = count($innerBorder); for ($i=0; $i<$iMax; $i++){ if (count($innerBorder[$i])==1){ unset($innerBorder[$i]); } } $innerBorder = array_values($innerBorder); return $innerBorder; } //=========================値が3つ以下の点のデータの時には削除する。 function deleteShortLine($innerBorder){ $iMax = count($innerBorder); for ($i=0; $i<$iMax; $i++){ echo(" step3 (" . $i . "/" . $iMax . ") \r"); if (isset($innerBorder[$i])){ $a = count($innerBorder[$i])-1; if ($a <= 5){//図形のポイントが少ない時に他の図形と端点が重ならない時に削除 $X = $innerBorder[$i][0]; $Y = $innerBorder[$i][$a]; for ($j=0; $j<$iMax; $j++){//他の図形と比較 if (isset($innerBorder[$j])){ $b = count($innerBorder[$j])-1;//最後の点の番号 if ($b > 5 ){ $pointA = $innerBorder[$j][0];//重なりがあるかチェック $pointB = $innerBorder[$j][$b]; if($pointA === $X || $pointA === $Y ||// $pointB === $X || $pointB === $Y ){ //点がどこかと重なっている。 } else { unset($innerBorder[$i]); }//if }//if } }//for j }//if }//isset }//for i $innerBorder = array_values($innerBorder); return $innerBorder; } //=======================市区町村の境界線から県境を求める。 function getPrefBorder($coordinate,$cityBorder){ $prefBorder = $coordinate;//データを$prefBorderにいれてこちらで処理する。 $iMax = count($prefBorder); $jMax = count($cityBorder); //print("iMax=$iMax jMax=$jMax\n"); for ($i=0; $i<$iMax; $i++){ echo(" step2 (" . $i . "/" . $iMax . ") \r"); for ($j=0 ;$j<$jMax; $j++){ $checkArray = array_intersect($prefBorder[$i],$cityBorder[$j]);//重なった値を返す if (count($checkArray)!== 0){//重なった時だけ引き算をする。 $prefBorder[$i] = array_values(array_diff($prefBorder[$i],$cityBorder[$j])); } }//for $j }//for $i $prefBorder = array_values($prefBorder); return $prefBorder; } //===================線が飛んでいる場合には2つの図形にわける。 function separateBorder($prefBorder,$max){ $iMax = count($prefBorder); $m = 0; for ($i=0 ;$i<$iMax ; $i++){ $jMax = count($prefBorder[$i]); if($jMax !=0){ $n = 0;//新しい配列に入れるためにnを0にする。 $j=0; $coadingData = explode(" ",$prefBorder[$i][$j]);//空白区切りでデータを取り出す $ox = floatval($coadingData[0]);//各要素を取り出す 文字列のデータをfloatに変換する。 $oy = floatval($coadingData[1]); $temp[$m][$n] = $prefBorder[$i][$j]; for ($j=1 ;$j<$jMax ; $j++){ $n++;//次のためにnをひとつ進める。 $coadingData = explode(" ",$prefBorder[$i][$j]);//空白区切りでデータを取り出す $x = floatval($coadingData[0]);//各要素を取り出す 文字列のデータをfloatに変換する。 $y = floatval($coadingData[1]); $dx = $x - $ox; $dy = $y - $oy; $s = $dx * $dx + $dy * $dy;//$jと$j+1の距離の二乗 この値で判定する。 if ($s > $max){//遠くにあるときの処理,最初のデータの中で最大の距離が$max。それよりも遠いものをはなれていると判断する。 $m++;//新規の図形 新しい配列に入れる。 $n = 0;//新しい配列の先頭から } $temp[$m][$n] = $prefBorder[$i][$j];//$jのデータを配列に入れる。tempは作業用の仮の配列 $ox = $x; $oy = $y; } $m++;//元の配列が一つ終わったら次の配列にする。 }// for $j }// for $i return $temp; } //=====================両端が離れている場合は削除 function deleteEndLine($coordinate,$max){ $iMax = count($coordinate); for ($i=0; $i<$iMax; $i++){ $a = count($coordinate[$i])-1; $p1 = $coordinate[$i][0]; $p2 = $coordinate[$i][1]; $p3 = $coordinate[$i][$a-1]; $p4 = $coordinate[$i][$a]; $coadingData = explode(" ",$p1);//空白区切りでデータを取り出す $ox = floatval($coadingData[0]);//各要素を取り出す 文字列のデータをfloatに変換する。 $oy = floatval($coadingData[1]); $coadingData = explode(" ",$p2);//空白区切りでデータを取り出す $x = floatval($coadingData[0]);//各要素を取り出す 文字列のデータをfloatに変換する。 $y = floatval($coadingData[1]); $dx = $x - $ox; $dy = $y - $oy; $s = $dx * $dx + $dy * $dy;//$jと$j+1の距離の二乗 この値で判定する。 if($s > $max ){ unset($coordinate[$i][0]); }//if $coadingData = explode(" ",$p3);//空白区切りでデータを取り出す $ox = floatval($coadingData[0]);//各要素を取り出す 文字列のデータをfloatに変換する。 $oy = floatval($coadingData[1]); $coadingData = explode(" ",$p4);//空白区切りでデータを取り出す $x = floatval($coadingData[0]);//各要素を取り出す 文字列のデータをfloatに変換する。 $y = floatval($coadingData[1]); $dx = $x - $ox; $dy = $y - $oy; $s = $dx * $dx + $dy * $dy;//$jと$j+1の距離の二乗 この値で判定する。 if($s > $max ){ unset($coordinate[$i][$a]); }//if $coordinate[$i] = array_values($coordinate[$i]); }//for i $coordinate = array_values($coordinate); return $coordinate; } //============================配列から空白やゼロの部分を取り除く function arrayCleanup($coordinate){ $figMax = count($coordinate); //print("in=$figMax\n"); for ($i= 0;$i<$figMax ; $i++){ $iMax = count($coordinate[$i]); if ($iMax != 0){ for ($j = 0 ; $j<$iMax; $j++){ if ($coordinate[$i][$j] == ""){ unset($coordinate[$i][$j]); print("*"); }// if }//for j $coordinate[$i] = array_values($coordinate[$i]); } else { unset($coordinate[$i]); } }//for i //print("\n"); $coordinate = array_values($coordinate); $figMax2 = count($coordinate); //print("int=$figMax out=$figMax2 \n"); return $coordinate; } //=========================XMLで図形を書き出す function saveXMLdata($saveXML,$coordinate){ $contents='<?xml version="1.0" encoding="UTF-8"?>' . "\n"; $contents .='<ksj>' . "\n"; $figMax = count($coordinate); for ($fig=0; $fig<$figMax; $fig++){ $jmax = count($coordinate[$fig]); $contents .='<obj>'. "\n";//県の書き出し for ($j=0 ;$j<$jmax ; $j++){ $contents .= $coordinate[$fig][$j] . "\n"; }//for j $contents .='</obj>'. "\n"; }//for fig $contents .='</ksj>' . "\n"; file_put_contents($saveXML, $contents); }//function //=====================配列データからSVGを書き出すfunction========================= function drawSVG($saveSVG,$coordinate){ //スクリーンサイズ $screenWidth = 4000; $screenHeight = 4000; //日本地図を描くための範囲を決める。 $north_max = 46.0;//最北端:北緯45度33分28秒 東経148度45分14秒 46.0 $south_max = 20.0;//最南端:北緯20度25分30.6585秒 東経136度4分11.1766秒 20.0 $east_max = 154.0;//最東端:北緯24度17分12秒 東経153度58分50秒 154.0 $west_max = 122.0;//最西端:北緯24度26分58秒 東経122度56分01秒 122.0 $y_max = log(tan(pi()/4.0 + deg2rad($north_max)/2.0)); $y_min = log(tan(pi()/4.0 + deg2rad($south_max)/2.0)); $x_max = deg2rad($east_max);//横方向 $x_min= deg2rad($west_max); $cLo = ($x_max - $x_min)/2 + $x_min; $dx = $x_max - $x_min; $dy = $y_max - $y_min; $mX = $screenWidth/$dx; $mY = $screenHeight/$dy; if ($mX < $mY){ $multiple = $mX; } else { $multiple = $mY; } //y軸の描画用の定数 $yOffset = $y_max; //==========================SVGファイルを書き出す $contents='<?xml version="1.0" standalone="no"?>' . "\n"; $contents .='<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">' . "\n"; $contents .='<svg width="40cm" height="40cm" viewBox="0 0 4000 4000" xmlns="http://www.w3.org/2000/svg" version="1.1">' . "\n"; $contents .='<desc>都道府県の境界線データ</desc>' . "\n"; $contents .='<polyline fill="none" stroke="black" stroke-width="1" points="0,0 4000,0 4000,4000,0,4000" />'. "\n";//図形の書き出し //境界線データの書き出し polylineでデータで書き出す。 $figMax = count($coordinate); //print("figMax=$figMax\n"); for ($fig= 0;$fig<$figMax ; $fig++){ $iMax = count($coordinate[$fig]); if ($iMax != 0){ $svgData = ""; for ($i = 0 ; $i<$iMax; $i++){ //メルカトル図法による計算。 $latilongArray = explode(" ",$coordinate[$fig][$i]);//空白で区切る //print("strring=" . $coordinate[$i][$j] . "\n"); $latitude1String = $latilongArray[0];//各要素を取り出す $longitude1String = $latilongArray[1]; $radian = deg2rad(floatval($latitude1String)); $x = deg2rad(floatval($longitude1String)) - $cLo;//横方向 $y = log(tan(pi()/4.0 + $radian/2.0));//縦方向 $x = $multiple * $x + $screenWidth/2; $y = $multiple * (1.0 - $y) - ($multiple * (1.0-$yOffset)); $svgData .= $x . "," . $y . " "; }// for i $contents .='<polyline fill="none" stroke="black" stroke-width="1" points="';//図形の書き出し $contents .= $svgData . '" />' . "\n"; } }//for fig $contents .="</svg>"; file_put_contents($saveSVG, $contents); } //==========================XMLをファイルから読み込むfunction====================== function readXMLfromFile($xmlFile){ //print("XML file loading...\n"); $dom = new DOMDocument('1.0', 'UTF-8'); $dom->preserveWhiteSpace = false; $dom->formatOutput = true; $dom->load($xmlFile); $root = $dom->getElementsByTagName("*")->item(0);//最初の要素の中身を取り出す $coordinate = array(); $i = 0; foreach( $root->getElementsByTagNameNS("*","Curve") as $itemGML ){//これでgml:Curveのところが取れるらしい。 $dataArray = explode("\n",$itemGML->nodeValue);//改行コードで配列にわける $j=0; foreach ($dataArray as $item) {//各要素ごとに繰り返す $item = trim($item); if($item != ""){//空白文字列の時には処理をしない。 $coordinate[$i][$j]=$item; $j++; }//if }//foreach $i++; }//foreach return $coordinate; } |
XMLはGISのサイトからダウンロードしたXMLを読み込むようにしてあります。ここらへんは国土数値情報から白地図を作る1をみてください。
メインの処理は次の通りです。forループで47都道府県を順番に処理しています。
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 |
for ($i=$startKenNo; $i<$endKenNo+1; $i++){ $startTime = microtime(true); $coordinate = readXMLfromFile($loadXML[$i]);//XMLから読み込み $max = getMaxPointDistance($coordinate);//座標データで一番はなれているところの二乗を求める。 $ave = getAvePointDistance($coordinate);//平均を求める $cityBorder = checkBorder($coordinate);//市区町村の境界線を求める。 $cityBorder = separateBorder($cityBorder,$max);//線が飛んでいるところを別の図形にする。 $cityBorder = arrayCleanup($cityBorder);//配列をきれいにする。 $cityBorder = cityBoarderEdge($cityBorder);//ここで端の線を1つ短くする処理をする。両側を短くする。 $cityBorder = arrayCleanup($cityBorder);//配列をきれいにする。 $prefBorder = getPrefBorder($coordinate,$cityBorder);//すべてのデータcoordinateと市区町村の境界線から県境をもとめる。 $prefBorder = separateBorder($prefBorder,$max);//線が飛んでいる部分を処理する。 $prefBorder = arrayCleanup($prefBorder);//配列をきれいにする。 $prefBorder = deleteShortLine($prefBorder);//点が3つ以下の部分を削除 $prefBorder = deleteEndLine($prefBorder,$ave);//図形の端っこの点が離れている場合には削除する。 $saveSVG = "./PrefSVG/" .$saveFile[$i] .".svg"; $saveXML = "./PrefXML/" .$saveFile[$i] .".xml"; drawSVG($saveSVG,$prefBorder); saveXMLdata($saveXML,$prefBorder); $endTime = microtime(true); $exeTime = $endTime - $startTime; print(" [$saveFile[$i]]exe Time=".$exeTime . "\n"); } |
47都道府県のどの都道府県を処理するのかはstartとendを指定して処理することもできます。
1 2 |
$startKenNo = 1; $endKenNo = 1; |
全都道府県を処理する場合には1〜47になります。
1 |
$coordinate = readXMLfromFile($loadXML[$i]); |
最初にXMLを読み込みます。読み込んだ結果は配列$coordinateに入ります。この配列は2次元配列です。最初の添え字で市区町村または島などの海岸線の番号。2番目の添え字で座標のデータが入っています。
読み込んだデータを描画すると次のようになります。下図は新潟県の場合です。図はIllustratorのアウトライン表示です。佐渡が消えてしまってますがIllustratorの図形の限界を超えてしまっているので描画されていません。データとしては存在します。Illusratorは一つのオブジェクトのポイントが3万を越えると表示ができなくなるようです。佐渡島の周囲にある岩などで佐渡形がかろうじてわかります。
データの読み込み後の最初の処理は、後の処理で使うために座標データと座標データの距離を求めておきます。この値は都道府県によってかなりバラツキがあるので各都道府県で求めて後の処理で使います。最大値と平均値を求めます。求めた値は$maxと$aveに入れます。
1 2 |
$max = getMaxPointDistance($coordinate);//座標データで一番はなれているところの二乗を求める。 $ave = getAvePointDistance($coordinate);//平均を求める |
次にこの配列から市区町村の境界線を求める処理を行います。この処理が一番時間がかかります。
1 |
$cityBorder = checkBorder($coordinate);//市区町村の境界線を求める。 |
県の中の市区町村の境界線だけを求めます。計算結果は配列$cityBorderに入ります。functionの中の処理は全ての市区町村の境界線が重なるかどうかarray_intersectを使いチェックします。重なっていた場合に重なったデータを返す様にしています。この配列も2次元配列です。最初の添え字で図形の番号。この番号でひとつなぎの境界線のデータになります。2番目の添え字で座標のデータが入っています。新潟県の場合はcityBorderを描画すると次のような画像になります。
重なりから境界線を作成しています。データの開始点と終了点を考慮していないので所々直線でつないでしまっているところがあります。そこで、直線を削除する処理を行います。それが
1 |
$cityBorder = separateBorder($cityBorder,$max);//線が飛んでいるところを別の図形にする。 |
です。ここで先ほど求めた$maxを使っています。$max以上離れた点はありえない部分なのでその部分で図形を分けています。処理した結果は次図のようになります。
次に元のデータから市区町村の境界線を引くと県境が求まりますが、そのまま引くと県境に穴が開いてしまいます。そこで市区町村の境界線のデータの始点と終点の点データを削除します。それが次の処理です。本当は県境と接する側だけを処理したいのですが、その判断が面倒なので両端を短くする処理をしています。
1 |
$cityBorder = cityBoarderEdge($cityBorder);//ここで端の線を1つ短くする処理をする。両側を短くする。 |
次に配列に余計な空白などのデータが入っていると後の処理でトラブルを起こすので削除しておきます。その処理が
1 |
$cityBorder = arrayCleanup($cityBorder);//配列をきれいにする。 |
です。(これはいらないかも?)
次に$cityBorderを$coordinateのデータから引き算します。
1 |
$prefBorder = getPrefBorder($coordinate,$cityBorder);//すべてのデータcoordinateと市区町村の境界線から県境をもとめる。 |
市区町村の境界線を削除した影響と境界線の始点と終点の関係で直線や点データが残っています。それらを解決するために次の処理を行います。
1 2 3 4 |
$prefBorder = separateBorder($prefBorder,$max);//線が飛んでいる部分を処理する。 $prefBorder = arrayCleanup($prefBorder);//配列をきれいにする。 $prefBorder = deleteShortLine($prefBorder);//点が3つ以下の部分を削除 $prefBorder = deleteEndLine($prefBorder,$ave);//図形の端っこの点が離れている場合には削除する。 |
佐渡が消えてますが先に説明した理由です。スクリーンショットを撮るのにIllustratorを使っているので図形がIllusratorの限界を超えてしまってるためです。
どこまで細かい部分まで入っているかというと新潟の東港部分を拡大してみます。
Google Mapでみると
https://www.google.co.jp/maps/@37.9922353,139.2383178,7064m/data=!3m1!1e3
といった感じになります。
47都道府県を連続で処理するとかなり時間がかかりますが、MacBook Pro (Core i5 2.9GHz)で実行した場合、次の結果になりました。合計で4時間ぐらいです。
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 |
[hokkaido]exe Time=3296.7090649605 [aomori]exe Time=122.16947507858 [iwate]exe Time=1320.5427498817 [miyagi]exe Time=1078.9129099846 [akita]exe Time=123.61177301407 [yamagata]exe Time=28.677213907242 [fukushima]exe Time=32.269643068314 [ibaraki]exe Time=6.4144871234894 [tochigi]exe Time=1.9351642131805 [gunnma]exe Time=2.7332141399384 [saitama]exe Time=5.6779367923737 [chiba]exe Time=81.843067884445 [tokyo]exe Time=535.52261805534 [kanagawa]exe Time=57.061334848404 [niigata]exe Time=168.06734609604 [toyama]exe Time=1.5160300731659 [isihikawa]exe Time=62.458130121231 [fukui]exe Time=124.91337704659 [yamanashi]exe Time=2.8849470615387 [nagano]exe Time=12.047899961472 [gifu]exe Time=3.2795400619507 [sizuoka]exe Time=212.89130711555 [aiti]exe Time=66.630383968353 [mie]exe Time=1041.8271570206 [shiga]exe Time=0.64241909980774 [kyoto]exe Time=44.150004148483 [osaka]exe Time=5.5262830257416 [hyougo]exe Time=185.11697602272 [nara]exe Time=2.021488904953 [wakayama]exe Time=263.70011901855 [tottori]exe Time=18.710318803787 [shimane]exe Time=237.44484496117 [okayama]exe Time=19.264281988144 [hiroshima]exe Time=37.061669111252 [yamaguchi]exe Time=211.55726194382 [tokushima]exe Time=168.82157993317 [kagawa]exe Time=23.631745100021 [ehime]exe Time=459.80693483353 [kouchi]exe Time=241.01473593712 [fukuoka]exe Time=137.41196608543 [saga]exe Time=7.0379889011383 [nagasaki]exe Time=2177.6520559788 [kumamoto]exe Time=117.25712108612 [ooita]exe Time=110.4707300663 [miyazaki]exe Time=104.72366809845 [kagoshima]exe Time=1371.7596409321 [okinawa]exe Time=356.01933002472 |
そしてこちらの記事で紹介した
plotXML2JapanPrefBound.php
を使い一枚の日本地図(都道府県の境界線入り)に仕上げます。前の記事と今回でXMLのタグの名前が違ってたのでそこだけ修正が必要です。一応全文載せます。
plotXML2JapanPrefBound.php
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 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 |
<?php //XMLフォルダに保存した各県のデータを読み取り日本の輪郭、県境のデータを書き出す。 //緯度経度のデータをstringのまま処理 //array_intersectを使い重なる緯度経度があるか判断。 ini_set('memory_limit', '2540M'); mb_language("Japanese");//文字コードの設定 mb_internal_encoding("UTF-8"); //ファイルリスト $xmlFile[1] ="./PrefXML/hokkaido.xml"; $xmlFile[2] ="./PrefXML/aomori.xml"; $xmlFile[3] ="./PrefXML/iwate.xml"; $xmlFile[4] ="./PrefXML/miyagi.xml"; $xmlFile[5] ="./PrefXML/akita.xml"; $xmlFile[6] ="./PrefXML/yamagata.xml"; $xmlFile[7] ="./PrefXML/fukushima.xml"; $xmlFile[8] ="./PrefXML/ibaraki.xml"; $xmlFile[9] ="./PrefXML/tochigi.xml"; $xmlFile[10] ="./PrefXML/gunnma.xml"; $xmlFile[11] ="./PrefXML/saitama.xml"; $xmlFile[12] ="./PrefXML/chiba.xml"; $xmlFile[13] ="./PrefXML/tokyo.xml"; $xmlFile[14] ="./PrefXML/kanagawa.xml"; $xmlFile[15] ="./PrefXML/niigata.xml"; $xmlFile[16] ="./PrefXML/toyama.xml"; $xmlFile[17] ="./PrefXML/isihikawa.xml"; $xmlFile[18] ="./PrefXML/fukui.xml"; $xmlFile[19] ="./PrefXML/yamanashi.xml"; $xmlFile[20] ="./PrefXML/nagano.xml"; $xmlFile[21] ="./PrefXML/gifu.xml"; $xmlFile[22] ="./PrefXML/sizuoka.xml"; $xmlFile[23] ="./PrefXML/aiti.xml"; $xmlFile[24] ="./PrefXML/mie.xml"; $xmlFile[25] ="./PrefXML/shiga.xml"; $xmlFile[26] ="./PrefXML/kyoto.xml"; $xmlFile[27] ="./PrefXML/osaka.xml"; $xmlFile[28] ="./PrefXML/hyougo.xml"; $xmlFile[29] ="./PrefXML/nara.xml"; $xmlFile[30] ="./PrefXML/wakayama.xml"; $xmlFile[31] ="./PrefXML/tottori.xml"; $xmlFile[32] ="./PrefXML/shimane.xml"; $xmlFile[33] ="./PrefXML/okayama.xml"; $xmlFile[34] ="./PrefXML/hiroshima.xml"; $xmlFile[35] ="./PrefXML/yamaguchi.xml"; $xmlFile[36] ="./PrefXML/tokushima.xml"; $xmlFile[37] ="./PrefXML/kagawa.xml"; $xmlFile[38] ="./PrefXML/ehime.xml"; $xmlFile[39] ="./PrefXML/kouchi.xml"; $xmlFile[40] ="./PrefXML/fukuoka.xml"; $xmlFile[41] ="./PrefXML/saga.xml"; $xmlFile[42] ="./PrefXML/nagasaki.xml"; $xmlFile[43] ="./PrefXML/kumamoto.xml"; $xmlFile[44] ="./PrefXML/ooita.xml"; $xmlFile[45] ="./PrefXML/miyazaki.xml"; $xmlFile[46] ="./PrefXML/kagoshima.xml"; $xmlFile[47] ="./PrefXML/okinawa.xml"; $saveSVG = "./japanPrefBound.svg"; readXMLmakeOutline($xmlFile,$saveSVG); function readXMLmakeOutline($xmlFile,$saveSVG){ $startKenNo = 1; $endKenNo = 47; for ($ken=$startKenNo; $ken<$endKenNo+1; $ken++){ //XMLの読み込み $dom = new DOMDocument('1.0', 'UTF-8'); $dom->preserveWhiteSpace = false; $dom->formatOutput = true; $dom->load($xmlFile[$ken]); $root = $dom->getElementsByTagName("*")->item(0);//最初の要素の中身を取り出す //読み込んだxmlの中身を$coordinateStringに展開する。 $j = 0; foreach( $root->getElementsByTagName("obj") as $itemGML ){//これでgmlの中身を$itemに取れる。 $dataArray = explode("\n",$itemGML->nodeValue);//改行コードで配列にわける $i=0; foreach ($dataArray as $item) {//各要素ごとに繰り返す $item = trim($item); if($item != ""){//空白文字列の時には処理をしない。 $coordinate[$ken][$j][$i]=$item; $i++; }//if }//foreach $j++; }//foreach( $root-> }//for ($ken=$startKenNo; $ken<$endKenNo+1; $ken++) XMLの読み込み完了 //=========================SVGで書き出す //スクリーンサイズ $screenWidth = 4000; $screenHeight = 4000; //日本地図を描くための範囲を決める。 $north_max = 46.0;//最北端:北緯45度33分28秒 東経148度45分14秒 46.0 $south_max = 20.0;//最南端:北緯20度25分30.6585秒 東経136度4分11.1766秒 20.0 $east_max = 154.0;//最東端:北緯24度17分12秒 東経153度58分50秒 154.0 $west_max = 122.0;//最西端:北緯24度26分58秒 東経122度56分01秒 122.0 $y_max = log(tan(pi()/4.0 + deg2rad($north_max)/2.0)); $y_min = log(tan(pi()/4.0 + deg2rad($south_max)/2.0)); $x_max = deg2rad($east_max);//横方向 $x_min= deg2rad($west_max); $cLo = ($x_max - $x_min)/2 + $x_min; $dx = $x_max - $x_min; $dy = $y_max - $y_min; $mX = $screenWidth/$dx; $mY = $screenHeight/$dy; if ($mX < $mY){ $multiple = $mX; } else { $multiple = $mY; } //y軸の描画用の定数 $yOffset = $y_max; print("multiple=" . $multiple ."\n"); //==========================SVGファイルを書き出す $contents='<?xml version="1.0" standalone="no"?>'; $contents .="\n"; $contents .='<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">'; $contents .="\n"; $contents .='<svg width="40cm" height="40cm" viewBox="0 0 4000 4000" xmlns="http://www.w3.org/2000/svg" version="1.1">'; $contents .="\n"; $contents .='<desc>都道府県の境界線データ</desc>'; $contents .="\n"; $contents .='<polyline fill="none" stroke="black" stroke-width="1" points="0,0 4000,0 4000,4000,0,4000" />';//図形の書き出し //境界線データの書き出し polylineでデータで書き出す。 for ($ken=$startKenNo; $ken<$endKenNo+1; $ken++){ $imax = count($coordinate[$ken]); print("imax=$imax\n"); for ($i=0 ;$i<$imax ; $i++){ $jmax = count($coordinate[$ken][$i]); //print("jmax=$jmax\n"); $svgData = ""; for ($j= 0;$j<$jmax ; $j++){ //メルカトル図法による計算。 $latilongArray = explode(" ",$coordinate[$ken][$i][$j]);//空白で区切る $latitude1String = $latilongArray[0];//各要素を取り出す $longitude1String = $latilongArray[1]; $radian = deg2rad(floatval($latitude1String)); $x = deg2rad(floatval($longitude1String)) - $cLo;//横方向 $y = log(tan(pi()/4.0 + $radian/2.0));//縦方向 $x = $multiple * $x + $screenWidth/2; $y = $multiple * (1.0 - $y) - ($multiple * (1.0-$yOffset)); $svgData .= $x . "," . $y . " "; } $contents .='<polyline fill="none" stroke="blue" stroke-width="1" points="';//図形の書き出し $contents .= $svgData . '" />' . "\n"; } } $contents .="</svg>"; file_put_contents($saveSVG, $contents); }// end of function |
これで出来上がった日本地図がこちら。
277MBあるSVGになっています。上記の画像はそのスクリーンショットChromeで開いています。他のアプリでは開けませんでした。
ここから県の輪郭線を閉じた線にして、県と県の接するところに不都合が起きない様にデータ数を減らすと目的のデータが完成します。それはまた後ほど。