ShapefileをOpenLayersで表示する。[その2]
ShapefileをOpenLayersで表示する。[その1] に続いて、Shapefileを直接読み込んで、OpenLayer上で表示してみます。
元ネタは、shapefile-jsで公開されているJavaScriptを使いました。
開発環境は、Windows 7 32bit,Aptana Studio 3.4
Firefox 20.0とGoogle Chromeで確認しました。IE9でも表示できるようです。
必要なJavaScript
shapefile-jsからmasterのZIPをdownloadして、unzipします。 とりあえずDesktopでも大丈夫です。
後は、OpenLayersをここから2.12をdownloadして解凍しておきます。
kanagawa folderに表示させたいShapefile一式をcopy、その際にshp,shx、dbfの3つのファイルが必要になります。
utf8 folderには、あらかじめshapefileからGeoJSONに変換したをjsonデータを入れてあります。encodeはutf-8です。
Shapefileを表示してみる。
exampleのファイルを編集します。
HTML5で作成します。file名は、ol_simple.htmlとol_simple.jsです。
codeは簡単です。元のcodeを参考にして若干手を加えています。
<html> <head> <title>Kanagawa Pref. Japan - Javascript Shapefile and DBF Loader</title> <script type="text/javascript" src="lib/binaryajax.js"></script> <script type="text/javascript" src="src/binarywrapper.js"></script> <script type="text/javascript" src="src/shapefile.js"></script> <script type="text/javascript" src="src/dbf.js"></script> <script type="text/javascript" src="src/ol_shapefile.js"></script> <script type="text/javascript" src="lib/OpenLayers/OpenLayers.js"></script> <script type="text/javascript" src="ol_simple.js"></script> <link rel="stylesheet" href="lib/OpenLayers/theme/default/style.css" type="text/css" /> <!--[if IE]> <script src="lib/excanvas.js"></script><![endif]--> <style type="text/css"> body { background-color: #eeeeee; color: #000000; font: 12px sans-serif; margin: 20px; } #map { width: 600px; height: 480px; margin: 0; padding: 0; border: 0; background-color: #9dc3e0; } a img { border: 0; } .olControlMousePosition { font-size: 12pt; background-color: white } </style> </head> <body> <h2 id="title">OpenstreetmapにShape fileをOverlay</h2> <div id="map" name="map"></div> <div id="docs"> <p> OSMと神奈川県のShape fileデータを表示します。 </p> <p> SHPデータは[EPSG:4326],dbfファイルはShift_JIS <br> 日本語2バイト文字は化けている </p> <p> See <a href="http://github.com/RandomEtc/shapefile-js">http://github.com/RandomEtc/shapefile-js</a> for more details. </p> </div> </body> </html>
次にol_simple.jsを編集します。
// all the interaction stuff is copied almost verbatim from // http://www.openlayers.org/dev/examples/dynamic-text-layer.html window.onload = function() { map = new OpenLayers.Map('map'); var osm = new OpenLayers.Layer.OSM("OpenStreetMap"); var shpLayer = new OpenLayers.Layer.Vector("shpLayer",{ projection : new OpenLayers.Projection('EPSG:4326') }); map.addLayers([osm, shpLayer]); map.setCenter(new OpenLayers.LonLat(139.4, 35.4). transform(new OpenLayers.Projection("EPSG:4326"), new OpenLayers.Projection("EPSG:3857")), 9 ); // Interaction; not needed for initial display. selectControl = new OpenLayers.Control.SelectFeature(shpLayer); map.addControl(selectControl); selectControl.activate(); shpLayer.events.on({ 'featureselected' : onFeatureSelect, 'featureunselected' : onFeatureUnselect }); map.addControl(new OpenLayers.Control.LayerSwitcher()); map.addControl(new OpenLayers.Control.Attribution()); map.addControl(new OpenLayers.Control.ScaleLine()); map.addControl(new OpenLayers.Control.MousePosition({ displayProjection: new OpenLayers.Projection("EPSG:4326") }) ); // load the shapefile var theUrl = 'kanagawa/c14_region'; getOpenLayersFeatures(theUrl, function(fs) { // reproject features // this is ordinarily done by the format object, but since we're adding features manually we have to do it. var fsLen = fs.length; var inProj = new OpenLayers.Projection('EPSG:4326'); var outProj = new OpenLayers.Projection('EPSG:3857'); for (var i = 0; i < fsLen; i++) { fs[i].geometry = fs[i].geometry.transform(inProj, outProj); } shpLayer.addFeatures(fs); }); } // Needed only for interaction, not for the display. function onPopupClose(evt) { // 'this' is the popup. var feature = this.feature; if (feature.layer) {// The feature is not destroyed selectControl.unselect(feature); } else {// After "moveend" or "refresh" events on POIs layer all // features have been destroyed by the Strategy.BBOX this.destroy(); } } function onFeatureSelect(evt) { feature = evt.feature; var table = '<table>'; for (var attr in feature.attributes.values) { table += '<tr><td>' + attr + '</td><td>' + feature.attributes.values[attr] + '</td></tr>'; } table += '</table>'; popup = new OpenLayers.Popup.FramedCloud("featurePopup", feature.geometry.getBounds().getCenterLonLat(), new OpenLayers.Size(100, 100), table, null, true, onPopupClose); feature.popup = popup; popup.feature = feature; map.addPopup(popup, true); } function onFeatureUnselect(evt) { feature = evt.feature; if (feature.popup) { popup.feature = null; map.removePopup(feature.popup); feature.popup.destroy(); feature.popup = null; } }
要するに、shpLayerというvector layerを作成して、Shapefileを読み込んで、JSONに変換して、shpLayer.addFeaturesを作成すると言うことです。
libとsourceのcodeは難しくないので、解読することができます。
では、kanagawa folderからshapefileを読み込んでみます。
な感じで、Shapefileが表示されます。Good Job!!ですね。
ここでは、神奈川県の市区町村の行政界を表示しています。
また、GeoJSONに変換して表示されたfeatureをクリックすると属性値が表示されます。
な感じです。
が。。。。 みての通り、nameが日本語表示されません。
ShapefileをOpenLayersで表示する。[その1] と同じで、どうやっても、属性データが文字化けしてしまいます。
これは、2byte codeで表現される日本語が化けるのです。
utf8変換のjsを利用したり、encodingを試したのですが。。。
問題点は、このbainaryをstream.jsで呼び出す際に1byteごと読み出して、code変換しているため文字化けすることはわかったのですが。Firebugで追っかけてもみました。
いかんせん、力量不足。。。
な感じでうまくいきます。 属性だけが。。。。
文字化けの問題点と修正
またもや、2byteの日本語の壁にぶち当たりました。
変更すべき点は、
-
binary dataをdbfのfield.lengthで一度に呼び込む
-
1byteか2byteかの判断をする。
-
2byteならば、encodingでutf8へ変換する。
これでいけるんじゃない? だからそのcodeは?
何か良い知恵はないでしょうか? ぜひお知恵を拝借させてください。
次回のお題
shapefileが読めなくてもいいさ。
GeoJSONでやれば。
ということで、ExtJS3.4とGeoExt1.1について、健忘禄をかねて少し書いてみます。
おまけ
電子国土とGoogle mapとOpenstreetmap上にGeoJSONを読み込んで表示させるcode
<!DOCTYPE html> <html lang="ja"> <head> <meta content="text/html; charset=utf-8" http-equiv="Content-Type"> <title>電子国土にGeoJSON fileをオーバーレイ表示する</title> <script type="text/javascript" src="lib/OpenLayers/OpenLayers.js" charset="UTF-8"></script> <script type="text/javascript" src="lib/v4/webtis/webtis_v4.js" charset="UTF-8"></script> <link rel="stylesheet" href="lib/v4/css/webtis.css" type="text/css"> <script src="http://maps.google.com/maps/api/js?v=3.9&sensor=false"></script> <script type="text/javascript"> //<!-- /*グローバル変数の宣言ここから*/ //地図インスタンス var map = null; //初期の経度 var initCX = 139.4; //初期の緯度 var initCY = 35.4; //初期のズームレベル //※ここで設定するズームレベルはデータセットの最小ズームレベルが0になる //※デフォルトデータセットでは「ズームレベル5」が0になる var initZoomLv = 4; //真球メルカトル投影(電子国土WebシステムVer.4もこれに準拠)を定義 var projection3857 = new OpenLayers.Projection("EPSG:3857"); //等経緯度投影を定義 var projection4326 = new OpenLayers.Projection("EPSG:4326"); /*グローバル変数の宣言ここまで*/ /*地図の初期表示設定ここから*/ function init() { //真球メルカトル投影のときの最大範囲(単位はm) var maxExtent = new OpenLayers.Bounds(-20037508, -20037508, 20037508, 20037508); //真球メルカトル投影のときの最大範囲に範囲を制限 var restrictedExtent = maxExtent.clone(); //真球メルカトル投影のときの最大解像度 var maxResolution = 156543.0339; //地図表示画面のオプション設定 var options = { //「controls」を設定することで、デフォルトのコントロールを破棄してコントロールを再設定 controls : [ //地図マウスイベントのハンドル設定。 new OpenLayers.Control.Navigation({ mouseWheelOptions : { interval : 100 } }), //左上のパンズームバーを設定 new OpenLayers.Control.PanZoomBar(), //キーボードをデフォルトに設定 new OpenLayers.Control.KeyboardDefaults(), //国土地理院著作表示 //※OpenLayrsサイトを作るときは必ずこれを書くこと //new OpenLayers.Control.Attribution() // ベースレイヤー切り替え new OpenLayers.Control.LayerSwitcher(), // マウス移動時に緯度経度を表示 new OpenLayers.Control.MousePosition()], //背景地図の地理座標系 projection : projection3857, //表示の地理座標系 displayProjection : projection4326, //背景地図の単位 units : "m", //背景地図の最大解像度 maxResolution : maxResolution, //背景地図の最大範囲 maxExtent : maxExtent, //背景地図の表示制限範囲 restrictedExtent : restrictedExtent }; //OpenLayers APIのMapクラスからインスタンスを作成 map = new OpenLayers.Map('map', options); //スケールバーコントロール表示(最大ピクセル150、下段単位無、EPSG:3857) map.addControl(new OpenLayers.Control.ScaleLine({ maxWidth : 150, bottomOutUnits : "", bottomInUnits : "", geodesic : true })); //電子国土WebシステムVer.4背景地図レイヤーインスタンスを作成。データセットは未指定で、デフォルトデータセットを利用 var webtisMap = new webtis.Layer.BaseMap("電子国土"); //背景地図レイヤーをMapに追加 map.addLayer(webtisMap); //背景地図レイヤーをGooglemap道路地図を追加 var gmaplayerRoad = new OpenLayers.Layer.Google('道路地図', { numZoomLevels : 20 }, { visibility : true }); map.addLayer(gmaplayerRoad); //背景地図レイヤーをGooglemap衛星写真を追加 var gmaplayerHybrid = new OpenLayers.Layer.Google('衛星写真', { type : google.maps.MapTypeId.HYBRID }, { numZoomLevels : 20 }, { visibility : true }); map.addLayer(gmaplayerHybrid); //Openstreetmapの追加 var osm = new OpenLayers.Layer.OSM('OSM'); map.addLayer(osm); //ShapeLayer用スタイル設定 var shpstyles = new OpenLayers.StyleMap({ "default" : {//通常状態 strokeWidth : 1, strokeColor : "blue", fillColor : "blue", fillOpacity : 0.3 } }); //行政界ShapeLayerの追加 /*var shpRegionLayer = new OpenLayers.Layer.Vector('行政界', { projection : projection4326, styleMap : shpstyles }); map.addLayer(shpRegionLayer);*/ var geojsonlayer = new OpenLayers.Layer.Vector("行政界", { strategies : [new OpenLayers.Strategy.Fixed()], protocol : new OpenLayers.Protocol.HTTP({ url : "utf8/c14_road_utf.json", format : new OpenLayers.Format.GeoJSON() }), projection: projection4326 //projection: projection3857 }); map.addLayer(geojsonlayer); // Interaction; not needed for initial display. selectControl = new OpenLayers.Control.SelectFeature(geojsonlayer); map.addControl(selectControl); selectControl.activate(); geojsonlayer.events.on({ 'featureselected' : onFeatureSelect, 'featureunselected' : onFeatureUnselect }); //初期の中心座標を指定(経緯度で入力して、内部的に真球メルカトル座標に変換して表示) map.setCenter(new OpenLayers.LonLat(initCX, initCY).transform(projection4326, projection3857), initZoomLv); //神奈川県の市町村区界shape fileを読み込む /*var theRegionUrl = 'kanagawa/c14_region'; getOpenLayersFeatures(theRegionUrl, function(fs1) { var fs1Len = fs1.length; var inProj = projection4326; var outProj = projection3857; for (var i = 0; i < fs1Len; i++) { fs1[i].geometry = fs1[i].geometry.transform(inProj, outProj); } shpRegionLayer.addFeatures(fs1); });*/ } // Needed only for interaction, not for the display. function onPopupClose(evt) { // 'this' is the popup. var feature = this.feature; if (feature.layer) {// The feature is not destroyed selectControl.unselect(feature); } else {// After "moveend" or "refresh" events on POIs layer all // features have been destroyed by the Strategy.BBOX this.destroy(); } } function onFeatureSelect(evt) { feature = evt.feature; var table = '<table> '; table += '<p>属性値</p>'; for (var attr in feature.attributes) { table += '<tr><td>' + attr + '</td><td>' + feature.attributes[attr] + '</td></tr>'; } //table += '<tr><td>' + 'name:' + '</td><td>' + feature.attributes.name + '</td></tr>'; table += '</table>'; popup = new OpenLayers.Popup.FramedCloud("featurePopup", feature.geometry.getBounds().getCenterLonLat(), new OpenLayers.Size(100, 100), table, null, true, onPopupClose); feature.popup = popup; popup.feature = feature; map.addPopup(popup, true); } function onFeatureUnselect(evt) { feature = evt.feature; if (feature.popup) { popup.feature = null; map.removePopup(feature.popup); feature.popup.destroy(); feature.popup = null; } } /*地図の初期表示設定ここまで*/ //--> </script> </head> <body onload="init();"> <h2 id="title">電子国土にShape fileをOverlay</h2> <div id="map" name="map" style="width:600px;height:480px;"></div> <div id="docs"> <p> 電子国土と神奈川県のGeoJSON fileデータを表示します。 </p> <p> GeoJSONデータは[EPSG:4326],UTF-8 </p> </div> </body> </html>
最近のコメント