ラベル JavaScript の投稿を表示しています。 すべての投稿を表示
ラベル JavaScript の投稿を表示しています。 すべての投稿を表示

JavaScriptで空間演算

2015/03/04
先日FirefoxとQGISで行った位置情報の取得・加工を、JavaScriptで行ってみる。
スクレイピングにはnode-horsemanを使用した。Nightmareによく似た使いやすいモジュール。結果をGeoJSON形式で出力する。1ページ5秒のウエイトと、結果に5000件の制限を設けてある。
var fs = require('fs');

var Horseman = require('node-horseman');
var horseman = new Horseman();

var results = [];

function getResult() {
    return horseman
        .evaluate(function() {
            var features = [];
            $('table[id="searchResult"] tbody tr:gt(1)').each(function(item) {
                var lat = $.trim($(this).find('td:eq(4)').text()).split("°");
                var lat_deg = parseFloat(lat[0]);
                lat = lat[1].split("′");
                var lat_min = parseFloat(lat[0]) / 60;
                lat = lat[1].split("″");
                var lat_sec = parseFloat(lat[0]) / 3600;
                var lng = $.trim($(this).find('td:eq(5)').text()).split("°");
                var lng_deg = parseFloat(lng[0]);
                lng = lng[1].split("′");
                var lng_min = parseFloat(lng[0]) / 60;
                lng = lng[1].split("″");
                var lng_sec = parseFloat(lng[0]) / 3600;

                var feature = {
                    'type': 'Feature',
                    'geometry': {
                        'type': 'Point',
                        'coordinates': [
                            (lng_deg + lng_min + lng_sec),
                            (lat_deg + lat_min + lat_sec)
                        ]
                    },
                    'properties': {
                        'id' :            $.trim($(this).find('td:eq(0) a').text()),
                        'project' :       $.trim($(this).find('td:eq(1)').text()),
                        'investigation' : $.trim($(this).find('td:eq(2)').text()),
                        'organization' :  $.trim($(this).find('td:eq(3)').text()),
                        'length' :        $.trim($(this).find('td:eq(6)').text()),
                        'elevation' :     $.trim($(this).find('td:eq(7)').text()),
                        'fig' :           $.trim($(this).find('td:eq(8) a').attr("href")),
                        'result' :        $.trim($(this).find('td:eq(9) a').attr("href")),
                        'xml' :           $.trim($(this).find('td:eq(0) a').attr("href"))
                    }
                };
                features.push(feature);
            });
            return features;
        });
}

function hasNextPage() {
    return horseman.exists('td.nextLink a');
}

function scrape() {
    var result = getResult();
    results = results.concat(result);
    var page = horseman
        .evaluate(function() {
            return $('td.pageCount').text();
        });
    console.log(page);
    if (hasNextPage() && results.length < 5000){
        horseman
            .click('td.nextLink a')
            .waitForNextPage()
            .wait(5000);
        scrape();
    }
}

horseman
    .userAgent("Mozilla/5.0 (Windows NT 6.3; WOW64; rv:35.0) Gecko/20100101 Firefox/35.0")
    .viewport(1024, 768)
    .open('http://www.kunijiban.pwri.go.jp/jp/denshikokudo/DB_Search/boringsearch.php')
    .type('input[name="tl_lat_deg"]', '34')
    .type('input[name="tl_lat_min"]', '23')
    .type('input[name="tl_lat_sec"]', '52')
    .type('input[name="tl_lon_deg"]', '131')
    .type('input[name="tl_lon_min"]', '54')
    .type('input[name="tl_lon_sec"]', '03')

    .type('input[name="br_lat_deg"]', '32')
    .type('input[name="br_lat_min"]', '51')
    .type('input[name="br_lat_sec"]', '40')
    .type('input[name="br_lon_deg"]', '133')
    .type('input[name="br_lon_min"]', '52')
    .type('input[name="br_lon_sec"]', '03')
    .click('input[value="検索"]')
    .waitForSelector('td.nextLink a')
//    .screenshot('kunijiban.png');

