1.サーバーサイド - Kazunori-Kimura/express-gis GitHub Wiki

Server side

2つの機能を提供します。

  • ブラウザで表示するhtml,css,javascriptを返す
  • 行政区域、小学校、校区の情報をJSON形式で返す REST API
    データの参照のみ、更新機能は実装していません。

REST API

GET /areas : 全ての行政区域情報を取得する。

GET /areas/:code : code(JIS5)を元に行政区域情報を取得する。

GET /areas/:code/schools : codeの行政区域内にある全ての小学校を取得する。

GET /areas/:code/districts : codeの行政区域内にある全ての小学校区を取得する。

コード詳細

要点をかいつまんで、ソースコードの中身を解説します。

app.js

// app.js
var express = require('express'),
    bodyParser = require('body-parser'),
    app = express();

// load environment data
var env = require('./env.json');

// route
var area = require('./routes/area.js'),
    school = require('./routes/school.js'),
    district = require('./routes/district.js');

必要なモジュール、設定、REST APIから呼ばれるメソッドを読み込みます。

/(ルート)へのリクエストの対応

// view
app.set('view engine', 'ejs');
app.set('views', __dirname + '/views');

// serve static files
app.use(express.static(__dirname + '/bower_components'));
app.use(express.static(__dirname + '/public'));

app.get('/', function(req, res){
    res.render('index', { title: 'GeoTest', key: env.api_key });
});

/(ルート) へのリクエストがあった場合は views/index.ejsを返します。

クライアントサイドの機能はすべて views/index.ejs, public/js/index.js にまとまっています。

REST APIへのリクエストの対応

// area 行政区域
app.get('/areas', area.findAll);
app.get('/areas/:code', area.find);

// school 小学校
app.get('/areas/:code/schools', school.find);

// district 校区
app.get('/areas/:code/districts', district.find);

リクエストされたurlroute のメソッドを紐付けています。

app.listen(3000);

Port:3000で待ち受けます。

routes/area.js

// routes/area.js
var pg = require('pg'),
    here = require('here').here;

// load environment data
var env = require('../env.json');
var cs = env.connection_string;

モジュールと設定の読み込みです。

findAll

登録されている全ての行政区域情報を取得しています。

module.exports = {
    // findAll
    findAll: function(req, res){
        // 市区町村を取得
        var sql = here(/*
select
    n03_007 as code
    , n03_001 || coalesce(n03_003, '') || coalesce(n03_004, '') as city 
from
    "n03-13_27_130401" 
union 
select
    n03_007 as code
    , n03_001 || coalesce(n03_003, '') || coalesce(n03_004, '') as city 
from
    "n03-13_28_130401" 
order by
    code
*/).valueOf();

        // postgres接続
        pg.connect(cs, function(err, client){
            if(err){
                // connection error
            }else{
                // sql実行
                client.query(sql, function(err, result){
                    console.log('row count= %d', result.rows.length);

                    client.end();

                    res.send(result.rows);
                });
            }
        });
    },

川や島などの関係で、ひとつの行政区域が複数のレコードで構成されている場合があるため、 unionで結合して重複をなくしています。

find

まず、リクエストから取得したcodeを元に、行政区域情報を取得します。

つづいて、該当の行政区域の空間データを結合し、GeoJSON型に変換しています。 Google Maps APIはGeoJSONが容易に表示出来ます。

GeoJSONの詳細については http://geojson.org/ を参照してください。

    find: function(req, res){
        console.log('area#find( code= %s )', req.params.code);

        // 市区町村を取得
        var sqlCity = here(/*
select
    n03_007 as code
    , n03_001 || coalesce(n03_003, '') || coalesce(n03_004, '') as city 
from
    "n03-13_27_130401" 
where
    n03_007 = $1 
union
select
    n03_007 as code
    , n03_001 || coalesce(n03_003, '') || coalesce(n03_004, '') as city 
from
    "n03-13_28_130401" 
where
    n03_007 = $1 
limit
    1
*/).valueOf();

        // polygonを取得
        var sqlPolygon = here(/*
select
    n03_007 as code
    , ST_AsGeoJSON(ST_Centroid(ST_Union(geom))) as center
    , ST_AsGeoJSON(ST_Union(geom)) as geojson 
from
    "n03-13_27_130401" 
where
    n03_007 = $1 
group by
    n03_007 
union all 
select
    n03_007 as code
    , ST_AsGeoJSON(ST_Centroid(ST_Union(geom))) as center
    , ST_AsGeoJSON(ST_Union(geom)) as geojson 
from
    "n03-13_28_130401" 
where
    n03_007 = $1 
group by
    n03_007
*/).valueOf();

        var ret = {};

        // postgres接続
        pg.connect(cs, function(err, client){
            if(err){
                // connection error
            }

            // 市区町村取得
            client.query(sqlCity, [req.params.code], function(err, result){
                if(err){
                    // query error
                }

                if(result.rows.length == 0){
                    // データなし
                }

                // コード、市区町村名保持
                ret.code = result.rows[0].code;
                ret.city = result.rows[0].city;

                // polygon取得
                client.query(sqlPolygon, [req.params.code], function(err, result){
                    if(err){
                        // query error
                    }

                    if(result.rows.length == 0){
                        // データなし
                    }

                    // ポリゴンデータ
                    ret.center = result.rows[0].center;
                    ret.geojson = result.rows[0].geojson;

                    client.end();

                    // レスポンスを返す
                    res.send(ret);
                });
            });
        });
    }
};

