import java.io.*; import java.nio.*; import java.util.*; import java.nio.file.*; public class Relation { boolean Debug = false; long rid; public static class MyTag { Key key; Val val; // codeタグのときの値 String keyval; String sval; // 文字列タグのときの値 int ival; // intタグのときの値 float fval; // floatタグのときの値 public MyTag() { keyval = null; } } public static class Member { char type; long ref; String role; public Member(char type, long ref, String role) { this.type = type; this.ref = ref; this.role = role; } } public static class Node { long id; MyTag[] tags; int lon, lat; public Node(long id, MyTag[] tags, int lon, int lat) { this.id = id; this.tags = tags; this.lon = lon; this.lat = lat; } } public static class Way { long id; MyTag[] tags; long[] lonlats; // lon<<32 + lat List rids; long rid; // 現在処理中の Relation ID boolean reversed; public Way(long id, MyTag[] tags, long[] lonlats) { this.id = id; this.tags = tags; this.lonlats = lonlats; rids = new ArrayList<>(); reversed = false; } } static class Rel { long id; MyTag[] tags; HashSet setTags; HashSet setKeys; Member[] members; String name = ""; int admin_level = 9999; public Rel(long id, MyTag[] tags, Member[] members) { this.id = id; this.tags = tags; this.members = members; setKeys = new HashSet<>(); setTags = new HashSet<>(); for (MyTag tag : tags) { setKeys.add(tag.key); if (tag.keyval != null) setTags.add(tag.keyval); if (tag.key == Key.name) this.name = tag.sval; if (tag.key == Key.admin_level) this.admin_level = tag.ival; } } } String srcDir; String dstDir; HashMap mapNodes; HashMap mapWays; LinkedHashMap mapRels; public Relation(String name) { srcDir = "c:/map/obf/" + name + "/"; dstDir = "c:/map/dat/" + name + "/"; } MyTag[] readTags(DataInputStream dis) { List listMyTags = new ArrayList<>(); try { short num_tags = dis.readShort(); while (num_tags-- > 0) { MyTag tag = new MyTag(); short ikey = dis.readShort(); tag.key = Tag.keys[ikey]; if (ikey < Key.endCodeKey.ordinal() || ikey > Key.endFloatKey.ordinal()) { short ival = dis.readShort(); tag.val = Tag.vals[ival]; tag.keyval = tag.key.name() + "=" + tag.val.name(); } else if (ikey < Key.endStrKey.ordinal()) { short len = dis.readShort(); if (len < 0) System.out.printf("Error key=%s[%d]\n", Tag.keys[ikey].name(), ikey); byte[] buff = new byte[len]; dis.read(buff); tag.sval = new String(buff); } else if (ikey < Key.endIntKey.ordinal()) { tag.ival = dis.readInt(); } else if (ikey <= Key.endFloatKey.ordinal()) { tag.fval = dis.readFloat(); } else { System.out.printf("Error key=%s[%d]\n", Tag.keys[ikey].name(), ikey); continue; } listMyTags.add(tag); } } catch (EOFException e) { ; } catch (Exception e) { e.printStackTrace(); } return listMyTags.toArray(new MyTag[0]); } void scanNode() { String src = srcDir + "node0.obf"; try (DataInputStream dis = new DataInputStream( new BufferedInputStream(new FileInputStream(src)))) { while (true) { long nid = dis.readLong(); MyTag[] tags = readTags(dis); int lon = dis.readInt(); int lat = dis.readInt(); if (mapNodes.containsKey(nid)) { mapNodes.put(nid, new Node(nid, tags, lon, lat)); } } } catch (EOFException e) { ; } catch (Exception e) { e.printStackTrace(); } //System.out.println("mapNodes.size()=" + mapNodes.size()); } void scanWay() { for (int ix = 0; ; ix++) { String src = srcDir + "way" + ix + ".obf"; if (!Files.exists(Paths.get(src))) { break; } try (DataInputStream dis = new DataInputStream( new BufferedInputStream(new FileInputStream(src)))) { while (true) { long wid = dis.readLong(); MyTag[] tags = readTags(dis); short num_nodes = dis.readShort(); long[] lonlats = new long[num_nodes]; for (int i = 0; i < lonlats.length; i++) { long lon = dis.readInt(); long lat = dis.readInt(); lonlats[i] = (lon<<32) | lat; // lon, lat は非負 } if (mapWays.containsKey(wid)) { mapWays.put(wid, new Way(wid, tags, lonlats)); } } } catch (EOFException e) { ; } catch (Exception e) { e.printStackTrace(); } } //System.out.println("mapWays.size()=" + mapWays.size()); } void parseRelation() { String src = srcDir + "relation0.obf"; String dst = dstDir + "relation.dat"; short data; mapNodes = new HashMap<>(); mapWays = new HashMap<>(); mapRels = new LinkedHashMap<>(); try (DataInputStream dis = new DataInputStream( new BufferedInputStream(new FileInputStream(src))); DataOutputStream dos = new DataOutputStream( new BufferedOutputStream(new FileOutputStream(dst)))) { while (true) { rid = dis.readLong(); MyTag[] tags = readTags(dis); // Member読み込み short num_members = dis.readShort(); Member[] members = new Member[num_members]; for (int n = 0; n < num_members; n++) { char type = (char)dis.readShort(); long ref = dis.readLong(); short len = dis.readShort(); byte[] buff = new byte[len]; dis.read(buff); String role = new String(buff); if (type == 'n') { mapNodes.put(ref, null); } else if (type == 'w') { mapWays.put(ref, null); } members[n] = new Member(type, ref, role); } mapRels.put(rid, new Rel(rid, tags, members)); } } catch (EOFException e) { ; } catch (Exception e) { e.printStackTrace(); } System.out.println("mapNodes.size=" + mapNodes.size()); System.out.println("mapWays.size=" + mapWays.size()); } void procRelation() { int cntNotFound = 0; int cntOuterError = 0, cntInnerError = 0; for (Rel rel : mapRels.values()) { boolean isMultipolygon = rel.setTags.contains("type=multipolygon"); boolean isAdminBoundary = rel.setTags.contains("type=boundary") && rel.setTags.contains("boundary=administrative"); if (!isMultipolygon && !isAdminBoundary) continue; if (isAdminBoundary && rel.admin_level >= 8) continue; List listPendingOuter = new ArrayList<>(); List listPendingInner = new ArrayList<>(); int notfoundNodes = 0, notfoundWays = 0; for (Member m : rel.members) { if (m.type == 'n') { Node node = mapNodes.get(m.ref); if (node == null) { notfoundNodes++; continue; } } else if (m.type == 'w') { Way way = mapWays.get(m.ref); if (way == null) { notfoundWays++; continue; } if (m.role.equals("outer") || m.role.startsWith("o")) listPendingOuter.add(way); else if (m.role.equals("inner") || m.role.startsWith("i")) listPendingInner.add(way); else { listPendingOuter.add(way); // 暫定 } } } if (notfoundWays > 0) continue; // 領域外の Way がある int rest; while ((rest=listPendingOuter.size()) > 1) { List listOuter = concat(listPendingOuter, rel.id); // Outer if (listOuter == null) { System.out.printf("Error Outer %d#: 残=%d id=%d %s\n", ++cntOuterError, rest, rel.id, rel.name); break; } else { listOuter = new ArrayList<>(); } } while ((rest=listPendingInner.size()) > 1) { List listInner = concat(listPendingInner, rel.id); // Inner if (listInner == null) { System.out.printf("Error Inner %d#: 残=%d id=%d %s\n", ++cntInnerError, rest, rel.id, rel.name); break; } else { listInner = new ArrayList<>(); } } } } List concat(List listSrc, long id) { long lastLonlat = -1; List listDst = new ArrayList<>(); // listDstに最初の一つを入れる Way way = listSrc.get(0); way.reversed = false; listDst.add(way); listSrc.remove(0); lastLonlat = way.lonlats[way.lonlats.length-1]; if (way.lonlats[0] == lastLonlat) { return listDst; // 閉ループであった } long firstLonlat = way.lonlats[0]; while (listSrc.size() > 0) { // 後ろに繋がるものを探す boolean fSuccess = false; for (int n = 0; n < listSrc.size(); n++) { Way w = listSrc.get(n); if (w.lonlats[0] == lastLonlat || w.lonlats[w.lonlats.length-1] == lastLonlat) { fSuccess = true; if (w.lonlats[0] == lastLonlat) { w.reversed = false; lastLonlat = w.lonlats[w.lonlats.length-1]; } else { w.reversed = true; // 反転してつながった lastLonlat = w.lonlats[0]; } listDst.add(w); listSrc.remove(n); if (lastLonlat == firstLonlat) { return listDst; // polygon完成 } } } if (!fSuccess) break; } return null; } // 失敗した場合、listSrc は元に戻らない static void run(String name) { long start = System.currentTimeMillis(); Relation rel = new Relation(name); rel.parseRelation(); rel.scanNode(); rel.scanWay(); rel.procRelation(); System.out.printf("実行時間: %.2f分\n", (System.currentTimeMillis()-start)/60000.0); } }