2014年8月30日(土)
 これくらい涼しくなってくれると、暑さによる消耗がない分ジョギングも楽になります・・・ということで六本木まで遠征。国立新美術館のオルセー美術館展を観覧してきました。人気の展覧会だけに賑わっていましたが激混みという程ではなく、行列することなくじっくり観覧出来ました。
 
・イアソン(ギュスターヴ・モロー)
 私にとっては、この作品こそが今回の最大の目当て!
 モローらしさ全開の素晴らしい作品で鳥肌が立ちました。この1枚が見られただけでも大満足です。

イアソン
<ギュスターヴ・モロー>

・笛を吹く少年(エドゥアール・マネ)
 当展の目玉作品のようで、ポスターやちらしの表紙にはこの絵が使用されています。
 「世界一有名な少年、来日」というコピーは大袈裟にしても、この絵は美術の教科書に載っていたので、「見覚えある」っていう人は確かに多いでしょうね。思っていたよりもサイズの大きい(160.5×97cm)絵で見応えありましたが、マネの代表作という程ではないような・・・?

笛を吹く少年
<エドゥアール・マネ>

・晩鐘(ジャン=フランソワ・ミレー)
 「落穂拾い」の次に有名な(と思われる)ミレー作品。「笛を吹く少年」とは逆で、こちらは思ったよりも小さい絵(55.5×66cm)でしたが、見ることが出来て嬉しいです。地元の府中市美術館では9/10からミレー展が開催されるので、その予習的感覚で観賞・・・にしては有名作すぎるので何だか微妙な気分です。

晩鐘
<ジャン=フランソワ・ミレー>

・床に鉋をかける人々(ギュスターヴ・カイユボット)
 昨年のカイユボット展には出展されなかったので、これも見たかった1枚。カイユボット・・・やっぱりいいです。印象派の画家の中で、一番絵が上手かったのはこの人じゃないかなあ?

床に鉋をかける人々
<ギュスターヴ・カイユボット>

・白い霜(カミーユ・ピサロ)
 4年前のオルセー美術館展でモロー作品(オルフェウス)に出逢うまでは、「好きな画家は?」と聞かれたらピサロと答えてたなあ・・・なんて思い出しながら鑑賞してました。
 今回4点出展されているピサロ作品の中では、今作が色合い的に一番良いと思いましたが、ピサロの描く田園風景はどれも長閑で癒されます。

白い霜
<カミーユ・ピサロ>

・サン=ラザール駅(クロード・モネ)
 当展に出展されているモネ作品(6点)は全て初期から前期のもので、そのせいか(うまく表現出来ませんが)もあもあっ!とした感じの「いかにもモネ」といった作品は少なかったように思います。その中では、今作が一番モネらしい?

サン=ラザール駅
<クロード・モネ>


 
 

市から帰るフラジェの農民たち
<ギュスターヴ・クールベ>


ケラー伯爵夫人
<アレクサンドル・カバネル>

 「オルセー美術館展」というタイトルにはルーヴルやボストンと同様に全く真新しさ全く感じませんが、さすがはオルセー!素晴らしい作品ばかりでした。
 10月20日(月)まで。

コメント   

 前回までで、操作可能な白地図は一応作成出来たのですが、GoogleMapsAPI利用によるものなので、次のような欠点があります。

・大きさを自由に変更出来ない
 GoogleMapですからズーム機能はもちろんあるのですが、ちょうど良いサイズに調整することが出来ません。実際ズームレベル=5では少し小さく、ズームレベル=6にすると画面に収まらない・・・といった感じ。
 0.1くらいの単位で変更出来ればいいのですが、現在はそういう仕様になっていません。APIを利用する以上、そのAPIにない機能の実現は諦めるしかありません。
 