ST_AsGeoJSON : geometryをGetJSONに変換します。

ST_Union : geometryを結合します。

ST_Centroid : geometryの重心を取得します。

routes/school.js

// routes/school.js
var pg = require('pg'),
    here = require('here').here;

// load environment data
var env = require('../env.json');
var cs = env.connection_string;

module.exports = {
    /**
     * 指定された市区町村にある小学校を取得
     */
    find: function(req, res){
        console.log('school#find( code= %s )', req.params.code);

        var sql = here(/*
select
    coalesce(a27_002, '') || ' ' || coalesce(a27_003, '') as school
    , a27_004 as address
    , ST_AsGeoJSON(geom) as point 
from
    "a27-10_27-g_publicelementaryschool" 
where
    a27_001 = $1 
UNION ALL 
select
    coalesce(a27_002, '') || ' ' || coalesce(a27_003, '') as school
    , a27_004 as address
    , ST_AsGeoJSON(geom) as point 
from
    "a27-10_28-g_publicelementaryschool" 
where
    a27_001 = $1 
order by
    school
*/).valueOf();

        // postgres接続
        pg.connect(cs, function(err, client){
            if(err){
                // connection error
            }else{
                // sql実行
                client.query(sql, [req.params.code], function(err, result){
                    console.log('row count= %d', result.rows.length);

                    // 接続終了
                    client.end();

                    res.send(result.rows);
                });
            }
        });
    }
};

find

リクエストから取得したcodeを元に、小学校情報を取得します。 小学校の空間データは Point型 のようです。

routes/district.js

// routes/district.js
var pg = require('pg'),
    here = require('here').here;

// load environment data
var env = require('../env.json');
var cs = env.connection_string;

module.exports = {
    find: function(req, res){
        console.log('district#find( code= %s )', req.params.code);

        var sql = here(/*
select
    coalesce(a27_006, '') || ' ' || coalesce(a27_007, '') as school
    , ST_AsGeoJSON(ST_Union(geom)) as district 
from
    "a27-10_27-g_schooldistrict" 
where
    a27_005 = $1 
group by
    a27_006
    , a27_007 
UNION ALL 
select
    coalesce(a27_006, '') || ' ' || coalesce(a27_007, '') as school
    , ST_AsGeoJSON(ST_Union(geom)) as district 
from
    "a27-10_28-g_schooldistrict" 
where
    a27_005 = $1 
group by
    a27_006
    , a27_007
*/).valueOf();

        // postgres接続
        pg.connect(cs, function(err, client){
            if(err){
                // connection error
            }else{
                // sql実行
                client.query(sql, [req.params.code], function(err, result){
                    console.log('row count= %d', result.rows.length);

                    client.end();

                    res.send(result.rows);
                });
            }
        });
    }
};

find

リクエストから取得したcodeを元に、校区情報を取得します。 行政区域情報と同様、複数のレコードでひとつの校区を表しているため、ST_Unionで空間データを結合します。