Newer
Older
osmCoverage / src / osm / jp / api / Osmdb.java
@yuu yuu on 18 Nov 2018 26 KB fixed: read REMOVED text file.
package osm.jp.api;

import hayashi.yuu.tools.json.JsonTool;
import java.io.*;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLIntegrityConstraintViolationException;
import java.sql.SQLSyntaxErrorException;
import javax.json.Json;
import javax.json.JsonArray;
import javax.json.JsonBuilderFactory;
import javax.json.JsonObject;
import javax.json.JsonObjectBuilder;
import javax.json.JsonValue;
import javax.xml.parsers.ParserConfigurationException;
import jp.co.areaweb.tools.csv.CsvRecord;
import jp.co.areaweb.tools.database.DatabaseTool;
import org.hsqldb.HsqlException;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import osm.jp.coverage.busstop.DbBusstop;

public abstract class Osmdb {
    public String TABLE_NAME = "EXIST_osm";
    public String tableName = TABLE_NAME;
    public Connection hsqldb = null;   // hsqldb DatabaseTool.openDb("database");

    public Osmdb(Connection hsqldb, String tableName) {
        this.hsqldb = hsqldb;
        if (tableName != null) {
            this.tableName = tableName;
        }
    }
    
    /**
     * 'HSQLDB.table.OSM_EXIST'を新規に作る
     * 'HSQLDB.table.AREA_NODE'を新規に作る
     * 既にテーブルが存在する時にはERROR
     * @throws SQLException
     */
    public void create() throws SQLException {
        String createSt;
        
        sql("DROP TABLE IF EXISTS "+ tableName +" CASCADE");
        sql("DROP INDEX IF EXISTS "+ tableName +"_index;");
        sql("DROP TABLE IF EXISTS AREA_NODE CASCADE");

        // 'table.OSM_EXIST'を新規に作る
        createSt = "CREATE TABLE "+ tableName 
            + " ("
            +   "idref VARCHAR(12) NOT NULL, "
            +   "name VARCHAR(128), "
            +   "lat DOUBLE, "
            +   "lon DOUBLE, "
            +   "score INT, "
            +   "gmlid VARCHAR(12), "
            +   "area INT,  "
            +   "PRIMARY KEY(idref), "
            +   "removed BOOLEAN DEFAULT FALSE NOT NULL"
            + ");";
        Db.updateSQL(hsqldb, createSt);
        createSt = "CREATE INDEX "+ tableName +"_index ON "+ tableName 
            + " (lat, lon);";
        Db.updateSQL(hsqldb, createSt);

        // 'table.AREA_NODE'を新規に作る
        createSt = "CREATE TABLE AREA_NODE "
            + "("
            +   "idref VARCHAR(12) NOT NULL, "
            +   "pid VARCHAR(12), "
            +   "lat DOUBLE, "
            +   "lon DOUBLE"
            + ");";
        Db.updateSQL(hsqldb, createSt);
    }
    
    void sql(String sql) throws SQLException {
        System.out.println(sql);
        try (PreparedStatement ps = hsqldb.prepareStatement(sql)) {
            ps.executeUpdate();
        }
        catch (SQLSyntaxErrorException e) {
            System.out.println("107:"+ e.toString());
            if (!(e.toString().startsWith("java.sql.SQLSyntaxErrorException: user lacks privilege or object not found:"))) {
                throw e;
            }
        }
    }

    public static void outputWriter(PrintWriter pw, String text) {
        System.out.println("\t" + text);
        pw.print(text);
    }
    