・地図を画像出力出来ない
 画像出力出来れば、着色した地図をExcel等に貼り付けるなど用途も広がります。現状では画面のスクリーンショットしかありません。しかも商用利用する場合等には著作権の問題も発生しそう。
 
 GoogleMapsAPIならではの「お手軽さ」は捨てがたいのですが、上記のような問題をカバー出来る方法も別途用意しておきたい・・・。始めはHtml5のCanvas要素にしようかと考えたのですが、クリックした都道府県の色を変更する等のイベント処理には向いていないようなので(プラグインもあり、出来ないこともないのですが)、SVGを試すことにしました。
 今後、SVGがWebグラフィックの主流になっていくのかどうかは分かりませんが、最近では各ブラウザともサポートするようになりました。IE8以下ではサポートしていませんが、これもJavaScriptライブラリ(Snap.svgなど)を利用することで表示可能となるようです。
 
 前回作成したGeoJSONには緯度・経度のデータが収納されていますので、これをSVG描画するには平面座標(x,y)の数値に変換する必要があります。これはWeb上で操作可能な日本の白地図(都道府県別)を作る(3)で作成した緯度経度を平面直角座標に変換する関数(国土地理院サイトにある計算式を適用)すれば良いのですが、いろいろと調べてるうちにD3.jsというJavaScriptライブラリがあることを知り、これが非常に多機能で応用が効きそうなので、今回は学習も兼ねてこのD3.jsを利用することにしました。
 
 ・D3.js公式サイト:http://d3js.org/
 ・D3.js 日本語ドキュメント:http://ja.d3js.node.ws/
 
 まだ日本ではD3.jsはあまり普及していないようで、事例が紹介されているサイトも多くはないのですが、よく分からないまま試行錯誤でソースを組んでみました。
 GeoJSONを読み込んでSVGに地図描画する部分は以下ソースで一応実現出来ました(効率的なものではないかもしれませんが)。

     var mapScale = 3000;  //MAPスケール(この数値を大きくすると地図サイズも大きくなる)
     var mapWidth = 1200;  //SVG要素の幅
     var mapHeight = 1200; //SVG要素の高さ
     var jsonfilename = "json/PrefectureBorder.json";  //GeoJSONファイル名

     var svg = d3.select("body").append("svg")
        .attr("width", mapWidth)
        .attr("height", mapHeight)
        .attr("id", "svg");

     //SVG要素上で右クリックした時にコンテキストメニューが表示されないようにする
     document.getElementById("svg").addEventListener("contextmenu", function(e){
        e.preventDefault();
     }, false);

     d3.json(jsonfilename, function(error, json) {
        var mapbounds = d3.geo.bounds(json);
        var mapcenter = [(mapbounds[0][0]+mapbounds[1][0])/2, (mapbounds[0][1]+mapbounds[1][1])/2];

        var mapProjection = d3.geo.mercator()
           .center(mapcenter)
           .translate([mapWidth/2, mapHeight/2])
           .scale(mapScale);
 
        var mapPath = d3.geo.path()
           .projection(mapProjection);

        svg.selectAll(".pref")
           .data(json.features)
           .enter()
           .append("path")
           .attr("d", mapPath)
           .attr("fill", "#ffffff")
           .attr("stroke", "#808080")
           .attr("stroke-width", "1")
           .attr("class", "pref")
           .attr('prefname', function(d) {
              return d.properties["prefname"];
           })
           .attr('prefcode', function(d) {
              return d.properties["prefcode"];
           })
           .on('mouseover', function(d) {
              d3.select(this).attr("stroke-width", "2");
           })
           .on('mouseout', function(d) {
              d3.select(this).attr("stroke-width", "1");
           })
           .on("click", function(d){
              d3.select(this).attr("fill",document.getElementById('f_colorpicker').value);
           })
           .on('contextmenu', function(e) {
              d3.select(this).attr("fill","#ffffff");
           });
     });

まず、

var svg = d3.select("body").append("svg")
   .attr("width", mapWidth)
   .attr("height", mapHeight)
   .attr("id", "svg");

として、svg要素をbodyに追加します。同時に要素の幅や高さ、IDを設定しています。
次に

