スクレイピングにはnode-horsemanを使用した。Nightmareによく似た使いやすいモジュール。結果をGeoJSON形式で出力する。1ページ5秒のウエイトと、結果に5000件の制限を設けてある。
var fs = require('fs');1var fs = require('fs');23var Horseman = require('node-horseman');4var horseman = new Horseman();56var results = [];78function getResult() {9return horseman10.evaluate(function() {11var features = [];12$('table[id="searchResult"] tbody tr:gt(1)').each(function(item) {13var lat = $.trim($(this).find('td:eq(4)').text()).split("°");14var lat_deg = parseFloat(lat[0]);15lat = lat[1].split("′");16var lat_min = parseFloat(lat[0]) / 60;17lat = lat[1].split("″");18var lat_sec = parseFloat(lat[0]) / 3600;19var lng = $.trim($(this).find('td:eq(5)').text()).split("°");20var lng_deg = parseFloat(lng[0]);21lng = lng[1].split("′");22var lng_min = parseFloat(lng[0]) / 60;23lng = lng[1].split("″");24var lng_sec = parseFloat(lng[0]) / 3600;2526var feature = {27'type': 'Feature',28'geometry': {
29'type': 'Point',30'coordinates': [
31(lng_deg + lng_min + lng_sec),32(lat_deg + lat_min + lat_sec)33]34},35'properties': {
36'id' : $.trim($(this).find('td:eq(0) a').text()),37'project' : $.trim($(this).find('td:eq(1)').text()),38'investigation' : $.trim($(this).find('td:eq(2)').text()),39'organization' : $.trim($(this).find('td:eq(3)').text()),40'length' : $.trim($(this).find('td:eq(6)').text()),41'elevation' : $.trim($(this).find('td:eq(7)').text()),42'fig' : $.trim($(this).find('td:eq(8) a').attr("href")),43'result' : $.trim($(this).find('td:eq(9) a').attr("href")),44'xml' : $.trim($(this).find('td:eq(0) a').attr("href"))45}46};47features.push(feature);48});49return features;50});51}5253function hasNextPage() {54return horseman.exists('td.nextLink a');55}5657function scrape() {58var result = getResult();59results = results.concat(result);60var page = horseman61.evaluate(function() {62return $('td.pageCount').text();63});64console.log(page);65if (hasNextPage() && results.length < 5000){66horseman
67.click('td.nextLink a')68.waitForNextPage()
69.wait(5000);70scrape();
71}72}7374horseman
75.userAgent("Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0")76.viewport(1024, 768)77.open('http://www.kunijiban.pwri.go.jp/jp/denshikokudo/DB_Search/boringsearch.php')78.type('input[name="tl_lat_deg"]', '34')79.type('input[name="tl_lat_min"]', '23')80.type('input[name="tl_lat_sec"]', '52')81.type('input[name="tl_lon_deg"]', '131')82.type('input[name="tl_lon_min"]', '54')83.type('input[name="tl_lon_sec"]', '03')8485.type('input[name="br_lat_deg"]', '32')86.type('input[name="br_lat_min"]', '51')87.type('input[name="br_lat_sec"]', '40')88.type('input[name="br_lon_deg"]', '133')89.type('input[name="br_lon_min"]', '52')90.type('input[name="br_lon_sec"]', '03')91.click('input[value="検索"]')92.waitForSelector('td.nextLink a')93// .screenshot('kunijiban.png');
9495scrape();
9697var GeoJSON = {98"type": "FeatureCollection",99"crs": {
100"type": "name",101"properties": {
102"name": "urn:ogc:def:crs:EPSG::4612"103}104},105"features": results106};107108fs.writeFile('kunijiban.geojson', JSON.stringify(GeoJSON, null, " "));109110horseman.close();
次に行政区域の境界のバッファを作成して、空間演算により取得した検索結果のうち、愛媛県のみを抽出する。
行政区域の地形データはQGISで作成してもよいが、下記のGeoJSONを使用した。
dataofjapan/land
空間演算には下記のモジュールを使用した。
bjornharrtell/jsts
JSTS Example
下記のhtmlをローカルに置いてブラウザで読みこめば、同じディレクトリにあるjapan.geojson, kunijiban.geojsonを読み込んで地図上に表示する。ただしFirefoxとChromeのみ(Chromeはローカルファイルが読めるように、"--allow-file-access-from-files"オプションを付けて起動する必要がある)。空間演算が割と重くて時間がかかるので、ハングしたようになるかも。
12<html>
34<head>
5<title>ボーリング柱状図</title>6<meta charset="utf-8" />7<meta name="viewport" content="width=device-width, initial-scale=1.0">89<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.css" />1011<script src="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.js"></script>12<script src="http://code.jquery.com/jquery-2.1.0.min.js"></script>13<script src="https://rawgithub.com/bjornharrtell/jsts/master/lib/javascript.util.js"></script>14<script src="https://rawgithub.com/bjornharrtell/jsts/master/lib/jsts.js"></script>1516<link rel="kunijiban" type="application/json" href="kunijiban.geojson">17<link rel="japan" type="application/json" href="japan.geojson">1819<style>
20html,
21body,
22#map {
23height: 100%;24width: 100%;25padding: 0px;26margin: 0px;27}28</style>
29</head>
3031<body>
32<div id="map"></div>3334<script>
35var map = L.map('map').setView([33.523342, 132.863776], 10);36L.control.scale({'position':'bottomleft','metric':true,'imperial':false}).addTo(map);3738var distance = 5 / 111.12; // 5km39var geoReader = new jsts.io.GeoJSONReader(),40geoWriter = new jsts.io.GeoJSONWriter();4142var cyber = L.tileLayer('http://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png', {43maxZoom: 18,44attribution: '<a href="http://www.gsi.go.jp/kikakuchousei/kikakuchousei40182.html" target="_blank">国土地理院</a>'45}).addTo(map);46map.addLayer(cyber);4748var gbank = L.tileLayer.wms("https://gbank.gsj.jp/ows/seamlessgeology200k_b", {49layers: ['area,line,label'],50format: 'image/png',51transparent: true,52opacity: 0.5,53attribution: '<a href="https://gbank.gsj.jp/seamless/" target="_blank">シームレス地質図</a>'54}).addTo(map);55map.addLayer(gbank);5657L.control.layers({'地理院地図': cyber}, {'シームレス地質図': gbank}).addTo(map);5859function onEachFeature(feature, layer) {60var popupContent = '<p><a href="' + feature.properties.fig + '" target="_blank">';61popupContent += feature.properties.id + '</a><br />';62popupContent += feature.properties.investigation + '</p>';6364layer.bindPopup(popupContent);65}6667function createRegionBuffer() {68return new Promise(function (resolve, reject) {69$.getJSON($('link[rel="japan"]').attr("href"), function(data) {70var district;71data.features.forEach(function(item, index) {72if (item.properties.nam_ja == '愛媛県') district = item;73});74resolve(geoReader.read(district.geometry).buffer(distance));75});76});77}7879function readBoringData(region) {80return new Promise(function (resolve, reject) {81$.getJSON($('link[rel="kunijiban"]').attr("href"), function(data) {82var inRegion = data.features.filter(function(item, index) {83return geoReader.read(item.geometry).within(region);84});85resolve(inRegion);86});87});88}8990function renderRegionBuffer(region) {91return new Promise(function (resolve, reject) {92var buffer = geoWriter.write(region);93L.geoJson(buffer, {94style: {
95weight: 2,96color: "#999",97opacity: 1,98fillColor: "#B0DE5C",99fillOpacity: 0.8100}101}).addTo(map);102resolve(region);103});104}105106function renderBoringData(points) {107L.geoJson(points, {108onEachFeature: onEachFeature,109110pointToLayer: function(feature, latlng) {111return L.circleMarker(latlng, {112radius: 8,113fillColor: "#ff7800",114color: "#000",115weight: 1,116opacity: 1,117fillOpacity: 0.8118});119}120}).addTo(map);121}122123createRegionBuffer()
124// .then(renderRegionBuffer)
125.then(readBoringData)126.then(renderBoringData);127</script>
128</body>
129130</html>
マップは国土地理院のタイルと、日本シームレス地質図を使用している。
0 件のコメント:
コメントを投稿