reintroduced support for coverage maps

This commit is contained in:
Per Qvarforth 2022-01-30 13:33:56 +01:00
parent 93f124949a
commit 9658ebdb48
12 changed files with 936 additions and 14 deletions

View File

@ -95,4 +95,36 @@ class PacketPathRepository extends ModelRepository
$stmt = $pdo->prepareAndExec($sql, $args);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
/**
* Get latest data list by receiving station id
*
* @param int $stationId
* @param int $hours
* @param int $limit
* @return array
*/
public function getLatestDataListByReceivingStationId($stationId, $hours, $limit)
{
if (!isInt($stationId) || !isInt($hours)) {
return [];
}
$minTimestamp = time() - (60*60*$hours);
$sql = 'select pp.*
from packet_path pp
where pp.station_id = ?
and pp.timestamp >= ?
and pp.number = 0
and pp.sending_latitude is not null
and pp.sending_longitude is not null
order by pp.timestamp
limit ?';
$arg = [$stationId, $minTimestamp, $limit];
$pdo = PDOConnection::getInstance();
$stmt = $pdo->prepareAndExec($sql, $arg);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
}

View File

@ -0,0 +1,24 @@
<?php
require "../../includes/bootstrap.php";
$response = [];
$station = StationRepository::getInstance()->getObjectById($_GET['id'] ?? null);
if ($station->isExistingObject()) {
$response['station_id'] = $station->id;
$response['coverage'] = [];
$numberOfHours = 10*24; // latest 10 days should be enough
$limit = 10000; // Limit number of packets to reduce load on server (and browser)
$packetPaths = PacketPathRepository::getInstance()->getLatestDataListByReceivingStationId($_GET['id'] ?? null, $numberOfHours, $limit);
foreach ($packetPaths as $path) {
$row = [];
$row['latitude'] = $path['sending_latitude'];
$row['longitude'] = $path['sending_longitude'];
$row['distance'] = $path['distance'];
$response['coverage'][] = $row;
}
}
header('Content-type: application/json');
echo json_encode($response);

View File