    public static final int POINT_NO = 0;       // 評価ポイント無し→ score=50
    public static final int POINT_FIXME = 1;    // 評価ポイント|!(fixme=null) → score=1
    public static final int POINT_NO_BRAND = 2;    // 評価ポイント|brand=null → score=1
    public static final int POINT_NO_NAME = 4;     // 評価ポイント|name=null → score=1
    public static final int POINT_BUS_NO = 8;      // 評価ポイント|!(bus=yes) → score=0
    
    
    /**
     * 'table.OSM_EXIST'の内容をCSV形式にして標準出力に出力する
     * @throws java.sql.SQLException
     */
    public void export() throws SQLException {
        String header = "idref,lat,lon,score";
        System.out.println("TABLE: "+ tableName);
        System.out.println(header);
        PreparedStatement ps8 = hsqldb.prepareStatement(
            "SELECT idref,lat,lon,score,removed FROM "+ tableName
        );
        try (ResultSet rset8 = ps8.executeQuery()) {
            while (rset8.next()) {
                String idcode = rset8.getString(1);
                Double lat = rset8.getDouble(2);
                Double lon = rset8.getDouble(3);
                int score = rset8.getInt(4);
                boolean removed = rset8.getBoolean(5);
                String out = String.format(
                    "OSM: %s,%2.5f,%3.5f,%d,%b", 
                    idcode, lat, lon, score, removed
                );
                System.out.println(out);
            }
        }
    }
    

    public int importExistingNode(Connection osmdb, ResultSet rset, int point, boolean removed) throws IOException, SQLException {
        String osmidStr = rset.getString("osm_id");
        String latStr = rset.getString("lat");
        String lonStr = rset.getString("lon");
        String brandStr = rset.getString("brand");
        String nameStr = rset.getString("name");
        String fixmeStr = "";

        int score = 50;
        if (((point & POINT_NO_BRAND) != 0) && isNull(brandStr)) {
            score = 1;
        }
        if (((point & POINT_NO_NAME) != 0) && isNull(nameStr)) {
            score = 1;
        }
        
        boolean fixme = false;
        boolean busYes = false;
        if (((point & (POINT_FIXME | POINT_BUS_NO)) != 0)) {
            PreparedStatement ps8 = osmdb.prepareStatement(
                "SELECT * FROM planet_osm_nodes where id='"+ osmidStr +"'"
            );
            try (ResultSet rset8 = ps8.executeQuery()) {
                while (rset8.next()) {
                    String tags = rset8.getString("tags");
                    if (tags != null) {
                        tags = tags.substring(1);
                        tags = tags.substring(0, tags.length()-1);
                        CsvRecord csv = new CsvRecord();
                        csv.analizeRecord(tags);
                        
                        boolean key = true;
                        boolean bus = false;
                        for (String str : csv) {
                            if (key) {
                                if (str.startsWith("fixme")) {
                                    fixme = true;
                                }
                                if (str.equals("bus")) {
                                    bus = true;
                                }
                                key = false;
                            }
                            else {
                                if (bus) {
                                    if (str.equals("yes")) {
                                        busYes = true;
                                    }
                                    bus = false;
                                }
                                key = true;
                            }
                        }
                        
                        //System.out.println(tags);
                    }
                }
            }
            
            if (((point & POINT_FIXME) != 0) && fixme) {
                score = 1;
            }
            if (((point & POINT_BUS_NO) != 0) && !busYes) {
                score = 0;
            }
        }
        
        if (score > 0) {
            // idref と brandStr をデータベースに格納する
            System.out.println(
                "INSERT INTO "+ tableName 
                    + " (idref,lat,lon,score,name,removed) VALUES ("
                    + osmidStr +","
                    + latStr +","
                    + lonStr +","
                    + Integer.toString(score) +","
                    + "'"+ nameStr +"',"
                    + "false"
                    + ")N"
            );
            try (PreparedStatement ps5 = hsqldb.prepareStatement(
                "INSERT INTO "+ tableName +" (idref,lat,lon,score,name,removed) VALUES (?,?,?,?,?,?)"
            )) {
                ps5.setString(1, osmidStr);
                ps5.setDouble(2, Double.parseDouble(latStr));
                ps5.setDouble(3, Double.parseDouble(lonStr));
                ps5.setInt(4, score);
                ps5.setString(5, nameStr);
                ps5.setBoolean(6, removed);
                ps5.executeUpdate();
                return 1;
            }
            catch (HsqlException | SQLIntegrityConstraintViolationException e) {
                // integrity constraint violation: unique constraint or index violation; SYS_PK_10069 table: FUEL_EXIST
                // [HsqlException]は、無視する
                // integrity constraint violation: unique constraint or index violation; SYS_PK_10069 table: FUEL_EXIST
                // [SQLIntegrityConstraintViolationException]は、無視する
            }
        }

        return 0;
    }