d3.json(jsonfilename, function(error, json) {

と、GeoJSONファイルのデータを読み込みます。読み込みに成功した場合、情報は変数:jsonに収納されます。
d3.jsonメソッドは非同期なので、この取得したデータに関連するイベントは当メソッドのコールバック処理内に記載します。なお、d3で扱う地形データはGeoJSONを拡張したTopoJSONという形式が標準のようですが、GeoJSON形式でも読み込み出来たので今回はそのまま使用することにしました。

   var mapbounds = d3.geo.bounds(json);
   var mapcenter = [(mapbounds[0][0]+mapbounds[1][0])/2, (mapbounds[0][1]+mapbounds[1][1])/2];
 
   var mapProjection = d3.geo.mercator()
      .center(mapcenter)
      .translate([mapWidth/2, mapHeight/2])
      .scale(mapScale);
 
   var mapPath = d3.geo.path()
      .projection(mapProjection);

 この部分ではプロジェクション(地図投影法)の設定をしています。d3.geo.boundsメソッドで地形データ(json)の左下と右上の座標を取得し、その中間値(mapcenter)をマップの中心にします。これにより中間値が座標原点(0,0)となり、そうするとこれよりも北や西の地点は座標値がマイナスとなり描画範囲を外れてしまうので、translateメソッドで位置調整しています(22行目)。

 そして、これらの設定条件で地図描画を行うのが28行目以降の部分

svg.selectAll(".pref")

 クラス名が「pref」である要素を全て選択・・・という形で各県について描画を行っているのですが、このクラス名は仮のもので、適当な名前をつけても動作します。この辺りは「セレクション」というD3.jsの核ともいえる仕組みなのですが、現状では「よく分からないけど実装出来ている」という状態。D3.jsはかなり奥が深そうな感じなので、今後学習して理解を深めたいと思っています。
 
 以上で地図描画及びクリックした都道府県のペイント処理等は実装出来たので、次は画像ファイルの出力機能です。
 IE9以降であればSVG要素の画像を保存(右クリック→名前をつけて画像を保存)することが出来ますが、今の所FirefoxやChromeにはそういった機能はありません。SVG要素に描いた画像をダイレクトにjpegやpng形式で保存する方法は見つけられませんでしたが、以下の手順で保存出来ることが分かりました。
 1.canvas要素を生成して、svg要素の画像をそっくりそのままコピー
  javaScriptライブラリ「canvg.js」を利用:canvg.js
 2.canvas要素の表示画像をBlobオブジェクトに変換してファイル保存
  canvas-toBlob.js及びFileSaver.jsを利用
   ・canvas-toBlob.js
   ・FileSaver.js
 ※BlobはIEだと10以降でなければ対応していませんが、IE9の場合は前述した「右クリック→名前をつけて画像を保存」でpng保存出来ます。  

 ライブラリ利用なので、コードは簡素です。

     var svgElm = document.getElementById("svg");
     var svgData = new XMLSerializer().serializeToString(svgElm);

     d3.select("body").append("canvas")
        .attr("width", mapWidth)
        .attr("height", mapHeight)
        .attr("id", "canvas")
        canvg('canvas', svgData)
        var canvas = document.getElementById('canvas')
        canvas.toBlob(function(blob) {
           saveAs(blob, "MapImage.jpg");
        }, "image/jpeg")
        d3.select("canvas").remove();

 2行目でしているsvgの値の取得は、当初
  var svgData = svgElm.innerHTML;
としていたのですが、これだとIEでは何故か取得出来ないので、代わりにXMLSerializerのserializeToStringメソッドを使用しました(そのため、.NET Frameworkが入っていないPCでは動作しません)。
 保存する画像形式はpngが標準のようですが、今回はjpegにしてみました。jpegにはpngのように透過画像には対応していないので、今回描画のMAPをそのまま出力すると、背景が黒になってしまいます。この対策としては、前回までのGoogleMapsAPIバージョンでも実行した「画面全体に白い四角形を描画する」作戦。

      svg = d3.select("body").append("svg")
        .attr("width", mapWidth)
        .attr("height", mapHeight)
        .attr("id", "svg");

 こんな感じで・・・。ただし、svgにはz-indexのようなレイヤー的な概念はないので、一番最初にこの四角形を描くようにします。

 その他、沖縄県の位置調整やカラーピッカーの設定等は前回とほぼ同じ。以下のようなMAPとなりました。

日本の白地図(都道府県別)SVG+D3.js版

日本の白地図(都道府県別)SVG+D3.js版
このMAPを表示

 

 これに、ペイントした地図の情報をセーブ・ロードする機能や凡例の表示機能などを付加すれば実用出来るレベルになるかな・・・と思っています。

コメント   

 今回は、前回作成した白地図を、サーバと接続していないクライアント端末でも利用出来るよう、PHPやMySQLを使用しないものに加工してみます。

 まずはMySQLテーブルに保存されている都道府県輪郭情報をファイルに落とします。こういった場合、ファイルはCSV形式とするのが一般的なんでしょうが、位置情報を収納するファイル形式としてGeoJSONというものがあるとのこと。GoogleMapsAPIにも、このGeoJSONを読み込む機能が最近追加され、これからのデファクトスタンダードになるのかも(あるいは既にそうなのか)?・・・ということで、今回はGeoJSON形式で保存することにします。

GeoJSONのフォーマット仕様については公式ページに詳しく記載されています。対応しているジオメトリタイプはPoint(点)、LineString(線)、Polygon(ポリゴン)など様々ですが、今回はtype=MultiPolygonのフォーマットで作成することにします。Polygonでも良いのですが、島のある都道府県は複数の領域に分かれているので、これらを1つのオブジェクトとして扱うことの出来るMultiPolygonの方がより便利です。
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": [
          …
        ]
      }
    }
    …
  ]
}

 MultiPolygonでは位置情報(緯度・経度)を収納する配列(coordinates)は4層構造となります。
 [“coordinates”][d1][d2][d3][d4]
 1層目(d1):要素数=領域数
 2層目(d2):1領域の情報を収納。1番目の要素は外側の環、2番目以降の要素には内側の環(穴)となる
 3層目(d3):要素数=点数
 4層目(d4):[0]->経度 [1]->緯度
 今回GeoJSONへ加工する都道府県輪郭データには内側の環(穴)が存在しないため、2層目の要素数=1(d2=0)で固定となります。
 ※輪郭データの中には和歌山県の飛び地がありますが(北山村、新宮市)、いずれも奈良県と三重県の狭間にあるため両県に穴は出来ません。他の飛び地は面積が小さいためデータ加工の過程で除外しました。

