2.クライアントサイド - Kazunori-Kimura/express-gis GitHub Wiki
Google Mapに選択された行政区域のデータを表示します。
画面の制御には AngularJS
を使用しています。
<!doctype html>
<html lang="ja" ng-app="myModule">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title><%= title %></title>
<link rel="stylesheet" href="bootstrap/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="css/style.css">
<script src="angular/angular.min.js"></script>
<script src="http://maps.googleapis.com/maps/api/js?key=<%= key %>&sensor=false"></script>
<script src="js/index.js"></script>
</head>
bootstrap
, angular
, google maps api
とクライアントサイドのスクリプト本体であるjs/index.js
を読み込んでいます。
html
タグにはangular
と紐付けるためのng-app
の定義があります。
このアプリケーションは各scriptが読み込まれている前提になっているため、head
タグ内にscript
タグを書いています。
体感速度向上のために描画を優先したい場合は、body
タグの最後にscript
タグを書くべきです。
<body>
<div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="#"><%= title %></a>
</div>
</div>
</div>
画面上部に固定でタイトルを表示しています。
<div class="container-fluid">
<div class="row">
<!-- map -->
<div id="map_canvas" class="col-md-9"></div>
左カラムにGoogle Mapを表示する領域を確保します。
縦横のサイズはスタイルシートで定義しています。
<div id="controll" class="col-md-3" ng-controller="GisController">
<form class="form-inline" role="form">
<div class="form-group">
<label class="sr-only" for="searchAddress">Search Address</label>
<input type="text" class="form-control input-lg" id="searchAddress"
ng-model="searchText"
placeholder="Search">
</div>
</form>
<hr>
<div class="list-group">
<a herf="#" class="list-group-item"
ng-repeat="area in areas | filter:searchText"
ng-class="{ active: isSelected(area.code) }"
ng-click="selectArea(area.code)">
{{area.city}}
</a>
</div>
</div>
</div>
</div><!-- /.container -->
</body>
</html>
ng-controller="GisController"
で、angular
のコントローラと紐付けています。
form
部分はリストのフィルタリング機能です。
ng-model="searchText"
に、入力された文字列がリアルタイムに反映されます。
ng-repeat="area in areas"
により、取得した行政区域データの件数だけa
タグが生成されます。
| filter:searchText
は上記のsearchText
により表示する行政区域を絞り込んでいます。
ng-click="selectArea(area.code)"
により、a
タグのクリック時にselectArea()
メソッドを実行するように定義しています。
ng-class="{ active: isSelected(area.code) }"
は、選択された要素を反転表示するために、
class="active"
の表示を制御しています。
// Google Map x PostGis x Express x AngularJs
// index.js
(function(){
// AngularJS module
var myModule = angular.module('myModule', []);
// GoogleMap Object
var map;
他のライブラリのオブジェクト等を汚さないように、(function(){ ... })();
でくくっています。
唯一プログラム全体で使い回す、map
変数を定義しています。
// Controller
myModule.controller('GisController', function($scope, $http) {
// 選択中のJIS5
$scope.selectedCode = '';
// 選択中の項目かどうかの判定
$scope.isSelected = function(code){
return $scope.selectedCode == code;
}
$scope.selectedCode
は現在選択されている行政区域のコードを保持するプロパティです。
isSelected(code)
は ng-class
で使用している、選択中の行政区域かどうかを判定するメソッドです。
// リストクリック時の処理
$scope.selectArea = function(code){
// 選択アイテムのJIS5を保持
$scope.selectedCode = code;
// 市区町村情報取得
// GET: /areas/:code
var url = '/areas/' + code;
$http.get(url).success(function(data){
// map表示中の要素を削除
map.data.forEach(function(feature){
map.data.remove(feature);
});
Ajax
にてクリックされた行政区域のコードを元に、情報を取得します。
取得に成功した場合、現在mapに表示している要素を全て削除します。
// 選択された市区町村の中心点を取得
// ST_Centroid で返される Point の coordinates の座標が google.maps.LatLng の
// コンストラクタに渡す順番と逆転しているので注意。
var center = JSON.parse(data.center);
var centerLatlang = new google.maps.LatLng(
floor(center.coordinates[1], 4),
floor(center.coordinates[0], 4));
取得した行政区域情報から重心点を取得します。 後ほど、mapの中心をこの重心点に変更します。
// GoogleMapに表示するGeoJSON(FeatureCollection)の定義。
// http://geojson.org/
var geojson = {
type: 'FeatureCollection',
features: []
};
GoogleMapに表示するデータを格納するオブジェクト(GeoJSON)を用意しておきます。
// 市区町村のGeoJSON
// ST_AsGeoJSON は GoogleMapsAPI の addGeoJsonに必要な Feature の
// geometry 部分のみになっているので、不足部分を補う
var featureArea = {
type: 'Feature',
geometry: JSON.parse(data.geojson),
properties: {
type: 'area',
code: data.code,
name: data.city
}
};
// FeatureCollectionに市区町村を追加
geojson.features.push(featureArea);
取得した行政区域情報ではGeoJSONが文字列になっているため、JSON.parse
でJSONオブジェクトに変換しておきます。
properties
の設定は、後ほどsetStyle
にてポリゴンの色を設定する際に使用します。
properties
には任意のkey/valueが設定できます。
// 学校情報の取得
// GET: /areas/:code/schools
$http.get(url + '/schools').success(function(schools){
// 学校情報をFeatureCollectionにセット
angular.forEach(schools, function(school){
// 学校のGeoJSON
var featureSchool = {
type: 'Feature',
geometry: JSON.parse(school.point),
properties: {
type: 'school',
name: school.school,
address: school.address
}
};
// FeatureCollectionに学校を追加
geojson.features.push(featureSchool);
});
つづいて、小学校の情報を取得します。
// 校区情報の取得
// GET: /areas/:code/districts
$http.get(url + '/districts').success(function(districts){
// 校区情報をFeatureCollectionにセット
angular.forEach(districts, function(district){
// 校区のGeoJSON
var featureDistrict = {
type: 'Feature',
geometry: JSON.parse(district.district),
properties: {
type: 'district',
name: district.school
}
};
// FeatureCollectionに校区を追加
geojson.features.push(featureDistrict);
});
同様に、校区の情報を取得します。
// GeoJsonをセット
map.data.addGeoJson(geojson);
取得したデータを成形・登録したGeoJSONをaddGeoJson
で突っ込むと、Mapに表示されます。
各要素のproperties
の内容によって、表示する内容を切り替えます。
行政区域は青、学校は赤、校区は緑にしています。
また、重ねあわせた際の順序をzIndex
で定義しています。
// Style設定
// https://developers.google.com/maps/documentation/javascript/datalayer?hl=ja#style_geojson_data
map.data.setStyle(function(feature){
if(feature.getProperty('type') == 'area'){
// areaはPolygon
return {
clickable: false,
strokeWeight: 2,
strokeColor: 'blue',
zIndex: 4,
fillColor: '#45A1CF',
fillOpacity: 0.4,
visible: true
};
}else if(feature.getProperty('type') == 'school'){
// schoolはPoint
return {
icon: {
path: google.maps.SymbolPath.CIRCLE,
scale: 5,
strokeWeight: 0,
fillColor: 'red',
fillOpacity: 0.8
},
zIndex: 12,
visible: true
};
}else if(feature.getProperty('type') == 'district'){
// districtはPolygon
return {
clickable: false,
strokeWeight: 1,
strokeColor: 'green',
zIndex: 8,
fillColor: '#3eba2b',
fillOpacity: 0.4,
visible: true
};
}
});
最後に、地図の中心点を選択された行政区域の重心に設定しています。
// 中心点を移動
map.setCenter(centerLatlang);
});
});
});
};
順番が前後しますが、画面が表示された際の初期化処理および 全ての行政区域の取得の処理です。
// 市区町村リスト取得処理
$http.get('/areas').success(function(data){
$scope.areas = data;
});
// GoogleMapの初期化
initializeMap();
});
/**
* GoogleMapの初期化
*/
function initializeMap(){
// option
var options = {
// 神戸市
center: new google.maps.LatLng(34.6943, 135.1907),
zoom: 13,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
// map生成
map = new google.maps.Map(document.getElementById("map_canvas"), options);
初期表示時に神戸市が中央にくるように設定しています。
地図上で小学校をクリックした際に、名前と住所を吹き出しで表示する処理の定義です。
// 吹き出し保持用のプロパティを定義
map.infoWindow = false;
// クリック時のイベント設定
// https://developers.google.com/maps/documentation/javascript/datalayer?hl=ja#add_event_handlers
map.data.addListener('click', function(event){
if(event.feature){
// 小学校をクリックされた場合
if(event.feature.getProperty('type') == 'school'){
// 表示中の吹き出しがある場合は閉じる
if(map.infoWindow){
map.infoWindow.close();
map.infoWindow = false;
}
// 吹き出しを表示する
var point = event.feature.getGeometry().get();
var content = '<strong>' +
event.feature.getProperty('name') +
'</strong><br>' +
event.feature.getProperty('address');
map.infoWindow = new google.maps.InfoWindow({
content: content,
position: point,
zIndex: 20
});
map.infoWindow.open(map);
}
}
});
}
クリックイベント発生時に、クリックされた要素(event.feature) が小学校かどうかをproperties.type
を元に判定しています。
既に表示している吹き出しがある場合は、それをcloseします。
properties.name
, properties.address
を取得し、吹き出し(InfoWindow
)を表示します。
ちなみに、map.infoWindow
は吹き出しの状態を管理するために独自に定義したプロパティです。
PostGISから返される緯度・経度情報の精度が高すぎるように思われたため、 小数点以下の桁数を指定して、それ以下の桁を切り捨てるメソッドです。
ただ、わざわざ切り捨てなくても表示できるかもしれません。 (試してません)
/**
* 小数点桁数切り捨て
* @param Number num 切り捨て対象の数値
* @param Number digit 小数点以下桁数
*/
function floor(num, digit){
var a = num * Math.pow(10, digit);
a = Math.floor(a);
return a / Math.pow(10, digit);
}
})();