Feature: track color by speed, altitude; extended summary
This commit is contained in:
parent
d34cbbc9f0
commit
5ae7753353
1
images/altitude.svg
Normal file
1
images/altitude.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="white" d="M21.103 14.598c-.509-1.504-2.306-2.497-3.806-1.91l-6.235-10.688-11.062 20h20.25c2.067 0 3.75-1.682 3.75-3.75 0-1.774-1.239-3.265-2.897-3.652zm-.853 5.402h-3.26c-1.515-.008-2.505-1.653-1.708-3.009l-3.595-6.334-1.078 1.906-1-1.906-3.026 3.635 4.521-8.344 5.521 9.552c.875-1.781 3.328-.688 2.688 1.104 1.271-.5 2.687.224 2.687 1.646 0 .965-.785 1.75-1.75 1.75zm-2.236-8.579l-2.656-4.625.867-.498 2.656 4.625-.867.498zm3.298 1.579l-2.656-4.625.867-.498 2.656 4.625-.867.498z"/></svg>
|
After Width: | Height: | Size: 584 B |
4
images/speed.svg
Normal file
4
images/speed.svg
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||||
|
<path fill="white" d="M20.043 11.76c-.141-.427-.314-.844-.516-1.242l-2.454 1.106c.217.393.39.81.517 1.242l2.453-1.106zm-12.572-.904c.271-.354.579-.674.918-.957l-1.89-1.968c-.328.293-.637.614-.919.957l1.891 1.968zm1.714-1.514c.38-.221.781-.396 1.198-.523l-1.033-2.569c-.412.142-.813.317-1.2.524l1.035 2.568zm-2.759 3.615c.121-.435.287-.854.498-1.25l-2.47-1.066c-.196.403-.364.823-.498 1.25l2.47 1.066zm9.434-6.2c-.387-.205-.79-.379-1.2-.519l-1.023 2.573c.418.125.82.299 1.2.519l1.023-2.573zm2.601 2.131c-.281-.342-.59-.664-.918-.957l-1.891 1.968c.34.283.648.604.919.957l1.89-1.968zm-5.791-3.06c-.219-.017-.437-.026-.648-.026-.213 0-.432.009-.65.026v2.784c.216-.025.434-.038.65-.038.215 0 .434.013.648.038v-2.784zm11.33 8.172c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 2.583.816 5.042 2.205 7h19.59c1.389-1.958 2.205-4.417 2.205-7zm-9.08 5c-.007-1.086-.606-2.031-1.496-2.522l-1.402-6.571-1.402 6.571c-.889.491-1.489 1.436-1.496 2.522h-5.821c-.845-1.5-1.303-3.242-1.303-5 0-5.514 4.486-10 10-10s10 4.486 10 10c0 1.758-.458 3.5-1.303 5h-5.777z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
@ -85,8 +85,14 @@
|
|||||||
|
|
||||||
<div id="summary" class="section" data-bind="summary"></div>
|
<div id="summary" class="section" data-bind="summary"></div>
|
||||||
|
|
||||||
|
<div class="section" data-bind="trackColor">
|
||||||
|
<div class="menu-title"><?= $lang['trackcolor'] ?></div>
|
||||||
|
<input id="color-speed" type="checkbox" data-bind="speedVisible"> <label for="color-speed"><?= $lang['speed'] ?></label><br>
|
||||||
|
<input id="color-altitude" type="checkbox" data-bind="altitudeVisible"> <label for="color-altitude"><?= $lang['altitude'] ?></label><br>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="other" class="section">
|
<div id="other" class="section">
|
||||||
<a id="altitudes" data-bind="onChartToggle"><?= $lang['chart'] ?></a>
|
<a id="altitudes" class="menu-link menu-hidden" data-bind="onChartToggle"><?= $lang['chart'] ?></a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
@ -87,7 +87,7 @@ export default class ChartViewModel extends ViewModel {
|
|||||||
plugins: [
|
plugins: [
|
||||||
ctAxisTitle({
|
ctAxisTitle({
|
||||||
axisY: {
|
axisY: {
|
||||||
axisTitle: `${$._('altitude')} (${$.unit('unitDistance')})`,
|
axisTitle: `${$._('altitude')} (${$.unit('unitDistance')} ${$.unit('unitAltitude')})`,
|
||||||
axisClass: 'ct-axis-title',
|
axisClass: 'ct-axis-title',
|
||||||
offset: {
|
offset: {
|
||||||
x: 0,
|
x: 0,
|
||||||
@ -162,9 +162,9 @@ export default class ChartViewModel extends ViewModel {
|
|||||||
*/
|
*/
|
||||||
renderButton(isVisible) {
|
renderButton(isVisible) {
|
||||||
if (isVisible) {
|
if (isVisible) {
|
||||||
this.buttonElement.style.visibility = 'visible';
|
this.buttonElement.classList.remove('menu-hidden');
|
||||||
} else {
|
} else {
|
||||||
this.buttonElement.style.visibility = 'hidden';
|
this.buttonElement.classList.add('menu-hidden');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,6 +103,7 @@ export default class uConfig {
|
|||||||
this.unitDistanceMajor = 'unitkm';
|
this.unitDistanceMajor = 'unitkm';
|
||||||
}
|
}
|
||||||
this.unitDay = 'unitday';
|
this.unitDay = 'unitday';
|
||||||
|
this.unitAltitude = 'unitamsl';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -67,59 +67,67 @@ export default class uLang {
|
|||||||
/**
|
/**
|
||||||
* Get speed converted to locale units
|
* Get speed converted to locale units
|
||||||
* @param {number} ms Speed in meters per second
|
* @param {number} ms Speed in meters per second
|
||||||
* @param {boolean} withUnit
|
* @param {?boolean=false} withUnit
|
||||||
* @return {(number|string)} String when with unit
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
getLocaleSpeed(ms, withUnit) {
|
getLocaleSpeed(ms, withUnit = false) {
|
||||||
const value = Math.round(ms * this.config.factorSpeed * 100) / 100;
|
const value = Math.round(ms * this.config.factorSpeed * 100) / 100;
|
||||||
|
let localized = value.toLocaleString(this.config.lang);
|
||||||
if (withUnit) {
|
if (withUnit) {
|
||||||
return `${value.toLocaleString(this.config.lang)} ${this.unit('unitSpeed')}`;
|
localized += ` ${this.unit('unitSpeed')}`;
|
||||||
}
|
}
|
||||||
return value;
|
return localized;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get distance converted to locale units
|
* Get distance converted to locale units
|
||||||
* @param {number} m Distance in meters
|
* @param {number} m Distance in meters
|
||||||
* @param {boolean} withUnit
|
* @param {?boolean=false} withUnit
|
||||||
* @return {(number|string)} String when with unit
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
getLocaleDistanceMajor(m, withUnit) {
|
getLocaleDistanceMajor(m, withUnit = false) {
|
||||||
const value = Math.round(m * this.config.factorDistanceMajor / 10) / 100;
|
const value = Math.round(m * this.config.factorDistanceMajor / 10) / 100;
|
||||||
|
let localized = value.toLocaleString(this.config.lang);
|
||||||
if (withUnit) {
|
if (withUnit) {
|
||||||
return `${value.toLocaleString(this.config.lang)} ${this.unit('unitDistanceMajor')}`
|
localized += ` ${this.unit('unitDistanceMajor')}`;
|
||||||
}
|
}
|
||||||
return value;
|
return localized;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {number} m Distance in meters
|
* @param {number} m Distance in meters
|
||||||
* @param {boolean} withUnit
|
* @param {?boolean=false} withUnit
|
||||||
* @return {(number|string)} String when with unit
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
getLocaleDistance(m, withUnit) {
|
getLocaleDistance(m, withUnit = false) {
|
||||||
const value = Math.round(m * this.config.factorDistance * 100) / 100;
|
const value = Math.round(m * this.config.factorDistance * 100) / 100;
|
||||||
|
let localized = value.toLocaleString(this.config.lang);
|
||||||
if (withUnit) {
|
if (withUnit) {
|
||||||
return `${value.toLocaleString(this.config.lang)} ${this.unit('unitDistance')}`;
|
localized += ` ${this.unit('unitDistance')}`;
|
||||||
}
|
}
|
||||||
return value;
|
return localized;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {number} m Distance in meters
|
* @param {number} m Altitude in meters
|
||||||
* @param {boolean} withUnit
|
* @param {?boolean=false} withUnit
|
||||||
* @return {(number|string)} String when with unit
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
getLocaleAltitude(m, withUnit) {
|
getLocaleAltitude(m, withUnit = false) {
|
||||||
return this.getLocaleDistance(m, withUnit);
|
let localized = this.getLocaleDistance(m, withUnit);
|
||||||
|
if (withUnit) {
|
||||||
|
localized += ` ${this.unit('unitAltitude')}`;
|
||||||
|
}
|
||||||
|
// use typographic minus
|
||||||
|
return localized.replace('-', '−');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {number} m Distance in meters
|
* @param {number} m Accuracy in meters
|
||||||
* @param {boolean} withUnit
|
* @param {?boolean=false} withUnit
|
||||||
* @return {(number|string)} String when with unit
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
getLocaleAccuracy(m, withUnit) {
|
getLocaleAccuracy(m, withUnit = false) {
|
||||||
return this.getLocaleDistance(m, withUnit);
|
return this.getLocaleDistance(m, withUnit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,8 +142,10 @@ export default class GoogleMapsApi {
|
|||||||
const promise = new Promise((resolve) => {
|
const promise = new Promise((resolve) => {
|
||||||
google.maps.event.addListenerOnce(this.map, 'tilesloaded', () => {
|
google.maps.event.addListenerOnce(this.map, 'tilesloaded', () => {
|
||||||
console.log('tilesloaded');
|
console.log('tilesloaded');
|
||||||
this.saveState();
|
if (this.map) {
|
||||||
this.map.addListener('idle', this.saveState);
|
this.saveState();
|
||||||
|
this.map.addListener('idle', this.saveState);
|
||||||
|
}
|
||||||
resolve();
|
resolve();
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
@ -370,7 +372,26 @@ export default class GoogleMapsApi {
|
|||||||
// ignore for google API
|
// ignore for google API
|
||||||
}
|
}
|
||||||
|
|
||||||
static get loadTimeoutMs() {
|
/**
|
||||||
|
* Set default track style
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line class-methods-use-this
|
||||||
|
setTrackDefaultStyle() {
|
||||||
|
// ignore for google API
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set gradient style for given track property and scale
|
||||||
|
* @param {uTrack} track
|
||||||
|
* @param {string} property
|
||||||
|
* @param {{ minValue: number, maxValue: number, minColor: number[], maxColor: number[] }} scale
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line class-methods-use-this,no-unused-vars
|
||||||
|
setTrackGradientStyle(track, property, scale) {
|
||||||
|
// ignore for google API
|
||||||
|
}
|
||||||
|
|
||||||
|
static get loadTimeoutMs() {
|
||||||
return 10000;
|
return 10000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,17 +179,10 @@ export default class OpenLayersApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// add track and markers layers
|
// add track and markers layers
|
||||||
const lineStyle = new ol.style.Style({
|
|
||||||
stroke: new ol.style.Stroke({
|
|
||||||
color: uUtils.hexToRGBA(config.strokeColor, config.strokeOpacity),
|
|
||||||
width: config.strokeWeight
|
|
||||||
})
|
|
||||||
});
|
|
||||||
this.layerTrack = new ol.layer.VectorLayer({
|
this.layerTrack = new ol.layer.VectorLayer({
|
||||||
name: 'Track',
|
name: 'Track',
|
||||||
type: 'data',
|
type: 'data',
|
||||||
source: new ol.source.Vector(),
|
source: new ol.source.Vector()
|
||||||
style: lineStyle
|
|
||||||
});
|
});
|
||||||
this.layerMarkers = new ol.layer.VectorLayer({
|
this.layerMarkers = new ol.layer.VectorLayer({
|
||||||
name: 'Markers',
|
name: 'Markers',
|
||||||
@ -251,6 +244,107 @@ export default class OpenLayersApi {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set default track style
|
||||||
|
*/
|
||||||
|
setTrackDefaultStyle() {
|
||||||
|
const lineStyle = new ol.style.Style({
|
||||||
|
stroke: new ol.style.Stroke({
|
||||||
|
color: uUtils.hexToRGBA(config.strokeColor, config.strokeOpacity),
|
||||||
|
width: config.strokeWeight
|
||||||
|
})
|
||||||
|
})
|
||||||
|
this.layerTrack.setStyle(lineStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {CanvasRenderingContext2D} context
|
||||||
|
* @param {Coordinate[]} coordinates
|
||||||
|
* @param {string[]} colors
|
||||||
|
* @return {Style}
|
||||||
|
*/
|
||||||
|
getGradientStyle(context, coordinates, colors) {
|
||||||
|
const pixelStart = this.map.getPixelFromCoordinate(coordinates[0]);
|
||||||
|
const pixelEnd = this.map.getPixelFromCoordinate(coordinates[1]);
|
||||||
|
const scale = window.devicePixelRatio;
|
||||||
|
const x0 = pixelStart[0] * scale;
|
||||||
|
const y0 = pixelStart[1] * scale;
|
||||||
|
const x1 = pixelEnd[0] * scale;
|
||||||
|
const y1 = pixelEnd[1] * scale;
|
||||||
|
const gradient = context.createLinearGradient(x0, y0, x1, y1);
|
||||||
|
gradient.addColorStop(0, colors[0]);
|
||||||
|
gradient.addColorStop(1, colors[1]);
|
||||||
|
return new ol.style.Style({
|
||||||
|
geometry: new ol.geom.LineString(coordinates),
|
||||||
|
stroke: new ol.style.Stroke({
|
||||||
|
color: gradient,
|
||||||
|
width: config.strokeWeight * 2
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set gradient style for given track property and scale
|
||||||
|
* @param {uTrack} track
|
||||||
|
* @param {string} property
|
||||||
|
* @param {{ minValue: number, maxValue: number, minColor: number[], maxColor: number[] }} scale
|
||||||
|
*/
|
||||||
|
setTrackGradientStyle(track, property, scale) {
|
||||||
|
const minValue = scale.minValue;
|
||||||
|
const maxValue = scale.maxValue;
|
||||||
|
const minColor = scale.minColor;
|
||||||
|
const maxColor = scale.maxColor;
|
||||||
|
if (track.length < 2 || maxValue < minValue) {
|
||||||
|
this.setTrackDefaultStyle();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const canvas = document.createElement('canvas', { alpha: false, desynchronized: true });
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
/**
|
||||||
|
* @param {Feature} feature
|
||||||
|
* @return {Style[]}
|
||||||
|
*/
|
||||||
|
const lineStyle = (feature) => {
|
||||||
|
const styles = [
|
||||||
|
new ol.style.Style({
|
||||||
|
stroke: new ol.style.Stroke({
|
||||||
|
color: 'grey',
|
||||||
|
width: config.strokeWeight * 2 + 2
|
||||||
|
})
|
||||||
|
})
|
||||||
|
];
|
||||||
|
const geometry = feature.getGeometry();
|
||||||
|
if (minValue === maxValue) {
|
||||||
|
styles.push(new ol.style.Style({
|
||||||
|
geometry: geometry,
|
||||||
|
stroke: new ol.style.Stroke({
|
||||||
|
color: uUtils.getScaleColor(minColor, maxColor, 0.5),
|
||||||
|
width: config.strokeWeight * 2
|
||||||
|
})
|
||||||
|
}));
|
||||||
|
return styles;
|
||||||
|
}
|
||||||
|
let position = track.positions[0];
|
||||||
|
const value = position[property] !== null ? position[property] : 0;
|
||||||
|
let colorStart = uUtils.getScaleColor(minColor, maxColor, (value - minValue) / (maxValue - minValue));
|
||||||
|
let index = 1;
|
||||||
|
geometry.forEachSegment((start, end) => {
|
||||||
|
position = track.positions[index];
|
||||||
|
let colorStop;
|
||||||
|
if (position[property] !== null) {
|
||||||
|
colorStop = uUtils.getScaleColor(minColor, maxColor, (position[property] - minValue) / (maxValue - minValue));
|
||||||
|
} else {
|
||||||
|
colorStop = colorStart;
|
||||||
|
}
|
||||||
|
styles.push(this.getGradientStyle(ctx, [ start, end ], [ colorStart, colorStop ]));
|
||||||
|
colorStart = colorStop;
|
||||||
|
index++;
|
||||||
|
});
|
||||||
|
return styles;
|
||||||
|
};
|
||||||
|
this.layerTrack.setStyle(lineStyle);
|
||||||
|
}
|
||||||
|
|
||||||
initPopups() {
|
initPopups() {
|
||||||
const popupContainer = document.createElement('div');
|
const popupContainer = document.createElement('div');
|
||||||
popupContainer.id = 'popup-container';
|
popupContainer.id = 'popup-container';
|
||||||
@ -438,8 +532,10 @@ export default class OpenLayersApi {
|
|||||||
const promise = new Promise((resolve) => {
|
const promise = new Promise((resolve) => {
|
||||||
this.map.once('rendercomplete', () => {
|
this.map.once('rendercomplete', () => {
|
||||||
console.log('rendercomplete');
|
console.log('rendercomplete');
|
||||||
this.saveState();
|
if (this.map) {
|
||||||
this.map.on('moveend', this.saveState);
|
this.saveState();
|
||||||
|
this.map.on('moveend', this.saveState);
|
||||||
|
}
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -32,19 +32,20 @@ import uUtils from './utils.js';
|
|||||||
* @interface
|
* @interface
|
||||||
* @memberOf MapViewModel
|
* @memberOf MapViewModel
|
||||||
* @type {Object}
|
* @type {Object}
|
||||||
* @property {function(MapViewModel)} init
|
|
||||||
* @property {function} cleanup
|
|
||||||
* @property {function(uTrack, boolean)} displayTrack
|
|
||||||
* @property {function} clearMap
|
|
||||||
* @property {function(number)} animateMarker
|
* @property {function(number)} animateMarker
|
||||||
|
* @property {function(uTrack, boolean)} displayTrack
|
||||||
|
* @property {function} cleanup
|
||||||
|
* @property {function} clearMap
|
||||||
* @property {function} getBounds
|
* @property {function} getBounds
|
||||||
* @property {function} zoomToExtent
|
* @property {function(MapViewModel)} init
|
||||||
* @property {function} zoomToBounds
|
* @property {function} setTrackDefaultStyle
|
||||||
|
* @property {function(uTrack, string, Object)} setTrackGradientStyle
|
||||||
* @property {function} updateSize
|
* @property {function} updateSize
|
||||||
* @property {function} updateState
|
* @property {function} updateState
|
||||||
|
* @property {function} zoomToBounds
|
||||||
|
* @property {function} zoomToExtent
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} MapParams
|
* @typedef {Object} MapParams
|
||||||
* @property {number[]} center
|
* @property {number[]} center
|
||||||
@ -66,12 +67,19 @@ export default class MapViewModel extends ViewModel {
|
|||||||
/** @type {?number} */
|
/** @type {?number} */
|
||||||
markerSelect: null,
|
markerSelect: null,
|
||||||
// click handler
|
// click handler
|
||||||
onMenuToggle: null
|
onMenuToggle: null,
|
||||||
|
speedVisible: false,
|
||||||
|
altitudeVisible: false
|
||||||
});
|
});
|
||||||
this.model.onMenuToggle = () => this.onMapResize();
|
|
||||||
this.state = state;
|
this.state = state;
|
||||||
/** @type HTMLElement */
|
/** @type HTMLElement */
|
||||||
this.mapElement = document.querySelector('#map-canvas');
|
this.mapElement = document.querySelector('#map-canvas');
|
||||||
|
/** @type HTMLInputElement */
|
||||||
|
this.speedEl = this.getBoundElement('speedVisible');
|
||||||
|
/** @type HTMLInputElement */
|
||||||
|
this.altitudeEl = this.getBoundElement('altitudeVisible');
|
||||||
|
/** @type HTMLElement */
|
||||||
|
this.styleEl = this.getBoundElement('trackColor');
|
||||||
this.savedBounds = null;
|
this.savedBounds = null;
|
||||||
this.api = null;
|
this.api = null;
|
||||||
}
|
}
|
||||||
@ -138,6 +146,8 @@ export default class MapViewModel extends ViewModel {
|
|||||||
setObservers() {
|
setObservers() {
|
||||||
config.onChanged('mapApi', (mapApi) => {
|
config.onChanged('mapApi', (mapApi) => {
|
||||||
this.loadMapAPI(mapApi);
|
this.loadMapAPI(mapApi);
|
||||||
|
this.toggleStyleOptions();
|
||||||
|
this.toggleStyleMenu();
|
||||||
});
|
});
|
||||||
this.state.onChanged('currentTrack', (track) => {
|
this.state.onChanged('currentTrack', (track) => {
|
||||||
if (!this.api) {
|
if (!this.api) {
|
||||||
@ -151,6 +161,7 @@ export default class MapViewModel extends ViewModel {
|
|||||||
});
|
});
|
||||||
this.displayTrack(track, true);
|
this.displayTrack(track, true);
|
||||||
}
|
}
|
||||||
|
this.toggleStyleOptions();
|
||||||
});
|
});
|
||||||
this.state.onChanged('history', () => {
|
this.state.onChanged('history', () => {
|
||||||
const history = this.state.history;
|
const history = this.state.history;
|
||||||
@ -167,6 +178,19 @@ export default class MapViewModel extends ViewModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
this.model.onMenuToggle = () => this.onMapResize();
|
||||||
|
this.onChanged('speedVisible', (visible) => {
|
||||||
|
if (visible) {
|
||||||
|
this.model.altitudeVisible = false;
|
||||||
|
}
|
||||||
|
this.setTrackStyle();
|
||||||
|
});
|
||||||
|
this.onChanged('altitudeVisible', (visible) => {
|
||||||
|
if (visible) {
|
||||||
|
this.model.speedVisible = false;
|
||||||
|
}
|
||||||
|
this.setTrackStyle();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -180,10 +204,67 @@ export default class MapViewModel extends ViewModel {
|
|||||||
update = false;
|
update = false;
|
||||||
}
|
}
|
||||||
this.state.history = null;
|
this.state.history = null;
|
||||||
|
this.setTrackStyle();
|
||||||
this.api.displayTrack(track, update)
|
this.api.displayTrack(track, update)
|
||||||
.finally(() => this.state.jobStop());
|
.finally(() => this.state.jobStop());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onMapResize() {
|
||||||
|
if (this.api) {
|
||||||
|
this.api.updateSize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleStyleOptions() {
|
||||||
|
const track = this.state.currentTrack;
|
||||||
|
this.speedEl.disabled = !track || !track.hasSpeeds || track.length <= 1;
|
||||||
|
this.altitudeEl.disabled = !track || !track.hasAltitudes || track.length <= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleStyleMenu() {
|
||||||
|
if (config.mapApi === 'openlayers') {
|
||||||
|
this.styleEl.style.display = 'block';
|
||||||
|
} else {
|
||||||
|
this.styleEl.style.display = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setTrackStyle() {
|
||||||
|
const track = this.state.currentTrack;
|
||||||
|
if (!this.api || !track) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.model.speedVisible && track.hasSpeeds) {
|
||||||
|
this.setSpeedStyle();
|
||||||
|
} else if (this.model.altitudeVisible && track.hasAltitudes) {
|
||||||
|
this.setAltitudeStyle();
|
||||||
|
} else {
|
||||||
|
this.api.setTrackDefaultStyle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setSpeedStyle() {
|
||||||
|
const track = this.state.currentTrack;
|
||||||
|
const scale = {
|
||||||
|
minValue: 0,
|
||||||
|
maxValue: track.maxSpeed,
|
||||||
|
minColor: [ 0, 255, 0 ],
|
||||||
|
maxColor: [ 255, 0, 0 ]
|
||||||
|
};
|
||||||
|
this.api.setTrackGradientStyle(track, 'speed', scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
setAltitudeStyle() {
|
||||||
|
const track = this.state.currentTrack;
|
||||||
|
const scale = {
|
||||||
|
minValue: track.minAltitude,
|
||||||
|
maxValue: track.maxAltitude,
|
||||||
|
minColor: [ 0, 255, 0 ],
|
||||||
|
maxColor: [ 255, 0, 0 ]
|
||||||
|
};
|
||||||
|
this.api.setTrackGradientStyle(track, 'altitude', scale);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get popup html
|
* Get popup html
|
||||||
* @param {number} id Position index
|
* @param {number} id Position index
|
||||||
@ -302,11 +383,4 @@ export default class MapViewModel extends ViewModel {
|
|||||||
<g><path stroke="black" fill="${fill}" d="${MapViewModel.getMarkerPath(isLarge)}"/>${isExtra ? MapViewModel.getMarkerExtra(isLarge) : ''}</g></svg>`;
|
<g><path stroke="black" fill="${fill}" d="${MapViewModel.getMarkerPath(isLarge)}"/>${isExtra ? MapViewModel.getMarkerExtra(isLarge) : ''}</g></svg>`;
|
||||||
return `data:image/svg+xml,${encodeURIComponent(svg)}`;
|
return `data:image/svg+xml,${encodeURIComponent(svg)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
onMapResize() {
|
|
||||||
if (this.api) {
|
|
||||||
this.api.updateSize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -85,6 +85,20 @@ export default class uPosition {
|
|||||||
return (this.image != null && this.image.length > 0);
|
return (this.image != null && this.image.length > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
hasSpeed() {
|
||||||
|
return this.speed != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
hasAltitude() {
|
||||||
|
return this.altitude != null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {?string}
|
* @return {?string}
|
||||||
*/
|
*/
|
||||||
|
@ -49,6 +49,9 @@ export default class uTrack extends uPositionSet {
|
|||||||
this.user = user;
|
this.user = user;
|
||||||
this.plotData = [];
|
this.plotData = [];
|
||||||
this.maxId = 0;
|
this.maxId = 0;
|
||||||
|
this.maxSpeed = 0;
|
||||||
|
this.maxAltitude = null;
|
||||||
|
this.minAltitude = null;
|
||||||
this.totalMeters = 0;
|
this.totalMeters = 0;
|
||||||
this.totalSeconds = 0;
|
this.totalSeconds = 0;
|
||||||
this.listItem(id, name);
|
this.listItem(id, name);
|
||||||
@ -66,6 +69,9 @@ export default class uTrack extends uPositionSet {
|
|||||||
|
|
||||||
clearTrackCounters() {
|
clearTrackCounters() {
|
||||||
this.maxId = 0;
|
this.maxId = 0;
|
||||||
|
this.maxSpeed = 0;
|
||||||
|
this.maxAltitude = null;
|
||||||
|
this.minAltitude = null;
|
||||||
this.plotData.length = 0;
|
this.plotData.length = 0;
|
||||||
this.totalMeters = 0;
|
this.totalMeters = 0;
|
||||||
this.totalSeconds = 0;
|
this.totalSeconds = 0;
|
||||||
@ -86,6 +92,20 @@ export default class uTrack extends uPositionSet {
|
|||||||
return this.plotData.length > 0;
|
return this.plotData.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
get hasAltitudes() {
|
||||||
|
return this.maxAltitude !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
get hasSpeeds() {
|
||||||
|
return this.maxSpeed > 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get track data from json
|
* Get track data from json
|
||||||
* @param {Object[]} posArr Positions data
|
* @param {Object[]} posArr Positions data
|
||||||
@ -268,11 +288,20 @@ export default class uTrack extends uPositionSet {
|
|||||||
this.totalSeconds += position.seconds;
|
this.totalSeconds += position.seconds;
|
||||||
position.totalMeters = this.totalMeters;
|
position.totalMeters = this.totalMeters;
|
||||||
position.totalSeconds = this.totalSeconds;
|
position.totalSeconds = this.totalSeconds;
|
||||||
if (position.altitude != null) {
|
if (position.hasAltitude()) {
|
||||||
this.plotData.push({ x: position.totalMeters, y: position.altitude });
|
this.plotData.push({ x: position.totalMeters, y: position.altitude });
|
||||||
|
if (this.maxAltitude === null || position.altitude > this.maxAltitude) {
|
||||||
|
this.maxAltitude = position.altitude;
|
||||||
|
}
|
||||||
|
if (this.minAltitude === null || position.altitude < this.minAltitude) {
|
||||||
|
this.minAltitude = position.altitude;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (position.id > this.maxId) {
|
if (position.id > this.maxId) {
|
||||||
this.maxId = position.id;
|
this.maxId = position.id;
|
||||||
}
|
}
|
||||||
|
if (position.hasSpeed() && position.speed > this.maxSpeed) {
|
||||||
|
this.maxSpeed = position.speed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -345,11 +345,12 @@ export default class TrackViewModel extends ViewModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderSummary() {
|
renderSummary() {
|
||||||
if (!this.state.currentTrack || !this.state.currentTrack.hasPositions) {
|
const track = this.state.currentTrack;
|
||||||
|
if (!track || !track.hasPositions) {
|
||||||
this.model.summary = '';
|
this.model.summary = '';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const last = this.state.currentTrack.positions[this.state.currentTrack.length - 1];
|
const last = track.positions[track.length - 1];
|
||||||
|
|
||||||
if (this.state.showLatest) {
|
if (this.state.showLatest) {
|
||||||
const today = new Date();
|
const today = new Date();
|
||||||
@ -362,10 +363,21 @@ export default class TrackViewModel extends ViewModel {
|
|||||||
${dateString}
|
${dateString}
|
||||||
${timeString}`;
|
${timeString}`;
|
||||||
} else {
|
} else {
|
||||||
this.model.summary = `
|
let summary = `
|
||||||
<div class="menu-title">${$._('summary')}</div>
|
<div class="menu-title">${$._('summary')}</div>
|
||||||
<div><img class="icon" alt="${$._('tdistance')}" title="${$._('tdistance')}" src="images/distance.svg"> ${$.getLocaleDistanceMajor(last.totalMeters, true)}</div>
|
<div><img class="icon" alt="${$._('tdistance')}" title="${$._('tdistance')}" src="images/distance.svg"> ${$.getLocaleDistanceMajor(last.totalMeters, true)}</div>
|
||||||
<div><img class="icon" alt="${$._('ttime')}" title="${$._('ttime')}" src="images/time.svg"> ${$.getLocaleDuration(last.totalSeconds)}</div>`;
|
<div><img class="icon" alt="${$._('ttime')}" title="${$._('ttime')}" src="images/time.svg"> ${$.getLocaleDuration(last.totalSeconds)}</div>`;
|
||||||
|
if (track.hasSpeeds) {
|
||||||
|
summary += `<div><img class="icon" alt="${$._('speed')}" title="${$._('speed')}" src="images/speed.svg"><b>➚</b> ${$.getLocaleSpeed(track.maxSpeed, true)}</div>`;
|
||||||
|
}
|
||||||
|
if (track.hasAltitudes) {
|
||||||
|
let altitudes = `${$.getLocaleAltitude(track.maxAltitude, true)}`;
|
||||||
|
if (track.minAltitude !== track.maxAltitude) {
|
||||||
|
altitudes = `${$.getLocaleAltitude(track.minAltitude)}–${altitudes}`;
|
||||||
|
}
|
||||||
|
summary += `<div><img class="icon" alt="${$._('altitude')}" title="${$._('altitude')}" src="images/altitude.svg"> ${altitudes}</div>`;
|
||||||
|
}
|
||||||
|
this.model.summary = summary;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,6 +147,27 @@ export default class uUtils {
|
|||||||
.concat(opacity).join(',')})`;
|
.concat(opacity).join(',')})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get rgb color for given scale and intensity
|
||||||
|
* @param {number[]} start Minimum scale color as [ r, g, b ]
|
||||||
|
* @param {number[]} end Maximum scale color as [ r, g, b ]
|
||||||
|
* @param {number} intensity Intensity from 0 to 1
|
||||||
|
* @return {string} Color as rgb() string
|
||||||
|
*/
|
||||||
|
static getScaleColor(start, end, intensity) {
|
||||||
|
if (intensity < 0 || intensity > 1) {
|
||||||
|
throw new Error('Invalid value');
|
||||||
|
}
|
||||||
|
const rgb = [];
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
if (start[i] < 0 || start[i] > 255 || end[i] < 0 || end[i] > 255) {
|
||||||
|
throw new Error('Invalid value');
|
||||||
|
}
|
||||||
|
rgb[i] = Math.round((end[i] - start[i]) * intensity + start[i]);
|
||||||
|
}
|
||||||
|
return `rgb(${rgb[0]}, ${rgb[1]}, ${rgb[2]})`;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add link tag with type css
|
* Add link tag with type css
|
||||||
* @param {string} url attribute
|
* @param {string} url attribute
|
||||||
|
@ -112,8 +112,6 @@ describe('Openlayers map API tests', () => {
|
|||||||
expect(_layer.getVisible()).toBe(true);
|
expect(_layer.getVisible()).toBe(true);
|
||||||
expect(_layer).toEqual(jasmine.any(ol.layer.VectorLayer));
|
expect(_layer).toEqual(jasmine.any(ol.layer.VectorLayer));
|
||||||
expect(_layer.getSource()).toEqual(jasmine.any(ol.source.Vector));
|
expect(_layer.getSource()).toEqual(jasmine.any(ol.source.Vector));
|
||||||
expect(_layer.getStyle().getStroke().getColor()).toBe(uUtils.hexToRGBA(config.strokeColor, config.strokeOpacity));
|
|
||||||
expect(_layer.getStyle().getStroke().getWidth()).toBe(config.strokeWeight);
|
|
||||||
expect(_layer.get('type')).toBe('data');
|
expect(_layer.get('type')).toBe('data');
|
||||||
expect(api.layerTrack).toEqual(_layer);
|
expect(api.layerTrack).toEqual(_layer);
|
||||||
break;
|
break;
|
||||||
@ -570,4 +568,63 @@ describe('Openlayers map API tests', () => {
|
|||||||
expect(api.popup.getElement().firstElementChild.innerHTML).toBe('');
|
expect(api.popup.getElement().firstElementChild.innerHTML).toBe('');
|
||||||
expect(mockViewModel.model.markerSelect).toBe(null);
|
expect(mockViewModel.model.markerSelect).toBe(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should create gradient style', () => {
|
||||||
|
// given
|
||||||
|
const ctx = document.createElement('canvas').getContext('2d');
|
||||||
|
const coordinates = [ [ 0, 0 ], [ 1, 1 ] ];
|
||||||
|
const colors = [ 'white', 'red' ];
|
||||||
|
api.map = mockMap;
|
||||||
|
spyOn(api.map, 'getPixelFromCoordinate').and.callFake((coord) => coord);
|
||||||
|
// when
|
||||||
|
const style = api.getGradientStyle(ctx, coordinates, colors);
|
||||||
|
// then
|
||||||
|
expect(style).toBeInstanceOf(ol.style.Style);
|
||||||
|
expect(style.getGeometry().getCoordinates()).toEqual(coordinates);
|
||||||
|
expect(style.getStroke().getColor()).toBeInstanceOf(CanvasGradient);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set default style for track', () => {
|
||||||
|
// given
|
||||||
|
api.layerTrack = new ol.layer.VectorLayer();
|
||||||
|
config.strokeWeight = 1234;
|
||||||
|
config.strokeColor = 'test color';
|
||||||
|
config.strokeOpacity = 0.1234;
|
||||||
|
const color = 'rgba(1, 1, 1, 1)';
|
||||||
|
spyOn(uUtils, 'hexToRGBA').and.returnValue(color);
|
||||||
|
// when
|
||||||
|
api.setTrackDefaultStyle();
|
||||||
|
// then
|
||||||
|
expect(api.layerTrack.getStyle()).toBeInstanceOf(ol.style.Style);
|
||||||
|
expect(api.layerTrack.getStyle().getStroke().getWidth()).toBe(config.strokeWeight);
|
||||||
|
expect(api.layerTrack.getStyle().getStroke().getColor()).toBe(color);
|
||||||
|
expect(uUtils.hexToRGBA).toHaveBeenCalledWith(config.strokeColor, config.strokeOpacity);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set gradient style for track', () => {
|
||||||
|
// given
|
||||||
|
const track = TrackFactory.getTrack(3);
|
||||||
|
track.positions[0].speed = 0;
|
||||||
|
track.positions[1].speed = 1;
|
||||||
|
track.positions[2].speed = 2;
|
||||||
|
api.map = mockMap;
|
||||||
|
api.layerTrack = new ol.layer.VectorLayer({
|
||||||
|
source: new ol.source.Vector()
|
||||||
|
});
|
||||||
|
spyOn(uUtils, 'getScaleColor').and.returnValue('test color');
|
||||||
|
spyOn(api, 'getGradientStyle').and.returnValue(new ol.style.Style());
|
||||||
|
const lineFeature = new ol.Feature({ geometry: new ol.geom.LineString([]) });
|
||||||
|
for (let i = 0; i < track.length; i++) {
|
||||||
|
lineFeature.getGeometry().appendCoordinate(ol.proj.fromLonLat([ 0, 0 ]));
|
||||||
|
}
|
||||||
|
api.layerTrack.getSource().addFeature(lineFeature);
|
||||||
|
// when
|
||||||
|
api.setTrackGradientStyle(track, 'speed', { minValue: 0, maxValue: 2, minColor: [ 255, 255, 255 ], maxColor: [ 0, 0, 0 ] });
|
||||||
|
api.layerTrack.getStyle()(lineFeature);
|
||||||
|
// then
|
||||||
|
expect(api.layerTrack.getStyle()).toBeInstanceOf(Function);
|
||||||
|
expect(uUtils.getScaleColor).toHaveBeenCalledTimes(track.length);
|
||||||
|
expect(api.getGradientStyle).toHaveBeenCalledTimes(track.length - 1);
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -152,14 +152,14 @@ describe('ChartViewModel tests', () => {
|
|||||||
];
|
];
|
||||||
state.currentTrack = null;
|
state.currentTrack = null;
|
||||||
vm.model.buttonVisible = false;
|
vm.model.buttonVisible = false;
|
||||||
buttonEl.style.visibility = 'hidden';
|
buttonEl.classList.add('menu-hidden');
|
||||||
// when
|
// when
|
||||||
vm.setObservers();
|
vm.setObservers();
|
||||||
state.currentTrack = TrackFactory.getTrack(positions);
|
state.currentTrack = TrackFactory.getTrack(positions);
|
||||||
// then
|
// then
|
||||||
expect(vm.render).toHaveBeenCalledTimes(1);
|
expect(vm.render).toHaveBeenCalledTimes(1);
|
||||||
expect(vm.model.buttonVisible).toBe(true);
|
expect(vm.model.buttonVisible).toBe(true);
|
||||||
expect(buttonEl.style.visibility).toBe('visible');
|
expect(buttonEl.classList.contains('menu-hidden')).toBeFalse();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render chart on null track and hide altitudes button', () => {
|
it('should render chart on null track and hide altitudes button', () => {
|
||||||
@ -171,14 +171,14 @@ describe('ChartViewModel tests', () => {
|
|||||||
];
|
];
|
||||||
state.currentTrack = TrackFactory.getTrack(positions);
|
state.currentTrack = TrackFactory.getTrack(positions);
|
||||||
vm.model.buttonVisible = true;
|
vm.model.buttonVisible = true;
|
||||||
buttonEl.style.visibility = 'visible';
|
buttonEl.classList.remove('menu-hidden');
|
||||||
// when
|
// when
|
||||||
vm.setObservers();
|
vm.setObservers();
|
||||||
state.currentTrack = null;
|
state.currentTrack = null;
|
||||||
// then
|
// then
|
||||||
expect(vm.render).toHaveBeenCalledTimes(1);
|
expect(vm.render).toHaveBeenCalledTimes(1);
|
||||||
expect(vm.model.buttonVisible).toBe(false);
|
expect(vm.model.buttonVisible).toBe(false);
|
||||||
expect(buttonEl.style.visibility).toBe('hidden');
|
expect(buttonEl.classList.contains('menu-hidden')).toBeTrue();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render chart on empty track and hide altitudes button', () => {
|
it('should render chart on empty track and hide altitudes button', () => {
|
||||||
@ -186,36 +186,36 @@ describe('ChartViewModel tests', () => {
|
|||||||
spyOn(vm, 'render');
|
spyOn(vm, 'render');
|
||||||
state.currentTrack = TrackFactory.getTrack(2);
|
state.currentTrack = TrackFactory.getTrack(2);
|
||||||
vm.model.buttonVisible = true;
|
vm.model.buttonVisible = true;
|
||||||
buttonEl.style.visibility = 'visible';
|
buttonEl.classList.remove('menu-hidden');
|
||||||
// when
|
// when
|
||||||
vm.setObservers();
|
vm.setObservers();
|
||||||
state.currentTrack = TrackFactory.getTrack(0);
|
state.currentTrack = TrackFactory.getTrack(0);
|
||||||
// then
|
// then
|
||||||
expect(vm.render).toHaveBeenCalledTimes(1);
|
expect(vm.render).toHaveBeenCalledTimes(1);
|
||||||
expect(vm.model.buttonVisible).toBe(false);
|
expect(vm.model.buttonVisible).toBe(false);
|
||||||
expect(buttonEl.style.visibility).toBe('hidden');
|
expect(buttonEl.classList.contains('menu-hidden')).toBeTrue();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render button visible', () => {
|
it('should render button visible', () => {
|
||||||
// given
|
// given
|
||||||
vm.model.buttonVisible = false;
|
vm.model.buttonVisible = false;
|
||||||
buttonEl.style.visibility = 'hidden';
|
buttonEl.classList.add('menu-hidden');
|
||||||
// when
|
// when
|
||||||
vm.setObservers();
|
vm.setObservers();
|
||||||
vm.model.buttonVisible = true;
|
vm.model.buttonVisible = true;
|
||||||
// then
|
// then
|
||||||
expect(buttonEl.style.visibility).toBe('visible');
|
expect(buttonEl.classList.contains('menu-hidden')).toBeFalse();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render button hidden', () => {
|
it('should render button hidden', () => {
|
||||||
// given
|
// given
|
||||||
vm.model.buttonVisible = true;
|
vm.model.buttonVisible = true;
|
||||||
buttonEl.style.visibility = 'visible';
|
buttonEl.classList.remove('menu-hidden');
|
||||||
// when
|
// when
|
||||||
vm.setObservers();
|
vm.setObservers();
|
||||||
vm.model.buttonVisible = false;
|
vm.model.buttonVisible = false;
|
||||||
// then
|
// then
|
||||||
expect(buttonEl.style.visibility).toBe('hidden');
|
expect(buttonEl.classList.contains('menu-hidden')).toBeTrue();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render chart container visible and render chart', () => {
|
it('should render chart container visible and render chart', () => {
|
||||||
|
@ -153,8 +153,8 @@ describe('ConfigDialogModel tests', () => {
|
|||||||
// given
|
// given
|
||||||
spyOn(cm, 'validate').and.returnValue(true);
|
spyOn(cm, 'validate').and.returnValue(true);
|
||||||
spyOn(config, 'save').and.returnValue(Promise.resolve());
|
spyOn(config, 'save').and.returnValue(Promise.resolve());
|
||||||
cm.model.layerId = '1';
|
|
||||||
cm.init();
|
cm.init();
|
||||||
|
cm.model.layerId = '1';
|
||||||
const button = cm.dialog.element.querySelector("[data-bind='onSave']");
|
const button = cm.dialog.element.querySelector("[data-bind='onSave']");
|
||||||
// when
|
// when
|
||||||
button.click();
|
button.click();
|
||||||
|
@ -37,7 +37,8 @@ describe('Lang tests', () => {
|
|||||||
unitDistance: 'unitd',
|
unitDistance: 'unitd',
|
||||||
factorDistanceMajor: 0.55,
|
factorDistanceMajor: 0.55,
|
||||||
unitDistanceMajor: 'unitdm',
|
unitDistanceMajor: 'unitdm',
|
||||||
unitDay: 'unitday'
|
unitDay: 'unitday',
|
||||||
|
unitAltitude: 'unitamsl'
|
||||||
};
|
};
|
||||||
mockStrings = {
|
mockStrings = {
|
||||||
string1: 'łańcuch1',
|
string1: 'łańcuch1',
|
||||||
@ -45,7 +46,8 @@ describe('Lang tests', () => {
|
|||||||
units: 'jp',
|
units: 'jp',
|
||||||
unitd: 'jo',
|
unitd: 'jo',
|
||||||
unitdm: 'jo / 1000',
|
unitdm: 'jo / 1000',
|
||||||
unitday: 'd'
|
unitday: 'd',
|
||||||
|
unitamsl: 'a.s.l.'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -96,7 +98,7 @@ describe('Lang tests', () => {
|
|||||||
// when
|
// when
|
||||||
lang.init(mockConfig, mockStrings);
|
lang.init(mockConfig, mockStrings);
|
||||||
// then
|
// then
|
||||||
expect(lang.getLocaleSpeed(value, false)).toBe(330);
|
expect(lang.getLocaleSpeed(value, false)).toBe('330');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return localized speed value with unit', () => {
|
it('should return localized speed value with unit', () => {
|
||||||
@ -110,7 +112,7 @@ describe('Lang tests', () => {
|
|||||||
// when
|
// when
|
||||||
lang.init(mockConfig, mockStrings);
|
lang.init(mockConfig, mockStrings);
|
||||||
// then
|
// then
|
||||||
expect(lang.getLocaleDistanceMajor(value, false)).toBe(0.55);
|
expect(lang.getLocaleDistanceMajor(value, false)).toBe('0.55');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return localized distance major value with unit', () => {
|
it('should return localized distance major value with unit', () => {
|
||||||
@ -124,7 +126,7 @@ describe('Lang tests', () => {
|
|||||||
// when
|
// when
|
||||||
lang.init(mockConfig, mockStrings);
|
lang.init(mockConfig, mockStrings);
|
||||||
// then
|
// then
|
||||||
expect(lang.getLocaleDistance(value, false)).toBe(1300);
|
expect(lang.getLocaleDistance(value, false)).toBe('1,300');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return localized distance value with unit', () => {
|
it('should return localized distance value with unit', () => {
|
||||||
@ -138,28 +140,28 @@ describe('Lang tests', () => {
|
|||||||
// when
|
// when
|
||||||
lang.init(mockConfig, mockStrings);
|
lang.init(mockConfig, mockStrings);
|
||||||
// then
|
// then
|
||||||
expect(lang.getLocaleDistance(value, false)).toBe(1300);
|
expect(lang.getLocaleAltitude(value, false)).toBe('1,300');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return localized altitude value with unit', () => {
|
it('should return localized altitude value with unit', () => {
|
||||||
// when
|
// when
|
||||||
lang.init(mockConfig, mockStrings);
|
lang.init(mockConfig, mockStrings);
|
||||||
// then
|
// then
|
||||||
expect(lang.getLocaleDistance(value, true)).toBe(`1,300 ${mockStrings.unitd}`);
|
expect(lang.getLocaleAltitude(value, true)).toBe(`1,300 ${mockStrings.unitd} ${mockStrings.unitamsl}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return localized accuracy value', () => {
|
it('should return localized accuracy value', () => {
|
||||||
// when
|
// when
|
||||||
lang.init(mockConfig, mockStrings);
|
lang.init(mockConfig, mockStrings);
|
||||||
// then
|
// then
|
||||||
expect(lang.getLocaleDistance(value, false)).toBe(1300);
|
expect(lang.getLocaleAccuracy(value, false)).toBe('1,300');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return localized accuracy value with unit', () => {
|
it('should return localized accuracy value with unit', () => {
|
||||||
// when
|
// when
|
||||||
lang.init(mockConfig, mockStrings);
|
lang.init(mockConfig, mockStrings);
|
||||||
// then
|
// then
|
||||||
expect(lang.getLocaleDistance(value, true)).toBe(`1,300 ${mockStrings.unitd}`);
|
expect(lang.getLocaleAccuracy(value, true)).toBe(`1,300 ${mockStrings.unitd}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return localized time duration', () => {
|
it('should return localized time duration', () => {
|
||||||
|
@ -57,6 +57,8 @@ describe('MapViewModel tests', () => {
|
|||||||
'zoomToBounds': { /* ignored */ },
|
'zoomToBounds': { /* ignored */ },
|
||||||
'zoomToExtent': { /* ignored */ },
|
'zoomToExtent': { /* ignored */ },
|
||||||
'displayTrack': Promise.resolve(),
|
'displayTrack': Promise.resolve(),
|
||||||
|
'setTrackDefaultStyle': { /* ignored */ },
|
||||||
|
'setTrackGradientStyle': { /* ignored */ },
|
||||||
'clearMap': { /* ignored */ },
|
'clearMap': { /* ignored */ },
|
||||||
'updateSize': { /* ignored */ }
|
'updateSize': { /* ignored */ }
|
||||||
});
|
});
|
||||||
@ -206,6 +208,7 @@ describe('MapViewModel tests', () => {
|
|||||||
// given
|
// given
|
||||||
vm.api = mockApi;
|
vm.api = mockApi;
|
||||||
vm.bindAll();
|
vm.bindAll();
|
||||||
|
vm.setObservers();
|
||||||
// when
|
// when
|
||||||
menuButtonEl.click();
|
menuButtonEl.click();
|
||||||
// then
|
// then
|
||||||
|
@ -366,4 +366,15 @@ describe('Utils tests', () => {
|
|||||||
expect(result).toBeFalse();
|
expect(result).toBeFalse();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should create color from scale and intensity', () => {
|
||||||
|
// given
|
||||||
|
const start = [ 0, 128, 255 ];
|
||||||
|
const stop = [ 255, 128, 0 ];
|
||||||
|
const intensity = 0.5;
|
||||||
|
// when
|
||||||
|
const color = uUtils.getScaleColor(start, stop, intensity);
|
||||||
|
// then
|
||||||
|
expect(color).toBe('rgb(128, 128, 128)');
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -137,6 +137,7 @@ $lang["allusers"] = "All users";
|
|||||||
$lang["unitday"] = "d"; // abbreviation for days, like 4 d 11:11:11
|
$lang["unitday"] = "d"; // abbreviation for days, like 4 d 11:11:11
|
||||||
$lang["unitkmh"] = "km/h"; // kilometer per hour
|
$lang["unitkmh"] = "km/h"; // kilometer per hour
|
||||||
$lang["unitm"] = "m"; // meter
|
$lang["unitm"] = "m"; // meter
|
||||||
|
$lang["unitamsl"] = "a.s.l."; // above mean see level
|
||||||
$lang["unitkm"] = "km"; // kilometer
|
$lang["unitkm"] = "km"; // kilometer
|
||||||
$lang["unitmph"] = "mph"; // mile per hour
|
$lang["unitmph"] = "mph"; // mile per hour
|
||||||
$lang["unitft"] = "ft"; // feet
|
$lang["unitft"] = "ft"; // feet
|
||||||
@ -169,4 +170,5 @@ $lang["add"] = "Add";
|
|||||||
$lang["edit"] = "Edit";
|
$lang["edit"] = "Edit";
|
||||||
$lang["delete"] = "Delete";
|
$lang["delete"] = "Delete";
|
||||||
$lang["settings"] = "Settings";
|
$lang["settings"] = "Settings";
|
||||||
|
$lang["trackcolor"] = "Track color";
|
||||||
?>
|
?>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user