<?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"]) {
            $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 "終了しました。<br>";

   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_SERVER = "サーバー名";
      $MySQL_USER = "ユーザー名";
      $MySQL_PASSWORD = "パスワード";
      $MySQL_DBNAME = "データベース名";

      $err = "";
      $mysqli = new mysqli($MySQL_SERVER, $MySQL_USER, $MySQL_PASSWORD, $MySQL_DBNAME);
      if ($mysqli->connect_errno) {
         $err = "データベース接続に失敗しました。";
      } else {
         //文字化け対策
         if (!$mysqli->set_charset("utf8")) {
            $err = "文字コードセットに失敗しました。";
         }
      }
      if (strlen($err) > 0) {
         return false;
      } else {
         return $mysqli;
      }
   }
?>

 上記がGeojSON作成のソースとなります。特に難しいことはしていないシンプルな処理です。このくらいのオブジェクトであれば、配列だけで構成させても良いのですが、後々応用が効きそう・・・と思い、クラスを利用することにしました。
 propertiesメンバーには、任意の項目を設定することが出来ますので、今回はprefcode(都道府県コード)とprefname(都道府県名)の2項目を収納しました(PrefInfoクラス:79~87行目)。
 また、元データ(国土交通省の「行政区域」データ)では外周の点は反時計回りで並んでいるのですが、Shape形式のファイルではこれとは逆で時計回りが仕様のようなので、このままGeojSON出力すると利用ツールによっては不具合が出る可能性があるため、PointNo DESC(23行目)とデータ順を逆にして時計回りになるようにしました。

 生成したJSONをそのままファイル出力すると改行コードが入らず、それだと上手く読み込めない場合があるので、

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

 と、都道府県毎に改行コードを入れて出力するようにしました。これでもなお1行の文字数が多すぎる場合は、もう少し考慮する必要がありますが、今回はこれで大丈夫そうです。

 続いて、このGeoJSONを読み込んで地図出力する処理です。
 GoogleMapsAPIには「loadGeoJson」というお手軽なメソッドがあり、
  map.data.loadGeoJson(GeoJSONファイル名);
 と記載するだけで、GeoJSONデータを読み込んで地図出力してくれるので、当初はこれを利用しようと思っていたのですが、このメソッドでは位置情報(緯度・経度)を変更することが出来ません。GoogleMapsAPIのリファレンスを読むとsetGeometryという「それっぽい」メソッドがあるようなのですが、現時点(2014.8)では上手く動作してくれません。使い方を間違えているのかも?
 本来は緯度・経度情報なんて普通は変えたりするものではないのですが、前回行ったようにスペースの関係で白地図出力時には沖縄県の位置を調整したいので・・・。GeoJSON作成時に予め調整した値を収納してしまうという方法もありますが、それだと調整度合いを状況に合わせて変更出来ないのでスマートではありません。

 そのため、別の方法を考えることにしました。GeoJSONファイルをオブジェクトとして読み込んでから、データを加工し(沖縄の緯度・経度を調整)、GoogleMapsAPIのaddGeoJsonメソッドで地図に落とします。
 そして出来上がったのが以下のソース