    /**
     * File(HttpPOST.EXIST_FILE)を読み取って、データベースに反映させる。<br>
     * その際に、OSMノードを評価し、scoreを算定する
     * 
     * @param sqlWhere  POI条件
     * @param point     評価ポイント[POINT_NO|POINT_BRAND|POINT_NAME] 1: 'brand'  2:'name'
     * @param removed
     * @throws Exception              エラー
     */
    public void readExisting(String sqlWhere, int point, boolean removed) throws Exception {
        long counter = 0L;
        try (Connection osmdb =  DatabaseTool.openDb("osmdb")) {
            String sqlSelect = "osm_id,"
            +   "brand,"
            +   "disused,"
            +   "name,"
            +   "ST_Y(ST_Transform(way,4326)) as lat,"
            +   "ST_X(ST_Transform(way,4326)) as lon";
            String sqlNode = String.format(
                "SELECT %s FROM planet_osm_point %s", 
                sqlSelect, 
                sqlWhere
            );
            PreparedStatement ps1 = osmdb.prepareStatement(sqlNode);
            try (ResultSet rset1 = ps1.executeQuery()) {
                while (rset1.next()) {
                    counter += importExistingNode(osmdb, rset1, point, removed);
                }
            }
            
            String sqlArea = String.format(
                "SELECT %s FROM planet_osm_polygon %s", 
                "osm_id,brand,disused,name,ST_Y(ST_Transform(ST_Centroid(way),4326)) as lat,ST_X(ST_Transform(ST_Centroid(way),4326)) as lon", 
                sqlWhere
            );
            PreparedStatement ps2 = osmdb.prepareStatement(sqlArea);
            try (ResultSet rset2 = ps2.executeQuery()) {
                while (rset2.next()) {
                    counter += importExistingNode(osmdb, rset2, point, removed);
                }
            }

            System.out.println("Exists Node count = " + counter);
        }
    }

    /**
     * 
     * SELECT 
     *    planet_osm_nodes.id, 
     *    planet_osm_nodes.tags
     *    planet_osm_nodes.way --> lat & lon
     *  FROM 
     *    public.planet_osm_point, 
     *    public.planet_osm_nodes
     *  WHERE (
     *      planet_osm_point.osm_id = planet_osm_nodes.id
     *      and 'disused:amenity'=ANY(planet_osm_nodes.tags)
     *      and 'fuel'=ANY(planet_osm_nodes.tags)
     *    )
     * 
     * @param kStr
     * @param vStr
     * @param point
     * @throws Exception 
     */
    public void readExistingSub(String kStr, String vStr, int point) throws Exception {
        readExistingSub(kStr, vStr, point, true);
    }
    
    public void readExistingSub(String kStr, String vStr, int point, boolean removed) throws Exception {
        JsonBuilderFactory factory = Json.createBuilderFactory(null);
        JsonObjectBuilder builder = factory.createObjectBuilder();
        builder.add("k",kStr);
        builder.add("v", vStr);
        JsonObject tag = builder.build();
        readExistingSub(tag, point, removed);
    }
    
    /**
     * 
     * @param tag  {k: string, v: string}
     * @param point
     * @throws Exception 
     */
    public void readExistingSub(JsonObject tag,int point) throws Exception {
        readExistingSub(tag, point, true);
    }

