OsmAnd/OsmAnd-java/src/main/java/net/osmand/map/OsmandRegions.java
2020-01-23 18:31:04 +02:00

775 lines
25 KiB
Java

package net.osmand.map;
import net.osmand.OsmAndCollator;
import net.osmand.PlatformUtil;
import net.osmand.ResultMatcher;
import net.osmand.binary.BinaryMapDataObject;
import net.osmand.binary.BinaryMapIndexReader;
import net.osmand.binary.BinaryMapIndexReader.MapIndex;
import net.osmand.binary.BinaryMapIndexReader.TagValuePair;
import net.osmand.data.LatLon;
import net.osmand.data.QuadRect;
import net.osmand.data.QuadTree;
import net.osmand.util.Algorithms;
import net.osmand.util.MapAlgorithms;
import net.osmand.util.MapUtils;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import gnu.trove.iterator.TIntObjectIterator;
import gnu.trove.list.array.TIntArrayList;
public class OsmandRegions {
public static final String MAP_TYPE = "region_map";
public static final String FIELD_DOWNLOAD_NAME = "download_name";
public static final String FIELD_NAME = "name";
public static final String FIELD_NAME_EN = "name:en";
public static final String FIELD_REGION_PARENT_NAME = "region_parent_name";
public static final String FIELD_REGION_FULL_NAME = "region_full_name";
public static final String FIELD_LANG = "region_lang";
public static final String FIELD_METRIC = "region_metric";
public static final String FIELD_ROAD_SIGNS = "region_road_signs";
public static final String FIELD_LEFT_HAND_DRIVING = "region_left_hand_navigation";
public static final String FIELD_WIKI_LINK = "region_wiki_link";
public static final String FIELD_POPULATION = "region_population";
private BinaryMapIndexReader reader;
private String locale = "en";
private static final org.apache.commons.logging.Log LOG = PlatformUtil.getLog(OsmandRegions.class);
WorldRegion worldRegion = new WorldRegion(WorldRegion.WORLD);
Map<String, WorldRegion> fullNamesToRegionData = new HashMap<String, WorldRegion>();
Map<String, String> downloadNamesToFullNames = new HashMap<String, String>();
Map<String, LinkedList<BinaryMapDataObject>> countriesByDownloadName = new HashMap<String, LinkedList<BinaryMapDataObject>>();
QuadTree<String> quadTree;
MapIndexFields mapIndexFields;
RegionTranslation translator;
private class MapIndexFields {
Integer parentFullName = null;
Integer fullNameType = null;
Integer downloadNameType = null;
Integer nameEnType = null;
Integer nameType = null;
Integer nameLocaleType = null;
Integer langType = null;
Integer metricType = null;
Integer leftHandDrivingType = null;
Integer roadSignsType = null;
Integer wikiLinkType = null;
Integer populationType = null;
public String get(Integer tp, BinaryMapDataObject object) {
if (tp == null) {
return null;
}
return object.getNameByType(tp);
}
}
public void setTranslator(RegionTranslation translator) {
this.translator = translator;
}
public BinaryMapIndexReader prepareFile() throws IOException {
File regions = new File("regions.ocbf");
// internal version could be updated
// if (!regions.exists()) {
InputStream is = OsmandRegions.class.getResourceAsStream("regions.ocbf");
FileOutputStream fous = new FileOutputStream(regions);
Algorithms.streamCopy(is, fous);
fous.close();
// }
return prepareFile(regions.getAbsolutePath());
}
public BinaryMapIndexReader prepareFile(String fileName) throws IOException {
reader = new BinaryMapIndexReader(new RandomAccessFile(fileName, "r"), new File(fileName));
// final Collator clt = OsmAndCollator.primaryCollator();
final Map<String, String> parentRelations = new LinkedHashMap<String, String>();
final ResultMatcher<BinaryMapDataObject> resultMatcher = new ResultMatcher<BinaryMapDataObject>() {
@Override
public boolean publish(BinaryMapDataObject object) {
initTypes(object);
int[] types = object.getTypes();
for (int i = 0; i < types.length; i++) {
TagValuePair tp = object.getMapIndex().decodeType(types[i]);
if ("boundary".equals(tp.value)) {
return false;
}
}
WorldRegion rd = initRegionData(parentRelations, object);
if (rd == null) {
return false;
}
if (rd.regionDownloadName != null) {
downloadNamesToFullNames.put(rd.regionDownloadName, rd.regionFullName);
}
fullNamesToRegionData.put(rd.regionFullName, rd);
return false;
}
@Override
public boolean isCancelled() {
return false;
}
};
iterateOverAllObjects(resultMatcher);
// post process download names
for (Map.Entry<String, String> e : parentRelations.entrySet()) {
String fullName = e.getKey();
String parentFullName = e.getValue();
// String parentParentFulName = parentRelations.get(parentFullName); // could be used for japan/russia
WorldRegion rd = fullNamesToRegionData.get(fullName);
WorldRegion parent = fullNamesToRegionData.get(parentFullName);
if (parent != null && rd != null) {
parent.addSubregion(rd);
}
}
structureWorldRegions(new ArrayList<WorldRegion>(fullNamesToRegionData.values()));
return reader;
}
public boolean containsCountry(String name) {
return countriesByDownloadName.containsKey(name);
}
public String getLocaleName(String downloadName, boolean includingParent) {
final String lc = downloadName.toLowerCase();
if (downloadNamesToFullNames.containsKey(lc)) {
String fullName = downloadNamesToFullNames.get(lc);
return getLocaleNameByFullName(fullName, includingParent);
}
return downloadName.replace('_', ' ');
}
public String getLocaleNameByFullName(String fullName, boolean includingParent) {
WorldRegion rd = fullNamesToRegionData.get(fullName);
if (rd == null) {
return fullName.replace('_', ' ');
}
if (includingParent && rd.getSuperregion() != null && rd.getSuperregion().getSuperregion() != null) {
WorldRegion parentParent = rd.getSuperregion().getSuperregion();
WorldRegion parent = rd.getSuperregion();
if (parentParent.getRegionId().equals(WorldRegion.WORLD) &&
!parent.getRegionId().equals(WorldRegion.RUSSIA_REGION_ID)) {
return rd.getLocaleName();
}
if (parentParent.getRegionId().equals(WorldRegion.RUSSIA_REGION_ID)) {
return parentParent.getLocaleName() + " " + rd.getLocaleName();
}
if (parentParent.getRegionId().equals(WorldRegion.JAPAN_REGION_ID)) {
return parentParent.getLocaleName() + " " + rd.getLocaleName();
}
return parent.getLocaleName() + " " + rd.getLocaleName();
} else {
return rd.getLocaleName();
}
}
public WorldRegion getWorldRegion() {
return worldRegion;
}
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;
}
public boolean intersect(BinaryMapDataObject bo, int lx, int ty, int rx, int by) {
// 1. polygon in object
if (contain(bo, lx, ty)) {
return true;
}
// 2. object in polygon
if (bo.getPointsLength() == 0) {
return false;
}
if (bo.getPoint31XTile(0) >= lx && bo.getPoint31XTile(0) <= rx &&
bo.getPoint31YTile(0) >= ty && bo.getPoint31YTile(0) <= by) {
return true;
}
// 3. find intersection
for (int i = 1; i < bo.getPointsLength(); i++) {
int px = bo.getPoint31XTile(i - 1);
int x = bo.getPoint31XTile(i);
int py = bo.getPoint31YTile(i - 1);
int y = bo.getPoint31YTile(i);
if (x < lx && px < lx) {
continue;
} else if (x > rx && px > rx) {
continue;
} else if (y > by && py > by) {
continue;
} else if (y < ty && py < ty) {
continue;
}
long in = MapAlgorithms.calculateIntersection(px, py, x, y, lx, rx, by, ty);
if (in != -1) {
return true;
}
}
return false;
}
public static double getArea(BinaryMapDataObject bo) {
double area = 0.0;
if (bo.getPointsLength() > 0) {
for (int i = 1; i < bo.getPointsLength(); i++) {
double ax = bo.getPoint31XTile(i - 1);
double bx = bo.getPoint31XTile(i);
double ay = bo.getPoint31YTile(i - 1);
double by = bo.getPoint31YTile(i);
area += (bx + ax) * (by - ay) / 1.631E10;
}
}
return Math.abs(area);
}
private List<BinaryMapDataObject> getCountries(int lx, int rx, int ty, int by, final boolean checkCenter) throws IOException {
HashSet<String> set = new HashSet<String>(quadTree.queryInBox(new QuadRect(lx, ty, rx, by),
new ArrayList<String>()));
List<BinaryMapDataObject> result = new ArrayList<BinaryMapDataObject>();
Iterator<String> it = set.iterator();
int mx = lx / 2 + rx / 2;
int my = ty / 2 + by / 2;
while (it.hasNext()) {
String cname = it.next();
BinaryMapDataObject container = null;
int count = 0;
for (BinaryMapDataObject bo : countriesByDownloadName.get(cname)) {
if (!checkCenter || contain(bo, mx, my)) {
count++;
container = bo;
break;
}
}
if (count % 2 == 1) {
result.add(container);
}
}
return result;
}
public String getCountryName(LatLon ll) {
double lat = ll.getLatitude();
double lon = ll.getLongitude();
int y = MapUtils.get31TileNumberY(lat);
int x = MapUtils.get31TileNumberX(lon);
try {
List<BinaryMapDataObject> list = query(x, y);
for(BinaryMapDataObject o : list) {
if(contain(o, x, y)) {
String name = mapIndexFields.get(mapIndexFields.nameType, o);
if(name != null) {
return name;
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public List<BinaryMapDataObject> query(int lx, int rx, int ty, int by) throws IOException {
return query(lx, rx, ty, by, true);
}
public List<BinaryMapDataObject> query(int lx, int rx, int ty, int by, boolean checkCenter) throws IOException {
if (quadTree != null) {
return getCountries(lx, rx, ty, by, checkCenter);
}
return queryBboxNoInit(lx, rx, ty, by, checkCenter);
}
public List<BinaryMapDataObject> query(final int tile31x, final int tile31y) throws IOException {
if (quadTree != null) {
return getCountries(tile31x, tile31x, tile31y, tile31y, true);
}
return queryBboxNoInit(tile31x, tile31x, tile31y, tile31y, true);
}
private synchronized List<BinaryMapDataObject> queryBboxNoInit(int lx, int rx, int ty, int by, final boolean checkCenter) throws IOException {
final List<BinaryMapDataObject> result = new ArrayList<BinaryMapDataObject>();
final int mx = lx / 2 + rx / 2;
final int my = ty / 2 + by / 2;
BinaryMapIndexReader.SearchRequest<BinaryMapDataObject> sr = BinaryMapIndexReader.buildSearchRequest(lx, rx, ty, by,
5, new BinaryMapIndexReader.SearchFilter() {
@Override
public boolean accept(TIntArrayList types, BinaryMapIndexReader.MapIndex index) {
return true;
}
}, new ResultMatcher<BinaryMapDataObject>() {
@Override
public boolean publish(BinaryMapDataObject object) {
if (object.getPointsLength() < 1) {
return false;
}
initTypes(object);
if (!checkCenter || contain(object, mx, my)) {
result.add(object);
}
return false;
}
@Override
public boolean isCancelled() {
return false;
}
}
);
sr.log = false;
if (reader != null) {
reader.searchMapIndex(sr);
} else {
throw new IOException("Reader == null");
}
return result;
}
public void setLocale(String locale) {
this.locale = locale;
}
public WorldRegion getRegionData(String fullname) {
return fullNamesToRegionData.get(fullname);
}
public WorldRegion getRegionDataByDownloadName(String downloadName) {
if (downloadName == null) {
return null;
} else {
return getRegionData(downloadNamesToFullNames.get(downloadName.toLowerCase()));
}
}
public String getDownloadName(BinaryMapDataObject o) {
return mapIndexFields.get(mapIndexFields.downloadNameType, o);
}
public String getFullName(BinaryMapDataObject o) {
return mapIndexFields.get(mapIndexFields.fullNameType, o);
}
public List<WorldRegion> getAllRegionData() {
return new ArrayList<WorldRegion>(fullNamesToRegionData.values());
}
private WorldRegion initRegionData(final Map<String, String> parentRelations, BinaryMapDataObject object) {
String regionDownloadName = mapIndexFields.get(mapIndexFields.downloadNameType, object);
String regionFullName = mapIndexFields.get(mapIndexFields.fullNameType, object);
if (Algorithms.isEmpty(regionFullName)) {
return null;
}
WorldRegion rd = new WorldRegion(regionFullName, regionDownloadName);
double cx = 0;
double cy = 0;
for (int i = 0; i < object.getPointsLength(); i++) {
cx += object.getPoint31XTile(i);
cy += object.getPoint31YTile(i);
}
if (object.getPointsLength() > 0) {
cx /= object.getPointsLength();
cy /= object.getPointsLength();
rd.regionCenter = new LatLon(MapUtils.get31LatitudeY((int) cy), MapUtils.get31LongitudeX((int) cx));
}
rd.regionParentFullName = mapIndexFields.get(mapIndexFields.parentFullName, object);
if (!Algorithms.isEmpty(rd.regionParentFullName)) {
parentRelations.put(rd.regionFullName, rd.regionParentFullName);
}
rd.regionName = mapIndexFields.get(mapIndexFields.nameType, object);
rd.regionNameLocale = mapIndexFields.get(mapIndexFields.nameLocaleType, object);
rd.regionNameEn = mapIndexFields.get(mapIndexFields.nameEnType, object);
rd.params.regionLang = mapIndexFields.get(mapIndexFields.langType, object);
rd.params.regionLeftHandDriving = mapIndexFields.get(mapIndexFields.leftHandDrivingType, object);
rd.params.regionMetric = mapIndexFields.get(mapIndexFields.metricType, object);
rd.params.regionRoadSigns = mapIndexFields.get(mapIndexFields.roadSignsType, object);
rd.params.wikiLink = mapIndexFields.get(mapIndexFields.wikiLinkType, object);
rd.params.population = mapIndexFields.get(mapIndexFields.populationType, object);
rd.regionSearchText = getSearchIndex(object);
rd.regionMapDownload = isDownloadOfType(object, MAP_TYPE);
return rd;
}
private String getSearchIndex(BinaryMapDataObject object) {
MapIndex mi = object.getMapIndex();
TIntObjectIterator<String> it = object.getObjectNames().iterator();
StringBuilder ind = new StringBuilder();
while (it.hasNext()) {
it.advance();
TagValuePair tp = mi.decodeType(it.key());
if (tp.tag.startsWith("name") || tp.tag.equals("key_name")) {
final String vl = it.value().toLowerCase();
// if (!CollatorStringMatcher.ccontains(clt, ind.toString(), vl)) {
if (ind.indexOf(vl) == -1) {
ind.append(" ").append(vl);
}
}
}
return ind.toString();
}
public boolean isDownloadOfType(BinaryMapDataObject object, String type) {
int[] addtypes = object.getAdditionalTypes();
for (int i = 0; i < addtypes.length; i++) {
TagValuePair tp = object.getMapIndex().decodeType(addtypes[i]);
if (type.equals(tp.tag) && "yes".equals(tp.value)) {
return true;
}
}
return false;
}
public Map<String, LinkedList<BinaryMapDataObject>> cacheAllCountries() throws IOException {
quadTree = new QuadTree<String>(new QuadRect(0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE),
8, 0.55f);
final ResultMatcher<BinaryMapDataObject> resultMatcher = new ResultMatcher<BinaryMapDataObject>() {
// int c = 0;
@Override
public boolean publish(BinaryMapDataObject object) {
if (object.getPointsLength() < 1) {
return false;
}
initTypes(object);
String nm = mapIndexFields.get(mapIndexFields.downloadNameType, object);
if (!countriesByDownloadName.containsKey(nm)) {
LinkedList<BinaryMapDataObject> ls = new LinkedList<BinaryMapDataObject>();
countriesByDownloadName.put(nm, ls);
ls.add(object);
} else {
countriesByDownloadName.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;
}
};
iterateOverAllObjects(resultMatcher);
return countriesByDownloadName;
}
private synchronized void iterateOverAllObjects(final ResultMatcher<BinaryMapDataObject> resultMatcher) throws IOException {
BinaryMapIndexReader.SearchRequest<BinaryMapDataObject> 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;
}
}, resultMatcher);
if (reader != null) {
reader.searchMapIndex(sr);
}
}
private void initTypes(BinaryMapDataObject object) {
if (mapIndexFields == null) {
mapIndexFields = new MapIndexFields();
// mapIndexFields.mapIndex = object.getMapIndex();
mapIndexFields.downloadNameType = object.getMapIndex().getRule(FIELD_DOWNLOAD_NAME, null);
mapIndexFields.nameType = object.getMapIndex().getRule(FIELD_NAME, null);
mapIndexFields.nameEnType = object.getMapIndex().getRule(FIELD_NAME_EN, null);
mapIndexFields.nameLocaleType = object.getMapIndex().getRule(FIELD_NAME + ":" + locale, null);
mapIndexFields.parentFullName = object.getMapIndex().getRule(FIELD_REGION_PARENT_NAME, null);
mapIndexFields.fullNameType = object.getMapIndex().getRule(FIELD_REGION_FULL_NAME, null);
mapIndexFields.langType = object.getMapIndex().getRule(FIELD_LANG, null);
mapIndexFields.metricType = object.getMapIndex().getRule(FIELD_METRIC, null);
mapIndexFields.leftHandDrivingType = object.getMapIndex().getRule(FIELD_LEFT_HAND_DRIVING, null);
mapIndexFields.roadSignsType = object.getMapIndex().getRule(FIELD_ROAD_SIGNS, null);
mapIndexFields.wikiLinkType = object.getMapIndex().getRule(FIELD_WIKI_LINK, null);
mapIndexFields.populationType = object.getMapIndex().getRule(FIELD_POPULATION, null);
}
}
private static void testCountry(OsmandRegions or, double lat, double lon, String... test) throws IOException {
long t = System.currentTimeMillis();
List<BinaryMapDataObject> cs = or.query(MapUtils.get31TileNumberX(lon), MapUtils.get31TileNumberY(lat));
Set<String> expected = new TreeSet<String>(Arrays.asList(test));
Set<String> found = new TreeSet<String>();
for (BinaryMapDataObject b : cs) {
String nm = b.getNameByType(or.mapIndexFields.nameEnType);
if (nm == null) {
nm = b.getName();
}
if (or.isDownloadOfType(b, MAP_TYPE)) {
found.add(nm.toLowerCase());
}
}
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("/Users/victorshcherb/osmand/repos/resources/countries-info/regions.ocbf");
LinkedList<WorldRegion> lst = new LinkedList<WorldRegion>();
lst.add(or.getWorldRegion());
// int i =0;
while (!lst.isEmpty()) {
WorldRegion wd = lst.pollFirst();
System.out.println((wd.superregion == null ? "" : wd.superregion.getLocaleName()) + " "
+ wd.getLocaleName() + " " + wd.getRegionDownloadName());
// if(i++ <=5)
// lst.addAll(wd.getSubregions());
}
or.cacheAllCountries();
// 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, "the netherlands", "utrecht");
testCountry(or, 52.15, 7.50, "north rhine-westphalia");
testCountry(or, 28.8056, 29.9858, "egypt");
// testCountry(or, 40.0760, 9.2807, "italy", "sardinia");
testCountry(or, 35.7521, 139.7887, "japan");
testCountry(or, 46.5145, 102.2580, "mongolia");
testCountry(or, 62.54, 43.36, "arkhangelsk oblast", "northwestern federal district");
}
public interface RegionTranslation {
public String getTranslation(String id);
}
private void initWorldRegion(WorldRegion world, String id) {
WorldRegion rg = new WorldRegion(id);
rg.regionParentFullName = world.regionFullName;
if (translator != null) {
rg.regionName = translator.getTranslation(id);
}
world.addSubregion(rg);
}
public void structureWorldRegions(List<WorldRegion> loadedItems) {
if (loadedItems.size() == 0) {
return;
}
WorldRegion world = new WorldRegion(WorldRegion.WORLD);
initWorldRegion(world, WorldRegion.ANTARCTICA_REGION_ID);
initWorldRegion(world, WorldRegion.AFRICA_REGION_ID);
initWorldRegion(world, WorldRegion.ASIA_REGION_ID);
initWorldRegion(world, WorldRegion.CENTRAL_AMERICA_REGION_ID);
initWorldRegion(world, WorldRegion.EUROPE_REGION_ID);
initWorldRegion(world, WorldRegion.NORTH_AMERICA_REGION_ID);
initWorldRegion(world, WorldRegion.RUSSIA_REGION_ID);
initWorldRegion(world, WorldRegion.SOUTH_AMERICA_REGION_ID);
initWorldRegion(world, WorldRegion.AUSTRALIA_AND_OCEANIA_REGION_ID);
Iterator<WorldRegion> it = loadedItems.iterator();
while (it.hasNext()) {
WorldRegion region = it.next();
if (region.superregion == null) {
boolean found = false;
for (WorldRegion worldSubregion : world.subregions) {
if (worldSubregion.getRegionId().equalsIgnoreCase(region.regionFullName)) {
for (WorldRegion rg : region.subregions) {
worldSubregion.addSubregion(rg);
}
found = true;
break;
}
}
if (found) {
it.remove();
} else if (region.getRegionId().contains("basemap")) {
it.remove();
}
} else {
it.remove();
}
}
Comparator<WorldRegion> nameComparator = new Comparator<WorldRegion>() {
final net.osmand.Collator collator = OsmAndCollator.primaryCollator();
@Override
public int compare(WorldRegion w1, WorldRegion w2) {
return collator.compare(w1.getLocaleName(), w2.getLocaleName());
}
};
sortSubregions(world, nameComparator);
this.worldRegion = world;
if (loadedItems.size() > 0) {
LOG.warn("Found orphaned regions: " + loadedItems.size());
for (WorldRegion regionId : loadedItems) {
LOG.warn("FullName = " + regionId.regionFullName +
" parent=" + regionId.regionParentFullName);
}
}
}
private void sortSubregions(WorldRegion region, Comparator<WorldRegion> comparator) {
Collections.sort(region.subregions, comparator);
for (WorldRegion r : region.subregions) {
if (r.subregions.size() > 0) {
sortSubregions(r, comparator);
}
}
}
public List<WorldRegion> getWoldRegionsAt(LatLon latLon) throws IOException {
List<WorldRegion> result = new ArrayList<>();
List<BinaryMapDataObject> mapDataObjects = getBinaryMapDataObjectsAt(latLon);
for (BinaryMapDataObject obj : mapDataObjects) {
String fullName = getFullName(obj);
if (fullName != null) {
WorldRegion reg = getRegionData(fullName);
if (reg != null) {
result.add(reg);
}
}
}
return result;
}
public BinaryMapDataObject getSmallestBinaryMapDataObjectAt(LatLon latLon) throws IOException {
List<BinaryMapDataObject> mapDataObjects = getBinaryMapDataObjectsAt(latLon);
return getSmallestBinaryMapDataObjectAt(mapDataObjects);
}
public BinaryMapDataObject getSmallestBinaryMapDataObjectAt(List<BinaryMapDataObject> mapDataObjects) {
BinaryMapDataObject res = null;
double smallestArea = -1;
for (BinaryMapDataObject o : mapDataObjects) {
double area = OsmandRegions.getArea(o);
if (smallestArea == -1) {
smallestArea = area;
res = o;
} else if (area < smallestArea) {
smallestArea = area;
res = o;
}
}
return res;
}
private List<BinaryMapDataObject> getBinaryMapDataObjectsAt(LatLon latLon) throws IOException {
int point31x = MapUtils.get31TileNumberX(latLon.getLongitude());
int point31y = MapUtils.get31TileNumberY(latLon.getLatitude());
List<BinaryMapDataObject> mapDataObjects;
try {
mapDataObjects = queryBboxNoInit(point31x, point31x, point31y, point31y, true);
} catch (IOException e) {
throw new IOException("Error while calling queryBbox");
}
if (mapDataObjects != null) {
Iterator<BinaryMapDataObject> it = mapDataObjects.iterator();
while (it.hasNext()) {
BinaryMapDataObject o = it.next();
if (o.getTypes() != null) {
WorldRegion downloadRegion = getRegionData(getFullName(o));
if ( downloadRegion == null
|| !downloadRegion.isRegionMapDownload()
|| !contain(o, point31x, point31y)) {
it.remove();
}
}
}
}
return mapDataObjects;
}
public List<String> getRegionsToDownload(double lat, double lon, List<String> keyNames) throws IOException {
keyNames.clear();
int x31 = MapUtils.get31TileNumberX(lon);
int y31 = MapUtils.get31TileNumberY(lat);
List<BinaryMapDataObject> cs = query(x31, y31);
for (BinaryMapDataObject b : cs) {
if (contain(b, x31, y31)) {
String downloadName = getDownloadName(b);
if(!Algorithms.isEmpty(downloadName)) {
keyNames.add(downloadName);
}
}
}
return keyNames;
}
}