地図について#6

ひつじかいさんの「ひつじかいの雑記帳」というブログを拝見し、その中に、地図の描写において色々と興味深い記事を拝読しました。これは自分でもやってみたい、そう思い、その記録を記載してみたいと思います。

Web上で操作可能な日本の白地図(都道府県別)を作る(6) ― データをGeoJSON形式にする ―

今回はMySQLのデータをGeoJSONコードに落として、DBを使わず、スタンドアロンでも使用できるようにします。なお現在、国土数値情報でもGeoJSON形式でデータが公開されているようです。

データはMultiPolygonとして、次の様式です。

{
    "type": "FeatureCollection",
    "features": [
        {
            "type": "Feature",
            "properties": {
                "prefcode": "1",
                "prefname": "北海道"
            },
            "geometry": {
                "type": "MultiPolygon",
                "coordinates": [
                    [
                        [
                            [141.883998, 45.496344],
                            [141.878319, 45.488176],
                            …,
                            [141.883998, 45.496344],
                            [141.883998, 45.496344]
                        ]
                    ],
                    [
                        [
                            [141.188495,45.2545],
                            [141.178855,45.24859],
                            …,
                            [141.197514,45.258546],
                            [141.188495,45.2545]
                        ]
                    ],
                    …
                ]
            }
        },{
                "type": "Feature",
                "properties": {
                    "prefcode": "2",
                    "prefname": "青森県"
                },
                "geometry": {
                    "coordinates": [
                    …
                    ]
                }
        }
    …
    ]
}

その上で、次のプログラムを実行させました。GeoJSONファイルは生成されるのですが、白地図が表示できません。症状としては、北海道から秋田県(05)までは表示されるのですが、山形県(06)を加えると表示されなくなります。ひつじかいさんのブログにある[blankmap.zip]では正しく表示されるのに、自分の環境で生成したGeoJSONファイルでは表示されません。

<?php

$errMsg;
$mysqli = connectDBi($errMsg);
if ($mysqli) {
    echo "MySQL接続OK<br>";
} else {
    echo "MySQL接続NG<br>" . $errMsg;
    exit();
}


$fc = new FeatureCollection;
$d2 = 0; // 配列coordinatesの第2層の要素。ポリゴンに穴がある場合は$d2>0となるが、今回扱うデータには存在しないため0で固定

// for ($PrefCode = 1; $PrefCode <= 47; $PrefCode++) {
for ($PrefCode = 1; $PrefCode <= 5; $PrefCode++) {
    $formerPoint = null;
    $feature = new Feature;
    $strSQL = "SELECT t_JapanPrefectureBorder.*, t_Prefecture.PrefectureName";
    $strSQL .= " FROM t_JapanPrefectureBorder";
    $strSQL .= " INNER JOIN t_Prefecture";
    $strSQL .= " ON t_JapanPrefectureBorder.PrefectureCode = t_Prefecture.PrefectureCode";
    $strSQL .= " WHERE t_JapanPrefectureBorder.PrefectureCode = " . $PrefCode; $strSQL .= " ORDER BY BorderCode, PointNo DESC";
    $rst = $mysqli->query($strSQL);

    while($col = $rst->fetch_array(MYSQLI_ASSOC)) {
        if (!isset($formerPoint)) {
            $cNo = 0;
            $feature->properties = new PrefInfo($col);
            $feature->geometry = new Geometry("MultiPolygon");
        } else if ($formerPoint["BorderCode"]<>$col["BorderCode"]) {
            $cNo ++;
        }
    
        if (!isset($feature->geometry->coordinates[$cNo])) {
            $feature->geometry->coordinates[$cNo][$d2] = array();
        }

        array_push($feature->geometry->coordinates[$cNo][$d2],array(floatval($col["Lng"]),floatval($col["Lat"])));
        $formerPoint = $col;
    }
    $rst->close();
    array_push($fc->features,$feature);
}

if ($mysqli) {
    $mysqli->close();
}

$geojson = json_encode($fc);
$geojson = str_replace("}},","}},\n",$geojson);

echo $geojson . "<br>";

$path = "json/PrefectureBorder.json";
$fp = fopen($path,'w');
fwrite($fp, $geojson);
fclose($fp);

echo "終了しました。";

class FeatureCollection {
    public $type = "FeatureCollection";
    public $features = array();
}

class Feature {
    public $type = "Feature";
    public $properties;
    public $geometry;
}

class Geometry {
    public $type;
    public $coordinates = array();

    function __construct($type=""){
        $this->type = $type;
    }
}

class PrefInfo {
    public $prefcode;
    public $prefname;

    function __construct($p){
        $this->prefcode = $p["PrefectureCode"];
        $this->prefname = $p["PrefectureName"];
    }
}

