trackdirect2/jslib/src/trackdirect/models/Map.js

1569 lines
46 KiB
JavaScript
Executable File

/**
* Class trackdirect.models.Map
* @see https://developers.google.com/maps/documentation/javascript/reference#Map
* @param {string} mapElementId
* @param {object} options
*/
trackdirect.models.Map = function (mapElementId, options) {
this._init(mapElementId, options);
// Call the parent constructor
if (typeof google === "object" && typeof google.maps === "object") {
google.maps.Map.call(
this,
document.getElementById(mapElementId),
this._getGoolgeMapOptions()
);
} else if (typeof L === "object") {
L.Map.call(
this,
document.getElementById(mapElementId),
this._getLeafletMapOptions()
);
this._updateLeafletTileLayer();
L.control
.zoom({
position: "bottomright",
})
.addTo(this);
}
this._initMap();
};
if (typeof google === "object" && typeof google.maps === "object") {
trackdirect.models.Map.prototype = Object.create(google.maps.Map.prototype);
} else if (typeof L === "object") {
trackdirect.models.Map.prototype = Object.create(L.Map.prototype);
}
trackdirect.models.Map.prototype.constructor = trackdirect.models.Map;
/**
* Init
*/
trackdirect.models.Map.prototype._init = function (mapElementId, options) {
this._mapElementId = mapElementId;
this._tdMapOptions = options;
this._initBasic();
this._initMapTypes();
};
/**
* Init basic stuff
*/
trackdirect.models.Map.prototype._initBasic = function () {
this._tdEventListeners = {};
this._tdEventTimeout = null;
this._currentContentZoom = null;
this._visibleMapSectors = [];
this._newMarkersToShow = [];
this._leafletTileLayer = null;
this._heatMap = null;
// Init public variables
this.markerCollection = new trackdirect.models.MarkerCollection();
this.state = new trackdirect.models.MapState();
};
/**
* Init map types
*/
trackdirect.models.Map.prototype._initMapTypes = function () {
this._supportedMapTypes = {};
if (
typeof this._tdMapOptions.supportedMapTypes !== "undefined" &&
this._tdMapOptions.supportedMapTypes !== null &&
Object.keys(this._tdMapOptions.supportedMapTypes).length > 0
) {
this._supportedMapTypes = this._tdMapOptions.supportedMapTypes;
} else if (typeof google === "object" && typeof google.maps === "object") {
this._supportedMapTypes["roadmap"] = google.maps.MapTypeId.ROADMAP;
this._supportedMapTypes["terrain"] = google.maps.MapTypeId.TERRAIN;
this._supportedMapTypes["satellite"] = google.maps.MapTypeId.SATELLITE;
this._supportedMapTypes["hybrid"] = google.maps.MapTypeId.HYBRID;
} else if (typeof L === "object") {
this._supportedMapTypes["roadmap"] = "OpenStreetMap.Mapnik";
this._supportedMapTypes["terrain"] = "OpenTopoMap";
}
if (
typeof this._tdMapOptions.maptype !== "undefined" &&
this._tdMapOptions.maptype !== null &&
this._tdMapOptions.maptype in this._supportedMapTypes
) {
this._mapType = this._tdMapOptions.maptype;
} else {
this._mapType = Object.keys(this._supportedMapTypes)[0];
}
};
/**
* Init the map
*/
trackdirect.models.Map.prototype._initMap = function () {
this._heatMap = new trackdirect.models.HeatMap(this);
this._setMapInitialLocation();
this._initOms();
this._initInfoWindowEvent();
this._updateMapContent();
this._initMapEvents();
// create kmlLayer object from URL
if (typeof google === "object" && typeof google.maps === "object") {
if (typeof this._tdMapOptions.mid !== "undefined") {
var kmlUrl =
"https://www.google.com/maps/d/u/0/kml?mid=" + this._tdMapOptions.mid;
var kmlLayer = new google.maps.KmlLayer(kmlUrl, { map: this });
}
}
};
/**
* Trigger a resize event
*/
trackdirect.models.Map.prototype.triggerResize = function () {
if (typeof google === "object" && typeof google.maps === "object") {
google.maps.event.trigger(this, "resize");
} else if (typeof L === "object") {
L.Map.prototype._onResize.call(this);
}
};
/**
* Set Map center
* @param {LatLngLiteral} pos
* @param {int} zoom
*/
trackdirect.models.Map.prototype.setCenter = function (pos, zoom) {
if (typeof google === "object" && typeof google.maps === "object") {
google.maps.Map.prototype.setCenter.call(this, pos);
if (typeof zoom !== "undefined") {
google.maps.Map.prototype.setZoom.call(this, zoom);
}
} else if (typeof L === "object") {
zoom = typeof zoom !== "undefined" ? zoom : this.getZoom();
L.Map.prototype.setView.call(this, pos, zoom);
}
this._renderCordinatesContainer(pos);
};
/**
* Get Map center literal
* @return LatLngLiteral
*/
trackdirect.models.Map.prototype.getCenterLiteral = function () {
if (typeof google === "object" && typeof google.maps === "object") {
var latLng = google.maps.Map.prototype.getCenter.call(this);
if (typeof latLng !== "undefined" && typeof latLng.lat === "function") {
return { lat: latLng.lat(), lng: latLng.lng() };
} else {
return latLng;
}
} else if (typeof L === "object") {
var latLng = L.Map.prototype.getCenter.call(this);
if (typeof latLng !== "undefined") {
return { lat: latLng.lat, lng: latLng.lng };
} else {
return latLng;
}
}
return null;
};
/**
* Fit bounds
* @param {Array} bounds
*/
trackdirect.models.Map.prototype.fitBounds = function (bounds) {
if (typeof google === "object" && typeof google.maps === "object") {
var latLngBounds = new google.maps.LatLngBounds();
for (var i = 0; i < bounds.length; i++) {
latLngBounds.extend(bounds[i]);
}
google.maps.Map.prototype.fitBounds.call(this, latLngBounds);
} else if (typeof L === "object") {
L.Map.prototype.fitBounds.call(this, bounds);
}
};
/**
* set Map center by station position
* @param {LatLngLiteral} pos
*/
trackdirect.models.Map.prototype.setCenterByStationId = function (stationId) {
var latestVisibleMarker =
this.markerCollection.getStationLatestVisibleMarker(stationId);
if (latestVisibleMarker !== null) {
this.setCenter(latestVisibleMarker.packet.getLatLngLiteral());
}
};
/**
* Get map DOM element id
* @return {string}
*/
trackdirect.models.Map.prototype.getMapElementId = function () {
return this._mapElementId;
};
/**
* Get map options
* @return {object}
*/
trackdirect.models.Map.prototype.getTdMapOptions = function () {
return this._tdMapOptions;
};
/**
* Returns true if specified map sector is visible
* If we are filtering we consider all mapsectors visible
* @param {int} mapSector
* @return {boolean}
*/
trackdirect.models.Map.prototype.isMapSectorVisible = function (mapSector) {
if (
this._visibleMapSectors.indexOf(mapSector) >= 0 ||
this.state.isFilterMode
) {
return true;
}
return false;
};
/**
* Returnes the number of new markers in queue
* @return {int}
*/
trackdirect.models.Map.prototype.getNumberOfNewMarkersToShow = function () {
return this._newMarkersToShow;
};
/**
* Show all markers that has recently been added
* @param {boolean} track
*/
trackdirect.models.Map.prototype.showNewMarkersInQueue = function (track) {
track = typeof track !== "undefined" ? track : true;
var oldestAllowedTrackingTimestamp = 0;
if (this.state.onlyTrackRecentPackets) {
oldestAllowedTrackingTimestamp = Math.floor(Date.now() / 1000) - 60;
}
while ((markerIdKey = this._newMarkersToShow.pop()) != null) {
if (!this.markerCollection.isExistingMarker(markerIdKey)) {
continue;
}
var marker = this.markerCollection.getMarker(markerIdKey);
trackdirect.services.callbackExecutor.addWithPriority(
marker,
marker.showCompleteMarker,
[]
);
// Track
if (track && marker.packet.packet_order_id == 1) {
if (
marker.shouldMarkerBeVisible() &&
marker.showAsMarker &&
this.state.trackStationId !== null &&
this.state.trackStationId == marker.packet.station_id &&
this.state.getClientTimestamp(marker.packet.timestamp) >
oldestAllowedTrackingTimestamp
) {
trackdirect.services.callbackExecutor.addWithPriority(
this,
this.setCenterByStationId,
[marker.packet.station_id]
);
}
}
// Open previoulsy open info window
if (
this.state.openInfoWindowForMarkerIdKey !== null &&
this.state.openInfoWindowForMarkerIdKey == markerIdKey &&
marker.packet.packet_order_id == 1 &&
marker.shouldMarkerBeVisible() &&
marker.showAsMarker
) {
trackdirect.services.callbackExecutor.addWithPriority(
this,
this.openLatestStationInfoWindow,
[marker.packet.station_id]
);
}
}
this.state.openInfoWindowForMarkerIdKey = null;
};
/**
* Activate filtered mode
*/
trackdirect.models.Map.prototype.activateFilteredMode = function () {
this.state.isFilterMode = true;
this._deactivateHeatMap();
this._updateMapContent();
};
/**
* Deactivate filtered mode
*/
trackdirect.models.Map.prototype.deactivateFilteredMode = function () {
if (this.state.isFilterMode) {
this._activateHeatMap();
this.state.isFilterMode = false;
this.state.filterStationIds = [];
this._updateMapContent();
}
};
/**
* Get the north east latitude of the current map view
* @return {float}
*/
trackdirect.models.Map.prototype.getNorthEastLat = function () {
if (this.getBounds() != null) {
if (typeof google === "object" && typeof google.maps === "object") {
return this.getBounds().getNorthEast().lat();
} else if (typeof L === "object") {
return this.getBounds().getNorthEast().lat;
}
}
return 0;
};
/**
* Get the north east longtitue of the current map view
* @return {float}
*/
trackdirect.models.Map.prototype.getNorthEastLng = function () {
if (this.getBounds() != null) {
if (typeof google === "object" && typeof google.maps === "object") {
return this.getBounds().getNorthEast().lng();
} else if (typeof L === "object") {
return this.getBounds().getNorthEast().lng;
}
}
return 0;
};
/**
* Get the south west latitude of the current map view
* @return {float}
*/
trackdirect.models.Map.prototype.getSouthWestLat = function () {
if (this.getBounds() != null) {
if (typeof google === "object" && typeof google.maps === "object") {
return this.getBounds().getSouthWest().lat();
} else if (typeof L === "object") {
return this.getBounds().getSouthWest().lat;
}
}
return 0;
};
/**
* Get the south west longitude of the current map view
* @return {float}
*/
trackdirect.models.Map.prototype.getSouthWestLng = function () {
if (this.getBounds() != null) {
if (typeof google === "object" && typeof google.maps === "object") {
return this.getBounds().getSouthWest().lng();
} else if (typeof L === "object") {
return this.getBounds().getSouthWest().lng;
}
}
return 0;
};
/**
* Returns true if map is ready
* @return {boolean}
*/
trackdirect.models.Map.prototype.isMapReady = function () {
if (this.getBounds() != null) {
return true;
}
return false;
};
/**
* Set map type
* @param {string} mapType
*/
trackdirect.models.Map.prototype.setMapType = function (mapType) {
if (mapType in this._supportedMapTypes) {
this._mapType = mapType;
if (typeof google === "object" && typeof google.maps === "object") {
this._updateGoogleMapTileLayer();
} else if (typeof L === "object") {
this._updateLeafletTileLayer();
}
this._emitTdEventListeners("change");
}
};
/**
* Get map type
* @return {string}
*/
trackdirect.models.Map.prototype.getMapType = function () {
return this._mapType;
};
/**
* Get leaflet tile layer
* @return {string}
*/
trackdirect.models.Map.prototype.getLeafletTileLayer = function () {
return this._leafletTileLayer;
};
/**
* Returns current mid
* @return {String}
*/
trackdirect.models.Map.prototype.getMid = function () {
if (typeof this._tdMapOptions.mid !== "undefined") {
return this._tdMapOptions.mid;
}
return null;
};
/**
* Reset all markers, this will remove everything from map and memory
*/
trackdirect.models.Map.prototype.resetAllMarkers = function () {
while (this.markerCollection.getNumberOfMarkers() > 0) {
var i = this.markerCollection.getNumberOfMarkers();
while (i--) {
var marker = this.markerCollection.getMarker(i);
if (marker !== null) {
marker.stopToOldTimeout();
marker.stopDirectionPolyline();
marker.hide();
marker.hideMarkerPrevPosition();
marker.hideMarkerTail();
var stationCoverage = this.markerCollection.getStationCoverage(marker.packet.station_id);
if (stationCoverage) {
stationCoverage.hide();
}
}
this.markerCollection.removeMarker(i);
}
}
if (this.state.openInfoWindow !== null) {
this.state.openInfoWindow.hide();
}
if (this.oms) {
this.oms.clearMarkers();
}
this.markerCollection.resetAllMarkers();
};
/**
* Open marker info window
* @param {trackdirect.models.Marker} marker
* @param {boolean} disableAutoPan
*/
trackdirect.models.Map.prototype.openMarkerInfoWindow = function (
marker,
disableAutoPan
) {
disableAutoPan =
typeof disableAutoPan !== "undefined" ? disableAutoPan : true;
if (marker.getMap() !== null) {
if (
this.state.openInfoWindow !== null &&
this.state.openInfoWindow.getMarker().packet.id != marker.packet.id
) {
this.state.openInfoWindow.hide();
}
if (
this.state.openInfoWindow !== null &&
this.state.openInfoWindow.getMarker().packet.id == marker.packet.id
) {
// Just update existing infoWindow
this.state.openInfoWindow.setMarker(marker);
} else {
this.state.openInfoWindow = new trackdirect.models.InfoWindow(
marker,
this,
disableAutoPan
);
}
this._addInfoWindowListeners(this.state.openInfoWindow);
this.state.openInfoWindow.show();
}
};
/**
* Open latest station info window
* @param {int} stationId
*/
trackdirect.models.Map.prototype.openLatestStationInfoWindow = function (
stationId
) {
var latestVisibleMarker =
this.markerCollection.getStationLatestVisibleMarker(stationId);
if (latestVisibleMarker !== null) {
// open marker info-window since this marker replaced a previous one
this.openMarkerInfoWindow(latestVisibleMarker);
}
};
/**
* Open polyline info window
* @param {trackdirect.models.Marker} marker
* @param {LatLng} position
*/
trackdirect.models.Map.prototype.openPolylineInfoWindow = function (
marker,
position
) {
if (this.state.openInfoWindow !== null) {
this.state.openInfoWindow.hide();
}
this.state.openInfoWindow = new trackdirect.models.InfoWindow(marker, this);
this._addInfoWindowListeners(this.state.openInfoWindow);
this.state.openInfoWindow.show(true, position);
};
/**
* Add some trackdirect.models.InfoWindow listeners
* @param {trackdirect.models.InfoWindow} infoWindow
*/
trackdirect.models.Map.prototype._addInfoWindowListeners = function (
infoWindow
) {
var me = this;
infoWindow.addTdListener("station-tail-needed", function (stationId) {
me._emitTdEventListeners("station-tail-needed", stationId);
});
};
/**
* Add listener to events
* @param {string} event
* @param {string} handler
*/
(trackdirect.models.Map.prototype.addTdListener = function (event, handler) {
if (!(event in this._tdEventListeners)) {
this._tdEventListeners[event] = [];
}
this._tdEventListeners[event].push(handler);
}),
/**
* Emit all event listeners for a specified event
* @param {string} event
* @param {object} arg
*/
(trackdirect.models.Map.prototype._emitTdEventListeners = function (
event,
arg
) {
if (event in this._tdEventListeners) {
for (var i = 0; i < this._tdEventListeners[event].length; i++) {
this._tdEventListeners[event][i](arg);
}
}
});
/**
* Make sure all listener that are waiting for a map change event gets called
*/
(trackdirect.models.Map.prototype._triggerMapChangeEvent = function () {
// Execute event "change", but wait some to avoid to many events
if (this._tdEventTimeout != null) {
clearTimeout(this._tdEventTimeout);
}
var me = this;
this._tdEventTimeout = window.setTimeout(function () {
me._emitTdEventListeners("change");
me._tdEventTimeout = null;
}, 5);
}),
/**
* Deactivate heatmap
*/
(trackdirect.models.Map.prototype._deactivateHeatMap = function () {
if (typeof google === "object" && typeof google.maps === "object") {
if (
typeof this.overlayMapTypes !== "undefined" &&
this.overlayMapTypes.length > 0
) {
this.overlayMapTypes.clear();
}
} else if (typeof L === "object") {
if (this._heatMap !== null) {
this.removeLayer(this._heatMap);
}
}
});
/**
* Activate heatmap
*/
trackdirect.models.Map.prototype._activateHeatMap = function () {
if (typeof google === "object" && typeof google.maps === "object") {
if (
typeof this.overlayMapTypes !== "undefined" &&
this.overlayMapTypes.length == 0
) {
this.overlayMapTypes.setAt(0, this._heatMap);
}
} else if (typeof L === "object") {
if (this._heatMap !== null) {
this._heatMap.addTo(this);
this._heatMap.bringToFront();
this._heatMap.setZIndex(1000);
}
}
};
/**
* Update google map tile layer
*/
trackdirect.models.Map.prototype._updateGoogleMapTileLayer = function () {
this.setMapTypeId(this._supportedMapTypes[this._mapType]);
};
/**
* Update Leaflet Map Tile Layer
*/
trackdirect.models.Map.prototype._updateLeafletTileLayer = function () {
// Skip this part if we are running the Windy API or if embedded
if (typeof windyInit !== "function") {
if (this._leafletTileLayer !== null) {
this._leafletTileLayer.remove();
}
if (typeof L.mapboxGL === "function") {
var attribution = "";
var accessToken = "no-token";
var style = "";
if ("mapboxGLStyle" in this._tdMapOptions) {
style = this._tdMapOptions["mapboxGLStyle"];
}
if ("mapboxGLAccessToken" in this._tdMapOptions) {
accessToken = this._tdMapOptions["mapboxGLAccessToken"];
}
if ("mapboxGLAttribution" in this._tdMapOptions) {
attribution = this._tdMapOptions["mapboxGLAttribution"];
}
this._leafletTileLayer = L.mapboxGL({
accessToken: accessToken,
style: style,
});
this.addLayer(this._leafletTileLayer);
this.attributionControl.addAttribution(attribution);
} else {
var options = {};
if (isHighDensity()) {
options["ppi"] = "320";
options["size"] = "512";
} else if (trackdirect.isMobile) {
options["ppi"] = "250";
}
this._leafletTileLayer = L.tileLayer.provider(
this._supportedMapTypes[this._mapType],
options
);
this.addLayer(this._leafletTileLayer);
this._leafletTileLayer.bringToBack();
}
}
};
/**
* Handle map change event, show markers in new map sectors and hide markers in previous map sectors
*/
trackdirect.models.Map.prototype._updateMapContent = function () {
if (this.getBounds() != null) {
var previousVisibleMapSectors = [];
for (var i = 0; i < this._visibleMapSectors.length; i++) {
previousVisibleMapSectors.push(this._visibleMapSectors[i]);
}
this._visibleMapSectors =
trackdirect.services.MapSectorCalculator.getMapSectors(this.getBounds());
this._triggerMapChangeEvent();
if (!this.state.isFilterMode) {
if (this.getZoom() < trackdirect.settings.minZoomForMarkers) {
// When all markers should be hidden we also make sure that markers in current visible map-sectors are removed
this.hideAllMarkers();
this._activateHeatMap();
} else {
this._deactivateHeatMap();
this._hideMarkersInPreviousVisibleMapSectors(previousVisibleMapSectors);
this._showMarkersInNewVisibleMapSectors(previousVisibleMapSectors);
if (this._isAnyMarkerDetailsVisible()) {
this._showVisibleMarkerDetails();
}
if (this._isAnyMarkerDetailsHidden()) {
this._hideVisibleMarkerDetails();
}
}
}
}
this._currentContentZoom = this.getZoom();
};
/**
* Returns true if any marker details should be visible
* @return {boolean}
*/
trackdirect.models.Map.prototype._isAnyMarkerDetailsVisible = function () {
// This is only needed when zooming (when moving the regular show-marker will also show the details)
var showPrevPosition =
this.getZoom() >= trackdirect.settings.minZoomForMarkerPrevPosition &&
this._currentContentZoom <
trackdirect.settings.minZoomForMarkerPrevPosition;
var showMarkerTail =
this.getZoom() >= trackdirect.settings.minZoomForMarkerTail &&
this._currentContentZoom < trackdirect.settings.minZoomForMarkerTail;
var showMarkerLabel =
this.getZoom() >= trackdirect.settings.minZoomForMarkerLabel &&
this._currentContentZoom < trackdirect.settings.minZoomForMarkerLabel;
if (showPrevPosition || showMarkerTail || showMarkerLabel) {
return true;
}
return false;
};
/**
* Returns true if any marker details should be hidden
* @return {boolean}
*/
trackdirect.models.Map.prototype._isAnyMarkerDetailsHidden = function () {
// This is only needed when zooming (when moving the regular show-marker will also show the details)
var hidePrevPosition =
this.getZoom() < trackdirect.settings.minZoomForMarkerPrevPosition &&
this._currentContentZoom >=
trackdirect.settings.minZoomForMarkerPrevPosition;
var hideMarkerTail =
this.getZoom() < trackdirect.settings.minZoomForMarkerTail &&
this._currentContentZoom >= trackdirect.settings.minZoomForMarkerTail;
var hideMarkerLabel =
this.getZoom() < trackdirect.settings.minZoomForMarkerLabel &&
this._currentContentZoom >= trackdirect.settings.minZoomForMarkerLabel;
if (hidePrevPosition || hideMarkerTail || hideMarkerLabel) {
return true;
}
return false;
};
/**
* Show markers details that should be visible in visible map sectors
*/
trackdirect.models.Map.prototype._showVisibleMarkerDetails = function () {
for (var i = 0; i < this._visibleMapSectors.length; i++) {
var mapSector = this._visibleMapSectors[i];
var mapSectorMarkerKeys =
this.markerCollection.getMapSectorMarkerKeys(mapSector);
// Array with markers fo this sector exists, we have something to show
for (var j = 0; j < mapSectorMarkerKeys.length; j++) {
var markerIdKey = mapSectorMarkerKeys[j];
var marker = this.markerCollection.getMarker(markerIdKey);
if (marker === null) {
continue;
}
trackdirect.services.callbackExecutor.addWithPriority(
marker,
marker.showMarkerDetails,
[]
);
}
}
};
/**
* Hide markers details that should not be visible in visible map sectors
*/
trackdirect.models.Map.prototype._hideVisibleMarkerDetails = function () {
for (var i = 0; i < this._visibleMapSectors.length; i++) {
var mapSector = this._visibleMapSectors[i];
var mapSectorMarkerKeys =
this.markerCollection.getMapSectorMarkerKeys(mapSector);
// Array with markers fo this sector exists, we have something to hide
for (var j = 0; j < mapSectorMarkerKeys.length; j++) {
var markerIdKey = mapSectorMarkerKeys[j];
var marker = this.markerCollection.getMarker(markerIdKey);
if (marker === null) {
continue;
}
trackdirect.services.callbackExecutor.addWithPriority(
marker,
marker.hideMarkerDetails,
[]
);
}
}
};
/**
* Show all markers that should be visible and hide alla markers that should not be visible
* This is used to show or hide markers when user activate/deactivate filters
*/
trackdirect.models.Map.prototype.showHideMarkers = function () {
if (this.oms) {
this.oms.unspiderfy();
}
if (this.state.isFilterMode) {
for (var markerIdKey in this.markerCollection.getAllMarkers()) {
if (this.markerCollection.isExistingMarker(markerIdKey)) {
var marker = this.markerCollection.getMarker(markerIdKey);
if (marker) {
if (marker.shouldMarkerBeVisible()) {
marker.showCompleteMarker();
} else {
marker.hideCompleteMarker();
}
}
if (marker.packet) {
this.showTopLabelOnPosition(
marker.packet.latitude,
marker.packet.longitude
);
}
}
}
} else {
for (var i = 0; i < this._visibleMapSectors.length; i++) {
var mapSector = this._visibleMapSectors[i];
var mapSectorMarkerKeys =
this.markerCollection.getMapSectorMarkerKeys(mapSector);
// Array with markers for this sector exists, we have something to show/hide
for (var j = 0; j < mapSectorMarkerKeys.length; j++) {
var markerIdKey = mapSectorMarkerKeys[j];
if (this.markerCollection.isExistingMarker(markerIdKey)) {
var marker = this.markerCollection.getMarker(markerIdKey);
if (marker.shouldMarkerBeVisible()) {
marker.showCompleteMarker();
} else {
marker.hideCompleteMarker();
}
this.showTopLabelOnPosition(
marker.packet.latitude,
marker.packet.longitude
);
}
}
}
}
};
/**
* Show or hide ALL PHG Circles
* This is used to show or hide circles when user activate/deactivate PHG circles
*/
trackdirect.models.Map.prototype.showHidePHGCircles = function () {
if (this.getZoom() >= trackdirect.settings.minZoomForMarkers) {
if (this.state.isFilterMode) {
for (var markerIdKey in this.markerCollection.getAllMarkers()) {
if (this.markerCollection.isExistingMarker(markerIdKey)) {
var marker = this.markerCollection.getMarker(markerIdKey);
if (
marker.showAsMarker &&
marker.packet.phg != null &&
marker.getMap() !== null &&
marker.shouldMarkerBeVisible()
) {
if (this.state.showPHGCircles == 0) {
marker.hidePHGCircle();
} else if (this.state.showPHGCircles == 1) {
marker.showPHGCircle(true);
} else if (this.state.showPHGCircles == 2) {
marker.showPHGCircle(false);
}
}
}
}
} else {
for (var i = 0; i < this._visibleMapSectors.length; i++) {
var mapSector = this._visibleMapSectors[i];
var mapSectorMarkerKeys =
this.markerCollection.getMapSectorMarkerKeys(mapSector);
// Array with markers for this sector exists, we have something to show/hide
for (var j = 0; j < mapSectorMarkerKeys.length; j++) {
var markerIdKey = mapSectorMarkerKeys[j];
if (this.markerCollection.isExistingMarker(markerIdKey)) {
var marker = this.markerCollection.getMarker(markerIdKey);
if (
marker.showAsMarker &&
marker.packet.phg != null &&
marker.getMap() !== null &&
marker.shouldMarkerBeVisible()
) {
if (this.state.showPHGCircles == 0) {
marker.hidePHGCircle();
} else if (this.state.showPHGCircles == 1) {
marker.showPHGCircle(true);
} else if (this.state.showPHGCircles == 2) {
marker.showPHGCircle(false);
}
}
}
}
}
}
}
};
/**
* Show or hide ALL RNG Circles
* This is used to show or hide circles when user activate/deactivate PHG circles
*/
trackdirect.models.Map.prototype.showHideRNGCircles = function () {
if (this.getZoom() >= trackdirect.settings.minZoomForMarkers) {
if (this.state.isFilterMode) {
for (var markerIdKey in this.markerCollection.getAllMarkers()) {
if (this.markerCollection.isExistingMarker(markerIdKey)) {
var marker = this.markerCollection.getMarker(markerIdKey);
if (
marker.showAsMarker &&
marker.packet.rng != null &&
marker.getMap() !== null &&
marker.shouldMarkerBeVisible()
) {
if (this.state.showRNGCircles == 0) {
marker.hideRNGCircle();
} else if (this.state.showRNGCircles == 1) {
marker.showRNGCircle(true);
} else if (this.state.showRNGCircles == 2) {
marker.showRNGCircle(false);
}
}
}
}
} else {
for (var i = 0; i < this._visibleMapSectors.length; i++) {
var mapSector = this._visibleMapSectors[i];
var mapSectorMarkerKeys =
this.markerCollection.getMapSectorMarkerKeys(mapSector);
// Array with markers for this sector exists, we have something to show/hide
for (var j = 0; j < mapSectorMarkerKeys.length; j++) {
var markerIdKey = mapSectorMarkerKeys[j];
if (this.markerCollection.isExistingMarker(markerIdKey)) {
var marker = this.markerCollection.getMarker(markerIdKey);
if (
marker.showAsMarker &&
marker.packet.rng != null &&
marker.getMap() !== null &&
marker.shouldMarkerBeVisible()
) {
if (this.state.showRNGCircles == 0) {
marker.hideRNGCircle();
} else if (this.state.showRNGCircles == 1) {
marker.showRNGCircle(true);
} else if (this.state.showRNGCircles == 2) {
marker.showRNGCircle(false);
}
}
}
}
}
}
}
};
/**
* Make sure that all markers that we know is hidden
*/
trackdirect.models.Map.prototype.hideAllMarkers = function () {
for (var markerIdKey in this.markerCollection.getAllMarkers()) {
var marker = this.markerCollection.getMarker(markerIdKey);
if (marker !== null) {
// hide this marker
marker.hideCompleteMarker();
}
}
};
/**
* Make the label of the latest received marker show on specified position
* @param {number} latitude
* @param {number} longitude
*/
trackdirect.models.Map.prototype.showTopLabelOnPosition = function (
latitude,
longitude
) {
var topMarkerIdKey = -1;
var topMarkerZindex = 0;
if (
Object.keys(
this.markerCollection.getPositionMarkerIdKeys(latitude, longitude)
).length > 1
) {
for (var markerIdKey in this.markerCollection.getPositionMarkerIdKeys(
latitude,
longitude
)) {
var marker = this.markerCollection.getMarker(markerIdKey);
if (marker !== null && marker.shouldMarkerBeVisible()) {
if (marker.getZIndex() > topMarkerZindex) {
topMarkerZindex = marker.getZIndex();
topMarkerIdKey = markerIdKey;
}
marker.hideLabel();
marker.hasLabel = false;
}
}
if (topMarkerIdKey != -1) {
var topMarker = this.markerCollection.getMarker(topMarkerIdKey);
topMarker.hasLabel = true;
var topMarkerMapSector =
trackdirect.services.MapSectorCalculator.getMapSector(
topMarker.getPositionLiteral().lat,
topMarker.getPositionLiteral().lng
);
if (this.state.isFilterMode) {
topMarker.showLabel();
} else if (
this.isMapSectorVisible(topMarkerMapSector) &&
this.getZoom() >= trackdirect.settings.minZoomForMarkerLabel
) {
topMarker.showLabel();
}
}
}
};
/**
* Hide markers in previously visible map sectors
* (This should handle both zooming and moving)
* @param {array} previousVisibleMapSectors
*/
trackdirect.models.Map.prototype._hideMarkersInPreviousVisibleMapSectors =
function (previousVisibleMapSectors) {
// IMPORTANT: Do this before showing marker since marker may exist in both previus shown sectors and visible sectors
// (if we show first we may hide something that should be visible)
if (this._currentContentZoom >= trackdirect.settings.minZoomForMarkers) {
var markerIdKeyListToMaybeHide = {};
var markerIdKeyListNotToHide = {};
for (var i = 0; i < previousVisibleMapSectors.length; i++) {
var mapSector = previousVisibleMapSectors[i];
if (
!this.isMapSectorVisible(mapSector) ||
this.getZoom() < trackdirect.settings.minZoomForMarkers
) {
// Seems like this sector is not visible any more (or we have zoomed out), hide markers
var mapSectorMarkerKeys =
this.markerCollection.getMapSectorMarkerKeys(mapSector);
for (var j = 0; j < mapSectorMarkerKeys.length; j++) {
var markerIdKey = mapSectorMarkerKeys[j];
markerIdKeyListToMaybeHide[markerIdKey] = markerIdKey;
}
} else if (this.getZoom() >= trackdirect.settings.minZoomForMarkers) {
// Seems like this map sector is still visible (and we have not zoomed out)
var mapSectorMarkerKeys =
this.markerCollection.getMapSectorMarkerKeys(mapSector);
for (var j = 0; j < mapSectorMarkerKeys.length; j++) {
var markerIdKey = mapSectorMarkerKeys[j];
// Marker exists in map sector that is still visible, do not hide it
markerIdKeyListNotToHide[markerIdKey] = markerIdKey;
}
}
}
for (var markerIdKey in markerIdKeyListToMaybeHide) {
if (markerIdKey in markerIdKeyListNotToHide) {
continue;
}
var marker = this.markerCollection.getMarker(markerIdKey);
if (marker !== null) {
// hide this marker
trackdirect.services.callbackExecutor.addWithPriority(
marker,
marker.hideCompleteMarker,
[]
);
}
}
}
};
/**
* Show markers in new visible map sectors
* @param {array} previousVisibleMapSectors
*/
trackdirect.models.Map.prototype._showMarkersInNewVisibleMapSectors = function (
previousVisibleMapSectors
) {
// Show new markers that is visible in new map sectors
// This should handle both zooming and moving
if (this.getZoom() >= trackdirect.settings.minZoomForMarkers) {
for (var i = 0; i < this._visibleMapSectors.length; i++) {
var mapSector = this._visibleMapSectors[i];
if (
previousVisibleMapSectors.indexOf(mapSector) == -1 ||
this._currentContentZoom < trackdirect.settings.minZoomForMarkers
) {
// Seems like this sector is new (or we have zoomed in now), show markers
var mapSectorMarkerKeys =
this.markerCollection.getMapSectorMarkerKeys(mapSector);
for (var j = 0; j < mapSectorMarkerKeys.length; j++) {
var markerIdKey = mapSectorMarkerKeys[j];
var marker = this.markerCollection.getMarker(markerIdKey);
if (marker !== null) {
trackdirect.services.callbackExecutor.addWithPriority(
marker,
marker.showCompleteMarker,
[]
);
}
}
}
}
// Also make sure all stations with a visible coverage is shown
var stationIdList =
this.markerCollection.getStationIdListWithVisibleCoverage();
for (var i = 0; i < stationIdList.length; i++) {
var latestMarker = this.markerCollection.getStationLatestMarker(
stationIdList[i]
);
if (latestMarker !== null) {
if (latestMarker.shouldMarkerBeVisible() && latestMarker.showAsMarker) {
latestMarker.show();
}
}
}
}
};
/**
* Set map initial position
*/
trackdirect.models.Map.prototype._setMapInitialLocation = function () {
var zoom = this._getInitialZoom();
if (
typeof this._tdMapOptions.initCenter !== "undefined" &&
this._tdMapOptions.initCenter !== null
) {
var pos = this._tdMapOptions.initCenter;
this.setCenter(pos, zoom);
} else {
this.setMapDefaultLocation();
this.setZoom(zoom);
}
this._emitTdEventListeners("change");
};
/**
* Set map default location
*/
trackdirect.models.Map.prototype.setMapDefaultLocation = function (
setDefaultZoom
) {
setDefaultZoom =
typeof setDefaultZoom !== "undefined" ? setDefaultZoom : false;
var defaultLatitude =
typeof this._tdMapOptions.defaultLatitude !== "undefined"
? this._tdMapOptions.defaultLatitude
: 59.35;
var defaultLongitude =
typeof this._tdMapOptions.defaultLongitude !== "undefined"
? this._tdMapOptions.defaultLongitude
: 18.05;
var pos = {
lat: parseFloat(defaultLatitude),
lng: parseFloat(defaultLongitude),
};
if (setDefaultZoom) {
if (trackdirect.isMobile) {
this.setCenter(pos, trackdirect.settings.defaultCurrentZoomMobile);
} else {
this.setCenter(pos, trackdirect.settings.defaultCurrentZoom);
}
} else {
this.setCenter(pos);
}
};
/**
* Add marker to multiple Map-Sectors, useful for very long polylines related to a marker
* @param {int} markerIdKey
* @param {LatLngLiteral} startLatLng
* @param {LatLngLiteral} endLatLng
*/
trackdirect.models.Map.prototype.addMarkerToMapSectorInterval = function (
markerIdKey,
startLatLng,
endLatLng
) {
var minLat = startLatLng.lat;
var maxLat = endLatLng.lat;
var minLng = startLatLng.lng;
var maxLng = endLatLng.lng;
if (endLatLng.lat < minLat) {
minLat = endLatLng.lat;
maxLat = startLatLng.lat;
}
if (endLatLng.lng < minLng) {
minLng = endLatLng.lng;
maxLng = startLatLng.lng;
}
for (var lat = Math.floor(minLat); lat <= Math.ceil(maxLat); lat++) {
for (var lng = Math.floor(minLng); lng <= Math.ceil(maxLng); lng++) {
var markerMapSector =
trackdirect.services.MapSectorCalculator.getMapSector(lat, lng);
this.markerCollection.addMarkerToMapSector(markerIdKey, markerMapSector);
if (this.isMapSectorVisible(markerMapSector)) {
if (this._newMarkersToShow.indexOf(markerIdKey) < 0) {
this._newMarkersToShow.push(markerIdKey);
}
}
}
}
};
/**
* Add marker the related map sector based in latest packet
* @param {int} markerIdKey
* @param {object} packet
* @param {boolean} tryToShowPacket
*/
trackdirect.models.Map.prototype.addMarkerToMapSectors = function (
markerIdKey,
packet,
tryToShowPacket
) {
var markerMapSectors = [];
markerMapSectors.push(packet.map_sector);
if (
typeof packet.related_map_sectors !== "undefined" &&
packet.related_map_sectors !== null
) {
for (var i = 0; i < packet.related_map_sectors.length; i++) {
markerMapSectors.push(packet.related_map_sectors[i]);
}
}
for (var i = 0; i < markerMapSectors.length; i++) {
var markerMapSector = markerMapSectors[i];
this.markerCollection.addMarkerToMapSector(markerIdKey, markerMapSector);
if (tryToShowPacket) {
if (this.isMapSectorVisible(markerMapSector)) {
if (this._newMarkersToShow.indexOf(markerIdKey) < 0) {
this._newMarkersToShow.push(markerIdKey);
}
}
}
}
};
/**
* Initialize map basic events
*/
trackdirect.models.Map.prototype._initMapEvents = function () {
if (typeof google === "object" && typeof google.maps === "object") {
this._initGoogleMapEvents();
} else if (typeof L === "object") {
this._initLeafletMapEvents();
}
};
/**
* Initialize google map basic events
*/
trackdirect.models.Map.prototype._initGoogleMapEvents = function () {
var me = this;
google.maps.event.addListener(this, "mousemove", function (event) {
me._renderCordinatesContainer(event.latLng);
});
google.maps.event.addListener(this, "idle", function () {
me._updateMapContent();
});
google.maps.event.addListener(this, "maptypeid_changed", function () {
me._updateMapContent();
});
google.maps.event.addListener(this, "bounds_changed", function () {
if (me.isMapReady()) {
me._emitTdEventListeners("moving");
}
});
};
/**
* Initialize leaflet map basic events
*/
trackdirect.models.Map.prototype._initLeafletMapEvents = function () {
var me = this;
this.on("mousemove", function (event) {
me._renderCordinatesContainer(event.latlng);
});
this.on("moveend", function () {
me._updateMapContent();
});
/*
this.on('maptypeid_changed', function() {
me._updateMapContent();
});
*/
this.on("move", function () {
if (me.isMapReady()) {
me._emitTdEventListeners("moving");
}
});
};
/**
* Initialize OMS
*/
trackdirect.models.Map.prototype._initOms = function () {
var options = {};
options["nearbyDistance"] = 12;
if (typeof google === "object" && typeof google.maps === "object") {
var mti = google.maps.MapTypeId;
this.oms = new OverlappingMarkerSpiderfier(this, options);
this.oms.legColors.usual[mti.HYBRID] = this.oms.legColors.usual[
mti.SATELLITE
] = "#fff";
this.oms.legColors.usual[mti.TERRAIN] = this.oms.legColors.usual[
mti.ROADMAP
] = "#222";
this.oms.legColors.highlighted[mti.HYBRID] = this.oms.legColors.highlighted[
mti.SATELLITE
] = "#f00";
this.oms.legColors.highlighted[mti.TERRAIN] =
this.oms.legColors.highlighted[mti.ROADMAP] = "#f00";
} else if (typeof L === "object") {
this.oms = new OverlappingMarkerSpiderfier(this, options);
}
};
/**
* Initialize info window for markers
*/
trackdirect.models.Map.prototype._initInfoWindowEvent = function () {
var me = this;
if (this.oms) {
this.oms.addListener("click", function (marker, event) {
me.openMarkerInfoWindow(marker, false);
});
} else {
}
};
/**
* Get default google "map options", used for initializing google map
* @return {object}
*/
trackdirect.models.Map.prototype._getGoolgeMapOptions = function () {
var zoom = this._getInitialZoom();
var mapOptions = {
zoom: zoom,
panControl: false,
zoomControl: true,
zoomControlOptions: {
position: google.maps.ControlPosition.RIGHT_BOTTOM,
},
mapTypeControl: false,
scaleControl: false,
streetViewControl: false,
overviewMapControl: false,
fullscreenControl: false,
mapTypeId: google.maps.MapTypeId.ROADMAP,
gestureHandling: "greedy",
};
mapOptions.mapTypeId = this._supportedMapTypes[this._mapType];
return mapOptions;
};
/**
* Get default leaflet "map options", used for initializing google map
* @return {object}
*/
trackdirect.models.Map.prototype._getLeafletMapOptions = function () {
var zoom = this._getInitialZoom();
var mapOptions = {
zoom: zoom,
zoomControl: true,
attributionControl: true,
zoomControl: false,
minZoom: 3,
maxZoom: 16,
closePopupOnClick: false,
};
return mapOptions;
};
/**
* Get initial zoom
* @return {int}
*/
trackdirect.models.Map.prototype._getInitialZoom = function () {
var zoom = trackdirect.settings.defaultCurrentZoom;
if (
typeof this._tdMapOptions.zoom !== "undefined" &&
this._tdMapOptions.zoom !== null
) {
zoom = parseInt(this._tdMapOptions.zoom);
} else if (trackdirect.isMobile) {
zoom = trackdirect.settings.defaultCurrentZoomMobile;
}
return zoom;
};
/**
* Get radius in Km (radius is from center of current map view to the most north east position)
* @return {int}
*/
trackdirect.models.Map.prototype.getCurrentRadiusInKm = function () {
if (this.getBounds() != null) {
if (typeof google === "object" && typeof google.maps === "object") {
var latLng = this.getBounds().getNorthEast();
var latLngLiteral = { lat: latLng.lat(), lng: latLng.lng() };
} else if (typeof L === "object") {
var latLng = this.getBounds().getNorthEast();
var latLngLiteral = { lat: latLng.lat, lng: latLng.lng };
}
return (
trackdirect.services.distanceCalculator.getDistance(
this.getCenterLiteral(),
latLngLiteral
) / 1000
);
}
return 0;
};
/**
* Print position in the cordinates container
* @return {google.maps.LatLng/L.LatLng/LatLngLiteral} mouseLatLng
*/
trackdirect.models.Map.prototype._renderCordinatesContainer = function (
mouseLatLng
) {
var options = this.getTdMapOptions();
if (typeof options.cordinatesContainer === "undefined") {
return;
}
if (options.cordinatesContainer == null) {
return;
}
var lat = null;
var lng = null;
if (typeof mouseLatLng.lat == "function") {
lat = mouseLatLng.lat();
lng = mouseLatLng.lng();
} else {
lat = mouseLatLng.lat;
lng = mouseLatLng.lng;
}
if (lat <= 90 && lat >= -90 && lng <= 180 && lng >= -180) {
var content = "";
content += this._getGpsDegreeFromGpsDecimal(lat.toFixed(5), "lat");
content += " " + this._getGpsDegreeFromGpsDecimal(lng.toFixed(5), "lon");
content += "<br>" + lat.toFixed(5) + ", " + lng.toFixed(5);
content += "<br>" + this._getMaidenheadLocatorFromGpsDecimal(lat, lng);
$("#" + options.cordinatesContainer).html(content);
}
};
/**
* Convert decimal gps position to degree format
* @param {float} dms
* @param {string} type
* @return {string}
*/
trackdirect.models.Map.prototype._getGpsDegreeFromGpsDecimal = function (
dms,
type
) {
var sign = 1,
Abs = 0;
var days, minutes, secounds, direction;
if (dms < 0) {
sign = -1;
}
Abs = Math.abs(Math.round(dms * 1000000));
//Math.round is used to eliminate the small error caused by rounding in the computer:
//e.g. 0.2 is not the same as 0.20000000000284
//Error checks
if (type == "lat" && Abs > 90 * 1000000) {
//alert(" Degrees Latitude must be in the range of -90. to 90. ");
return false;
} else if (type == "lon" && Abs > 180 * 1000000) {
//alert(" Degrees Longitude must be in the range of -180 to 180. ");
return false;
}
days = Math.floor(Abs / 1000000);
minutes = Math.floor((Abs / 1000000 - days) * 60);
secounds = (
(Math.floor(((Abs / 1000000 - days) * 60 - minutes) * 100000) * 60) /
100000
).toFixed();
days = days * sign;
if (type == "lat") direction = days < 0 ? "S" : "N";
if (type == "lon") direction = days < 0 ? "W" : "E";
//else return value
return days * sign + "º " + minutes + "' " + secounds + "'' " + direction;
};
/**
* Convert decimal gps position to maidenhead locator
* @param {float} lat
* @param {float} lng
* @return {string}
*/
trackdirect.models.Map.prototype._getMaidenheadLocatorFromGpsDecimal = function (
lat,
lng,
) {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVX';
var result = '';
lng = lng + 180;
lat = lat + 90;
result = chars.charAt(parseInt(lng / 20));
result += chars.charAt(parseInt(lat / 10));
result += parseInt(lng / 2 % 10);
result += parseInt(lat % 10);
lng_r = (lng - parseInt(lng/2)*2) * 60;
lat_r = (lat - parseInt(lat)) * 60;
result += chars.charAt(parseInt(lng_r/5));
result += chars.charAt(parseInt(lat_r/2.5));
return result;
};