@ -20,6 +20,7 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js" integrity="sha512-jGsMH83oKe9asCpkOVkBnUrDDTp8wl+adkB2D+//JtlxO4SrLoJdhbOysIFQJloQFD+C4Fl1rMsQZF76JjV0eQ==" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment-with-locales.min.js" integrity="sha512-LGXaggshOkD/at6PFNcp2V2unf9LzFq6LE+sChH7ceMTDP0g2kn6Vxwgg7wkPP7AAtX+lmPqPdxB47A0Nz0cMQ==" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/autolinker/3.14.2/Autolinker.min.js" integrity="sha512-qyoXjTIJ69k6Ik7CxNVKFAsAibo8vW/s3WV3mBzvXz6Gq0yGup/UsdZBDqFwkRuevQaF2g7qhD3E4Fs+OwS4hw==" crossorigin="anonymous"></script>
<script src="/js/convex-hull.js" crossorigin="anonymous"></script>
<!-- Map api javascripts and related dependencies -->
<?php $mapapi = $_GET['mapapi'] ?? 'leaflet'; ?>
@ -56,6 +57,7 @@
var options = {};
options['isMobile'] = false;
options['useImperialUnit'] = <?php echo (isImperialUnitUser() ? 'true': 'false'); ?>;
options['coverageDataUrl'] = 'data/coverage.php';;
var md = new MobileDetect(window.navigator.userAgent);
if (md.mobile() !== null) {

View File

@ -0,0 +1,87 @@
/*
* Convex hull algorithm - Library (compiled from TypeScript)
*
* Copyright (c) 2021 Project Nayuki
* https://www.nayuki.io/page/convex-hull-algorithm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program (see COPYING.txt and COPYING.LESSER.txt).
* If not, see <http://www.gnu.org/licenses/>.
*/
"use strict";
var convexhull;
(function (convexhull) {
// Returns a new array of points representing the convex hull of
// the given set of points. The convex hull excludes collinear points.
// This algorithm runs in O(n log n) time.
function makeHull(points) {
var newPoints = points.slice();
newPoints.sort(convexhull.POINT_COMPARATOR);
return convexhull.makeHullPresorted(newPoints);
}
convexhull.makeHull = makeHull;
// Returns the convex hull, assuming that each points[i] <= points[i + 1]. Runs in O(n) time.
function makeHullPresorted(points) {
if (points.length <= 1)
return points.slice();
// Andrew's monotone chain algorithm. Positive y coordinates correspond to "up"
// as per the mathematical convention, instead of "down" as per the computer
// graphics convention. This doesn't affect the correctness of the result.
var upperHull = [];
for (var i = 0; i < points.length; i++) {
var p = points[i];
while (upperHull.length >= 2) {
var q = upperHull[upperHull.length - 1];
var r = upperHull[upperHull.length - 2];
if ((q.x - r.x) * (p.y - r.y) >= (q.y - r.y) * (p.x - r.x))
upperHull.pop();
else
break;
}
upperHull.push(p);
}
upperHull.pop();
var lowerHull = [];
for (var i = points.length - 1; i >= 0; i--) {
var p = points[i];
while (lowerHull.length >= 2) {
var q = lowerHull[lowerHull.length - 1];
var r = lowerHull[lowerHull.length - 2];
if ((q.x - r.x) * (p.y - r.y) >= (q.y - r.y) * (p.x - r.x))
lowerHull.pop();
else
break;
}
lowerHull.push(p);
}
lowerHull.pop();
if (upperHull.length == 1 && lowerHull.length == 1 && upperHull[0].x == lowerHull[0].x && upperHull[0].y == lowerHull[0].y)
return upperHull;
else
return upperHull.concat(lowerHull);
}
convexhull.makeHullPresorted = makeHullPresorted;
function POINT_COMPARATOR(a, b) {
if (a.x < b.x)
return -1;
else if (a.x > b.x)
return +1;
else if (a.y < b.y)
return -1;
else if (a.y > b.y)
return +1;
else
return 0;
}
convexhull.POINT_COMPARATOR = POINT_COMPARATOR;
})(convexhull || (convexhull = {}));

View File

@ -1,4 +1,4 @@
var trackdirect={services:{},models:{},_time:null,_timetravel:null,_center:null,_zoom:null,_maptype:null,_mid:null,_rulers:[],_filterTimeoutId:null,_waitForFilterResponse:false,_doNotChangeLocationOnFilterResponse:false,_doNotChangeLocationOnFilterResponseTmp:false,_filters:{},_defaultLatitude:null,_defaultLongitude:null,_eventListeners:{},_eventListenersOnce:{},_cordinatesContainerElementId:null,_statusContainerElementId:"td-status-text",_mapElementId:null,_wsServerUrl:null,_map:null,_websocket:null,_mapCreated:false,_trackdirectInitDone:false,isMobile:false,settings:{},init:function(wsServerUrl,mapElementId,options){this._initSettings();this._wsServerUrl=wsServerUrl;this._mapElementId=mapElementId;if($("#"+mapElementId).length<=0){console.log("ERROR: Specified map element missing");return;}
var trackdirect={services:{},models:{},_time:null,_timetravel:null,_center:null,_zoom:null,_maptype:null,_mid:null,_rulers:[],_filterTimeoutId:null,_waitForFilterResponse:false,_doNotChangeLocationOnFilterResponse:false,_doNotChangeLocationOnFilterResponseTmp:false,_filters:{},_defaultLatitude:null,_defaultLongitude:null,_eventListeners:{},_eventListenersOnce:{},_cordinatesContainerElementId:null,_statusContainerElementId:"td-status-text",_mapElementId:null,_wsServerUrl:null,_map:null,_websocket:null,_mapCreated:false,_trackdirectInitDone:false,isMobile:false,coverageDataUrl:null,settings:{},init:function(wsServerUrl,mapElementId,options){this._initSettings();this._wsServerUrl=wsServerUrl;this._mapElementId=mapElementId;if($("#"+mapElementId).length<=0){console.log("ERROR: Specified map element missing");return;}
if(typeof google==="object"&&typeof google.maps==="object"){this.settings.defaultMinZoomForMarkerLabel=12;this.settings.minZoomForMarkerLabel=12;}
this._parseOptions(options);var me=this;this.addListener("map-created",function(){me._initTime();me._websocket=new trackdirect.Websocket(me._wsServerUrl);me._initWebsocketListeners();me._handleWebsocketStateChange();me._initMapListeners();if(!me._initFilterUrlRequest()){trackdirect.services.callbackExecutor.add(me,me._sendPositionRequest,[]);}
me._setWebsocketStateIdle();if(inIframe()){var parentUrl="";try{parentUrl=window.location!=window.parent.location?document.referrer:document.location.href;}catch(e){parentUrl="Unknown";}}
@ -15,7 +15,8 @@ if(stationId!==null){var trackLinkElementClass="trackStationLink"+stationId;$(".
if(this._map.state.trackStationId!==null){var trackLinkElementClass="trackStationLink"+this._map.state.trackStationId;$("."+trackLinkElementClass).html("Track");}
this._map.state.onlyTrackRecentPackets=onlyTrackRecentPackets;this._map.state.trackStationId=stationId;this._emitEventListeners("track-changed",[stationId,stationName]);},focusOnStation:function(stationId,openInfoWindow){var map=this._map;openInfoWindow=typeof openInfoWindow!=="undefined"?openInfoWindow:false;var marker=map.markerCollection.getStationLatestMarker(stationId);if(marker!==null){marker.show();marker.showLabel();if(openInfoWindow){map.openMarkerInfoWindow(marker,false);}else{this.setCenter(marker.packet.latitude,marker.packet.longitude);}
marker.hide(5000,true);}},focusOnMarkerId:function(markerId,zoom){var map=this._map;var markerIdKey=map.markerCollection.getMarkerIdKey(markerId);if(map.markerCollection.isExistingMarker(markerIdKey)){var marker=map.markerCollection.getMarker(markerIdKey);if(map.markerCollection.hasRelatedDashedPolyline(marker)){newerMarker=map.markerCollection.getMarker(marker._relatedMarkerOriginDashedPolyLine.ownerMarkerIdKey);if(newerMarker.packet.hasConfirmedMapId()){return this.focusOnMarkerId(newerMarker.packet.marker_id);}}
marker.show();marker.showLabel();this.setCenter(marker.packet.latitude,marker.packet.longitude,zoom);map.openMarkerInfoWindow(marker);marker.hide(5000,true);}},setMapType:function(mapType){if(this._map!==null){this._map.setMapType(mapType);}},getMapType:function(){if(this._map!==null){return this._map.getMapType();}},setMapDefaultLocation:function(setDefaultZoom){this._map.setMapDefaultLocation(setDefaultZoom);},setMapLocationByGeoLocation:function(failCallBack,successCallBack,timeout){var me=this;if(navigator&&navigator.geolocation){navigator.geolocation.getCurrentPosition(function(position){var pos={lat:position.coords.latitude,lng:position.coords.longitude,};me._map.setCenter(pos,12);if(successCallBack!==null){successCallBack();}},function(error){if(failCallBack!==null){failCallBack(error.message);}},{enableHighAccuracy:false,timeout:timeout,maximumAge:5000,});}else{if(failCallBack!==null){failCallBack();}}},openStationInformationDialog:function(stationId){var packet=this._map.markerCollection.getStationLatestPacket(stationId);if(packet==null){packet={station_id:stationId,id:null};}
marker.show();marker.showLabel();this.setCenter(marker.packet.latitude,marker.packet.longitude,zoom);map.openMarkerInfoWindow(marker);marker.hide(5000,true);}},toggleStationCoverage:function(stationId,coverageLinkElementClass){coverageLinkElementClass=typeof coverageLinkElementClass!=="undefined"?coverageLinkElementClass:null;var coveragePolygon=this._map.markerCollection.getStationCoverage(stationId);if(coveragePolygon!==null&&coveragePolygon.isRequestedToBeVisible()){coveragePolygon.hide();if(coverageLinkElementClass!==null){$("."+coverageLinkElementClass).html("Coverage");}}else{if(coveragePolygon!==null){coveragePolygon.show();if(!coveragePolygon.hasContent()){alert("Currently we do not have enough data to create a max range coverage plot for this station. Try again later!");}else{if(coverageLinkElementClass!==null){$("."+coverageLinkElementClass).html("Hide coverage");}}}else{var packet=this._map.markerCollection.getStationLatestPacket(stationId);var center={lat:parseFloat(packet.latitude),lng:parseFloat(packet.longitude),};var coveragePolygon=new trackdirect.models.StationCoveragePolygon(center,this._map,true);this._map.markerCollection.addStationCoverage(stationId,coveragePolygon);coveragePolygon.showWhenDone();if(coverageLinkElementClass!==null){$("."+coverageLinkElementClass).html('Loading <i class="fa fa-spinner fa-spin" style="font-size:12px"></i>');coveragePolygon.addTdListener("visible",function(){if(!coveragePolygon.hasContent()){coveragePolygon.hide();alert("Currently we do not have enough data to create a max range coverage plot for this station. Try again later!");$("."+coverageLinkElementClass).html("Coverage");}else{$("."+coverageLinkElementClass).html("Hide coverage");}},true);}
var me=this;$.getJSON(this.coverageDataUrl+"?id="+stationId,function(data){if("station_id"in data&&"coverage"in data){coveragePolygon.setData(data["coverage"]);var marker=me._map.markerCollection.getStationLatestMarker(stationId);if(marker.isVisible()){if(coveragePolygon.isRequestedToBeVisible()){coveragePolygon.show();}}}}).fail(function(){coveragePolygon.hide();alert("Failed to fetch coverage data. Try again later!");$("."+coverageLinkElementClass).html("Coverage");}).always(function(){});}}},setMapType:function(mapType){if(this._map!==null){this._map.setMapType(mapType);}},getMapType:function(){if(this._map!==null){return this._map.getMapType();}},setMapDefaultLocation:function(setDefaultZoom){this._map.setMapDefaultLocation(setDefaultZoom);},setMapLocationByGeoLocation:function(failCallBack,successCallBack,timeout){var me=this;if(navigator&&navigator.geolocation){navigator.geolocation.getCurrentPosition(function(position){var pos={lat:position.coords.latitude,lng:position.coords.longitude,};me._map.setCenter(pos,12);if(successCallBack!==null){successCallBack();}},function(error){if(failCallBack!==null){failCallBack(error.message);}},{enableHighAccuracy:false,timeout:timeout,maximumAge:5000,});}else{if(failCallBack!==null){failCallBack();}}},openStationInformationDialog:function(stationId){var packet=this._map.markerCollection.getStationLatestPacket(stationId);if(packet==null){packet={station_id:stationId,id:null};}
this._emitEventListeners("station-name-clicked",packet);},openMarkerInfoWindow:function(markerId){var markerIdKey=this._map.markerCollection.getMarkerIdKey(markerId);if(this._map.markerCollection.isExistingMarker(markerIdKey)){var marker=this._map.markerCollection.getMarker(markerIdKey);this._map.openMarkerInfoWindow(marker);}},closeAnyOpenInfoWindow:function(){if(this._map!==null){var state=this._map.state;if(state.isInfoWindowOpen()){state.openInfoWindow.hide();}}},setTimeTravelTimestamp:function(ts,sendRequestToServer){if(ts!=0||this._map.state.endTimeTravelTimestamp!=null){sendRequestToServer=typeof sendRequestToServer!=="undefined"?sendRequestToServer:true;if(this._map.state.endTimeTravelTimestamp!=ts){if(ts!=null&&ts!=0&&ts!=""){this._map.state.endTimeTravelTimestamp=ts;}else{this._map.state.endTimeTravelTimestamp=null;}
if(sendRequestToServer){trackdirect.services.callbackExecutor.add(this,this._handleTimeChange,[]);}}
this._emitEventListeners("time-travel-changed",ts);this._emitEventListeners("mode-changed");}},getTimeTravelTimestamp:function(){return this._map.state.endTimeTravelTimestamp;},setTimeLength:function(time,sendRequestToServer){sendRequestToServer=typeof sendRequestToServer!=="undefined"?sendRequestToServer:true;if(this._map.state.getTimeLength()/60!=time){this._map.state.setTimeLength(time*60);if(sendRequestToServer){trackdirect.services.callbackExecutor.add(this,this._handleTimeChange,[]);}}
@ -35,6 +36,7 @@ var packet=this._map.markerCollection.getStationLatestPacket(stationId);if(packe
this.trackStation(stationId,stationName,true);}}},_initSettings:function(){this.settings={animate:true,defaultMinZoomForMarkerLabel:11,defaultMinZoomForMarkerPrevPosition:11,defaultMinZoomForMarkerTail:9,minZoomForMarkerLabel:11,minZoomForMarkerPrevPosition:11,minZoomForMarkerTail:9,minZoomForMarkers:8,markerSymbolBaseDir:"/symbols/",imagesBaseDir:"/images/",defaultCurrentZoom:11,defaultCurrentZoomMobile:11,dateFormat:"L LTSZ",dateFormatNoTimeZone:"L LTS",host:"www.aprsdirect.com",baseUrl:"https://www.aprsdirect.com",defaultTimeLength:60,symbolsToScale:[],primarySymbolWithNoDirectionPolyline:[87,64,95],alternativeSymbolWithNoDirectionPolyline:[40,42,64,74,84,85,96,98,101,102,112,116,119,121,123,],};},_parseOptions:function(options){if(typeof options["cordinatesContainerElementId"]!==undefined){this._cordinatesContainerElementId=options["cordinatesContainerElementId"];}
if(typeof options["statusContainerElementId"]!==undefined){this._statusContainerElementId=options["statusContainerElementId"];}
if(typeof options["isMobile"]!==undefined){this.isMobile=options["isMobile"];}
if(typeof options["coverageDataUrl"]!==undefined){this.coverageDataUrl=options["coverageDataUrl"];}
if(typeof options["time"]!==undefined){this._time=options["time"];}
if(typeof options["timetravel"]!==undefined){this._timetravel=options["timetravel"];}
if(typeof options["center"]!==undefined){this._center=options["center"];}
@ -257,6 +259,36 @@ trackdirect.models.TailPolyline.prototype.constructor=trackdirect.models.TailPol
return null;};trackdirect.models.TailPolyline.prototype.pushPathItem=function(latLng){if(typeof google==="object"&&typeof google.maps==="object"){var path=google.maps.Polyline.prototype.getPath.call(this);path.push(latLng);}else if(typeof L==="object"){this.addLatLng(latLng);}};trackdirect.models.TailPolyline.prototype.removePathItem=function(index){if(typeof google==="object"&&typeof google.maps==="object"){var path=google.maps.Polyline.prototype.getPath.call(this);path.removeAt(index);}else if(typeof L==="object"){var list=this.getLatLngs();if(typeof list[index]!=="undefined"){list.splice(index,1);this.setLatLngs(list);}}};trackdirect.models.TailPolyline.prototype.getPathLength=function(index){if(typeof google==="object"&&typeof google.maps==="object"){var path=google.maps.Polyline.prototype.getPath.call(this);return path.getLength();}else if(typeof L==="object"){var list=this.getLatLngs();return list.length;}};trackdirect.models.TailPolyline.prototype.getPath=function(){if(typeof google==="object"&&typeof google.maps==="object"){return google.maps.Polyline.prototype.getPath.call(this);}else if(typeof L==="object"){return this.getLatLngs();}
return[];};trackdirect.models.TailPolyline.prototype.getMap=function(){if(typeof google==="object"&&typeof google.maps==="object"){var map=google.maps.Polyline.prototype.getMap.call(this);if(typeof map!=="undefined"){return map;}}else if(typeof L==="object"){if(this._defaultMap.hasLayer(this)){return this._defaultMap;}}
return null;};trackdirect.models.TailPolyline.prototype.setMarkerIdKey=function(markerIdKey){this.markerIdKey=markerIdKey;this.ownerMarkerIdKey=markerIdKey;this._addInfoWindowListener(markerIdKey);};trackdirect.models.TailPolyline.prototype.setRelatedMarkerIdKey=function(markerIdKey){this.relatedMarkerIdKey=markerIdKey;};trackdirect.models.TailPolyline.prototype.show=function(){if(typeof google==="object"&&typeof google.maps==="object"){if(typeof this.getMap()==="undefined"||this.getMap()===null){this.setMap(this._defaultMap);}}else if(typeof L==="object"){if(!this._defaultMap.hasLayer(this)){this.addTo(this._defaultMap);}}};trackdirect.models.TailPolyline.prototype.hide=function(){if(typeof google==="object"&&typeof google.maps==="object"){if(this.getMap()!==null){this.setMap(null);}}else if(typeof L==="object"){if(this._defaultMap.hasLayer(this)){this._defaultMap.removeLayer(this);}}};trackdirect.models.TailPolyline.prototype.addMarker=function(marker){if(typeof google==="object"&&typeof google.maps==="object"){var latLng=new google.maps.LatLng(parseFloat(marker.packet.latitude),parseFloat(marker.packet.longitude));latLng.marker=marker;this.pushPathItem(latLng);}else if(typeof L==="object"){var latLng=new L.latLng(parseFloat(marker.packet.latitude),parseFloat(marker.packet.longitude));latLng.marker=marker;this.addLatLng(latLng);}};trackdirect.models.TailPolyline.prototype._addInfoWindowListener=function(markerIdKey){var me=this;if(typeof google==="object"&&typeof google.maps==="object"){google.maps.event.addListener(this,"click",function(event){var marker=me._defaultMap.markerCollection.getMarker(markerIdKey);me._defaultMap.openPolylineInfoWindow(marker,event.latLng);});}else if(typeof L==="object"){this.on("click",function(event){var marker=me._defaultMap.markerCollection.getMarker(markerIdKey);me._defaultMap.openPolylineInfoWindow(marker,event.latlng);});}};trackdirect.models.TailPolyline.prototype._getGooglePolylineOptions=function(color){return{geodesic:false,strokeOpacity:0.6,strokeWeight:4,strokeColor:color,map:null,zIndex:100,};};trackdirect.models.TailPolyline.prototype._getLeafletPolylineOptions=function(color){return{opacity:0.7,weight:4,color:color,};};
trackdirect.models.StationCoveragePolygon=function(center,map,tryToShowCoveragePolygon){tryToShowCoveragePolygon=typeof tryToShowCoveragePolygon!=="undefined"?tryToShowCoveragePolygon:true;this._showPolygon=tryToShowCoveragePolygon;this._map=map;this._center=center;this._isRequestedToBeVisible=false;this._polygon=null;this._polygonCoordinates=null;this._heatmapCoordinates=null;this._heatmap=null;this._tdEventListeners={};this._tdEventListenersOnce={};this._upperMaxRangeInMeters=1000*1000;this._percentile=95;this._paddingInPercentOfMaxRange=10;this._paddingMinInMeters=1000;};trackdirect.models.StationCoveragePolygon.prototype.setData=function(data){this._addParametersToData(data);this._heatmapCoordinates=this._getCoordinates(data);if(this._showPolygon){var maxRange=this._getCoveragePolygonMaxRange(data);if(maxRange<=0){this._showPolygon=false;}else{this._polygonCoordinates=this._getConvexHullCoordinates(data,maxRange);}}
if(typeof google==="object"&&typeof google.maps==="object"){this._googleMapsInit();}else if(typeof L==="object"){this._leafletInit();}};trackdirect.models.StationCoveragePolygon.prototype.addTdListener=function(event,handler,execOnce){execOnce=typeof execOnce!=="undefined"?execOnce:false;if(execOnce){if(!(event in this._tdEventListenersOnce)){this._tdEventListenersOnce[event]=[];}
this._tdEventListenersOnce[event].push(handler);}else{if(!(event in this._tdEventListeners)){this._tdEventListeners[event]=[];}
this._tdEventListeners[event].push(handler);}};trackdirect.models.StationCoveragePolygon.prototype.hasContent=function(){if(this._heatmapCoordinates!==null&&this._heatmapCoordinates.length>0){return true;}
return false;};trackdirect.models.StationCoveragePolygon.prototype.isRequestedToBeVisible=function(){return this._isRequestedToBeVisible;};trackdirect.models.StationCoveragePolygon.prototype.showWhenDone=function(){this._isRequestedToBeVisible=true;};trackdirect.models.StationCoveragePolygon.prototype.show=function(){if(typeof google==="object"&&typeof google.maps==="object"){if(this._polygon!==null){this._polygon.setMap(this._map);}
if(this._heatmap!==null){this._heatmap.setMap(this._map);}}else if(typeof L==="object"){if(this._polygon!==null){this._polygon.addTo(this._map);}
if(this._heatmap!==null){this._heatmap.addTo(this._map);}}
this._isRequestedToBeVisible=true;if(this._showPolygon&&this._polygonCoordinates!==null){this._emitTdEventListeners("visible");}else if(this._heatmapCoordinates!==null){this._emitTdEventListeners("visible");}};trackdirect.models.StationCoveragePolygon.prototype.hide=function(stillMarkAsVisible){stillMarkAsVisible=typeof stillMarkAsVisible!=="undefined"?stillMarkAsVisible:false;if(typeof google==="object"&&typeof google.maps==="object"){if(this._polygon!==null){this._polygon.setMap(null);}
if(this._heatmap!==null){this._heatmap.setMap(null);}}else if(typeof L==="object"){if(this._polygon!==null){this._map.removeLayer(this._polygon);}
if(this._heatmap!==null){this._map.removeLayer(this._heatmap);}}
if(!stillMarkAsVisible){this._isRequestedToBeVisible=false;}
this._emitTdEventListeners("hidden");};trackdirect.models.StationCoveragePolygon.prototype._googleMapsInit=function(){if(this._polygonCoordinates!==null&&this._polygonCoordinates.length>0){this._polygon=new google.maps.Polygon({paths:this._polygonCoordinates,strokeColor:"#0000FF",strokeOpacity:0,strokeWeight:0,fillColor:"#0000FF",fillOpacity:0.2,});}
if(this._heatmapCoordinates!==null&&this._heatmapCoordinates.length>0){var data=[];for(var i=0;i<this._heatmapCoordinates.length;i++){data.push({location:this._heatmapCoordinates[i],weight:1});}
this._heatmap=new google.maps.visualization.HeatmapLayer({data:data,radius:8,maxIntensity:5,gradient:["rgba(0, 255, 255, 0)","rgba(0, 255, 255, 1)","rgba(0, 191, 255, 1)","rgba(0, 127, 255, 1)","rgba(0, 63, 255, 1)","rgba(0, 0, 255, 1)","rgba(0, 0, 223, 1)","rgba(0, 0, 191, 1)","rgba(0, 0, 159, 1)","rgba(0, 0, 127, 1)","rgba(63, 0, 91, 1)","rgba(127, 0, 63, 1)","rgba(191, 0, 31, 1)","rgba(255, 0, 0, 1)",],map:null,});}};trackdirect.models.StationCoveragePolygon.prototype._leafletInit=function(){if(this._polygonCoordinates!==null&&this._polygonCoordinates.length>0){this._polygon=new L.polygon(this._polygonCoordinates,{color:"#0000FF",opacity:0,weight:0,fillColor:"#0000FF",fillOpacity:0.2,});}
if(this._heatmapCoordinates!==null&&this._heatmapCoordinates.length>0){var data=[];for(var i=0;i<this._heatmapCoordinates.length;i++){data.push([this._heatmapCoordinates[i].lat,this._heatmapCoordinates[i].lng,10,]);}
this._heatmap=L.heatLayer(this._heatmapCoordinates,{minOpacity:0.35,radius:6,blur:4,});}};trackdirect.models.StationCoveragePolygon.prototype._getConvexHullCoordinates=function(data,maxRange){var positions=this._getFilteredPositions(data,maxRange);positions.push(this._center);var xyPositions=this._convertToXYPos(positions);var convexHullXYPositions=convexhull.makeHull(xyPositions);var latLngPadding=(this._paddingInPercentOfMaxRange*0.01)*maxRange*0.000009;var latLngPaddingMin=this._paddingMinInMeters*0.000009;if(isNaN(latLngPadding)||latLngPadding<latLngPaddingMin){latLngPadding=latLngPaddingMin;}
var xyPositionsWithPadding=[];for(var i=0;i<convexHullXYPositions.length;i++){xyPositionsWithPadding.push(convexHullXYPositions[i]);for(var angle=0;angle<360;angle+=10){var x=convexHullXYPositions[i]['x']+latLngPadding*Math.cos(angle*Math.PI/180);var y=convexHullXYPositions[i]['y']+latLngPadding*Math.sin(angle*Math.PI/180)*2;if(!isNaN(x)&&!isNaN(y)){xyPositionsWithPadding.push({'x':x,'y':y});}}}
var convexHullXYPositionsWithPadding=convexhull.makeHull(xyPositionsWithPadding);return this._convertToLatLngPos(convexHullXYPositionsWithPadding);};trackdirect.models.StationCoveragePolygon.prototype._getFilteredPositions=function(data,maxRange){var result=[];for(var i=0;i<data.length;i++){if(typeof maxRange!=="undefined"&&data[i].distance>maxRange){continue;}
result.push(data[i].latLngLiteral);}
return result;};trackdirect.models.StationCoveragePolygon.prototype._getCoveragePolygonMaxRange=function(data){var maxRange=this._getDistancePercentile(data,this._percentile,this._upperMaxRangeInMeters);if(isNaN(maxRange)){maxRange=0;}
return maxRange;};trackdirect.models.StationCoveragePolygon.prototype._getDistancePercentile=function(data,percentile,upperMaxRange){var values=[];for(var i=0;i<data.length;i++){if((data[i].distance+0)<upperMaxRange){values.push(data[i].distance);}}
values.sort(function(a,b){return a-b;});var index=(percentile/100)*values.length;var result;if(Math.floor(index)==index){result=(values[index-1]+values[index])/2;}else{result=values[Math.floor(index)];}
return result;};trackdirect.models.StationCoveragePolygon.prototype._getNumberOfValues=function(data,maxRange){var counter=0;for(var i=0;i<data.length;i++){if(data[i].distance>maxRange){continue;}
counter++;}
return counter;};trackdirect.models.StationCoveragePolygon.prototype._convertToXYPos=function(positions){var result=[];for(var i=0;i<positions.length;i++){result.push({x:positions[i].lat,y:positions[i].lng});}
return result;};trackdirect.models.StationCoveragePolygon.prototype._convertToLatLngPos=function(positions){var result=[];for(var i=0;i<positions.length;i++){result.push({lat:positions[i].x,lng:positions[i].y});}
return result;};trackdirect.models.StationCoveragePolygon.prototype._getCoordinates=function(data){var result=[];for(var j=0;j<data.length;j++){if(typeof google==="object"&&typeof google.maps==="object"){var position=new google.maps.LatLng(parseFloat(data[j]["latitude"]),parseFloat(data[j]["longitude"]));}else{var position={lat:parseFloat(data[j]["latitude"]),lng:parseFloat(data[j]["longitude"]),};}
result.push(position);}
return result;};trackdirect.models.StationCoveragePolygon.prototype._addParametersToData=function(data){for(var j=0;j<data.length;j++){var latLngLiteral={lat:parseFloat(data[j].latitude),lng:parseFloat(data[j].longitude),};data[j].latLngLiteral=latLngLiteral;data[j].angle=trackdirect.services.distanceCalculator.getBearing(this._center,latLngLiteral);}};trackdirect.models.StationCoveragePolygon.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);}}
if(event in this._tdEventListenersOnce){var eventListenersOnce=this._tdEventListenersOnce[event].splice(0);this._tdEventListenersOnce[event]=[];for(var i=0;i<eventListenersOnce.length;i++){eventListenersOnce[i](arg);}}};
trackdirect.models.Ruler=function(defaultLength,map){this._map=map;if(typeof google==="object"&&typeof google.maps==="object"){this._googleMapsInit(defaultLength);this._addGoolgeMapsListeners();}else if(typeof L==="object"){this.leafletInit(defaultLength);this._addLeafletListeners();}};trackdirect.models.Ruler.prototype._googleMapsInit=function(defaultLength){this.marker1=new google.maps.Marker({position:trackdirect.services.distanceCalculator.getPositionByDistance(this._map.getCenterLiteral(),270,defaultLength/2),draggable:true,map:this._map,});this.marker2=new google.maps.Marker({position:trackdirect.services.distanceCalculator.getPositionByDistance(this._map.getCenterLiteral(),90,defaultLength/2),draggable:true,map:this._map,});this.marker1.markerLabel=new trackdirect.models.Label({position:this.marker1.getPosition(),text:this._getDistance(this.marker1,this.marker2),},this._map);this.marker1.markerLabel.show();this.marker2.markerLabel=new trackdirect.models.Label({position:this.marker2.getPosition(),text:this._getDistance(this.marker2,this.marker1),},this._map);this.marker2.markerLabel.show();this.line=new google.maps.Polyline({path:[this.marker1.getPosition(),this.marker2.getPosition()],strokeColor:"#ffff00",strokeOpacity:0.7,strokeWeight:6,});this.line.setMap(this._map);};trackdirect.models.Ruler.prototype.leafletInit=function(defaultLength){this.marker1=new L.Marker(trackdirect.services.distanceCalculator.getPositionByDistance(this._map.getCenterLiteral(),270,defaultLength/2),{draggable:true});this.marker1.addTo(this._map);this.marker2=new L.Marker(trackdirect.services.distanceCalculator.getPositionByDistance(this._map.getCenterLiteral(),90,defaultLength/2),{draggable:true});this.marker2.addTo(this._map);this.marker1.markerLabel=new trackdirect.models.Label({position:this.marker1.getLatLng(),text:this._getDistance(this.marker1,this.marker2),},this._map);this.marker1.markerLabel.show();this.marker2.markerLabel=new trackdirect.models.Label({position:this.marker2.getLatLng(),text:this._getDistance(this.marker2,this.marker1),},this._map);this.marker2.markerLabel.show();this.line=new L.Polyline([this.marker1.getLatLng(),this.marker2.getLatLng()],{color:"#ffff00",opacity:0.8,weight:6,});this.line.addTo(this._map);};trackdirect.models.Ruler.prototype.show=function(){this.marker1.markerLabel.show();this.marker2.markerLabel.show();if(typeof google==="object"&&typeof google.maps==="object"){this.marker1.setMap(this._map);this.marker2.setMap(this._map);this.line.setMap(this._map);}else if(typeof L==="object"){this.marker1.addTo(this._map);this.marker2.addTo(this._map);this.line.addTo(this._map);}};trackdirect.models.Ruler.prototype.hide=function(){this.marker1.markerLabel.hide();this.marker2.markerLabel.hide();if(typeof google==="object"&&typeof google.maps==="object"){this.marker1.setMap(null);this.marker2.setMap(null);this.line.setMap(null);}else if(typeof L==="object"){this._map.removeLayer(this.marker1);this._map.removeLayer(this.marker2);this._map.removeLayer(this.line);}};trackdirect.models.Ruler.prototype._addGoolgeMapsListeners=function(){var me=this;google.maps.event.addListener(this.marker1,"drag",function(){me.line.setPath([me.marker1.getPosition(),me.marker2.getPosition()]);me._updateLabels();});google.maps.event.addListener(this.marker2,"drag",function(){me.line.setPath([me.marker1.getPosition(),me.marker2.getPosition()]);me._updateLabels();});};trackdirect.models.Ruler.prototype._addLeafletListeners=function(){var me=this;this.marker1.on("drag",function(e){me.line.setLatLngs([me.marker1.getLatLng(),me.marker2.getLatLng()]);me._updateLabels();});this.marker2.on("drag",function(e){me.line.setLatLngs([me.marker1.getLatLng(),me.marker2.getLatLng()]);me._updateLabels();});};trackdirect.models.Ruler.prototype._updateLabels=function(){this.marker1.markerLabel.hide();this.marker2.markerLabel.hide();if(typeof google==="object"&&typeof google.maps==="object"){this.marker1.markerLabel=new trackdirect.models.Label({position:this.marker1.getPosition(),text:this._getDistance(this.marker1,this.marker2),},this._map);this.marker2.markerLabel=new trackdirect.models.Label({position:this.marker2.getPosition(),text:this._getDistance(this.marker2,this.marker1),},this._map);}else if(typeof L==="object"){this.marker1.markerLabel=new trackdirect.models.Label({position:this.marker1.getLatLng(),text:this._getDistance(this.marker1,this.marker2),},this._map);this.marker2.markerLabel=new trackdirect.models.Label({position:this.marker2.getLatLng(),text:this._getDistance(this.marker2,this.marker1),},this._map);}
this.marker1.markerLabel.show();this.marker2.markerLabel.show();};trackdirect.models.Ruler.prototype._getDistance=function(marker1,marker2){var p1=this._getPositionLiteral(marker1);var p2=this._getPositionLiteral(marker2);var distance=Math.round(trackdirect.services.distanceCalculator.getDistance(p1,p2),0);if(distance>99999){if(this._map.state.useImperialUnit){distance=Math.round(trackdirect.services.imperialConverter.convertKilometerToMile(distance/1000)).toString()+" miles";}else{distance=Math.round(distance/1000).toString()+" km";}}else if(distance>999){if(this._map.state.useImperialUnit){distance=(Math.round(trackdirect.services.imperialConverter.convertKilometerToMile(distance/1000)*10)/10).toString()+" miles";}else{distance=(Math.round(distance/100)/10).toString()+" km";}}else{distance=distance.toString()+" m";}
var bearing=Math.round(trackdirect.services.distanceCalculator.getBearing(p2,p1),0).toString();return bearing+"&ordm; "+distance;};trackdirect.models.Ruler.prototype._getPositionLiteral=function(marker){if(typeof google==="object"&&typeof google.maps==="object"){var latLng=marker.getPosition();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=marker.getLatLng();if(typeof latLng!=="undefined"){return{lat:latLng.lat,lng:latLng.lng};}else{return latLng;}}
@ -381,7 +413,7 @@ if(marker.overwrite==true&&packet.overwrite==0){return false;}else{if(marker.pac
if(marker.packet.reported_timestamp!==null&&packet.reported_timestamp!==null&&Math.abs(marker.packet.reported_timestamp-packet.reported_timestamp)<600){if(marker.packet.reported_timestamp>packet.reported_timestamp){return true;}}}}
if(packet.is_moving==1){var list=this._map.markerCollection.getStationMarkerIdKeys(packet.station_id);for(var relatedMarkerIdKey in list){var relatedMarker=this._map.markerCollection.getMarker(relatedMarkerIdKey);if(relatedMarker!==null&&relatedMarker.overwrite==false&&relatedMarker.packet.timestamp>packet.timestamp){return true;}}}
return false;};
trackdirect.models.MarkerCollection=function(){this._markerKeys={};this._markers=[];this._stationMarkers={};this._stationLastMovingMarkerIdKey={};this._stationLastMarker={};this._senderLastMarker={};this._positionMarkersIdKeys={};this._dotMarkers=[];this._markerPolyLines=[];this._markerOriginDashedPolyLines=[];this._mapSectorMarkerIdKeys={};this.resetAllMarkers();};trackdirect.models.MarkerCollection.prototype.getMarkerIdKey=function(markerId){if(!(markerId in this._markerKeys)){this._markers.push(null);this._markerPolyLines.push(null);this._dotMarkers.push(null);this._markerOriginDashedPolyLines.push(null);var markerIdKey=this._markers.length-1;this._markerKeys[markerId]=markerIdKey;return markerIdKey;}else{return this._markerKeys[markerId];}};trackdirect.models.MarkerCollection.prototype.isExistingMarker=function(markerIdKey){if(markerIdKey in this._markers&&this._markers[markerIdKey]!==null){return true;}
trackdirect.models.MarkerCollection=function(){this._markerKeys={};this._markers=[];this._stationMarkers={};this._stationLastMovingMarkerIdKey={};this._stationLastMarker={};this._senderLastMarker={};this._positionMarkersIdKeys={};this._dotMarkers=[];this._markerPolyLines=[];this._markerOriginDashedPolyLines=[];this._mapSectorMarkerIdKeys={};this._stationCoverage={};this.resetAllMarkers();};trackdirect.models.MarkerCollection.prototype.getMarkerIdKey=function(markerId){if(!(markerId in this._markerKeys)){this._markers.push(null);this._markerPolyLines.push(null);this._dotMarkers.push(null);this._markerOriginDashedPolyLines.push(null);var markerIdKey=this._markers.length-1;this._markerKeys[markerId]=markerIdKey;return markerIdKey;}else{return this._markerKeys[markerId];}};trackdirect.models.MarkerCollection.prototype.isExistingMarker=function(markerIdKey){if(markerIdKey in this._markers&&this._markers[markerIdKey]!==null){return true;}
return false;};trackdirect.models.MarkerCollection.prototype.setMarker=function(markerIdKey,marker){if(marker!==null&&typeof marker.packet!=="undefined"){var packet=marker.packet;this._markers[markerIdKey]=marker;this._addStationMarkerId(markerIdKey,packet);this._addStationLastMarker(packet,marker);this.addPostionMarkerId(markerIdKey,packet);}};trackdirect.models.MarkerCollection.prototype.getMarker=function(markerIdKey){if(markerIdKey!==null&&markerIdKey in this._markers){return this._markers[markerIdKey];}
return null;};trackdirect.models.MarkerCollection.prototype.getAllMarkers=function(){return this._markers;};trackdirect.models.MarkerCollection.prototype.removeMarker=function(markerIdKey){if(markerIdKey!==null&&markerIdKey in this._markers){this._markers.splice(markerIdKey,1);}};trackdirect.models.MarkerCollection.prototype.getNumberOfMarkers=function(){return this._markers.length;};trackdirect.models.MarkerCollection.prototype.setMarkerLabel=function(markerIdKey,label){if(markerIdKey in this._markers&&this._markers[markerIdKey]!==null){this._markers[markerIdKey].label=label;}};trackdirect.models.MarkerCollection.prototype.getMarkerLabel=function(markerIdKey){if(markerIdKey in this._markers&&this._markers[markerIdKey]!==null){return this._markers[markerIdKey].label;}
return null;};trackdirect.models.MarkerCollection.prototype.hasMarkerLabel=function(markerIdKey){if(markerIdKey in this._markers&&this._markers[markerIdKey]!==null&&this._markers[markerIdKey].label!==null){return true;}
@ -390,6 +422,9 @@ for(var markerIdKey in this.getPositionMarkerIdKeys(packet.latitude,packet.longi
return false;};trackdirect.models.MarkerCollection.prototype.addDotMarker=function(markerIdKey,dotMarker){if(markerIdKey in this._dotMarkers){if(this._dotMarkers[markerIdKey]===null){this._dotMarkers[markerIdKey]=[];}
this._dotMarkers[markerIdKey].push(dotMarker);}};trackdirect.models.MarkerCollection.prototype.getDotMarkers=function(markerIdKey){if(markerIdKey in this._dotMarkers&&this._dotMarkers[markerIdKey]!==null){return this._dotMarkers[markerIdKey];}
return[];};trackdirect.models.MarkerCollection.prototype.hasDotMarkers=function(markerIdKey){if(markerIdKey in this._dotMarkers&&this._dotMarkers[markerIdKey]!==null&&this._dotMarkers[markerIdKey].length>0){return true;}
return false;};trackdirect.models.MarkerCollection.prototype.addStationCoverage=function(stationId,stationCoveragePolygon){this._stationCoverage[stationId]=stationCoveragePolygon;};trackdirect.models.MarkerCollection.prototype.getStationCoverage=function(stationId){if(stationId in this._stationCoverage&&this._stationCoverage[stationId]!==null){return this._stationCoverage[stationId];}
return null;};trackdirect.models.MarkerCollection.prototype.getStationIdListWithVisibleCoverage=function(){var result=[];for(var stationId in this._stationCoverage){if(this._stationCoverage[stationId].isRequestedToBeVisible()){result.push(stationId);}}
return result;};trackdirect.models.MarkerCollection.prototype.hasCoveragePolygon=function(stationId){if(stationId in this._stationCoverage&&this._stationCoverage[stationId]!==null){return true;}
return false;};trackdirect.models.MarkerCollection.prototype.resetDotMarkers=function(markerIdKey){if(markerIdKey in this._dotMarkers&&this._dotMarkers[markerIdKey]!==null){this._dotMarkers[markerIdKey]=[];}};trackdirect.models.MarkerCollection.prototype.removeOldestDotMarker=function(markerIdKey){if(this.hasDotMarkers(markerIdKey)){latestMarker=this.getMarker(markerIdKey);var latestMarkerIndex=this.getDotMarkerIndex(markerIdKey,latestMarker);var dotMarkers=this.getDotMarkers(markerIdKey);var maxNumberOfPolyLinePoints=dotMarkers.length;if(latestMarkerIndex>-1){maxNumberOfPolyLinePoints=maxNumberOfPolyLinePoints-1;}
var removedItems=this._dotMarkers[markerIdKey].splice(0,1);if(removedItems.length==1){var removedMarker=removedItems[0];removedMarker.hide();if(this.hasPolyline(removedMarker.markerIdKey)){var polyline=this.getMarkerPolyline(removedMarker.markerIdKey);while(polyline.getPathLength()>maxNumberOfPolyLinePoints){polyline.removePathItem(0);}}
removedMarker=null;return true;}}
@ -409,7 +444,7 @@ return{};};trackdirect.models.MarkerCollection.prototype.getPositionMarkerIdKeys
return{};};trackdirect.models.MarkerCollection.prototype.addPostionMarkerId=function(markerIdKey,packet){var key=this._getCompareablePosition(packet.latitude,packet.longitude);if(!(key in this._positionMarkersIdKeys)){this._positionMarkersIdKeys[key]={};}
this._positionMarkersIdKeys[key][markerIdKey]=true;};trackdirect.models.MarkerCollection.prototype.removePostionMarkerId=function(latitude,longitude,markerIdKey){var key=this._getCompareablePosition(latitude,longitude);if(key in this._positionMarkersIdKeys){if(markerIdKey in this._positionMarkersIdKeys[key]){this._positionMarkersIdKeys[key][markerIdKey]=false;}}};trackdirect.models.MarkerCollection.prototype.addMarkerToMapSector=function(markerIdKey,markerMapSector){if(!(markerMapSector in this._mapSectorMarkerIdKeys)){this._mapSectorMarkerIdKeys[markerMapSector]=[];}
if(this._mapSectorMarkerIdKeys[markerMapSector].indexOf(markerIdKey)<0){this._mapSectorMarkerIdKeys[markerMapSector].push(markerIdKey);}};trackdirect.models.MarkerCollection.prototype.getMapSectorMarkerKeys=function(mapSector){if(mapSector in this._mapSectorMarkerIdKeys){return this._mapSectorMarkerIdKeys[mapSector];}
return[];};trackdirect.models.MarkerCollection.prototype.resetAllMarkers=function(){this._markerKeys={};this._markers=[];this._markerPolyLines=[];this._dotMarkers=[];this._markerOriginDashedPolyLines=[];this._stationMarkers={};this._stationLastMovingMarkerIdKey={};this._stationLastMarker={};this._senderLastMarker={};this._positionMarkersIdKeys={};this._mapSectorMarkerIdKeys={};};trackdirect.models.MarkerCollection.prototype.resetMarker=function(markerIdKey){if(this.isExistingMarker(markerIdKey)){var marker=this._markers[markerIdKey];this._markers[markerIdKey]=null;this._markerPolyLines[markerIdKey]=null;this._dotMarkers[markerIdKey]=null;this._markerOriginDashedPolyLines[markerIdKey]=null;if(typeof marker.packet.latitude!="undefined"&&typeof marker.packet.longitude!="undefined"){this.removePostionMarkerId(marker.packet.latitude,marker.packet.longitude,markerIdKey);}}};trackdirect.models.MarkerCollection.prototype.hasRelatedDashedPolyline=function(marker){if(typeof marker._relatedMarkerOriginDashedPolyLine!=="undefined"&&marker._relatedMarkerOriginDashedPolyLine!==null){return true;}
return[];};trackdirect.models.MarkerCollection.prototype.resetAllMarkers=function(){this._markerKeys={};this._markers=[];this._markerPolyLines=[];this._dotMarkers=[];this._markerOriginDashedPolyLines=[];this._stationMarkers={};this._stationLastMovingMarkerIdKey={};this._stationLastMarker={};this._senderLastMarker={};this._positionMarkersIdKeys={};this._mapSectorMarkerIdKeys={};this._stationCoverage={};};trackdirect.models.MarkerCollection.prototype.resetMarker=function(markerIdKey){if(this.isExistingMarker(markerIdKey)){var marker=this._markers[markerIdKey];this._markers[markerIdKey]=null;this._markerPolyLines[markerIdKey]=null;this._dotMarkers[markerIdKey]=null;this._markerOriginDashedPolyLines[markerIdKey]=null;if(typeof marker.packet.latitude!="undefined"&&typeof marker.packet.longitude!="undefined"){this.removePostionMarkerId(marker.packet.latitude,marker.packet.longitude,markerIdKey);}}};trackdirect.models.MarkerCollection.prototype.hasRelatedDashedPolyline=function(marker){if(typeof marker._relatedMarkerOriginDashedPolyLine!=="undefined"&&marker._relatedMarkerOriginDashedPolyLine!==null){return true;}
return false;};trackdirect.models.MarkerCollection.prototype.hasNonRelatedMovingMarkerId=function(packet){var latestStationMovingMarkerIdKey=this.getStationLatestMovingMarkerIdKey(packet.station_id);var newMarkerIdKey=this.getMarkerIdKey(packet.marker_id);if(latestStationMovingMarkerIdKey!==null&&latestStationMovingMarkerIdKey!==newMarkerIdKey){var latestStationMovingMarker=this.getMarker(latestStationMovingMarkerIdKey);if(latestStationMovingMarker.packet.hasConfirmedMapId()){return true;}}
return false;};trackdirect.models.MarkerCollection.prototype.isPacketReplacingMarker=function(packet){var markerIdKey=this.getMarkerIdKey(packet.marker_id);if(this.isExistingMarker(markerIdKey)){var marker=this.getMarker(markerIdKey);if(marker!==null){if(packet.map_id==14){return true;}
if(packet.is_moving==0){return true;}
@ -562,7 +597,7 @@ return 0;};trackdirect.models.Map.prototype.getSouthWestLng=function(){if(this.g
return 0;};trackdirect.models.Map.prototype.isMapReady=function(){if(this.getBounds()!=null){return true;}
return false;};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");}};trackdirect.models.Map.prototype.getMapType=function(){return this._mapType;};trackdirect.models.Map.prototype.getLeafletTileLayer=function(){return this._leafletTileLayer;};trackdirect.models.Map.prototype.getMid=function(){if(typeof this._tdMapOptions.mid!=="undefined"){return this._tdMapOptions.mid;}
return null;};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();}
return null;};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();}
@ -590,7 +625,8 @@ this.showTopLabelOnPosition(marker.packet.latitude,marker.packet.longitude);}}}}
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();}}}};trackdirect.models.Map.prototype._hideMarkersInPreviousVisibleMapSectors=function(previousVisibleMapSectors){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){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){var mapSectorMarkerKeys=this.markerCollection.getMapSectorMarkerKeys(mapSector);for(var j=0;j<mapSectorMarkerKeys.length;j++){var markerIdKey=mapSectorMarkerKeys[j];markerIdKeyListNotToHide[markerIdKey]=markerIdKey;}}}
for(var markerIdKey in markerIdKeyListToMaybeHide){if(markerIdKey in markerIdKeyListNotToHide){continue;}
var marker=this.markerCollection.getMarker(markerIdKey);if(marker!==null){trackdirect.services.callbackExecutor.addWithPriority(marker,marker.hideCompleteMarker,[]);}}}};trackdirect.models.Map.prototype._showMarkersInNewVisibleMapSectors=function(previousVisibleMapSectors){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){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,[]);}}}}}};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);}
var marker=this.markerCollection.getMarker(markerIdKey);if(marker!==null){trackdirect.services.callbackExecutor.addWithPriority(marker,marker.hideCompleteMarker,[]);}}}};trackdirect.models.Map.prototype._showMarkersInNewVisibleMapSectors=function(previousVisibleMapSectors){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){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,[]);}}}}
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();}}}}};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");};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);}};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);}}}}};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]);}}
@ -753,7 +789,8 @@ return weatherDiv;};trackdirect.models.InfoWindow.prototype._getTelemetryDiv=fun
if(comment==""){return null;}
var commentDiv=$(document.createElement("div"));commentDiv.css("clear","both");commentDiv.css("font-weight","bold");if(!trackdirect.isMobile){commentDiv.css("font-size","11px");}else{commentDiv.css("font-size","10px");}
commentDiv.html(comment);return commentDiv;};trackdirect.models.InfoWindow.prototype._addPhgLinkListeners=function(){var marker=this._marker;$("#half-phg-"+marker.packet.station_id+"-"+marker.packet.id).click(function(e){marker.showPHGCircle(true);return false;});$("#full-phg-"+marker.packet.station_id+"-"+marker.packet.id).click(function(e){marker.showPHGCircle(false);return false;});$("#none-phg-"+this._marker.packet.station_id+"-"+marker.packet.id).click(function(e){marker.hidePHGCircle();return false;});if($("#phglinks-"+marker.packet.station_id+"-"+marker.packet.id).length){$("#phglinks-"+marker.packet.station_id+"-"+marker.packet.id).show();}};trackdirect.models.InfoWindow.prototype._addRngLinkListeners=function(){var marker=this._marker;$("#half-rng-"+marker.packet.station_id+"-"+marker.packet.id).click(function(e){marker.showRNGCircle(true);return false;});$("#full-rng-"+marker.packet.station_id+"-"+marker.packet.id).click(function(e){marker.showRNGCircle(false);return false;});$("#none-rng-"+marker.packet.station_id+"-"+marker.packet.id).click(function(e){marker.hideRNGCircle();return false;});if($("#rnglinks-"+marker.packet.station_id+"-"+marker.packet.id).length){$("#rnglinks-"+marker.packet.station_id+"-"+marker.packet.id).show();}};trackdirect.models.InfoWindow.prototype._getMenuDiv=function(isInfoWindowOpen){var menuWrapperDiv=this._getMenuDivWrapperDiv();var menuDiv=this._getMenuDivMainDiv();menuWrapperDiv.append(menuDiv);var menuUl=this._getMenuDivUlDiv();menuDiv.append(menuUl);menuUl.append(this._getMenuDivTrackLink());menuUl.append(this._getMenuDivFilterLink());if(!trackdirect.isMobile){menuUl.append(this._getMenuDivCenterLink(isInfoWindowOpen));}
menuUl.append(this._getMenuDivZoomLink(isInfoWindowOpen));return menuWrapperDiv;};trackdirect.models.InfoWindow.prototype._getMenuDivWrapperDiv=function(){var menuWrapperDiv=$(document.createElement("div"));menuWrapperDiv.addClass("infowindow-menu-wrapper");menuWrapperDiv.css("clear","both");menuWrapperDiv.css("width","100%");menuWrapperDiv.css("padding-top","8px");return menuWrapperDiv;};trackdirect.models.InfoWindow.prototype._getMenuDivMainDiv=function(){var menuDiv=$(document.createElement("div"));menuDiv.addClass("infowindow-menu");menuDiv.css("width","100%");menuDiv.css("border-top","1px solid #cecece");return menuDiv;};trackdirect.models.InfoWindow.prototype._getMenuDivUlDiv=function(){var menuUl=$(document.createElement("ul"));menuUl.css("list-style-type","none");menuUl.css("list-style","none");menuUl.css("text-align","center");menuUl.css("margin","0");menuUl.css("padding","0");menuUl.css("display","table");return menuUl;};trackdirect.models.InfoWindow.prototype._getMenuDivLinkCss=function(){var liLinkCss={"list-style":"none",display:"table-cell","text-align":"center","padding-right":"10px",width:"auto",};return liLinkCss;};trackdirect.models.InfoWindow.prototype._getMenuDivTrackLink=function(){var trackLinkElementClass="trackStationLink"+this._marker.packet.station_id;var menuLi=$(document.createElement("li"));menuLi.css(this._getMenuDivLinkCss());var menuLink=$(document.createElement("a"));menuLink.css("color","#337ab7");menuLink.attr("href","#");menuLink.addClass(trackLinkElementClass);menuLink.attr("onclick","trackdirect.handleTrackStationRequest("+
menuUl.append(this._getMenuDivZoomLink(isInfoWindowOpen));if(!trackdirect.isEmbedded&&!inIframe()&&!this._marker.isMovingStation()&&this._marker.packet.source_id!=2){menuUl.append(this._getMenuDivCoverageLink());}
return menuWrapperDiv;};trackdirect.models.InfoWindow.prototype._getMenuDivWrapperDiv=function(){var menuWrapperDiv=$(document.createElement("div"));menuWrapperDiv.addClass("infowindow-menu-wrapper");menuWrapperDiv.css("clear","both");menuWrapperDiv.css("width","100%");menuWrapperDiv.css("padding-top","8px");return menuWrapperDiv;};trackdirect.models.InfoWindow.prototype._getMenuDivMainDiv=function(){var menuDiv=$(document.createElement("div"));menuDiv.addClass("infowindow-menu");menuDiv.css("width","100%");menuDiv.css("border-top","1px solid #cecece");return menuDiv;};trackdirect.models.InfoWindow.prototype._getMenuDivUlDiv=function(){var menuUl=$(document.createElement("ul"));menuUl.css("list-style-type","none");menuUl.css("list-style","none");menuUl.css("text-align","center");menuUl.css("margin","0");menuUl.css("padding","0");menuUl.css("display","table");return menuUl;};trackdirect.models.InfoWindow.prototype._getMenuDivLinkCss=function(){var liLinkCss={"list-style":"none",display:"table-cell","text-align":"center","padding-right":"10px",width:"auto",};return liLinkCss;};trackdirect.models.InfoWindow.prototype._getMenuDivTrackLink=function(){var trackLinkElementClass="trackStationLink"+this._marker.packet.station_id;var menuLi=$(document.createElement("li"));menuLi.css(this._getMenuDivLinkCss());var menuLink=$(document.createElement("a"));menuLink.css("color","#337ab7");menuLink.attr("href","#");menuLink.addClass(trackLinkElementClass);menuLink.attr("onclick","trackdirect.handleTrackStationRequest("+
this._marker.packet.station_id+
', "'+
trackLinkElementClass+
@ -768,6 +805,11 @@ Number.parseFloat(center.lat).toFixed(5)+
Number.parseFloat(center.lng).toFixed(5)+
"/zoom/"+
this._defaultMap.getZoom());}else{menuLink.html("Filter");menuLink.attr("href","/sid/"+this._marker.packet.station_id);}
menuLi.append(menuLink);return menuLi;};trackdirect.models.InfoWindow.prototype._getMenuDivCoverageLink=function(){var coverageLinkElementClass="stationCoverageLink"+this._marker.packet.station_id;var menuLi=$(document.createElement("li"));menuLi.css(this._getMenuDivLinkCss());var menuLink=$(document.createElement("a"));menuLink.css("color","#337ab7");menuLink.css("white-space","nowrap");menuLink.attr("href","#");menuLink.addClass(coverageLinkElementClass);menuLink.attr("onclick","trackdirect.toggleStationCoverage("+
this._marker.packet.station_id+
', "'+
coverageLinkElementClass+
'"); return false;');var coveragePolygon=this._defaultMap.markerCollection.getStationCoverage(this._marker.packet.station_id);if(coveragePolygon!==null&&coveragePolygon.isRequestedToBeVisible()){menuLink.html("Hide Coverage");}else{menuLink.html("Coverage");}
menuLi.append(menuLink);return menuLi;};trackdirect.models.InfoWindow.prototype._getMenuDivCenterLink=function(isInfoWindowOpen){var menuLi=$(document.createElement("li"));menuLi.css(this._getMenuDivLinkCss());var menuLink=$(document.createElement("a"));menuLink.css("color","#337ab7");if(isInfoWindowOpen){menuLink.attr("href","/center/"+
this._marker.packet.latitude.toFixed(5)+
","+

View File

@ -37,6 +37,7 @@ var trackdirect = {
_trackdirectInitDone: false,
isMobile: false,
coverageDataUrl: null,
settings: {},
/**
@ -184,9 +185,12 @@ var trackdirect = {
* @return None
*/
setCenter: function (latitude, longitude, zoom) {
latitude = typeof latitude !== "undefined" ? latitude : this._defaultLatitude;
longitude = typeof longitude !== "undefined" ? longitude : this._defaultLongitude;
zoom = typeof zoom !== "undefined" ? zoom : this.settings.defaultCurrentZoom;
latitude =
typeof latitude !== "undefined" ? latitude : this._defaultLatitude;
longitude =
typeof longitude !== "undefined" ? longitude : this._defaultLongitude;
zoom =
typeof zoom !== "undefined" ? zoom : this.settings.defaultCurrentZoom;
if (this._map !== null) {
this._map.setCenter({ lat: latitude, lng: longitude }, zoom);
@ -421,6 +425,99 @@ var trackdirect = {
}
},
/**
* Toggle station coverage
* @param {int} stationId
* @param {string} coverageLinkElementClass
*/
toggleStationCoverage: function (stationId, coverageLinkElementClass) {
coverageLinkElementClass =
typeof coverageLinkElementClass !== "undefined"
? coverageLinkElementClass
: null;
var coveragePolygon =
this._map.markerCollection.getStationCoverage(stationId);
if (coveragePolygon !== null && coveragePolygon.isRequestedToBeVisible()) {
coveragePolygon.hide();
if (coverageLinkElementClass !== null) {
$("." + coverageLinkElementClass).html("Coverage");
}
} else {
if (coveragePolygon !== null) {
coveragePolygon.show();
if (!coveragePolygon.hasContent()) {
alert(
"Currently we do not have enough data to create a max range coverage plot for this station. Try again later!"
);
} else {
if (coverageLinkElementClass !== null) {
$("." + coverageLinkElementClass).html("Hide coverage");
}
}
} else {
var packet =
this._map.markerCollection.getStationLatestPacket(stationId);
var center = {
lat: parseFloat(packet.latitude),
lng: parseFloat(packet.longitude),
};
var coveragePolygon = new trackdirect.models.StationCoveragePolygon(
center,
this._map,
true
);
this._map.markerCollection.addStationCoverage(
stationId,
coveragePolygon
);
coveragePolygon.showWhenDone();
if (coverageLinkElementClass !== null) {
$("." + coverageLinkElementClass).html(
'Loading <i class="fa fa-spinner fa-spin" style="font-size:12px"></i>'
);
coveragePolygon.addTdListener(
"visible",
function () {
if (!coveragePolygon.hasContent()) {
coveragePolygon.hide();
alert(
"Currently we do not have enough data to create a max range coverage plot for this station. Try again later!"
);
$("." + coverageLinkElementClass).html("Coverage");
} else {
$("." + coverageLinkElementClass).html("Hide coverage");
}
},
true
);
}
var me = this;
$.getJSON(this.coverageDataUrl + "?id=" + stationId, function (data) {
if ("station_id" in data && "coverage" in data) {
coveragePolygon.setData(data["coverage"]);
var marker =
me._map.markerCollection.getStationLatestMarker(stationId);
if (marker.isVisible()) {
if (coveragePolygon.isRequestedToBeVisible()) {
coveragePolygon.show();
}
}
}
})
.fail(function () {
coveragePolygon.hide();
alert("Failed to fetch coverage data. Try again later!");
$("." + coverageLinkElementClass).html("Coverage");
})
.always(function () {});
}
}
},
/**
* Set map type
* @param {string} mapType
@ -938,6 +1035,9 @@ var trackdirect = {
if (typeof options["isMobile"] !== undefined) {
this.isMobile = options["isMobile"];
}
if (typeof options["coverageDataUrl"] !== undefined) {
this.coverageDataUrl = options["coverageDataUrl"];
}
if (typeof options["time"] !== undefined) {
this._time = options["time"];
}

View File

@ -1224,6 +1224,14 @@ trackdirect.models.InfoWindow.prototype._getMenuDiv = function (
menuUl.append(this._getMenuDivCenterLink(isInfoWindowOpen));
}
menuUl.append(this._getMenuDivZoomLink(isInfoWindowOpen));
if (
!trackdirect.isEmbedded &&
!inIframe() &&
!this._marker.isMovingStation() &&
this._marker.packet.source_id != 2
) {
menuUl.append(this._getMenuDivCoverageLink());
}
return menuWrapperDiv;
};
@ -1360,6 +1368,41 @@ trackdirect.models.InfoWindow.prototype._getMenuDivFilterLink = function () {
return menuLi;
};
/**
* Get the info window menu coverage link
* @return {object}
*/
trackdirect.models.InfoWindow.prototype._getMenuDivCoverageLink = function () {
var coverageLinkElementClass =
"stationCoverageLink" + this._marker.packet.station_id;
var menuLi = $(document.createElement("li"));
menuLi.css(this._getMenuDivLinkCss());
var menuLink = $(document.createElement("a"));
menuLink.css("color", "#337ab7");
menuLink.css("white-space", "nowrap");
menuLink.attr("href", "#");
menuLink.addClass(coverageLinkElementClass);
menuLink.attr(
"onclick",
"trackdirect.toggleStationCoverage(" +
this._marker.packet.station_id +
', "' +
coverageLinkElementClass +
'"); return false;'
);
var coveragePolygon = this._defaultMap.markerCollection.getStationCoverage(
this._marker.packet.station_id
);
if (coveragePolygon !== null && coveragePolygon.isRequestedToBeVisible()) {
menuLink.html("Hide Coverage");
} else {
menuLink.html("Coverage");
}
menuLi.append(menuLink);
return menuLi;
};
/**
* Get the info window menu center link
* @param {boolean} isInfoWindowOpen

View File

@ -446,6 +446,11 @@ trackdirect.models.Map.prototype.resetAllMarkers = function () {
marker.hide();
marker.hideMarkerPrevPosition();
marker.hideMarkerTail();
var stationCoverage = this.markerCollection.getStationCoverage(marker.packet.station_id);
if (stationCoverage) {
stationCoverage.hide();
}
}
this.markerCollection.removeMarker(i);
}
@ -1132,6 +1137,20 @@ trackdirect.models.Map.prototype._showMarkersInNewVisibleMapSectors = function (
}
}
}
// 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();
}
}
}
}
};

View File

@ -35,6 +35,9 @@ trackdirect.models.MarkerCollection = function () {
// Contains arrays of markerKeys and is indexed by mapSectorId
this._mapSectorMarkerIdKeys = {};
// Contains coverage values indexed by stationId
this._stationCoverage = {};
this.resetAllMarkers();
};
@ -280,6 +283,67 @@ trackdirect.models.MarkerCollection.prototype.hasDotMarkers = function (
return false;
};
/*
* Add dot marker for specified markerIdKey
* @param {int} markerIdKey
* @param {object} dotMarker
*/
trackdirect.models.MarkerCollection.prototype.addStationCoverage = function (
stationId,
stationCoveragePolygon
) {
this._stationCoverage[stationId] = stationCoveragePolygon;
};
/**
* Returns the station coverage
* @param {int} stationId
* @return {StationCoveragePolygon}
*/
trackdirect.models.MarkerCollection.prototype.getStationCoverage = function (
stationId
) {
if (
stationId in this._stationCoverage &&
this._stationCoverage[stationId] !== null
) {
return this._stationCoverage[stationId];
}
return null;
};
/**
* Returns an array of stations the has a visible coverage
* @return {array}
*/
trackdirect.models.MarkerCollection.prototype.getStationIdListWithVisibleCoverage =
function () {
var result = [];
for (var stationId in this._stationCoverage) {
if (this._stationCoverage[stationId].isRequestedToBeVisible()) {
result.push(stationId);
}
}
return result;
};
/**
* Returns true if specified station has a coverage polygon
* @param {int} stationId
* @return {boolean}
*/
trackdirect.models.MarkerCollection.prototype.hasCoveragePolygon = function (
stationId
) {
if (
stationId in this._stationCoverage &&
this._stationCoverage[stationId] !== null
) {
return true;
}
return false;
};
/**
* Reset the dot markers for a specified marker
* @param {int} markerIdKey
@ -727,6 +791,7 @@ trackdirect.models.MarkerCollection.prototype.resetAllMarkers = function () {
this._senderLastMarker = {};
this._positionMarkersIdKeys = {};
this._mapSectorMarkerIdKeys = {};
this._stationCoverage = {};
};
/**

View File

@ -0,0 +1,506 @@
/**
* Class trackdirect.models.StationCoveragePolygon
* @param {LatLngLiteral} center
* @param {trackdirect.models.Map} map
* @param {boolean} tryToShowCoveragePolygon
*/
trackdirect.models.StationCoveragePolygon = function (
center,
map,
tryToShowCoveragePolygon
) {
tryToShowCoveragePolygon =
typeof tryToShowCoveragePolygon !== "undefined"
? tryToShowCoveragePolygon
: true;
this._showPolygon = tryToShowCoveragePolygon;
this._map = map;
this._center = center;
this._isRequestedToBeVisible = false;
this._polygon = null;
this._polygonCoordinates = null;
this._heatmapCoordinates = null;
this._heatmap = null;
this._tdEventListeners = {};
this._tdEventListenersOnce = {};
// I recommend ignoring positions with a very long distance.
// Ignoring everything with a distance longer than 1000km is reasonable.
this._upperMaxRangeInMeters = 1000 * 1000;
// Percentile affects how many packets we include in the coverage polygon.
this._percentile = 95;
// To get a smooth ploygon we add som padding to the convex hull positions.
this._paddingInPercentOfMaxRange = 10;
this._paddingMinInMeters = 1000;
};
/**
* Set coverage data
* @param {array} data
*/
trackdirect.models.StationCoveragePolygon.prototype.setData = function (data) {
this._addParametersToData(data);
this._heatmapCoordinates = this._getCoordinates(data);
if (this._showPolygon) {
var maxRange = this._getCoveragePolygonMaxRange(data);
if (maxRange <= 0) {
this._showPolygon = false;
} else {
this._polygonCoordinates = this._getConvexHullCoordinates(data, maxRange);
}
}
if (typeof google === "object" && typeof google.maps === "object") {
this._googleMapsInit();
} else if (typeof L === "object") {
this._leafletInit();
}
};
/**
* Add listener to events
* @param {string} event
* @param {string} handler
*/
trackdirect.models.StationCoveragePolygon.prototype.addTdListener = function (
event,
handler,
execOnce
) {
execOnce = typeof execOnce !== "undefined" ? execOnce : false;
if (execOnce) {
if (!(event in this._tdEventListenersOnce)) {
this._tdEventListenersOnce[event] = [];
}
this._tdEventListenersOnce[event].push(handler);
} else {
if (!(event in this._tdEventListeners)) {
this._tdEventListeners[event] = [];
}
this._tdEventListeners[event].push(handler);
}
};
/**
* Returns true if polygon has an area
* @return {boolean}
*/
trackdirect.models.StationCoveragePolygon.prototype.hasContent = function () {
if (
this._heatmapCoordinates !== null &&
this._heatmapCoordinates.length > 0
) {
return true;
}
return false;
};
/**
* Returns true if polygon is visible
* @return {boolean}
*/
trackdirect.models.StationCoveragePolygon.prototype.isRequestedToBeVisible =
function () {
return this._isRequestedToBeVisible;
};
/**
* Request polygon to be shown when complete
*/
trackdirect.models.StationCoveragePolygon.prototype.showWhenDone = function () {
this._isRequestedToBeVisible = true;
};
/**
* Show coverage
*/
trackdirect.models.StationCoveragePolygon.prototype.show = function () {
if (typeof google === "object" && typeof google.maps === "object") {
if (this._polygon !== null) {
this._polygon.setMap(this._map);
}
if (this._heatmap !== null) {
this._heatmap.setMap(this._map);
}
} else if (typeof L === "object") {
if (this._polygon !== null) {
this._polygon.addTo(this._map);
}
if (this._heatmap !== null) {
this._heatmap.addTo(this._map);
}
}
this._isRequestedToBeVisible = true;
// show will be called again when data has been updated
if (this._showPolygon && this._polygonCoordinates !== null) {
this._emitTdEventListeners("visible");
} else if (this._heatmapCoordinates !== null) {
this._emitTdEventListeners("visible");
}
};
/**
* Hide coverage
* @param {boolean} stillMarkAsVisible
*/
trackdirect.models.StationCoveragePolygon.prototype.hide = function (
stillMarkAsVisible
) {
stillMarkAsVisible =
typeof stillMarkAsVisible !== "undefined" ? stillMarkAsVisible : false;
if (typeof google === "object" && typeof google.maps === "object") {
if (this._polygon !== null) {
this._polygon.setMap(null);
}
if (this._heatmap !== null) {
this._heatmap.setMap(null);
}
} else if (typeof L === "object") {
if (this._polygon !== null) {
this._map.removeLayer(this._polygon);
}
if (this._heatmap !== null) {
this._map.removeLayer(this._heatmap);
}
}
if (!stillMarkAsVisible) {
this._isRequestedToBeVisible = false;
}
this._emitTdEventListeners("hidden");
};
/**
* Init function for Gogle Maps
*/
trackdirect.models.StationCoveragePolygon.prototype._googleMapsInit =
function () {
if (
this._polygonCoordinates !== null &&
this._polygonCoordinates.length > 0
) {
this._polygon = new google.maps.Polygon({
paths: this._polygonCoordinates,
strokeColor: "#0000FF",
strokeOpacity: 0,
strokeWeight: 0,
fillColor: "#0000FF",
fillOpacity: 0.2,
});
}
if (
this._heatmapCoordinates !== null &&
this._heatmapCoordinates.length > 0
) {
var data = [];
for (var i = 0; i < this._heatmapCoordinates.length; i++) {
data.push({ location: this._heatmapCoordinates[i], weight: 1 });
}
this._heatmap = new google.maps.visualization.HeatmapLayer({
data: data,
radius: 8,
maxIntensity: 5,
gradient: [
"rgba(0, 255, 255, 0)",
"rgba(0, 255, 255, 1)",
"rgba(0, 191, 255, 1)",
"rgba(0, 127, 255, 1)",
"rgba(0, 63, 255, 1)",
"rgba(0, 0, 255, 1)",
"rgba(0, 0, 223, 1)",
"rgba(0, 0, 191, 1)",
"rgba(0, 0, 159, 1)",
"rgba(0, 0, 127, 1)",
"rgba(63, 0, 91, 1)",
"rgba(127, 0, 63, 1)",
"rgba(191, 0, 31, 1)",
"rgba(255, 0, 0, 1)",
],
map: null,
});
}
};
/**
* Init function for Leaflet
*/
trackdirect.models.StationCoveragePolygon.prototype._leafletInit = function () {
if (
this._polygonCoordinates !== null &&
this._polygonCoordinates.length > 0
) {
this._polygon = new L.polygon(this._polygonCoordinates, {
color: "#0000FF",
opacity: 0,
weight: 0,
fillColor: "#0000FF",
fillOpacity: 0.2,
});
}
if (
this._heatmapCoordinates !== null &&
this._heatmapCoordinates.length > 0
) {
var data = [];
for (var i = 0; i < this._heatmapCoordinates.length; i++) {
data.push([
this._heatmapCoordinates[i].lat,
this._heatmapCoordinates[i].lng,
10,
]);
}
this._heatmap = L.heatLayer(this._heatmapCoordinates, {
minOpacity: 0.35,
radius: 6,
blur: 4,
});
}
};
/**
* Get convex hull coordinates
* @param {array} data
* @param {int} maxRange
* @return {array}
*/
trackdirect.models.StationCoveragePolygon.prototype._getConvexHullCoordinates =
function (data, maxRange) {
var positions = this._getFilteredPositions(data, maxRange);
positions.push(this._center);
var xyPositions = this._convertToXYPos(positions);
var convexHullXYPositions = convexhull.makeHull(xyPositions);
// Calc padding
var latLngPadding =
this._paddingInPercentOfMaxRange * 0.01 * maxRange * 0.000009;
var latLngPaddingMin = this._paddingMinInMeters * 0.000009;
if (isNaN(latLngPadding) || latLngPadding < latLngPaddingMin) {
latLngPadding = latLngPaddingMin;
}
// Add padding
var xyPositionsWithPadding = [];
for (var i = 0; i < convexHullXYPositions.length; i++) {
xyPositionsWithPadding.push(convexHullXYPositions[i]);
for (var angle = 0; angle < 360; angle += 10) {
var x =
convexHullXYPositions[i]["x"] +
latLngPadding * Math.cos((angle * Math.PI) / 180);
var y =
convexHullXYPositions[i]["y"] +
latLngPadding * Math.sin((angle * Math.PI) / 180) * 2;
if (!isNaN(x) && !isNaN(y)) {
xyPositionsWithPadding.push({ x: x, y: y });
}
}
}
var convexHullXYPositionsWithPadding = convexhull.makeHull(
xyPositionsWithPadding
);
// Convert to LatLng and return
return this._convertToLatLngPos(convexHullXYPositionsWithPadding);
};
/**
* Get an array with valid positions
* @param {array} data
* @param {int} maxRange
* @return {array}
*/
trackdirect.models.StationCoveragePolygon.prototype._getFilteredPositions =
function (data, maxRange) {
var result = [];
for (var i = 0; i < data.length; i++) {
if (typeof maxRange !== "undefined" && data[i].distance > maxRange) {
continue;
}
result.push(data[i].latLngLiteral);
}
return result;
};
/**
* Calculate coverage polygon max range
* @param {array} data
* @return {int}
*/
trackdirect.models.StationCoveragePolygon.prototype._getCoveragePolygonMaxRange =
function (data) {
var maxRange = this._getDistancePercentile(
data,
this._percentile,
this._upperMaxRangeInMeters
);
if (isNaN(maxRange)) {
maxRange = 0;
}
return maxRange;
};
/**
* Calculate the specified percentile
* @param {array} data
* @param {int} percentile
* @param {int} upperMaxRange
* @return {int}
*/
trackdirect.models.StationCoveragePolygon.prototype._getDistancePercentile =
function (data, percentile, upperMaxRange) {
var values = [];
for (var i = 0; i < data.length; i++) {
if (data[i].distance + 0 < upperMaxRange) {
values.push(data[i].distance);
}
}
values.sort(function (a, b) {
return a - b;
});
var index = (percentile / 100) * values.length;
var result;
if (Math.floor(index) == index) {
result = (values[index - 1] + values[index]) / 2;
} else {
result = values[Math.floor(index)];
}
return result;
};
/**
* Calculate number of values
* @param {array} data
* @param {int} maxRange
* @return {int}
*/
trackdirect.models.StationCoveragePolygon.prototype._getNumberOfValues =
function (data, maxRange) {
var counter = 0;
for (var i = 0; i < data.length; i++) {
if (data[i].distance > maxRange) {
continue;
}
counter++;
}
return counter;
};
/**
* Convert to xy positions
* @param {array} data
* @return {array}
*/
trackdirect.models.StationCoveragePolygon.prototype._convertToXYPos = function (
positions
) {
var result = [];
for (var i = 0; i < positions.length; i++) {
result.push({ x: positions[i].lat, y: positions[i].lng });
}
return result;
};
/**
* Convert to lat/lng positions
* @param {array} data
* @return {array}
*/
trackdirect.models.StationCoveragePolygon.prototype._convertToLatLngPos =
function (positions) {
var result = [];
for (var i = 0; i < positions.length; i++) {
result.push({ lat: positions[i].x, lng: positions[i].y });
}
return result;
};
/**
* Get an array of all coordinates for the specified move type
* @param {array} data
* @return {array}
*/
trackdirect.models.StationCoveragePolygon.prototype._getCoordinates = function (
data
) {
var result = [];
for (var j = 0; j < data.length; j++) {
if (typeof google === "object" && typeof google.maps === "object") {
var position = new google.maps.LatLng(
parseFloat(data[j]["latitude"]),
parseFloat(data[j]["longitude"])
);
} else {
var position = {
lat: parseFloat(data[j]["latitude"]),
lng: parseFloat(data[j]["longitude"]),
};
}
result.push(position);
}
return result;
};
/**
* Add the angle paramter for each coverage position
* @param {array} data
*/
trackdirect.models.StationCoveragePolygon.prototype._addParametersToData =
function (data) {
for (var j = 0; j < data.length; j++) {
var latLngLiteral = {
lat: parseFloat(data[j].latitude),
lng: parseFloat(data[j].longitude),
};
data[j].latLngLiteral = latLngLiteral;
data[j].angle = trackdirect.services.distanceCalculator.getBearing(
this._center,
latLngLiteral
);
}
};
/**
* Emit all event listeners for a specified event
* @param {string} event
* @param {object} arg
*/
trackdirect.models.StationCoveragePolygon.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);
}
}
if (event in this._tdEventListenersOnce) {
var eventListenersOnce = this._tdEventListenersOnce[event].splice(0);
this._tdEventListenersOnce[event] = [];
for (var i = 0; i < eventListenersOnce.length; i++) {
eventListenersOnce[i](arg);
}
}
};

View File

@ -1,13 +1,15 @@
create table packet_path (
"id" bigserial not null,
"packet_id" bigint not null,
"sending_station_id" bigint not null,
"station_id" bigint not null,
"latitude" double precision null,
"longitude" double precision null,
"timestamp" bigint null,
"distance" int null,
"number" smallint,
"sending_station_id" bigint not null,
"sending_latitude" double precision null,
"sending_longitude" double precision null,
primary key (id),
foreign key(station_id) references station(id),
foreign key(sending_station_id) references station(id)

View File

@ -232,7 +232,7 @@ class PacketBatchInserter():
distance = packet.getTransmitDistance()
pathTuples.append(
(packet.id, stationId, packet.stationId, latitude, longitude, packet.timestamp, distance, number))
(packet.id, stationId, latitude, longitude, packet.timestamp, distance, number, packet.stationId, packet.latitude, packet.longitude))
number += 1
i += 1
@ -240,9 +240,9 @@ class PacketBatchInserter():
if pathTuples:
try:
argString = ','.join(cur.mogrify(
"(%s, %s, %s, %s, %s, %s, %s, %s)", x) for x in pathTuples)
"(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)", x) for x in pathTuples)
cur.execute("insert into " + packetPathTable +
"(packet_id, station_id, sending_station_id, latitude, longitude, timestamp, distance, number) values " + argString)
"(packet_id, station_id, latitude, longitude, timestamp, distance, number, sending_station_id, sending_latitude, sending_longitude) values " + argString)
except psycopg2.InterfaceError as e:
# Connection to database is lost, better just exit
raise e