// *************************************************************** 
//  ~Web上で操作可能な日本の白地図(都道府県別)を作る~ 
//
//  テスト:都道府県ポリゴンを指定した色でペイント
//
// *************************************************************** 

  var map;
  var json;
  var OkinawaLine;

  function initialize() {

     setDivSize();
     setColorPicker();

     var iniCenter = new google.maps.LatLng(37,138);
     var iniZoom = 6;

     var styleAllOFF = [
        {
           featureType: 'all',
           stylers: [
              {visibility: 'off'},
           ],
        }
     ];

     var myOptions = {
        zoom: iniZoom,
        center: iniCenter,
        backgroundColor: '#ffffff',
        styles: styleAllOFF
     };
     map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);

     getGeoJson();
     drawRectangle();

  }

  //ウィンドウサイズに合わせて地図サイズを調整
  function setDivSize() {

     var w = 1024; //デフォルト値
     var h = 768;  //デフォルト値
     if (window.innerWidth) {
        w = window.innerWidth;
        h = window.innerHeight;
     } else if (document.all) {
        if (document.documentElement.clientWidth) {
           w = document.documentElement.clientWidth;
           h = document.documentElement.clientHeight;
        } else if (document.body.clientWidth) {
           w = document.body.clientWidth;
           h = document.body.clientHeight;
        }
     } else {
        return false;
     }

     var divTop = document.getElementById("top_bar").style;
     var divMain = document.getElementById("map_canvas").style;
     var divHeightTop = 60;

     divTop.height = divHeightTop + "px";
     divMain.width = (w - 20) + "px";
     divMain.height = (h - divHeightTop - 20) + "px";

  }

  function getGeoJson() {

     var jsonfilename = "json/PrefectureBorder.json";
     jQuery.ajax({
        url : jsonfilename,
        dataType: 'json',
        async : false,
        success: function(request){
           json = request;
           setMapGeoJSON();
        },
        error: function() {
           alert('JSONデータの取得に失敗しました');
        }
     });

  }

  function setMapGeoJSON() {

     for (var i = 0; i < json["features"].length; i++) {
        if (parseInt(json["features"][i]["properties"]["prefcode"])==47) {
           moveLocation(i,5,15);
        }
     }

     map.data.addGeoJson(json);

     map.data.setStyle(function(feature) {
        return ({
           title: feature.getProperty('prefname'),
           strokeColor: "#666666",
           strokeOpacity: 1.0,
           strokeWeight: 1,
           fillColor: "#FFFFFF",
           fillOpacity: 1.0,
           zIndex: 1
        });
     });

     map.data.addListener('mouseover', function(event) {
        map.data.overrideStyle(event.feature, {strokeWeight: 2.0});
     });

     map.data.addListener('mouseout', function(event) {
        map.data.overrideStyle(event.feature, {strokeWeight: 1.0});
     });

     map.data.addListener('click', function(event) {
        var prefFld = document.getElementById("f_pref");
        for(var j = 0; j < prefFld.length; j++) {
           if (parseInt(prefFld.options[j].value) == event.feature.getProperty('prefcode')) {
              prefFld.options[j].selected = true;
           } else {
              prefFld.options[j].selected = false;
           }
        }
        var fColor = document.getElementById("f_colorpicker").value;
        paintPref(event.feature.getProperty('prefcode'),fColor);
     });

     map.data.addListener('rightclick', function(event) {
        var fColor = "#ffffff";
        paintPref(event.feature.getProperty('prefcode'),fColor);
     });

     setPrefOption();

  }

  function setPrefOption() {

     var i;
     var fld = document.getElementById("f_pref");
     var txtOption = "<option value='0' SELECTED >--------</option>";
     var pCode,pName;

     map.data.forEach(function(feature) {
        pCode = feature.getProperty('prefcode');
        pName = feature.getProperty('prefname');
        txtOption += "<option value='"+pCode+"'";
        if (fld.value == pCode) {
           txtOption += " SELECTED";
        }
        txtOption += " >"+pName+"</option>"
     });
     fld.innerHTML = txtOption;

  }

  //沖縄県の場所を移動(スペースの都合。白地図によくあるケース)
  function moveLocation(fNo,my,mx) {

     for (var i = 0; i < json["features"][fNo]["geometry"]["coordinates"].length; i++) {
        for (var j = 0; j < json["features"][fNo]["geometry"]["coordinates"][i].length; j++) {  //実際のデータはj=0固定
           for (var k = 0; k < json["features"][fNo]["geometry"]["coordinates"][i][j].length; k++) {
             json["features"][fNo]["geometry"]["coordinates"][i][j][k][0] += mx; //Lng
             json["features"][fNo]["geometry"]["coordinates"][i][j][k][1] += my; //Lat
           }
        }
     }

     //実際の位置から移動していることを示すライン
     if (!OkinawaLine && parseInt(json["features"][fNo]["properties"]["prefcode"])==47) {
        var OkinawaLineCoords = [
           new google.maps.LatLng(25.7+my, 126.3+mx),
           new google.maps.LatLng(26.7+my, 126.3+mx),
           new google.maps.LatLng(27.7+my, 127.8+mx),
           new google.maps.LatLng(27.7+my, 129.3+mx)
        ];
        OkinawaLine = new google.maps.Polyline({
           path: OkinawaLineCoords,
           strokeColor: "#808080",
           strokeOpacity: 1.0,
           strokeWeight: 1.5,
           zIndex: 1
        });
        OkinawaLine.setMap(map);
     }
  }

  //都道府県の領域(ポリゴン)を指定した色でペイント
  function paintPref(prefcode,fcolor) {
     map.data.forEach(function(feature) {
        if (parseInt(feature.getProperty('prefcode')) == parseInt(prefcode)) {
           map.data.overrideStyle(feature, {fillColor: fcolor});
        }
     });
  }

  //巨大な矩形で地図全体を覆う(背景の設定)
  function drawRectangle() {

     var rectangle = new google.maps.Rectangle();
     var rectBounds = new google.maps.LatLngBounds(
              new google.maps.LatLng(-90.0, -180),
              new google.maps.LatLng(90.0, 180));
     var rectOptions = {
        strokeColor: "#FFFFFF",
        strokeOpacity: 1.0,
        strokeWeight: 0.0,
        fillColor: "#FFFFFF",
        fillOpacity: 1.0,
        zIndex: 0,
        map: map,
        bounds: rectBounds
     };
     rectangle.setOptions(rectOptions);

  }

  //カラーピッカー(Spectrum)の設定
  function setColorPicker() {

     var fld = document.getElementById("f_colorpicker");

     $(function(){
        $("#f_colorpicker").spectrum({
          color: fld.value,
          showInput: true,
          showInitial: true,
          showPalette: true,
          showSelectionPalette: true,
          preferredFormat: "hex",
          chooseText: "OK",
          cancelText: "Cancel",
          palette: [
             ["#000000", "#434343", "#666666", "#999999", "#b7b7b7", "#cccccc", "#d9d9d9", "#efefef", "#f3f3f3", "#ffffff"],
             ["#980000", "#ff0000", "#ff9900", "#ffff00", "#00ff00", "#00ffff", "#4a86e8", "#0000ff", "#9900ff", "#ff00ff"],
             ["#e6b8af", "#f4cccc", "#fce5cd", "#fff2cc", "#d9ead3", "#d9ead3", "#c9daf8", "#cfe2f3", "#d9d2e9", "#ead1dc"],
             ["#dd7e6b", "#ea9999", "#f9cb9c", "#ffe599", "#b6d7a8", "#a2c4c9", "#a4c2f4", "#9fc5e8", "#b4a7d6", "#d5a6bd"],
             ["#cc4125", "#e06666", "#f6b26b", "#ffd966", "#93c47d", "#76a5af", "#6d9eeb", "#6fa8dc", "#8e7cc3", "#c27ba0"],
             ["#a61c00", "#cc0000", "#e69138", "#f1c232", "#6aa84f", "#45818e", "#3c78d8", "#3d85c6", "#674ea7", "#a64d79"],
             ["#85200c", "#990000", "#b45f06", "#bf9000", "#38761d", "#134f5c", "#1155cc", "#0b5394", "#351c75", "#741b47"],
             ["#5b0f00", "#660000", "#783f04", "#7f6000", "#274e13", "#0c343d", "#1c4587", "#073763", "#20124d", "#4c1130"]
          ]

        });
     })
  }

 
