kopia lustrzana https://github.com/JOSM/MapWithAI
Fix an NPE caused by Territories in JOSM
Also add spotless for code formatting Signed-off-by: Taylor Smock <taylor.smock@kaart.com>pull/1/head
rodzic
96294ec888
commit
ddcf69001b
12
build.gradle
12
build.gradle
|
@ -15,6 +15,7 @@ plugins {
|
|||
id "com.github.spotbugs" version "3.0.0"
|
||||
id "org.openstreetmap.josm" version "0.6.5"
|
||||
id "net.ltgt.errorprone" version "1.1.1"
|
||||
id "com.diffplug.gradle.spotless" version "3.28.1"
|
||||
//id 'de.aaschmid.cpd' version '2.0'
|
||||
}
|
||||
|
||||
|
@ -125,6 +126,17 @@ jacocoTestCoverageVerification {
|
|||
}
|
||||
}
|
||||
|
||||
spotless {
|
||||
java {
|
||||
licenseHeader "// License: GPL. For details, see LICENSE file."
|
||||
removeUnusedImports()
|
||||
endWithNewline()
|
||||
indentWithSpaces(4)
|
||||
eclipse().configFile "config/josm_formatting.xml"
|
||||
trimTrailingWhitespace()
|
||||
}
|
||||
}
|
||||
|
||||
josm {
|
||||
manifest {
|
||||
oldVersionDownloadLink 15820, "v1.3.7", new URL("https://gokaart.gitlab.io/JOSM_MapWithAI/dist/v1.3.7/mapwithai.jar")
|
||||
|
|
|
@ -28,7 +28,7 @@ import org.openstreetmap.josm.tools.Utils;
|
|||
|
||||
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";
|
||||
public static String DEFAULT_SERVER_URL = "https://gokaart.gitlab.io/JOSM_MapWithAI/json/sources.json";
|
||||
/** A map of tag -> message of possible data types */
|
||||
static final Map<String, String> POSSIBLE_DATA_POINTS = new TreeMap<>();
|
||||
|
||||
|
@ -44,9 +44,7 @@ public class DataAvailability {
|
|||
* This holds classes that can give availability of data for a specific service
|
||||
*/
|
||||
private static final List<Class<? extends DataAvailability>> DATA_SOURCES = new ArrayList<>();
|
||||
static {
|
||||
DATA_SOURCES.add(MapWithAIAvailability.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* A map of countries to a map of available types
|
||||
* ({@code Map<Country, Map<Type, IsAvailable>>}
|
||||
|
@ -264,4 +262,13 @@ public class DataAvailability {
|
|||
return "";
|
||||
}).filter(Objects::nonNull).filter(str -> !Utils.removeWhiteSpaces(str).isEmpty()).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the URL to use to get MapWithAI information
|
||||
*
|
||||
* @param url The URL which serves MapWithAI servers
|
||||
*/
|
||||
public static void setReleaseUrl(String url) {
|
||||
DEFAULT_SERVER_URL = url;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,146 +0,0 @@
|
|||
// License: GPL. For details, see LICENSE file.
|
||||
package org.openstreetmap.josm.plugins.mapwithai.backend;
|
||||
|
||||
import static org.openstreetmap.josm.tools.I18n.tr;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Optional;
|
||||
import java.util.TreeMap;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.json.Json;
|
||||
import javax.json.JsonArray;
|
||||
import javax.json.JsonObject;
|
||||
import javax.json.JsonValue;
|
||||
import javax.json.stream.JsonParser;
|
||||
|
||||
import org.openstreetmap.josm.io.CachedFile;
|
||||
import org.openstreetmap.josm.plugins.mapwithai.MapWithAIPlugin;
|
||||
import org.openstreetmap.josm.tools.Logging;
|
||||
import org.openstreetmap.josm.tools.Territories;
|
||||
|
||||
public final class MapWithAIAvailability extends DataAvailability {
|
||||
private static String rapidReleases = "https://raw.githubusercontent.com/facebookmicrosites/Open-Mapping-At-Facebook/master/data/rapid_releases.geojson";
|
||||
/** Original country, replacement countries */
|
||||
private static final Map<String, Collection<String>> COUNTRY_NAME_FIX = new HashMap<>();
|
||||
|
||||
static {
|
||||
COUNTRY_NAME_FIX.put("Egypt", Collections.singleton("Egypt, Arab Rep."));
|
||||
COUNTRY_NAME_FIX.put("Dem. Rep. Congo", Collections.singleton("Congo, Dem. Rep."));
|
||||
COUNTRY_NAME_FIX.put("Democratic Republic of the Congo", Collections.singleton("Congo, Dem. Rep."));
|
||||
COUNTRY_NAME_FIX.put("eSwatini", Collections.singleton("Swaziland"));
|
||||
COUNTRY_NAME_FIX.put("Gambia", Collections.singleton("Gambia, The"));
|
||||
COUNTRY_NAME_FIX.put("The Bahamas", Collections.singleton("Bahamas, The"));
|
||||
COUNTRY_NAME_FIX.put("Ivory Coast", Collections.singleton("Côte d'Ivoire"));
|
||||
COUNTRY_NAME_FIX.put("Somaliland", Collections.singleton("Somalia")); // Technically a self-declared independent
|
||||
// area of Somalia
|
||||
COUNTRY_NAME_FIX.put("Carribean Countries",
|
||||
Arrays.asList("Antigua and Barbuda", "Anguilla", "Barbados", "British Virgin Islands", "Cayman Islands",
|
||||
"Dominica", "Dominican Republic", "Grenada", "Guadeloupe", "Haiti", "Jamaica", "Martinique",
|
||||
"Montserrat", "Puerto Rico", "Saba", "Saint-Barthélemy", "Saint-Martin", "Sint Eustatius",
|
||||
"Sint Maarten", "St. Kitts and Nevis", "St. Lucia", "St. Vincent and the Grenadines",
|
||||
"Turks and Caicos Islands"));
|
||||
COUNTRY_NAME_FIX.put("Falkland Islands (Islas Maldivas)", Collections.singleton("Falkland Islands"));
|
||||
COUNTRY_NAME_FIX.put("Laos", Collections.singleton("Lao PDR"));
|
||||
COUNTRY_NAME_FIX.put("East Timor", Collections.singleton("Timor-Leste"));
|
||||
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"));
|
||||
COUNTRY_NAME_FIX.put("South Korea", Collections.singleton("Korea, Rep."));
|
||||
COUNTRY_NAME_FIX.put("Kyrgyzstan", Collections.singleton("Kyrgyz Republic"));
|
||||
COUNTRY_NAME_FIX.put("Northern Cyprus", Collections.singleton("Cyprus"));
|
||||
COUNTRY_NAME_FIX.put("Yemen", Collections.singleton("Yemen, Rep."));
|
||||
POSSIBLE_DATA_POINTS.put("highway", "RapiD roads available");
|
||||
POSSIBLE_DATA_POINTS.put("building", "MS buildings available");
|
||||
}
|
||||
|
||||
public MapWithAIAvailability() {
|
||||
super();
|
||||
try (CachedFile cachedRapidReleases = new CachedFile(rapidReleases);
|
||||
JsonParser parser = Json.createParser(cachedRapidReleases.getContentReader())) {
|
||||
cachedRapidReleases.setMaxAge(604_800);
|
||||
parser.next();
|
||||
final Stream<Entry<String, JsonValue>> entries = parser.getObjectStream();
|
||||
final Optional<Entry<String, JsonValue>> objects = entries.filter(entry -> "objects".equals(entry.getKey()))
|
||||
.findFirst();
|
||||
if (objects.isPresent()) {
|
||||
final JsonObject value = objects.get().getValue().asJsonObject();
|
||||
if (value != null) {
|
||||
final JsonObject centroid = value.getJsonObject("rapid_releases_points");
|
||||
if (centroid != null) {
|
||||
final JsonArray countries = centroid.getJsonArray("geometries");
|
||||
if (countries != null) {
|
||||
COUNTRIES.clear();
|
||||
COUNTRIES.putAll(parseForCountries(countries));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Logging.debug(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static Map<String, Map<String, Boolean>> parseForCountries(JsonArray countries) {
|
||||
final Map<String, Map<String, Boolean>> returnCountries = new TreeMap<>();
|
||||
Territories.initialize();
|
||||
for (int i = 0; i < countries.size(); i++) {
|
||||
final JsonObject country = countries.getJsonObject(i).getJsonObject("properties");
|
||||
for (String countryName : cornerCaseNames(country.getString("Country"))) {
|
||||
final Optional<String> realCountryISO = Territories.getOriginalDataSet().allPrimitives()
|
||||
.parallelStream()
|
||||
.filter(o -> o.hasKey("name:en") && o.get("name:en").equalsIgnoreCase(countryName))
|
||||
.map(o -> o.hasKey("ISO3166-1:alpha2") ? o.get("ISO3166-1:alpha2") : o.get("ISO3166-2"))
|
||||
.min(Comparator.comparing(String::length));
|
||||
if (realCountryISO.isPresent()) {
|
||||
String key = realCountryISO.get();
|
||||
// We need to handle cases like Alaska more elegantly
|
||||
final Map<String, Boolean> data = returnCountries.getOrDefault(key, new TreeMap<>());
|
||||
for (final Entry<String, String> entry : POSSIBLE_DATA_POINTS.entrySet()) {
|
||||
final boolean hasData = "yes".equals(country.getString(entry.getValue()));
|
||||
if (hasData || !data.containsKey(entry.getKey())) {
|
||||
data.put(entry.getKey(), hasData);
|
||||
}
|
||||
}
|
||||
returnCountries.put(key, data);
|
||||
} else {
|
||||
Logging.error(tr("{0}: We couldn''t find {1}", MapWithAIPlugin.NAME, countryName));
|
||||
}
|
||||
}
|
||||
}
|
||||
return returnCountries;
|
||||
}
|
||||
|
||||
private static Collection<String> cornerCaseNames(String name) {
|
||||
return COUNTRY_NAME_FIX.containsKey(name) ? COUNTRY_NAME_FIX.get(name) : Collections.singleton(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTermsOfUseUrl() {
|
||||
return "https://mapwith.ai/doc/license/MapWithAILicense.pdf";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrivacyPolicyUrl() {
|
||||
return "https://mapwith.ai/doc/license/MapWithAIPrivacyPolicy.pdf#page=3";
|
||||
}
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
// License: GPL. For details, see LICENSE file.
|
||||
package org.openstreetmap.josm.plugins.mapwithai.backend;
|
||||
|
||||
import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// License: GPL. For details, see LICENSE file.
|
||||
package org.openstreetmap.josm.plugins.mapwithai.backend;
|
||||
|
||||
import static org.openstreetmap.josm.tools.I18n.tr;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// License: GPL. For details, see LICENSE file.
|
||||
package org.openstreetmap.josm.plugins.mapwithai.commands;
|
||||
|
||||
import static org.openstreetmap.josm.tools.I18n.tr;
|
||||
|
|
|
@ -82,4 +82,4 @@ public class ConnectingNodeInformationTest extends Test {
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// License: GPL. For details, see LICENSE file.
|
||||
package org.openstreetmap.josm.plugins.mapwithai.data.validation.tests;
|
||||
|
||||
import static org.openstreetmap.josm.tools.I18n.marktr;
|
||||
|
|
|
@ -124,8 +124,7 @@ public class MapWithAIParametersPanel extends JPanel {
|
|||
|
||||
private static List<Object[]> getHeadersAsVector(Map<String, Pair<String, Boolean>> headers) {
|
||||
return headers.entrySet().stream().sorted((e1, e2) -> e1.getKey().compareTo(e2.getKey()))
|
||||
.map(e -> new Object[] { e.getKey(), e.getValue().a, e.getValue().b })
|
||||
.collect(Collectors.toList());
|
||||
.map(e -> new Object[] { e.getKey(), e.getValue().a, e.getValue().b }).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
// License: GPL. For details, see LICENSE file.
|
||||
package org.openstreetmap.josm.plugins.mapwithai.io.mapwithai;
|
||||
|
||||
import static org.openstreetmap.josm.tools.I18n.tr;
|
||||
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.PathIterator;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
@ -17,21 +22,17 @@ import javax.json.JsonReader;
|
|||
import javax.json.JsonStructure;
|
||||
import javax.json.JsonValue;
|
||||
|
||||
import org.openstreetmap.josm.data.Bounds;
|
||||
import org.openstreetmap.josm.data.coor.LatLon;
|
||||
import org.openstreetmap.josm.data.imagery.ImageryInfo;
|
||||
import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryBounds;
|
||||
import org.openstreetmap.josm.data.imagery.Shape;
|
||||
import org.openstreetmap.josm.data.osm.BBox;
|
||||
import org.openstreetmap.josm.data.osm.DataSet;
|
||||
import org.openstreetmap.josm.data.osm.Node;
|
||||
import org.openstreetmap.josm.data.osm.OsmPrimitive;
|
||||
import org.openstreetmap.josm.data.osm.Relation;
|
||||
import org.openstreetmap.josm.data.osm.RelationMember;
|
||||
import org.openstreetmap.josm.data.osm.Way;
|
||||
import org.openstreetmap.josm.io.CachedFile;
|
||||
import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIInfo;
|
||||
import org.openstreetmap.josm.tools.DefaultGeoProperty;
|
||||
import org.openstreetmap.josm.tools.GeoPropertyIndex;
|
||||
import org.openstreetmap.josm.tools.HttpClient;
|
||||
import org.openstreetmap.josm.tools.Logging;
|
||||
import org.openstreetmap.josm.tools.Territories;
|
||||
import org.openstreetmap.josm.tools.Utils;
|
||||
|
||||
|
@ -104,32 +105,29 @@ public class MapWithAISourceReader implements Closeable {
|
|||
List<ImageryBounds> bounds = new ArrayList<>();
|
||||
if (JsonValue.ValueType.OBJECT.equals(countries.getValueType())) {
|
||||
Set<String> codes = Territories.getKnownIso3166Codes();
|
||||
DataSet ds = Territories.getOriginalDataSet();
|
||||
for (Map.Entry<String, JsonValue> country : countries.asJsonObject().entrySet()) {
|
||||
if (codes.contains(country.getKey())) {
|
||||
Collection<OsmPrimitive> countryData = ds
|
||||
.getPrimitives(i -> i.getKeys().containsValue(country.getKey()));
|
||||
OsmPrimitive prim = countryData.iterator().next();
|
||||
ImageryBounds tmp = new ImageryBounds(bboxToBoundsString(prim.getBBox(), ","), ",");
|
||||
countryData
|
||||
.stream().map(OsmPrimitive::getBBox).map(b -> new Bounds(b.getBottomRightLat(),
|
||||
b.getTopLeftLon(), b.getTopLeftLat(), b.getBottomRightLon()))
|
||||
.forEach(tmp::extend);
|
||||
countryData.stream().filter(Way.class::isInstance).map(Way.class::cast)
|
||||
.map(MapWithAISourceReader::wayToShape).forEach(tmp::addShape);
|
||||
// This doesn't subtract inner ways. TODO?
|
||||
countryData.stream().filter(Relation.class::isInstance).map(Relation.class::cast)
|
||||
.flatMap(r -> r.getMembers().stream().filter(m -> "outer".equals(m.getRole()))
|
||||
.map(RelationMember::getMember).filter(Way.class::isInstance)
|
||||
.map(Way.class::cast))
|
||||
.map(MapWithAISourceReader::wayToShape).forEach(tmp::addShape);
|
||||
bounds.add(tmp);
|
||||
GeoPropertyIndex<Boolean> geoPropertyIndex = Territories.getGeoPropertyIndex(country.getKey());
|
||||
if (geoPropertyIndex.getGeoProperty() instanceof DefaultGeoProperty) {
|
||||
DefaultGeoProperty prop = (DefaultGeoProperty) geoPropertyIndex.getGeoProperty();
|
||||
Rectangle2D areaBounds = prop.getArea().getBounds2D();
|
||||
ImageryBounds tmp = new ImageryBounds(bboxToBoundsString(new BBox(areaBounds.getMinX(),
|
||||
areaBounds.getMinY(), areaBounds.getMaxX(), areaBounds.getMaxY()), ","), ",");
|
||||
areaToShapes(prop.getArea()).forEach(tmp::addShape);
|
||||
bounds.add(tmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
MapWithAIInfo info = new MapWithAIInfo(name, url, type, eula, id);
|
||||
info.setDefaultEntry(values.getBoolean("default", false));
|
||||
info.setParameters(values.getJsonArray("parameters"));
|
||||
if (values.containsKey("terms_of_use_url")) {
|
||||
info.setTermsOfUseText(values.getString("terms_of_use_url"));
|
||||
}
|
||||
if (values.containsKey("privacy_policy_url")) {
|
||||
info.setPrivacyPolicyURL(values.getString("privacy_policy_url"));
|
||||
}
|
||||
if (!bounds.isEmpty()) {
|
||||
ImageryBounds bound = bounds.get(0);
|
||||
bounds.remove(0);
|
||||
|
@ -142,18 +140,40 @@ public class MapWithAISourceReader implements Closeable {
|
|||
return new MapWithAIInfo(name);
|
||||
}
|
||||
|
||||
private static Collection<Shape> areaToShapes(java.awt.Shape shape) {
|
||||
PathIterator iterator = shape.getPathIterator(new AffineTransform());
|
||||
Shape defaultShape = new Shape();
|
||||
Collection<Shape> shapes = new ArrayList<>();
|
||||
float[] moveTo = null;
|
||||
while (!iterator.isDone()) {
|
||||
float[] coords = new float[6];
|
||||
int type = iterator.currentSegment(coords);
|
||||
if (type == PathIterator.SEG_MOVETO || type == PathIterator.SEG_LINETO) {
|
||||
if (type == PathIterator.SEG_MOVETO) {
|
||||
moveTo = coords;
|
||||
}
|
||||
defaultShape.addPoint(Float.toString(coords[1]), Float.toString(coords[0]));
|
||||
} else if (type == PathIterator.SEG_CLOSE && moveTo != null && moveTo.length >= 2) {
|
||||
defaultShape.addPoint(Float.toString(moveTo[1]), Float.toString(moveTo[0]));
|
||||
shapes.add(defaultShape);
|
||||
defaultShape = new Shape();
|
||||
} else {
|
||||
Logging.error(tr("No implementation for converting a segment of type {0} to coordinates", type));
|
||||
}
|
||||
iterator.next();
|
||||
}
|
||||
if (!defaultShape.getPoints().isEmpty()) {
|
||||
shapes.add(defaultShape);
|
||||
}
|
||||
return shapes;
|
||||
}
|
||||
|
||||
private static String bboxToBoundsString(BBox bbox, String separator) {
|
||||
return String.join(separator, LatLon.cDdFormatter.format(bbox.getBottomRightLat()),
|
||||
LatLon.cDdFormatter.format(bbox.getTopLeftLon()), LatLon.cDdFormatter.format(bbox.getTopLeftLat()),
|
||||
LatLon.cDdFormatter.format(bbox.getBottomRightLon()));
|
||||
}
|
||||
|
||||
private static Shape wayToShape(Way way) {
|
||||
return new Shape(way.getNodes().stream().map(Node::getCoor)
|
||||
.map(l -> Double.toString(l.lat()) + "," + Double.toString(l.lon())).collect(Collectors.joining(",")),
|
||||
",");
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether opening HTTP connections should fail fast, i.e., whether a
|
||||
* {@link HttpClient#setConnectTimeout(int) low connect timeout} should be used.
|
||||
|
|
|
@ -29,7 +29,6 @@ import org.openstreetmap.josm.data.osm.WaySegment;
|
|||
import org.openstreetmap.josm.data.projection.ProjectionRegistry;
|
||||
import org.openstreetmap.josm.gui.MainApplication;
|
||||
import org.openstreetmap.josm.gui.layer.OsmDataLayer;
|
||||
import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAILayerInfo;
|
||||
import org.openstreetmap.josm.plugins.mapwithai.gui.preferences.MapWithAILayerInfoTest;
|
||||
import org.openstreetmap.josm.testutils.JOSMTestRules;
|
||||
import org.openstreetmap.josm.tools.Geometry;
|
||||
|
|
|
@ -35,7 +35,7 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
|||
public class MapWithAIActionTest {
|
||||
@Rule
|
||||
@SuppressFBWarnings("URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
|
||||
public JOSMTestRules test = new JOSMTestRules().main().projection().timeout(100000);
|
||||
public JOSMTestRules test = new JOSMTestRules().main().projection().territories().timeout(100000);
|
||||
|
||||
private MapWithAIAction action;
|
||||
|
||||
|
@ -45,9 +45,7 @@ public class MapWithAIActionTest {
|
|||
public void setUp() {
|
||||
action = new MapWithAIAction();
|
||||
wireMock.start(); // This is required to avoid failing a test in MapWithAIAvailabilityTest
|
||||
MapWithAIAvailability.setReleaseUrl(
|
||||
wireMock.baseUrl() + "/facebookmicrosites/Open-Mapping-At-Facebook/master/data/rapid_releases.geojson");
|
||||
Territories.initialize();
|
||||
DataAvailability.setReleaseUrl(wireMock.baseUrl() + "/JOSM_MapWithAI/json/sources.json");
|
||||
LatLon temp = new LatLon(40, -100);
|
||||
await().atMost(Durations.TEN_SECONDS).until(() -> Territories.isIso3166Code("US", temp));
|
||||
}
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
/**
|
||||
*
|
||||
*/
|
||||
// License: GPL. For details, see LICENSE file.
|
||||
package org.openstreetmap.josm.plugins.mapwithai.backend;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// License: GPL. For details, see LICENSE file.
|
||||
package org.openstreetmap.josm.plugins.mapwithai.commands.conflation;
|
||||
|
||||
import static org.openstreetmap.josm.tools.I18n.tr;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// License: GPL. For details, see LICENSE file.
|
||||
package org.openstreetmap.josm.plugins.mapwithai.data.validation.tests;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// License: GPL. For details, see LICENSE file.
|
||||
package org.openstreetmap.josm.plugins.mapwithai.data.validation.tests;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// License: GPL. For details, see LICENSE file.
|
||||
package org.openstreetmap.josm.plugins.mapwithai.testutils;
|
||||
|
||||
import static org.openstreetmap.josm.tools.I18n.tr;
|
||||
|
|
Ładowanie…
Reference in New Issue