    /**
     * 
     * @param tag  {k: string, v: string}
     * @param point
     * @param removed
     * @throws Exception 
     */
    public void readExistingSub(JsonObject tag,int point, boolean removed) throws Exception {
        long counter = 0L;
        try (Connection osmdb =  DatabaseTool.openDb("osmdb")) {
            String sqlSelect = "SELECT tags,id,ST_Y(ST_Transform(way,4326)) as lat,ST_X(ST_Transform(way,4326)) as lon";
            String sqlWhere = String.format(
                "WHERE (osm_id=id AND '%s'=ANY(tags) AND '%s'=ANY(tags))", 
                tag.getString("k"), tag.getString("v")
            );
            String sql = String.format(
                "%s FROM planet_osm_point,planet_osm_nodes %s", sqlSelect, sqlWhere
            );
            System.out.println(sql);
            
            PreparedStatement ps1 = osmdb.prepareStatement(sql);
            try (ResultSet rset1 = ps1.executeQuery()) {
                while (rset1.next()) {
                    String tags = rset1.getString("tags");
                    String idref = rset1.getString("id");
                    String latStr = rset1.getString("lat");
                    String lonStr = rset1.getString("lon");
                    double lat = Double.valueOf(latStr);
                    double lon = Double.valueOf(lonStr);
                    int score = score(point, tags);
                    counter += insertExistingNode(idref, lat, lon, score, "", removed);
                }
            }
            System.out.println("Exists Node count = " + counter);
        }
    }

    /**
     * 
     * @param array  tags: [{k: string, v: string}]
     * @param point
     * @throws Exception 
     */
    public void readExistingSub(JsonArray array,int point) throws Exception {
        readExistingSub(array,point, true);
    }

    /**
     * 
     * @param array  tags: [{k: string, v: string}]
     * @param point
     * @param removed
     * @throws Exception 
     */
    public void readExistingSub(JsonArray array,int point, boolean removed) throws Exception {
        long counter = 0L;
        try (Connection osmdb =  DatabaseTool.openDb("osmdb")) {
            String sqlSelect = "SELECT "
            +   "tags,"
            +   "id,"
            +   "ST_Y(ST_Transform(way,4326)) as lat,"
            +   "ST_X(ST_Transform(way,4326)) as lon";
            String sqlSS = "";
            for (JsonValue v : array) {
                JsonObject tag = (JsonObject)v;
                sqlSS += String.format(
                    " AND ('%s'=ANY(tags) AND '%s'=ANY(tags))", 
                    tag.getString("k"), tag.getString("v")
                );
            }
            String sqlWhere = String.format("WHERE (osm_id=id %s)", sqlSS);
            String sql = String.format(
                "%s FROM planet_osm_point,planet_osm_nodes %s", 
                sqlSelect, sqlWhere
            );
            System.out.println(sql);
            
            PreparedStatement ps1 = osmdb.prepareStatement(sql);
            try (ResultSet rset1 = ps1.executeQuery()) {
                while (rset1.next()) {
                    String tags = rset1.getString("tags");
                    String idref = rset1.getString("id");
                    String latStr = rset1.getString("lat");
                    String lonStr = rset1.getString("lon");
                    double lat = Double.valueOf(latStr);
                    double lon = Double.valueOf(lonStr);
                    int score = score(point, tags);
                    counter += insertExistingNode(idref, lat, lon, score, "", removed);
                }
            }
            System.out.println("Exists Node count = " + counter);
        }
    }

    int score(int point, String tags) {
        int score = 50;
        if (tags == null) {
            return score;
        }
        
        boolean brandYes = false;
        boolean busYes = false;
        boolean fixmeYes = false;
        boolean nameYes = false;
        
        tags = tags.substring(1);
        tags = tags.substring(0, tags.length()-1);
        CsvRecord csv = new CsvRecord();
        csv.analizeRecord(tags);

        boolean key = true;
        boolean bus = false;
        for (String str : csv) {
            if (key) {
                if (str.startsWith("fixme")) {
                    fixmeYes = true;
                }
                else if (str.equals("bus")) {
                    bus = true;
                }
                else if (str.equals("brand")) {
                    brandYes = true;
                }
                else if (str.startsWith("name")) {
                    nameYes = true;
                }
                key = false;
            }
            else {
                if (bus) {
                    if (str.equals("yes")) {
                        busYes = true;
                    }
                    bus = false;
                }
                key = true;
            }
        }
        
        if (((point & POINT_NO_BRAND) != 0) && !brandYes) {
            score = 1;
        }
        if (((point & POINT_NO_NAME) != 0) && !nameYes) {
            score = 1;
        }
        if (((point & POINT_FIXME) != 0) && fixmeYes) {
            score = 1;
        }
        if (((point & POINT_BUS_NO) != 0) && !busYes) {
            score = 0;
        }
        return score;
    }
    
