|
<!DOCTYPE html> |
|
<html lang="ja"> |
|
|
|
<head> |
|
<meta charset="UTF-8"> |
|
<title>地図上に図形を描画する</title> |
|
|
|
<script src="https://code.jquery.com/jquery-2.1.1.min.js"></script> |
|
|
|
<!-- Leaflet.draw0.4.9がv1.0.xまでしか対応していないので --> |
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.0.3/leaflet-src.js"></script> |
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.0.3/leaflet.css" /> |
|
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/0.4.9/leaflet.draw-src.js"></script> |
|
<link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/0.4.9/leaflet.draw-src.css' /> |
|
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet-minimap/3.5.0/Control.MiniMap.js"></script> |
|
<link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/leaflet-minimap/3.5.0/Control.MiniMap.css' /> |
|
|
|
<style type="text/css"> |
|
#map { |
|
z-index: 0; |
|
height: 445px; |
|
} |
|
|
|
.actions { |
|
display: inline-block; |
|
cursor: pointer; |
|
text-decoration: none; |
|
color: black; |
|
font-family: 'Helvetica Neue'; |
|
font-size: 12px; |
|
background: -webkit-linear-gradient(top, #fff 0%, #f0f0f0 100%); |
|
background: linear-gradient(to bottom, #fff 0%, #f0f0f0 100%); |
|
border: 1px solid #ccc; |
|
box-shadow: 0 -1px 0 rgba(255, 255, 255, 1) inset; |
|
margin: 0.3em 0; |
|
padding: 1em; |
|
} |
|
|
|
.input-file { |
|
position: relative; |
|
display: inline-block; |
|
} |
|
|
|
.input-file input[type="file"] { |
|
position: absolute; |
|
width: 100%; |
|
height: 100%; |
|
left: 0; |
|
top: 0; |
|
opacity: 0; |
|
} |
|
|
|
.notes { |
|
border: 1px solid #ccc; |
|
} |
|
</style> |
|
</head> |
|
|
|
<body> |
|
<div id="map"></div> |
|
<div class='actions' id='delete'>Delete Features</div> |
|
<a href='#' class='actions' id='export'>Export Features</a> |
|
<!-- input type="file" のデザイン変更は右参照 http://var.blog.jp/archives/57292423.html --> |
|
<div class="input-file"> |
|
<span class="actions">Import Features</span> |
|
<input type="file" id='import'> |
|
</div> |
|
|
|
<script type="text/javascript"> |
|
// 地図レイヤの準備 |
|
var t_pale = new L.tileLayer('http://cyberjapandata.gsi.go.jp/xyz/pale/{z}/{x}/{y}.png', { |
|
attribution: "<a href='http://www.gsi.go.jp/kikakuchousei/kikakuchousei40182.html' target='_blank'>国土地理院</a>" |
|
}); |
|
var t_std = new L.tileLayer('http://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png', { |
|
attribution: "<a href='http://www.gsi.go.jp/kikakuchousei/kikakuchousei40182.html' target='_blank'>国土地理院</a>" |
|
}); |
|
var t_ort = new L.tileLayer('http://cyberjapandata.gsi.go.jp/xyz/ort/{z}/{x}/{y}.jpg', { |
|
attribution: "<a href='http://www.gsi.go.jp/kikakuchousei/kikakuchousei40182.html' target='_blank'>国土地理院</a>" |
|
}); |
|
var t_pale_mini = new L.tileLayer('http://cyberjapandata.gsi.go.jp/xyz/pale/{z}/{x}/{y}.png'); |
|
var map = L.map('map', { |
|
center: [34.816702, 135.497171], |
|
zoom: 14, |
|
zoomControl: true, |
|
layers: [t_pale] |
|
}); |
|
var miniMap = new L.Control.MiniMap(t_pale_mini, { toggleDisplay: true }).addTo(map); |
|
var baseLayer = { |
|
"地理院地図 淡色": t_pale, |
|
"地理院地図 標準": t_std, |
|
"地理院地図 オルソ": t_ort, |
|
}; |
|
L.control.layers(baseLayer, null, { |
|
collapsed: true |
|
}).addTo(map); |
|
L.control.scale({ |
|
imperial: false, |
|
maxWidth: 200 |
|
}).addTo(map); |
|
|
|
// 描画レイヤの初期化 |
|
var drawnItems = new L.FeatureGroup().addTo(map); |
|
var drawControl = new L.Control.Draw({ |
|
draw: { |
|
circle: { |
|
feet: false |
|
}, |
|
}, |
|
edit: { |
|
featureGroup: drawnItems, |
|
}, |
|
}).addTo(map); |
|
|
|
// 何かを描画した際の生成やら設定やら |
|
map.on(L.Draw.Event.CREATED, function (e) { |
|
// featureのポップアップで値を入力する方法は下記を参考にしました |
|
// https://gis.stackexchange.com/questions/202966/leaflet-popups-preserving-user-input-on-close-reopen |
|
// geojsonに落とす際にpropertiesに値を保持する方法は下記を参考にしました |
|
// https://stackoverflow.com/questions/35760126/leaflet-draw-not-taking-properties-when-converting-featuregroup-to-geojson |
|
drawnItems.addLayer(e.layer); |
|
e.layer.feature = e.layer.feature || {}; |
|
e.layer.feature.properties = e.layer.feature.properties || {}; |
|
e.layer.feature.properties.note = e.layer.feature.properties.note || ""; |
|
e.layer.feature.type = "Feature"; |
|
popup = e.layer.bindPopup(""); |
|
setFeatureProperties(e.layer); |
|
popup.on("popupopen", function (p) { |
|
$('#note_' + p.target._leaflet_id).attr('value', p.target.feature.properties.note).focus(); |
|
}); |
|
popup.on("popupclose", function (p) { |
|
p.target.feature.properties.note = $('#note_' + p.target._leaflet_id).val(); |
|
}); |
|
}); |
|
map.on(L.Draw.Event.EDITED, function (e) { |
|
e.layers.eachLayer(function (layer) { |
|
setFeatureProperties(layer); |
|
}); |
|
}); |
|
|
|
// 描画物の情報を計算し保持するメソッド(以下を参考にしました) |
|
// http://leaflet.github.io/Leaflet.draw/docs/examples/popup.html |
|
var setFeatureProperties = function (layer) { |
|
// 線と多角形と四角形 |
|
if (layer instanceof L.Polyline) { |
|
var latlngs = layer._defaultShape ? layer._defaultShape() : layer.getLatLngs(); |
|
if (latlngs.length >= 2) { |
|
var distance = 0; |
|
for (var i = 0; i < latlngs.length - 1; i++) { |
|
distance += latlngs[i].distanceTo(latlngs[i + 1]); |
|
} |
|
layer.feature.properties.distance = distance.toFixed(2) + " m"; // ex. distance 3728.81 m |
|
} |
|
layer.feature.properties.drawtype = L.Draw.Polyline.TYPE; |
|
} |
|
// 多角形と四角形 |
|
if (layer instanceof L.Polygon) { |
|
var latlngs = layer._defaultShape ? layer._defaultShape() : layer.getLatLngs(); |
|
var area = L.GeometryUtil.geodesicArea(latlngs); |
|
layer.feature.properties.area = L.GeometryUtil.readableArea(area, true); // ex. area 174.19 ha |
|
layer.feature.properties.drawtype = L.Draw.Polygon.TYPE; |
|
} |
|
// 四角形 |
|
if (layer instanceof L.Rectangle) { |
|
layer.feature.properties.drawtype = L.Draw.Rectangle.TYPE; |
|
} |
|
// 円 |
|
if (layer instanceof L.Circle) { |
|
layer.feature.properties.radius = layer.getRadius().toFixed(2) + " m"; // ex. radius 1097.02 m |
|
layer.feature.properties.drawtype = L.Draw.Circle.TYPE; |
|
} |
|
// マーカー |
|
if (layer instanceof L.Marker) { |
|
layer.feature.properties.drawtype = L.Draw.Marker.TYPE; |
|
} |
|
// popup時の表示内容の差し替え |
|
var contents = ""; |
|
for (var key in layer.feature.properties) { |
|
if (key != 'note' && key != 'drawtype') { |
|
contents = contents + key + " " + layer.feature.properties[key] + "<br />"; |
|
} |
|
} |
|
contents += "note <input type='text' class='notes' id='note_" + layer._leaflet_id + "' value=''>"; |
|
layer.setPopupContent(contents); |
|
}; |
|
|
|
// geoJSONのdeleteボタン有効化 |
|
document.getElementById('delete').onclick = function (e) { |
|
drawnItems.clearLayers(); |
|
} |
|
// geoJSONのexportボタン有効化 |
|
document.getElementById('export').onclick = function (e) { |
|
// Extractions GeoJson from featureGroup |
|
var geojson = drawnItems.toGeoJSON(); |
|
// Stringify the GeoJson |
|
var convertedData = 'text/json;charset=utf-8,' + encodeURIComponent(JSON.stringify(geojson)); |
|
// Create export |
|
document.getElementById('export').setAttribute('href', 'data:' + convertedData); |
|
document.getElementById('export').setAttribute('download', 'data.geojson'); |
|
} |
|
// geoJSONのimportボタン有効化 |
|
document.getElementById('import').onchange = function (e) { |
|
var reader = new FileReader(); |
|
reader.onload = function () { |
|
var features; |
|
try { |
|
features = JSON.parse(reader.result).features; |
|
} |
|
catch (ex) { |
|
console.log("Imported file is not a JSON file."); |
|
} |
|
// importするfeatureをleaflet.drawで描画したかのように差し込む |
|
for (var feature of features) { |
|
if (feature.type == 'Feature') { |
|
// 線 |
|
if (feature.geometry.type == 'LineString') { |
|
var latlngs = []; |
|
for (var point of feature.geometry.coordinates) { |
|
latlngs.push(L.latLng(point[1], point[0])); |
|
} |
|
var handler = drawControl._toolbars.draw._modes.polyline.handler; |
|
var layer = new L.Polyline(latlngs, handler.options.shapeOptions); |
|
layer.feature = feature; |
|
L.Draw.Feature.prototype._fireCreatedEvent.call(handler, layer); |
|
} |
|
else if (feature.geometry.type == 'Polygon') { |
|
if (feature.properties.drawtype == L.Draw.Rectangle.TYPE) { |
|
// 四角形 |
|
var handler = drawControl._toolbars.draw._modes.rectangle.handler; |
|
var corner1 = L.latLng(feature.geometry.coordinates[0][0][1], feature.geometry.coordinates[0][0][0]); |
|
var corner2 = L.latLng(feature.geometry.coordinates[0][2][1], feature.geometry.coordinates[0][2][0]); |
|
var layer = new L.Rectangle(new L.LatLngBounds(corner1, corner2), handler.options.shapeOptions); |
|
layer.feature = feature; |
|
L.Draw.SimpleShape.prototype._fireCreatedEvent.call(handler, layer); |
|
} |
|
else { |
|
// 多角形 |
|
var rings = []; |
|
for (var ring of feature.geometry.coordinates) { |
|
var latlngs = []; |
|
for (var point of ring) { |
|
latlngs.push(L.latLng(point[1], point[0])); |
|
} |
|
latlngs.pop(); // geoJsonのPolygonでは、最後に先頭と同じ点が入ってくるので削る |
|
rings.push(latlngs); |
|
} |
|
var handler = drawControl._toolbars.draw._modes.polygon.handler; |
|
var layer = new L.Polygon(rings, handler.options.shapeOptions); |
|
layer.feature = feature; |
|
L.Draw.Feature.prototype._fireCreatedEvent.call(handler, layer); |
|
} |
|
} |
|
else if (feature.geometry.type == 'Point') { |
|
var latlng = L.latLng(feature.geometry.coordinates[1], feature.geometry.coordinates[0]); |
|
if (feature.properties.drawtype == L.Draw.Circle.TYPE) { |
|
// 円 |
|
var handler = drawControl._toolbars.draw._modes.circle.handler; |
|
var radius = parseFloat(feature.properties.radius); //" m"が勝手にとれるとは… |
|
var layer = new L.Circle(latlng, radius, handler.options.shapeOptions); |
|
layer.feature = feature; |
|
L.Draw.SimpleShape.prototype._fireCreatedEvent.call(handler, layer); |
|
} |
|
else { |
|
// マーカー |
|
var handler = drawControl._toolbars.draw._modes.marker.handler; |
|
var layer = new L.Marker(latlng, handler.options); |
|
layer.feature = feature; |
|
L.Draw.Feature.prototype._fireCreatedEvent.call(handler, layer); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
console.log("Imported file: " + e.target.files[0].name); |
|
reader.readAsText(e.target.files[0]); |
|
// input type fileで同じファイルで2回目以降onChangeが発火しない問題への対応(IE10だと下記ではダメらしい) |
|
$('input[type=file]').val(''); |
|
} |
|
</script> |
|
|
|
</body> |
|
|
|
</html> |