diff --git a/src/org/openstreetmap/josm/actions/AlignGridRotationAction.java b/src/org/openstreetmap/josm/actions/AlignGridRotationAction.java
index 5f7459a839..8062902e44 100644
|
a
|
b
|
import org.openstreetmap.josm.gui.layer.MapGridPaintable.GridType;
|
| 26 | 26 | */ |
| 27 | 27 | public class AlignGridRotationAction extends JosmAction { |
| 28 | 28 | |
| | 29 | /** returned when the selection does not define a direction */ |
| | 30 | private static final EastNorth[] EMPTY_DIRECTION = new EastNorth[0]; |
| | 31 | |
| 29 | 32 | /** |
| 30 | 33 | * Constructs a new {@code AlignGridRotationAction}. |
| 31 | 34 | */ |
| … |
… |
public class AlignGridRotationAction extends JosmAction {
|
| 39 | 42 | /** |
| 40 | 43 | * Determines the direction given by the selection: a single way with at least two nodes, or exactly two nodes. |
| 41 | 44 | * @param ds the data set, may be null |
| 42 | | * @return start and end point, or {@code null} if the selection does not define a direction |
| | 45 | * @return start and end point, or an empty array if the selection does not define a direction |
| 43 | 46 | */ |
| 44 | 47 | static EastNorth[] getSelectedDirection(DataSet ds) { |
| 45 | 48 | if (ds == null) { |
| 46 | | return null; |
| | 49 | return EMPTY_DIRECTION; |
| 47 | 50 | } |
| 48 | 51 | Collection<Way> ways = ds.getSelectedWays(); |
| 49 | 52 | Collection<Node> nodes = ds.getSelectedNodes(); |
| … |
… |
public class AlignGridRotationAction extends JosmAction {
|
| 61 | 64 | b = it.next(); |
| 62 | 65 | } |
| 63 | 66 | if (a == null || b == null || !a.isLatLonKnown() || !b.isLatLonKnown()) { |
| 64 | | return null; |
| | 67 | return EMPTY_DIRECTION; |
| 65 | 68 | } |
| 66 | 69 | EastNorth[] result = {a.getEastNorth(), b.getEastNorth()}; |
| 67 | | return result[0].equalsEpsilon(result[1], 1e-9) ? null : result; |
| | 70 | return result[0].equalsEpsilon(result[1], 1e-9) ? EMPTY_DIRECTION : result; |
| 68 | 71 | } |
| 69 | 72 | |
| 70 | 73 | /** |
| … |
… |
public class AlignGridRotationAction extends JosmAction {
|
| 85 | 88 | |
| 86 | 89 | @Override |
| 87 | 90 | protected void updateEnabledState() { |
| 88 | | setEnabled(getSelectedDirection(getLayerManager().getEditDataSet()) != null); |
| | 91 | setEnabled(getSelectedDirection(getLayerManager().getEditDataSet()).length == 2); |
| 89 | 92 | } |
| 90 | 93 | |
| 91 | 94 | @Override |
| … |
… |
public class AlignGridRotationAction extends JosmAction {
|
| 96 | 99 | @Override |
| 97 | 100 | public void actionPerformed(ActionEvent e) { |
| 98 | 101 | EastNorth[] direction = getSelectedDirection(getLayerManager().getEditDataSet()); |
| 99 | | if (direction == null) { |
| | 102 | if (direction.length != 2) { |
| 100 | 103 | return; |
| 101 | 104 | } |
| 102 | 105 | MapGridPaintable.ROTATION.put(rotationOf(direction[0], direction[1])); |
diff --git a/src/org/openstreetmap/josm/gui/MapFrame.java b/src/org/openstreetmap/josm/gui/MapFrame.java
index 341371d0c4..a9864246c5 100644
|
a
|
b
|
public class MapFrame extends JPanel implements Destroyable, ActiveLayerChangeLi
|
| 124 | 124 | */ |
| 125 | 125 | public final MapView mapView; |
| 126 | 126 | /** The grid drawn over the map view, see {@link MapGridPaintable} */ |
| 127 | | private final MapGridPaintable gridOverlay = new MapGridPaintable(); |
| | 127 | private final transient MapGridPaintable gridOverlay = new MapGridPaintable(); |
| 128 | 128 | |
| 129 | 129 | /** |
| 130 | 130 | * This object allows to detect key press and release events |
diff --git a/src/org/openstreetmap/josm/gui/layer/MapGridPaintable.java b/src/org/openstreetmap/josm/gui/layer/MapGridPaintable.java
index f6fc491529..0a5bc43691 100644
|
a
|
b
|
import java.awt.geom.Path2D;
|
| 12 | 12 | import java.awt.geom.Point2D; |
| 13 | 13 | import java.util.ArrayList; |
| 14 | 14 | import java.util.List; |
| | 15 | import java.util.concurrent.atomic.AtomicReference; |
| 15 | 16 | |
| 16 | 17 | import org.openstreetmap.josm.data.Bounds; |
| 17 | 18 | import org.openstreetmap.josm.data.ProjectionBounds; |
| 18 | 19 | import org.openstreetmap.josm.data.coor.EastNorth; |
| 19 | | import org.openstreetmap.josm.data.coor.ILatLon; |
| 20 | 20 | import org.openstreetmap.josm.data.coor.LatLon; |
| 21 | 21 | import org.openstreetmap.josm.data.preferences.BooleanProperty; |
| 22 | 22 | import org.openstreetmap.josm.data.preferences.DoubleProperty; |
| … |
… |
public class MapGridPaintable extends AbstractMapViewPaintable implements Prefer
|
| 137 | 137 | private static final int MAX_LINES = 500; |
| 138 | 138 | |
| 139 | 139 | /** written by {@link #paint} and read by {@link #preferenceChanged}, which may run on another thread */ |
| 140 | | private volatile MapView mapView; |
| | 140 | private final AtomicReference<MapView> mapView = new AtomicReference<>(); |
| 141 | 141 | |
| 142 | 142 | /** |
| 143 | 143 | * Constructs a new {@code MapGridPaintable}. |
| … |
… |
public class MapGridPaintable extends AbstractMapViewPaintable implements Prefer
|
| 148 | 148 | |
| 149 | 149 | @Override |
| 150 | 150 | public void paint(Graphics2D g, MapView mv, Bounds bbox) { |
| 151 | | mapView = mv; |
| | 151 | mapView.set(mv); |
| 152 | 152 | if (!Boolean.TRUE.equals(ENABLED.get()) || mv.getWidth() <= 0 || mv.getHeight() <= 0) { |
| 153 | 153 | return; |
| 154 | 154 | } |
| … |
… |
public class MapGridPaintable extends AbstractMapViewPaintable implements Prefer
|
| 318 | 318 | if (!a.isValid() || !b.isValid()) { |
| 319 | 319 | return 1; |
| 320 | 320 | } |
| 321 | | double metres = a.greatCircleDistance((ILatLon) b); |
| | 321 | double metres = a.greatCircleDistance(b); |
| 322 | 322 | return metres > 0 && Double.isFinite(metres) ? step / metres : 1; |
| 323 | 323 | } catch (IllegalArgumentException e) { |
| 324 | 324 | Logging.trace(e); |
| … |
… |
public class MapGridPaintable extends AbstractMapViewPaintable implements Prefer
|
| 357 | 357 | public static List<ProjectedGridLine> getProjectedGridLines(ProjectionBounds area, double spacingX, double spacingY, |
| 358 | 358 | double rotationDegrees, double originX, double originY) { |
| 359 | 359 | List<ProjectedGridLine> lines = new ArrayList<>(); |
| 360 | | if (!(spacingX > 0) || !(spacingY > 0)) { |
| | 360 | if (!isPositive(spacingX) || !isPositive(spacingY)) { |
| 361 | 361 | return lines; |
| 362 | 362 | } |
| 363 | 363 | double angle = Math.toRadians(rotationDegrees); |
| … |
… |
public class MapGridPaintable extends AbstractMapViewPaintable implements Prefer
|
| 420 | 420 | public static List<LatLonGridLine> getLatLonGridLines(Bounds area, Bounds world, double spacingLon, double spacingLat, |
| 421 | 421 | double originLon, double originLat, int segments) { |
| 422 | 422 | List<LatLonGridLine> lines = new ArrayList<>(); |
| 423 | | if (!(spacingLon > 0) || !(spacingLat > 0) || segments < 1) { |
| | 423 | if (!isPositive(spacingLon) || !isPositive(spacingLat) || segments < 1) { |
| 424 | 424 | return lines; |
| 425 | 425 | } |
| 426 | 426 | double minLat = Math.max(area.getMinLat(), world.getMinLat()); |
| … |
… |
public class MapGridPaintable extends AbstractMapViewPaintable implements Prefer
|
| 445 | 445 | boolean limitedLon = world.getMinLon() > -180 || world.getMaxLon() < 180; |
| 446 | 446 | for (long k = kLonMin; k <= kLonMax; k++) { |
| 447 | 447 | double lon = LatLon.toIntervalLon(originLon + k * spacingLon); |
| 448 | | if (limitedLon && (lon < world.getMinLon() || lon > world.getMaxLon())) { |
| 449 | | continue; |
| 450 | | } |
| 451 | | List<LatLon> line = new ArrayList<>(segments + 1); |
| 452 | | for (int i = 0; i <= segments; i++) { |
| 453 | | line.add(new LatLon(minLat + (maxLat - minLat) * i / segments, lon)); |
| | 448 | if (!limitedLon || (lon >= world.getMinLon() && lon <= world.getMaxLon())) { |
| | 449 | addMeridian(lines, lon, minLat, maxLat, segments); |
| 454 | 450 | } |
| 455 | | lines.add(new LatLonGridLine(line, true)); |
| 456 | 451 | } |
| 457 | 452 | for (long k = kLatMin; k <= kLatMax; k++) { |
| 458 | 453 | double lat = originLat + k * spacingLat; |
| … |
… |
public class MapGridPaintable extends AbstractMapViewPaintable implements Prefer
|
| 464 | 459 | return lines; |
| 465 | 460 | } |
| 466 | 461 | |
| | 462 | /** |
| | 463 | * Adds a meridian running from one latitude to another. |
| | 464 | * @param lines the list to add to |
| | 465 | * @param lon the longitude of the meridian |
| | 466 | * @param minLat the latitude to start at |
| | 467 | * @param maxLat the latitude to end at |
| | 468 | * @param segments number of segments of the polyline |
| | 469 | */ |
| | 470 | private static void addMeridian(List<LatLonGridLine> lines, double lon, double minLat, double maxLat, int segments) { |
| | 471 | List<LatLon> line = new ArrayList<>(segments + 1); |
| | 472 | for (int i = 0; i <= segments; i++) { |
| | 473 | line.add(new LatLon(minLat + (maxLat - minLat) * i / segments, lon)); |
| | 474 | } |
| | 475 | lines.add(new LatLonGridLine(line, true)); |
| | 476 | } |
| | 477 | |
| 467 | 478 | /** |
| 468 | 479 | * Adds a parallel running from one longitude to another. A parallel which crosses the antimeridian is added |
| 469 | 480 | * as two lines, one on each side of it: wrapping the longitudes of a single polyline would instead make it |
| … |
… |
public class MapGridPaintable extends AbstractMapViewPaintable implements Prefer
|
| 480 | 491 | addParallel(lines, lat, -180, maxLon - 360, segments); |
| 481 | 492 | return; |
| 482 | 493 | } |
| 483 | | if (!(maxLon > minLon)) { |
| | 494 | if (maxLon <= minLon) { |
| 484 | 495 | return; |
| 485 | 496 | } |
| 486 | 497 | List<LatLon> line = new ArrayList<>(segments + 1); |
| … |
… |
public class MapGridPaintable extends AbstractMapViewPaintable implements Prefer
|
| 490 | 501 | lines.add(new LatLonGridLine(line, false)); |
| 491 | 502 | } |
| 492 | 503 | |
| | 504 | /** |
| | 505 | * Determines if a value is strictly positive. Unlike {@code value <= 0}, this rejects NaN. |
| | 506 | * @param value the value |
| | 507 | * @return {@code true} if the value is greater than 0 |
| | 508 | */ |
| | 509 | private static boolean isPositive(double value) { |
| | 510 | return value > 0; |
| | 511 | } |
| | 512 | |
| 493 | 513 | @Override |
| 494 | 514 | public void preferenceChanged(PreferenceChangeEvent e) { |
| 495 | 515 | if (e.getKey().startsWith(PREFIX) || e.getKey().equals(COLOR_EAST.getKey()) || e.getKey().equals(COLOR_NORTH.getKey())) { |
| 496 | 516 | invalidate(); |
| 497 | | if (mapView != null) { |
| 498 | | mapView.repaint(); |
| | 517 | MapView mv = mapView.get(); |
| | 518 | if (mv != null) { |
| | 519 | mv.repaint(); |
| 499 | 520 | } |
| 500 | 521 | } |
| 501 | 522 | } |
| … |
… |
public class MapGridPaintable extends AbstractMapViewPaintable implements Prefer
|
| 503 | 524 | @Override |
| 504 | 525 | public void destroy() { |
| 505 | 526 | Config.getPref().removePreferenceChangeListener(this); |
| 506 | | mapView = null; |
| | 527 | mapView.set(null); |
| 507 | 528 | } |
| 508 | 529 | } |
diff --git a/src/org/openstreetmap/josm/gui/layer/imagery/ShowTileBordersAction.java b/src/org/openstreetmap/josm/gui/layer/imagery/ShowTileBordersAction.java
index 8c2cf5ecbe..8a53b22978 100644
|
a
|
b
|
import org.openstreetmap.josm.gui.layer.Layer.LayerAction;
|
| 20 | 20 | */ |
| 21 | 21 | public class ShowTileBordersAction extends AbstractAction implements LayerAction { |
| 22 | 22 | |
| 23 | | private final AbstractTileSourceLayer<?> layer; |
| | 23 | private final transient AbstractTileSourceLayer<?> layer; |
| 24 | 24 | |
| 25 | 25 | /** |
| 26 | 26 | * Constructs a new {@code ShowTileBordersAction}. |
diff --git a/src/org/openstreetmap/josm/gui/preferences/display/GridPreference.java b/src/org/openstreetmap/josm/gui/preferences/display/GridPreference.java
index 5899b72d03..1af89faf58 100644
|
a
|
b
|
package org.openstreetmap.josm.gui.preferences.display;
|
| 4 | 4 | import static org.openstreetmap.josm.tools.I18n.tr; |
| 5 | 5 | |
| 6 | 6 | import java.awt.Component; |
| | 7 | import java.awt.GridBagConstraints; |
| 7 | 8 | import java.awt.GridBagLayout; |
| 8 | 9 | import java.text.DecimalFormat; |
| 9 | 10 | import java.text.DecimalFormatSymbols; |
| … |
… |
public class GridPreference extends DefaultTabPreferenceSetting {
|
| 100 | 101 | |
| 101 | 102 | panel.add(enabled, GBC.eol().insets(0, 0, 0, 10)); |
| 102 | 103 | panel.add(new JLabel(tr("Grid type")), GBC.std().insets(5, 0, 5, 5)); |
| 103 | | panel.add(type, GBC.eol().fill(GBC.HORIZONTAL).insets(0, 0, 0, 5)); |
| | 104 | panel.add(type, GBC.eol().fill(GridBagConstraints.HORIZONTAL).insets(0, 0, 0, 5)); |
| 104 | 105 | panel.add(spacingXLabel, GBC.std().insets(5, 0, 5, 5)); |
| 105 | 106 | panel.add(spacingX, GBC.eol().insets(0, 0, 0, 5)); |
| 106 | 107 | panel.add(spacingYLabel, GBC.std().insets(5, 0, 5, 5)); |
| … |
… |
public class GridPreference extends DefaultTabPreferenceSetting {
|
| 116 | 117 | panel.add(new JLabel(tr("Line width (pixels)")), GBC.std().insets(5, 0, 5, 5)); |
| 117 | 118 | panel.add(lineWidth, GBC.eol().insets(0, 0, 0, 5)); |
| 118 | 119 | panel.add(new JLabel(tr("Blend mode")), GBC.std().insets(5, 0, 5, 5)); |
| 119 | | panel.add(blendMode, GBC.eol().fill(GBC.HORIZONTAL).insets(0, 0, 0, 5)); |
| 120 | | panel.add(GBC.glue(0, 0), GBC.eol().fill(GBC.BOTH)); |
| | 120 | panel.add(blendMode, GBC.eol().fill(GridBagConstraints.HORIZONTAL).insets(0, 0, 0, 5)); |
| | 121 | panel.add(GBC.glue(0, 0), GBC.eol().fill(GridBagConstraints.BOTH)); |
| 121 | 122 | |
| 122 | 123 | createPreferenceTabWithScrollPane(gui, panel); |
| 123 | 124 | } |
| … |
… |
public class GridPreference extends DefaultTabPreferenceSetting {
|
| 142 | 143 | |
| 143 | 144 | /** Renders enum values with a translated name */ |
| 144 | 145 | private static final class TranslatedRenderer<T> extends DefaultListCellRenderer { |
| 145 | | private final Function<T, String> name; |
| | 146 | private final transient Function<T, String> name; |
| 146 | 147 | |
| 147 | 148 | TranslatedRenderer(Function<T, String> name) { |
| 148 | 149 | this.name = name; |
| … |
… |
public class GridPreference extends DefaultTabPreferenceSetting {
|
| 178 | 179 | private static void save(JosmTextField field, DoubleProperty property, boolean positive) { |
| 179 | 180 | try { |
| 180 | 181 | double value = FORMAT.parse(field.getText().trim()).doubleValue(); |
| 181 | | if (positive && !(value > 0)) { |
| | 182 | if (positive && (Double.isNaN(value) || value <= 0)) { |
| 182 | 183 | Logging.warn("Ignoring invalid grid setting {0}: {1}", property.getKey(), field.getText()); |
| 183 | 184 | return; |
| 184 | 185 | } |
diff --git a/test/unit/org/openstreetmap/josm/actions/GridActionsTest.java b/test/unit/org/openstreetmap/josm/actions/GridActionsTest.java
index 37e9d667c6..5d59504846 100644
|
a
|
b
|
class GridActionsTest {
|
| 164 | 164 | ds.addPrimitive(w); |
| 165 | 165 | |
| 166 | 166 | ds.setSelected(); |
| 167 | | assertNull(AlignGridRotationAction.getSelectedDirection(ds)); |
| | 167 | assertEquals(0, AlignGridRotationAction.getSelectedDirection(ds).length); |
| 168 | 168 | AlignGridRotationAction action = new AlignGridRotationAction(); |
| 169 | 169 | assertFalse(action.isEnabled()); |
| 170 | 170 | ds.setSelected(a); |
| 171 | | assertNull(AlignGridRotationAction.getSelectedDirection(ds)); |
| | 171 | assertEquals(0, AlignGridRotationAction.getSelectedDirection(ds).length); |
| 172 | 172 | assertFalse(action.isEnabled()); |
| 173 | 173 | ds.setSelected(a, b, c); |
| 174 | | assertNull(AlignGridRotationAction.getSelectedDirection(ds)); |
| | 174 | assertEquals(0, AlignGridRotationAction.getSelectedDirection(ds).length); |
| 175 | 175 | assertFalse(action.isEnabled()); |
| 176 | 176 | |
| 177 | 177 | ds.setSelected(a, b); |
diff --git a/test/unit/org/openstreetmap/josm/io/OsmConnectionTest.java b/test/unit/org/openstreetmap/josm/io/OsmConnectionTest.java
index f742a38422..61fb9b5c11 100644
|
a
|
b
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
| 8 | 8 | import static org.junit.jupiter.api.Assertions.assertTrue; |
| 9 | 9 | |
| 10 | 10 | import java.net.MalformedURLException; |
| 11 | | import java.net.URL; |
| | 11 | import java.net.URI; |
| 12 | 12 | |
| 13 | 13 | import javax.swing.JOptionPane; |
| 14 | 14 | |
| … |
… |
class OsmConnectionTest {
|
| 59 | 59 | } |
| 60 | 60 | |
| 61 | 61 | private static HttpClient apiRequest() throws MalformedURLException { |
| 62 | | return HttpClient.create(new URL(API_URL + "/0.6/changeset/create"), "PUT"); |
| | 62 | return HttpClient.create(URI.create(API_URL + "/0.6/changeset/create").toURL(), "PUT"); |
| 63 | 63 | } |
| 64 | 64 | |
| 65 | 65 | /** |