    /**
     * 
     * @param idref
     * @param lat
     * @param lon
     * @param score
     * @param name
     * @return
     * @throws IOException
     * @throws SQLException 
     */
    public int insertExistingNode(String idref, double lat, double lon, int score, String name) throws IOException, SQLException {
        return insertExistingNode(idref, lat, lon, score, name, false);
    }

    /**
     * 
     * @param idref
     * @param lat
     * @param lon
     * @param score
     * @param name
     * @param removed
     * @return
     * @throws IOException
     * @throws SQLException 
     */
    public int insertExistingNode(String idref, double lat, double lon, int score, String name, boolean removed) throws IOException, SQLException {
        // idref と brandStr をデータベースに格納する
        /*
        */
        
        String sql = String.format("DELETE FROM %s WHERE idref=?", tableName);
        try (PreparedStatement ps5 = hsqldb.prepareStatement(sql)) {
            ps5.setString(1, idref);
            ps5.executeUpdate();
        }
        catch (HsqlException | SQLIntegrityConstraintViolationException e) {
            // integrity constraint violation: unique constraint or index violation; SYS_PK_10069 table: FUEL_EXIST
            // [HsqlException]は、無視する
            // integrity constraint violation: unique constraint or index violation; SYS_PK_10069 table: FUEL_EXIST
            // [SQLIntegrityConstraintViolationException]は、無視する
        }

        sql = String.format(
            "INSERT INTO %s (idref,lat,lon,score,name,removed) VALUES (%s,%2.7f,%3.7f,%d,%s,%s)", 
            tableName, 
            idref, lat, lon, score, name, (removed ? "true":"false")
        );
        System.out.println(sql);
        
        sql = String.format(
            "INSERT INTO %s (idref,lat,lon,score,name,removed) VALUES (?,?,?,?,?,?)", 
            tableName
        );
        try (PreparedStatement ps5 = hsqldb.prepareStatement(sql)) {
            ps5.setString(1, idref);
            ps5.setDouble(2, lat);
            ps5.setDouble(3, lon);
            ps5.setInt(4, score);
            ps5.setString(5, name);
            ps5.setBoolean(6, removed);
            ps5.executeUpdate();
            return 1;
        }
        catch (HsqlException | SQLIntegrityConstraintViolationException e) {
            // integrity constraint violation: unique constraint or index violation; SYS_PK_10069 table: FUEL_EXIST
            // [HsqlException]は、無視する
            // integrity constraint violation: unique constraint or index violation; SYS_PK_10069 table: FUEL_EXIST
            // [SQLIntegrityConstraintViolationException]は、無視する
        }
        return 0;
    }
    
    /**
     * REMOVEDファイルからデータを揉み込む
     * 
     * @param revovedFile
     * @throws FileNotFoundException
     * @throws IOException
     * @throws SQLException 
     */
    public void loadRemoved(File revovedFile) throws FileNotFoundException, IOException, SQLException {
        if (revovedFile == null) {
            throw new FileNotFoundException();
        }
        if (!revovedFile.exists()) {
            throw new FileNotFoundException();
        }
        if (!revovedFile.isFile()) {
            throw new FileNotFoundException();
        }
        
        try (PreparedStatement ps = hsqldb.prepareStatement(
            "UPDATE "
                + tableName 
                + " SET idref=?,removed=true,fixed1=100"
                + " WHERE gmlid=? AND area=?"
        )) {
            LineNumberReader reader = new LineNumberReader(
                new InputStreamReader(new FileInputStream(revovedFile))
            );
            String lineStr;
            while ((lineStr = reader.readLine()) != null) {
                if (lineStr == null) {
                    continue;
                }
                if (lineStr.trim().length() < 3) {
                    continue;
                }

                JsonObject json = JsonTool.parse(lineStr);
                String gmlid = json.getString("gmlid");
                String idref = json.getString("idref");
                int area = json.getInt("area");

                ps.setString(1, idref);
                ps.setString(2, gmlid);
                ps.setInt(3, area);
                ps.executeUpdate();
            }
        }
    }
    
