mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-10-06 20:33:54 +00:00
use Bounds instead of LatLngBounds for map bounds calculations and don't change zoom/pan if not necessary
This commit is contained in:
1 parent
682b7feddf
commit
17c443b6f6
6 files changed
+114
-47
No files matched your search
@@ -2,17 +2,35 @@ package com.sap.sailing.domain.common;
|
||||
|
||||
public interface Bounds {
|
||||
Position getNorthEast();
|
||||
|
||||
Position getSouthWest();
|
||||
|
||||
Position getNorthWest();
|
||||
|
||||
Position getSouthEast();
|
||||
|
||||
|
||||
Bounds intersect(Bounds other);
|
||||
|
||||
/**
|
||||
* A short-hand for {@link #union}
|
||||
*/
|
||||
Bounds extend(Bounds other);
|
||||
|
||||
Bounds extend(Position p);
|
||||
|
||||
Bounds union(Bounds other);
|
||||
|
||||
boolean intersects(Bounds other);
|
||||
|
||||
boolean contains(Position other);
|
||||
|
||||
boolean contains(Bounds other);
|
||||
|
||||
boolean isCrossesDateLine();
|
||||
|
||||
boolean containsLatDeg(double latDeg);
|
||||
|
||||
boolean containsLngDeg(double lngDeg);
|
||||
|
||||
boolean isEmpty();
|
||||
}
|
||||
+10
@@ -98,6 +98,16 @@ public class BoundsImpl implements Bounds {
|
||||
}
|
||||
return new BoundsImpl(new DegreePosition(minLatDeg, bestWest), new DegreePosition(maxLatDeg, bestEast));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bounds extend(Bounds other) {
|
||||
return union(other);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bounds extend(Position p) {
|
||||
return union(new BoundsImpl(p, p));
|
||||
}
|
||||
|
||||
private boolean spansLngDeg(double westLngDeg, double eastLngDeg, double lngDeg) {
|
||||
return isCrossingDateLine(westLngDeg, eastLngDeg)
|
||||
|
||||
@@ -88,6 +88,28 @@ public class BoundsTest {
|
||||
assertEquals(5.0, u.getNorthEast().getLngDeg(), 0.00000001);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unionTestWithEmptyBounds() {
|
||||
Bounds b1 = new BoundsImpl(new DegreePosition(1, 1), new DegreePosition(3, 3));
|
||||
Bounds b2 = new BoundsImpl(new DegreePosition(4, 4), new DegreePosition(4, 4));
|
||||
Bounds u = b1.union(b2);
|
||||
assertEquals(1.0, u.getSouthWest().getLatDeg(), 0.00000001);
|
||||
assertEquals(1.0, u.getSouthWest().getLngDeg(), 0.00000001);
|
||||
assertEquals(4.0, u.getNorthEast().getLatDeg(), 0.00000001);
|
||||
assertEquals(4.0, u.getNorthEast().getLngDeg(), 0.00000001);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unionTestWithTwoEmptyBounds() {
|
||||
Bounds b1 = new BoundsImpl(new DegreePosition(1, 1), new DegreePosition(1, 1));
|
||||
Bounds b2 = new BoundsImpl(new DegreePosition(4, 4), new DegreePosition(4, 4));
|
||||
Bounds u = b1.union(b2);
|
||||
assertEquals(1.0, u.getSouthWest().getLatDeg(), 0.00000001);
|
||||
assertEquals(1.0, u.getSouthWest().getLngDeg(), 0.00000001);
|
||||
assertEquals(4.0, u.getNorthEast().getLatDeg(), 0.00000001);
|
||||
assertEquals(4.0, u.getNorthEast().getLngDeg(), 0.00000001);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void simpleIntersectsTest() {
|
||||
Bounds b1 = new BoundsImpl(new DegreePosition(1, 1), new DegreePosition(3, 3));
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
package com.sap.sailing.gwt.ui.client.shared.racemap;
|
||||
|
||||
import com.google.gwt.maps.client.base.LatLngBounds;
|
||||
import com.sap.sailing.domain.common.Bounds;
|
||||
|
||||
public interface LatLngBoundsCalculator {
|
||||
LatLngBounds calculateNewBounds(RaceMap forMap);
|
||||
Bounds calculateNewBounds(RaceMap forMap);
|
||||
}
|
||||
+56
-39
@@ -57,7 +57,9 @@ import com.google.gwt.user.client.ui.Label;
|
||||
import com.google.gwt.user.client.ui.RequiresResize;
|
||||
import com.google.gwt.user.client.ui.VerticalPanel;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.sap.sailing.domain.common.Bounds;
|
||||
import com.sap.sailing.domain.common.ManeuverType;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.RaceIdentifier;
|
||||
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
|
||||
import com.sap.sailing.domain.common.Tack;
|
||||
@@ -65,6 +67,7 @@ import com.sap.sailing.domain.common.WindSource;
|
||||
import com.sap.sailing.domain.common.WindSourceType;
|
||||
import com.sap.sailing.domain.common.dto.CompetitorDTO;
|
||||
import com.sap.sailing.domain.common.dto.PositionDTO;
|
||||
import com.sap.sailing.domain.common.impl.BoundsImpl;
|
||||
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
|
||||
import com.sap.sailing.domain.common.impl.RGBColor;
|
||||
import com.sap.sailing.domain.common.scalablevalue.impl.ScalableBearing;
|
||||
@@ -592,7 +595,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
|
||||
// Rezoom the map
|
||||
// TODO make this a loop across the LatLngBoundsCalculators, pulling them from a collection updated in updateSettings
|
||||
if (!settings.getZoomSettings().containsZoomType(ZoomTypes.NONE)) { // Auto zoom if setting is not manual
|
||||
LatLngBounds bounds = settings.getZoomSettings().getNewBounds(RaceMap.this);
|
||||
Bounds bounds = settings.getZoomSettings().getNewBounds(RaceMap.this);
|
||||
zoomMapToNewBounds(bounds);
|
||||
mapFirstZoomDone = true;
|
||||
} else if (!mapFirstZoomDone) { // Zoom once to the marks
|
||||
@@ -1177,17 +1180,38 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
|
||||
}
|
||||
}
|
||||
|
||||
private void zoomMapToNewBounds(LatLngBounds newBounds) {
|
||||
private void zoomMapToNewBounds(Bounds newBounds) {
|
||||
if (newBounds != null) {
|
||||
List<ZoomTypes> oldZoomSettings = settings.getZoomSettings().getTypesToConsiderOnZoom();
|
||||
setAutoZoomInProgress(true);
|
||||
map.setCenter(newBounds.getCenter());
|
||||
map.fitBounds(newBounds);
|
||||
settings.getZoomSettings().setTypesToConsiderOnZoom(oldZoomSettings);
|
||||
setAutoZoomInProgress(false);
|
||||
Bounds currentMapBounds = BoundsUtil.getAsBounds(map.getBounds());
|
||||
if (!currentMapBounds.contains(newBounds) || isTwiceTheGraticuleArea(currentMapBounds, newBounds)) {
|
||||
// only change bounds if the new bounds don't fit into the current map zoom
|
||||
// TODO check if we need to shrink every once in a while...
|
||||
List<ZoomTypes> oldZoomSettings = settings.getZoomSettings().getTypesToConsiderOnZoom();
|
||||
setAutoZoomInProgress(true);
|
||||
// map.setCenter(newBounds.getCenter()); // TODO try without this to avoid frequent suspension of CSS boat animation
|
||||
LatLngBounds newLatLngBounds = BoundsUtil.getAsLatLngBounds(newBounds);
|
||||
map.fitBounds(newLatLngBounds);
|
||||
settings.getZoomSettings().setTypesToConsiderOnZoom(oldZoomSettings);
|
||||
setAutoZoomInProgress(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isTwiceTheGraticuleArea(Bounds containing, Bounds contained) {
|
||||
assert containing.contains(contained);
|
||||
double containingAreaRatio = getGraticuleArea(containing) / getGraticuleArea(contained);
|
||||
return containingAreaRatio >= 2.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* A much simplified "area" calculation for a {@link Bounds} object, multiplying the differences in latitude and longitude degrees.
|
||||
* The result therefore is in the order of magnitude of 60*60 square nautical miles.
|
||||
*/
|
||||
private double getGraticuleArea(Bounds bounds) {
|
||||
return ((bounds.isCrossesDateLine() ? bounds.getNorthEast().getLngDeg()+360 : bounds.getNorthEast().getLngDeg())-bounds.getSouthWest().getLngDeg()) *
|
||||
(bounds.getNorthEast().getLatDeg() - bounds.getSouthWest().getLatDeg());
|
||||
}
|
||||
|
||||
private void setAutoZoomInProgress(boolean autoZoomInProgress) {
|
||||
this.autoZoomInProgress = autoZoomInProgress;
|
||||
}
|
||||
@@ -1781,8 +1805,8 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
|
||||
public static class BoatsBoundsCalculator extends LatLngBoundsCalculatorForSelected {
|
||||
|
||||
@Override
|
||||
public LatLngBounds calculateNewBounds(RaceMap forMap) {
|
||||
LatLngBounds newBounds = null;
|
||||
public Bounds calculateNewBounds(RaceMap forMap) {
|
||||
Bounds newBounds = null;
|
||||
Iterable<CompetitorDTO> selectedCompetitors = forMap.competitorSelection.getSelectedCompetitors();
|
||||
Iterable<CompetitorDTO> competitors = new ArrayList<CompetitorDTO>();
|
||||
if (selectedCompetitors == null || !selectedCompetitors.iterator().hasNext()) {
|
||||
@@ -1794,16 +1818,11 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
|
||||
try {
|
||||
GPSFixDTO competitorFix = forMap.getBoatFix(competitor, forMap.timer.getTime());
|
||||
PositionDTO competitorPosition = competitorFix != null ? competitorFix.position : null;
|
||||
LatLng competitorLatLng = competitorPosition != null ? LatLng.newInstance(competitorPosition.latDeg,
|
||||
competitorPosition.lngDeg) : null;
|
||||
LatLngBounds bounds = competitorLatLng != null ? LatLngBounds.newInstance(competitorLatLng,
|
||||
competitorLatLng) : null;
|
||||
if (bounds != null) {
|
||||
if (competitorPosition != null) {
|
||||
if (newBounds == null) {
|
||||
newBounds = bounds;
|
||||
newBounds = BoundsUtil.getAsBounds(competitorPosition);
|
||||
} else {
|
||||
newBounds.extend(bounds.getNorthEast());
|
||||
newBounds.extend(bounds.getSouthWest());
|
||||
newBounds = newBounds.extend(BoundsUtil.getAsPosition(competitorPosition));
|
||||
}
|
||||
}
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
@@ -1819,27 +1838,26 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
|
||||
public static class TailsBoundsCalculator extends LatLngBoundsCalculatorForSelected {
|
||||
|
||||
@Override
|
||||
public LatLngBounds calculateNewBounds(RaceMap racemap) {
|
||||
LatLngBounds newBounds = null;
|
||||
public Bounds calculateNewBounds(RaceMap racemap) {
|
||||
Bounds newBounds = null;
|
||||
Iterable<CompetitorDTO> competitors = isZoomOnlyToSelectedCompetitors(racemap) ? racemap.competitorSelection.getSelectedCompetitors() : racemap.getCompetitorsToShow();
|
||||
for (CompetitorDTO competitor : competitors) {
|
||||
Polyline tail = racemap.fixesAndTails.getTail(competitor);
|
||||
LatLngBounds bounds = null;
|
||||
Bounds bounds = null;
|
||||
// TODO: Find a replacement for missing Polyline function getBounds() from v2
|
||||
// see also http://stackoverflow.com/questions/3284808/getting-the-bounds-of-a-polyine-in-google-maps-api-v3;
|
||||
// optionally, consider providing a bounds cache with two sorted sets that organize the LatLng objects for O(1) bounds calculation and logarithmic add, ideally O(1) remove
|
||||
if(tail != null && tail.getPath().getLength() >= 2) {
|
||||
bounds = LatLngBounds.newInstance(tail.getPath().get(0), tail.getPath().get(1));
|
||||
for(int i = 2; i < tail.getPath().getLength(); i++) {
|
||||
bounds.extend(tail.getPath().get(i));
|
||||
if (tail != null && tail.getPath().getLength() >= 1) {
|
||||
bounds = BoundsUtil.getAsBounds(BoundsUtil.getAsPosition(tail.getPath().get(0)));
|
||||
for(int i = 1; i < tail.getPath().getLength(); i++) {
|
||||
bounds = bounds.extend(BoundsUtil.getAsPosition(tail.getPath().get(i)));
|
||||
}
|
||||
}
|
||||
if (bounds != null) {
|
||||
if (newBounds == null) {
|
||||
newBounds = bounds;
|
||||
} else {
|
||||
newBounds.extend(bounds.getNorthEast());
|
||||
newBounds.extend(bounds.getSouthWest());
|
||||
newBounds = newBounds.extend(bounds);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1850,17 +1868,16 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
|
||||
|
||||
public static class CourseMarksBoundsCalculator implements LatLngBoundsCalculator {
|
||||
@Override
|
||||
public LatLngBounds calculateNewBounds(RaceMap forMap) {
|
||||
LatLngBounds newBounds = null;
|
||||
public Bounds calculateNewBounds(RaceMap forMap) {
|
||||
Bounds newBounds = null;
|
||||
Iterable<MarkDTO> marksToZoom = forMap.markDTOs.values();
|
||||
if (marksToZoom != null) {
|
||||
for (MarkDTO markDTO : marksToZoom) {
|
||||
LatLng markLatLng = LatLng.newInstance(markDTO.position.latDeg, markDTO.position.lngDeg);
|
||||
LatLngBounds bounds = LatLngBounds.newInstance(markLatLng, markLatLng);
|
||||
Bounds bounds = BoundsUtil.getAsBounds(markDTO.position);
|
||||
if (newBounds == null) {
|
||||
newBounds = bounds;
|
||||
} else {
|
||||
newBounds.extend(markLatLng);
|
||||
newBounds = newBounds.extend(bounds);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1870,18 +1887,18 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
|
||||
|
||||
public static class WindSensorsBoundsCalculator implements LatLngBoundsCalculator {
|
||||
@Override
|
||||
public LatLngBounds calculateNewBounds(RaceMap forMap) {
|
||||
LatLngBounds newBounds = null;
|
||||
public Bounds calculateNewBounds(RaceMap forMap) {
|
||||
Bounds newBounds = null;
|
||||
Collection<WindSensorOverlay> marksToZoom = forMap.windSensorOverlays.values();
|
||||
if (marksToZoom != null) {
|
||||
for (WindSensorOverlay windSensorOverlay: marksToZoom) {
|
||||
LatLng windSensorLatLng = windSensorOverlay.getLatLngPosition();
|
||||
if(windSensorLatLng != null) {
|
||||
LatLngBounds bounds = LatLngBounds.newInstance(windSensorLatLng, windSensorLatLng);
|
||||
for (WindSensorOverlay windSensorOverlay : marksToZoom) {
|
||||
Position windSensorPosition = BoundsUtil.getAsPosition(windSensorOverlay.getLatLngPosition());
|
||||
if (windSensorPosition != null) {
|
||||
Bounds bounds = new BoundsImpl(windSensorPosition, windSensorPosition);
|
||||
if (newBounds == null) {
|
||||
newBounds = bounds;
|
||||
} else {
|
||||
newBounds.extend(windSensorLatLng);
|
||||
newBounds = newBounds.extend(windSensorPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -3,7 +3,7 @@ package com.sap.sailing.gwt.ui.client.shared.racemap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.gwt.maps.client.base.LatLngBounds;
|
||||
import com.sap.sailing.domain.common.Bounds;
|
||||
|
||||
/**
|
||||
* @author Lennart Hensler (D054527)
|
||||
@@ -24,7 +24,7 @@ public class RaceMapZoomSettings {
|
||||
this.calculator = calculator;
|
||||
}
|
||||
|
||||
public LatLngBounds calculateNewBounds(RaceMap forMap) {
|
||||
public Bounds calculateNewBounds(RaceMap forMap) {
|
||||
return calculator == null ? null : calculator.calculateNewBounds(forMap);
|
||||
}
|
||||
};
|
||||
@@ -50,12 +50,12 @@ public class RaceMapZoomSettings {
|
||||
this.zoomToSelectedCompetitors = zoomToSelected;
|
||||
}
|
||||
|
||||
public LatLngBounds getNewBounds(RaceMap forMap) {
|
||||
LatLngBounds newBounds = null;
|
||||
public Bounds getNewBounds(RaceMap forMap) {
|
||||
Bounds newBounds = null;
|
||||
if (typesToConsiderOnZoom != null) {
|
||||
for (ZoomTypes type : typesToConsiderOnZoom) {
|
||||
//Calculate the new bounds and extend the result
|
||||
LatLngBounds calculatedBounds = type.calculateNewBounds(forMap);
|
||||
Bounds calculatedBounds = type.calculateNewBounds(forMap);
|
||||
if (calculatedBounds != null) {
|
||||
if (newBounds == null) {
|
||||
newBounds = calculatedBounds;
|
||||
|
||||
Reference in new issue
Block a user