package net.osmand.data.preparation; import java.awt.geom.Line2D; import java.awt.geom.Point2D; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.text.Collator; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.Map.Entry; import net.osmand.Algoritms; import net.osmand.IProgress; import net.osmand.binary.BinaryMapIndexWriter; import net.osmand.data.Amenity; import net.osmand.data.Boundary; import net.osmand.data.Building; import net.osmand.data.City; import net.osmand.data.DataTileManager; import net.osmand.data.Street; import net.osmand.data.TransportRoute; import net.osmand.data.TransportStop; import net.osmand.data.City.CityType; import net.osmand.data.index.DataIndexReader; import net.osmand.data.index.DataIndexWriter; import net.osmand.data.index.IndexConstants; import net.osmand.impl.ConsoleProgressImplementation; import net.osmand.osm.Entity; import net.osmand.osm.LatLon; import net.osmand.osm.MapRenderingTypes; import net.osmand.osm.MapUtils; import net.osmand.osm.Node; import net.osmand.osm.Relation; import net.osmand.osm.Way; import net.osmand.osm.Entity.EntityId; import net.osmand.osm.Entity.EntityType; import net.osmand.osm.OSMSettings.OSMTagKey; import net.osmand.osm.io.IOsmStorageFilter; import net.osmand.osm.io.OsmBaseStorage; import net.osmand.swing.DataExtractionSettings; import net.osmand.swing.Messages; import net.sf.junidecode.Junidecode; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.tools.bzip2.CBZip2InputStream; import org.xml.sax.SAXException; import rtree.Element; import rtree.IllegalValueException; import rtree.LeafElement; import rtree.NonLeafElement; import rtree.Pack; import rtree.RTree; import rtree.RTreeException; import rtree.Rect; /** * http://wiki.openstreetmap.org/wiki/OSM_tags_for_routing#Is_inside.2Foutside * http://wiki.openstreetmap.org/wiki/Relations/Proposed/Postal_Addresses * http://wiki.openstreetmap.org/wiki/Proposed_features/House_numbers/Karlsruhe_Schema#Tags (node, way) * * That data extraction has aim, save runtime memory and generate indexes on the fly. It will be longer than load in memory (needed part) * and save into index. */ @SuppressWarnings("unchecked") public class IndexCreator { private static final Log log = LogFactory.getLog(IndexCreator.class); // ONLY derby.jar needed for derby dialect private static final String DERBY_DIALECT = "DERBY"; private static final String H2_DIALECT = "H2"; private static final String SQLITE_DIALECT = "SQLITE"; private static final String CURRENT_DB = SQLITE_DIALECT; public static final int BATCH_SIZE = 5000; public static final int BATCH_SIZE_OSM = 10000; public static final String TEMP_NODES_DB = "nodes.tmp.odb"; public static final int STEP_CITY_NODES = 1; public static final int STEP_ADDRESS_RELATIONS_AND_MULTYPOLYGONS = 2; public static final int STEP_BORDER_CITY_WAYS = 3; public static final int STEP_MAIN = 4; private File workingDir = null; private boolean indexMap; private boolean indexPOI; private boolean indexTransport; private boolean indexAddress; private boolean normalizeStreets = true; // true by default private boolean saveAddressWays = true; // true by default private String regionName; private String poiFileName = null; private String mapFileName = null; private Long lastModifiedDate = null; private PreparedStatement pselectNode; private PreparedStatement pselectWay; private PreparedStatement pselectRelation; private PreparedStatement pselectTags; // constants to start process from the middle and save temporary results private boolean recreateOnlyBinaryFile = false; // false; private boolean deleteOsmDB = false; private boolean deleteDatabaseIndexes = true; private Connection dbConn; private File dbFile; Map<PreparedStatement, Integer> pStatements = new LinkedHashMap<PreparedStatement, Integer>(); private Connection poiConnection; private File poiIndexFile; private PreparedStatement poiPreparedStatement; private File mapFile; private RandomAccessFile mapRAFile; private Connection mapConnection; private PreparedStatement mapBinaryStat; private PreparedStatement mapLowLevelBinaryStat; private int lowLevelWays = -1; private PreparedStatement addressCityStat; private PreparedStatement addressStreetStat; private PreparedStatement addressBuildingStat; private PreparedStatement addressStreetNodeStat; private Set<Long> visitedStops = new HashSet<Long>(); private PreparedStatement transRouteStat; private PreparedStatement transRouteStopsStat; private PreparedStatement transStopsStat; private RTree transportStopsTree; private RTree[] mapTree = null; private MapZooms mapZooms = MapZooms.getDefault(); private MapRenderingTypes renderingTypes = MapRenderingTypes.getDefault(); private String cityAdminLevel = "8"; // MEMORY map : save it in memory while that is allowed private Map<Long, Set<Integer>>[] multiPolygonsWays; private Map<Long, String> multiPolygonsNames = new LinkedHashMap<Long, String>(); private Map<Long, List<Long>> highwayRestrictions = new LinkedHashMap<Long, List<Long>>(); // MEMORY address : choose what to use ? private boolean loadInMemory = true; private PreparedStatement addressSearchStreetStat; private PreparedStatement addressSearchBuildingStat; private PreparedStatement addressSearchStreetNodeStat; private Map<String, Long> addressStreetLocalMap = new LinkedHashMap<String, Long>(); private Set<Long> addressBuildingLocalSet = new LinkedHashSet<Long>(); private Set<Long> addressStreetNodeLocalSet = new LinkedHashSet<Long>(); // MEMORY address : address structure // load it in memory private Map<EntityId, City> cities = new LinkedHashMap<EntityId, City>(); private DataTileManager<City> cityVillageManager = new DataTileManager<City>(13); private DataTileManager<City> cityManager = new DataTileManager<City>(10); private List<Relation> postalCodeRelations = new ArrayList<Relation>(); private Map<City, Boundary> citiBoundaries = new LinkedHashMap<City, Boundary>(); private Set<Long> visitedBoundaryWays = new HashSet<Long>(); private String[] normalizeDefaultSuffixes; private String[] normalizeSuffixes; // local purpose List<Integer> typeUse = new ArrayList<Integer>(8); List<Long> restrictionsUse = new ArrayList<Long>(8); public static boolean usingSQLite() { return CURRENT_DB.equals(SQLITE_DIALECT); } public static boolean usingDerby() { return CURRENT_DB.equals(DERBY_DIALECT); } public static boolean usingH2() { return CURRENT_DB.equals(H2_DIALECT); } public IndexCreator(File workingDir) { this.workingDir = workingDir; } public void setIndexAddress(boolean indexAddress) { this.indexAddress = indexAddress; } public void setIndexMap(boolean indexMap) { this.indexMap = indexMap; } public void setIndexPOI(boolean indexPOI) { this.indexPOI = indexPOI; } public void setIndexTransport(boolean indexTransport) { this.indexTransport = indexTransport; } public void setSaveAddressWays(boolean saveAddressWays) { this.saveAddressWays = saveAddressWays; } public void setNormalizeStreets(boolean normalizeStreets) { this.normalizeStreets = normalizeStreets; } /* protected static int defineLevel(int minZoom) { int level = 0; if (minZoom < 15) { for (int i = 1; i < MAP_ZOOMS.length; i++) { if (minZoom <= MAP_ZOOMS[i]) { level = MAP_ZOOMS.length - 1 - i; break; } } } return level; } */ protected class NewDataExtractionOsmFilter implements IOsmStorageFilter { int currentCountNode = 0; private PreparedStatement prepNode; int allNodes = 0; int currentRelationsCount = 0; private PreparedStatement prepRelations; int allRelations = 0; int currentWaysCount = 0; private PreparedStatement prepWays; int allWays = 0; int currentTagsCount = 0; private PreparedStatement prepTags; public void initDatabase() throws SQLException { // prepare tables Statement stat = dbConn.createStatement(); if (usingDerby()) { try { stat.executeUpdate("drop table node"); //$NON-NLS-1$ } catch (SQLException e) { // ignore it } } else { stat.executeUpdate("drop table if exists node"); //$NON-NLS-1$ } stat.executeUpdate("create table node (id bigint primary key, latitude double, longitude double)"); //$NON-NLS-1$ stat.executeUpdate("create index IdIndex ON node (id)"); //$NON-NLS-1$ if (usingDerby()) { try { stat.executeUpdate("drop table ways"); //$NON-NLS-1$ } catch (SQLException e) { // ignore it } } else { stat.executeUpdate("drop table if exists ways"); //$NON-NLS-1$ } stat.executeUpdate("create table ways (id bigint, node bigint, ord smallint, primary key (id, ord))"); //$NON-NLS-1$ stat.executeUpdate("create index IdWIndex ON ways (id)"); //$NON-NLS-1$ if (usingDerby()) { try { stat.executeUpdate("drop table relations"); //$NON-NLS-1$ } catch (SQLException e) { // ignore it } } else { stat.executeUpdate("drop table if exists relations"); //$NON-NLS-1$ } stat .executeUpdate("create table relations (id bigint, member bigint, type smallint, role varchar(255), ord smallint, primary key (id, ord))"); //$NON-NLS-1$ stat.executeUpdate("create index IdRIndex ON relations (id)"); //$NON-NLS-1$ if (usingDerby()) { try { stat.executeUpdate("drop table tags"); //$NON-NLS-1$ } catch (SQLException e) { // ignore it } } else { stat.executeUpdate("drop table if exists tags"); //$NON-NLS-1$ } stat.executeUpdate("create table tags (id bigint, type smallint, skeys varchar(255), value varchar(255), primary key (id, type, skeys))"); //$NON-NLS-1$ stat.executeUpdate("create index IdTIndex ON tags (id, type)"); //$NON-NLS-1$ stat.close(); prepNode = dbConn.prepareStatement("insert into node values (?, ?, ?)"); //$NON-NLS-1$ prepWays = dbConn.prepareStatement("insert into ways values (?, ?, ?)"); //$NON-NLS-1$ prepRelations = dbConn.prepareStatement("insert into relations values (?, ?, ?, ?, ?)"); //$NON-NLS-1$ prepTags = dbConn.prepareStatement("insert into tags values (?, ?, ?, ?)"); //$NON-NLS-1$ dbConn.setAutoCommit(false); } public void finishLoading() throws SQLException { if (currentCountNode > 0) { prepNode.executeBatch(); } prepNode.close(); if (currentWaysCount > 0) { prepWays.executeBatch(); } prepWays.close(); if (currentRelationsCount > 0) { prepRelations.executeBatch(); } prepRelations.close(); if (currentTagsCount > 0) { prepTags.executeBatch(); } prepTags.close(); } @Override public boolean acceptEntityToLoad(OsmBaseStorage storage, EntityId entityId, Entity e) { // Register all city labelbs registerCityIfNeeded(e); // put all nodes into temporary db to get only required nodes after loading all data try { if (e instanceof Node) { currentCountNode++; if (!e.getTags().isEmpty()) { allNodes++; } prepNode.setLong(1, e.getId()); prepNode.setDouble(2, ((Node) e).getLatitude()); prepNode.setDouble(3, ((Node) e).getLongitude()); prepNode.addBatch(); if (currentCountNode >= BATCH_SIZE_OSM) { prepNode.executeBatch(); dbConn.commit(); // clear memory currentCountNode = 0; } } else if (e instanceof Way) { allWays++; short ord = 0; for (Long i : ((Way) e).getNodeIds()) { currentWaysCount++; prepWays.setLong(1, e.getId()); prepWays.setLong(2, i); prepWays.setLong(3, ord++); prepWays.addBatch(); } if (currentWaysCount >= BATCH_SIZE_OSM) { prepWays.executeBatch(); dbConn.commit(); // clear memory currentWaysCount = 0; } } else { allRelations++; short ord = 0; for (Entry<EntityId, String> i : ((Relation) e).getMembersMap().entrySet()) { currentRelationsCount++; prepRelations.setLong(1, e.getId()); prepRelations.setLong(2, i.getKey().getId()); prepRelations.setLong(3, i.getKey().getType().ordinal()); prepRelations.setString(4, i.getValue()); prepRelations.setLong(5, ord++); prepRelations.addBatch(); } if (currentRelationsCount >= BATCH_SIZE_OSM) { prepRelations.executeBatch(); dbConn.commit(); // clear memory currentRelationsCount = 0; } } for (Entry<String, String> i : e.getTags().entrySet()) { currentTagsCount++; prepTags.setLong(1, e.getId()); prepTags.setLong(2, EntityType.valueOf(e).ordinal()); prepTags.setString(3, i.getKey()); prepTags.setString(4, i.getValue()); prepTags.addBatch(); } if (currentTagsCount >= BATCH_SIZE_OSM) { prepTags.executeBatch(); dbConn.commit(); // clear memory currentTagsCount = 0; } } catch (SQLException ex) { log.error("Could not save in db", ex); //$NON-NLS-1$ } // do not add to storage return false; } public int getAllNodes() { return allNodes; } public int getAllRelations() { return allRelations; } public int getAllWays() { return allWays; } } public String getRegionName() { if (regionName == null) { return "Region"; //$NON-NLS-1$ } return regionName; } public void setRegionName(String regionName) { this.regionName = regionName; } private Connection getDatabaseConnection(String fileName) throws SQLException { return getDatabaseConnection(fileName, false); } public static void removeDatabase(File file) throws SQLException { if (usingH2()) { File[] list = file.getParentFile().listFiles(); for (File f : list) { if (f.getName().startsWith(file.getName())) { Algoritms.removeAllFiles(f); } } } else { Algoritms.removeAllFiles(file); } } public static boolean databaseFileExists(File dbFile) { if (usingH2()) { return new File(dbFile.getAbsolutePath() + ".h2.db").exists(); //$NON-NLS-1$ } else { return dbFile.exists(); } } private Connection getDatabaseConnection(String fileName, boolean forceSqLite) throws SQLException { if (usingSQLite() || forceSqLite) { try { Class.forName("org.sqlite.JDBC"); } catch (ClassNotFoundException e) { log.error("Illegal configuration", e); throw new IllegalStateException(e); } Connection connection = DriverManager.getConnection("jdbc:sqlite:" + fileName); Statement statement = connection.createStatement(); statement.executeUpdate("PRAGMA synchronous = 0"); statement.close(); return connection; } else if (usingDerby()) { try { Class.forName("org.apache.derby.jdbc.EmbeddedDriver"); } catch (ClassNotFoundException e) { log.error("Illegal configuration", e); throw new IllegalStateException(e); } Connection conn = DriverManager.getConnection("jdbc:derby:" + fileName + ";create=true"); conn.setAutoCommit(false); return conn; } else if (usingH2()) { try { Class.forName("org.h2.Driver"); } catch (ClassNotFoundException e) { log.error("Illegal configuration", e); throw new IllegalStateException(e); } return DriverManager.getConnection("jdbc:h2:file:" + fileName); } else { throw new UnsupportedOperationException(); } } public void loadEntityData(Entity e, boolean loadTags) throws SQLException { if (e instanceof Node || (e instanceof Way && !((Way) e).getNodes().isEmpty())) { // do not load tags for nodes inside way return; } Map<EntityId, Entity> map = new LinkedHashMap<EntityId, Entity>(); if (e instanceof Relation && ((Relation) e).getMemberIds().isEmpty()) { pselectRelation.setLong(1, e.getId()); if (pselectRelation.execute()) { ResultSet rs = pselectRelation.getResultSet(); boolean first = true; while (rs.next()) { int ord = rs.getInt(4); if (ord > 0 || first) { first = false; ((Relation) e).addMember(rs.getLong(1), EntityType.values()[rs.getInt(2)], rs.getString(3)); } } rs.close(); } } else if (e instanceof Way && ((Way) e).getEntityIds().isEmpty()) { pselectWay.setLong(1, e.getId()); if (pselectWay.execute()) { ResultSet rs = pselectWay.getResultSet(); boolean first = true; while (rs.next()) { int ord = rs.getInt(2); if (ord > 0 || first) { first = false; ((Way) e).addNode(new Node(rs.getDouble(5), rs.getDouble(6), rs.getLong(1))); } } rs.close(); } } Collection<EntityId> ids = e instanceof Relation ? ((Relation) e).getMemberIds() : ((Way) e).getEntityIds(); for (EntityId i : ids) { // pselectNode = dbConn.prepareStatement("select n.latitude, n.longitude, t.skeys, t.value from node n left join tags t on n.id = t.id and t.type = 0 where n.id = ?"); if (i.getType() == EntityType.NODE) { pselectNode.setLong(1, i.getId()); if (pselectNode.execute()) { ResultSet rs = pselectNode.getResultSet(); Node n = null; while (rs.next()) { if (n == null) { n = new Node(rs.getDouble(1), rs.getDouble(2), i.getId()); } if (rs.getObject(3) != null) { n.putTag(rs.getString(3), rs.getString(4)); } } map.put(i, n); rs.close(); } } else if (i.getType() == EntityType.WAY) { // pselectWay = dbConn.prepareStatement("select w.node, w.ord, t.skeys, t.value, n.latitude, n.longitude " + // "from ways w left join tags t on w.id = t.id and t.type = 1 and w.ord = 0 inner join node n on w.node = n.id " + // "where w.id = ? order by w.ord"); pselectWay.setLong(1, i.getId()); if (pselectWay.execute()) { ResultSet rs = pselectWay.getResultSet(); Way way = new Way(i.getId()); map.put(i, way); boolean first = true; while (rs.next()) { int ord = rs.getInt(2); if (ord > 0 || first) { first = false; way.addNode(new Node(rs.getDouble(5), rs.getDouble(6), rs.getLong(1))); } if (ord == 0 && rs.getObject(3) != null) { way.putTag(rs.getString(3), rs.getString(4)); } } rs.close(); } } else if (i.getType() == EntityType.RELATION) { pselectRelation.setLong(1, i.getId()); // pselectRelation = dbConn.prepareStatement("select r.member, r.type, r.role, r.ord, t.skeys, t.value" + // "from relations r left join tags t on r.id = t.id and t.type = 2 and r.ord = 0 " + // "where r.id = ? order by r.ord"); if (pselectRelation.execute()) { ResultSet rs = pselectRelation.getResultSet(); Relation rel = new Relation(i.getId()); map.put(i, rel); boolean first = true; while (rs.next()) { int ord = rs.getInt(4); if (ord > 0 || first) { first = false; rel.addMember(rs.getLong(1), EntityType.values()[rs.getInt(2)], rs.getString(3)); } if (ord == 0 && rs.getObject(5) != null) { rel.putTag(rs.getString(5), rs.getString(6)); } } // do not load relation members recursively ? It is not needed for transport, address, poi before rs.close(); } } } e.initializeLinks(map); } public void setPoiFileName(String poiFileName) { this.poiFileName = poiFileName; } public void setNodesDBFile(File file) { dbFile = file; } public void setMapFileName(String mapFileName) { this.mapFileName = mapFileName; } public String getMapFileName() { if (mapFileName == null) { return getRegionName() + IndexConstants.BINARY_MAP_INDEX_EXT; } return mapFileName; } public String getTempMapDBFileName() { return getMapFileName() + ".tmp"; //$NON-NLS-1$ } public Long getLastModifiedDate() { return lastModifiedDate; } public void setLastModifiedDate(Long lastModifiedDate) { this.lastModifiedDate = lastModifiedDate; } public String getPoiFileName() { if (poiFileName == null) { return IndexConstants.POI_INDEX_DIR + getRegionName() + IndexConstants.POI_INDEX_EXT; } return poiFileName; } public int iterateOverEntities(IProgress progress, EntityType type, int allCount, int step) throws SQLException { Statement statement = dbConn.createStatement(); String select; int count = 0; // stat.executeUpdate("create table tags (id "+longType+", type smallint, skeys varchar(255), value varchar(255))"); // stat.executeUpdate("create table ways (id "+longType+", node "+longType+", ord smallint)"); // stat.executeUpdate("create table relations (id "+longType+", member "+longType+", type smallint, role varchar(255), ord smallint)"); if (type == EntityType.NODE) { // filter out all nodes without tags select = "select n.id, n.latitude, n.longitude, t.skeys, t.value from node n inner join tags t on n.id = t.id and t.type = 0 order by n.id"; //$NON-NLS-1$ } else if (type == EntityType.WAY) { select = "select w.id, w.node, w.ord, t.skeys, t.value, n.latitude, n.longitude " + //$NON-NLS-1$ "from ways w left join tags t on w.id = t.id and t.type = 1 and w.ord = 0 inner join node n on w.node = n.id " + //$NON-NLS-1$ "order by w.id, w.ord"; //$NON-NLS-1$ } else { select = "select r.id, t.skeys, t.value from relations r inner join tags t on t.id = r.id and t.type = 2 and r.ord = 0"; //$NON-NLS-1$ } ResultSet rs = statement.executeQuery(select); Entity prevEntity = null; long prevId = -1; while (rs.next()) { long curId = rs.getLong(1); boolean newEntity = curId != prevId; Entity e = prevEntity; if (type == EntityType.NODE) { if (newEntity) { e = new Node(rs.getDouble(2), rs.getDouble(3), curId); } e.putTag(rs.getString(4), rs.getString(5)); } else if (type == EntityType.WAY) { if (newEntity) { e = new Way(curId); } int ord = rs.getInt(3); if (ord == 0 && rs.getObject(4) != null) { e.putTag(rs.getString(4), rs.getString(5)); } if (newEntity || ord > 0) { ((Way) e).addNode(new Node(rs.getDouble(6), rs.getDouble(7), rs.getLong(2))); } } else { if (newEntity) { e = new Relation(curId); } e.putTag(rs.getString(2), rs.getString(3)); } if (newEntity) { count++; if (progress != null) { progress.progress(1); } if (prevEntity != null) { iterateEntity(prevEntity, step); } prevEntity = e; } prevId = curId; } if (prevEntity != null) { count++; iterateEntity(prevEntity, step); } rs.close(); return count; } protected void loadEntityTags(EntityType type, Entity e) throws SQLException { pselectTags.setLong(1, e.getId()); pselectTags.setByte(2, (byte) type.ordinal()); ResultSet rsTags = pselectTags.executeQuery(); while (rsTags.next()) { e.putTag(rsTags.getString(1), rsTags.getString(2)); } rsTags.close(); } private static Set<String> acceptedRoutes = new HashSet<String>(); static { acceptedRoutes.add("bus"); //$NON-NLS-1$ acceptedRoutes.add("trolleybus"); //$NON-NLS-1$ acceptedRoutes.add("share_taxi"); //$NON-NLS-1$ acceptedRoutes.add("subway"); //$NON-NLS-1$ acceptedRoutes.add("train"); //$NON-NLS-1$ acceptedRoutes.add("tram"); //$NON-NLS-1$ acceptedRoutes.add("ferry"); //$NON-NLS-1$ } private TransportRoute indexTransportRoute(Relation rel) { String ref = rel.getTag(OSMTagKey.REF); String route = rel.getTag(OSMTagKey.ROUTE); String operator = rel.getTag(OSMTagKey.OPERATOR); if (route == null || ref == null) { return null; } if (!acceptedRoutes.contains(route)) { return null; } TransportRoute r = new TransportRoute(rel, ref); r.setOperator(operator); r.setType(route); if (operator != null) { route = operator + " : " + route; //$NON-NLS-1$ } final Map<TransportStop, Integer> forwardStops = new LinkedHashMap<TransportStop, Integer>(); final Map<TransportStop, Integer> backwardStops = new LinkedHashMap<TransportStop, Integer>(); int currentStop = 0; int forwardStop = 0; int backwardStop = 0; for (Entry<Entity, String> e : rel.getMemberEntities().entrySet()) { if (e.getValue().contains("stop")) { //$NON-NLS-1$ if (e.getKey() instanceof Node) { TransportStop stop = new TransportStop(e.getKey()); boolean forward = e.getValue().contains("forward"); //$NON-NLS-1$ boolean backward = e.getValue().contains("backward"); //$NON-NLS-1$ currentStop++; if (forward || !backward) { forwardStop++; } if (backward) { backwardStop++; } boolean common = !forward && !backward; int index = -1; int i = e.getValue().length() - 1; int accum = 1; while (i >= 0 && Character.isDigit(e.getValue().charAt(i))) { if (index < 0) { index = 0; } index = accum * Character.getNumericValue(e.getValue().charAt(i)) + index; accum *= 10; i--; } if (index < 0) { index = forward ? forwardStop : (backward ? backwardStop : currentStop); } if (forward || common) { forwardStops.put(stop, index); r.getForwardStops().add(stop); } if (backward || common) { if (common) { // put with negative index backwardStops.put(stop, -index); } else { backwardStops.put(stop, index); } r.getBackwardStops().add(stop); } } } else if (e.getKey() instanceof Way) { r.addWay((Way) e.getKey()); } } if (forwardStops.isEmpty() && backwardStops.isEmpty()) { return null; } Collections.sort(r.getForwardStops(), new Comparator<TransportStop>() { @Override public int compare(TransportStop o1, TransportStop o2) { return forwardStops.get(o1) - forwardStops.get(o2); } }); // all common stops are with negative index (reeval them) for (TransportStop s : new ArrayList<TransportStop>(backwardStops.keySet())) { if (backwardStops.get(s) < 0) { backwardStops.put(s, backwardStops.size() + backwardStops.get(s) - 1); } } Collections.sort(r.getBackwardStops(), new Comparator<TransportStop>() { @Override public int compare(TransportStop o1, TransportStop o2) { return backwardStops.get(o1) - backwardStops.get(o2); } }); return r; } public String getCityAdminLevel() { return cityAdminLevel; } public void setCityAdminLevel(String cityAdminLevel) { this.cityAdminLevel = cityAdminLevel; } public void indexBoundariesRelation(Entity e) throws SQLException { String adminLevel = e.getTag("admin_level"); Boundary boundary = null; if (cityAdminLevel.equals(adminLevel)) { if (e instanceof Relation) { Relation i = (Relation) e; loadEntityData(i, true); boundary = new Boundary(); if (i.getTag(OSMTagKey.NAME) != null) { boundary.setName(i.getTag(OSMTagKey.NAME)); } boundary.setBoundaryId(i.getId()); Map<Entity, String> entities = i.getMemberEntities(); for (Entity es : entities.keySet()) { if (es instanceof Way) { boolean inner = "inner".equals(entities.get(es)); //$NON-NLS-1$ if (inner) { boundary.getInnerWays().add((Way) es); } else { String wName = es.getTag(OSMTagKey.NAME); // if name are not equal keep the way for further check (it could be different suburb) if (Algoritms.objectEquals(wName, boundary.getName()) || wName == null) { visitedBoundaryWays.add(es.getId()); } boundary.getOuterWays().add((Way) es); } } } } else if (e instanceof Way) { if (!visitedBoundaryWays.contains(e.getId())) { boundary = new Boundary(); if (e.getTag(OSMTagKey.NAME) != null) { boundary.setName(e.getTag(OSMTagKey.NAME)); } boundary.setBoundaryId(e.getId()); boundary.getOuterWays().add((Way) e); } } } if (boundary != null && boundary.getCenterPoint() != null) { LatLon point = boundary.getCenterPoint(); boolean cityFound = false; boolean containsCityInside = false; for (City c : cityManager.getClosestObjects(point.getLatitude(), point.getLongitude(), 3)) { if (boundary.containsPoint(c.getLocation())) { if (boundary.getName() == null || boundary.getName().equalsIgnoreCase(c.getName())) { citiBoundaries.put(c, boundary); cityFound = true; containsCityInside = true; } } } // TODO mark all suburbs inside city as is_in tag (!) or use another solution if (!cityFound) { for (City c : cityVillageManager.getClosestObjects(point.getLatitude(), point.getLongitude(), 3)) { if (boundary.containsPoint(c.getLocation())) { if (boundary.getName() == null || boundary.getName().equalsIgnoreCase(c.getName())) { citiBoundaries.put(c, boundary); cityFound = true; } } } } if (!cityFound && boundary.getName() != null) { // / create new city for named boundary very rare case that's why do not proper generate id // however it could be a problem City nCity = new City(containsCityInside ? CityType.CITY : CityType.SUBURB); nCity.setLocation(point.getLatitude(), point.getLongitude()); nCity.setId(-boundary.getBoundaryId()); nCity.setName(boundary.getName()); citiBoundaries.put(nCity, boundary); cityVillageManager.registerObject(point.getLatitude(), point.getLongitude(), nCity); DataIndexWriter.writeCity(addressCityStat, pStatements, nCity, BATCH_SIZE); // commit to put all cities if (pStatements.get(addressCityStat) > 0) { addressCityStat.executeBatch(); pStatements.put(addressCityStat, 0); } } } } public void indexAddressRelation(Relation i) throws SQLException { String type = i.getTag(OSMTagKey.ADDRESS_TYPE); boolean house = "house".equals(type); //$NON-NLS-1$ boolean street = "a6".equals(type); //$NON-NLS-1$ if (house || street) { // try to find appropriate city/street City c = null; // load with member ways with their nodes and tags ! loadEntityData(i, true); Collection<Entity> members = i.getMembers("is_in"); //$NON-NLS-1$ Relation a3 = null; Relation a6 = null; if (!members.isEmpty()) { if (street) { a6 = i; } Entity in = members.iterator().next(); loadEntityData(in, true); if (in instanceof Relation) { // go one level up for house if (house) { a6 = (Relation) in; members = ((Relation) in).getMembers("is_in"); //$NON-NLS-1$ if (!members.isEmpty()) { in = members.iterator().next(); loadEntityData(in, true); if (in instanceof Relation) { a3 = (Relation) in; } } } else { a3 = (Relation) in; } } } if (a3 != null) { Collection<EntityId> memberIds = a3.getMemberIds("label"); //$NON-NLS-1$ if (!memberIds.isEmpty()) { c = cities.get(memberIds.iterator().next()); } } if (c != null && a6 != null) { String name = a6.getTag(OSMTagKey.NAME); if (name != null) { LatLon location = c.getLocation(); for (Entity e : i.getMembers(null)) { if (e instanceof Way) { LatLon l = ((Way) e).getLatLon(); if (l != null) { location = l; break; } } } Long streetId = getStreetInCity(c, name, location, (a6.getId() << 2) | 2); if (streetId == null) { return; } if (street) { for (Map.Entry<Entity, String> r : i.getMemberEntities().entrySet()) { if ("street".equals(r.getValue())) { //$NON-NLS-1$ if (r.getKey() instanceof Way && saveAddressWays) { DataIndexWriter.writeStreetWayNodes(addressStreetNodeStat, pStatements, streetId, (Way) r.getKey(), BATCH_SIZE); if (loadInMemory) { addressStreetNodeLocalSet.add(r.getKey().getId()); } } } else if ("house".equals(r.getValue())) { //$NON-NLS-1$ // will be registered further in other case if (!(r.getKey() instanceof Relation)) { String hno = r.getKey().getTag(OSMTagKey.ADDR_HOUSE_NUMBER); if (hno != null) { Building building = new Building(r.getKey()); building.setName(hno); DataIndexWriter.writeBuilding(addressBuildingStat, pStatements, streetId, building, BATCH_SIZE); if (loadInMemory) { addressBuildingLocalSet.add(r.getKey().getId()); } } } } } } else { String hno = i.getTag(OSMTagKey.ADDRESS_HOUSE); if (hno == null) { hno = i.getTag(OSMTagKey.ADDR_HOUSE_NUMBER); } if (hno == null) { hno = i.getTag(OSMTagKey.NAME); } members = i.getMembers("border"); //$NON-NLS-1$ if (!members.isEmpty()) { Entity border = members.iterator().next(); if (border != null) { EntityId id = EntityId.valueOf(border); // special check that address do not contain twice in a3 - border and separate a6 if (!a6.getMemberIds().contains(id)) { Building building = new Building(border); if (building.getLocation() != null) { building.setName(hno); DataIndexWriter.writeBuilding(addressBuildingStat, pStatements, streetId, building, BATCH_SIZE); if (loadInMemory) { addressBuildingLocalSet.add(id.getId()); } } else { log.error("Strange border " + id + " location couldn't be found"); } } } } else { log.info("For relation " + i.getId() + " border not found"); //$NON-NLS-1$ //$NON-NLS-2$ } } } } } } public City getClosestCity(LatLon point) { if (point == null) { return null; } for (City c : cityManager.getClosestObjects(point.getLatitude(), point.getLongitude(), 3)) { Boundary boundary = citiBoundaries.get(c); if(boundary != null){ if(boundary.containsPoint(point)){ return c; } } } for (City c : cityVillageManager.getClosestObjects(point.getLatitude(), point.getLongitude(), 3)) { Boundary boundary = citiBoundaries.get(c); if(boundary != null){ if(boundary.containsPoint(point)){ return c; } } } City closest = null; double relDist = Double.POSITIVE_INFINITY; for (City c : cityManager.getClosestObjects(point.getLatitude(), point.getLongitude(), 3)) { double rel = MapUtils.getDistance(c.getLocation(), point) / c.getType().getRadius(); if (rel < relDist) { closest = c; relDist = rel; if (relDist < 0.2d) { break; } } } if (relDist < 0.2d) { return closest; } for (City c : cityVillageManager.getClosestObjects(point.getLatitude(), point.getLongitude(), 3)) { double rel = MapUtils.getDistance(c.getLocation(), point) / c.getType().getRadius(); if (rel < relDist) { closest = c; relDist = rel; if (relDist < 0.2d) { break; } } } return closest; } public String normalizeStreetName(String name) { name = name.trim(); if (normalizeStreets) { String newName = name; boolean processed = newName.length() != name.length(); for (String ch : normalizeDefaultSuffixes) { int ind = checkSuffix(newName, ch); if (ind != -1) { newName = cutSuffix(newName, ind, ch.length()); processed = true; break; } } if (!processed) { for (String ch : normalizeSuffixes) { int ind = checkSuffix(newName, ch); if (ind != -1) { newName = putSuffixToEnd(newName, ind, ch.length()); processed = true; break; } } } if (processed) { return newName; } } return name; } private int checkSuffix(String name, String suffix) { int i = -1; boolean searchAgain = false; do { i = name.indexOf(suffix, i); searchAgain = false; if (i > 0) { if (Character.isLetterOrDigit(name.charAt(i - 1))) { i++; searchAgain = true; } } } while (searchAgain); return i; } private String cutSuffix(String name, int ind, int suffixLength) { String newName = name.substring(0, ind); if (name.length() > ind + suffixLength + 1) { newName += name.substring(ind + suffixLength + 1); } return newName.trim(); } private String putSuffixToEnd(String name, int ind, int suffixLength) { if (name.length() <= ind + suffixLength) { return name; } String newName; if (ind > 0) { newName = name.substring(0, ind); newName += name.substring(ind + suffixLength); newName += name.substring(ind - 1, ind + suffixLength); } else { newName = name.substring(suffixLength + 1) + name.charAt(suffixLength) + name.substring(0, suffixLength); } return newName.trim(); } public Long getStreetInCity(City city, String name, LatLon location, long initId) throws SQLException { if (name == null || city == null) { return null; } Long foundId = null; name = normalizeStreetName(name); if (loadInMemory) { foundId = addressStreetLocalMap.get(name + "_" + city.getId()); //$NON-NLS-1$ } else { addressSearchStreetStat.setLong(1, city.getId()); addressSearchStreetStat.setString(2, name); ResultSet rs = addressSearchStreetStat.executeQuery(); if (rs.next()) { foundId = rs.getLong(1); } rs.close(); } if (foundId == null) { DataIndexWriter.insertStreetData(addressStreetStat, initId, name, Junidecode.unidecode(name), location.getLatitude(), location.getLongitude(), city.getId()); if (loadInMemory) { DataIndexWriter.addBatch(pStatements, addressStreetStat, BATCH_SIZE); addressStreetLocalMap.put(name + "_" + city.getId(), initId); //$NON-NLS-1$ } else { addressStreetStat.execute(); // commit immediately to search after mapConnection.commit(); } foundId = initId; } return foundId; } private List<Amenity> tempAmenityList = new ArrayList<Amenity>(); public void checkEntity(Entity e){ String name = e.getTag(OSMTagKey.NAME); if (name == null){ String msg = ""; Collection<String> keys = e.getTagKeySet(); int cnt = 0; for (Iterator iter = keys.iterator(); iter.hasNext();) { String key = (String) iter.next(); if (key.startsWith("name:") && key.length() <= 8) { // ignore specialties like name:botanical if (cnt == 0) msg += "Entity misses default name tag, but it has localized name tag(s):\n"; msg += key + "=" + e.getTag(key) + "\n"; cnt++; } } if (cnt > 0) { msg += "Consider adding the name tag at " + e.getOsmUrl(); log.warn(msg); } } } private void iterateEntity(Entity e, int step) throws SQLException { if (step == STEP_MAIN) { if (indexPOI) { tempAmenityList.clear(); tempAmenityList = Amenity.parseAmenities(e, tempAmenityList); if (!tempAmenityList.isEmpty() && poiPreparedStatement != null) { // load data for way (location etc...) loadEntityData(e, false); for (Amenity a : tempAmenityList) { checkEntity(e); a.setEntity(e); if (a.getLocation() != null) { // do not convert english name // convertEnglishName(a); DataIndexWriter.insertAmenityIntoPoi(poiPreparedStatement, pStatements, a, BATCH_SIZE); } } } } if (indexTransport) { if (e instanceof Relation && e.getTag(OSMTagKey.ROUTE) != null) { loadEntityData(e, true); TransportRoute route = indexTransportRoute((Relation) e); if (route != null) { DataIndexWriter.insertTransportIntoIndex(transRouteStat, transRouteStopsStat, transStopsStat, transportStopsTree, visitedStops, route, pStatements, BATCH_SIZE); } } } if (indexMap && (e instanceof Way || e instanceof Node)) { // manipulate what kind of way to load loadEntityData(e, false); boolean inverse = "-1".equals(e.getTag(OSMTagKey.ONEWAY)); //$NON-NLS-1$ for (int i = 0; i < mapZooms.size(); i++) { writeBinaryEntityToMapDatabase(e, e.getId(), i == 0 ? inverse : false, i); } } if (indexAddress) { // index not only buildings but also nodes that belongs to addr:interpolation ways if (e.getTag(OSMTagKey.ADDR_HOUSE_NUMBER) != null && e.getTag(OSMTagKey.ADDR_STREET) != null) { // TODO e.getTag(OSMTagKey.ADDR_CITY) could be used to find city however many cities could have same name! // check that building is not registered already boolean exist = false; if (loadInMemory) { exist = addressBuildingLocalSet.contains(e.getId()); } else { addressSearchBuildingStat.setLong(1, e.getId()); ResultSet rs = addressSearchBuildingStat.executeQuery(); exist = rs.next(); rs.close(); } if (!exist) { loadEntityData(e, false); LatLon l = e.getLatLon(); City city = getClosestCity(l); Long idStreet = getStreetInCity(city, e.getTag(OSMTagKey.ADDR_STREET), l, (e.getId() << 2)); if (idStreet != null) { Building building = new Building(e); building.setName(e.getTag(OSMTagKey.ADDR_HOUSE_NUMBER)); DataIndexWriter.writeBuilding(addressBuildingStat, pStatements, idStreet, building, BATCH_SIZE); } } } else if (e instanceof Way /* && OSMSettings.wayForCar(e.getTag(OSMTagKey.HIGHWAY)) */ && e.getTag(OSMTagKey.HIGHWAY) != null && e.getTag(OSMTagKey.NAME) != null) { // suppose that streets with names are ways for car // Ignore all ways that have house numbers and highway type boolean exist = false; // if we saved address ways we could checked that we registered before if (saveAddressWays) { if (loadInMemory) { exist = addressStreetNodeLocalSet.contains(e.getId()); } else { addressSearchStreetNodeStat.setLong(1, e.getId()); ResultSet rs = addressSearchStreetNodeStat.executeQuery(); exist = rs.next(); rs.close(); } } // check that street way is not registered already if (!exist) { loadEntityData(e, false); LatLon l = e.getLatLon(); City city = getClosestCity(l); Long idStreet = getStreetInCity(city, e.getTag(OSMTagKey.NAME), l, (e.getId() << 2) | 1); if (idStreet != null && saveAddressWays) { DataIndexWriter.writeStreetWayNodes(addressStreetNodeStat, pStatements, idStreet, (Way) e, BATCH_SIZE); } } } if (e instanceof Relation) { if (e.getTag(OSMTagKey.POSTAL_CODE) != null) { loadEntityData(e, false); postalCodeRelations.add((Relation) e); } } } } else if(step == STEP_BORDER_CITY_WAYS) { if (indexAddress) { if (e instanceof Way && "administrative".equals(e.getTag(OSMTagKey.BOUNDARY))) { //$NON-NLS-1$ indexBoundariesRelation(e); } } } else if (step == STEP_ADDRESS_RELATIONS_AND_MULTYPOLYGONS) { if (indexAddress) { if (e instanceof Relation && "address".equals(e.getTag(OSMTagKey.TYPE))) { //$NON-NLS-1$ indexAddressRelation((Relation) e); } if (e instanceof Relation && "administrative".equals(e.getTag(OSMTagKey.BOUNDARY))) { //$NON-NLS-1$ indexBoundariesRelation((Relation) e); } } if (indexMap && e instanceof Relation && "restriction".equals(e.getTag(OSMTagKey.TYPE))) { //$NON-NLS-1$ String val = e.getTag("restriction"); //$NON-NLS-1$ if (val != null) { byte type = -1; if ("no_right_turn".equalsIgnoreCase(val)) { //$NON-NLS-1$ type = MapRenderingTypes.RESTRICTION_NO_RIGHT_TURN; } else if ("no_left_turn".equalsIgnoreCase(val)) { //$NON-NLS-1$ type = MapRenderingTypes.RESTRICTION_NO_LEFT_TURN; } else if ("no_u_turn".equalsIgnoreCase(val)) { //$NON-NLS-1$ type = MapRenderingTypes.RESTRICTION_NO_U_TURN; } else if ("no_straight_on".equalsIgnoreCase(val)) { //$NON-NLS-1$ type = MapRenderingTypes.RESTRICTION_NO_STRAIGHT_ON; } else if ("only_right_turn".equalsIgnoreCase(val)) { //$NON-NLS-1$ type = MapRenderingTypes.RESTRICTION_ONLY_RIGHT_TURN; } else if ("only_left_turn".equalsIgnoreCase(val)) { //$NON-NLS-1$ type = MapRenderingTypes.RESTRICTION_ONLY_LEFT_TURN; } else if ("only_straight_on".equalsIgnoreCase(val)) { //$NON-NLS-1$ type = MapRenderingTypes.RESTRICTION_ONLY_STRAIGHT_ON; } if (type != -1) { loadEntityData(e, true); Collection<EntityId> fromL = ((Relation) e).getMemberIds("from"); //$NON-NLS-1$ Collection<EntityId> toL = ((Relation) e).getMemberIds("to"); //$NON-NLS-1$ if (!fromL.isEmpty() && !toL.isEmpty()) { EntityId from = fromL.iterator().next(); EntityId to = toL.iterator().next(); if (from.getType() == EntityType.WAY) { if (!highwayRestrictions.containsKey(from.getId())) { highwayRestrictions.put(from.getId(), new ArrayList<Long>(4)); } highwayRestrictions.get(from.getId()).add((to.getId() << 3) | (long) type); } } } } } if (indexMap && e instanceof Relation && "multipolygon".equals(e.getTag(OSMTagKey.TYPE))) { //$NON-NLS-1$ loadEntityData(e, true); Map<Entity, String> entities = ((Relation) e).getMemberEntities(); boolean outerFound = false; for (Entity es : entities.keySet()) { if (es instanceof Way) { boolean inner = "inner".equals(entities.get(es)); //$NON-NLS-1$ if (!inner) { outerFound = true; for (String t : es.getTagKeySet()) { e.putTag(t, es.getTag(t)); } break; } } } if(!outerFound){ log.warn("Probably map bug: Multipoligon id=" + e.getId() + " contains only inner ways : "); //$NON-NLS-1$ //$NON-NLS-2$ return; } int mtType = findMultiPolygonType(e, 0); if (mtType != 0) { String name = renderingTypes.getEntityName(e); List<List<Way>> completedRings = new ArrayList<List<Way>>(); List<List<Way>> incompletedRings = new ArrayList<List<Way>>(); for (Entity es : entities.keySet()) { if (es instanceof Way) { if (!((Way) es).getNodeIds().isEmpty()) { combineMultiPolygons((Way) es, completedRings, incompletedRings); } } } // skip incompleted rings and do not add whole relation ? if (!incompletedRings.isEmpty()) { // log.warn("In multipolygon " + e.getId() + " there are incompleted ways : " + incompletedRings); return; // completedRings.addAll(incompletedRings); } // skip completed rings that are not one type for (List<Way> l : completedRings) { boolean innerType = "inner".equals(entities.get(l.get(0))); //$NON-NLS-1$ for (Way way : l) { boolean inner = "inner".equals(entities.get(way)); //$NON-NLS-1$ if (innerType != inner) { log.warn("Probably map bug: Multipoligon contains outer and inner ways.\n" + //$NON-NLS-1$ "Way:" + way.getId() + " is strange part of completed ring. InnerType:" + innerType + " way inner: " + inner + " way inner string:" + entities.get(way)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ return; } } } for (List<Way> l : completedRings) { boolean innerType = "inner".equals(entities.get(l.get(0))); //$NON-NLS-1$ boolean clockwise = isClockwiseWay(l); // clockwise - outer (like coastline), anticlockwise - inner boolean inverse = clockwise != !innerType; for (Way way : l) { boolean inner = "inner".equals(entities.get(way)); //$NON-NLS-1$ if (!inner && name != null) { multiPolygonsNames.put(way.getId(), name); } putMultipolygonType(multiPolygonsWays[0], way.getId(), mtType, inverse); for (int i = 1; i < multiPolygonsWays.length; i++) { int type = findMultiPolygonType(e, i); if (type != 0) { putMultipolygonType(multiPolygonsWays[i], way.getId(), type, inverse); } } } } } } } else if (step == STEP_CITY_NODES) { registerCityIfNeeded(e); } } public void combineMultiPolygons(Way w, List<List<Way>> completedRings, List<List<Way>> incompletedRings) { long lId = w.getEntityIds().get(w.getEntityIds().size() - 1).getId().longValue(); long fId = w.getEntityIds().get(0).getId().longValue(); if (fId == lId) { completedRings.add(Collections.singletonList(w)); } else { List<Way> l = new ArrayList<Way>(); l.add(w); boolean add = true; for (int k = 0; k < incompletedRings.size();) { boolean remove = false; List<Way> i = incompletedRings.get(k); Way last = i.get(i.size() - 1); Way first = i.get(0); long lastId = last.getEntityIds().get(last.getEntityIds().size() - 1).getId().longValue(); long firstId = first.getEntityIds().get(0).getId().longValue(); if (fId == lastId) { i.addAll(l); remove = true; l = i; fId = firstId; } else if (lId == firstId) { l.addAll(i); remove = true; lId = lastId; } if (remove) { incompletedRings.remove(k); } else { k++; } if (fId == lId) { completedRings.add(l); add = false; break; } } if (add) { incompletedRings.add(l); } } } private void putMultipolygonType(Map<Long, Set<Integer>> multiPolygonsWays, long baseId, int mtType, boolean inverse) { if (mtType == 0) { return; } if (!multiPolygonsWays.containsKey(baseId)) { multiPolygonsWays.put(baseId, new LinkedHashSet<Integer>()); } if (inverse) { multiPolygonsWays.get(baseId).add(mtType | (1 << 15)); } else { multiPolygonsWays.get(baseId).add(mtType); } } private int findMultiPolygonType(Entity e, int level) { int t = renderingTypes.encodeEntityWithType(e, mapZooms.getLevel(level).getMaxZoom(), true, typeUse); int mtType = 0; if (t != 0) { if ((t & 3) == MapRenderingTypes.MULTY_POLYGON_TYPE) { mtType = t; } else { for (Integer i : typeUse) { if ((i & 3) == MapRenderingTypes.MULTY_POLYGON_TYPE) { mtType = i; break; } } } } return mtType; } public Point2D.Float getIntersectionPoint(Line2D.Float line1, Line2D.Float line2) { if (!line1.intersectsLine(line2)) return null; double px = line1.getX1(), py = line1.getY1(), rx = line1.getX2() - px, ry = line1.getY2() - py; double qx = line2.getX1(), qy = line2.getY1(), sx = line2.getX2() - qx, sy = line2.getY2() - qy; double det = sx * ry - sy * rx; if (det == 0) { return null; } else { double z = (sx * (qy - py) + sy * (px - qx)) / det; if (z <= 0 || z >= 1) return null; // intersection at end point! return new Point2D.Float((float) (px + z * rx), (float) (py + z * ry)); } } // end intersection line-line private boolean isClockwiseWay(List<Way> ways) { if (ways.isEmpty()) { return false; } List<Node> nodes; if (ways.size() == 1) { nodes = ways.get(0).getNodes(); } else { nodes = new ArrayList<Node>(); boolean first = true; for (Way e : ways) { if (first) { first = false; nodes.addAll(e.getNodes()); } else { nodes.addAll(e.getNodes().subList(1, e.getNodes().size())); } } } if (nodes.isEmpty()) { return false; } double angle = 0; double prevAng = 0; double firstAng = 0; double selfIntersection = 0; boolean open = nodes.get(nodes.size() - 1).getId() != nodes.get(0).getId(); for (int i = 1; open ? i < nodes.size() : i <= nodes.size(); i++) {// nodes.get(i).getId() double ang; if (i < nodes.size()) { ang = Math.atan2(nodes.get(i).getLatitude() - nodes.get(i - 1).getLatitude(), nodes.get(i).getLongitude() - nodes.get(i - 1).getLongitude()); // find self intersection Line2D.Float l = new Line2D.Float((float) nodes.get(i).getLongitude(), (float) nodes.get(i).getLatitude(), (float) nodes.get(i - 1).getLongitude(), (float) nodes.get(i -1).getLatitude()); for (int j = i - 2; j > i - 7; j--) { if (j < 1) { break; } Line2D.Float l2 = new Line2D.Float((float) nodes.get(j).getLongitude(), (float) nodes.get(j).getLatitude(), (float) nodes.get(j - 1).getLongitude(), (float) nodes.get(j - 1).getLatitude()); java.awt.geom.Point2D.Float point = getIntersectionPoint(l, l2); if (point != null) { double dang = Math.atan2(nodes.get(j).getLatitude() - nodes.get(j - 1).getLatitude(), nodes.get(j).getLongitude() - nodes.get(j - 1).getLongitude()); if (adjustDirection(ang - dang) < 0) { selfIntersection += 2 * Math.PI; } else { selfIntersection -= 2 * Math.PI; } } } } else { ang = firstAng; } if (i > 1) { angle += adjustDirection(ang - prevAng); prevAng = ang; } else { prevAng = ang; firstAng = ang; } } return (angle - selfIntersection) < 0; } private double adjustDirection(double ang) { if (ang < -Math.PI) { ang += 2 * Math.PI; } else if (ang > Math.PI) { ang -= 2 * Math.PI; } return ang; } private void registerCityIfNeeded(Entity e) { if (e instanceof Node && e.getTag(OSMTagKey.PLACE) != null) { City city = new City((Node) e); if (city.getType() != null && !Algoritms.isEmpty(city.getName())) { if (city.getType() == CityType.CITY || city.getType() == CityType.TOWN) { cityManager.registerObject(((Node) e).getLatitude(), ((Node) e).getLongitude(), city); } else { cityVillageManager.registerObject(((Node) e).getLatitude(), ((Node) e).getLongitude(), city); } cities.put(city.getEntityId(), city); } } } private void writeBinaryEntityToMapDatabase(Entity e, long baseId, boolean inverse, int level) throws SQLException { int type = renderingTypes.encodeEntityWithType(e, mapZooms.getLevel(level).getMaxZoom(), false, typeUse); Map<Long, Set<Integer>> multiPolygonsWays = this.multiPolygonsWays[level]; boolean hasMulti = e instanceof Way && multiPolygonsWays.containsKey(e.getId()); if (hasMulti) { Set<Integer> set = multiPolygonsWays.get(e.getId()); boolean first = true; for (Integer i : set) { if (first && type == 0) { type = i; first = false; } else { // do not compare direction int k = i & 0x7fff; int ks = k | MapRenderingTypes.POLYGON_TYPE; // turn of polygon type 3 ^ (suppose polygon = multipolygon) if (ks == type) { type = i; } else { int ind = typeUse.indexOf(ks); if (ind == -1) { typeUse.add(i); } else { typeUse.set(ind, i); } } } } } if (type == 0) { return; } restrictionsUse.clear(); // try to find restrictions only for max zoom level if (level == 0 && highwayRestrictions.containsKey(baseId)) { restrictionsUse.addAll(highwayRestrictions.get(baseId)); } boolean point = (type & 3) == MapRenderingTypes.POINT_TYPE; RTree rtree = null; int zoom; long id = (baseId << 3) | ((level & 3) << 1); rtree = mapTree[level]; zoom = mapZooms.getLevel(level).getMaxZoom() - 1; boolean skip = false; String eName = renderingTypes.getEntityName(e); if (eName == null) { eName = multiPolygonsNames.get(baseId); } int highwayAttributes = 0; if (e.getTag(OSMTagKey.HIGHWAY) != null) { highwayAttributes = MapRenderingTypes.getHighwayAttributes(e); } if (e instanceof Way) { id |= 1; // simplify route if (level > 0) { e = simplifyWay((Way) e, id, hasMulti, zoom, eName, type, level); skip = e == null; } } if (!skip) { DataIndexWriter.insertBinaryMapRenderObjectIndex(pStatements, mapBinaryStat, rtree, e, eName, id, type, typeUse, highwayAttributes, restrictionsUse, inverse, point, true); } } protected long encodeTypesToOneLong(int mainType) { long i = 0; int ind = 0; int sh = 0; if(typeUse.size() > 3){ log.error("Types for low index way more than 4"); //$NON-NLS-1$ } i |= (mainType << sh); if (typeUse.size() > ind) { sh += 16; i |= ((long)typeUse.get(ind++) << sh ); if (typeUse.size() > ind) { sh += 16; i |= ((long)typeUse.get(ind++) << sh ); if (typeUse.size() > ind) { sh += 16; i |= ((long)typeUse.get(ind++) << sh); } } } return i; } protected int decodeTypesFromOneLong(long i) { typeUse.clear(); int mask = (1 << 16) - 1; int k = (int) (i & mask); int r = 0; if (k > 0) { r = k; i >>= 16; k = (int) (i & mask); if (k > 0) { typeUse.add(k); i >>= 16; k = (int) (i & mask); if (k > 0) { typeUse.add(k); i >>= 16; k = (int) (i & mask); if (k > 0) { typeUse.add(k); i >>= 16; } } } } return r; } protected Way simplifyWay(Way originalE, long id, boolean hasMulti, int zoom, String name, int type, int level) throws SQLException { List<Node> nodes = originalE.getNodes(); Way way = new Way(id); for (String t : originalE.getTagKeySet()) { way.putTag(t, originalE.getTag(t)); } boolean cycle = originalE.getNodeIds().get(0).longValue() == originalE.getNodeIds().get(nodes.size() - 1).longValue(); long longType = encodeTypesToOneLong(type); boolean skip = checkForSmallAreas(nodes, zoom, 3, 3); if (skip && cycle/* || !hasMulti)*/) { return null; } simplifyDouglasPeucker(nodes, zoom + 8, 3, way); if (way.getNodes().size() < 2) { return null; } if (cycle) { // nothing to do return way; } else { lowLevelWays ++; DataIndexWriter.insertLowLevelMapBinaryObject(pStatements, mapLowLevelBinaryStat, level, longType, id, way.getNodes(), name); return null; } } private void loadNodes(byte[] nodes, List<Float> toPut){ toPut.clear(); for (int i = 0; i < nodes.length;) { int lat = Algoritms.parseIntFromBytes(nodes, i); i += 4; int lon = Algoritms.parseIntFromBytes(nodes, i); i += 4; toPut.add(Float.intBitsToFloat(lat)); toPut.add(Float.intBitsToFloat(lon)); } } private void processingLowLevelWays(IProgress progress) throws SQLException { restrictionsUse.clear(); mapLowLevelBinaryStat.executeBatch(); mapLowLevelBinaryStat.close(); pStatements.remove(mapLowLevelBinaryStat); mapLowLevelBinaryStat = null; mapConnection.commit(); PreparedStatement startStat = mapConnection.prepareStatement("SELECT id, start_node, end_node, nodes FROM " + IndexConstants.LOW_LEVEL_MAP_TABLE + " WHERE start_node = ? AND type=? AND level = ? AND name=?"); PreparedStatement endStat = mapConnection.prepareStatement("SELECT id, start_node, end_node, nodes FROM " + IndexConstants.LOW_LEVEL_MAP_TABLE + " WHERE end_node = ? AND type=? AND level = ? AND name=?"); Statement selectStatement = mapConnection.createStatement(); ResultSet rs = selectStatement.executeQuery("SELECT id, start_node, end_node, name, nodes, type, level FROM " + IndexConstants.LOW_LEVEL_MAP_TABLE); Set<Long> visitedWays = new LinkedHashSet<Long>(); ArrayList<Float> list = new ArrayList<Float>(100); while(rs.next()){ if(lowLevelWays != -1){ progress.progress(1); } long id = rs.getLong(1); if(visitedWays.contains(id)){ continue; } visitedWays.add(id); int level = rs.getInt(7); int zoom = mapZooms.getLevel(level).getMaxZoom(); long startNode = rs.getLong(2); long endNode = rs.getLong(3); String name = rs.getString(4); long ltype = rs.getLong(6); loadNodes(rs.getBytes(5), list); ArrayList<Float> wayNodes = new ArrayList<Float>(list); // combine startPoint with EndPoint boolean combined = true; while (combined) { combined = false; endStat.setLong(1, startNode); endStat.setLong(2, ltype); endStat.setShort(3, (short) level); endStat.setString(4, name); ResultSet fs = endStat.executeQuery(); while (fs.next()) { if (!visitedWays.contains(fs.getLong(1))) { combined = true; long lid = fs.getLong(1); startNode = fs.getLong(2); visitedWays.add(lid); loadNodes(fs.getBytes(4), list); ArrayList li = new ArrayList<Float>(list); // remove first lat/lon point wayNodes.remove(0); wayNodes.remove(0); li.addAll(wayNodes); wayNodes = li; } } fs.close(); } // combined end point combined = true; while (combined) { combined = false; startStat.setLong(1, endNode); startStat.setLong(2, ltype); startStat.setShort(3, (short) level); startStat.setString(4, name); ResultSet fs = startStat.executeQuery(); while (fs.next()) { if (!visitedWays.contains(fs.getLong(1))) { combined = true; long lid = fs.getLong(1); endNode = fs.getLong(3); visitedWays.add(lid); loadNodes(fs.getBytes(4), list); for (int i = 2; i < list.size(); i++) { wayNodes.add(list.get(i)); } } } fs.close(); } List<Node> wNodes = new ArrayList<Node>(); for (int i = 0; i < wayNodes.size(); i += 2) { wNodes.add(new Node(wayNodes.get(i), wayNodes.get(i + 1), i == 0 ? startNode : endNode)); } boolean skip = false; boolean cycle = startNode == endNode; boolean hasMulti = multiPolygonsWays[level].containsKey(id >> 3); if(cycle || !hasMulti){ skip = checkForSmallAreas(wNodes, zoom - 1, 1, 4); } if (!skip) { Way newWs = new Way(id); simplifyDouglasPeucker(wNodes, zoom - 1 + 8, 3, newWs); int type = decodeTypesFromOneLong(ltype); DataIndexWriter.insertBinaryMapRenderObjectIndex(pStatements, mapBinaryStat, mapTree[level], newWs, name, id, type, typeUse, 0, restrictionsUse, false, false, false); } } } private boolean checkForSmallAreas(List<Node> nodes, int zoom, int minz, int maxz) { int minX = Integer.MAX_VALUE; int maxX = Integer.MIN_VALUE; int minY = Integer.MAX_VALUE; int maxY = Integer.MIN_VALUE; int c = 0; for (int i = 0; i < nodes.size(); i++) { if (nodes.get(i) != null) { c++; int x = (int) (MapUtils.getTileNumberX(zoom, nodes.get(i).getLongitude()) * 256d); int y = (int) (MapUtils.getTileNumberY(zoom, nodes.get(i).getLatitude()) * 256d); minX = Math.min(minX, x); maxX = Math.max(maxX, x); minY = Math.min(minY, y); maxY = Math.max(maxY, y); } } if (c < 2) { return true; } return ((maxX - minX) <= minz && (maxY - minY) <= maxz) || ((maxX - minX) <= maxz && (maxY - minY) <= minz); } private void simplifyDouglasPeucker(List<Node> n, int zoom, int epsilon, Way w){ ArrayList<Integer> l = new ArrayList<Integer>(); int first = 0; while(first < n.size()){ if(n.get(first) != null){ break; } first++; } int last = n.size() - 1; while (last >= 0) { if (n.get(last) != null) { break; } last--; } if(last - first < 1){ return; } boolean cycle = n.get(first).getId() == n.get(last).getId(); simplifyDouglasPeucker(n, zoom, epsilon, l, first, cycle ? last - 1: last); w.addNode(n.get(first)); for (int i = 0; i < l.size(); i++) { w.addNode(n.get(l.get(i))); } if (cycle) { w.addNode(n.get(first)); } } private void simplifyDouglasPeucker(List<Node> n, int zoom, int epsilon, List<Integer> ints, int start, int end){ double dmax = -1; int index = -1; for (int i = start + 1; i <= end - 1; i++) { if(n.get(i) == null){ continue; } double d = orthogonalDistance(zoom, n.get(start), n.get(end), n.get(i));// calculate distance from line if (d > dmax) { dmax = d; index = i; } } if(dmax >= epsilon){ simplifyDouglasPeucker(n, zoom, epsilon, ints, start, index); simplifyDouglasPeucker(n, zoom, epsilon, ints, index, end); } else { ints.add(end); } } private double orthogonalDistance(int zoom, Node nodeLineStart, Node nodeLineEnd, Node node) { double x1 = MapUtils.getTileNumberX(zoom, nodeLineStart.getLongitude()); double y1 = MapUtils.getTileNumberY(zoom, nodeLineStart.getLatitude()); double x2 = MapUtils.getTileNumberX(zoom, nodeLineEnd.getLongitude()); double y2 = MapUtils.getTileNumberY(zoom, nodeLineEnd.getLatitude()); double x = MapUtils.getTileNumberX(zoom, node.getLongitude()); double y = MapUtils.getTileNumberY(zoom, node.getLatitude()); double A = x - x1; double B = y - y1; double C = x2 - x1; double D = y2 - y1; return Math.abs(A * D - C * B) / Math.sqrt(C * C + D * D); } public boolean nodeIsLastSubTree(RTree tree, long ptr) throws RTreeException { rtree.Node parent = tree.getReadNode(ptr); Element[] e = parent.getAllElements(); for (int i = 0; i < parent.getTotalElements(); i++) { if (e[i].getElementType() != rtree.Node.LEAF_NODE) { return false; } } return true; } public void writeBinaryAddressIndex(BinaryMapIndexWriter writer, IProgress progress) throws IOException, SQLException { boolean readWayNodes = saveAddressWays; writer.startWriteAddressIndex(getRegionName()); DataIndexReader reader = new DataIndexReader(); List<City> cities = reader.readCities(mapConnection); List<Street> streets = new ArrayList<Street>(); Collections.sort(cities, new Comparator<City>() { @Override public int compare(City o1, City o2) { if (o1.getType() != o2.getType()) { return (o1.getType().ordinal() - o2.getType().ordinal()); } return Collator.getInstance().compare(o1.getName(), o2.getName()); } }); PreparedStatement streetstat = reader.getStreetsBuildingPreparedStatement(mapConnection); PreparedStatement waynodesStat = null; if (readWayNodes) { waynodesStat = reader.getStreetsWayNodesPreparedStatement(mapConnection); } int j = 0; for (; j < cities.size(); j++) { City c = cities.get(j); if (c.getType() != CityType.CITY && c.getType() != CityType.TOWN) { break; } } progress.startTask(Messages.getString("IndexCreator.SERIALIZING_ADRESS"), j + ((cities.size() - j) / 100 + 1)); //$NON-NLS-1$ Map<String, Set<Street>> postcodes = new TreeMap<String, Set<Street>>(); boolean writeCities = true; // collect suburbs with is in value List<City> suburbs = new ArrayList<City>(); for(City s : cities){ if(s.getType() == CityType.SUBURB && s.getIsInValue() != null){ suburbs.add(s); } } // write cities and after villages writer.startCityIndexes(false); for (int i = 0; i < cities.size(); i++) { City c = cities.get(i); List<City> listSuburbs = null; for (City suburb : suburbs) { if (suburb.getIsInValue().contains(c.getName().toLowerCase())) { if(listSuburbs == null){ listSuburbs = new ArrayList<City>(); } listSuburbs.add(suburb); } } if (writeCities) { progress.progress(1); } else if ((cities.size() - i) % 100 == 0) { progress.progress(1); } if (writeCities && c.getType() != CityType.CITY && c.getType() != CityType.TOWN) { writer.endCityIndexes(false); writer.startCityIndexes(true); writeCities = false; } streets.clear(); Map<Street, List<Node>> streetNodes = null; if (readWayNodes) { streetNodes = new LinkedHashMap<Street, List<Node>>(); } long time = System.currentTimeMillis(); reader.readStreetsBuildings(streetstat, c, streets, waynodesStat, streetNodes, listSuburbs); long f = System.currentTimeMillis() - time; writer.writeCityIndex(c, streets, streetNodes); int bCount = 0; for (Street s : streets) { bCount++; for (Building b : s.getBuildings()) { bCount++; if (b.getPostcode() != null) { if (!postcodes.containsKey(b.getPostcode())) { postcodes.put(b.getPostcode(), new LinkedHashSet<Street>(3)); } postcodes.get(b.getPostcode()).add(s); } } } if (f > 500) { System.out.println("! " + c.getName() + " ! " + f + " " + bCount + " streets " + streets.size()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ } } writer.endCityIndexes(!writeCities); // write postcodes writer.startPostcodes(); for (String s : postcodes.keySet()) { writer.writePostcode(s, postcodes.get(s)); } writer.endPostcodes(); progress.finishTask(); writer.endWriteAddressIndex(); writer.flush(); streetstat.close(); if (readWayNodes) { waynodesStat.close(); } } public void writeBinaryMapIndex(BinaryMapIndexWriter writer) throws IOException, SQLException { try { PreparedStatement selectData = mapConnection.prepareStatement("SELECT nodes, types, name, highway, restrictions FROM binary_map_objects WHERE id = ?"); //$NON-NLS-1$ writer.startWriteMapIndex(regionName); for (int i = 0; i < mapZooms.size(); i++) { RTree rtree = mapTree[i]; long rootIndex = rtree.getFileHdr().getRootIndex(); rtree.Node root = rtree.getReadNode(rootIndex); Rect rootBounds = calcBounds(root); if (rootBounds != null) { boolean last = nodeIsLastSubTree(rtree, rootIndex); writer.startWriteMapLevelIndex(mapZooms.getLevel(i).getMinZoom(), mapZooms.getLevel(i).getMaxZoom(), rootBounds .getMinX(), rootBounds.getMaxX(), rootBounds.getMinY(), rootBounds.getMaxY()); if (last) { writer.startMapTreeElement(rootBounds.getMinX(), rootBounds.getMaxX(), rootBounds.getMinY(), rootBounds.getMaxY()); } writeBinaryMapTree(root, rtree, writer, selectData); if (last) { writer.endWriteMapTreeElement(); } writer.endWriteMapLevelIndex(); } } selectData.close(); writer.writeMapEncodingRules(renderingTypes.getEncodingRuleTypes()); writer.endWriteMapIndex(); writer.flush(); } catch (RTreeException e) { throw new IllegalStateException(e); } } public void writeBinaryMapTree(rtree.Node parent, RTree r, BinaryMapIndexWriter writer, PreparedStatement selectData) throws IOException, RTreeException, SQLException { Element[] e = parent.getAllElements(); for (int i = 0; i < parent.getTotalElements(); i++) { Rect re = e[i].getRect(); if (e[i].getElementType() == rtree.Node.LEAF_NODE) { long id = ((LeafElement) e[i]).getPtr(); selectData.setLong(1, id); ResultSet rs = selectData.executeQuery(); if (rs.next()) { // mapConnection.prepareStatement("SELECT nodes, types, name, highway, restrictions FROM binary_map_objects WHERE id = ?"); writer.writeMapData(id, rs.getBytes(1), rs.getBytes(2), rs.getString(3), rs.getInt(4), rs.getBytes(5)); } else { log.error("Something goes wrong with id = " + id); //$NON-NLS-1$ } } else { long ptr = ((NonLeafElement) e[i]).getPtr(); rtree.Node ns = r.getReadNode(ptr); writer.startMapTreeElement(re.getMinX(), re.getMaxX(), re.getMinY(), re.getMaxY()); writeBinaryMapTree(ns, r, writer, selectData); writer.endWriteMapTreeElement(); } } } private int registerString(Map<String, Integer> stringTable, String s) { if (stringTable.containsKey(s)) { return stringTable.get(s); } int size = stringTable.size(); stringTable.put(s, size); return size; } private Map<String, Integer> createStringTableForTransport() { Map<String, Integer> stringTable = new LinkedHashMap<String, Integer>(); registerString(stringTable, "bus"); //$NON-NLS-1$ registerString(stringTable, "trolleybus"); //$NON-NLS-1$ registerString(stringTable, "subway"); //$NON-NLS-1$ registerString(stringTable, "tram"); //$NON-NLS-1$ registerString(stringTable, "share_taxi"); //$NON-NLS-1$ registerString(stringTable, "taxi"); //$NON-NLS-1$ registerString(stringTable, "train"); //$NON-NLS-1$ registerString(stringTable, "ferry"); //$NON-NLS-1$ return stringTable; } public void writeBinaryTransportIndex(BinaryMapIndexWriter writer) throws IOException, SQLException { try { transportStopsTree.flush(); visitedStops = null; // allow gc to collect it PreparedStatement selectTransportRouteData = mapConnection.prepareStatement( "SELECT id, dist, name, name_en, ref, operator, type FROM transport_route"); //$NON-NLS-1$ PreparedStatement selectTransportData = mapConnection.prepareStatement("SELECT S.stop, S.direction," + //$NON-NLS-1$ " A.latitude, A.longitude, A.name, A.name_en " + //$NON-NLS-1$ "FROM transport_route_stop S INNER JOIN transport_stop A ON A.id = S.stop WHERE S.route = ? ORDER BY S.ord asc"); //$NON-NLS-1$ writer.startWriteTransportIndex(regionName); writer.startWriteTransportRoutes(); // expect that memory would be enough Map<String, Integer> stringTable = createStringTableForTransport(); Map<Long, Long> transportRoutes = new LinkedHashMap<Long, Long>(); ResultSet rs = selectTransportRouteData.executeQuery(); List<TransportStop> directStops = new ArrayList<TransportStop>(); List<TransportStop> reverseStops = new ArrayList<TransportStop>(); while (rs.next()) { long idRoute = rs.getLong(1); int dist = rs.getInt(2); String routeName = rs.getString(3); String routeEnName = rs.getString(4); if (routeEnName != null && routeEnName.equals(Junidecode.unidecode(routeName))) { routeEnName = null; } String ref = rs.getString(5); String operator = rs.getString(6); String type = rs.getString(7); selectTransportData.setLong(1, idRoute); ResultSet rset = selectTransportData.executeQuery(); reverseStops.clear(); directStops.clear(); while (rset.next()) { boolean dir = rset.getInt(2) != 0; long idStop = rset.getInt(1); String stopName = rset.getString(5); String stopEnName = rset.getString(6); if (stopEnName != null && stopEnName.equals(Junidecode.unidecode(stopName))) { stopEnName = null; } TransportStop st = new TransportStop(); st.setId(idStop); st.setName(stopName); st.setLocation(rset.getDouble(3), rset.getDouble(4)); if (stopEnName != null) { st.setEnName(stopEnName); } if (dir) { directStops.add(st); } else { reverseStops.add(st); } } writer.writeTransportRoute(idRoute, routeName, routeEnName, ref, operator, type, dist, directStops, reverseStops, stringTable, transportRoutes); } rs.close(); selectTransportRouteData.close(); selectTransportData.close(); writer.endWriteTransportRoutes(); PreparedStatement selectTransportStop = mapConnection.prepareStatement( "SELECT A.id, A.latitude, A.longitude, A.name, A.name_en FROM transport_stop A where A.id = ?"); //$NON-NLS-1$ PreparedStatement selectTransportRouteStop = mapConnection.prepareStatement( "SELECT DISTINCT S.route FROM transport_route_stop S WHERE S.stop = ? "); //$NON-NLS-1$ long rootIndex = transportStopsTree.getFileHdr().getRootIndex(); rtree.Node root = transportStopsTree.getReadNode(rootIndex); Rect rootBounds = calcBounds(root); if (rootBounds != null) { writer.startTransportTreeElement(rootBounds.getMinX(), rootBounds.getMaxX(), rootBounds.getMinY(), rootBounds.getMaxY()); writeBinaryTransportTree(root, transportStopsTree, writer, selectTransportStop, selectTransportRouteStop, transportRoutes, stringTable); writer.endWriteTransportTreeElement(); } selectTransportStop.close(); selectTransportRouteStop.close(); writer.writeTransportStringTable(stringTable); writer.endWriteTransportIndex(); writer.flush(); } catch (RTreeException e) { throw new IllegalStateException(e); } } public void writeBinaryTransportTree(rtree.Node parent, RTree r, BinaryMapIndexWriter writer, PreparedStatement selectTransportStop, PreparedStatement selectTransportRouteStop, Map<Long, Long> transportRoutes, Map<String, Integer> stringTable) throws IOException, RTreeException, SQLException { Element[] e = parent.getAllElements(); List<Long> routes = null; for (int i = 0; i < parent.getTotalElements(); i++) { Rect re = e[i].getRect(); if (e[i].getElementType() == rtree.Node.LEAF_NODE) { long id = ((LeafElement) e[i]).getPtr(); selectTransportStop.setLong(1, id); selectTransportRouteStop.setLong(1, id); ResultSet rs = selectTransportStop.executeQuery(); if (rs.next()) { int x24 = (int) MapUtils.getTileNumberX(24, rs.getDouble(3)); int y24 = (int) MapUtils.getTileNumberY(24, rs.getDouble(2)); String name = rs.getString(4); String nameEn = rs.getString(5); if (nameEn != null && nameEn.equals(Junidecode.unidecode(name))) { nameEn = null; } ResultSet rset = selectTransportRouteStop.executeQuery(); if (routes == null) { routes = new ArrayList<Long>(); } else { routes.clear(); } while (rset.next()) { Long route = transportRoutes.get(rset.getLong(1)); if (route == null) { log.error("Something goes wrong with transport route id = " + rset.getLong(1)); //$NON-NLS-1$ } else { routes.add(route); } } rset.close(); writer.writeTransportStop(id, x24, y24, name, nameEn, stringTable, routes); } else { log.error("Something goes wrong with transport id = " + id); //$NON-NLS-1$ } } else { long ptr = ((NonLeafElement) e[i]).getPtr(); rtree.Node ns = r.getReadNode(ptr); writer.startTransportTreeElement(re.getMinX(), re.getMaxX(), re.getMinY(), re.getMaxY()); writeBinaryTransportTree(ns, r, writer, selectTransportStop, selectTransportRouteStop, transportRoutes, stringTable); writer.endWriteTransportTreeElement(); } } } public Rect calcBounds(rtree.Node n) { Rect r = null; Element[] e = n.getAllElements(); for (int i = 0; i < n.getTotalElements(); i++) { Rect re = e[i].getRect(); if (r == null) { try { r = new Rect(re.getMinX(), re.getMinY(), re.getMaxX(), re.getMaxY()); } catch (IllegalValueException ex) { } } else { r.expandToInclude(re); } } return r; } public String getRTreeMapIndexNonPackFileName() { return mapFile.getAbsolutePath() + ".rtree"; //$NON-NLS-1$ } public String getRTreeTransportStopsFileName() { return mapFile.getAbsolutePath() + ".trans"; //$NON-NLS-1$ } public String getRTreeTransportStopsPackFileName() { return mapFile.getAbsolutePath() + ".ptrans"; //$NON-NLS-1$ } public String getRTreeMapIndexPackFileName() { return mapFile.getAbsolutePath() + ".prtree"; //$NON-NLS-1$ } public void generateIndexes(File readFile, IProgress progress, IOsmStorageFilter addFilter) throws IOException, SAXException, SQLException{ generateIndexes(readFile, progress, addFilter, null, null); } public void generateIndexes(File readFile, IProgress progress, IOsmStorageFilter addFilter, MapZooms mapZooms, MapRenderingTypes renderingTypes) throws IOException, SAXException, SQLException { if(renderingTypes != null){ this.renderingTypes = renderingTypes; } if(mapZooms != null){ this.mapZooms = mapZooms; } else { mapZooms = this.mapZooms; } multiPolygonsWays = new Map[mapZooms.size()]; for (int i = 0; i < multiPolygonsWays.length; i++) { multiPolygonsWays[i] = new LinkedHashMap<Long, Set<Integer>>(); } // clear previous results and setting variables if (readFile != null && regionName == null) { int i = readFile.getName().indexOf('.'); if (i > -1) { regionName = Algoritms.capitalizeFirstLetterAndLowercase(readFile.getName().substring(0, i)); } } cities.clear(); cityManager.clear(); lowLevelWays = -1; postalCodeRelations.clear(); if (normalizeStreets) { normalizeDefaultSuffixes = DataExtractionSettings.getSettings().getDefaultSuffixesToNormalizeStreets(); normalizeSuffixes = DataExtractionSettings.getSettings().getSuffixesToNormalizeStreets(); } // Main generation method try { // //////////////////////////////////////////////////////////////////////// // 1. creating nodes db to fast access for all nodes and simply import all relations, ways, nodes to it boolean loadFromPath = dbFile == null || !databaseFileExists(dbFile); if (dbFile == null) { dbFile = new File(workingDir, TEMP_NODES_DB); // to save space if (databaseFileExists(dbFile)) { removeDatabase(dbFile); } } dbConn = getDatabaseConnection(dbFile.getAbsolutePath()); int allRelations = 100000; int allWays = 1000000; int allNodes = 10000000; if (loadFromPath) { progress.setGeneralProgress("[35 / 100]"); //$NON-NLS-1$ progress.startTask(Messages.getString("IndexCreator.LOADING_FILE") + readFile.getAbsolutePath(), -1); //$NON-NLS-1$ NewDataExtractionOsmFilter filter = extractOsmToNodesDB(readFile, progress, addFilter); if (filter != null) { allNodes = filter.getAllNodes(); allWays = filter.getAllWays(); allRelations = filter.getAllRelations(); } } pselectNode = dbConn.prepareStatement("select n.latitude, n.longitude, t.skeys, t.value from node n left join tags t on n.id = t.id and t.type = 0 where n.id = ?"); //$NON-NLS-1$ pselectWay = dbConn.prepareStatement("select w.node, w.ord, t.skeys, t.value, n.latitude, n.longitude " + //$NON-NLS-1$ "from ways w left join tags t on w.id = t.id and t.type = 1 and w.ord = 0 inner join node n on w.node = n.id " + //$NON-NLS-1$ "where w.id = ? order by w.ord"); //$NON-NLS-1$ pselectRelation = dbConn.prepareStatement("select r.member, r.type, r.role, r.ord, t.skeys, t.value " + //$NON-NLS-1$ "from relations r left join tags t on r.id = t.id and t.type = 2 and r.ord = 0 " + //$NON-NLS-1$ "where r.id = ? order by r.ord"); //$NON-NLS-1$ pselectTags = dbConn.prepareStatement("select skeys, value from tags where id = ? and type = ?"); //$NON-NLS-1$ // do not create temp map file and rtree files if (recreateOnlyBinaryFile) { mapFile = new File(workingDir, getMapFileName()); File tempDBMapFile = new File(workingDir, getTempMapDBFileName()); mapConnection = getDatabaseConnection(tempDBMapFile.getAbsolutePath()); mapConnection.setAutoCommit(false); try { if (indexMap) { mapTree = new RTree[mapZooms.size()]; for (int i = 0; i < mapZooms.size(); i++) { mapTree[i] = new RTree(getRTreeMapIndexPackFileName() + i); } } if (indexTransport) { transportStopsTree = new RTree(getRTreeTransportStopsPackFileName()); } } catch (RTreeException e) { log.error("Error flushing", e); //$NON-NLS-1$ throw new IOException(e); } } else { // 2. Create index connections and index structure createDatabaseIndexesStructure(); // 3. Processing all entries // 3.1 write all cities if (indexAddress) { progress.setGeneralProgress("[40 / 100]"); //$NON-NLS-1$ progress.startTask(Messages.getString("IndexCreator.INDEX_CITIES"), allNodes); //$NON-NLS-1$ if (!loadFromPath) { allNodes = iterateOverEntities(progress, EntityType.NODE, allNodes, STEP_CITY_NODES); } for (City c : cities.values()) { DataIndexWriter.writeCity(addressCityStat, pStatements, c, BATCH_SIZE); } // commit to put all cities if (pStatements.get(addressCityStat) > 0) { addressCityStat.executeBatch(); pStatements.put(addressCityStat, 0); mapConnection.commit(); } } // 3.2 index address relations if (indexAddress || indexMap) { progress.setGeneralProgress("[30 / 100]"); //$NON-NLS-1$ progress.startTask(Messages.getString("IndexCreator.PREINDEX_ADRESS_MAP"), allRelations); //$NON-NLS-1$ allRelations = iterateOverEntities(progress, EntityType.RELATION, allRelations, STEP_ADDRESS_RELATIONS_AND_MULTYPOLYGONS); if (indexAddress) { progress.setGeneralProgress("[40 / 100]"); //$NON-NLS-1$ progress.startTask(Messages.getString("IndexCreator.PREINDEX_ADRESS_MAP"), allWays); //$NON-NLS-1$ allWays = iterateOverEntities(progress, EntityType.WAY, allWays, STEP_BORDER_CITY_WAYS); } // commit to put all cities if (indexAddress) { if (pStatements.get(addressBuildingStat) > 0) { addressBuildingStat.executeBatch(); pStatements.put(addressBuildingStat, 0); } if (pStatements.get(addressStreetNodeStat) > 0) { addressStreetNodeStat.executeBatch(); pStatements.put(addressStreetNodeStat, 0); } mapConnection.commit(); } } // 3.3 MAIN iterate over all entities if (indexPOI || indexAddress || indexMap) { progress.setGeneralProgress("[50 / 100]"); progress.startTask(Messages.getString("IndexCreator.PROCESS_OSM_NODES"), allNodes); iterateOverEntities(progress, EntityType.NODE, allNodes, STEP_MAIN); progress.setGeneralProgress("[70 / 100]"); progress.startTask(Messages.getString("IndexCreator.PROCESS_OSM_WAYS"), allWays); iterateOverEntities(progress, EntityType.WAY, allWays, STEP_MAIN); } progress.setGeneralProgress("[85 / 100]"); progress.startTask(Messages.getString("IndexCreator.PROCESS_OSM_REL"), allRelations); iterateOverEntities(progress, EntityType.RELATION, allRelations, STEP_MAIN); // 3.4 combine all low level ways and simplify them if(indexMap){ progress.setGeneralProgress("[90 / 100]"); progress.startTask(Messages.getString("IndexCreator.INDEX_LO_LEVEL_WAYS"), lowLevelWays); processingLowLevelWays(progress); } // 3.5 update all postal codes from relations if (indexAddress && !postalCodeRelations.isEmpty()) { progress.setGeneralProgress("[90 / 100]"); progress.startTask(Messages.getString("IndexCreator.REGISTER_PCODES"), -1); if (pStatements.get(addressBuildingStat) > 0) { addressBuildingStat.executeBatch(); pStatements.put(addressBuildingStat, 0); mapConnection.commit(); } processingPostcodes(); } // 4. packing map rtree indexes if (indexMap) { progress.setGeneralProgress("[90 / 100]"); //$NON-NLS-1$ progress.startTask(Messages.getString("IndexCreator.PACK_RTREE_MAP"), -1); //$NON-NLS-1$ for (int i = 0; i < mapZooms.size(); i++) { mapTree[i] = packRtreeFile(mapTree[i], getRTreeMapIndexNonPackFileName() + i, getRTreeMapIndexPackFileName() + i); } } if (indexTransport) { progress.setGeneralProgress("[90 / 100]"); //$NON-NLS-1$ progress.startTask(Messages.getString("IndexCreator.PACK_RTREE_TRANSP"), -1); //$NON-NLS-1$ transportStopsTree = packRtreeFile(transportStopsTree, getRTreeTransportStopsFileName(), getRTreeTransportStopsPackFileName()); } } // 5. Writing binary file if (indexMap || indexAddress || indexTransport) { if (mapFile.exists()) { mapFile.delete(); } mapRAFile = new RandomAccessFile(mapFile, "rw"); BinaryMapIndexWriter writer = new BinaryMapIndexWriter(mapRAFile); if (indexMap) { progress.setGeneralProgress("[95 of 100]"); progress.startTask("Writing map index to binary file...", -1); closePreparedStatements(mapBinaryStat, mapLowLevelBinaryStat); mapConnection.commit(); writeBinaryMapIndex(writer); } if (indexAddress) { progress.setGeneralProgress("[95 of 100]"); progress.startTask("Writing address index to binary file...", -1); closePreparedStatements(addressCityStat, addressStreetStat, addressStreetNodeStat, addressBuildingStat); mapConnection.commit(); writeBinaryAddressIndex(writer, progress); } if (indexTransport) { progress.setGeneralProgress("[95 of 100]"); progress.startTask("Writing transport index to binary file...", -1); closePreparedStatements(transRouteStat, transRouteStopsStat, transStopsStat); mapConnection.commit(); writeBinaryTransportIndex(writer); } progress.finishTask(); writer.close(); mapRAFile.close(); log.info("Finish writing binary file"); //$NON-NLS-1$ } } catch (RuntimeException e) { log.error("Log exception", e); //$NON-NLS-1$ throw e; } catch (SQLException e) { log.error("Log exception", e); //$NON-NLS-1$ throw e; } catch (IOException e) { log.error("Log exception", e); //$NON-NLS-1$ throw e; } catch (SAXException e) { log.error("Log exception", e); //$NON-NLS-1$ throw e; } finally { try { if (pselectNode != null) { pselectNode.close(); } if (pselectWay != null) { pselectWay.close(); } if (pselectRelation != null) { pselectRelation.close(); } if (pselectTags != null) { pselectTags.close(); } for (PreparedStatement p : pStatements.keySet()) { if (pStatements.get(p) > 0) { p.executeBatch(); } p.close(); } if (poiConnection != null) { poiConnection.commit(); poiConnection.close(); poiConnection = null; if (lastModifiedDate != null) { poiIndexFile.setLastModified(lastModifiedDate); } } if (mapConnection != null) { mapConnection.commit(); mapConnection.close(); mapConnection = null; File tempDBFile = new File(workingDir, getTempMapDBFileName()); if (databaseFileExists(tempDBFile) && deleteDatabaseIndexes) { // do not delete it for now removeDatabase(tempDBFile); } } // delete map rtree files if (mapTree != null) { for (int i = 0; i < mapTree.length; i++) { if (mapTree[i] != null) { RandomAccessFile file = mapTree[i].getFileHdr().getFile(); file.close(); } } for (int i = 0; i < mapTree.length; i++) { File f = new File(getRTreeMapIndexNonPackFileName() + i); if (f.exists() && deleteDatabaseIndexes) { f.delete(); } f = new File(getRTreeMapIndexPackFileName() + i); if (f.exists() && deleteDatabaseIndexes) { f.delete(); } } } // delete transport rtree files if (transportStopsTree != null) { transportStopsTree.getFileHdr().getFile().close(); File f = new File(getRTreeTransportStopsFileName()); if (f.exists() && deleteDatabaseIndexes) { f.delete(); } f = new File(getRTreeTransportStopsPackFileName()); if (f.exists() && deleteDatabaseIndexes) { f.delete(); } } // do not delete first db connection if (dbConn != null) { if (usingH2()) { dbConn.createStatement().execute("SHUTDOWN COMPACT"); //$NON-NLS-1$ } dbConn.close(); } if (deleteOsmDB) { if (usingDerby()) { try { DriverManager.getConnection("jdbc:derby:;shutdown=true"); //$NON-NLS-1$ } catch (SQLException e) { // ignore exception } } removeDatabase(dbFile); } } catch (SQLException e) { e.printStackTrace(); } } } private void processingPostcodes() throws SQLException { PreparedStatement pstat = DataIndexWriter.getUpdateBuildingPostcodePreparedStatement(mapConnection);; pStatements.put(pstat, 0); for (Relation r : postalCodeRelations) { String tag = r.getTag(OSMTagKey.POSTAL_CODE); for (EntityId l : r.getMemberIds()) { pstat.setString(1, tag); pstat.setLong(2, l.getId()); DataIndexWriter.addBatch(pStatements, pstat, BATCH_SIZE); } } if (pStatements.get(pstat) > 0) { pstat.executeBatch(); } pStatements.remove(pstat); } private RTree packRtreeFile(RTree tree, String nonPackFileName, String packFileName) throws IOException { try { assert rtree.Node.MAX < 50 : "It is better for search performance"; //$NON-NLS-1$ tree.flush(); File file = new File(packFileName); if (file.exists()) { file.delete(); } long rootIndex = tree.getFileHdr().getRootIndex(); if (!nodeIsLastSubTree(tree, rootIndex)) { // there is a bug for small files in packing method new Pack().packTree(tree, packFileName); tree.getFileHdr().getFile().close(); file = new File(nonPackFileName); file.delete(); return new RTree(packFileName); } } catch (RTreeException e) { log.error("Error flushing", e); //$NON-NLS-1$ throw new IOException(e); } return tree; } private NewDataExtractionOsmFilter extractOsmToNodesDB(File readFile, IProgress progress, IOsmStorageFilter addFilter) throws FileNotFoundException, IOException, SQLException, SAXException { boolean pbfFile = false; InputStream stream = new BufferedInputStream(new FileInputStream(readFile), 8192*4);; InputStream streamFile = stream; long st = System.currentTimeMillis(); if (readFile.getName().endsWith(".bz2")) { //$NON-NLS-1$ if (stream.read() != 'B' || stream.read() != 'Z') { throw new RuntimeException("The source stream must start with the characters BZ if it is to be read as a BZip2 stream."); //$NON-NLS-1$ } else { stream = new CBZip2InputStream(stream); } } else if (readFile.getName().endsWith(".pbf")) { //$NON-NLS-1$ pbfFile = true; } OsmBaseStorage storage = new OsmBaseStorage(); storage.setSupressWarnings(DataExtractionSettings.getSettings().isSupressWarningsForDuplicatedId()); if (addFilter != null) { storage.getFilters().add(addFilter); } // 1. Loading osm file NewDataExtractionOsmFilter filter = new NewDataExtractionOsmFilter(); try { // 1 init database to store temporary data filter.initDatabase(); storage.getFilters().add(filter); if (pbfFile) { storage.parseOSMPbf(stream, progress, false); } else { storage.parseOSM(stream, progress, streamFile, false); } filter.finishLoading(); dbConn.commit(); if (log.isInfoEnabled()) { log.info("File parsed : " + (System.currentTimeMillis() - st)); //$NON-NLS-1$ } progress.finishTask(); return filter; } finally { if (log.isInfoEnabled()) { log.info("File indexed : " + (System.currentTimeMillis() - st)); //$NON-NLS-1$ } } } private void createDatabaseIndexesStructure() throws SQLException, IOException { // 2.1 create temporary sqlite database to put temporary results to it if (indexMap || indexAddress || indexTransport) { mapFile = new File(workingDir, getMapFileName()); // to save space mapFile.getParentFile().mkdirs(); File tempDBMapFile = new File(workingDir, getTempMapDBFileName()); removeDatabase(tempDBMapFile); mapConnection = getDatabaseConnection(tempDBMapFile.getAbsolutePath()); mapConnection.setAutoCommit(false); } // 2.2 create rtree map if (indexMap) { DataIndexWriter.createMapIndexStructure(mapConnection); mapBinaryStat = DataIndexWriter.createStatementMapBinaryInsert(mapConnection); mapLowLevelBinaryStat = DataIndexWriter.createStatementLowLevelMapBinaryInsert(mapConnection); try { mapTree = new RTree[mapZooms.size()]; for (int i = 0; i < mapZooms.size(); i++) { File file = new File(getRTreeMapIndexNonPackFileName() + i); if (file.exists()) { file.delete(); } mapTree[i] = new RTree(getRTreeMapIndexNonPackFileName() + i); // very slow // mapTree[i].getFileHdr().setBufferPolicy(true); } } catch (RTreeException e) { throw new IOException(e); } pStatements.put(mapBinaryStat, 0); pStatements.put(mapLowLevelBinaryStat, 0); } if (indexAddress) { DataIndexWriter.createAddressIndexStructure(mapConnection); addressCityStat = DataIndexWriter.getCityInsertPreparedStatement(mapConnection); addressStreetStat = DataIndexWriter.getStreetInsertPreparedStatement(mapConnection); addressBuildingStat = DataIndexWriter.getBuildingInsertPreparedStatement(mapConnection); addressStreetNodeStat = DataIndexWriter.getStreetNodeInsertPreparedStatement(mapConnection); addressSearchStreetStat = DataIndexWriter.getSearchStreetPreparedStatement(mapConnection); addressSearchBuildingStat = DataIndexWriter.getSearchBuildingPreparedStatement(mapConnection); addressSearchStreetNodeStat = DataIndexWriter.getStreeNodeSearchPreparedStatement(mapConnection); pStatements.put(addressCityStat, 0); pStatements.put(addressStreetStat, 0); pStatements.put(addressStreetNodeStat, 0); pStatements.put(addressBuildingStat, 0); // put search statements to close them after all pStatements.put(addressSearchBuildingStat, 0); pStatements.put(addressSearchStreetNodeStat, 0); pStatements.put(addressSearchStreetStat, 0); } if (indexPOI) { poiIndexFile = new File(workingDir, getPoiFileName()); // to save space if (poiIndexFile.exists()) { Algoritms.removeAllFiles(poiIndexFile); } poiIndexFile.getParentFile().mkdirs(); // creating nodes db to fast access for all nodes poiConnection = getDatabaseConnection(poiIndexFile.getAbsolutePath(), true); DataIndexWriter.createPoiIndexStructure(poiConnection); poiPreparedStatement = DataIndexWriter.createStatementAmenityInsert(poiConnection); pStatements.put(poiPreparedStatement, 0); poiConnection.setAutoCommit(false); } if (indexTransport) { DataIndexWriter.createTransportIndexStructure(mapConnection); try { File file = new File(getRTreeTransportStopsFileName()); if (file.exists()) { file.delete(); } transportStopsTree = new RTree(file.getAbsolutePath()); } catch (RTreeException e) { throw new IOException(e); } transRouteStat = DataIndexWriter.createStatementTransportRouteInsert(mapConnection); transRouteStopsStat = DataIndexWriter.createStatementTransportRouteStopInsert(mapConnection); transStopsStat = DataIndexWriter.createStatementTransportStopInsert(mapConnection); pStatements.put(transRouteStat, 0); pStatements.put(transRouteStopsStat, 0); pStatements.put(transStopsStat, 0); mapConnection.setAutoCommit(false); } } protected void closePreparedStatements(PreparedStatement... preparedStatements) throws SQLException { for (PreparedStatement p : preparedStatements) { if (p != null) { p.executeBatch(); p.close(); pStatements.remove(p); } } } public static void removeWayNodes(File sqlitedb) throws SQLException { Connection dbConn = DriverManager.getConnection("jdbc:sqlite:" + sqlitedb.getAbsolutePath()); //$NON-NLS-1$ dbConn.setAutoCommit(false); Statement st = dbConn.createStatement(); st.execute("DELETE FROM street_node WHERE 1=1"); //$NON-NLS-1$ st.close(); dbConn.commit(); st = dbConn.createStatement(); if (usingSQLite()) { st.execute("VACUUM"); //$NON-NLS-1$ } st.close(); dbConn.close(); } public static void main(String[] args) throws IOException, SAXException, SQLException { long time = System.currentTimeMillis(); IndexCreator creator = new IndexCreator(new File("/home/victor/projects/OsmAnd/download/384/")); //$NON-NLS-1$ creator.setIndexMap(true); creator.setIndexAddress(true); creator.setIndexPOI(true); creator.setIndexTransport(true); // for NL // creator.setCityAdminLevel("10"); creator.recreateOnlyBinaryFile = false; creator.deleteDatabaseIndexes = true; creator.generateIndexes(new File("/home/victor/projects/OsmAnd/download/384/spain.osm.pbf"), new ConsoleProgressImplementation(1), null, MapZooms.getDefault(), null); // creator.setNodesDBFile(new File("e:/Information/OSM maps/osmand/minsk.tmp.odb")); // creator.generateIndexes(new File("e:/Information/OSM maps/belarus osm/minsk.osm"), new ConsoleProgressImplementation(3), null, MapZooms.getDefault(), null); System.out.println("WHOLE GENERATION TIME : " + (System.currentTimeMillis() - time)); //$NON-NLS-1$ System.out.println("COORDINATES_SIZE " + BinaryMapIndexWriter.COORDINATES_SIZE + " count " + BinaryMapIndexWriter.COORDINATES_COUNT); //$NON-NLS-1$ //$NON-NLS-2$ System.out.println("TYPES_SIZE " + BinaryMapIndexWriter.TYPES_SIZE); //$NON-NLS-1$ System.out.println("ID_SIZE " + BinaryMapIndexWriter.ID_SIZE); //$NON-NLS-1$ System.out.println("- COORD_TYPES_ID SIZE " + (BinaryMapIndexWriter.COORDINATES_SIZE + BinaryMapIndexWriter.TYPES_SIZE + BinaryMapIndexWriter.ID_SIZE)); //$NON-NLS-1$ System.out.println("- MAP_DATA_SIZE " + BinaryMapIndexWriter.MAP_DATA_SIZE); //$NON-NLS-1$ System.out.println("- STRING_TABLE_SIZE " + BinaryMapIndexWriter.STRING_TABLE_SIZE); //$NON-NLS-1$ System.out.println("-- MAP_DATA_AND_STRINGS SIZE " + (BinaryMapIndexWriter.MAP_DATA_SIZE + BinaryMapIndexWriter.STRING_TABLE_SIZE)); //$NON-NLS-1$ } }