package com.tcf_corp.postgis.service; import it.rambow.master.javautils.PolylineEncoder; import it.rambow.master.javautils.Track; import it.rambow.master.javautils.Trackpoint; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import java.util.Map; import javax.annotation.Resource; import javax.sql.DataSource; import org.postgis.Geometry; import org.postgis.MultiPolygon; import org.postgis.PGgeometry; import org.postgis.Point; import org.postgis.Polygon; import org.seasar.extension.dbcp.ConnectionWrapper; import com.tcf_corp.postgis.dto.GeoPointDto; import com.tcf_corp.postgis.dto.LocalAreaDto; public class LocalAreaService { @Resource private DataSource ds; public List<LocalAreaDto> getLocalArea(Trackpoint ne, Trackpoint sw) { Connection con = null; PreparedStatement st = null; ResultSet rs = null; List<LocalAreaDto> list = new ArrayList<LocalAreaDto>(); try { con = ds.getConnection(); // seasarはラッパーを返すのでそのままではaddDataTypeできない if(con instanceof ConnectionWrapper) { ConnectionWrapper cw = (ConnectionWrapper)con; Connection conn = cw.getPhysicalConnection(); ((org.postgresql.PGConnection)conn).addDataType("geometry",org.postgis.PGgeometry.class); } // シングルクォート内は、パラメータがセットできない String sql = "SELECT gid, geom FROM localarea WHERE ST_Intersects(geom,ST_GeomFromText(?, 4612)) = true;"; st = con.prepareStatement(sql); String arg = String.format("MULTIPOLYGON(((%s %s,%s %s,%s %s,%s %s,%s %s)))" , ne.getLonDouble(), ne.getLatDouble() , ne.getLonDouble(), sw.getLatDouble() , sw.getLonDouble(), sw.getLatDouble() , sw.getLonDouble(), ne.getLatDouble() , ne.getLonDouble(), ne.getLatDouble()); st.setString(1, arg); rs = st.executeQuery(); // エンコーダ PolylineEncoder pe = new PolylineEncoder(); while(rs.next()) { int gid = rs.getInt("gid"); PGgeometry geom = (PGgeometry)rs.getObject("geom"); if(geom.getGeoType() == Geometry.MULTIPOLYGON) { MultiPolygon mp = (MultiPolygon)geom.getGeometry(); // MultiPolygonの中にはPolygonが複数ある for(int i = 0 ; i < mp.numPolygons() ; i++) { LocalAreaDto dto = new LocalAreaDto(); Track trk = new Track(); Polygon pl = mp.getPolygon(i); dto.points = new ArrayList<GeoPointDto>(); // Polygonは境界をあらわすPointで構成される for(int j = 0 ; j < pl.numPoints() ; j++) { Point p = pl.getPoint(j); Trackpoint tp = new Trackpoint(p.y, p.x); trk.addTrackpoint(tp); GeoPointDto gpoint = new GeoPointDto(); gpoint.lat = p.y; gpoint.lng = p.x; dto.points.add(gpoint); } Map<String, String> enc = pe.dpEncode(trk); dto.gid = gid; dto.enc = enc.get("encodedPoints"); list.add(dto); } } } rs.close(); st.close(); } catch (SQLException e) { e.printStackTrace(); } finally { if(con != null) { try { con.close(); } catch (SQLException e) { e.printStackTrace(); } } } return list; } }
43行目のConnectionに対してaddDataTypeをしているところ。
PostGISのサンプルに書いてあるのはorg.postgresql.Connectionだがそのようなクラスはないので変更。同様に2番目の引数も文字からクラス型に変更。
その前のif文はseasar2を使っているのでコネクションのラッパーから実コネクションを使用するためのもの。
46行目のSQLは苦労するポイント。
心情的にはMULTIPOLYGONの引数もパラメータに設定したいがリテラル内なのでこれはできない。
MULTIPOLYGONから始まる文字列を渡さないといけない。
あとは、MULTIPOLYGON→複数のポリゴン→複数の点というところがわかれば問題はないと思う。
PolylineEncoderは点の集まりをエンコードして少ない文字列で表すためのものだが、うまくいっていない。
今後の課題。
このメソッドの呼び出し側で作ったリストをJSON形式にしている。
JSONはJSONICを使った。
で、今度はJavaScript側。
var map; var local = {}; $(function(){ var latlng = new google.maps.LatLng(35.6807428288539, 139.931797719145); var myOptions = { zoom: 15, center: latlng, mapTypeId: google.maps.MapTypeId.ROADMAP }; map = new google.maps.Map(document.getElementById("map_canvas"), myOptions); // マップが最初に起動したときのイベント var boundslistener = google.maps.event.addListener(map, 'bounds_changed', function(){ getBoundsArea(); google.maps.event.removeListener(boundslistener); }); google.maps.event.addListener(map, 'idle', function(){ getBoundsArea(); }); // google.maps.event.addListener(map, 'dragend', function(){ // getBoundsArea(); // }); }); function getBoundsArea() { var bounds = map.getBounds(); var ne = bounds.getNorthEast(); var sw = bounds.getSouthWest(); var zoom = map.getZoom(); var data = { neLat : ne.lat() ,neLng : ne.lng() ,swLat : sw.lat() ,swLng : sw.lng() ,zoom: zoom ,component : 'areaPage' ,action : 'ajaxLocalArea' } var opt = { type : 'post' ,url : 'http://localhost:8080/postgis/teeda.ajax' ,data : data ,success : function (data, dataType) { var arr = $.parseJSON(data); for(var i = 0 ; i < arr.length ; i++) { if(local[arr[i].gid]) { } else { var enc = arr[i].enc; var path = google.maps.geometry.encoding.decodePath(enc); var points = []; for(var j = 0 ; j < arr[i].points.length ; j++) { points.push(new google.maps.LatLng(arr[i].points[j].lat, arr[i].points[j].lng)); } var polygon = new google.maps.Polygon({ fillColor : '#0000FF' ,fillOpacity : 0.1 ,strokeColor : '#0000FF' ,strokeOpacity : 0.2 ,strokeWeight : 2 // ,path : path ,path : points ,map : map }); arr[i].polygon = polygon; local[arr[i].gid] = arr[i]; } } } }; $.ajax(opt); }
getBoundsで表示をしているマップの右上と左下の座標を取得して、サーバ側の呼び出し。
返ってきたJSONでポリゴンを生成している。
マップ移動のたびに呼び出して作ると同じ場所に何度もポリゴンを作ることになり
色が濃くなっていくので怪しい判定で回避している。
いろいろとチューニングの余地があるがとりあえず表示までたどり着いた。