//MySQLへ接続
function connectDBi(&$err)
{
    //MySQL 接続情報
    $MySQL_SERVER = "localhost";
    $MySQL_USER = "[user_name]";
    $MySQL_PASSWORD = "[password]";
    $MySQL_DBNAME = "map";
    $err = "";
    $mysqli = new mysqli($MySQL_SERVER, $MySQL_USER, $MySQL_PASSWORD, $MySQL_DBNAME);
    if ($mysqli->connect_errno) {
        $err = "データベース接続に失敗しました。";
    }
    if (strlen($err) > 0) {
        return false;
    } else {
        return $mysqli;
    }
}

※06の山形県を加えるとすべて表示されなくなる。

データを確認すると、”coordinates”の最初と最後のデータが一致している必要があるのですが、どうもそれが一致していないようです。そこで”coordinates”の最初と最後のデータを比較し、異なるようであれば最後のデータに最初のデータを付け加えるようプログラムを少し変更してみました。

<?php

$errMsg;
$mysqli = connectDBi($errMsg);
if ($mysqli) {
    echo "MySQL接続OK<br>";
} else {
    echo "MySQL接続NG<br>" . $errMsg;
    exit();
}


$fc = new FeatureCollection;
$d2 = 0; // 配列coordinatesの第2層の要素。ポリゴンに穴がある場合は$d2>0となるが、今回扱うデータには存在しないため0で固定

for ($PrefCode = 1; $PrefCode <= 47; $PrefCode++) {
    $formerPoint = null;
    $feature = new Feature;
    $strSQL = "SELECT t_JapanPrefectureBorder.*, t_Prefecture.PrefectureName";
    $strSQL .= " FROM t_JapanPrefectureBorder";
    $strSQL .= " INNER JOIN t_Prefecture";
    $strSQL .= " ON t_JapanPrefectureBorder.PrefectureCode = t_Prefecture.PrefectureCode";
    $strSQL .= " WHERE t_JapanPrefectureBorder.PrefectureCode = " . $PrefCode;
    $strSQL .= " ORDER BY BorderCode, PointNo DESC";
    $rst = $mysqli->query($strSQL);

    while ($col = $rst->fetch_array(MYSQLI_ASSOC)) {
        if (!isset($formerPoint)) {
            $cNo = 0;
            $feature->properties = new PrefInfo($col);
            $feature->geometry = new Geometry("MultiPolygon");

        } else if ($formerPoint["BorderCode"] <> $col["BorderCode"]) {
            //! 最初と最後のデータを確認する...
            if ($tmp1 <> $tmp2){
                array_push($feature->geometry->coordinates[$cNo][$d2], $tmp1);
            }
            $cNo++;
        }

        if (!isset($feature->geometry->coordinates[$cNo])) {
            $feature->geometry->coordinates[$cNo][$d2] = array();
            $tmp1 = array(floatval($col["Lng"]), floatval($col["Lat"]));
        }
        $tmp2 = array(floatval($col["Lng"]), floatval($col["Lat"]));
        array_push($feature->geometry->coordinates[$cNo][$d2], $tmp2);
        $formerPoint = $col;
    }
    //! 最初と最後のデータを確認する...
    if ($tmp1 <> $tmp2){
        array_push($feature->geometry->coordinates[$cNo][$d2], $tmp1);
    }
    $rst->close();
    array_push($fc->features, $feature);
}

if ($mysqli) {
    $mysqli->close();
}

$geojson = json_encode($fc);
$geojson = str_replace("}},", "}},\n", $geojson);

echo $geojson . "<br>";

$path = "json/PrefectureBorder.json";
$fp = fopen($path, 'w');
fwrite($fp, $geojson);
fclose($fp);

echo "終了しました。";

class FeatureCollection
{
    public $type = "FeatureCollection";
    public $features = array();
}

class Feature
{
    public $type = "Feature";
    public $properties;
    public $geometry;
}

class Geometry
{
    public $type;
    public $coordinates = array();

    function __construct($type = "")
    {
        $this->type = $type;
    }
}

class PrefInfo
{
    public $prefcode;
    public $prefname;

    function __construct($p)
    {
        $this->prefcode = $p["PrefectureCode"];
        $this->prefname = $p["PrefectureName"];
    }
}

//MySQLへ接続
function connectDBi(&$err)
{
    //MySQL 接続情報
    $MySQL_SERVER = "localhost";
    $MySQL_USER = "[user_name]"";
    $MySQL_PASSWORD = "[password]";
    $MySQL_DBNAME = "map";
    $err = "";
    $mysqli = new mysqli($MySQL_SERVER, $MySQL_USER, $MySQL_PASSWORD, $MySQL_DBNAME);
    if ($mysqli->connect_errno) {
        $err = "データベース接続に失敗しました。";
    }
    if (strlen($err) > 0) {
        return false;
    } else {
        return $mysqli;
    }
}

これで正常に表示されるようになりました。