ボロノイ図を使って、クリックした位置から最短距離の施設を見つけるサンプル(D3 ver4版)。
Built with blockbuilder.org
| license: gpl-3.0 |
ボロノイ図を使って、クリックした位置から最短距離の施設を見つけるサンプル(D3 ver4版)。
Built with blockbuilder.org
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <meta charset="utf-8" /> | |
| <meta http-equiv="X-UA-Compatible" content="IE=edge"/> | |
| <title>ボロノイ図 - 最近傍探索</title> | |
| <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/leaflet.css"/> | |
| <style> | |
| html, body{ | |
| padding: 0px; | |
| margin: 0px; | |
| width:100%; | |
| height: 100%; | |
| } | |
| #map { | |
| width:100%; | |
| height: 488px; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| Click on the map | |
| <input name="volonoi_togle" type="radio" value="true">show | |
| <input name="volonoi_togle" type="radio" value="false" checked="checked">hidden | |
| <div id="map"></div> | |
| <script src="//unpkg.com/babel-standalone@6.26.0/babel.min.js"></script> | |
| <script src="//unpkg.com/d3@4.12.2/build/d3.min.js"></script> | |
| <script src="//cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/leaflet-src.js"></script> | |
| <script type="text/babel"> | |
| const map = L.map("map") | |
| let markers | |
| L.tileLayer('http://cyberjapandata.gsi.go.jp/xyz/pale/{z}/{x}/{y}.png', { | |
| attribution: "<a href='http://maps.gsi.go.jp/development/ichiran.html' target='_blank'>地理院タイル</a>" | |
| }).addTo(map); | |
| map.setView([36.322356, 139.013057], 14) | |
| map._initPathRoot() | |
| const svg = d3.select("#map").select("svg") | |
| const voloLayer = svg.append("g") | |
| .attr("class", "leaflet-zoom-hide") | |
| .attr("id", "voloLayer") | |
| d3.json("point.geojson", main) | |
| function main(data) { | |
| const positions = getPositions(data) | |
| addMakers(positions) | |
| drawVolonoi(positions) | |
| setEvent(data) | |
| } | |
| function getPositions(data) { | |
| const result = [] | |
| //位置情報→座標変換 | |
| data.features.forEach(d => { | |
| const latlng = new L.LatLng(d.geometry.coordinates[1], d.geometry.coordinates[0]) | |
| result.push({ | |
| "id": d.id, | |
| latlng: latlng | |
| }) | |
| }) | |
| return result | |
| } | |
| function addMakers(positions) { | |
| positions.forEach(d => { | |
| L.marker(d.latlng).addTo(map) | |
| }) | |
| } | |
| function setEvent(data) { | |
| map.on("viewreset moveend", function(){ | |
| d3.selectAll(".saitan").remove() | |
| const positions = getPositions(data) | |
| drawVolonoi(positions) | |
| }) | |
| const volonoi_toggle = function(){ | |
| if(this.value === "true"){ | |
| d3.selectAll(".volonoi").attr("stroke-opacity", 1) | |
| }else{ | |
| d3.selectAll(".volonoi").attr("stroke-opacity", 0) | |
| } | |
| } | |
| const elm = d3.selectAll("input[name=volonoi_togle]") | |
| elm.on("change", volonoi_toggle) | |
| } | |
| function drawVolonoi(data){ | |
| data.forEach(d => { | |
| d.x = map.latLngToLayerPoint(d.latlng).x | |
| d.y = map.latLngToLayerPoint(d.latlng).y | |
| }) | |
| const voronoi = d3.voronoi() | |
| .x(d => d.x ) | |
| .y(d => d.y ) | |
| const polygons = voronoi(data).polygons() | |
| voloLayer.selectAll(".volonoi").remove() | |
| voloLayer.selectAll("path") | |
| .data(polygons) | |
| .enter() | |
| .append("svg:path") | |
| .attr("class", "volonoi") | |
| .attr("id", (d, i) => `volo${i}` ) | |
| .attr("d", d => { | |
| if(!d) return null | |
| return "M" + d.filter( df => df != null ).join("L") + "Z" | |
| }) | |
| .attr("stroke", "black") | |
| .attr("fill", "white") | |
| .attr("fill-opacity", 0) | |
| .on("click", function(d){ | |
| voloLayer.selectAll(".saitan").remove() | |
| addCircle(d.data.x, d.data.y, 20) | |
| const mouseXY = d3.mouse(this) | |
| addLine(d.data.x, d.data.y, mouseXY[0], mouseXY[1]) | |
| addCircle(mouseXY[0], mouseXY[1], 4) | |
| }) | |
| const elm = document.querySelector("input[name=volonoi_togle]:checked") | |
| if(elm.value === "true"){ | |
| d3.selectAll(".volonoi").attr("stroke-opacity", 1) | |
| }else{ | |
| d3.selectAll(".volonoi").attr("stroke-opacity", 0) | |
| } | |
| } | |
| function addCircle(x, y, r){ | |
| voloLayer.append("circle") | |
| .attr("class", "saitan") | |
| .attr("cx", x) | |
| .attr("cy", y) | |
| .attr("r", 0) | |
| .transition() | |
| .attr("cx", x) | |
| .attr("cy", y) | |
| .attr("r", r) | |
| .attr("fill", "none") | |
| .attr("stroke", "red") | |
| .attr("stroke-width", 5) | |
| } | |
| function addLine(x1, y1, x2, y2){ | |
| voloLayer.append("line") | |
| .attr("class", "saitan") | |
| .attr("x1", x2) | |
| .attr("y1", y2) | |
| .attr("x2", x2) | |
| .attr("y2", y2) | |
| .transition() | |
| .attr("x1", x1) | |
| .attr("y1", y1) | |
| .attr("x2", x2) | |
| .attr("y2", y2) | |
| .attr("stroke", "red") | |
| .attr("stroke-width", 5) | |
| } | |
| </script> | |
| </body> | |
| </html> |