scrape();

var GeoJSON = {
    "type": "FeatureCollection",
    "crs": {
        "type": "name",
        "properties": {
            "name": "urn:ogc:def:crs:EPSG::4612"
        }
    },
    "features": results
};

fs.writeFile('kunijiban.geojson', JSON.stringify(GeoJSON, null, "    "));

horseman.close();

次に行政区域の境界のバッファを作成して、空間演算により取得した検索結果のうち、愛媛県のみを抽出する。

行政区域の地形データはQGISで作成してもよいが、下記のGeoJSONを使用した。

dataofjapan/land


空間演算には下記のモジュールを使用した。

bjornharrtell/jsts
JSTS Example

下記のhtmlをローカルに置いてブラウザで読みこめば、同じディレクトリにあるjapan.geojson, kunijiban.geojsonを読み込んで地図上に表示する。ただしFirefoxとChromeのみ(Chromeはローカルファイルが読めるように、"--allow-file-access-from-files"オプションを付けて起動する必要がある)。空間演算が割と重くて時間がかかるので、ハングしたようになるかも。
<!DOCTYPE html>
<html>

<head>
    <title>ボーリング柱状図</title>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.css" />

    <script src="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.js"></script>
    <script src="http://code.jquery.com/jquery-2.1.0.min.js"></script>
    <script src="https://rawgithub.com/bjornharrtell/jsts/master/lib/javascript.util.js"></script>
    <script src="https://rawgithub.com/bjornharrtell/jsts/master/lib/jsts.js"></script>

    <link rel="kunijiban" type="application/json" href="kunijiban.geojson">
    <link rel="japan" type="application/json" href="japan.geojson">

    <style>
        html,
        body,
        #map {
            height: 100%;
            width: 100%;
            padding: 0px;
            margin: 0px;
        }
    </style>
</head>

<body>
    <div id="map"></div>

    <script>
        var map = L.map('map').setView([33.523342, 132.863776], 10);
        L.control.scale({'position':'bottomleft','metric':true,'imperial':false}).addTo(map);

        var distance = 5 / 111.12; // 5km
        var geoReader = new jsts.io.GeoJSONReader(),
            geoWriter = new jsts.io.GeoJSONWriter();

        var cyber = L.tileLayer('http://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png', {
            maxZoom: 18,
            attribution: '<a href="http://www.gsi.go.jp/kikakuchousei/kikakuchousei40182.html" target="_blank">国土地理院</a>'
        }).addTo(map);
        map.addLayer(cyber);

        var gbank = L.tileLayer.wms("https://gbank.gsj.jp/ows/seamlessgeology200k_b", {
            layers: ['area,line,label'],
            format: 'image/png',
            transparent: true,
            opacity: 0.5,
            attribution: '<a href="https://gbank.gsj.jp/seamless/" target="_blank">シームレス地質図</a>'
        }).addTo(map);
        map.addLayer(gbank);

        L.control.layers({'地理院地図': cyber}, {'シームレス地質図': gbank}).addTo(map);

        function onEachFeature(feature, layer) {
            var popupContent = '<p><a href="' + feature.properties.fig + '" target="_blank">';
            popupContent += feature.properties.id + '</a><br />';
            popupContent += feature.properties.investigation + '</p>';

            layer.bindPopup(popupContent);
        }

        function createRegionBuffer() {
            return new Promise(function (resolve, reject) {
                $.getJSON($('link[rel="japan"]').attr("href"), function(data) {
                    var district;
                    data.features.forEach(function(item, index) {
                        if (item.properties.nam_ja == '愛媛県') district = item;
                    });
                    resolve(geoReader.read(district.geometry).buffer(distance));
                });
            });
        }

        function readBoringData(region) {
            return new Promise(function (resolve, reject) {
                $.getJSON($('link[rel="kunijiban"]').attr("href"), function(data) {
                    var inRegion = data.features.filter(function(item, index) {
                        return geoReader.read(item.geometry).within(region);
                    });
                    resolve(inRegion);
                });
            });
        }

        function renderRegionBuffer(region) {
            return new Promise(function (resolve, reject) {
                var buffer = geoWriter.write(region);
                L.geoJson(buffer, {
                    style: {
                        weight: 2,
                        color: "#999",
                        opacity: 1,
                        fillColor: "#B0DE5C",
                        fillOpacity: 0.8
                    }
                }).addTo(map);
                resolve(region);
            });
        }

        function renderBoringData(points) {
            L.geoJson(points, {
                onEachFeature: onEachFeature,

                pointToLayer: function(feature, latlng) {
                    return L.circleMarker(latlng, {
                        radius: 8,
                        fillColor: "#ff7800",
                        color: "#000",
                        weight: 1,
                        opacity: 1,
                        fillOpacity: 0.8
                    });
                }
            }).addTo(map);
        }

        createRegionBuffer()
