kopia lustrzana https://github.com/JOSM/MapWithAI
Reduce startup costs
This largely focuses on reducing memory allocations. There are also some conversions to Java 17 standards. Signed-off-by: Taylor Smock <tsmock@meta.com>pull/31/head
rodzic
9aab77dd4f
commit
056b9db064
|
@ -3,7 +3,6 @@ package org.openstreetmap.josm.plugins.mapwithai;
|
|||
|
||||
import static org.openstreetmap.josm.tools.I18n.tr;
|
||||
|
||||
import javax.swing.JMenu;
|
||||
import javax.swing.JMenuItem;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
@ -12,7 +11,6 @@ import java.util.Arrays;
|
|||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.openstreetmap.josm.actions.JosmAction;
|
||||
import org.openstreetmap.josm.actions.PreferencesAction;
|
||||
|
@ -70,8 +68,6 @@ public final class MapWithAIPlugin extends Plugin implements Destroyable {
|
|||
|
||||
private final List<Destroyable> destroyables;
|
||||
|
||||
private final PreferencesAction preferenceAction;
|
||||
|
||||
private final MapWithAIMenu mapwithaiMenu;
|
||||
|
||||
private static final Map<Class<? extends JosmAction>, Boolean> MENU_ENTRIES = new LinkedHashMap<>();
|
||||
|
@ -92,7 +88,7 @@ public final class MapWithAIPlugin extends Plugin implements Destroyable {
|
|||
preferenceSetting = new MapWithAIPreferences();
|
||||
|
||||
// Add MapWithAI specific menu
|
||||
JMenu dataMenu = MainApplication.getMenu().dataMenu;
|
||||
final var dataMenu = MainApplication.getMenu().dataMenu;
|
||||
mapwithaiMenu = new MapWithAIMenu();
|
||||
|
||||
dataMenu.add(mapwithaiMenu);
|
||||
|
@ -111,8 +107,8 @@ public final class MapWithAIPlugin extends Plugin implements Destroyable {
|
|||
}
|
||||
|
||||
// Add the preferences last to data
|
||||
preferenceAction = PreferencesAction.forPreferenceTab(tr("MapWithAI Preferences"), tr("MapWithAI Preferences"),
|
||||
MapWithAIPreferences.class);
|
||||
final var preferenceAction = PreferencesAction.forPreferenceTab(tr("MapWithAI Preferences"),
|
||||
tr("MapWithAI Preferences"), MapWithAIPreferences.class);
|
||||
MainMenu.add(mapwithaiMenu, preferenceAction);
|
||||
|
||||
VALIDATORS.forEach(clazz -> {
|
||||
|
@ -137,6 +133,9 @@ public final class MapWithAIPlugin extends Plugin implements Destroyable {
|
|||
mapFrameInitialized(null, MainApplication.getMap());
|
||||
OSMDownloadSource.addDownloadType(new MapWithAIDownloadSourceType());
|
||||
MainApplication.worker.execute(() -> UpdateProd.doProd(info.mainversion));
|
||||
// Preload the MapWithAILayerInfo for the JOSM download window
|
||||
// This reduces the amount of time taken for first button click by 100ms.
|
||||
MainApplication.worker.execute(MapWithAILayerInfo::getInstance);
|
||||
|
||||
destroyables.add(new MapWithAICopyProhibit());
|
||||
}
|
||||
|
@ -146,7 +145,7 @@ public final class MapWithAIPlugin extends Plugin implements Destroyable {
|
|||
// Run in EDT to avoid blocking (has to be run before MapWithAIDownloadOptions
|
||||
// so its already initialized)
|
||||
GuiHelper.runInEDT(MapWithAILayerInfo::getInstance);
|
||||
MapWithAIDownloadOptions mapWithAIDownloadOptions = new MapWithAIDownloadOptions();
|
||||
final var mapWithAIDownloadOptions = new MapWithAIDownloadOptions();
|
||||
MainApplication.worker
|
||||
.execute(() -> GuiHelper.runInEDT(() -> mapWithAIDownloadOptions.addGui(DownloadDialog.getInstance())));
|
||||
destroyables.add(mapWithAIDownloadOptions);
|
||||
|
@ -154,9 +153,8 @@ public final class MapWithAIPlugin extends Plugin implements Destroyable {
|
|||
|
||||
@Override
|
||||
public void mapFrameInitialized(MapFrame oldFrame, MapFrame newFrame) {
|
||||
final Optional<MapWithAIObject> possibleMapWithAIObject = destroyables.stream()
|
||||
.filter(MapWithAIObject.class::isInstance).map(MapWithAIObject.class::cast).findFirst();
|
||||
final MapWithAIObject mapWithAIObject = possibleMapWithAIObject.orElse(new MapWithAIObject());
|
||||
final var mapWithAIObject = destroyables.stream().filter(MapWithAIObject.class::isInstance)
|
||||
.map(MapWithAIObject.class::cast).findFirst().orElseGet(MapWithAIObject::new);
|
||||
if ((oldFrame != null) && (oldFrame.statusLine != null)) {
|
||||
mapWithAIObject.removeMapStatus(oldFrame.statusLine);
|
||||
}
|
||||
|
|
|
@ -4,14 +4,12 @@ package org.openstreetmap.josm.plugins.mapwithai.backend;
|
|||
import static org.openstreetmap.josm.tools.I18n.tr;
|
||||
|
||||
import javax.json.Json;
|
||||
import javax.json.JsonObject;
|
||||
import javax.json.JsonReader;
|
||||
import javax.json.JsonStructure;
|
||||
import javax.json.JsonValue;
|
||||
import javax.json.stream.JsonParser;
|
||||
import javax.swing.JOptionPane;
|
||||
|
||||
import java.awt.geom.Area;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
@ -24,13 +22,10 @@ import java.time.Instant;
|
|||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.OptionalInt;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.openstreetmap.josm.data.Bounds;
|
||||
import org.openstreetmap.josm.data.DataSource;
|
||||
|
@ -54,6 +49,7 @@ import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIInfo;
|
|||
import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAILayerInfo;
|
||||
import org.openstreetmap.josm.plugins.mapwithai.data.mapwithai.MapWithAIType;
|
||||
import org.openstreetmap.josm.plugins.mapwithai.tools.MapPaintUtils;
|
||||
import org.openstreetmap.josm.spi.preferences.Config;
|
||||
import org.openstreetmap.josm.tools.HttpClient;
|
||||
import org.openstreetmap.josm.tools.JosmRuntimeException;
|
||||
import org.openstreetmap.josm.tools.Logging;
|
||||
|
@ -64,11 +60,6 @@ import org.openstreetmap.josm.tools.Logging;
|
|||
* @author Taylor Smock
|
||||
*/
|
||||
public class BoundingBoxMapWithAIDownloader extends BoundingBoxDownloader {
|
||||
/**
|
||||
* The time that the data URL's were last updated. See #22683 for why this is
|
||||
* necessary.
|
||||
*/
|
||||
private static Instant DATA_LAST_UPDATED = Instant.EPOCH;
|
||||
private final String url;
|
||||
private final boolean crop;
|
||||
private final int start;
|
||||
|
@ -157,23 +148,22 @@ public class BoundingBoxMapWithAIDownloader extends BoundingBoxDownloader {
|
|||
} catch (OsmTransferException e) {
|
||||
if (e.getCause() instanceof SocketTimeoutException && (System.nanoTime() - startTime) > 30_000_000_000L) {
|
||||
updateLastErrorTime(System.nanoTime());
|
||||
Notification note = new Notification();
|
||||
final var note = new Notification();
|
||||
GuiHelper.runInEDT(() -> note.setContent(tr(
|
||||
"Attempting to download data in the background. This may fail or succeed in a few minutes.")));
|
||||
GuiHelper.runInEDT(note::show);
|
||||
} else if (e.getCause() instanceof IllegalDataException) {
|
||||
final Instant lastUpdated;
|
||||
final Instant now;
|
||||
final var now = Instant.now();
|
||||
synchronized (BoundingBoxMapWithAIDownloader.class) {
|
||||
lastUpdated = DATA_LAST_UPDATED;
|
||||
now = Instant.now();
|
||||
DATA_LAST_UPDATED = now;
|
||||
lastUpdated = Instant.ofEpochSecond(Config.getPref().getLong("mapwithai.layerinfo.lastupdated", 0));
|
||||
Config.getPref().putLong("mapwithai.layerinfo.lastupdated", now.getEpochSecond());
|
||||
}
|
||||
// Only force an update if the last update time is sufficiently old.
|
||||
if (now.toEpochMilli() - lastUpdated.toEpochMilli() > TimeUnit.MINUTES.toMillis(10)) {
|
||||
MapWithAILayerInfo.getInstance().loadDefaults(true, MapWithAIDataUtils.getForkJoinPool(), false,
|
||||
() -> GuiHelper.runInEDT(() -> {
|
||||
Notification notification = new Notification(tr(
|
||||
final var notification = new Notification(tr(
|
||||
"MapWithAI layers reloaded. Removing and re-adding the MapWithAI layer may be necessary."));
|
||||
notification.setIcon(JOptionPane.INFORMATION_MESSAGE);
|
||||
notification.setDuration(Notification.TIME_LONG);
|
||||
|
@ -185,8 +175,8 @@ public class BoundingBoxMapWithAIDownloader extends BoundingBoxDownloader {
|
|||
}
|
||||
}
|
||||
// Just in case something happens, try again...
|
||||
DataSet ds = new DataSet();
|
||||
GetDataRunnable runnable = new GetDataRunnable(downloadArea, ds, NullProgressMonitor.INSTANCE);
|
||||
final var ds = new DataSet();
|
||||
final var runnable = new GetDataRunnable(downloadArea, ds, NullProgressMonitor.INSTANCE);
|
||||
runnable.setMapWithAIInfo(info);
|
||||
MainApplication.worker.execute(() -> {
|
||||
try {
|
||||
|
@ -210,12 +200,11 @@ public class BoundingBoxMapWithAIDownloader extends BoundingBoxDownloader {
|
|||
* @return The dataset to send to the server
|
||||
*/
|
||||
private static DataSet getConflationData(Bounds bound) {
|
||||
Area area = DataSource.getDataSourceArea(Collections.singleton(new DataSource(bound, "")));
|
||||
final var area = DataSource.getDataSourceArea(Collections.singleton(new DataSource(bound, "")));
|
||||
if (area != null) {
|
||||
List<OsmDataLayer> layers = MainApplication
|
||||
.getLayerManager().getLayersOfType(OsmDataLayer.class).stream().filter(l -> l.getDataSet()
|
||||
.getDataSourceBounds().stream().anyMatch(b -> area.contains(bound.asRect())))
|
||||
.collect(Collectors.toList());
|
||||
final var layers = MainApplication.getLayerManager().getLayersOfType(OsmDataLayer.class).stream().filter(
|
||||
l -> l.getDataSet().getDataSourceBounds().stream().anyMatch(b -> area.contains(bound.asRect())))
|
||||
.toList();
|
||||
return layers.stream().max(Comparator.comparingInt(l -> l.getDataSet().allPrimitives().size()))
|
||||
.map(OsmDataLayer::getDataSet).orElse(null);
|
||||
}
|
||||
|
@ -229,7 +218,7 @@ public class BoundingBoxMapWithAIDownloader extends BoundingBoxDownloader {
|
|||
@Override
|
||||
protected DataSet parseDataSet(InputStream source, ProgressMonitor progressMonitor) throws IllegalDataException {
|
||||
DataSet ds;
|
||||
String contentType = this.activeConnection.getResponse().getContentType();
|
||||
final var contentType = this.activeConnection.getResponse().getContentType();
|
||||
if (Arrays.asList("text/json", "application/json", "application/geo+json").contains(contentType)
|
||||
// Fall back to Esri Feature Server check. They don't always indicate a json
|
||||
// return type. :(
|
||||
|
@ -238,15 +227,14 @@ public class BoundingBoxMapWithAIDownloader extends BoundingBoxDownloader {
|
|||
// if we need to make additional calls
|
||||
try (JsonReader reader = Json.createReader(source)) {
|
||||
JsonStructure structure = reader.read();
|
||||
try (ByteArrayInputStream bais = new ByteArrayInputStream(
|
||||
structure.toString().getBytes(StandardCharsets.UTF_8))) {
|
||||
try (var bais = new ByteArrayInputStream(structure.toString().getBytes(StandardCharsets.UTF_8))) {
|
||||
ds = GeoJSONReader.parseDataSet(bais, progressMonitor);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
/* We should only call this from the "root" call */
|
||||
if (this.start == 0 && structure.getValueType() == JsonValue.ValueType.OBJECT) {
|
||||
final JsonObject serverObj = structure.asJsonObject();
|
||||
final var serverObj = structure.asJsonObject();
|
||||
final boolean exceededTransferLimit = serverObj.entrySet().stream()
|
||||
.filter(entry -> "properties".equals(entry.getKey())
|
||||
&& entry.getValue().getValueType() == JsonValue.ValueType.OBJECT)
|
||||
|
@ -254,7 +242,7 @@ public class BoundingBoxMapWithAIDownloader extends BoundingBoxDownloader {
|
|||
.map(obj -> obj.getBoolean("exceededTransferLimit", false)).findFirst().orElse(false);
|
||||
if (exceededTransferLimit && this.info.getSourceType() == MapWithAIType.ESRI_FEATURE_SERVER) {
|
||||
final int size = serverObj.getJsonArray("features").size();
|
||||
final DataSet other = this.getAdditionalEsriData(progressMonitor,
|
||||
final var other = this.getAdditionalEsriData(progressMonitor,
|
||||
this.getRequestForBbox(this.lon1, this.lat1, this.lon2, this.lat2), size);
|
||||
ds.mergeFrom(other, progressMonitor.createSubTaskMonitor(0, false));
|
||||
}
|
||||
|
@ -280,15 +268,15 @@ public class BoundingBoxMapWithAIDownloader extends BoundingBoxDownloader {
|
|||
}
|
||||
|
||||
private DataSet getAdditionalEsriData(ProgressMonitor progressMonitor, String baseUrl, int size) {
|
||||
DataSet returnDs = new DataSet();
|
||||
final var returnDs = new DataSet();
|
||||
try {
|
||||
HttpClient client = HttpClient.create(new URL(baseUrl + "&returnCountOnly=true"));
|
||||
final var client = HttpClient.create(new URL(baseUrl + "&returnCountOnly=true"));
|
||||
int objects = Integer.MIN_VALUE;
|
||||
try (InputStream is = client.connect().getContent(); JsonParser parser = Json.createParser(is)) {
|
||||
while (parser.hasNext()) {
|
||||
JsonParser.Event event = parser.next();
|
||||
final var event = parser.next();
|
||||
if (event == JsonParser.Event.START_OBJECT) {
|
||||
OptionalInt objCount = parser.getObjectStream()
|
||||
final var objCount = parser.getObjectStream()
|
||||
.filter(entry -> "properties".equals(entry.getKey()))
|
||||
.map(entry -> entry.getValue().asJsonObject())
|
||||
.mapToInt(properties -> properties.getInt("count")).findFirst();
|
||||
|
@ -310,7 +298,7 @@ public class BoundingBoxMapWithAIDownloader extends BoundingBoxDownloader {
|
|||
// We have already downloaded some of the objects. Set the ticks.
|
||||
progressMonitor.worked(size);
|
||||
while (progressMonitor.getTicks() < progressMonitor.getTicksCount() - 1) {
|
||||
DataSet next = new BoundingBoxMapWithAIDownloader(this.downloadArea, this.info, this.crop,
|
||||
final var next = new BoundingBoxMapWithAIDownloader(this.downloadArea, this.info, this.crop,
|
||||
this.start + size).parseOsm(progressMonitor.createSubTaskMonitor(0, false));
|
||||
progressMonitor.worked((int) next.allPrimitives().stream().filter(IPrimitive::isTagged).count());
|
||||
returnDs.mergeFrom(next);
|
||||
|
@ -348,7 +336,7 @@ public class BoundingBoxMapWithAIDownloader extends BoundingBoxDownloader {
|
|||
|
||||
@Override
|
||||
protected void adaptRequest(HttpClient request) {
|
||||
final StringBuilder defaultUserAgent = new StringBuilder();
|
||||
final var defaultUserAgent = new StringBuilder();
|
||||
request.setReadTimeout(DEFAULT_TIMEOUT);
|
||||
defaultUserAgent.append(request.getHeaders().get("User-Agent"));
|
||||
if (defaultUserAgent.toString().trim().length() == 0) {
|
||||
|
|
|
@ -59,6 +59,11 @@ public final class MapWithAIDataUtils {
|
|||
public static final int MAXIMUM_SIDE_DIMENSIONS = 10_000; // RapiD is about 1 km, max is 10 km, but 10 km causes
|
||||
// timeouts
|
||||
private static final int TOO_MANY_BBOXES = 4;
|
||||
/**
|
||||
* {@code true} if we need a fork join pool that is not the
|
||||
* {@link ForkJoinPool#commonPool()}
|
||||
*/
|
||||
private static Boolean requiresForkJoinPool;
|
||||
private static ForkJoinPool forkJoinPool;
|
||||
static final Object LAYER_LOCK = new Object();
|
||||
|
||||
|
@ -248,7 +253,10 @@ public final class MapWithAIDataUtils {
|
|||
* @return The {@link ForkJoinPool} for MapWithAI use.
|
||||
*/
|
||||
public static ForkJoinPool getForkJoinPool() {
|
||||
if (Utils.isRunningWebStart() || System.getSecurityManager() != null) {
|
||||
if (requiresForkJoinPool == null) {
|
||||
requiresForkJoinPool = Utils.isRunningWebStart() || System.getSecurityManager() != null;
|
||||
}
|
||||
if (requiresForkJoinPool) {
|
||||
synchronized (MapWithAIDataUtils.class) {
|
||||
if (Objects.isNull(forkJoinPool) || forkJoinPool.isShutdown()) {
|
||||
forkJoinPool = Utils.newForkJoinPool(MapWithAIPlugin.NAME.concat(".forkjoinpoolthreads"),
|
||||
|
|
|
@ -9,6 +9,7 @@ import javax.json.JsonObject;
|
|||
import javax.json.JsonValue;
|
||||
import javax.json.stream.JsonParser;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
|
@ -140,7 +141,7 @@ public class MapWithAIInfo extends
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder s = new StringBuilder("MapWithAIPreferenceEntry [name=").append(name);
|
||||
final var s = new StringBuilder("MapWithAIPreferenceEntry [name=").append(name);
|
||||
if (id != null) {
|
||||
s.append(" id=").append(id);
|
||||
}
|
||||
|
@ -154,6 +155,7 @@ public class MapWithAIInfo extends
|
|||
*/
|
||||
public static class MapWithAIInfoCategoryComparator implements Comparator<MapWithAIInfo>, Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = -7992892476979310835L;
|
||||
|
||||
@Override
|
||||
|
@ -219,15 +221,8 @@ public class MapWithAIInfo extends
|
|||
*/
|
||||
public MapWithAIInfo(String name, String url, String type, String eulaAcceptanceRequired, String id) {
|
||||
this(name, url, id);
|
||||
MapWithAIType t = MapWithAIType.fromString(type);
|
||||
this.setEulaAcceptanceRequired(eulaAcceptanceRequired);
|
||||
if (t != null) {
|
||||
super.setSourceType(t);
|
||||
} else if (type != null && !type.isEmpty()) {
|
||||
throw new IllegalArgumentException("unknown type: " + type);
|
||||
} else {
|
||||
super.setSourceType(MapWithAIType.THIRD_PARTY.getDefault());
|
||||
}
|
||||
super.setSourceType(MapWithAIType.fromString(type));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -243,7 +238,7 @@ public class MapWithAIInfo extends
|
|||
setCookies(e.cookies);
|
||||
setEulaAcceptanceRequired(e.eula);
|
||||
if (e.parameters != null) {
|
||||
try (JsonParser parser = Json.createParser(new StringReader(e.parameters))) {
|
||||
try (var parser = Json.createParser(new StringReader(e.parameters))) {
|
||||
if (parser.hasNext() && JsonParser.Event.START_ARRAY == parser.next()) {
|
||||
setParameters(parser.getArray());
|
||||
}
|
||||
|
@ -379,7 +374,7 @@ public class MapWithAIInfo extends
|
|||
return true;
|
||||
}
|
||||
if (first != null && second != null && first.size() == second.size()) {
|
||||
for (JsonObject value : Utils.filteredCollection(first, JsonObject.class)) {
|
||||
for (var value : Utils.filteredCollection(first, JsonObject.class)) {
|
||||
if (value.containsKey(PARAMETER_STRING)
|
||||
&& value.get(PARAMETER_STRING).getValueType() == JsonValue.ValueType.STRING
|
||||
&& Utils.filteredCollection(second, JsonObject.class).stream()
|
||||
|
@ -483,35 +478,42 @@ public class MapWithAIInfo extends
|
|||
return getParametersString(this.conflationParameters);
|
||||
}
|
||||
|
||||
public String getUrlExpanded() {
|
||||
StringBuilder sb;
|
||||
if (this.isConflated()) {
|
||||
sb = getConflationUrl();
|
||||
} else {
|
||||
sb = getNonConflatedUrl();
|
||||
/**
|
||||
* Check if this source will have a valid URL when {@link #getUrlExpanded()} is
|
||||
* called
|
||||
*
|
||||
* @return {@code true} if this source will have a valid url
|
||||
*/
|
||||
public boolean hasValidUrl() {
|
||||
return this.url != null || (this.isConflated() && this.conflationUrl != null);
|
||||
}
|
||||
|
||||
public String getUrlExpanded() {
|
||||
if (this.isConflated()) {
|
||||
return getConflationUrl().toString();
|
||||
} else {
|
||||
return getNonConflatedUrl().toString();
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private StringBuilder getConflationUrl() {
|
||||
if (conflationUrl == null) {
|
||||
return getNonConflatedUrl();
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
final var sb = new StringBuilder();
|
||||
if (this.conflationUrl.contains("{id}") && this.id != null) {
|
||||
sb.append(conflationUrl.replace("{id}", this.id));
|
||||
} else if (this.conflationUrl.contains("{id}")) {
|
||||
// We need to trigger synchronization. This means that the current download
|
||||
// may won't be conflated.
|
||||
// But this should automatically correct the behavior for the next attempt.
|
||||
final MapWithAILayerInfo mwli = MapWithAILayerInfo.getInstance();
|
||||
final var mwli = MapWithAILayerInfo.getInstance();
|
||||
mwli.load(false, () -> {
|
||||
Optional<MapWithAIInfo> defaultLayer = mwli.getAllDefaultLayers().stream().filter(this::equals)
|
||||
.findFirst();
|
||||
final var defaultLayer = mwli.getAllDefaultLayers().stream().filter(this::equals).findFirst();
|
||||
if (defaultLayer.isPresent()) {
|
||||
this.id = defaultLayer.get().id;
|
||||
} else {
|
||||
MapWithAIInfo newInfo = mwli.getAllDefaultLayers().stream()
|
||||
final var newInfo = mwli.getAllDefaultLayers().stream()
|
||||
.filter(layer -> Objects.equals(this.url, layer.url) && Objects.nonNull(layer.id))
|
||||
.findFirst().orElse(this);
|
||||
this.id = newInfo.id;
|
||||
|
@ -530,7 +532,7 @@ public class MapWithAIInfo extends
|
|||
}
|
||||
|
||||
private StringBuilder getNonConflatedUrl() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
final var sb = new StringBuilder();
|
||||
if (url != null && !url.trim().isEmpty()) {
|
||||
sb.append(url);
|
||||
if (MapWithAIType.ESRI_FEATURE_SERVER == sourceType) {
|
||||
|
@ -624,7 +626,7 @@ public class MapWithAIInfo extends
|
|||
* @param parameters Set the conflation parameters
|
||||
*/
|
||||
public void setConflationParameters(JsonArray parameters) {
|
||||
this.conflationParameters = parameters != null ? Json.createArrayBuilder(parameters).build() : null;
|
||||
this.conflationParameters = parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -7,6 +7,8 @@ import javax.annotation.Nonnull;
|
|||
import javax.swing.SwingUtilities;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Serial;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
@ -16,7 +18,6 @@ import java.util.HashSet;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
|
@ -80,7 +81,7 @@ public class MapWithAILayerInfo {
|
|||
if (instance != null) {
|
||||
return instance;
|
||||
}
|
||||
final AtomicBoolean finished = new AtomicBoolean();
|
||||
final var finished = new AtomicBoolean();
|
||||
synchronized (MapWithAILayerInfo.class) {
|
||||
if (instance == null) {
|
||||
instance = new MapWithAILayerInfo(() -> {
|
||||
|
@ -156,16 +157,6 @@ public class MapWithAILayerInfo {
|
|||
layerIds.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the custom as well as default imagery entries.
|
||||
*
|
||||
* @param fastFail whether opening HTTP connections should fail fast, see
|
||||
* {@link ImageryReader#setFastFail(boolean)}
|
||||
*/
|
||||
public void load(boolean fastFail) {
|
||||
load(fastFail, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the custom as well as default imagery entries.
|
||||
*
|
||||
|
@ -175,12 +166,12 @@ public class MapWithAILayerInfo {
|
|||
*/
|
||||
public void load(boolean fastFail, FinishListener listener) {
|
||||
clear();
|
||||
List<MapWithAIPreferenceEntry> entries = StructUtils.getListOfStructs(Config.getPref(),
|
||||
CONFIG_PREFIX + "entries", null, MapWithAIPreferenceEntry.class);
|
||||
final var entries = StructUtils.getListOfStructs(Config.getPref(), CONFIG_PREFIX + "entries", null,
|
||||
MapWithAIPreferenceEntry.class);
|
||||
if (entries != null) {
|
||||
for (MapWithAIPreferenceEntry prefEntry : entries) {
|
||||
try {
|
||||
MapWithAIInfo i = new MapWithAIInfo(prefEntry);
|
||||
final var i = new MapWithAIInfo(prefEntry);
|
||||
add(i);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Logging.warn("Unable to load imagery preference entry:" + e);
|
||||
|
@ -200,7 +191,7 @@ public class MapWithAILayerInfo {
|
|||
|
||||
/**
|
||||
* Loads the available imagery entries.
|
||||
*
|
||||
* <p>
|
||||
* The data is downloaded from the JOSM website (or loaded from cache). Entries
|
||||
* marked as "default" are added to the user selection, if not already present.
|
||||
*
|
||||
|
@ -214,7 +205,7 @@ public class MapWithAILayerInfo {
|
|||
* @since 12634
|
||||
*/
|
||||
public void loadDefaults(boolean clearCache, ForkJoinPool worker, boolean fastFail, FinishListener listener) {
|
||||
final DefaultEntryLoader loader = new DefaultEntryLoader(clearCache, fastFail);
|
||||
final var loader = new DefaultEntryLoader(clearCache, fastFail);
|
||||
if (this.finishListenerListenerList == null) {
|
||||
this.finishListenerListenerList = ListenerList.create();
|
||||
}
|
||||
|
@ -222,7 +213,7 @@ public class MapWithAILayerInfo {
|
|||
this.finishListenerListenerList.addListener(listener);
|
||||
}
|
||||
if (worker == null) {
|
||||
PleaseWaitRunnable pleaseWaitRunnable = new PleaseWaitRunnable(tr("Update default entries")) {
|
||||
final var pleaseWaitRunnable = new PleaseWaitRunnable(tr("Update default entries")) {
|
||||
@Override
|
||||
protected void cancel() {
|
||||
loader.canceled = true;
|
||||
|
@ -262,6 +253,7 @@ public class MapWithAILayerInfo {
|
|||
*/
|
||||
class DefaultEntryLoader extends RecursiveTask<List<MapWithAIInfo>> {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 12550342142551680L;
|
||||
private final boolean clearCache;
|
||||
private final boolean fastFail;
|
||||
|
@ -329,7 +321,7 @@ public class MapWithAILayerInfo {
|
|||
reader = new MapWithAISourceReader(source);
|
||||
this.reader.setClearCache(this.clearCache);
|
||||
reader.setFastFail(fastFail);
|
||||
Collection<MapWithAIInfo> result = reader.parse().orElse(Collections.emptyList());
|
||||
final var result = reader.parse().orElse(Collections.emptyList());
|
||||
// This is called here to "pre-cache" the layer information, to avoid blocking
|
||||
// the EDT
|
||||
this.updateEsriLayers(result);
|
||||
|
@ -346,9 +338,9 @@ public class MapWithAILayerInfo {
|
|||
* @param layers The layers to update
|
||||
*/
|
||||
private void updateEsriLayers(@Nonnull final Collection<MapWithAIInfo> layers) {
|
||||
for (MapWithAIInfo layer : layers) {
|
||||
for (var layer : layers) {
|
||||
if (MapWithAIType.ESRI == layer.getSourceType()) {
|
||||
for (ForkJoinTask<MapWithAIInfo> future : parseEsri(layer)) {
|
||||
for (var future : parseEsri(layer)) {
|
||||
try {
|
||||
allDefaultLayers.add(future.get());
|
||||
} catch (InterruptedException e) {
|
||||
|
@ -385,8 +377,9 @@ public class MapWithAILayerInfo {
|
|||
if (!loadError && !defaultLayerIds.isEmpty()) {
|
||||
dropOldEntries();
|
||||
}
|
||||
final ListenerList<FinishListener> listenerList = MapWithAILayerInfo.this.finishListenerListenerList;
|
||||
final var listenerList = MapWithAILayerInfo.this.finishListenerListenerList;
|
||||
MapWithAILayerInfo.this.finishListenerListenerList = null;
|
||||
Config.getPref().putLong("mapwithai.layerinfo.lastupdated", Instant.now().getEpochSecond());
|
||||
if (listenerList != null) {
|
||||
listenerList.fireEvent(FinishListener::onFinish);
|
||||
}
|
||||
|
@ -415,8 +408,8 @@ public class MapWithAILayerInfo {
|
|||
*/
|
||||
private void buildIdMap(List<MapWithAIInfo> lst, Map<String, MapWithAIInfo> idMap) {
|
||||
idMap.clear();
|
||||
Set<String> notUnique = new HashSet<>();
|
||||
for (MapWithAIInfo i : lst) {
|
||||
final var notUnique = new HashSet<String>();
|
||||
for (var i : lst) {
|
||||
if (i.getId() != null) {
|
||||
if (idMap.containsKey(i.getId())) {
|
||||
notUnique.add(i.getId());
|
||||
|
@ -427,7 +420,7 @@ public class MapWithAILayerInfo {
|
|||
idMap.put(i.getId(), i);
|
||||
}
|
||||
}
|
||||
for (String i : notUnique) {
|
||||
for (var i : notUnique) {
|
||||
idMap.remove(i);
|
||||
}
|
||||
}
|
||||
|
@ -441,14 +434,14 @@ public class MapWithAILayerInfo {
|
|||
*/
|
||||
public void updateEntriesFromDefaults(boolean dropold) {
|
||||
// add new default entries to the user selection
|
||||
boolean changed = false;
|
||||
Collection<String> knownDefaults = new TreeSet<>(Config.getPref().getList(CONFIG_PREFIX + "layers.default"));
|
||||
Collection<String> newKnownDefaults = new TreeSet<>();
|
||||
var changed = false;
|
||||
final var knownDefaults = new TreeSet<>(Config.getPref().getList(CONFIG_PREFIX + "layers.default"));
|
||||
final var newKnownDefaults = new TreeSet<String>();
|
||||
synchronized (defaultLayers) {
|
||||
for (MapWithAIInfo def : defaultLayers) {
|
||||
for (var def : defaultLayers) {
|
||||
if (def.isDefaultEntry()) {
|
||||
boolean isKnownDefault = false;
|
||||
for (String entry : knownDefaults) {
|
||||
var isKnownDefault = false;
|
||||
for (var entry : knownDefaults) {
|
||||
if (entry.equals(def.getId())) {
|
||||
isKnownDefault = true;
|
||||
newKnownDefaults.add(entry);
|
||||
|
@ -463,11 +456,11 @@ public class MapWithAILayerInfo {
|
|||
break;
|
||||
}
|
||||
}
|
||||
boolean isInUserList = false;
|
||||
var isInUserList = false;
|
||||
if (!isKnownDefault) {
|
||||
if (def.getId() != null) {
|
||||
newKnownDefaults.add(def.getId());
|
||||
for (MapWithAIInfo i : layers) {
|
||||
for (var i : layers) {
|
||||
if (isSimilar(def, i)) {
|
||||
isInUserList = true;
|
||||
break;
|
||||
|
@ -490,12 +483,12 @@ public class MapWithAILayerInfo {
|
|||
Config.getPref().putList(CONFIG_PREFIX + "layers.default", new ArrayList<>(newKnownDefaults));
|
||||
|
||||
// automatically update user entries with same id as a default entry
|
||||
for (int i = 0; i < layers.size(); i++) {
|
||||
MapWithAIInfo info = layers.get(i);
|
||||
for (var i = 0; i < layers.size(); i++) {
|
||||
final var info = layers.get(i);
|
||||
if (info.getId() == null) {
|
||||
continue;
|
||||
}
|
||||
MapWithAIInfo matchingDefault = defaultLayerIds.get(info.getId());
|
||||
final var matchingDefault = defaultLayerIds.get(info.getId());
|
||||
if (matchingDefault != null && !matchingDefault.equalsPref(info)) {
|
||||
layers.set(i, matchingDefault);
|
||||
Logging.info(tr("Update imagery ''{0}''", info.getName()));
|
||||
|
@ -514,9 +507,9 @@ public class MapWithAILayerInfo {
|
|||
* @since 11527
|
||||
*/
|
||||
public void dropOldEntries() {
|
||||
List<String> drop = new ArrayList<>();
|
||||
final var drop = new ArrayList<String>();
|
||||
|
||||
for (Map.Entry<String, MapWithAIInfo> info : layerIds.entrySet()) {
|
||||
for (var info : layerIds.entrySet()) {
|
||||
if (!defaultLayerIds.containsKey(info.getKey())) {
|
||||
remove(info.getValue());
|
||||
drop.add(info.getKey());
|
||||
|
@ -525,7 +518,7 @@ public class MapWithAILayerInfo {
|
|||
}
|
||||
|
||||
if (!drop.isEmpty()) {
|
||||
for (String id : drop) {
|
||||
for (var id : drop) {
|
||||
layerIds.remove(id);
|
||||
}
|
||||
save();
|
||||
|
@ -573,9 +566,9 @@ public class MapWithAILayerInfo {
|
|||
* Save the list of imagery entries to preferences.
|
||||
*/
|
||||
public synchronized void save() {
|
||||
List<MapWithAIPreferenceEntry> entries = new ArrayList<>();
|
||||
final var entries = new ArrayList<MapWithAIPreferenceEntry>();
|
||||
synchronized (layers) {
|
||||
for (MapWithAIInfo info : layers) {
|
||||
for (var info : layers) {
|
||||
entries.add(new MapWithAIPreferenceEntry(info));
|
||||
}
|
||||
}
|
||||
|
@ -625,8 +618,7 @@ public class MapWithAILayerInfo {
|
|||
return layers.stream()
|
||||
.filter(i -> i.getCategory() != MapWithAICategory.PREVIEW
|
||||
&& !i.getAdditionalCategories().contains(MapWithAICategory.PREVIEW))
|
||||
.filter(info -> !Utils.isBlank(info.getUrlExpanded()) && !Utils.isBlank(info.getUrl()))
|
||||
.collect(Collectors.toList());
|
||||
.filter(MapWithAIInfo::hasValidUrl).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -652,7 +644,7 @@ public class MapWithAILayerInfo {
|
|||
|
||||
/**
|
||||
* Get unique id for ImageryInfo.
|
||||
*
|
||||
* <p>
|
||||
* This takes care, that no id is used twice (due to a user error)
|
||||
*
|
||||
* @param info the ImageryInfo to look up
|
||||
|
|
|
@ -37,7 +37,7 @@ public class MapWithAIDownloadOptions extends JPanel implements DownloadSelectio
|
|||
*/
|
||||
public MapWithAIDownloadOptions() {
|
||||
optionPanel = new JPanel(new GridBagLayout());
|
||||
JPanel infoHeader = new JPanel();
|
||||
final var infoHeader = new JPanel();
|
||||
infoHeader.add(new JLabel("Browse and activate extra data sets to facilitate your mapping needs."));
|
||||
optionPanel.add(infoHeader, GBC.eol().fill(GridBagConstraints.HORIZONTAL).anchor(GridBagConstraints.NORTH));
|
||||
mapwithaiProvidersPanel = new MapWithAIProvidersPanel(this);
|
||||
|
@ -63,7 +63,7 @@ public class MapWithAIDownloadOptions extends JPanel implements DownloadSelectio
|
|||
@Override
|
||||
public void destroy() {
|
||||
if (this.iGui != null) {
|
||||
for (JComponent component : getJComponents(this.iGui.getComponents())) {
|
||||
for (var component : getJComponents(this.iGui.getComponents())) {
|
||||
removeFromComponent(component);
|
||||
}
|
||||
this.iGui.removeDownloadAreaListener(this);
|
||||
|
@ -77,7 +77,7 @@ public class MapWithAIDownloadOptions extends JPanel implements DownloadSelectio
|
|||
}
|
||||
|
||||
private boolean removeFromComponent(JComponent component) {
|
||||
for (JComponent newComponent : getJComponents(component.getComponents())) {
|
||||
for (var newComponent : getJComponents(component.getComponents())) {
|
||||
if (optionPanel.equals(newComponent)) {
|
||||
component.remove(optionPanel);
|
||||
return true;
|
||||
|
|
|
@ -25,10 +25,9 @@ import java.util.Arrays;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.openstreetmap.josm.actions.ExpertToggleAction;
|
||||
import org.openstreetmap.josm.data.preferences.BooleanProperty;
|
||||
import org.openstreetmap.josm.gui.preferences.DefaultTabPreferenceSetting;
|
||||
import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane;
|
||||
import org.openstreetmap.josm.gui.preferences.advanced.PrefEntry;
|
||||
|
@ -68,17 +67,14 @@ public class MapWithAIPreferences extends DefaultTabPreferenceSetting {
|
|||
}
|
||||
|
||||
private static void fillReplacementTagDisplayData(List<PrefEntry> list) {
|
||||
final Map<String, String> current = new TreeMap<>(MapWithAIPreferenceHelper.getReplacementTags());
|
||||
for (final Map.Entry<String, String> entry : current.entrySet()) {
|
||||
list.add(
|
||||
new PrefEntry(entry.getKey(), new StringSetting(entry.getValue()), new StringSetting(null), false));
|
||||
}
|
||||
MapWithAIPreferenceHelper.getReplacementTags().forEach(
|
||||
(key, value) -> list.add(new PrefEntry(key, new StringSetting(value), new StringSetting(null), false)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addGui(PreferenceTabbedPane gui) {
|
||||
final JPanel p = gui.createPreferenceTab(this);
|
||||
final JTabbedPane panel = getTabPane();
|
||||
final var p = gui.createPreferenceTab(this);
|
||||
final var panel = getTabPane();
|
||||
if (panel.getTabCount() == 0) {
|
||||
panel.addTab(tr("Servers"), getServerList(gui));
|
||||
panel.addTab(tr("Settings"), getSettingsPanel(gui));
|
||||
|
@ -91,12 +87,12 @@ public class MapWithAIPreferences extends DefaultTabPreferenceSetting {
|
|||
}
|
||||
|
||||
private Component getSettingsPanel(PreferenceTabbedPane gui) {
|
||||
final JPanel pane = new JPanel(new GridBagLayout());
|
||||
final int width = 200;
|
||||
final int height = 200;
|
||||
final JLabel switchLayer = new JLabel(tr("Automatically switch layers"));
|
||||
final JLabel maximumAddition = new JLabel(tr("Maximum features (add)"));
|
||||
final JLabel mergeBuildingWithAddress = new JLabel(tr("Merge address nodes and buildings"));
|
||||
final var pane = new JPanel(new GridBagLayout());
|
||||
final var width = 200;
|
||||
final var height = 200;
|
||||
final var switchLayer = new JLabel(tr("Automatically switch layers"));
|
||||
final var maximumAddition = new JLabel(tr("Maximum features (add)"));
|
||||
final var mergeBuildingWithAddress = new JLabel(tr("Merge address nodes and buildings"));
|
||||
|
||||
switchLayer.setToolTipText(
|
||||
tr("If checked, automatically switch from the {0} layer to the OSM layer when objects are added",
|
||||
|
@ -116,9 +112,9 @@ public class MapWithAIPreferences extends DefaultTabPreferenceSetting {
|
|||
pane.setAlignmentY(Component.TOP_ALIGNMENT);
|
||||
pane.setAlignmentX(Component.LEFT_ALIGNMENT);
|
||||
|
||||
final GBC first = GBC.std().weight(0, 1).anchor(GridBagConstraints.WEST);
|
||||
final GBC second = GBC.eol().fill(GridBagConstraints.HORIZONTAL);
|
||||
final GBC buttonInsets = GBC.std().insets(5, 5, 0, 0);
|
||||
final var first = GBC.std().weight(0, 1).anchor(GridBagConstraints.WEST);
|
||||
final var second = GBC.eol().fill(GridBagConstraints.HORIZONTAL);
|
||||
final var buttonInsets = GBC.std().insets(5, 5, 0, 0);
|
||||
|
||||
pane.add(switchLayer, first);
|
||||
|
||||
|
@ -132,11 +128,11 @@ public class MapWithAIPreferences extends DefaultTabPreferenceSetting {
|
|||
pane.add(mergeBuildingWithAddress, first);
|
||||
pane.add(mergeBuildingAddressCheckBox, second);
|
||||
|
||||
final Component expertHorizontalGlue = Box.createHorizontalGlue();
|
||||
final var expertHorizontalGlue = Box.createHorizontalGlue();
|
||||
pane.add(expertHorizontalGlue, GBC.eol().fill(GridBagConstraints.HORIZONTAL));
|
||||
final JLabel previewFeatureSets = new JLabel(tr("Show Preview DataSets"));
|
||||
final JCheckBox previewFeatureSetCheckbox = new JCheckBox();
|
||||
BooleanProperty previewFeatureSetProperty = MapWithAILayerInfo.SHOW_PREVIEW;
|
||||
final var previewFeatureSets = new JLabel(tr("Show Preview DataSets"));
|
||||
final var previewFeatureSetCheckbox = new JCheckBox();
|
||||
final var previewFeatureSetProperty = MapWithAILayerInfo.SHOW_PREVIEW;
|
||||
previewFeatureSetCheckbox.setToolTipText(tr("If selected, show datasets which may have various issues"));
|
||||
previewFeatureSetCheckbox.setSelected(Boolean.TRUE.equals(previewFeatureSetProperty.get()));
|
||||
previewFeatureSetCheckbox
|
||||
|
@ -144,19 +140,19 @@ public class MapWithAIPreferences extends DefaultTabPreferenceSetting {
|
|||
pane.add(previewFeatureSets, first);
|
||||
pane.add(previewFeatureSetCheckbox, second);
|
||||
|
||||
final JLabel replacementTags = new JLabel(tr("Replacement Tags (to be replaced on download)"));
|
||||
final var replacementTags = new JLabel(tr("Replacement Tags (to be replaced on download)"));
|
||||
pane.add(replacementTags, first);
|
||||
final JScrollPane scroll2 = new JScrollPane(replacementPreferenceTable);
|
||||
final var scroll2 = new JScrollPane(replacementPreferenceTable);
|
||||
pane.add(scroll2, GBC.eol().fill(GridBagConstraints.BOTH));
|
||||
scroll2.setPreferredSize(new Dimension(width, height));
|
||||
|
||||
pane.add(new JLabel(), first);
|
||||
final JPanel replaceAddEditDeleteScroll2 = new JPanel(new GridBagLayout());
|
||||
final var replaceAddEditDeleteScroll2 = new JPanel(new GridBagLayout());
|
||||
pane.add(replaceAddEditDeleteScroll2, second);
|
||||
final JButton addScroll2 = new JButton(tr("Add"));
|
||||
final var addScroll2 = new JButton(tr("Add"));
|
||||
replaceAddEditDeleteScroll2.add(addScroll2, buttonInsets);
|
||||
addScroll2.addActionListener(e -> {
|
||||
final PrefEntry pe = replacementPreferenceTable.addPreference(gui);
|
||||
final var pe = replacementPreferenceTable.addPreference(gui);
|
||||
if ((pe != null) && (pe.getValue() instanceof StringSetting)) {
|
||||
replacementTableDisplayData.add(pe);
|
||||
Collections.sort(replacementTableDisplayData);
|
||||
|
@ -164,19 +160,19 @@ public class MapWithAIPreferences extends DefaultTabPreferenceSetting {
|
|||
}
|
||||
});
|
||||
|
||||
final JButton editScroll2 = new JButton(tr("Edit"));
|
||||
final var editScroll2 = new JButton(tr("Edit"));
|
||||
replaceAddEditDeleteScroll2.add(editScroll2, buttonInsets);
|
||||
editScroll2.addActionListener(e -> {
|
||||
final List<PrefEntry> toEdit = replacementPreferenceTable.getSelectedItems();
|
||||
final var toEdit = replacementPreferenceTable.getSelectedItems();
|
||||
if (toEdit.size() == MAX_SELECTED_TO_EDIT) {
|
||||
replacementPreferenceTable.editPreference(gui);
|
||||
}
|
||||
});
|
||||
|
||||
final JButton deleteScroll2 = new JButton(tr("Delete"));
|
||||
final var deleteScroll2 = new JButton(tr("Delete"));
|
||||
replaceAddEditDeleteScroll2.add(deleteScroll2, buttonInsets);
|
||||
deleteScroll2.addActionListener(e -> {
|
||||
final List<PrefEntry> toRemove = replacementPreferenceTable.getSelectedItems();
|
||||
final var toRemove = replacementPreferenceTable.getSelectedItems();
|
||||
if (!toRemove.isEmpty()) {
|
||||
replacementTableDisplayData.removeAll(toRemove);
|
||||
}
|
||||
|
@ -185,7 +181,7 @@ public class MapWithAIPreferences extends DefaultTabPreferenceSetting {
|
|||
|
||||
pane.add(Box.createHorizontalGlue(), second);
|
||||
|
||||
JButton kaartLogo = new JButton(ImageProvider.getIfAvailable("kaart") == null ? null
|
||||
final var kaartLogo = new JButton(ImageProvider.getIfAvailable("kaart") == null ? null
|
||||
: new ImageProvider("kaart").setHeight(ImageProvider.ImageSizes.SETTINGS_TAB.getAdjustedHeight())
|
||||
.get());
|
||||
kaartLogo.setToolTipText(tr("Link to source code repository"));
|
||||
|
@ -198,7 +194,7 @@ public class MapWithAIPreferences extends DefaultTabPreferenceSetting {
|
|||
kaartLogo.setCursor(new Cursor(Cursor.HAND_CURSOR));
|
||||
pane.add(kaartLogo, GBC.std().anchor(GridBagConstraints.WEST));
|
||||
|
||||
JButton mapWithAILogo = new JButton(ImageProvider.getIfAvailable("mapwithai_text") == null ? null
|
||||
final var mapWithAILogo = new JButton(ImageProvider.getIfAvailable("mapwithai_text") == null ? null
|
||||
: new ImageProvider("mapwithai_text")
|
||||
.setHeight(ImageProvider.ImageSizes.SETTINGS_TAB.getAdjustedHeight()).get());
|
||||
mapWithAILogo.setCursor(new Cursor(Cursor.HAND_CURSOR));
|
||||
|
@ -218,22 +214,21 @@ public class MapWithAIPreferences extends DefaultTabPreferenceSetting {
|
|||
@Override
|
||||
public boolean ok() {
|
||||
MapWithAIPreferenceHelper.setSwitchLayers(switchLayerCheckBox.isSelected(), true);
|
||||
final Object value = maximumAdditionSpinner.getValue();
|
||||
final var value = maximumAdditionSpinner.getValue();
|
||||
MapWithAIPreferenceHelper.setMergeBuildingAddress(this.mergeBuildingAddressCheckBox.isSelected(), true);
|
||||
if (value instanceof Number) {
|
||||
MapWithAIPreferenceHelper.setMaximumAddition(((Number) value).intValue(), true);
|
||||
if (value instanceof Number number) {
|
||||
MapWithAIPreferenceHelper.setMaximumAddition(number.intValue(), true);
|
||||
}
|
||||
MapWithAILayerInfo.getInstance().save();
|
||||
MapWithAILayerInfo.getInstance().clear();
|
||||
MapWithAILayerInfo.getInstance().load(false);
|
||||
MapWithAILayerInfo.getInstance().load(false, null);
|
||||
MapWithAIPreferenceHelper.setReplacementTags(convertReplacementPrefToMap(replacementTableDisplayData));
|
||||
return false;
|
||||
}
|
||||
|
||||
private static Map<String, String> convertReplacementPrefToMap(List<PrefEntry> displayData) {
|
||||
final Map<String, String> returnMap = displayData.isEmpty() ? Collections.emptyMap() : new TreeMap<>();
|
||||
displayData.forEach(entry -> returnMap.put(entry.getKey(), entry.getValue().getValue().toString()));
|
||||
return returnMap;
|
||||
return displayData.stream()
|
||||
.collect(Collectors.toMap(PrefEntry::getKey, entry -> entry.getValue().getValue().toString()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -4,6 +4,7 @@ package org.openstreetmap.josm.plugins.mapwithai.gui.preferences.mapwithai;
|
|||
import static org.openstreetmap.josm.tools.I18n.marktr;
|
||||
import static org.openstreetmap.josm.tools.I18n.tr;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.Box;
|
||||
import javax.swing.JButton;
|
||||
|
@ -18,7 +19,6 @@ import javax.swing.UIManager;
|
|||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
import javax.swing.table.DefaultTableCellRenderer;
|
||||
import javax.swing.table.TableColumnModel;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
|
@ -28,8 +28,8 @@ import java.awt.GridBagLayout;
|
|||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.io.IOException;
|
||||
import java.io.Serial;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
@ -37,7 +37,6 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
@ -47,13 +46,10 @@ import java.util.stream.Stream;
|
|||
import org.openstreetmap.gui.jmapviewer.Coordinate;
|
||||
import org.openstreetmap.gui.jmapviewer.MapPolygonImpl;
|
||||
import org.openstreetmap.gui.jmapviewer.MapRectangleImpl;
|
||||
import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate;
|
||||
import org.openstreetmap.gui.jmapviewer.interfaces.MapPolygon;
|
||||
import org.openstreetmap.gui.jmapviewer.interfaces.MapRectangle;
|
||||
import org.openstreetmap.josm.data.Bounds;
|
||||
import org.openstreetmap.josm.data.coor.LatLon;
|
||||
import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryBounds;
|
||||
import org.openstreetmap.josm.data.imagery.Shape;
|
||||
import org.openstreetmap.josm.data.preferences.NamedColorProperty;
|
||||
import org.openstreetmap.josm.gui.MainApplication;
|
||||
import org.openstreetmap.josm.gui.bbox.SlippyMapBBoxChooser;
|
||||
|
@ -92,6 +88,7 @@ public class MapWithAIProvidersPanel extends JPanel {
|
|||
SHOW_ACTIVE
|
||||
}
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = -5876039771496409422L;
|
||||
// Public JTables and JosmMapViewer
|
||||
/** The table of active providers **/
|
||||
|
@ -112,9 +109,9 @@ public class MapWithAIProvidersPanel extends JPanel {
|
|||
|
||||
// Public models
|
||||
/** The model of active providers **/
|
||||
public static final MapWithAILayerTableModel ACTIVE_MODEL = new MapWithAILayerTableModel();
|
||||
static final MapWithAILayerTableModel ACTIVE_MODEL = new MapWithAILayerTableModel();
|
||||
/** The model of default providers **/
|
||||
public static final MapWithAIDefaultLayerTableModel DEFAULT_MODEL = new MapWithAIDefaultLayerTableModel();
|
||||
static final MapWithAIDefaultLayerTableModel DEFAULT_MODEL = new MapWithAIDefaultLayerTableModel();
|
||||
|
||||
// Public JToolbars
|
||||
/** The toolbar on the right of active providers **/
|
||||
|
@ -188,24 +185,24 @@ public class MapWithAIProvidersPanel extends JPanel {
|
|||
public final Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
|
||||
boolean hasFocus, int row, int column) {
|
||||
T obj = (T) value;
|
||||
JLabel label = (JLabel) super.getTableCellRendererComponent(table, mapper.apply(obj), isSelected, hasFocus,
|
||||
row, column);
|
||||
Color defaultColor = UIManager.getColor("Table.background");
|
||||
Color selectedColor = UIManager.getColor("Table.selectionBackground");
|
||||
final var label = (JLabel) super.getTableCellRendererComponent(table, mapper.apply(obj), isSelected,
|
||||
hasFocus, row, column);
|
||||
final var defaultColor = UIManager.getColor("Table.background");
|
||||
final var selectedColor = UIManager.getColor("Table.selectionBackground");
|
||||
GuiHelper.setBackgroundReadable(label, defaultColor);
|
||||
|
||||
GuiHelper.setBackgroundReadable(label, isSelected ? selectedColor : defaultColor);
|
||||
if (this.highlightIfActive && obj != null) {
|
||||
MapWithAIInfo info = obj instanceof MapWithAIInfo ? (MapWithAIInfo) obj : reverseMapper.apply(obj);
|
||||
final var info = obj instanceof MapWithAIInfo ? (MapWithAIInfo) obj : reverseMapper.apply(obj);
|
||||
if (info == null) {
|
||||
GuiHelper.setBackgroundReadable(label, defaultColor);
|
||||
} else {
|
||||
if (MapWithAILayerTableModel.contains(info)) {
|
||||
Color t = IMAGERY_BACKGROUND_COLOR.get();
|
||||
final var t = IMAGERY_BACKGROUND_COLOR.get();
|
||||
GuiHelper.setBackgroundReadable(label, isSelected ? t.darker() : t);
|
||||
} else if (this.area != null && info.getBounds() != null
|
||||
&& (this.area.intersects(info.getBounds()) || info.getBounds().intersects(this.area))) {
|
||||
Color t = MAPWITHAI_AREA_BACKGROUND_COLOR.get();
|
||||
final var t = MAPWITHAI_AREA_BACKGROUND_COLOR.get();
|
||||
GuiHelper.setBackgroundReadable(label, isSelected ? t.darker() : t);
|
||||
} else {
|
||||
GuiHelper.setBackgroundReadable(label, isSelected ? selectedColor : defaultColor);
|
||||
|
@ -300,6 +297,7 @@ public class MapWithAIProvidersPanel extends JPanel {
|
|||
private static class MapWithAINameTableCellRenderer
|
||||
extends MapWithAIProvidersPanel.MapWithAITableCellRenderer<MapWithAIInfo> {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 6669934435517244629L;
|
||||
|
||||
MapWithAINameTableCellRenderer(boolean showActive) {
|
||||
|
@ -308,6 +306,26 @@ public class MapWithAIProvidersPanel extends JPanel {
|
|||
}
|
||||
}
|
||||
|
||||
private static class ProvidersTable extends JTable {
|
||||
@Serial
|
||||
private static final long serialVersionUID = -6136421378119093719L;
|
||||
|
||||
ProvidersTable() {
|
||||
super(ACTIVE_MODEL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getToolTipText(@Nonnull MouseEvent e) {
|
||||
final var p = e.getPoint();
|
||||
try {
|
||||
return ACTIVE_MODEL.getValueAt(rowAtPoint(p), columnAtPoint(p)).toString();
|
||||
} catch (ArrayIndexOutOfBoundsException ex) {
|
||||
Logging.debug(ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@code MapWithAIProvidersPanel}.
|
||||
*
|
||||
|
@ -320,21 +338,7 @@ public class MapWithAIProvidersPanel extends JPanel {
|
|||
this.options = options;
|
||||
boolean showActive = Arrays.asList(options).contains(Options.SHOW_ACTIVE);
|
||||
|
||||
activeTable = new JTable(ACTIVE_MODEL) {
|
||||
|
||||
private static final long serialVersionUID = -6136421378119093719L;
|
||||
|
||||
@Override
|
||||
public String getToolTipText(MouseEvent e) {
|
||||
java.awt.Point p = e.getPoint();
|
||||
try {
|
||||
return ACTIVE_MODEL.getValueAt(rowAtPoint(p), columnAtPoint(p)).toString();
|
||||
} catch (ArrayIndexOutOfBoundsException ex) {
|
||||
Logging.debug(ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
activeTable = new ProvidersTable();
|
||||
activeTable.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
|
||||
|
||||
defaultTable = new JTable(DEFAULT_MODEL);
|
||||
|
@ -346,25 +350,25 @@ public class MapWithAIProvidersPanel extends JPanel {
|
|||
|
||||
setupDefaultTable(defaultTable, options, areaListeners);
|
||||
|
||||
TableColumnModel mod = activeTable.getColumnModel();
|
||||
final var mod = activeTable.getColumnModel();
|
||||
mod.getColumn(1).setPreferredWidth(800);
|
||||
MapWithAIURLTableCellRenderer activeTableCellRenderer = new MapWithAIURLTableCellRenderer();
|
||||
final var activeTableCellRenderer = new MapWithAIURLTableCellRenderer();
|
||||
areaListeners.addListener(activeTableCellRenderer);
|
||||
mod.getColumn(1).setCellRenderer(activeTableCellRenderer);
|
||||
mod.getColumn(0).setMaxWidth(200);
|
||||
|
||||
RemoveEntryAction remove = new RemoveEntryAction();
|
||||
final var remove = new RemoveEntryAction();
|
||||
activeTable.getSelectionModel().addListSelectionListener(remove);
|
||||
|
||||
EditEntryAction edit = new EditEntryAction();
|
||||
final var edit = new EditEntryAction();
|
||||
activeTable.getSelectionModel().addListSelectionListener(edit);
|
||||
|
||||
add(new JLabel(tr("Available default entries:")), GBC.std().insets(5, 5, 0, 0));
|
||||
add(new JLabel(tr("Boundaries of selected MapWithAI entries:")), GBC.eol().insets(5, 5, 0, 0));
|
||||
|
||||
// Add default item list
|
||||
JPanel defaultPane = new JPanel(new GridBagLayout());
|
||||
JScrollPane scrolldef = new JScrollPane(defaultTable);
|
||||
final var defaultPane = new JPanel(new GridBagLayout());
|
||||
final var scrolldef = new JScrollPane(defaultTable);
|
||||
scrolldef.setPreferredSize(new Dimension(200, 200));
|
||||
defaultPane.add(defaultFilter, GBC.eol().insets(0, 0, 0, 0).fill(GridBagConstraints.HORIZONTAL));
|
||||
defaultPane.add(scrolldef, GBC.eol().insets(0, 0, 0, 0).fill(GridBagConstraints.BOTH));
|
||||
|
@ -384,10 +388,10 @@ public class MapWithAIProvidersPanel extends JPanel {
|
|||
defaultMap.setZoomControlsVisible(false);
|
||||
defaultMap.setMinimumSize(new Dimension(100, 200));
|
||||
defaultMap.addJMVListener(e -> {
|
||||
Rectangle2D visibleRect = defaultMap.getVisibleRect();
|
||||
ICoordinate max = defaultMap.getPosition((int) visibleRect.getMaxX(), (int) visibleRect.getMaxY());
|
||||
ICoordinate min = defaultMap.getPosition((int) visibleRect.getMinX(), (int) visibleRect.getMinY());
|
||||
Bounds b = new Bounds(
|
||||
final var visibleRect = defaultMap.getVisibleRect();
|
||||
final var max = defaultMap.getPosition((int) visibleRect.getMaxX(), (int) visibleRect.getMaxY());
|
||||
final var min = defaultMap.getPosition((int) visibleRect.getMinX(), (int) visibleRect.getMinY());
|
||||
final var b = new Bounds(
|
||||
new LatLon(Math.min(max.getLat(), min.getLat()),
|
||||
LatLon.toIntervalLon(Math.min(max.getLon(), min.getLon()))),
|
||||
new LatLon(Math.max(max.getLat(), min.getLat()),
|
||||
|
@ -411,15 +415,15 @@ public class MapWithAIProvidersPanel extends JPanel {
|
|||
defaultToolbar.add(new ReloadAction());
|
||||
add(defaultToolbar, GBC.eol().anchor(GBC.SOUTH).insets(0, 0, 5, 0));
|
||||
|
||||
HtmlPanel help = new HtmlPanel(
|
||||
final var help = new HtmlPanel(
|
||||
tr("New default entries can be added in the <a href=\"{0}\">GitHub Repository</a>.",
|
||||
"https://github.com/JOSM/MapWithAI/blob/pages/json/sources.json"));
|
||||
help.enableClickableHyperlinks();
|
||||
add(help, GBC.eol().insets(10, 0, 0, 0).fill(GBC.HORIZONTAL));
|
||||
|
||||
ActivateAction activate = new ActivateAction();
|
||||
final var activate = new ActivateAction();
|
||||
defaultTable.getSelectionModel().addListSelectionListener(activate);
|
||||
JButton btnActivate = new JButton(activate);
|
||||
final var btnActivate = new JButton(activate);
|
||||
|
||||
middleToolbar = new JToolBar(JToolBar.HORIZONTAL);
|
||||
middleToolbar.setFloatable(false);
|
||||
|
@ -430,7 +434,7 @@ public class MapWithAIProvidersPanel extends JPanel {
|
|||
|
||||
add(Box.createHorizontalGlue(), GBC.eol().fill(GridBagConstraints.HORIZONTAL));
|
||||
|
||||
JScrollPane scroll = new JScrollPane(activeTable);
|
||||
final var scroll = new JScrollPane(activeTable);
|
||||
scroll.setPreferredSize(new Dimension(200, 200));
|
||||
|
||||
activeToolbar = new JToolBar(JToolBar.VERTICAL);
|
||||
|
@ -452,7 +456,7 @@ public class MapWithAIProvidersPanel extends JPanel {
|
|||
ListenerList<AreaListener> areaListeners) {
|
||||
boolean showActive = Arrays.asList(options).contains(Options.SHOW_ACTIVE);
|
||||
int tenXWidth = defaultTable.getFontMetrics(defaultTable.getFont()).stringWidth("XXXXXXXXXX");
|
||||
TableColumnModel mod = defaultTable.getColumnModel();
|
||||
final var mod = defaultTable.getColumnModel();
|
||||
int urlWidth = (showActive ? 3 : 0) * tenXWidth;
|
||||
mod.getColumn(6).setCellRenderer(defaultTable.getDefaultRenderer(Boolean.class));
|
||||
mod.getColumn(6).setMaxWidth(20);
|
||||
|
@ -461,13 +465,13 @@ public class MapWithAIProvidersPanel extends JPanel {
|
|||
mod.getColumn(4).setPreferredWidth((showActive ? 2 : 0) * tenXWidth);
|
||||
mod.getColumn(4).setCellRenderer(new MapWithAIProviderTableCellRenderer());
|
||||
mod.getColumn(3).setPreferredWidth(urlWidth);
|
||||
MapWithAIURLTableCellRenderer defaultUrlTableCellRenderer = new MapWithAIURLTableCellRenderer();
|
||||
final var defaultUrlTableCellRenderer = new MapWithAIURLTableCellRenderer();
|
||||
mod.getColumn(3).setCellRenderer(defaultUrlTableCellRenderer);
|
||||
mod.getColumn(2).setPreferredWidth((int) ((showActive ? 0 : 0.3) * tenXWidth));
|
||||
|
||||
mod.getColumn(2).setCellRenderer(new MapWithAITypeTableCellRenderer());
|
||||
|
||||
MapWithAINameTableCellRenderer defaultNameTableCellRenderer = new MapWithAINameTableCellRenderer(!showActive);
|
||||
final var defaultNameTableCellRenderer = new MapWithAINameTableCellRenderer(!showActive);
|
||||
mod.getColumn(1).setCellRenderer(defaultNameTableCellRenderer);
|
||||
mod.getColumn(1).setPreferredWidth((showActive ? 3 : 2) * tenXWidth);
|
||||
mod.getColumn(0).setCellRenderer(new MapWithAICategoryTableCellRenderer());
|
||||
|
@ -496,20 +500,18 @@ public class MapWithAIProvidersPanel extends JPanel {
|
|||
* @param e The MouseEvent (used to get the appropriate JTable)
|
||||
*/
|
||||
private static void clickListener(MouseEvent e) {
|
||||
if (e.getSource() instanceof JTable) {
|
||||
JTable table = (JTable) e.getSource();
|
||||
if (table.getSelectedRow() >= 0 && table.getSelectedColumn() >= 0) {
|
||||
int realCol = table.convertColumnIndexToModel(table.getSelectedColumn());
|
||||
int realRow = table.convertRowIndexToModel(table.getSelectedRow());
|
||||
String tableName = table.getModel().getColumnName(realCol);
|
||||
if (e.getSource() instanceof JTable table && table.getSelectedRow() >= 0 && table.getSelectedColumn() >= 0) {
|
||||
final int realCol = table.convertColumnIndexToModel(table.getSelectedColumn());
|
||||
final int realRow = table.convertRowIndexToModel(table.getSelectedRow());
|
||||
final var tableName = table.getModel().getColumnName(realCol);
|
||||
if (tr("License").equals(tableName)) {
|
||||
MapWithAIInfo info = MapWithAIDefaultLayerTableModel.getRow(realRow);
|
||||
final var info = MapWithAIDefaultLayerTableModel.getRow(realRow);
|
||||
if (info.getTermsOfUseURL() != null) {
|
||||
OpenBrowser.displayUrl(info.getTermsOfUseURL());
|
||||
}
|
||||
} else if (tr("Enabled").equals(tableName)) {
|
||||
MapWithAIInfo info = MapWithAIDefaultLayerTableModel.getRow(realRow);
|
||||
MapWithAILayerInfo instance = MapWithAILayerInfo.getInstance();
|
||||
final var info = MapWithAIDefaultLayerTableModel.getRow(realRow);
|
||||
final var instance = MapWithAILayerInfo.getInstance();
|
||||
if (instance.getLayers().contains(info)) {
|
||||
instance.remove(info);
|
||||
} else {
|
||||
|
@ -518,7 +520,6 @@ public class MapWithAIProvidersPanel extends JPanel {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current bounds of the map and the area to select
|
||||
|
@ -585,17 +586,17 @@ public class MapWithAIProvidersPanel extends JPanel {
|
|||
* @param i model index
|
||||
*/
|
||||
private void updateBoundsAndShapes(int i) {
|
||||
ImageryBounds bounds = MapWithAIDefaultLayerTableModel.getRow(i).getBounds();
|
||||
final var bounds = MapWithAIDefaultLayerTableModel.getRow(i).getBounds();
|
||||
if (bounds != null) {
|
||||
int viewIndex = defaultTable.convertRowIndexToView(i);
|
||||
List<Shape> shapes = bounds.getShapes();
|
||||
final var shapes = bounds.getShapes();
|
||||
if (shapes != null && !shapes.isEmpty()) {
|
||||
if (defaultTable.getSelectionModel().isSelectedIndex(viewIndex)) {
|
||||
mapPolygons.computeIfAbsent(i, key -> {
|
||||
List<MapPolygon> list = new ArrayList<>();
|
||||
final var list = new ArrayList<MapPolygon>(shapes.size());
|
||||
// Add new map polygons
|
||||
for (Shape shape : shapes) {
|
||||
MapPolygon polygon = new MapPolygonImpl(shape.getPoints());
|
||||
for (var shape : shapes) {
|
||||
final var polygon = new MapPolygonImpl(shape.getPoints());
|
||||
list.add(polygon);
|
||||
defaultMap.addMapPolygon(polygon);
|
||||
}
|
||||
|
@ -603,7 +604,7 @@ public class MapWithAIProvidersPanel extends JPanel {
|
|||
});
|
||||
} else if (mapPolygons.containsKey(i)) {
|
||||
// Remove previously drawn map polygons
|
||||
for (MapPolygon polygon : mapPolygons.get(i)) {
|
||||
for (var polygon : mapPolygons.get(i)) {
|
||||
defaultMap.removeMapPolygon(polygon);
|
||||
}
|
||||
mapPolygons.remove(i);
|
||||
|
@ -613,9 +614,9 @@ public class MapWithAIProvidersPanel extends JPanel {
|
|||
if (defaultTable.getSelectionModel().isSelectedIndex(viewIndex)) {
|
||||
mapRectangles.computeIfAbsent(i, key -> {
|
||||
// Add new map rectangle
|
||||
Coordinate topLeft = new Coordinate(bounds.getMaxLat(), bounds.getMinLon());
|
||||
Coordinate bottomRight = new Coordinate(bounds.getMinLat(), bounds.getMaxLon());
|
||||
MapRectangle rectangle = new MapRectangleImpl(topLeft, bottomRight);
|
||||
final var topLeft = new Coordinate(bounds.getMaxLat(), bounds.getMinLon());
|
||||
final var bottomRight = new Coordinate(bounds.getMinLat(), bounds.getMaxLon());
|
||||
final var rectangle = new MapRectangleImpl(topLeft, bottomRight);
|
||||
defaultMap.addMapRectangle(rectangle);
|
||||
return rectangle;
|
||||
});
|
||||
|
@ -629,8 +630,8 @@ public class MapWithAIProvidersPanel extends JPanel {
|
|||
}
|
||||
|
||||
private <T> void doCleanupResidualBounds(Map<Integer, T> map, Consumer<T> removalEffect) {
|
||||
List<Integer> toRemove = new ArrayList<>();
|
||||
for (Integer i : map.keySet()) {
|
||||
final var toRemove = new ArrayList<Integer>();
|
||||
for (var i : map.keySet()) {
|
||||
int viewIndex = defaultTable.convertRowIndexToView(i);
|
||||
if (!defaultTable.getSelectionModel().isSelectedIndex(viewIndex)) {
|
||||
toRemove.add(i);
|
||||
|
@ -647,32 +648,32 @@ public class MapWithAIProvidersPanel extends JPanel {
|
|||
|
||||
private class NewEntryAction extends AbstractAction {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 7451336680150337942L;
|
||||
|
||||
NewEntryAction(MapWithAIType type) {
|
||||
putValue(NAME, type.toString());
|
||||
putValue(SHORT_DESCRIPTION, tr("Add a new {0} entry by entering the URL", type.toString()));
|
||||
String icon = /* ICON(dialogs/) */ "add";
|
||||
new ImageProvider(DIALOG_IMAGES_DIR, icon).getResource().attachImageIcon(this, true);
|
||||
new ImageProvider(DIALOG_IMAGES_DIR, "add").getResource().attachImageIcon(this, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
final AddMapWithAIPanel p = new AddMapWithAIPanel();
|
||||
final AddMapWithAIDialog addDialog = new AddMapWithAIDialog(gui, p);
|
||||
final var p = new AddMapWithAIPanel();
|
||||
final var addDialog = new AddMapWithAIDialog(gui, p);
|
||||
addDialog.showDialog();
|
||||
|
||||
if (addDialog.getValue() == 1) {
|
||||
try {
|
||||
MapWithAIInfo info = p.getSourceInfo();
|
||||
final var info = p.getSourceInfo();
|
||||
// Fix a possible NPE
|
||||
if (info.getSourceType() == null) {
|
||||
info.setSourceType(MapWithAIType.THIRD_PARTY);
|
||||
}
|
||||
if (MapWithAIType.ESRI == info.getSourceType()) {
|
||||
final ESRISourceReader reader = new ESRISourceReader(info);
|
||||
final var reader = new ESRISourceReader(info);
|
||||
try {
|
||||
for (Future<MapWithAIInfo> i : reader.parse()) {
|
||||
for (var i : reader.parse()) {
|
||||
try {
|
||||
ACTIVE_MODEL.addRow(i.get());
|
||||
} catch (InterruptedException e) {
|
||||
|
@ -701,6 +702,7 @@ public class MapWithAIProvidersPanel extends JPanel {
|
|||
|
||||
private class EditEntryAction extends AbstractAction implements ListSelectionListener {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = -1682304557691078801L;
|
||||
|
||||
/**
|
||||
|
@ -725,9 +727,8 @@ public class MapWithAIProvidersPanel extends JPanel {
|
|||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
if (activeTable.getSelectedRow() != -1) {
|
||||
final AddMapWithAIPanel p = new AddMapWithAIPanel(
|
||||
MapWithAILayerTableModel.getRow(activeTable.getSelectedRow()));
|
||||
final AddMapWithAIDialog addDialog = new AddMapWithAIDialog(gui, p);
|
||||
final var p = new AddMapWithAIPanel(MapWithAILayerTableModel.getRow(activeTable.getSelectedRow()));
|
||||
final var addDialog = new AddMapWithAIDialog(gui, p);
|
||||
addDialog.showDialog();
|
||||
if (addDialog.getValue() == 1) {
|
||||
p.getSourceInfo();
|
||||
|
@ -738,6 +739,7 @@ public class MapWithAIProvidersPanel extends JPanel {
|
|||
|
||||
private class RemoveEntryAction extends AbstractAction implements ListSelectionListener {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 2666450386256004180L;
|
||||
|
||||
/**
|
||||
|
@ -770,6 +772,7 @@ public class MapWithAIProvidersPanel extends JPanel {
|
|||
|
||||
private class ActivateAction extends AbstractAction implements ListSelectionListener, LayerChangeListener {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = -452335751201424801L;
|
||||
private final transient ImageResource activate;
|
||||
private final transient ImageResource deactivate;
|
||||
|
@ -788,9 +791,8 @@ public class MapWithAIProvidersPanel extends JPanel {
|
|||
|
||||
protected void updateEnabledState() {
|
||||
setEnabled(defaultTable.getSelectedRowCount() > 0);
|
||||
List<MapWithAIInfo> selected = Arrays.stream(defaultTable.getSelectedRows())
|
||||
.map(defaultTable::convertRowIndexToModel).mapToObj(MapWithAIDefaultLayerTableModel::getRow)
|
||||
.collect(Collectors.toList());
|
||||
final var selected = Arrays.stream(defaultTable.getSelectedRows()).map(defaultTable::convertRowIndexToModel)
|
||||
.mapToObj(MapWithAIDefaultLayerTableModel::getRow).toList();
|
||||
if (selected.stream().anyMatch(MapWithAILayerTableModel::doesNotContain)) {
|
||||
activate.attachImageIcon(this, true);
|
||||
putValue(NAME, tr("Activate"));
|
||||
|
@ -817,14 +819,12 @@ public class MapWithAIProvidersPanel extends JPanel {
|
|||
JOptionPane.INFORMATION_MESSAGE);
|
||||
return;
|
||||
}
|
||||
List<MapWithAIInfo> selected = Arrays.stream(defaultTable.getSelectedRows())
|
||||
.map(defaultTable::convertRowIndexToModel).mapToObj(MapWithAIDefaultLayerTableModel::getRow)
|
||||
.collect(Collectors.toList());
|
||||
final var selected = Arrays.stream(defaultTable.getSelectedRows()).map(defaultTable::convertRowIndexToModel)
|
||||
.mapToObj(MapWithAIDefaultLayerTableModel::getRow).collect(Collectors.toCollection(ArrayList::new));
|
||||
if (selected.stream().anyMatch(MapWithAILayerTableModel::doesNotContain)) {
|
||||
List<MapWithAIInfo> toAdd = selected.stream().filter(MapWithAILayerTableModel::doesNotContain)
|
||||
.collect(Collectors.toList());
|
||||
final var toAdd = selected.stream().filter(MapWithAILayerTableModel::doesNotContain).toList();
|
||||
activeTable.getSelectionModel().clearSelection();
|
||||
for (MapWithAIInfo info : toAdd) {
|
||||
for (var info : toAdd) {
|
||||
ACTIVE_MODEL.addRow(new MapWithAIInfo(info));
|
||||
int lastLine = ACTIVE_MODEL.getRowCount() - 1;
|
||||
activeTable.getSelectionModel().setSelectionInterval(lastLine, lastLine);
|
||||
|
@ -853,6 +853,7 @@ public class MapWithAIProvidersPanel extends JPanel {
|
|||
|
||||
private class ReloadAction extends AbstractAction {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 7801339998423585685L;
|
||||
|
||||
/**
|
||||
|
|
|
@ -7,10 +7,9 @@ import javax.json.Json;
|
|||
import javax.json.JsonArray;
|
||||
import javax.json.JsonNumber;
|
||||
import javax.json.JsonObject;
|
||||
import javax.json.JsonReader;
|
||||
import javax.json.JsonString;
|
||||
import javax.json.JsonStructure;
|
||||
import javax.json.JsonValue;
|
||||
import javax.json.spi.JsonProvider;
|
||||
import javax.json.stream.JsonParsingException;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
|
@ -31,7 +30,6 @@ import java.util.regex.Pattern;
|
|||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.jcs3.access.CacheAccess;
|
||||
import org.apache.commons.jcs3.engine.behavior.IElementAttributes;
|
||||
import org.openstreetmap.josm.data.cache.JCSCacheManager;
|
||||
import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryBounds;
|
||||
import org.openstreetmap.josm.data.preferences.LongProperty;
|
||||
|
@ -59,6 +57,8 @@ public class ESRISourceReader {
|
|||
private static final String JSON_QUERY_PARAM = "?f=json";
|
||||
private static final LongProperty MIRROR_MAXTIME = new LongProperty("mirror.maxtime", TimeUnit.DAYS.toSeconds(7));
|
||||
|
||||
private final JsonProvider jsonProvider = JsonProvider.provider();
|
||||
|
||||
/**
|
||||
* Constructs a {@code ImageryReader} from a given filename, URL or internal
|
||||
* resource.
|
||||
|
@ -88,36 +88,36 @@ public class ESRISourceReader {
|
|||
* @throws IOException if any I/O error occurs
|
||||
*/
|
||||
public List<ForkJoinTask<MapWithAIInfo>> parse() throws IOException {
|
||||
Pattern startReplace = Pattern.compile("\\{start}");
|
||||
String search = "/search" + JSON_QUERY_PARAM + "&sortField=added&sortOrder=desc&num=" + INITIAL_SEARCH
|
||||
final var startReplace = Pattern.compile("\\{start}");
|
||||
final var search = "/search" + JSON_QUERY_PARAM + "&sortField=added&sortOrder=desc&num=" + INITIAL_SEARCH
|
||||
+ "&start={start}";
|
||||
String url = source.getUrl();
|
||||
String group = source.getId();
|
||||
final var group = source.getId();
|
||||
var url = source.getUrl();
|
||||
if (!url.endsWith("/")) {
|
||||
url = url.concat("/");
|
||||
}
|
||||
|
||||
final List<ForkJoinTask<MapWithAIInfo>> information = new ArrayList<>();
|
||||
final var information = new ArrayList<ForkJoinTask<MapWithAIInfo>>();
|
||||
|
||||
int next = 1;
|
||||
String searchUrl = startReplace.matcher(search).replaceAll(Integer.toString(next));
|
||||
var next = 1;
|
||||
var searchUrl = startReplace.matcher(search).replaceAll(Integer.toString(next));
|
||||
|
||||
while (next != -1) {
|
||||
final String finalUrl = url + "content/groups/" + group + searchUrl;
|
||||
final String jsonString = getJsonString(finalUrl, TimeUnit.SECONDS.toMillis(MIRROR_MAXTIME.get()) / 7,
|
||||
final var finalUrl = url + "content/groups/" + group + searchUrl;
|
||||
final var jsonString = getJsonString(finalUrl, TimeUnit.SECONDS.toMillis(MIRROR_MAXTIME.get()) / 7,
|
||||
this.fastFail);
|
||||
if (jsonString == null) {
|
||||
continue;
|
||||
}
|
||||
try (JsonReader reader = Json
|
||||
try (var reader = jsonProvider
|
||||
.createReader(new ByteArrayInputStream(jsonString.getBytes(StandardCharsets.UTF_8)))) {
|
||||
JsonStructure parser = reader.read();
|
||||
final var parser = reader.read();
|
||||
if (parser.getValueType() == JsonValue.ValueType.OBJECT) {
|
||||
JsonObject obj = parser.asJsonObject();
|
||||
final var obj = parser.asJsonObject();
|
||||
next = obj.getInt("nextStart", -1);
|
||||
searchUrl = startReplace.matcher(search).replaceAll(Integer.toString(next));
|
||||
JsonArray features = obj.getJsonArray("results");
|
||||
for (JsonObject feature : features.getValuesAs(JsonObject.class)) {
|
||||
final var features = obj.getJsonArray("results");
|
||||
for (var feature : features.getValuesAs(JsonObject.class)) {
|
||||
information.add(parse(feature));
|
||||
}
|
||||
}
|
||||
|
@ -126,7 +126,7 @@ public class ESRISourceReader {
|
|||
next = -1;
|
||||
}
|
||||
}
|
||||
for (ForkJoinTask<MapWithAIInfo> future : information) {
|
||||
for (var future : information) {
|
||||
try {
|
||||
future.join();
|
||||
future.get(1, TimeUnit.MINUTES);
|
||||
|
@ -142,7 +142,7 @@ public class ESRISourceReader {
|
|||
|
||||
private ForkJoinTask<MapWithAIInfo> parse(JsonObject feature) {
|
||||
// Use the initial esri server information to keep conflation info
|
||||
MapWithAIInfo newInfo = new MapWithAIInfo(source);
|
||||
final var newInfo = new MapWithAIInfo(source);
|
||||
newInfo.setId(feature.getString("id"));
|
||||
ForkJoinTask<MapWithAIInfo> future;
|
||||
if ("Feature Service".equals(feature.getString("type", ""))) {
|
||||
|
@ -154,11 +154,10 @@ public class ESRISourceReader {
|
|||
}
|
||||
MapWithAIDataUtils.getForkJoinPool().execute(future);
|
||||
newInfo.setName(feature.getString("title", feature.getString("name")));
|
||||
String[] extent = feature.getJsonArray("extent").getValuesAs(JsonArray.class).stream()
|
||||
final var extent = feature.getJsonArray("extent").getValuesAs(JsonArray.class).stream()
|
||||
.flatMap(array -> array.getValuesAs(JsonNumber.class).stream()).map(JsonNumber::doubleValue)
|
||||
.map(Object::toString).toArray(String[]::new);
|
||||
ImageryBounds imageryBounds = new ImageryBounds(String.join(",", extent[1], extent[0], extent[3], extent[2]),
|
||||
",");
|
||||
final var imageryBounds = new ImageryBounds(String.join(",", extent[1], extent[0], extent[3], extent[2]), ",");
|
||||
newInfo.setBounds(imageryBounds);
|
||||
newInfo.setSourceType(MapWithAIType.ESRI_FEATURE_SERVER);
|
||||
newInfo.setTermsOfUseText(feature.getString("licenseInfo", null));
|
||||
|
@ -167,11 +166,10 @@ public class ESRISourceReader {
|
|||
source.getUrl() + "content/items/" + newInfo.getId() + "/info/" + feature.getString("thumbnail"));
|
||||
}
|
||||
if (feature.containsKey("groupCategories")) {
|
||||
List<MapWithAICategory> categories = feature.getJsonArray("groupCategories").getValuesAs(JsonString.class)
|
||||
.stream().map(JsonString::getString).map(s -> s.replace("/Categories/", ""))
|
||||
.map(MapWithAICategory::fromString).filter(Objects::nonNull)
|
||||
.collect(Collectors.toCollection(ArrayList::new));
|
||||
MapWithAICategory category = categories.stream().filter(c -> MapWithAICategory.FEATURED != c).findFirst()
|
||||
final var categories = feature.getJsonArray("groupCategories").getValuesAs(JsonString.class).stream()
|
||||
.map(JsonString::getString).map(s -> s.replace("/Categories/", ""))
|
||||
.map(MapWithAICategory::fromString).collect(Collectors.toCollection(ArrayList::new));
|
||||
final var category = categories.stream().filter(c -> MapWithAICategory.FEATURED != c).findFirst()
|
||||
.orElse(MapWithAICategory.OTHER);
|
||||
newInfo.setCategory(category);
|
||||
categories.remove(category);
|
||||
|
@ -187,7 +185,7 @@ public class ESRISourceReader {
|
|||
}
|
||||
newInfo.setDescription(feature.getString("snippet"));
|
||||
if (newInfo.getSource() != null) {
|
||||
StringBuilder sourceTag = new StringBuilder(newInfo.getSource());
|
||||
final var sourceTag = new StringBuilder(newInfo.getSource());
|
||||
if (!sourceTag.toString().endsWith("/")) {
|
||||
sourceTag.append('/');
|
||||
}
|
||||
|
@ -208,7 +206,7 @@ public class ESRISourceReader {
|
|||
*/
|
||||
@Nullable
|
||||
private static String getJsonString(@Nonnull final String url, final long defaultMaxAge, final boolean fastFail) {
|
||||
String jsonString = SOURCE_CACHE.get(url);
|
||||
var jsonString = SOURCE_CACHE.get(url);
|
||||
if (jsonString == null) {
|
||||
HttpClient client = null;
|
||||
try {
|
||||
|
@ -216,12 +214,12 @@ public class ESRISourceReader {
|
|||
if (fastFail) {
|
||||
client.setReadTimeout(1000);
|
||||
}
|
||||
final HttpClient.Response response = client.connect();
|
||||
final var response = client.connect();
|
||||
jsonString = response.fetchContent();
|
||||
if (jsonString != null && response.getResponseCode() < 400 && response.getResponseCode() >= 200) {
|
||||
// getExpiration returns milliseconds
|
||||
final long expirationTime = response.getExpiration();
|
||||
final IElementAttributes elementAttributes = SOURCE_CACHE.getDefaultElementAttributes();
|
||||
final var elementAttributes = SOURCE_CACHE.getDefaultElementAttributes();
|
||||
if (expirationTime > 0) {
|
||||
elementAttributes.setMaxLife(response.getExpiration());
|
||||
} else {
|
||||
|
@ -249,22 +247,21 @@ public class ESRISourceReader {
|
|||
*/
|
||||
@Nullable
|
||||
private String featureService(@Nonnull MapWithAIInfo mapwithaiInfo, @Nonnull String url) {
|
||||
final String toGet = url.endsWith(JSON_QUERY_PARAM) ? url : url.concat(JSON_QUERY_PARAM);
|
||||
final String jsonString = getJsonString(toGet, TimeUnit.SECONDS.toMillis(MIRROR_MAXTIME.get()), this.fastFail);
|
||||
final var toGet = url.endsWith(JSON_QUERY_PARAM) ? url : url + JSON_QUERY_PARAM;
|
||||
final var jsonString = getJsonString(toGet, TimeUnit.SECONDS.toMillis(MIRROR_MAXTIME.get()), this.fastFail);
|
||||
if (jsonString == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try (JsonReader reader = Json
|
||||
.createReader(new ByteArrayInputStream(jsonString.getBytes(StandardCharsets.UTF_8)))) {
|
||||
JsonObject info = reader.readObject();
|
||||
JsonArray layers = info.getJsonArray("layers");
|
||||
try (var reader = Json.createReader(new ByteArrayInputStream(jsonString.getBytes(StandardCharsets.UTF_8)))) {
|
||||
final var info = reader.readObject();
|
||||
final var layers = info.getJsonArray("layers");
|
||||
// This fixes #20551
|
||||
if (layers == null || layers.stream().noneMatch(Objects::nonNull)) {
|
||||
return null;
|
||||
}
|
||||
// TODO use all the layers?
|
||||
JsonObject layer = layers.stream().filter(Objects::nonNull).findFirst().orElse(JsonValue.EMPTY_JSON_OBJECT)
|
||||
final var layer = layers.stream().filter(Objects::nonNull).findFirst().orElse(JsonValue.EMPTY_JSON_OBJECT)
|
||||
.asJsonObject();
|
||||
if (layer.containsKey("id")) {
|
||||
String partialUrl = (url.endsWith("/") ? url : url + "/") + layer.getInt("id");
|
||||
|
@ -286,10 +283,10 @@ public class ESRISourceReader {
|
|||
*/
|
||||
@Nonnull
|
||||
private Map<String, String> getReplacementTags(@Nonnull String layerUrl) {
|
||||
String toGet = layerUrl.endsWith(JSON_QUERY_PARAM) ? layerUrl : layerUrl.concat(JSON_QUERY_PARAM);
|
||||
final String jsonString = getJsonString(toGet, TimeUnit.SECONDS.toMillis(MIRROR_MAXTIME.get()), this.fastFail);
|
||||
final var toGet = layerUrl.endsWith(JSON_QUERY_PARAM) ? layerUrl : layerUrl.concat(JSON_QUERY_PARAM);
|
||||
final var jsonString = getJsonString(toGet, TimeUnit.SECONDS.toMillis(MIRROR_MAXTIME.get()), this.fastFail);
|
||||
if (jsonString != null) {
|
||||
try (JsonReader reader = Json
|
||||
try (var reader = Json
|
||||
.createReader(new ByteArrayInputStream(jsonString.getBytes(StandardCharsets.UTF_8)))) {
|
||||
return reader.readObject().getJsonArray("fields").getValuesAs(JsonObject.class).stream()
|
||||
.collect(Collectors.toMap(o -> o.getString("name"), ESRISourceReader::getReplacementTag));
|
||||
|
|
Ładowanie…
Reference in New Issue