    /**
     * removedデータをファイルに出力する
     * 
     * @param removedFile
     * @throws SQLException 
     * @throws java.io.IOException 
     */
    public void outputRemoved(File removedFile) throws SQLException, IOException {
        String whereStr = "WHERE (removed=?)";
        String fromStr = "FROM "+ DbBusstop.TABLE_NAME;
        String sortStr = "ORDER BY area,gmlid";
        String sql = String.format("SELECT * %s %s %s", fromStr, whereStr, sortStr);
        
        try (FileWriter fw = new FileWriter(removedFile);
            PreparedStatement ps1 = hsqldb.prepareStatement(sql))
        {
            System.out.println(sql);
            ps1.setBoolean(1, true);
            try (ResultSet rset1 = ps1.executeQuery()) {
                while (rset1.next()) {
                    String gmlid = rset1.getString("gmlid");
                    int area = rset1.getInt("area");
                    String idref = rset1.getString("idref");
                    
                    JsonObjectBuilder jsonBuilder = Json.createObjectBuilder();
                    jsonBuilder.add("gmlid", gmlid);
                    jsonBuilder.add("idref", (idref==null ? "" : idref));
                    jsonBuilder.add("area", area);
                    JsonObject value = jsonBuilder.build();
                    String line = value.toString();
                    
                    fw.write(line);
                    fw.write("\n");
                    fw.flush();
                }
            }
            fw.close();
        }
    }

    boolean isNull(String vstr) {
        if (vstr == null) {
            return true;
        }
        return (vstr.trim().length() < 1);
    }

    int readAreaNodes(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);
            switch (node2.getNodeName()) {
                case "node":
                    iCounter++;
                    importAreaNode(con, node2);
                    break;
                default:
                    iCounter += readAreaNodes(con, node2);
                    break;
            }
        }
        return iCounter;
    }

    void importAreaNode(Connection con, Node node) throws IOException, SQLException {
        String idrefStr = "";
        String latStr = "";
        String lonStr = "";

        NodeList nodes = node.getChildNodes();
        if (nodes.getLength() > 0) {
            return;
        }

        NamedNodeMap nodeMap = node.getAttributes();
        if (nodeMap != null) {
            for (int j=0; j < nodeMap.getLength(); j++) {
                switch (nodeMap.item(j).getNodeName()) {
                    case "id":
                        idrefStr = nodeMap.item(j).getNodeValue();
                        break;
                    case "lat":
                        latStr = nodeMap.item(j).getNodeValue();
                        break;
                    case "lon":
                        lonStr = nodeMap.item(j).getNodeValue();
                        break;
                    default:
                        break;
                }
            }
            
            // idref と brandStr をデータベースに格納する
            System.out.println("insert into AREA_NODE(idref,lat,lon) values("+ idrefStr +","+ latStr +","+ lonStr+")");
            try (PreparedStatement ps5 = con.prepareStatement(
                "INSERT INTO AREA_NODE (idref,lat,lon) VALUES (?,?,?)"
            )) {
                ps5.setString(1, idrefStr);
                ps5.setDouble(2, Double.parseDouble(latStr));
                ps5.setDouble(3, Double.parseDouble(lonStr));
                ps5.executeUpdate();
            }
        }
    }
    
    static Position getNdPosition(Connection con, String idref) throws SQLException {
        PreparedStatement ps8 = con.prepareStatement(
            "SELECT lat,lon FROM AREA_NODE where idref=?"
        );
        ps8.setString(1, idref);
        try (ResultSet rset8 = ps8.executeQuery()) {
            while (rset8.next()) {
                Double lat = rset8.getDouble(1);
                Double lon = rset8.getDouble(2);
                return new Position(lat,lon);
            }
        }
        return null;
    }
    
    public void dropTable () throws FileNotFoundException, ClassNotFoundException, SQLException, IOException, ParserConfigurationException, SAXException {
        String sqlStr = "DROP TABLE IF EXISTS "+ tableName;
        try (PreparedStatement ps = hsqldb.prepareStatement(sqlStr)) {
            System.out.println(sqlStr);
            ps.executeUpdate();
        }
    }
    
}