//        .then(renderRegionBuffer)
        .then(readBoringData)
        .then(renderBoringData);
    </script>
</body>

</html>

マップは国土地理院のタイルと、日本シームレス地質図を使用している。

Read more ...

JavaScriptのライブラリをホスティングしてくれているネ申サイト

2013/07/30

Google Hosted Libraries
https://developers.google.com/speed/libraries/devguide

Microsoft Ajax Content Delivery Network
http://www.asp.net/ajaxlibrary/cdn.ashx

cdnjs - the missing cdn
http://cdnjs.com/

Cached Commons
http://cachedcommons.org/

Read more ...

CompoundJS

2013/05/12
CompoundJS
Crash Course to CompoundJS
もともとはRailwayJSという名前だったみたいだ。

npm を使ってCompoundJSをダウンロード&インストール
# npm install compound -g
アプリ作成
# compound init compound-blog create  compound-blog create  compound-blog/app/ create  compound-blog/app/assets/ create  compound-blog/app/assets/coffeescripts/ create  compound-blog/app/assets/stylesheets/ create  compound-blog/app/models/ create  compound-blog/app/controllers/ create  compound-blog/app/observers/ create  compound-blog/app/helpers/ create  compound-blog/app/views/ create  compound-blog/app/views/layouts/ create  compound-blog/db/ create  compound-blog/db/seeds/ create  compound-blog/db/seeds/development/ : :
依存モジュールのインストール
# cd compound-blog; npm install npm http GET https://registry.npmjs.org/ejs-ext npm http GET https://registry.npmjs.org/co-assets-compiler npm http GET https://registry.npmjs.org/express npm http GET https://registry.npmjs.org/nodeunit npm http GET https://registry.npmjs.org/sinon npm http GET https://registry.npmjs.org/supertest npm http GET https://registry.npmjs.org/coffee-script npm http GET https://registry.npmjs.org/mocha npm http GET https://registry.npmjs.org/semicov npm http GET https://registry.npmjs.org/stylus : :
Generatorを使ってappの機能を作成
# compound generate crud post title content exists  app/ exists  app/controllers/ exists  app/helpers/ exists  app/views/ create  app/views/posts/ exists  app/views/layouts create  test/ create  test/controllers/ create  app/controllers/posts_controller.js exists  app/ exists  app/models/ create  app/models/post.js patch   db/schema.js create  app/views/layouts/posts_layout.ejs create  app/views/posts/_form.ejs create  app/views/posts/show.ejs create  app/views/posts/new.ejs create  app/views/posts/edit.ejs create  app/views/posts/index.ejs create  app/helpers/posts.js create  test/controllers/posts_controller.test.js create  test/init.js patch   config/routes.js
起動!
# compound server 3000 Compound server listening on 0.0.0.0:3000 within development environment
compound01

