diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/DataAvailability.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/DataAvailability.java new file mode 100644 index 0000000..f967d0e --- /dev/null +++ b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/DataAvailability.java @@ -0,0 +1,246 @@ +package org.openstreetmap.josm.plugins.mapwithai.backend; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.TreeMap; +import java.util.stream.Collectors; + +import javax.json.Json; +import javax.json.JsonArray; +import javax.json.JsonException; +import javax.json.JsonObject; +import javax.json.JsonValue; +import javax.json.stream.JsonParser; + +import org.openstreetmap.josm.data.coor.LatLon; +import org.openstreetmap.josm.data.osm.BBox; +import org.openstreetmap.josm.io.CachedFile; +import org.openstreetmap.josm.plugins.mapwithai.backend.commands.conflation.DataUrl; +import org.openstreetmap.josm.tools.Logging; +import org.openstreetmap.josm.tools.Territories; + +public class DataAvailability { + /** This points to a list of default sources that can be used with MapWithAI */ + public static final String DEFAULT_SERVER_URL = "https://gokaart.gitlab.io/JOSM_MapWithAI/json/sources.json"; + /** A map of tag -> message of possible data types */ + protected static final Map POSSIBLE_DATA_POINTS = new TreeMap<>(); + + private static final String PROVIDES = "provides"; + + /** + * Map<Source, + * Map<(url|parameters|countries|license|osm_compatible|permission_url), + * Object>> + */ + protected static final Map> COUNTRY_MAP = new HashMap<>(); + /** + * This holds classes that can give availability of data for a specific service + */ + private static final List> DATA_SOURCES = new ArrayList<>(); + static { + DATA_SOURCES.add(MapWithAIAvailability.class); + } + /** + * A map of countries to a map of available types + * ({@code Map>} + */ + protected static final Map> COUNTRIES = new HashMap<>(); + + private static class InstanceHelper { + static DataAvailability instance = new DataAvailability(); + } + + protected DataAvailability() { + if (DataAvailability.class.equals(this.getClass())) { + initialize(); + } + } + + /** + * Initialize the class + */ + private static void initialize() { + try (CachedFile jsonFile = new CachedFile(DEFAULT_SERVER_URL); + JsonParser jsonParser = Json.createParser(jsonFile.getContentReader());) { + jsonFile.setMaxAge(604_800); + jsonParser.next(); + JsonObject jsonObject = jsonParser.getObject(); + boolean initializePreferences = MapWithAIPreferenceHelper.getMapWithAIUrl().isEmpty(); + for (Entry entry : jsonObject.entrySet()) { + if (initializePreferences) { + DataUrl url = new DataUrl(entry.getKey(), entry.getValue().asJsonObject().getString("url", ""), + false, entry.getValue().asJsonObject().getJsonArray("parameters").toString()); + MapWithAIPreferenceHelper.setMapWithAIUrl(url, false, true); + } + Logging.error("{0}: {1}", entry.getKey(), entry.getValue()); + if (JsonValue.ValueType.OBJECT.equals(entry.getValue().getValueType()) + && entry.getValue().asJsonObject().containsKey("countries")) { + JsonValue countries = entry.getValue().asJsonObject().get("countries"); + parseCountries(COUNTRIES, countries, entry.getValue()); + } + } + } catch (JsonException | IOException e) { + Logging.debug(e); + } + + DATA_SOURCES.forEach(clazz -> { + try { + clazz.getConstructor().newInstance(); + } catch (InstantiationException | IllegalAccessException | IllegalArgumentException + | InvocationTargetException | NoSuchMethodException | SecurityException e) { + Logging.debug(e); + } + }); + } + + /** + * Parse a JSON Value for country information + * + * @param countriesMap The countries map (will be modified) + * @param countries The country object (JsonObject) + * @param information The information for the source + */ + private static void parseCountries(Map> countriesMap, JsonValue countries, + JsonValue information) { + if (JsonValue.ValueType.ARRAY.equals(countries.getValueType())) { + parseCountriesArray(countriesMap, countries.asJsonArray(), information); + } else if (JsonValue.ValueType.OBJECT.equals(countries.getValueType())) { + parseCountriesObject(countriesMap, countries.asJsonObject(), information); + } + } + + /** + * Parse a JsonObject for countries + * + * @param countriesMap The countries map (will be modified) + * @param countryObject The country object (JsonObject) + * @param information The information for the source + */ + private static void parseCountriesObject(Map> countriesMap, JsonObject countryObject, + JsonValue information) { + for (Entry entry : countryObject.entrySet()) { + Map providesMap = countriesMap.getOrDefault(entry.getKey(), new TreeMap<>()); + countriesMap.putIfAbsent(entry.getKey(), providesMap); + if (JsonValue.ValueType.ARRAY.equals(entry.getValue().getValueType())) { + for (String provide : entry.getValue().asJsonArray().parallelStream() + .filter(c -> JsonValue.ValueType.STRING.equals(c.getValueType())).map(JsonValue::toString) + .map(DataAvailability::stripQuotes).collect(Collectors.toList())) { + providesMap.put(provide, true); + } + } + if (providesMap.isEmpty() && JsonValue.ValueType.OBJECT.equals(information.getValueType()) + && information.asJsonObject().containsKey(PROVIDES) + && JsonValue.ValueType.ARRAY.equals(information.asJsonObject().get(PROVIDES).getValueType())) { + for (String provide : information.asJsonObject().getJsonArray(PROVIDES).stream() + .filter(val -> JsonValue.ValueType.STRING.equals(val.getValueType())).map(JsonValue::toString) + .map(DataAvailability::stripQuotes).collect(Collectors.toList())) { + providesMap.put(provide, Boolean.TRUE); + } + } + } + } + + /** + * Parse a JsonArray for countries + * + * @param countriesMap The countries map (will be modified) + * @param countryArray The country array (JsonArray) + * @param information The information for the source + */ + private static void parseCountriesArray(Map> countriesMap, JsonArray countryArray, + JsonValue information) { + List array = countryArray.parallelStream() + .filter(c -> JsonValue.ValueType.STRING.equals(c.getValueType())).map(JsonValue::toString) + .map(DataAvailability::stripQuotes).collect(Collectors.toList()); + if (JsonValue.ValueType.OBJECT.equals(information.getValueType()) + && information.asJsonObject().containsKey(PROVIDES)) { + List provides = information.asJsonObject().getJsonArray(PROVIDES).parallelStream() + .filter(p -> JsonValue.ValueType.STRING.equals(p.getValueType())).map(JsonValue::toString) + .map(DataAvailability::stripQuotes).collect(Collectors.toList()); + for (String countryValue : array) { + for (String provide : provides) { + Map providesMap = countriesMap.getOrDefault(countryValue, new TreeMap<>()); + countriesMap.putIfAbsent(countryValue, providesMap); + providesMap.put(provide, true); + } + } + } + } + + /** + * Strip double quotes (") from a string + * + * @param string A string that may have quotes at the beginning, the end, or + * both + * @return A string that doesn't have quotes at the beginning or end + */ + public static String stripQuotes(String string) { + return string.replaceAll("(^\"|\"$)", ""); + } + + /** + * @return the unique instance + */ + public static DataAvailability getInstance() { + if (InstanceHelper.instance == null || COUNTRIES.isEmpty()) { + InstanceHelper.instance = new DataAvailability(); + } + return InstanceHelper.instance; + } + + /** + * @param bbox An area that may have data + * @return True if one of the corners of the {@code bbox} is in a country with + * available data. + */ + public boolean hasData(BBox bbox) { + final List corners = new ArrayList<>(); + corners.add(bbox.getBottomRight()); + corners.add(new LatLon(bbox.getBottomRightLat(), bbox.getTopLeftLon())); + corners.add(bbox.getTopLeft()); + corners.add(new LatLon(bbox.getTopLeftLat(), bbox.getBottomRightLon())); + return corners.parallelStream().anyMatch(this::hasData); + } + + /** + * @param latLon A point that may have data from MapWithAI + * @return true if it is in an ares with data from MapWithAI + */ + public boolean hasData(LatLon latLon) { + boolean returnBoolean = false; + for (final Entry> entry : COUNTRIES.entrySet()) { + Logging.debug(entry.getKey()); + if (Territories.isIso3166Code(entry.getKey(), latLon)) { + returnBoolean = entry.getValue().entrySet().parallelStream().anyMatch(Entry::getValue); + break; + } + } + return returnBoolean; + } + + /** + * Get data types that may be visible around a point + * + * @param latLon The point of interest + * @return A map that may have available data types (or be empty) + */ + public static Map getDataTypes(LatLon latLon) { + return COUNTRIES.entrySet().parallelStream().filter(entry -> Territories.isIso3166Code(entry.getKey(), latLon)) + .map(Entry::getValue).findFirst().orElse(Collections.emptyMap()); + } + + /** + * Get the URL that this class is responsible for + * + * @return The url (e.g., example.com/addresses/{bbox}), or null if generic + */ + public String getUrl() { + return null; + } +} diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIAction.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIAction.java index d07f54e..e1b9a56 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIAction.java +++ b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIAction.java @@ -9,7 +9,6 @@ import java.awt.event.KeyEvent; import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import java.util.Objects; import java.util.TreeMap; import java.util.stream.Collectors; @@ -118,19 +117,16 @@ public class MapWithAIAction extends JosmAction { } final StringBuilder message = new StringBuilder(); message.append(MapWithAIPlugin.NAME).append(": "); - final MapWithAIAvailability availability = MapWithAIAvailability.getInstance(); + DataAvailability.getInstance(); // force initialization, if it hasn't already occured final Map availableTypes = new TreeMap<>(); for (final Bounds bound : bounds) { - availability.getDataTypes(bound.getCenter()) + DataAvailability.getDataTypes(bound.getCenter()) .forEach((type, available) -> availableTypes.merge(type, available, Boolean::logicalOr)); } - final List types = availableTypes.entrySet().stream().filter(Entry::getValue) - .map(entry -> MapWithAIAvailability.getPossibleDataTypesAndMessages().get(entry.getKey())) - .collect(Collectors.toList()); - if (types.isEmpty()) { + if (availableTypes.isEmpty()) { message.append(tr("No data available")); } else { - message.append("Data available: ").append(String.join(", ", types)); + message.append("Data available: ").append(String.join(", ", availableTypes.keySet())); } notification.setContent(message.toString()); diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIAvailability.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIAvailability.java index b120647..2d69c35 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIAvailability.java +++ b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIAvailability.java @@ -4,12 +4,10 @@ package org.openstreetmap.josm.plugins.mapwithai.backend; import static org.openstreetmap.josm.tools.I18n.tr; import java.io.IOException; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Optional; @@ -22,8 +20,6 @@ import javax.json.JsonObject; import javax.json.JsonValue; import javax.json.stream.JsonParser; -import org.openstreetmap.josm.data.coor.LatLon; -import org.openstreetmap.josm.data.osm.BBox; import org.openstreetmap.josm.data.osm.DataSet; import org.openstreetmap.josm.data.osm.OsmPrimitive; import org.openstreetmap.josm.io.CachedFile; @@ -31,18 +27,11 @@ import org.openstreetmap.josm.plugins.mapwithai.MapWithAIPlugin; import org.openstreetmap.josm.tools.Logging; import org.openstreetmap.josm.tools.Territories; -public final class MapWithAIAvailability { +public final class MapWithAIAvailability extends DataAvailability { private static String rapidReleases = "https://raw.githubusercontent.com/facebookmicrosites/Open-Mapping-At-Facebook/master/data/rapid_releases.geojson"; - /** A map of country to a map of available types */ - private static final Map> COUNTRIES = new HashMap<>(); - private static final Map POSSIBLE_DATA_POINTS = new TreeMap<>(); /** Original country, replacement countries */ private static final Map> COUNTRY_NAME_FIX = new HashMap<>(); - private static class InstanceHelper { - static MapWithAIAvailability instance = new MapWithAIAvailability(); - } - static { COUNTRY_NAME_FIX.put("Egypt", Collections.singleton("Egypt, Arab Rep.")); COUNTRY_NAME_FIX.put("Dem. Rep. Congo", Collections.singleton("Congo, Dem. Rep.")); @@ -65,11 +54,12 @@ public final class MapWithAIAvailability { COUNTRY_NAME_FIX.put("Congo", Collections.singleton("Congo, Rep.")); COUNTRY_NAME_FIX.put("North Macedonia", Collections.singleton("Macedonia, FYR")); COUNTRY_NAME_FIX.put("Venezuela", Collections.singleton("Venezuela, RB")); - POSSIBLE_DATA_POINTS.put("roads", "RapiD roads available"); - POSSIBLE_DATA_POINTS.put("buildings", "MS buildings available"); + POSSIBLE_DATA_POINTS.put("highway", "RapiD roads available"); + POSSIBLE_DATA_POINTS.put("building", "MS buildings available"); } - private MapWithAIAvailability() { + public MapWithAIAvailability() { + super(); try (CachedFile cachedRapidReleases = new CachedFile(rapidReleases); JsonParser parser = Json.createParser(cachedRapidReleases.getContentReader())) { cachedRapidReleases.setMaxAge(604_800); @@ -95,16 +85,6 @@ public final class MapWithAIAvailability { } } - /** - * @return the unique instance - */ - public static MapWithAIAvailability getInstance() { - if (COUNTRIES.isEmpty()) { - InstanceHelper.instance = new MapWithAIAvailability(); - } - return InstanceHelper.instance; - } - private static Map> parseForCountries(JsonArray countries) { final Map> returnCountries = new TreeMap<>(); Territories.initialize(); @@ -146,59 +126,15 @@ public final class MapWithAIAvailability { return COUNTRY_NAME_FIX.containsKey(name) ? COUNTRY_NAME_FIX.get(name) : Collections.singleton(name); } - /** - * @param bbox An area that may have data - * @return True if one of the corners of the {@code bbox} is in a country with - * available data. - */ - public boolean hasData(BBox bbox) { - final List corners = new ArrayList<>(); - corners.add(bbox.getBottomRight()); - corners.add(new LatLon(bbox.getBottomRightLat(), bbox.getTopLeftLon())); - corners.add(bbox.getTopLeft()); - corners.add(new LatLon(bbox.getTopLeftLat(), bbox.getBottomRightLon())); - return corners.parallelStream().anyMatch(this::hasData); - } - - /** - * @param latLon A point that may have data from MapWithAI - * @return true if it is in an ares with data from MapWithAI - */ - public boolean hasData(LatLon latLon) { - boolean returnBoolean = false; - for (final Entry> entry : COUNTRIES.entrySet()) { - Logging.debug(entry.getKey()); - if (Territories.isIso3166Code(entry.getKey(), latLon)) { - returnBoolean = entry.getValue().entrySet().parallelStream().anyMatch(Entry::getValue); - break; - } - } - - return returnBoolean; - } - - /** - * Get data types that may be visible around a point - * - * @param latLon The point of interest - * @return A map that may have available data types (or be empty) - */ - public Map getDataTypes(LatLon latLon) { - return COUNTRIES.entrySet().parallelStream().filter(entry -> Territories.isIso3166Code(entry.getKey(), latLon)) - .map(Entry::getValue).findFirst().orElse(Collections.emptyMap()); - } - - /** - * @return A map of possible data types with their messages - */ - public static Map getPossibleDataTypesAndMessages() { - return POSSIBLE_DATA_POINTS; - } - /** * @param url The URL where the MapWithAI data releases are. */ public static void setReleaseUrl(String url) { rapidReleases = url; } + + @Override + public String getUrl() { + return MapWithAIPreferenceHelper.DEFAULT_MAPWITHAI_API; + } } diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIDataUtils.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIDataUtils.java index e12dd2f..6a95c03 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIDataUtils.java +++ b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIDataUtils.java @@ -8,12 +8,14 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.TreeSet; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.locks.Lock; import java.util.stream.Collectors; +import javax.json.JsonObject; import javax.swing.JOptionPane; import javax.swing.SwingUtilities; diff --git a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIPreferenceHelper.java b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIPreferenceHelper.java index 8577bf7..455dc55 100644 --- a/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIPreferenceHelper.java +++ b/src/main/java/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIPreferenceHelper.java @@ -11,6 +11,7 @@ import java.util.stream.Collectors; import org.openstreetmap.josm.data.osm.Tag; import org.openstreetmap.josm.plugins.mapwithai.MapWithAIPlugin; +import org.openstreetmap.josm.plugins.mapwithai.backend.commands.conflation.DataUrl; import org.openstreetmap.josm.spi.preferences.Config; public final class MapWithAIPreferenceHelper { @@ -69,6 +70,14 @@ public final class MapWithAIPreferenceHelper { public static List> getMapWithAIURLs() { final List> returnMap = Config.getPref().getListOfMaps(API_MAP_CONFIG, new ArrayList<>()) .stream().map(TreeMap::new).collect(Collectors.toList()); + if (MapWithAIDataUtils.getLayer(false) != null) { + TreeMap layerMap = new TreeMap<>(); + layerMap.put("url", MapWithAIDataUtils.getLayer(false).getMapWithAIUrl()); + if (layerMap.get("url") != null && !layerMap.get("url").trim().isEmpty() && returnMap.parallelStream() + .noneMatch(map -> map.getOrDefault("url", "").equals(layerMap.get("url")))) { + returnMap.add(layerMap); + } + } if (returnMap.isEmpty()) { final List defaultAPIs = Collections.singletonList(DEFAULT_MAPWITHAI_API); final List defaultList = Config.getPref().getList(API_CONFIG).isEmpty() ? defaultAPIs @@ -137,24 +146,29 @@ public final class MapWithAIPreferenceHelper { * sessions */ public static void setMapWithAIUrl(String source, String url, boolean enabled, boolean permanent) { - final MapWithAILayer layer = MapWithAIDataUtils.getLayer(false); - final String setUrl = url; + setMapWithAIUrl(new DataUrl(source, url, permanent), enabled, permanent); + } - final List> urls = new ArrayList<>(getMapWithAIURLs()); - Map addOrModifyMap = urls.parallelStream() - .filter(map -> map.getOrDefault(URL_STRING, "").equals(url)).findFirst().orElse(new TreeMap<>()); - if (addOrModifyMap.isEmpty()) { - urls.add(addOrModifyMap); - } else { - urls.remove(addOrModifyMap); - addOrModifyMap = new TreeMap<>(addOrModifyMap); - urls.add(addOrModifyMap); + public static void setMapWithAIUrl(DataUrl dataUrl, boolean enabled, boolean permanent) { + final MapWithAILayer layer = MapWithAIDataUtils.getLayer(false); + final String setUrl = dataUrl.getMap().getOrDefault("url", DEFAULT_MAPWITHAI_API); + + if (permanent) { + final List> urls = new ArrayList<>(getMapWithAIURLs()); + Map addOrModifyMap = urls.parallelStream() + .filter(map -> map.getOrDefault(URL_STRING, "").equals(setUrl)).findFirst().orElse(new TreeMap<>()); + if (addOrModifyMap.isEmpty()) { + urls.add(addOrModifyMap); + } else { + urls.remove(addOrModifyMap); + addOrModifyMap = new TreeMap<>(addOrModifyMap); + urls.add(addOrModifyMap); + } + addOrModifyMap.putAll(dataUrl.getMap()); + addOrModifyMap.put(ENABLED_STRING, Boolean.toString(enabled)); + setMapWithAIURLs(urls); } - addOrModifyMap.put(URL_STRING, url); - addOrModifyMap.put(SOURCE_STRING, source); - addOrModifyMap.put(ENABLED_STRING, Boolean.toString(permanent)); - setMapWithAIURLs(urls); - if ((layer != null) && !permanent && enabled) { + if (layer != null && !permanent && enabled) { layer.setMapWithAIUrl(setUrl); } } diff --git a/test/unit/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIActionTest.java b/test/unit/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIActionTest.java index 1eaa367..9e75cf1 100644 --- a/test/unit/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIActionTest.java +++ b/test/unit/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIActionTest.java @@ -22,6 +22,7 @@ import org.openstreetmap.josm.gui.Notification; import org.openstreetmap.josm.gui.layer.Layer; import org.openstreetmap.josm.gui.layer.OsmDataLayer; import org.openstreetmap.josm.testutils.JOSMTestRules; +import org.openstreetmap.josm.tools.Territories; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; @@ -35,6 +36,7 @@ public class MapWithAIActionTest { @Before public void setUp() { action = new MapWithAIAction(); + Territories.initialize(); } @Test diff --git a/test/unit/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIAvailabilityTest.java b/test/unit/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIAvailabilityTest.java index d649795..91cc280 100644 --- a/test/unit/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIAvailabilityTest.java +++ b/test/unit/org/openstreetmap/josm/plugins/mapwithai/backend/MapWithAIAvailabilityTest.java @@ -20,7 +20,7 @@ import com.github.tomakehurst.wiremock.WireMockServer; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; public class MapWithAIAvailabilityTest { - private MapWithAIAvailability instance; + private DataAvailability instance; @Rule @SuppressFBWarnings("URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD") @@ -34,7 +34,7 @@ public class MapWithAIAvailabilityTest { MapWithAIAvailability.setReleaseUrl( wireMock.baseUrl() + "/facebookmicrosites/Open-Mapping-At-Facebook/master/data/rapid_releases.geojson"); Territories.initialize(); - instance = MapWithAIAvailability.getInstance(); + instance = DataAvailability.getInstance(); LatLon temp = new LatLon(40, -100); await().atMost(Durations.TEN_SECONDS).until(() -> Territories.isIso3166Code("US", temp)); } @@ -60,12 +60,16 @@ public class MapWithAIAvailabilityTest { @Test public void testgetDataLatLon() { - Assert.assertTrue(instance.getDataTypes(new LatLon(0, 0)).isEmpty()); - Assert.assertTrue(instance.getDataTypes(new LatLon(40, -100)).get("roads")); - Assert.assertTrue(instance.getDataTypes(new LatLon(40, -100)).get("buildings")); - Assert.assertFalse(instance.getDataTypes(new LatLon(45.424722, -75.695)).get("roads")); - Assert.assertTrue(instance.getDataTypes(new LatLon(45.424722, -75.695)).get("buildings")); - Assert.assertTrue(instance.getDataTypes(new LatLon(19.433333, -99.133333)).get("roads")); - Assert.assertFalse(instance.getDataTypes(new LatLon(19.433333, -99.133333)).get("buildings")); + Assert.assertTrue(DataAvailability.getDataTypes(new LatLon(0, 0)).isEmpty()); + Assert.assertTrue(DataAvailability.getDataTypes(new LatLon(40, -100)).getOrDefault("highway", false)); + Assert.assertTrue(DataAvailability.getDataTypes(new LatLon(40, -100)).getOrDefault("building", false)); + Assert.assertFalse( + DataAvailability.getDataTypes(new LatLon(45.424722, -75.695)).getOrDefault("highway", false)); + Assert.assertTrue( + DataAvailability.getDataTypes(new LatLon(45.424722, -75.695)).getOrDefault("building", false)); + Assert.assertTrue( + DataAvailability.getDataTypes(new LatLon(19.433333, -99.133333)).getOrDefault("highway", false)); + Assert.assertFalse( + DataAvailability.getDataTypes(new LatLon(19.433333, -99.133333)).getOrDefault("building", false)); } }