package net.osmand.map; import gnu.trove.list.array.TIntArrayList; import net.osmand.ResultMatcher; import net.osmand.binary.BinaryMapDataObject; import net.osmand.binary.BinaryMapIndexReader; import net.osmand.data.QuadRect; import net.osmand.data.QuadTree; import net.osmand.util.MapAlgorithms; import net.osmand.util.MapUtils; import java.io.*; import java.util.*; public class OsmandRegions { private BinaryMapIndexReader reader; Map> countries = new HashMap>(); QuadTree quadTree = null ; Integer downloadNameType = null; Integer prefixType = null; private Integer suffixType; private String fname; public void prepareFile(String fileName) throws IOException { reader = new BinaryMapIndexReader(new RandomAccessFile(fileName, "r")); } public String getDownloadName(BinaryMapDataObject o) { if(downloadNameType == null) { return null; } return o.getNameByType(downloadNameType); } public String getPrefix(BinaryMapDataObject o) { if(prefixType == null) { return null; } return o.getNameByType(prefixType); } public String getSuffix(BinaryMapDataObject o) { if(suffixType == null) { return null; } return o.getNameByType(suffixType); } public boolean isInitialized(){ return reader != null; } public boolean contain(BinaryMapDataObject bo, int tx, int ty) { int t = 0; for (int i = 1; i < bo.getPointsLength(); i++) { int fx = MapAlgorithms.ray_intersect_x(bo.getPoint31XTile(i - 1), bo.getPoint31YTile(i - 1), bo.getPoint31XTile(i), bo.getPoint31YTile(i), ty); if (Integer.MIN_VALUE != fx && tx >= fx) { t++; } } return t % 2 == 1; } private List getCountries(int tile31x, int tile31y) { HashSet set = new HashSet(quadTree.queryInBox(new QuadRect(tile31x, tile31y, tile31x, tile31y), new ArrayList())); List result = new ArrayList(); Iterator it = set.iterator(); while (it.hasNext()) { String cname = it.next(); BinaryMapDataObject container = null; int count = 0; for (BinaryMapDataObject bo : countries.get(cname)) { if (contain(bo, tile31x, tile31y)) { count++; container = bo; break; } } if (count % 2 == 1) { result.add(container); } } return result; } public List query(final int tile31x, final int tile31y) throws IOException { if(quadTree != null) { return getCountries(tile31x, tile31y); } return queryNoInit(tile31x, tile31y); } private List queryNoInit(final int tile31x, final int tile31y) throws IOException { final List result = new ArrayList(); BinaryMapIndexReader.SearchRequest sr = BinaryMapIndexReader.buildSearchRequest(tile31x, tile31x, tile31y, tile31y, 5, new BinaryMapIndexReader.SearchFilter() { @Override public boolean accept(TIntArrayList types, BinaryMapIndexReader.MapIndex index) { return true; } }, new ResultMatcher() { @Override public boolean publish(BinaryMapDataObject object) { if (object.getPointsLength() < 1) { return false; } initTypes(object); if (contain(object, tile31x, tile31y)) { result.add(object); } return false; } @Override public boolean isCancelled() { return false; } } ); reader.searchMapIndex(sr); return result; } public List queryBbox(int lx, int rx, int ty, int by) throws IOException { final List result = new ArrayList(); BinaryMapIndexReader.SearchRequest sr = BinaryMapIndexReader.buildSearchRequest(lx, rx, ty, by, 5, new BinaryMapIndexReader.SearchFilter() { @Override public boolean accept(TIntArrayList types, BinaryMapIndexReader.MapIndex index) { return true; } }, new ResultMatcher() { @Override public boolean publish(BinaryMapDataObject object) { if (object.getPointsLength() < 1) { return false; } initTypes(object); result.add(object); return false; } @Override public boolean isCancelled() { return false; } } ); reader.searchMapIndex(sr); return result; } public void cacheAllCountries() throws IOException { quadTree = new QuadTree(new QuadRect(0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE), 8, 0.55f); BinaryMapIndexReader.SearchRequest sr = BinaryMapIndexReader.buildSearchRequest(0, Integer.MAX_VALUE, 0, Integer.MAX_VALUE, 5, new BinaryMapIndexReader.SearchFilter() { @Override public boolean accept(TIntArrayList types, BinaryMapIndexReader.MapIndex index) { return true; } }, new ResultMatcher() { @Override public boolean publish(BinaryMapDataObject object) { if (object.getPointsLength() < 1) { return false; } initTypes(object); String nm = object.getNameByType(downloadNameType); if (!countries.containsKey(nm)) { LinkedList ls = new LinkedList(); countries.put(nm, ls); ls.add(object); } else { countries.get(nm).add(object); } int maxx = object.getPoint31XTile(0); int maxy = object.getPoint31YTile(0); int minx = maxx; int miny = maxy; for (int i = 1; i < object.getPointsLength(); i++) { int x = object.getPoint31XTile(i); int y = object.getPoint31YTile(i); if (y < miny) { miny = y; } else if (y > maxy) { maxy = y; } if (x < minx) { minx = x; } else if (x > maxx) { maxx = x; } } quadTree.insert(nm, new QuadRect(minx, miny, maxx, maxy)); return false; } @Override public boolean isCancelled() { return false; } } ); reader.searchMapIndex(sr); } private void initTypes(BinaryMapDataObject object) { if (downloadNameType == null) { downloadNameType = object.getMapIndex().getRule("download_name", null); prefixType = object.getMapIndex().getRule("region_prefix", null); suffixType = object.getMapIndex().getRule("region_suffix", null); if (downloadNameType == null) { throw new IllegalStateException(); } } } private static void testCountry(OsmandRegions or, double lat, double lon, String... test) throws IOException { long t = System.currentTimeMillis(); List cs = or.query(MapUtils.get31TileNumberX(lon), MapUtils.get31TileNumberY(lat)); Set expected = new TreeSet(Arrays.asList(test)); Set found = new TreeSet(); for(BinaryMapDataObject b : cs) { found.add(b.getName()); } if(!found.equals(expected)) { throw new IllegalStateException(" Expected " + expected + " but was " + found); } System.out.println("Found " + expected + " in " + (System.currentTimeMillis() - t) + " ms"); } public static void main(String[] args) throws IOException { OsmandRegions or = new OsmandRegions(); or.prepareFile("/home/victor/projects/osmand/osm-gen/Osmand_regions.obf"); // long t = System.currentTimeMillis(); // or.cacheAllCountries(); // System.out.println("Init " + (System.currentTimeMillis() - t)); //testCountry(or, 15.8, 23.09, "chad"); testCountry(or, 52.10, 4.92, "netherlands"); testCountry(or, 52.15, 7.50, "nordrhein-westfalen"); testCountry(or, 40.0760, 9.2807, "italy"); testCountry(or, 28.8056, 29.9858, "africa", "egypt" ); testCountry(or, 35.7521, 139.7887, "japan"); testCountry(or, 46.5145, 102.2580, "mongolia"); testCountry(or, 62.54, 43.36, "arkhangelsk", "northwestern-federal-district"); } }