なんか出た。ルーティングをしろとあるので、してみる。
config/routes.js
exports.routes = function (map) {     map.resources('posts');     map.get('/', 'posts#index');     // Generic routes. Add all your routes below this line     // feel free to remove generic routes     map.all(':controller/:action');     map.all(':controller/:action/:id'); };
しかし何も変わらない。なぜに。仕方ないので直接 posts#index にアクセス。
compound02
compound03

さすがにここから先は無理っぽい。
compound04
Read more ...

Fedora 18 で Calipso

2013/03/16
いつも使ってるのは drupal なんですが、Calipso を使ってみたくなった。

node.js のインストール


まずは node.js から。
yum でインストール場合
# yum install nodejs npm GitHub から落としてきてビルドする場合
# git clone https://github.com/joyent/node.git # yum install gcc-c++ openssl-devel # cd node # ls AUTHORS CONTRIBUTING.md LICENSE README.md common.gypi deps lib src tools BSDmakefile ChangeLog Makefile benchmark configure doc node.gyp test vcbuild.bat # export PYTHON=/usr/bin/python # $PYTHON ./configure # make # make install

MongoDB のインストール


mongoDBのも、yum でさくっと入る。
# yum install mongodb-server
依存関係で追加されるパッケージは以下
 boost-chrono
 boost-filesystem
 boost-program-options
 boost-system
 boost-thread
 gperftools-libs
 libmongodb
 libunwind
 mongodb
 snappy
 v8

サービスを起動する
# systemctl enable mongod.service ln -s '/usr/lib/systemd/system/mongod.service' '/etc/systemd/system/multi-user.target.wants/mongod.service' # systemctl start mongod.service # ps -ef | grep mongod mongodb 14801 1 2 17:09 ? 00:00:02 /usr/bin/mongod --quiet -f /etc/mongodb.conf run root 14842 14380 0 17:11 pts/2 00:00:00 grep --color=auto mongod
動作確認
# mongo MongoDB shell version: 2.2.3 connecting to: test Welcome to the MongoDB shell. For interactive help, type "help". For more comprehensive documentation, see http://docs.mongodb.org/ Questions? Try the support group http://groups.google.com/group/mongodb-user > use test switched to db test > db.foo.find() > db.foo.save({a: 1}) > db.foo.find() { "_id" : ObjectId("5144296efe6aab3b2eae5a85"), "a" : 1 } > db.foo.update( {a: 1}, {a: 5}) > db.foo.find() { "_id" : ObjectId("5144296efe6aab3b2eae5a85"), "a" : 5 } > exit bye

calipso のインストール


npm を使ってダウンロード&インストール
# npm install calipso -g
サイト作成
# mkdir /var/www/html/calipso # calipso site /var/www/html/calipso
サーバ起動
# cd /var/www/html/calipso # calipso server server.port=3000 Launching calipso from: /var/www/html/calipso Calipso directory: /usr/local/lib/node_modules/calipso/lib/../ _ _ ___ __ _| (_)_ __ ___ ___ / __|/ _` | | | '_ \/ __|/ _ \ | (__| (_| | | | |_) \__ \ (_) | \___|\__,_|_|_| .__/|___/\___/ |_| Calipso version: 0.3.17 Calipso configured for: development environment. Calipso server listening on port: 3000 16 Mar 19:48:25 - info: Saving configuration ... 16 Mar 19:48:25 - info: Reloading updated configuration ... 16 Mar 19:48:25 - info: Installing module content 16 Mar 19:48:25 - info: Installing module contentTypes 16 Mar 19:48:25 - info: Installing module permissions 16 Mar 19:48:25 - info: Installing module user 16 Mar 19:48:25 - debug: NOTE: bcrypt is not available. 16 Mar 19:48:25 - info: Content module installed ... 16 Mar 19:48:25 - info: Content types module installed ... 16 Mar 19:48:25 - info: User module installed ... 16 Mar 19:48:25 - info: User Roles sub-module installed ... 16 Mar 19:51:00 - info: No Calipso module routes matched the current URL /css/login-icons.css 16 Mar 20:04:53 - info: No Calipso module routes matched the current URL /css/login-icons.css 16 Mar 20:08:11 - debug: ValidationError: Validator "required" failed for path alias 16 Mar 20:11:05 - debug: ValidationError: Validator "required" failed for path alias
初期設定




できた!


まだいろいろと不安定っぽい。
Read more ...