・getGeoJson(72~88行目)
 GeoJSONファイルの読み込みにはjQueryの$.ajaxを利用しました。$.getJSONでも良いのですが、何となく同期通信にて読み込みたかったので(非同期でも良いんですけど)・・・。

・setMapGeoJSON(90~140行目)
 上記のgetGeoJsonで取得した情報を地図に出力します。92~96行目で沖縄の位置調整を行い、

     map.data.addGeoJson(json);   

 
 この1行だけでポリゴン描画。とっても簡単です。
 他は前回同様、クリック時のペイント処理、マウス移動時に輪郭線を強調する処理などですが、記載が
 google.maps.event.addListener(ポリゴンオブジェクト, “click”, function(event)
ではなく
 map.data.addListener(‘click’, function(event)
 となる所が前回との違いです。

・setPrefOption(142~160行目)
 GeoJSONファイルのpropertiesメンバーに収納した情報は、addGeoJsonメソッドによりgoogle.maps.Data.Featureクラスにインポートされ、この値をgetPropertyメソッドで取得することが出来ます(150・151行目)。

・moveLocation(162~191行目)
 沖縄の位置を調整。前回と同じ処理ですが、GeoJSONから取得した緯度・経度は階層が深いので、やや分かりづらいです。

             json["features"][fNo]["geometry"]["coordinates"][i][j][k][0] += mx; //Lng
             json["features"][fNo]["geometry"]["coordinates"][i][j][k][1] += my; //Lat

 
 fNo → 都道府県コード
 i → 領域の番号(新潟県の例では、本土:i=0、佐渡ヶ島:i=1)
 j → 今回扱うデータではj=0固定(穴のある領域を扱う場合j>0)
 k → 領域を構成する点番号

 
 
出力される地図は見た目上は前回と全く同じです(MAP表示)が、今回のソースはローカルPC上だけで動作する仕様ですので、インターネット接続環境(インターネットに繋がっていないとGoogleMapが見られない)さえあれば見ることが出来ます。。

今回作成の白地図 ファイル一式ダウンロード:blankmap.zip
<圧縮ファイル(blankmap.zip)の構成>
 ・blankmap.html : 白地図表示のHTML
 ・js/blankmap.jp : 白地図表示用javascriptソース
 ・js/jquery.js : jQuery(v1.10.2)
 ・js/spectrum.js : カラーピッカー表示用のjQueryプラグイン
 ・css/spectrum.css : spectrum用css
 ・json/PrefectureBorder.json : 都道府県輪郭データ(GeoJSONフォーマット)
※jQuery及びspectrumはMITライセンスです。著作権など詳細は各サイトにてご確認下さい。
※その他は私が作成したものです。ご自由に利用・改変していただいて結構です。ただし、GoogleMapsAPIを利用している関係上、使用にあたってはGoogleMapsAPIの利用規約に適合する必要があります。詳しくはGoogleMapsAPIのページにてご確認下さい。
※当ソースの使用または使用不能により生じたいかなる障害・損害について一切の責任を負いません。
※非営利目的での転載・再配布は自由です。報告なども特に必要ありません。

コメント