├── README.md ├── leaflet-hash.js ├── index.html └── TileLayer.GeoJSON.js /README.md: -------------------------------------------------------------------------------- 1 | experimental_pni 2 | ================ 3 | 国土地理院ベクトルタイル提供実験(地名情報) 4 | 5 | ## 地名情報の GeoJSON タイル 6 | 国土地理院が整備している地名情報のうち、居住地名、自然地名、公共施設、住居表示住所について、GeoJSONタイルに変換したものを提供実験いたします。 7 | 8 | ## 地名情報 9 | ### テンプレートURL: 10 | - 地名情報(居住地名) 11 | https://cyberjapandata.gsi.go.jp/xyz/experimental_nrpt/{z}/{x}/{y}.geojson 12 | - 地名情報(自然地名) 13 | https://cyberjapandata.gsi.go.jp/xyz/experimental_nnfpt/{z}/{x}/{y}.geojson 14 | - 地名情報(公共施設) 15 | https://cyberjapandata.gsi.go.jp/xyz/experimental_pfpt/{z}/{x}/{y}.geojson 16 | - 地名情報(住居表示住所) 17 | https://cyberjapandata.gsi.go.jp/xyz/experimental_jhj/{z}/{x}/{y}.geojson 18 | - サンプル: 19 | https://cyberjapandata.gsi.go.jp/xyz/experimental_nrpt/15/27574/14087.geojson 20 | 21 | ### ベクトルタイルスタイル定義: 22 | - 地名情報(居住地名) 23 | https://cyberjapandata.gsi.go.jp/xyz/experimental_nrpt/style.js 24 | - 地名情報(自然地名) 25 | https://cyberjapandata.gsi.go.jp/xyz/experimental_nnfpt/style.js 26 | - 地名情報(公共施設) 27 | https://cyberjapandata.gsi.go.jp/xyz/experimental_pfpt/style.js 28 | - 地名情報(住居表示住所) 29 | https://cyberjapandata.gsi.go.jp/xyz/experimental_jhj/style.js 30 | 31 | ## データについて 32 | 電子国土基本図(地名情報)のうち、居住地名、自然地名、公共施設をズームレベル15の、住居表示住所をズームレベル18のGeoJSONタイルに変換したものを提供実験いたします。 33 | 提供範囲は日本全国になります。 34 | データの内容の詳細は、電子国土基本図(地名情報)ファイル仕様書(PDFファイル)(http://www.gsi.go.jp/common/000187334.pdf ) 35 | 及び電子国土基本図(地名情報)「住居表示住所」ファイル仕様書(http://www.gsi.go.jp/common/000187306.pdf )等をご覧ください。 36 | 37 | なお、以下の属性については本提供実験には含まれません。 38 | - 居住地名:都道府県名のよみ、市区町村名のよみ   39 | - 自然地名:行政コード、都道府県名、市区町村名   40 | - 住居表示住所:住所コード(可読)、住所コード(数値)、経度、緯度 41 | 42 | 43 | ## デモサイトについて 44 | デモサイトを次の場所に用意しております。 45 | - 本レポジトリ 46 | https://gsi-cyberjapan.github.io/experimental_pni/ 47 | - 地理院地図 48 | https://maps.gsi.go.jp/#18/35.682597/139.856129/&base=ort&ls=ort|experimental_nrpt|experimental_nnfpt|experimental_pfpt|experimental_jhj&disp=11111&lcd=experimental_jhj&vs=c1j0l0u0t0z0r0f0&d=vl 49 | 50 | ## 提供の位置づけ 51 | 国土地理院ベクトルタイル提供実験におけるデータの提供の位置づけは次のとおりです。 52 | - 本提供実験は、ベクトルタイル提供における技術的・施策的課題を国土地理院が把握するとともに、外部からの技術的な提案を受け取り、外部との技術的な議論を通じてベクトルタイルの適切な提供方法を研究開発することを目的とするものです。 53 | - 本提供実験の期間は、2014年8月1日から本提供実験終了までとなります。 54 | - 本提供実験のデータは、国土地理院コンテンツ利用規約( http://www.gsi.go.jp/kikakuchousei/kikakuchousei40182.html )に従って利用できます。 55 | - 本提供実験のベクトルタイルは基本測量成果と位置付けているものではありませんが、基本測量成果としての提供を検討するにあたって、提供を行うものです。 56 | - 本提供実験の利用により生じた損失及び損害等について、国土地理院はいかなる責任も負わないものとします。 57 | 58 | ## 履歴 59 | 2017-08-04「地名情報(居住地名)」、「地名情報(自然地名)」、「地名情報(公共施設)」及び「地名情報(住居表示住所)」の提供実験を開始。 60 | -------------------------------------------------------------------------------- /leaflet-hash.js: -------------------------------------------------------------------------------- 1 | // original: https://github.com/mlevans/leaflet-hash/blob/master/leaflet-hash.js 2 | // license: https://github.com/mlevans/leaflet-hash/blob/master/LICENSE.md 3 | (function(window) { 4 | var HAS_HASHCHANGE = (function() { 5 | var doc_mode = window.documentMode; 6 | return ('onhashchange' in window) && 7 | (doc_mode === undefined || doc_mode > 7); 8 | })(); 9 | 10 | L.Hash = function(map) { 11 | this.onHashChange = L.Util.bind(this.onHashChange, this); 12 | 13 | if (map) { 14 | this.init(map); 15 | } 16 | }; 17 | 18 | L.Hash.parseHash = function(hash) { 19 | if(hash.indexOf('#') === 0) { 20 | hash = hash.substr(1); 21 | } 22 | var args = hash.split("/"); 23 | if (args.length == 3) { 24 | var zoom = parseInt(args[0], 10), 25 | lat = parseFloat(args[1]), 26 | lon = parseFloat(args[2]); 27 | if (isNaN(zoom) || isNaN(lat) || isNaN(lon)) { 28 | return false; 29 | } else { 30 | return { 31 | center: new L.LatLng(lat, lon), 32 | zoom: zoom 33 | }; 34 | } 35 | } else { 36 | return false; 37 | } 38 | }; 39 | 40 | L.Hash.formatHash = function(map) { 41 | var center = map.getCenter(), 42 | zoom = map.getZoom(), 43 | precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2)); 44 | 45 | return "#" + [zoom, 46 | center.lat.toFixed(precision), 47 | center.lng.toFixed(precision) 48 | ].join("/"); 49 | }, 50 | 51 | L.Hash.prototype = { 52 | map: null, 53 | lastHash: null, 54 | 55 | parseHash: L.Hash.parseHash, 56 | formatHash: L.Hash.formatHash, 57 | 58 | init: function(map) { 59 | this.map = map; 60 | 61 | // reset the hash 62 | this.lastHash = null; 63 | this.onHashChange(); 64 | 65 | if (!this.isListening) { 66 | this.startListening(); 67 | } 68 | }, 69 | 70 | removeFrom: function(map) { 71 | if (this.changeTimeout) { 72 | clearTimeout(this.changeTimeout); 73 | } 74 | 75 | if (this.isListening) { 76 | this.stopListening(); 77 | } 78 | 79 | this.map = null; 80 | }, 81 | 82 | onMapMove: function() { 83 | // bail if we're moving the map (updating from a hash), 84 | // or if the map is not yet loaded 85 | 86 | if (this.movingMap || !this.map._loaded) { 87 | return false; 88 | } 89 | 90 | var hash = this.formatHash(this.map); 91 | if (this.lastHash != hash) { 92 | location.replace(hash); 93 | this.lastHash = hash; 94 | } 95 | }, 96 | 97 | movingMap: false, 98 | update: function() { 99 | var hash = location.hash; 100 | if (hash === this.lastHash) { 101 | return; 102 | } 103 | var parsed = this.parseHash(hash); 104 | if (parsed) { 105 | this.movingMap = true; 106 | 107 | this.map.setView(parsed.center, parsed.zoom); 108 | 109 | this.movingMap = false; 110 | } else { 111 | this.onMapMove(this.map); 112 | } 113 | }, 114 | 115 | // defer hash change updates every 100ms 116 | changeDefer: 100, 117 | changeTimeout: null, 118 | onHashChange: function() { 119 | // throttle calls to update() so that they only happen every 120 | // `changeDefer` ms 121 | if (!this.changeTimeout) { 122 | var that = this; 123 | this.changeTimeout = setTimeout(function() { 124 | that.update(); 125 | that.changeTimeout = null; 126 | }, this.changeDefer); 127 | } 128 | }, 129 | 130 | isListening: false, 131 | hashChangeInterval: null, 132 | startListening: function() { 133 | this.map.on("moveend", this.onMapMove, this); 134 | 135 | if (HAS_HASHCHANGE) { 136 | L.DomEvent.addListener(window, "hashchange", this.onHashChange); 137 | } else { 138 | clearInterval(this.hashChangeInterval); 139 | this.hashChangeInterval = setInterval(this.onHashChange, 50); 140 | } 141 | this.isListening = true; 142 | }, 143 | 144 | stopListening: function() { 145 | this.map.off("moveend", this.onMapMove, this); 146 | 147 | if (HAS_HASHCHANGE) { 148 | L.DomEvent.removeListener(window, "hashchange", this.onHashChange); 149 | } else { 150 | clearInterval(this.hashChangeInterval); 151 | } 152 | this.isListening = false; 153 | } 154 | }; 155 | L.hash = function(map) { 156 | return new L.Hash(map); 157 | }; 158 | L.Map.prototype.addHash = function() { 159 | this._hash = L.hash(this); 160 | }; 161 | L.Map.prototype.removeHash = function() { 162 | this._hash.removeFrom(); 163 | }; 164 | })(window); 165 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 地理院ベクトルタイル提供実験 7 | 8 | 9 | 10 | 11 | 22 | 23 | 24 |
25 | 200 | 201 | 202 | -------------------------------------------------------------------------------- /TileLayer.GeoJSON.js: -------------------------------------------------------------------------------- 1 | // copy of http://raw.github.com/glenrobertson/leaflet-tilelayer-geojson/master/TileLayer.GeoJSON.js 2 | // LICENCE: https://github.com/glenrobertson/leaflet-tilelayer-geojson/blob/master/LICENCE 3 | // Load data tiles from an AJAX data source 4 | L.TileLayer.Ajax = L.TileLayer.extend({ 5 | _requests: [], 6 | _addTile: function (tilePoint) { 7 | var tile = { datum: null, processed: false }; 8 | this._tiles[tilePoint.x + ':' + tilePoint.y] = tile; 9 | this._loadTile(tile, tilePoint); 10 | }, 11 | // XMLHttpRequest handler; closure over the XHR object, the layer, and the tile 12 | _xhrHandler: function (req, layer, tile, tilePoint) { 13 | return function () { 14 | if (req.readyState !== 4) { 15 | return; 16 | } 17 | var s = req.status; 18 | if ((s >= 200 && s < 300) || s === 304) { 19 | tile.datum = JSON.parse(req.responseText); 20 | layer._tileLoaded(tile, tilePoint); 21 | } else { 22 | layer._tileLoaded(tile, tilePoint); 23 | } 24 | }; 25 | }, 26 | // Load the requested tile via AJAX 27 | _loadTile: function (tile, tilePoint) { 28 | this._adjustTilePoint(tilePoint); 29 | var layer = this; 30 | var req = new XMLHttpRequest(); 31 | this._requests.push(req); 32 | req.onreadystatechange = this._xhrHandler(req, layer, tile, tilePoint); 33 | req.open('GET', this.getTileUrl(tilePoint), true); 34 | req.send(); 35 | }, 36 | _reset: function () { 37 | L.TileLayer.prototype._reset.apply(this, arguments); 38 | for (var i in this._requests) { 39 | this._requests[i].abort(); 40 | } 41 | this._requests = []; 42 | }, 43 | _update: function () { 44 | if (this._map._panTransition && this._map._panTransition._inProgress) { return; } 45 | if (this._tilesToLoad < 0) { this._tilesToLoad = 0; } 46 | L.TileLayer.prototype._update.apply(this, arguments); 47 | } 48 | }); 49 | 50 | 51 | L.TileLayer.GeoJSON = L.TileLayer.Ajax.extend({ 52 | // Store each GeometryCollection's layer by key, if options.unique function is present 53 | _keyLayers: {}, 54 | 55 | // Used to calculate svg path string for clip path elements 56 | _clipPathRectangles: {}, 57 | 58 | initialize: function (url, options, geojsonOptions) { 59 | L.TileLayer.Ajax.prototype.initialize.call(this, url, options); 60 | this.geojsonLayer = new L.GeoJSON(null, geojsonOptions); 61 | }, 62 | onAdd: function (map) { 63 | this._map = map; 64 | L.TileLayer.Ajax.prototype.onAdd.call(this, map); 65 | map.addLayer(this.geojsonLayer); 66 | }, 67 | onRemove: function (map) { 68 | map.removeLayer(this.geojsonLayer); 69 | L.TileLayer.Ajax.prototype.onRemove.call(this, map); 70 | }, 71 | _reset: function () { 72 | this.geojsonLayer.clearLayers(); 73 | this._keyLayers = {}; 74 | this._removeOldClipPaths(); 75 | L.TileLayer.Ajax.prototype._reset.apply(this, arguments); 76 | }, 77 | 78 | // Remove clip path elements from other earlier zoom levels 79 | _removeOldClipPaths: function () { 80 | for (var clipPathId in this._clipPathRectangles) { 81 | var clipPathZXY = clipPathId.split('_').slice(1); 82 | var zoom = parseInt(clipPathZXY[0], 10); 83 | if (zoom !== this._map.getZoom()) { 84 | var rectangle = this._clipPathRectangles[clipPathId]; 85 | this._map.removeLayer(rectangle); 86 | var clipPath = document.getElementById(clipPathId); 87 | if (clipPath !== null) { 88 | clipPath.parentNode.removeChild(clipPath); 89 | } 90 | delete this._clipPathRectangles[clipPathId]; 91 | } 92 | } 93 | }, 94 | 95 | // Recurse LayerGroups and call func() on L.Path layer instances 96 | _recurseLayerUntilPath: function (func, layer) { 97 | if (layer instanceof L.Path) { 98 | func(layer); 99 | } 100 | else if (layer instanceof L.LayerGroup) { 101 | // Recurse each child layer 102 | layer.getLayers().forEach(this._recurseLayerUntilPath.bind(this, func), this); 103 | } 104 | }, 105 | 106 | _clipLayerToTileBoundary: function (layer, tilePoint) { 107 | // Only perform SVG clipping if the browser is using SVG 108 | if (!L.Path.SVG) { return; } 109 | 110 | var svg = this._map._pathRoot; 111 | 112 | // create the defs container if it doesn't exist 113 | var defs = null; 114 | if (svg.getElementsByTagName('defs').length === 0) { 115 | defs = document.createElementNS(L.Path.SVG_NS, 'defs'); 116 | svg.insertBefore(defs, svg.firstChild); 117 | } 118 | else { 119 | defs = svg.getElementsByTagName('defs')[0]; 120 | } 121 | 122 | // Create the clipPath for the tile if it doesn't exist 123 | var clipPathId = 'tileClipPath_' + tilePoint.z + '_' + tilePoint.x + '_' + tilePoint.y; 124 | var clipPath = document.getElementById(clipPathId); 125 | if (clipPath === null) { 126 | clipPath = document.createElementNS(L.Path.SVG_NS, 'clipPath'); 127 | clipPath.id = clipPathId; 128 | 129 | // Create a hidden L.Rectangle to represent the tile's area 130 | var tileSize = this.options.tileSize, 131 | nwPoint = tilePoint.multiplyBy(tileSize), 132 | sePoint = nwPoint.add([tileSize, tileSize]), 133 | nw = this._map.unproject(nwPoint), 134 | se = this._map.unproject(sePoint); 135 | this._clipPathRectangles[clipPathId] = new L.Rectangle(new L.LatLngBounds([nw, se]), { 136 | opacity: 0, 137 | fillOpacity: 0, 138 | clickable: false, 139 | noClip: true 140 | }); 141 | this._map.addLayer(this._clipPathRectangles[clipPathId]); 142 | 143 | // Add a clip path element to the SVG defs element 144 | // With a path element that has the hidden rectangle's SVG path string 145 | var path = document.createElementNS(L.Path.SVG_NS, 'path'); 146 | var pathString = this._clipPathRectangles[clipPathId].getPathString(); 147 | path.setAttribute('d', pathString); 148 | clipPath.appendChild(path); 149 | defs.appendChild(clipPath); 150 | } 151 | 152 | // Add the clip-path attribute to reference the id of the tile clipPath 153 | this._recurseLayerUntilPath(function (pathLayer) { 154 | pathLayer._container.setAttribute('clip-path', 'url(#' + clipPathId + ')'); 155 | }, layer); 156 | }, 157 | 158 | // Add a geojson object from a tile to the GeoJSON layer 159 | // * If the options.unique function is specified, merge geometries into GeometryCollections 160 | // grouped by the key returned by options.unique(feature) for each GeoJSON feature 161 | // * If options.clipTiles is set, and the browser is using SVG, perform SVG clipping on each 162 | // tile's GeometryCollection 163 | addTileData: function (geojson, tilePoint) { 164 | var features = L.Util.isArray(geojson) ? geojson : geojson.features, 165 | i, len, feature; 166 | 167 | if (features) { 168 | for (i = 0, len = features.length; i < len; i++) { 169 | // Only add this if geometry or geometries are set and not null 170 | feature = features[i]; 171 | if (feature.geometries || feature.geometry || feature.features || feature.coordinates) { 172 | this.addTileData(features[i], tilePoint); 173 | } 174 | } 175 | return this; 176 | } 177 | 178 | var options = this.geojsonLayer.options; 179 | 180 | if (options.filter && !options.filter(geojson)) { return; } 181 | 182 | var parentLayer = this.geojsonLayer; 183 | var incomingLayer = null; 184 | if (this.options.unique && typeof(this.options.unique) === 'function') { 185 | var key = this.options.unique(geojson); 186 | 187 | // When creating the layer for a unique key, 188 | // Force the geojson to be a geometry collection 189 | if (!(key in this._keyLayers && geojson.geometry.type !== 'GeometryCollection')) { 190 | geojson.geometry = { 191 | type: 'GeometryCollection', 192 | geometries: [geojson.geometry] 193 | }; 194 | } 195 | 196 | // Transform the geojson into a new Layer 197 | try { 198 | incomingLayer = L.GeoJSON.geometryToLayer(geojson, options.pointToLayer, options.coordsToLatLng); 199 | } 200 | // Ignore GeoJSON objects that could not be parsed 201 | catch (e) { 202 | return this; 203 | } 204 | 205 | // Add the incoming Layer to existing key's GeometryCollection 206 | if (key in this._keyLayers) { 207 | parentLayer = this._keyLayers[key]; 208 | parentLayer.feature.geometry.geometries.push(geojson.geometry); 209 | } 210 | // Convert the incoming GeoJSON feature into a new GeometryCollection layer 211 | else { 212 | incomingLayer.feature = L.GeoJSON.asFeature(geojson); 213 | this._keyLayers[key] = incomingLayer; 214 | } 215 | } 216 | // Add the incoming geojson feature to the L.GeoJSON Layer 217 | else { 218 | // Transform the geojson into a new layer 219 | try { 220 | incomingLayer = L.GeoJSON.geometryToLayer(geojson, options.pointToLayer, options.coordsToLatLng); 221 | } 222 | // Ignore GeoJSON objects that could not be parsed 223 | catch (e) { 224 | return this; 225 | } 226 | incomingLayer.feature = L.GeoJSON.asFeature(geojson); 227 | } 228 | incomingLayer.defaultOptions = incomingLayer.options; 229 | 230 | this.geojsonLayer.resetStyle(incomingLayer); 231 | 232 | if (options.onEachFeature) { 233 | options.onEachFeature(geojson, incomingLayer); 234 | } 235 | parentLayer.addLayer(incomingLayer); 236 | 237 | // If options.clipTiles is set and the browser is using SVG 238 | // then clip the layer using SVG clipping 239 | if (this.options.clipTiles) { 240 | this._clipLayerToTileBoundary(incomingLayer, tilePoint); 241 | } 242 | return this; 243 | }, 244 | 245 | _tileLoaded: function (tile, tilePoint) { 246 | L.TileLayer.Ajax.prototype._tileLoaded.apply(this, arguments); 247 | if (tile.datum === null) { return null; } 248 | this.addTileData(tile.datum, tilePoint); 249 | } 250 | }); 251 | --------------------------------------------------------------------------------