Don't zoom after new position added, center when last position is not visible (fixes #161)
This commit is contained in:
parent
1180a9fba8
commit
f641b68e14
@ -32,9 +32,11 @@ import Vector from 'ol/source/Vector';
|
||||
import VectorLayer from 'ol/layer/Vector';
|
||||
import View from 'ol/View';
|
||||
import XYZ from 'ol/source/XYZ';
|
||||
import { containsCoordinate } from 'ol/extent.js';
|
||||
|
||||
export { Feature, Map, Overlay, View };
|
||||
export const control = { Control, Rotate, ScaleLine, Zoom, ZoomToExtent };
|
||||
export const extent = { containsCoordinate };
|
||||
export const geom = { LineString, Point };
|
||||
export const layer = { TileLayer, VectorLayer };
|
||||
export const proj = { fromLonLat, toLonLat };
|
||||
|
@ -364,6 +364,28 @@ export default class GoogleMapsApi {
|
||||
this.map.fitBounds(latLngBounds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is given position within viewport
|
||||
* @param {number} id
|
||||
* @return {boolean}
|
||||
*/
|
||||
isPositionVisible(id) {
|
||||
if (id >= this.markers.length) {
|
||||
return false;
|
||||
}
|
||||
return this.map.getBounds().contains(this.markers[id].getPosition());
|
||||
}
|
||||
|
||||
/**
|
||||
* Center to given position
|
||||
* @param {number} id
|
||||
*/
|
||||
centerToPosition(id) {
|
||||
if (id < this.markers.length) {
|
||||
this.map.setCenter(this.markers[id].getPosition());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update size
|
||||
*/
|
||||
|
@ -587,16 +587,36 @@ export default class OpenLayersApi {
|
||||
}
|
||||
|
||||
/**
|
||||
* Fit to extent, respect max zoom
|
||||
* Fit to extent, respect max zoom, add padding
|
||||
* @param {Array.<number>} extent
|
||||
* @return {Array.<number>}
|
||||
*/
|
||||
fitToExtent(extent) {
|
||||
this.map.getView().fit(extent, { padding: [ 40, 10, 10, 10 ], maxZoom: OpenLayersApi.ZOOM_MAX });
|
||||
if (this.map.getView().getZoom() === OpenLayersApi.ZOOM_MAX) {
|
||||
extent = this.map.getView().calculateExtent(this.map.getSize());
|
||||
this.map.getView().fit(extent, { padding: OpenLayersApi.TRACK_PADDING, maxZoom: OpenLayersApi.ZOOM_MAX });
|
||||
return this.map.getView().calculateExtent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Is given position within viewport
|
||||
* @param {number} id
|
||||
* @return {boolean}
|
||||
*/
|
||||
isPositionVisible(id) {
|
||||
const mapExtent = this.map.getView().calculateExtent();
|
||||
const marker = this.layerMarkers.getSource().getFeatureById(id).getGeometry();
|
||||
return marker ? ol.extent.containsCoordinate(mapExtent, marker.getCoordinates()) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Center to given position
|
||||
* @param {number} id
|
||||
*/
|
||||
centerToPosition(id) {
|
||||
const marker = this.layerMarkers.getSource().getFeatureById(id).getGeometry();
|
||||
if (marker) {
|
||||
console.log(`Setting center to position ${id}`)
|
||||
this.map.getView().setCenter(marker.getCoordinates());
|
||||
}
|
||||
return extent;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -691,17 +711,17 @@ export default class OpenLayersApi {
|
||||
* @returns {number[]} Bounds [ lon_sw, lat_sw, lon_ne, lat_ne ]
|
||||
*/
|
||||
getBounds() {
|
||||
const extent = this.map.getView().calculateExtent(this.map.getSize());
|
||||
const extent = this.map.getView().calculateExtent();
|
||||
const sw = ol.proj.toLonLat([ extent[0], extent[1] ]);
|
||||
const ne = ol.proj.toLonLat([ extent[2], extent[3] ]);
|
||||
return [ sw[0], sw[1], ne[0], ne[1] ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Zoom to track extent, respect max zoom
|
||||
* Zoom to track extent, respect max zoom, add padding
|
||||
*/
|
||||
zoomToExtent() {
|
||||
this.map.getView().fit(this.layerMarkers.getSource().getExtent(), { maxZoom: OpenLayersApi.ZOOM_MAX });
|
||||
this.fitToExtent(this.layerMarkers.getSource().getExtent());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -760,3 +780,5 @@ export default class OpenLayersApi {
|
||||
}
|
||||
/** @type {number} */
|
||||
OpenLayersApi.ZOOM_MAX = 20;
|
||||
/** @type {number[]} */
|
||||
OpenLayersApi.TRACK_PADDING = [ 40, 10, 10, 10 ];
|
||||
|
@ -25,6 +25,7 @@ import ViewModel from './viewmodel.js';
|
||||
import uAlert from './alert.js';
|
||||
import uDialog from './dialog.js';
|
||||
import uObserve from './observe.js';
|
||||
import uTrack from './track.js';
|
||||
import uUtils from './utils.js';
|
||||
|
||||
/**
|
||||
@ -157,7 +158,10 @@ export default class MapViewModel extends ViewModel {
|
||||
if (track) {
|
||||
uObserve.observe(track, 'positions', () => {
|
||||
this.displayTrack(track, false);
|
||||
this.api.zoomToExtent();
|
||||
if (track instanceof uTrack && !this.api.isPositionVisible(track.length - 1)) {
|
||||
console.log('last track position not visible');
|
||||
this.api.centerToPosition(track.length - 1);
|
||||
}
|
||||
this.toggleStyleOptions();
|
||||
});
|
||||
this.displayTrack(track, true);
|
||||
|
@ -301,6 +301,70 @@ describe('Google Maps map API tests', () => {
|
||||
expect(setTimeout).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should center map to given marker', () => {
|
||||
// given
|
||||
const track = TrackFactory.getTrack(1);
|
||||
const coordinates = [ 1, 3 ];
|
||||
spyOn(GoogleMapsApi, 'getMarkerIcon');
|
||||
spyOn(google.maps.Map.prototype, 'setCenter');
|
||||
spyOn(google.maps.Marker.prototype, 'getPosition').and.returnValue(coordinates);
|
||||
spyOn(google.maps, 'Marker').and.callThrough();
|
||||
|
||||
api.map = new google.maps.Map(container);
|
||||
|
||||
const id = 0;
|
||||
api.setMarker(id, track);
|
||||
// when
|
||||
api.centerToPosition(id);
|
||||
|
||||
// then
|
||||
expect(google.maps.Map.prototype.setCenter).toHaveBeenCalledWith(coordinates);
|
||||
});
|
||||
|
||||
it('should confirm that position is visible', () => {
|
||||
// given
|
||||
const track = TrackFactory.getTrack(1);
|
||||
const coordinates = [ 1, 3 ];
|
||||
spyOn(GoogleMapsApi, 'getMarkerIcon');
|
||||
spyOn(google.maps.Map.prototype, 'getBounds').and.returnValue(new google.maps.LatLngBounds());
|
||||
spyOn(google.maps.LatLngBounds.prototype, 'contains').and.returnValue(true);
|
||||
spyOn(google.maps.Marker.prototype, 'getPosition').and.returnValue(coordinates);
|
||||
spyOn(google.maps, 'Marker').and.callThrough();
|
||||
|
||||
api.map = new google.maps.Map(container);
|
||||
|
||||
const id = 0;
|
||||
api.setMarker(id, track);
|
||||
// when
|
||||
const result = api.isPositionVisible(id);
|
||||
|
||||
// then
|
||||
expect(google.maps.LatLngBounds.prototype.contains).toHaveBeenCalledWith(coordinates);
|
||||
expect(result).toBeTrue();
|
||||
});
|
||||
|
||||
it('should confirm that position is not visible', () => {
|
||||
// given
|
||||
const track = TrackFactory.getTrack(1);
|
||||
const coordinates = [ 1, 3 ];
|
||||
spyOn(GoogleMapsApi, 'getMarkerIcon');
|
||||
spyOn(google.maps.Map.prototype, 'getBounds').and.returnValue(new google.maps.LatLngBounds());
|
||||
spyOn(google.maps.LatLngBounds.prototype, 'contains').and.returnValue(false);
|
||||
spyOn(google.maps.Marker.prototype, 'getPosition').and.returnValue(coordinates);
|
||||
spyOn(google.maps, 'Marker').and.callThrough();
|
||||
|
||||
api.map = new google.maps.Map(container);
|
||||
|
||||
const id = 0;
|
||||
api.setMarker(id, track);
|
||||
// when
|
||||
const result = api.isPositionVisible(id);
|
||||
|
||||
// then
|
||||
expect(google.maps.LatLngBounds.prototype.contains).toHaveBeenCalledWith(coordinates);
|
||||
expect(result).toBeFalse();
|
||||
});
|
||||
|
||||
it('should create marker from track position and add it to markers array', () => {
|
||||
// given
|
||||
const track = TrackFactory.getTrack(1);
|
||||
|
@ -18,7 +18,7 @@
|
||||
*/
|
||||
|
||||
import * as ol from '../src/lib/ol.js';
|
||||
import OpenlayersApi from '../src/mapapi/api_openlayers.js';
|
||||
import OpenLayersApi from '../src/mapapi/api_openlayers.js';
|
||||
import TrackFactory from './helpers/trackfactory.js';
|
||||
import { config } from '../src/initializer.js'
|
||||
import uLayer from '../src/layer.js';
|
||||
@ -35,10 +35,14 @@ describe('Openlayers map API tests', () => {
|
||||
config.reinitialize();
|
||||
document.body.innerHTML = '';
|
||||
container = document.createElement('div');
|
||||
container.setAttribute('style', 'display: block; width: 100px; height: 100px');
|
||||
document.body.appendChild(container);
|
||||
mockViewModel = { mapElement: container, model: {} };
|
||||
api = new OpenlayersApi(mockViewModel, ol);
|
||||
mockMap = new ol.Map({ target: container });
|
||||
api = new OpenLayersApi(mockViewModel, ol);
|
||||
mockMap = new ol.Map({
|
||||
target: container,
|
||||
view: new ol.View({ zoom: 8, center: [ 0, 0 ] })
|
||||
});
|
||||
});
|
||||
|
||||
it('should load and initialize api scripts', (done) => {
|
||||
@ -353,13 +357,12 @@ describe('Openlayers map API tests', () => {
|
||||
// given
|
||||
api.map = mockMap;
|
||||
spyOn(ol.View.prototype, 'fit');
|
||||
spyOn(ol.View.prototype, 'getZoom').and.returnValue(OpenlayersApi.ZOOM_MAX - 1);
|
||||
spyOn(ol.View.prototype, 'getZoom').and.returnValue(OpenLayersApi.ZOOM_MAX - 1);
|
||||
const extent = [ 0, 1, 2, 3 ];
|
||||
// when
|
||||
const result = api.fitToExtent(extent);
|
||||
api.fitToExtent(extent);
|
||||
// then
|
||||
expect(ol.View.prototype.fit).toHaveBeenCalledWith(extent, jasmine.any(Object));
|
||||
expect(result).toEqual(extent);
|
||||
});
|
||||
|
||||
it('should fit to extent and zoom to max value', () => {
|
||||
@ -368,7 +371,7 @@ describe('Openlayers map API tests', () => {
|
||||
const zoomedExtent = [ 3, 2, 1, 0 ];
|
||||
api.map = mockMap;
|
||||
spyOn(ol.View.prototype, 'fit');
|
||||
spyOn(ol.View.prototype, 'getZoom').and.returnValue(OpenlayersApi.ZOOM_MAX);
|
||||
spyOn(ol.View.prototype, 'getZoom').and.returnValue(OpenLayersApi.ZOOM_MAX);
|
||||
spyOn(ol.View.prototype, 'setZoom');
|
||||
spyOn(ol.View.prototype, 'calculateExtent').and.returnValue(zoomedExtent);
|
||||
// when
|
||||
@ -394,6 +397,68 @@ describe('Openlayers map API tests', () => {
|
||||
expect(marker.getGeometry().getFirstCoordinate()).toEqual(ol.proj.fromLonLat([ track.positions[0].longitude, track.positions[0].latitude ]));
|
||||
});
|
||||
|
||||
it('should center map to given marker', () => {
|
||||
// given
|
||||
const track = TrackFactory.getTrack(1);
|
||||
const coordinates = [ 1, 3 ];
|
||||
track.positions[0].timestamp = 1;
|
||||
track.positions[0].longitude = coordinates[0];
|
||||
track.positions[0].latitude = coordinates[1];
|
||||
const id = 0;
|
||||
api.map = mockMap;
|
||||
api.layerMarkers = new ol.layer.VectorLayer({ source: new ol.source.Vector() });
|
||||
spyOn(ol.View.prototype, 'setCenter');
|
||||
spyOn(api, 'getMarkerStyle');
|
||||
|
||||
api.setMarker(id, track);
|
||||
// when
|
||||
api.centerToPosition(id);
|
||||
// then
|
||||
expect(ol.View.prototype.setCenter).toHaveBeenCalledWith(ol.proj.fromLonLat(coordinates));
|
||||
});
|
||||
|
||||
it('should confirm that position is visible', () => {
|
||||
// given
|
||||
const track = TrackFactory.getTrack(1);
|
||||
const coordinates = [ 1, 3 ];
|
||||
track.positions[0].timestamp = 1;
|
||||
track.positions[0].longitude = coordinates[0];
|
||||
track.positions[0].latitude = coordinates[1];
|
||||
const id = 0;
|
||||
api.map = mockMap;
|
||||
api.layerMarkers = new ol.layer.VectorLayer({ source: new ol.source.Vector() });
|
||||
spyOn(ol.extent, 'containsCoordinate').and.returnValue(true);
|
||||
spyOn(api, 'getMarkerStyle');
|
||||
|
||||
api.setMarker(id, track);
|
||||
// when
|
||||
const result = api.isPositionVisible(id);
|
||||
// then
|
||||
expect(ol.extent.containsCoordinate).toHaveBeenCalledWith(jasmine.any(Array), ol.proj.fromLonLat(coordinates));
|
||||
expect(result).toBeTrue();
|
||||
});
|
||||
|
||||
it('should confirm that position is not visible', () => {
|
||||
// given
|
||||
const track = TrackFactory.getTrack(1);
|
||||
const coordinates = [ 1, 3 ];
|
||||
track.positions[0].timestamp = 1;
|
||||
track.positions[0].longitude = coordinates[0];
|
||||
track.positions[0].latitude = coordinates[1];
|
||||
const id = 0;
|
||||
api.map = mockMap;
|
||||
api.layerMarkers = new ol.layer.VectorLayer({ source: new ol.source.Vector() });
|
||||
spyOn(ol.extent, 'containsCoordinate').and.returnValue(false);
|
||||
spyOn(api, 'getMarkerStyle');
|
||||
|
||||
api.setMarker(id, track);
|
||||
// when
|
||||
const result = api.isPositionVisible(id);
|
||||
// then
|
||||
expect(ol.extent.containsCoordinate).toHaveBeenCalledWith(jasmine.any(Array), ol.proj.fromLonLat(coordinates));
|
||||
expect(result).toBeFalse();
|
||||
});
|
||||
|
||||
it('should get different marker style for start, end and normal position', () => {
|
||||
// given
|
||||
const track = TrackFactory.getTrack(3);
|
||||
@ -499,7 +564,7 @@ describe('Openlayers map API tests', () => {
|
||||
// when
|
||||
api.zoomToExtent();
|
||||
// then
|
||||
expect(ol.View.prototype.fit).toHaveBeenCalledWith(extent, { maxZoom: OpenlayersApi.ZOOM_MAX });
|
||||
expect(ol.View.prototype.fit).toHaveBeenCalledWith(extent, { padding: OpenLayersApi.TRACK_PADDING, maxZoom: OpenLayersApi.ZOOM_MAX });
|
||||
});
|
||||
|
||||
it('should get map bounds and convert to WGS84 (EPSG:4326)', () => {
|
||||
@ -510,7 +575,7 @@ describe('Openlayers map API tests', () => {
|
||||
// when
|
||||
const bounds = api.getBounds();
|
||||
// then
|
||||
expect(ol.View.prototype.calculateExtent).toHaveBeenCalledWith(jasmine.any(Array));
|
||||
expect(ol.View.prototype.calculateExtent).toHaveBeenCalledTimes(1);
|
||||
expect(bounds[0]).toBeCloseTo(20.597985430276808);
|
||||
expect(bounds[1]).toBeCloseTo(52.15547181298076);
|
||||
expect(bounds[2]).toBeCloseTo(21.363595171488573);
|
||||
|
@ -53,6 +53,7 @@ export const setupGmapsStub = () => {
|
||||
this.sw = sw;
|
||||
this.ne = ne;
|
||||
}
|
||||
contains() {/* ignore */}
|
||||
extend() {/* ignore */}
|
||||
getNorthEast() { return this.ne; }
|
||||
getSouthWest() { return this.sw; }
|
||||
|
@ -60,7 +60,9 @@ describe('MapViewModel tests', () => {
|
||||
'setTrackDefaultStyle': { /* ignored */ },
|
||||
'setTrackGradientStyle': { /* ignored */ },
|
||||
'clearMap': { /* ignored */ },
|
||||
'updateSize': { /* ignored */ }
|
||||
'updateSize': { /* ignored */ },
|
||||
'isPositionVisible': { /* ignored */ },
|
||||
'centerToPosition': { /* ignored */ }
|
||||
});
|
||||
state = new uState();
|
||||
vm = new MapViewModel(state);
|
||||
@ -251,7 +253,6 @@ describe('MapViewModel tests', () => {
|
||||
state.currentTrack.positions.push(TrackFactory.getPosition(100));
|
||||
// then
|
||||
setTimeout(() => {
|
||||
expect(mockApi.zoomToExtent).toHaveBeenCalledTimes(1);
|
||||
expect(mockApi.displayTrack).toHaveBeenCalledTimes(1);
|
||||
expect(mockApi.displayTrack).toHaveBeenCalledWith(track, false);
|
||||
done();
|
||||
|
Loading…
x
Reference in New Issue
Block a user