package osm.jp.coverage.busstop;
import osm.jp.api.RectArea;
import osm.jp.api.HttpPOST;
import osm.jp.api.KatakanaToHiragana;
import javax.xml.parsers.*;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.*;
import org.xml.sax.*;
import java.io.*;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import jp.co.areaweb.tools.csv.CsvFile;
import jp.co.areaweb.tools.csv.CsvRecord;
import jp.co.areaweb.tools.database.*;
public class NagoyaBusstop {
String filter = "";
String urlStr = "";
public static final boolean DB_INIT = false;
// 近くのバス停を探す範囲(バス停を中心としたNEERm四方の領域
static final int NEER = 25; // 20m
public static SimpleDateFormat timeStampFmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
/**
* メイン
*
* java -cp .:ConvBusstop.jar:hayashi_0225.jar:hsqldb_2.2.9.jar osm.jp.ConvBusstop [option]
* OPTION: -check OSMデータ上に既存のバス停が存在するかどうかをチェックする
* @param args
* @throws Exception */
public static void main(String[] args) throws Exception
{
File outFile = new File("update.sql");
if (outFile.isFile()) {
outFile.delete();
}
// HSQLディレクトリがなければ作る
File dbdir = new File("database");
if (!dbdir.isDirectory()) {
dbdir.mkdir();
}
Connection con = DatabaseTool.openDb("database");
NagoyaBusstop.initDb(con);
try {
NagoyaBusstop.clearDb(con);
new NagoyaBusstop(con, new File("TRF0009630..csv"));
// ローカルデータベース内の情報をPostGIS用の'ToPostgis.SQL_FILE_NAME'「./busstop.sql」に出力する
ToPostgis postgis = new ToPostgis();
//postgis.setNagoyaMode(true);
//postgis.outputDb(con);
//postgis.close();
}
finally {
DatabaseTool.closeDb(con);
}
Connection conPost = null;
try {
conPost = DatabaseTool.openDb("postgis");
DoSQL.sqlExecute(conPost, "DROP TABLE nagoya_busstop;");
DoSQL.sqlExecute(conPost, "CREATE TABLE nagoya_busstop (gid SERIAL PRIMARY KEY, name text, kana text, fixed integer, area integer, geom GEOMETRY(POINT, 4612));");
DoSQL.sqlExecute(conPost, "CREATE INDEX ix_nagoya_busstop_geom ON nagoya_busstop USING GiST (geom);");
DoSQL.sqlExecute(conPost, "commit;");
//Do_sqlfiles.sqlExecute(conPost, new File(ToPostgis.SQL_FILE_NAME));
outputDb2Osm(conPost, new File("Nagoya_busstop.osm"));
}
finally {
if (conPost != null) {
DatabaseTool.closeDb(conPost);
}
}
}
/**
*
* @param con
* @param csvFile
* @throws Exception
*/
public NagoyaBusstop(Connection con, File csvFile) throws Exception {
inputFile(con, csvFile);
/**
* 既存のOSMバス停を読み込む
* --> 'existing.xml'
*/
File existingFile = new File("existing.xml");
/**
* インポートしたデータの緯度経度範囲を読み取る
*/
double maxLon = -180.0D;
double minLon = 180.0D;
double maxLat = -90.0D;
double minLat = 90.0D;
PreparedStatement ps8;
ps8 = con.prepareStatement("SELECT lat,lon FROM "+ DbBusstop.TABLE_NAME);
try (ResultSet rset8 = ps8.executeQuery()) {
while (rset8.next()) {
Double lat = rset8.getDouble("lat");
Double lon = rset8.getDouble("lon");
if (lat > maxLat) {
maxLat = lat;
}
if (lon > maxLon) {
maxLon = lon;
}
if (lat < minLat) {
minLat = lat;
}
if (lon < minLon) {
minLon = lon;
}
}
}
/**
* OSM OverPassAPI を使って、既存のOSMバス停のデータを取得して、「existing.xml」に出力する
*/
HttpPOST.getCapabilities("highway", "bus_stop", minLon, maxLon, minLat, maxLat);
readExistingFile(con, existingFile);
HttpPOST.getCapabilities("disused:highway", "bus_stop", minLon, maxLon, minLat, maxLat);
readExistingFile(con, existingFile);
HttpPOST.getCapabilities("public_transport", "platform", minLon, maxLon, minLat, maxLat);
readExistingFile(con, existingFile);
PreparedStatement ps1;
ps1 = con.prepareStatement("SELECT idref,name,lat,lon,fixed FROM " + DbBusstop.TABLE_NAME);
PreparedStatement ps2 = con.prepareStatement("SELECT count(*) FROM existing_data where (lat > ?) and (lat < ?) and (lon > ?) and (lon < ?)");
PreparedStatement ps3 = con.prepareStatement("UPDATE "+ DbBusstop.TABLE_NAME +" SET fixed=? WHERE idref=?");
PreparedStatement ps4 = con.prepareStatement("SELECT count(*) FROM existing_data where (lat > ?) and (lat < ?) and (lon > ?) and (lon < ?)");
ResultSet rset1 = ps1.executeQuery();
while (rset1.next()) {
String idref = rset1.getString(1);
String name = rset1.getString("name");
Double lat = rset1.getDouble("lat");
Double lon = rset1.getDouble("lon");
// 指定の緯度経度を中心とする半径5x2m四方の矩形領域
System.out.print(idref + "("+ name + ") ....");
RectArea rect = new RectArea(lat, lon, NEER); // 25m 四方
ps2.setDouble(1, rect.minlat);
ps2.setDouble(2, rect.maxlat);
ps2.setDouble(3, rect.minlon);
ps2.setDouble(4, rect.maxlon);
ResultSet rset2 = ps2.executeQuery();
if (rset2.next()) {
int score = rset2.getInt(1);
if (score > 0) {
score = 100;
}
else {
rect = new RectArea(lat, lon, NEER*2); // 50m 四方
ps4.setDouble(1, rect.minlat);
ps4.setDouble(2, rect.maxlat);
ps4.setDouble(3, rect.minlon);
ps4.setDouble(4, rect.maxlon);
ResultSet rset4 = ps4.executeQuery();
if (rset4.next()) {
score = rset4.getInt(1);
if (score > 0) {
score = 50;
}
}
rset4.close();
}
System.out.println("."+ score);
ps3.setInt(1, score);
ps3.setString(2, idref);
ps3.executeUpdate();
}
rset2.close();
}
rset1.close();
ps4.close();
ps3.close();
ps2.close();
ps1.close();
}
static String[] shiftArgs(String[] args) {
String[] values = new String[args.length - 1];
for (int i=1; i < args.length; i++) {
values[i - 1] = new String(args[i]);
}
return values;
}
public static void readExistingFile (Connection con, File existingFile) throws FileNotFoundException, ClassNotFoundException, SQLException, IOException, ParserConfigurationException, SAXException {
int iCounter = 0;
DocumentBuilderFactory factory;
DocumentBuilder builder;
Node root;
iCounter = 0;
factory = DocumentBuilderFactory.newInstance();
builder = factory.newDocumentBuilder();
factory.setIgnoringElementContentWhitespace(true);
factory.setIgnoringComments(true);
factory.setValidating(true);
root = builder.parse(existingFile);
iCounter += readExistingNodes(con, root);
System.out.println("既存バス停数["+ iCounter +"]");
}
static int readExistingNodes(Connection con, Node node) throws IOException, SQLException {
int iCounter = 0;
NodeList nodes = node.getChildNodes();
for (int i = 0; i < nodes.getLength(); i++) {
Node node2 = nodes.item(i);
if (node2.getNodeName().equals("node")) {
iCounter++;
importExistingNode(con, node2);
}
else {
iCounter += readExistingNodes(con, node2);
}
}
return iCounter;
}
static void importExistingNode(Connection con, Node node) throws IOException, SQLException {
String idrefStr = "";
String latStr = "";
String lonStr = "";
String nameStr = "";
String fixmeStr = "";
int score = 0;
NamedNodeMap nodeMap = node.getAttributes();
if (null != nodeMap) {
for (int j=0; j < nodeMap.getLength(); j++) {
if (nodeMap.item(j).getNodeName().equals("id")) {
idrefStr = nodeMap.item(j).getNodeValue();
}
else if (nodeMap.item(j).getNodeName().equals("lat")) {
latStr = nodeMap.item(j).getNodeValue();
}
else if (nodeMap.item(j).getNodeName().equals("lon")) {
lonStr = nodeMap.item(j).getNodeValue();
}
}
NodeList nodes = node.getChildNodes();
for (int i = 0; i < nodes.getLength(); i++) {
Node node2 = nodes.item(i);
if (node2.getNodeName().equals("tag")) {
NamedNodeMap nodeMap2 = node2.getAttributes();
if (null != nodeMap2) {
String key = null;
String value = null;
for (int j=0; j < nodeMap2.getLength(); j++) {
if (nodeMap2.item(j).getNodeName().equals("k")) {
key = nodeMap2.item(j).getNodeValue();
}
else if (nodeMap2.item(j).getNodeName().equals("v")) {
value = nodeMap2.item(j).getNodeValue();
}
}
if ((key != null) && key.toLowerCase().equals("name") && (value != null)) {
nameStr = new String(value);
}
if ((key != null) && key.toLowerCase().equals("fixme") && (value != null)) {
fixmeStr = new String(value);
}
}
}
}
score = 50;
if (nameStr.equals("")) {
score = 1;
}
if (!fixmeStr.equals("")) {
score = 1;
}
// idref と nameStr をデータベースに格納する
PreparedStatement ps1 = con.prepareStatement("SELECT name,score FROM existing_data WHERE idref=?");
ps1.setString(1, idrefStr);
ResultSet rset1 = ps1.executeQuery();
if (rset1.next()) {
int fixed = rset1.getInt("score");
if (fixed < score) {
System.out.println("import existing_data : [id:"+ idrefStr +"] score="+ Integer.toString(score) +" "+ nameStr);
PreparedStatement ps5 = con.prepareStatement("UPDATE existing_data SET score=? WHERE idref=?");
ps5.setInt(1, score);
ps5.setString(2, idrefStr);
ps5.executeUpdate();
ps5.close();
}
}
else {
System.out.println("import existing_data : "+ idrefStr +" ("+ latStr +","+ lonStr+")["+ Integer.toString(score) +"]"+ nameStr);
PreparedStatement ps5 = con.prepareStatement("INSERT INTO existing_data (idref,lat,lon, name, score) VALUES (?,?,?,?,?)");
ps5.setString(1, idrefStr);
ps5.setDouble(2, Double.parseDouble(latStr));
ps5.setDouble(3, Double.parseDouble(lonStr));
ps5.setString(4, nameStr);
ps5.setInt(5, score);
ps5.executeUpdate();
ps5.close();
}
rset1.close();
ps1.close();
}
}
/**
* CSVファイルを読み取ってローカルベータベースへ記録する
* @param con
* @param iFile
* @throws Exception
*/
public static void inputFile (Connection con, File iFile) throws Exception {
PreparedStatement ps2 = con.prepareStatement("INSERT INTO "+ DbBusstop.TABLE_NAME +" (idref,name,kana,fixed,area,lat,lon) VALUES (?,?,?,?,?,?,?)");
CsvFile csv = new CsvFile(iFile);
csv.load();
int cnt = 0;
for (Iterator<CsvRecord> i = csv.iterator(); i.hasNext(); ) {
CsvRecord record = (CsvRecord)i.next();
if (cnt > 0) {
String name = (String)record.get(0);
String idStr = (String)record.get(1);
String kana = (String)record.get(5);
String latStr = (String)record.get(6);
String lonStr = (String)record.get(7);
System.out.println(record.toString());
if (!idStr.isEmpty()) {
ps2.setString(1, idStr);
ps2.setString(2, name);
ps2.setString(3, kana);
ps2.setInt(4, 0);
ps2.setInt(5, 0);
ps2.setDouble(6, Double.parseDouble(latStr));
ps2.setDouble(7, Double.parseDouble(lonStr));
ps2.executeUpdate();
}
}
cnt++;
}
ps2.close();
System.out.println("バス停数["+ cnt +"]");
}
public static void clearDb(Connection con) throws SQLException {
Statement stmt = con.createStatement();
long count = stmt.executeUpdate("delete from "+ DbBusstop.TABLE_NAME);
System.out.println("'Database.busstop'から "+ count +" 件のデータを削除しました。");
count = stmt.executeUpdate("delete from existing_data");
System.out.println("'Database.existing_data'から "+ count +" 件のデータを削除しました。");
}
public static void initDb(Connection con) throws SQLException, ClassNotFoundException, IOException, FileNotFoundException, ParserConfigurationException, SAXException {
// DB.tableを作成(初期化)
DbBusstop db = new DbBusstop(con);
db.dropTable();
db.create();
}
public static String[] anaComm(Node briNode) {
String[] rtn = new String[3];
rtn[0] = ""; // corp type
rtn[1] = ""; // course name
rtn[2] = ""; // corp name
NodeList nodes = briNode.getChildNodes();
for (int i=0; i < nodes.getLength(); i++) {
Node node2 = nodes.item(i);
if (node2.getNodeName().equals("ksj:BSC")) {
rtn[0] = node2.getTextContent();
}
else if (node2.getNodeName().equals("ksj:BLN")) {
rtn[1] = node2.getTextContent();
}
else if (node2.getNodeName().equals("ksj:BOC")) {
rtn[2] = node2.getTextContent();
}
}
return rtn;
}
/**
* ローカルデータベース内の情報をOSMファイルに出力する
* @param con
* @param osmFile
* @throws IOException
* @throws SQLException
* @throws TransformerException
* @throws ParserConfigurationException
* @throws SAXException
*/
public static void outputDb2Osm(Connection con, File osmFile) throws IOException, SQLException, SAXException, ParserConfigurationException, TransformerException {
String timeStampStr = timeStampFmt.format(new Date(Calendar.getInstance().getTimeInMillis()));
BufferedWriter ow = null;
PreparedStatement ps8 = con.prepareStatement("SELECT gid,name,kana,ST_Y(geom) AS lat,ST_X(geom) AS lon,fixed FROM nagoya_busstop WHERE fixed=0");
{
// OSM file header
ow = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(osmFile), "UTF-8"));
ow.write("<?xml version='1.0' encoding='UTF-8'?>");
ow.newLine();
ow.write("<osm version='0.6' generator='ConvBusstop'>");
ow.newLine();
double maxLat = -180.0;
double minLat = 180.0;
double maxLon = -180.0;
double minLon = 180.0;
{
ResultSet rset8 = ps8.executeQuery();
while (rset8.next()) {
long idref = rset8.getLong("gid");
String name = rset8.getString("name");
String kana = rset8.getString("kana");
Double lat = rset8.getDouble("lat");
Double lon = rset8.getDouble("lon");
if (lat > maxLat) {
maxLat = lat;
}
if (lon > maxLon) {
maxLon = lon;
}
if (lat < minLat) {
minLat = lat;
}
if (lon < minLon) {
minLon = lon;
}
// OSM node
String osm_node = nodeBusstop(con, idref, name, KatakanaToHiragana.hiraganaToKatakana(kana), lat, lon, timeStampStr);
ow.write(osm_node);
ow.newLine();
}
rset8.close();
}
// OSM file footer
ow.write("</osm>");
ow.newLine();
ow.close();
}
}
public static Element osmTemplateNode = null;
public static String nodeBusstop(Connection con, long gid, String name, String hirakana, Double lat, Double lon, String timeStampStr) throws SAXException, IOException, ParserConfigurationException, TransformerException, SQLException {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setIgnoringElementContentWhitespace(true);
factory.setIgnoringComments(true);
factory.setValidating(false);
if (osmTemplateNode == null) {
Document root = factory.newDocumentBuilder().parse(new File("osmTag.xml"));
osmTemplateNode = root.getDocumentElement();
}
Document document = factory.newDocumentBuilder().newDocument(); // Documentの生成
Element node = document.createElement("node");
document.appendChild(node);
node.setAttribute("id", String.valueOf(gid * -1));
node.setAttribute("timestamp", timeStampStr);
node.setAttribute("lat", String.valueOf(lat));
node.setAttribute("lon", String.valueOf(lon));
node.setAttribute("version", "1");
NodeList nodes = osmTemplateNode.getChildNodes();
for (int i = 0; i < nodes.getLength(); i++) {
Node node2 = nodes.item(i);
if (node2.getNodeName().equals("tag")) {
NamedNodeMap nodeMap2 = node2.getAttributes();
if (null != nodeMap2) {
String key = null;
String value = null;
for (int j=0; j < nodeMap2.getLength(); j++) {
if (nodeMap2.item(j).getNodeName().equals("k")) {
key = nodeMap2.item(j).getNodeValue();
}
else if (nodeMap2.item(j).getNodeName().equals("v")) {
value = nodeMap2.item(j).getNodeValue();
}
}
if ((key != null) && (value != null)) {
Element tag = document.createElement("tag");
tag.setAttribute("k", key);
tag.setAttribute("v", value);
node.appendChild(tag);
}
}
}
else if (node2.getNodeName().equals("ksj")) {
NamedNodeMap nodeMap2 = node2.getAttributes();
if (null != nodeMap2) {
for (int j=0; j < nodeMap2.getLength(); j++) {
String nodeName = nodeMap2.item(j).getNodeName();
String nodeValue = nodeMap2.item(j).getNodeValue();
if (nodeName.equals("busStopName")) {
Element tagName = document.createElement("tag");
tagName.setAttribute("k", nodeValue);
tagName.setAttribute("v", name);
node.appendChild(tagName);
}
else if (nodeName.equals("busStopNameKana")) {
Element tagName = document.createElement("tag");
tagName.setAttribute("k", nodeValue);
tagName.setAttribute("v", hirakana);
node.appendChild(tagName);
}
}
}
}
}
StringWriter sw = new StringWriter();
TransformerFactory tfactory = TransformerFactory.newInstance();
Transformer transformer = tfactory.newTransformer();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
transformer.transform(new DOMSource(node), new StreamResult(sw));
return sw.toString();
}
}