2022-10-13 17:43:56 +00:00
{ //run in own scope for fast switch
2023-06-08 12:59:26 +00:00
const MODE _MENU = 0 ;
const MODE _MAP = 1 ;
const MODE _SLICES = 2 ;
2022-09-21 19:33:14 +00:00
const STORAGE = require ( "Storage" ) ;
2022-11-01 23:31:06 +00:00
const BAT _FULL = require ( "Storage" ) . readJSON ( "setting.json" ) . batFullVoltage || 0.3144 ;
2023-05-19 15:56:54 +00:00
const SETTINGS = {
2023-05-20 10:47:11 +00:00
mapCompass : true ,
2023-05-27 21:54:24 +00:00
mapScale : 0.2 , //initial value
mapRefresh : 1000 , //minimum time in ms between refreshs of the map
mapChunkSize : 5 , //render this many waypoints at a time
overviewScroll : 30 , //scroll this amount on swipe in pixels
overviewScale : 0.02 , //initial value
refresh : 500 , //general refresh interval in ms
refreshLocked : 3000 , //general refresh interval when Bangle is locked
2023-05-22 08:53:52 +00:00
cacheMinFreeMem : 2000 ,
2023-05-20 10:47:11 +00:00
cacheMaxEntries : 0 ,
2023-05-27 21:54:24 +00:00
minCourseChange : 5 , //course change needed in degrees before redrawing the map
minPosChange : 5 , //position change needed in pixels before redrawing the map
waypointChangeDist : 50 , //distance in m to next waypoint before advancing automatically
2023-06-08 19:52:50 +00:00
queueWaitingTime : 5 , // waiting time during processing of task queue items when running with timeouts
2023-06-10 09:07:21 +00:00
autosearch : true ,
2023-06-10 12:54:22 +00:00
maxDistForAutosearch : 300 ,
2023-06-10 11:19:11 +00:00
autosearchLimit : 3
2023-05-19 15:56:54 +00:00
} ;
2022-11-01 23:31:06 +00:00
2022-10-13 17:43:56 +00:00
let init = function ( ) {
global . screen = 1 ;
global . drawTimeout = undefined ;
global . lastDrawnScreen = 0 ;
global . firstDraw = true ;
global . slices = [ ] ;
2023-06-08 12:59:26 +00:00
global . maxSlicePages = 1 ;
2022-10-13 17:43:56 +00:00
global . scheduleDraw = false ;
2022-09-21 19:33:14 +00:00
Bangle . loadWidgets ( ) ;
2022-10-21 15:04:53 +00:00
WIDGETS . gpstrek . start ( false ) ;
2023-05-16 17:48:30 +00:00
if ( ! WIDGETS . gpstrek . getState ( ) . numberOfSlices ) WIDGETS . gpstrek . getState ( ) . numberOfSlices = 2 ;
2023-06-08 12:59:26 +00:00
if ( ! WIDGETS . gpstrek . getState ( ) . mode ) WIDGETS . gpstrek . getState ( ) . mode = MODE _MENU ;
2022-10-13 17:43:56 +00:00
} ;
init ( ) ;
scheduleDraw = true ;
2022-09-21 19:33:14 +00:00
2022-10-13 17:43:56 +00:00
let parseNumber = function ( toParse ) {
2022-09-21 19:33:14 +00:00
if ( toParse . includes ( "." ) ) return parseFloat ( toParse ) ;
return parseFloat ( "" + toParse + ".0" ) ;
2022-10-13 17:43:56 +00:00
} ;
2022-09-21 19:33:14 +00:00
2022-10-13 17:43:56 +00:00
let parseWaypoint = function ( filename , offset , result ) {
2022-09-21 19:33:14 +00:00
result . lat = parseNumber ( STORAGE . read ( filename , offset , 11 ) ) ;
result . lon = parseNumber ( STORAGE . read ( filename , offset += 11 , 12 ) ) ;
return offset + 12 ;
2022-10-13 17:43:56 +00:00
} ;
2022-09-21 19:33:14 +00:00
2022-10-13 17:43:56 +00:00
let parseWaypointWithElevation = function ( filename , offset , result ) {
2022-09-21 19:33:14 +00:00
offset = parseWaypoint ( filename , offset , result ) ;
result . alt = parseNumber ( STORAGE . read ( filename , offset , 6 ) ) ;
return offset + 6 ;
2022-10-13 17:43:56 +00:00
} ;
2022-09-21 19:33:14 +00:00
2022-10-13 17:43:56 +00:00
let parseWaypointWithName = function ( filename , offset , result ) {
2022-09-21 19:33:14 +00:00
offset = parseWaypoint ( filename , offset , result ) ;
return parseName ( filename , offset , result ) ;
2022-10-13 17:43:56 +00:00
} ;
2022-09-21 19:33:14 +00:00
2022-10-13 17:43:56 +00:00
let parseName = function ( filename , offset , result ) {
2022-09-21 19:33:14 +00:00
let nameLength = STORAGE . read ( filename , offset , 2 ) - 0 ;
result . name = STORAGE . read ( filename , offset += 2 , nameLength ) ;
return offset + nameLength ;
2022-10-13 17:43:56 +00:00
} ;
2022-09-21 19:33:14 +00:00
2022-10-13 17:43:56 +00:00
let parseWaypointWithElevationAndName = function ( filename , offset , result ) {
2022-09-21 19:33:14 +00:00
offset = parseWaypointWithElevation ( filename , offset , result ) ;
return parseName ( filename , offset , result ) ;
2022-10-13 17:43:56 +00:00
} ;
2022-09-21 19:33:14 +00:00
2023-05-18 18:20:55 +00:00
let cache = { } ;
2023-05-27 13:57:23 +00:00
let cachedOffsets = [ ] ;
2023-05-18 18:20:55 +00:00
2023-05-27 16:15:37 +00:00
let getFromCache = function ( filename , offset , result ) {
if ( filename == cache . filename && cache [ offset ] ) {
2023-05-18 18:20:55 +00:00
Object . assign ( result , cache [ offset ] ) ;
result . access = Date . now ( ) ;
return offset + cache [ offset ] . fileLength ;
}
2023-05-27 16:15:37 +00:00
return offset ;
} ;
let cacheCleanup = function ( ) {
if ( SETTINGS . cacheMinFreeMem ) {
while ( cachedOffsets . length > 0 && process . memory ( false ) . free < SETTINGS . cacheMinFreeMem ) {
cache [ cachedOffsets . shift ( ) ] = undefined ;
}
}
if ( SETTINGS . cacheMaxEntries ) {
while ( cachedOffsets . length > SETTINGS . cacheMaxEntries ) {
cache [ cachedOffsets . shift ( ) ] = undefined ;
}
}
} ;
let cacheAdd = function ( filename , result ) {
cacheCleanup ( ) ;
if ( cache . filename != filename ) {
cache = { } ;
cache . filename = filename ;
}
cache [ result . fileOffset ] = result ;
cachedOffsets . push ( result . fileOffset ) ;
} ;
let getEntry = function ( filename , offset , result , noCaching ) {
if ( offset < 0 ) return offset ;
let cacheOffset = getFromCache ( filename , offset , result ) ;
if ( cacheOffset != offset ) return cacheOffset ;
2022-09-21 19:33:14 +00:00
result . fileOffset = offset ;
let type = STORAGE . read ( filename , offset ++ , 1 ) ;
if ( type == "" ) return - 1 ;
switch ( type ) {
case "A" :
offset = parseWaypoint ( filename , offset , result ) ;
break ;
case "B" :
offset = parseWaypointWithName ( filename , offset , result ) ;
break ;
case "C" :
offset = parseWaypointWithElevation ( filename , offset , result ) ;
break ;
case "D" :
offset = parseWaypointWithElevationAndName ( filename , offset , result ) ;
break ;
default :
print ( "Unknown entry type" , type ) ;
return - 1 ;
}
offset ++ ;
result . fileLength = offset - result . fileOffset ;
2023-05-27 16:15:37 +00:00
if ( ! noCaching && ( SETTINGS . cacheMaxEntries || SETTINGS . cacheMinFreeMem ) ) {
cacheAdd ( filename , result ) ;
2023-05-20 10:47:11 +00:00
}
2022-09-21 19:33:14 +00:00
return offset ;
2022-10-13 17:43:56 +00:00
} ;
2022-09-21 19:33:14 +00:00
const labels = [ "N" , "NE" , "E" , "SE" , "S" , "SW" , "W" , "NW" ] ;
const loc = require ( "locale" ) ;
2022-10-13 17:43:56 +00:00
let matchFontSize = function ( graphics , text , height , width ) {
2022-09-21 19:33:14 +00:00
graphics . setFontVector ( height ) ;
let metrics ;
let size = 1 ;
while ( graphics . stringMetrics ( text ) . width > 0.90 * width ) {
size -= 0.05 ;
graphics . setFont ( "Vector" , Math . floor ( height * size ) ) ;
}
2022-10-13 17:43:56 +00:00
} ;
2022-09-21 19:33:14 +00:00
2023-05-16 16:15:52 +00:00
let getDoubleLineSlice = function ( title1 , title2 , provider1 , provider2 ) {
2022-09-21 19:33:14 +00:00
return {
draw : function ( graphics , x , y , height , width ) {
lastDrawn = Date . now ( ) ;
if ( typeof title1 == "function" ) title1 = title1 ( ) ;
if ( typeof title2 == "function" ) title2 = title2 ( ) ;
graphics . clearRect ( x , y , x + width , y + height ) ;
2022-11-02 17:02:47 +00:00
lastValue1 = provider1 ( ) ;
matchFontSize ( graphics , title1 + lastValue1 , Math . floor ( height * 0.5 ) , width ) ;
2022-09-21 19:33:14 +00:00
graphics . setFontAlign ( - 1 , - 1 ) ;
graphics . drawString ( title1 , x + 2 , y ) ;
graphics . setFontAlign ( 1 , - 1 ) ;
2022-11-02 17:02:47 +00:00
graphics . drawString ( lastValue1 , x + width , y ) ;
2022-09-21 19:33:14 +00:00
2022-11-02 17:02:47 +00:00
lastValue2 = provider2 ( ) ;
matchFontSize ( graphics , title2 + lastValue2 , Math . floor ( height * 0.5 ) , width ) ;
2022-09-21 19:33:14 +00:00
graphics . setFontAlign ( - 1 , - 1 ) ;
graphics . drawString ( title2 , x + 2 , y + ( height * 0.5 ) ) ;
graphics . setFontAlign ( 1 , - 1 ) ;
2022-11-02 17:02:47 +00:00
graphics . drawString ( lastValue2 , x + width , y + ( height * 0.5 ) ) ;
2022-09-21 19:33:14 +00:00
}
} ;
2022-10-13 17:43:56 +00:00
} ;
2022-09-21 19:33:14 +00:00
2023-05-19 15:29:22 +00:00
const dot = Graphics . createImage ( `
XX
XX
` );
2023-05-16 17:48:30 +00:00
const arrow = Graphics . createImage ( `
2023-05-20 20:48:10 +00:00
X
2023-05-16 17:48:30 +00:00
XXX
2023-05-20 20:48:10 +00:00
XXXXX
2023-05-16 17:48:30 +00:00
XXX XXX
2023-05-20 20:48:10 +00:00
XXX XXX
2023-05-16 17:48:30 +00:00
XXX XXX
` );
const cross = Graphics . createImage ( `
2023-05-22 06:22:23 +00:00
XX XX
XX XX
2023-05-16 17:48:30 +00:00
XX XX
2023-05-22 06:22:23 +00:00
XX XX
2023-05-22 06:24:32 +00:00
XXX
2023-05-22 06:22:23 +00:00
XX XX
2023-05-16 17:48:30 +00:00
XX XX
2023-05-22 06:22:23 +00:00
XX XX
XX XX
2023-05-16 17:48:30 +00:00
` );
2023-06-10 12:51:14 +00:00
const move = Graphics . createImage ( `
X
XXX
X X X
X
X
X X X
X X X
XXXXXXXXXXXXX
X X X
X X X
X
X
X X X
XXX
X
` );
2023-05-16 17:48:30 +00:00
const point = Graphics . createImage ( `
2023-05-20 20:48:10 +00:00
XX
XXXX
XX XX
XX XX
XX XX
XX XX
XXXX
XX
2023-05-16 17:48:30 +00:00
` );
2023-05-20 09:52:37 +00:00
let isGpsCourse = function ( ) {
2023-05-27 21:54:24 +00:00
return WIDGETS . gpstrek . getState ( ) . currentPos && isFinite ( WIDGETS . gpstrek . getState ( ) . currentPos . course ) ;
2023-05-20 09:52:37 +00:00
} ;
2023-05-20 13:13:02 +00:00
let isMapOverview = false ;
2023-05-22 13:49:34 +00:00
let forceMapRedraw = false ;
2023-05-20 13:13:02 +00:00
2023-05-22 08:53:52 +00:00
let activeTimeouts = [ ] ;
2023-05-25 20:11:35 +00:00
let taskQueue = [ ] ;
2023-05-22 08:53:52 +00:00
let queueProcessing = false ;
2023-05-25 20:11:35 +00:00
let addToTaskQueue = function ( func , data ) {
2023-05-25 18:19:46 +00:00
if ( queueProcessing ) print ( "Adding during processing, this should not happen" ) ;
2023-05-25 20:11:35 +00:00
taskQueue . push ( { f : func , d : data } ) ;
2023-05-22 08:53:52 +00:00
} ;
2023-05-25 20:11:35 +00:00
let prependTaskQueue = function ( func , data , force ) {
2023-05-25 18:19:46 +00:00
if ( queueProcessing && ! force ) print ( "Prepending during processing, this should not happen" ) ;
2023-05-25 20:11:35 +00:00
taskQueue . unshift ( { f : func , d : data } ) ;
2023-05-22 08:53:52 +00:00
} ;
2023-05-25 20:11:35 +00:00
let runQueue = function ( inTimeouts ) {
if ( taskQueue . length > 0 ) {
let current = taskQueue . shift ( ) ;
if ( inTimeouts ) {
let id = setTimeout ( ( ) => {
current . f ( current . d ) ;
activeTimeouts = activeTimeouts . filter ( ( c ) => c != id ) ;
2023-05-27 11:15:51 +00:00
runQueue ( inTimeouts ) ;
2023-05-27 21:54:24 +00:00
} , SETTINGS . queueWaitingTime ) ;
2023-05-25 20:11:35 +00:00
activeTimeouts . push ( id ) ;
} else {
2023-05-22 08:53:52 +00:00
current . f ( current . d ) ;
2023-05-27 11:15:51 +00:00
runQueue ( inTimeouts ) ;
2023-05-25 20:11:35 +00:00
}
2023-05-22 08:53:52 +00:00
}
} ;
2023-05-25 20:11:35 +00:00
let processTaskQueue = function ( inTimeouts ) {
2023-05-24 08:51:55 +00:00
if ( queueProcessing ) return ;
2023-05-25 20:11:35 +00:00
addToTaskQueue ( ( ) => { queueProcessing = false ; } ) ;
2023-05-24 08:51:55 +00:00
queueProcessing = true ;
2023-05-25 20:11:35 +00:00
runQueue ( inTimeouts ) ;
2023-05-22 14:40:30 +00:00
} ;
2023-05-27 11:52:49 +00:00
let clearTaskQueue = function ( ) {
2023-05-25 20:11:35 +00:00
taskQueue = [ ] ;
2023-05-22 08:53:52 +00:00
for ( let c of activeTimeouts ) {
clearTimeout ( c ) ;
}
queueProcessing = false ;
} ;
2023-05-16 16:15:52 +00:00
let getMapSlice = function ( ) {
2023-05-20 09:52:37 +00:00
let lastDrawn = 0 ;
2023-05-20 10:54:02 +00:00
let lastCourse = 0 ;
let lastStart ;
let lastCurrent ;
2023-05-14 13:37:54 +00:00
return {
2023-05-18 18:20:55 +00:00
draw : function ( graphics , x , y , height , width ) {
2023-05-20 11:29:18 +00:00
let s = WIDGETS . gpstrek . getState ( ) ;
2023-05-20 14:12:54 +00:00
2023-05-20 13:13:02 +00:00
let course = 0 ;
2023-06-11 10:03:49 +00:00
if ( isGpsCourse ( ) )
course = s . currentPos . course ;
else
course = getAveragedCompass ( ) ;
2023-05-14 14:43:42 +00:00
2023-05-20 11:29:18 +00:00
let route = s . route ;
2023-05-18 18:20:55 +00:00
if ( ! route ) return ;
2023-06-08 12:59:26 +00:00
let waypoint = get ( route ) ;
2023-06-08 19:52:50 +00:00
let currentRouteIndex = getWaypointIndex ( route ) ;
2023-05-25 20:11:35 +00:00
let startingPoint = Bangle . project ( waypoint ) ;
2023-05-20 08:52:31 +00:00
let current = startingPoint ;
2023-05-27 21:54:24 +00:00
let currentPosFromGPS = false ;
2023-05-21 14:04:48 +00:00
if ( s . currentPos . lat ) {
current = Bangle . project ( s . currentPos ) ;
2023-05-27 21:54:24 +00:00
currentPosFromGPS = true ;
2023-05-25 20:11:35 +00:00
} else {
// in case of no actual position assume previous waypoint as current
2023-05-27 11:17:23 +00:00
let prevPoint = getPrev ( route , currentRouteIndex ) ;
2023-05-25 20:11:35 +00:00
if ( prevPoint && prevPoint . lat ) current = Bangle . project ( prevPoint ) ;
2023-05-21 14:04:48 +00:00
}
2023-06-10 11:20:49 +00:00
const interfaceHeight = g . getHeight ( ) * 0.1 ;
2023-05-20 09:52:37 +00:00
const errorMarkerSize = 3 ;
2023-05-17 21:47:57 +00:00
let compassHeight = height * 0.4 ;
2023-05-19 17:05:40 +00:00
if ( ! SETTINGS . mapCompass ) compassHeight = 0 ;
2023-05-17 21:47:57 +00:00
if ( compassHeight > g . getHeight ( ) * 0.1 ) compassHeight = g . getHeight ( ) * 0.1 ;
2023-06-10 12:51:14 +00:00
let compassCenterX = x + errorMarkerSize + 8 + compassHeight ;
let compassCenterY = y + errorMarkerSize + 8 + compassHeight ;
2023-05-24 19:08:39 +00:00
let mapCenterX = isMapOverview ? mapOverviewX : x + ( width - 10 ) / 2 + compassHeight + 5 ;
2023-05-25 20:11:35 +00:00
let mapCenterY = isMapOverview ? mapOverviewY : y + height * 0.4 ;
2023-05-25 15:59:58 +00:00
let mapScale = isMapOverview ? mapOverviewScale : mapLiveScale ;
2023-06-11 10:03:49 +00:00
let mapRot = require ( "graphics_utils" ) . degreesToRadians ( 180 - ( isMapOverview ? 0 : course ) ) ;
2023-05-25 15:59:58 +00:00
let mapTrans = {
scale : mapScale ,
rotate : mapRot ,
x : mapCenterX ,
y : mapCenterY
} ;
2023-05-20 09:52:37 +00:00
2023-05-24 09:02:08 +00:00
let drawInterface = function ( ) {
2023-06-10 11:20:49 +00:00
2023-05-24 09:02:08 +00:00
graphics . setClipRect ( x , y , x + width , y + height ) ;
graphics . setFont ( "Vector" , 25 ) . setFontAlign ( 0 , 0 ) ;
graphics . setColor ( graphics . theme . fg ) ;
2023-06-10 11:20:49 +00:00
graphics . clearRect ( x , y + height - interfaceHeight , x + width , y + height - 1 ) ;
graphics . drawRect ( x , y + height - interfaceHeight , x + width / 4 , y + height - 1 ) ;
graphics . drawString ( "-" , x + width * 0.125 , y + height - interfaceHeight * 0.5 ) ;
2023-05-24 09:02:08 +00:00
2023-06-10 11:20:49 +00:00
graphics . drawRect ( x + width * 0.75 , y + height - interfaceHeight , x + width - 1 , y + height - 1 ) ;
graphics . drawString ( "+" , x + width * 0.875 , y + height - interfaceHeight * 0.5 ) ;
2023-05-24 09:02:08 +00:00
2023-06-10 16:28:07 +00:00
let refs = [ 10 , 20 , 50 , 100 , 200 , 300 , 400 , 500 , 800 , 1000 , 2000 , 5000 , 10000 , 50000 ] ;
2023-05-24 09:02:08 +00:00
let l = width * 0.4 ;
2023-06-10 16:28:07 +00:00
let scale = refs [ 0 ] ;
2023-05-24 09:02:08 +00:00
for ( let c of refs ) {
if ( c * mapScale > l )
break ;
else
scale = c ;
}
2023-06-10 16:28:07 +00:00
let scaleString = loc . distance ( scale , 2 ) ;
2023-06-10 11:20:49 +00:00
let scaleHeight = interfaceHeight * 0.2 ;
graphics . setFontAlign ( - 1 , - 1 ) . setFont ( "Vector" , 12 ) ;
2023-06-10 16:28:07 +00:00
graphics . drawString ( scaleString , x + width * 0.31 , y + height - interfaceHeight , true ) ;
2023-05-27 21:54:24 +00:00
if ( isFinite ( scale ) ) {
2023-06-10 11:20:49 +00:00
graphics . drawLine ( x + width * 0.3 , y + height - scaleHeight , x + width * 0.3 + scale * mapScale , y + height - scaleHeight ) ;
graphics . drawLine ( x + width * 0.3 , y + height - scaleHeight , x + width * 0.3 , y + height - interfaceHeight ) ;
graphics . drawLine ( x + width * 0.3 + scale * mapScale , y + height - scaleHeight , x + width * 0.3 + scale * mapScale , y + height - interfaceHeight ) ;
2023-05-24 09:02:08 +00:00
}
} ;
let drawMapCompass = function ( ) {
2023-06-10 11:20:49 +00:00
graphics . setClipRect ( x , y , x + width , y + height - interfaceHeight - 1 ) ;
2023-05-24 09:02:08 +00:00
graphics . setFont6x15 ( ) ;
let compass = [ 0 , 0 , 0 , compassHeight , 0 , - compassHeight , compassHeight , 0 , - compassHeight , 0 ] ;
compass = graphics . transformVertices ( compass , {
rotate : require ( "graphics_utils" ) . degreesToRadians ( 180 - course ) ,
x : compassCenterX ,
y : compassCenterY
} ) ;
graphics . setFontAlign ( 0 , 0 ) ;
graphics . setColor ( graphics . theme . bg ) ;
graphics . fillCircle ( compassCenterX , compassCenterY , compassHeight + 7 ) ;
graphics . setColor ( graphics . theme . fg ) ;
graphics . drawCircle ( compassCenterX , compassCenterY , compassHeight ) ;
graphics . drawString ( "N" , compass [ 2 ] , compass [ 3 ] , true ) ;
graphics . drawString ( "S" , compass [ 4 ] , compass [ 5 ] , true ) ;
graphics . drawString ( "W" , compass [ 6 ] , compass [ 7 ] , true ) ;
graphics . drawString ( "E" , compass [ 8 ] , compass [ 9 ] , true ) ;
if ( ! isGpsCourse ( ) && ! isMapOverview ) {
let xh = E . clip ( s . acc . x * compassHeight , - compassHeight , compassHeight ) ;
let yh = E . clip ( s . acc . y * compassHeight , - compassHeight , compassHeight ) ;
graphics . fillCircle ( compassCenterX + xh , compassCenterY + yh , 3 ) ;
} else if ( isMapOverview ) {
graphics . setColor ( 0 , 0 , 1 ) ;
graphics . fillCircle ( compassCenterX , compassCenterY , 3 ) ;
}
} ;
2023-05-24 19:08:39 +00:00
let drawCurrentPos = function ( ) {
2023-06-10 11:20:49 +00:00
graphics . setClipRect ( x , y , x + width , y + height - interfaceHeight - 1 ) ;
2023-05-24 19:08:39 +00:00
graphics . setColor ( graphics . theme . fg ) ;
2023-05-27 21:54:24 +00:00
if ( currentPosFromGPS ) {
let pos = graphics . transformVertices ( [ startingPoint . x - current . x , ( startingPoint . y - current . y ) * - 1 ] , mapTrans ) ;
2023-05-24 19:08:39 +00:00
2023-06-11 12:56:09 +00:00
if ( ! isMapOverview ) {
if ( pos [ 0 ] < x ) { pos [ 0 ] = x + errorMarkerSize + 5 ; graphics . setColor ( 1 , 0 , 0 ) . fillRect ( x , y , x + errorMarkerSize , y + height ) ; }
if ( pos [ 0 ] > x + width ) { pos [ 0 ] = x + width - errorMarkerSize - 5 ; graphics . setColor ( 1 , 0 , 0 ) . fillRect ( x + width - errorMarkerSize , y , x + width , y + height ) ; }
if ( pos [ 1 ] < y ) { pos [ 1 ] = y + errorMarkerSize + 5 ; graphics . setColor ( 1 , 0 , 0 ) . fillRect ( x , y , x + width , y + errorMarkerSize ) ; }
if ( pos [ 1 ] > y + height - interfaceHeight - 1 ) { pos [ 1 ] = y + height - errorMarkerSize - 5 - interfaceHeight - 1 ; graphics . setColor ( 1 , 0 , 0 ) . fillRect ( x , y + height - errorMarkerSize - interfaceHeight - 1 , x + width , y + height - interfaceHeight - 1 ) ; }
}
2023-05-24 19:08:39 +00:00
2023-06-11 10:03:49 +00:00
if ( isMapOverview ) {
graphics . drawImage ( arrow , pos [ 0 ] , pos [ 1 ] , { rotate : require ( "graphics_utils" ) . degreesToRadians ( course ) } ) ;
} else {
graphics . drawImage ( arrow , pos [ 0 ] - arrow . width / 2 , pos [ 1 ] ) ;
}
2023-05-24 19:08:39 +00:00
graphics . setColor ( 0 , 1 , 0 ) ;
graphics . fillRect ( mapCenterX - 1 , mapCenterY - 1 , mapCenterX + 1 , mapCenterY + 1 ) ;
graphics . drawCircle ( mapCenterX , mapCenterY , mapScale * SETTINGS . waypointChangeDist ) ;
graphics . setColor ( graphics . theme . fg ) ;
} else {
graphics . setColor ( 0 , 1 , 0 ) ;
graphics . fillCircle ( mapCenterX , mapCenterY , 5 ) ;
graphics . setColor ( graphics . theme . fg ) ;
}
} ;
2023-05-27 21:54:24 +00:00
let courseChanged = Math . abs ( lastCourse - course ) > SETTINGS . minCourseChange ;
let oldEnough = Date . now ( ) - lastDrawn > SETTINGS . mapRefresh ;
let startChanged = ( ! lastStart || lastStart . x != startingPoint . x || lastStart . y != startingPoint . y ) ;
let neededChange = ( SETTINGS . minPosChange / ( isMapOverview ? mapOverviewScale : mapLiveScale ) ) ;
let currentChanged = ( ! lastCurrent || ( Math . abs ( lastCurrent . x - current . x ) ) > neededChange || ( Math . abs ( lastCurrent . y - current . y ) ) > neededChange ) ;
let liveChanged = ! isMapOverview && ( startChanged || currentChanged || courseChanged ) ;
let refreshMap = forceMapRedraw || ( oldEnough && ( liveChanged ) ) ;
2023-05-24 08:51:55 +00:00
2023-05-27 21:54:24 +00:00
let renderInTimeouts = isMapOverview || ! Bangle . isLocked ( ) ;
2023-05-24 08:51:55 +00:00
if ( refreshMap ) {
2023-05-27 11:52:49 +00:00
clearTaskQueue ( ) ;
2023-05-20 09:52:37 +00:00
2023-05-27 21:54:24 +00:00
//clear map view
2023-06-10 11:20:49 +00:00
graphics . clearRect ( x , y , x + width , y + height - interfaceHeight - 1 ) ;
2023-05-24 09:02:08 +00:00
2023-06-10 12:51:14 +00:00
if ( isMapOverview && scrolling ) {
addToTaskQueue ( ( ) => {
graphics . setColor ( graphics . theme . fg ) ;
graphics . drawImage ( move , compassCenterX - move . width / 2 , compassCenterY - move . height / 2 ) ;
} ) ;
}
2023-05-27 21:54:24 +00:00
if ( ! isMapOverview ) {
2023-05-24 19:08:39 +00:00
drawCurrentPos ( ) ;
2023-05-27 21:54:24 +00:00
}
if ( ! isMapOverview && renderInTimeouts ) {
drawMapCompass ( ) ;
}
if ( renderInTimeouts ) drawInterface ( ) ;
2023-05-24 08:51:55 +00:00
2023-05-27 11:52:49 +00:00
forceMapRedraw = false ;
lastDrawn = Date . now ( ) ;
2023-05-27 21:54:24 +00:00
let drawPath = function ( reverse , startingIndex , maxWaypoints ) {
2023-05-22 08:53:52 +00:00
let data = {
2023-05-27 21:54:24 +00:00
i : startingIndex ,
poly : [ ] ,
maxWaypoints : maxWaypoints ,
2023-05-22 08:53:52 +00:00
breakLoop : false
} ;
2023-05-20 09:52:37 +00:00
2023-05-22 08:53:52 +00:00
let drawChunk = function ( data ) {
if ( data . breakLoop ) return ;
2023-05-25 16:03:07 +00:00
graphics . setColor ( graphics . theme . fg ) ;
2023-06-10 11:20:49 +00:00
graphics . setClipRect ( x , y , x + width , y + height - interfaceHeight - 1 ) ;
2023-05-22 08:53:52 +00:00
let finish ;
2023-05-27 21:54:24 +00:00
let last ;
2023-05-22 08:53:52 +00:00
let toDraw ;
2023-05-20 09:52:37 +00:00
let named = [ ] ;
2023-05-20 11:29:18 +00:00
for ( let j = 0 ; j < SETTINGS . mapChunkSize ; j ++ ) {
2023-06-11 12:56:09 +00:00
data . i = data . i + ( reverse ? - 1 : 1 ) ;
2023-06-08 19:52:50 +00:00
let p = get ( route , data . i ) ;
2023-05-20 09:52:37 +00:00
if ( ! p || ! p . lat ) {
2023-06-11 12:56:09 +00:00
data . i = data . i + ( reverse ? 1 : - 1 ) ;
2023-05-22 08:53:52 +00:00
data . breakLoop = true ;
2023-05-20 09:52:37 +00:00
break ;
}
2023-06-11 12:56:09 +00:00
if ( data . maxWaypoints && Math . abs ( startingIndex - data . i ) > data . maxWaypoints ) {
2023-05-27 21:54:24 +00:00
data . breakLoop = true ;
last = true ;
break ;
}
2023-05-20 09:52:37 +00:00
toDraw = Bangle . project ( p ) ;
2023-05-22 08:53:52 +00:00
if ( p . name ) named . push ( { i : data . poly . length , n : p . name } ) ;
data . poly . push ( startingPoint . x - toDraw . x ) ;
data . poly . push ( ( startingPoint . y - toDraw . y ) * - 1 ) ;
2023-05-19 17:05:40 +00:00
}
2023-06-11 12:56:09 +00:00
finish = isLast ( route , getWaypointIndex ( route , data . i ) ) ;
2023-05-19 15:29:22 +00:00
2023-05-22 08:53:52 +00:00
data . poly = graphics . transformVertices ( data . poly , mapTrans ) ;
graphics . drawPoly ( data . poly , false ) ;
2023-05-20 05:31:31 +00:00
2023-05-22 08:53:52 +00:00
if ( ! isMapOverview && ( data . poly [ data . poly . length - 2 ] < ( x - 10 )
|| data . poly [ data . poly . length - 2 ] > ( x + width + 10 )
|| data . poly [ data . poly . length - 1 ] < ( y - 10 )
|| data . poly [ data . poly . length - 1 ] > ( y + height + 10 ) ) ) data . breakLoop = true ;
2023-05-20 10:28:02 +00:00
2023-05-25 20:55:12 +00:00
graphics . setFont6x15 ( ) . setFontAlign ( - 1 , 0 ) ;
2023-05-20 09:52:37 +00:00
for ( let c of named ) {
2023-05-27 21:54:24 +00:00
if ( data . i != 0 || currentPosFromGPS ) {
2023-06-10 09:11:56 +00:00
graphics . drawImage ( point , Math . round ( data . poly [ c . i ] - point . width / 2 ) , Math . round ( data . poly [ c . i + 1 ] - point . height / 2 ) ) ;
2023-05-20 09:52:37 +00:00
}
2023-05-25 20:55:12 +00:00
graphics . drawString ( c . n , data . poly [ c . i ] + 10 , data . poly [ c . i + 1 ] ) ;
2023-05-20 09:52:37 +00:00
}
2023-05-27 21:54:24 +00:00
if ( finish )
graphics . drawImage ( finishIcon , data . poly [ data . poly . length - 2 ] - 5 , data . poly [ data . poly . length - 1 ] - 4 ) ;
2023-06-10 16:29:14 +00:00
else if ( last ) {
2023-05-27 21:54:24 +00:00
graphics . drawImage ( cross , data . poly [ data . poly . length - 2 ] - cross . width / 2 , data . poly [ data . poly . length - 1 ] - cross . height / 2 ) ;
2023-05-20 09:52:37 +00:00
}
2023-05-18 18:20:55 +00:00
2023-05-20 09:52:37 +00:00
//Add last drawn point to get closed path
if ( toDraw ) {
2023-05-22 08:53:52 +00:00
data . poly = [ startingPoint . x - toDraw . x , ( startingPoint . y - toDraw . y ) * - 1 ] ;
2023-05-20 09:52:37 +00:00
toDraw = null ;
}
2023-05-17 20:56:06 +00:00
2023-05-25 18:19:46 +00:00
if ( ! data . breakLoop ) {
2023-05-25 20:11:35 +00:00
prependTaskQueue ( drawChunk , data , true ) ;
2023-05-25 18:19:46 +00:00
}
2023-05-22 08:53:52 +00:00
} ;
2023-05-25 20:11:35 +00:00
addToTaskQueue ( drawChunk , data ) ;
2023-05-20 09:52:37 +00:00
} ;
2023-05-19 15:29:22 +00:00
2023-06-11 12:56:09 +00:00
drawPath ( true , currentRouteIndex + 1 ) ;
drawPath ( false , currentRouteIndex - 1 ) ;
2023-05-25 18:19:46 +00:00
2023-05-25 20:11:35 +00:00
addToTaskQueue ( drawInterface ) ;
2023-06-08 20:40:52 +00:00
addToTaskQueue ( drawCurrentPos ) ;
2023-05-27 21:54:24 +00:00
addToTaskQueue ( ( ) => {
lastCourse = course ;
lastStart = startingPoint ;
lastCurrent = current ;
} )
processTaskQueue ( renderInTimeouts ) ;
}
if ( SETTINGS . mapCompass && ! isMapOverview ) {
drawMapCompass ( ) ;
2023-05-20 09:52:37 +00:00
}
2023-05-14 13:37:54 +00:00
}
} ;
} ;
2022-10-13 17:43:56 +00:00
let getTargetSlice = function ( targetDataSource ) {
2022-09-21 19:33:14 +00:00
let nameIndex = 0 ;
return {
draw : function ( graphics , x , y , height , width ) {
graphics . clearRect ( x , y , x + width , y + height ) ;
if ( targetDataSource . icon ) {
graphics . drawImage ( targetDataSource . icon , x , y + ( height - 16 ) / 2 ) ;
x += 16 ;
width -= 16 ;
}
2023-05-12 08:19:23 +00:00
let start = targetDataSource . getStart ( ) ;
let target = targetDataSource . getTarget ( ) ;
2022-09-21 19:33:14 +00:00
2023-05-12 08:19:23 +00:00
if ( ! target || ! start ) return ;
let dist = distance ( start , target ) ;
2022-09-21 19:33:14 +00:00
if ( isNaN ( dist ) ) dist = Infinity ;
2023-05-27 11:34:30 +00:00
let bearingString = bearing ( start , target ) + "°" ;
2023-05-12 08:19:23 +00:00
if ( target . name ) {
2022-09-21 19:33:14 +00:00
graphics . setFont ( "Vector" , Math . floor ( height * 0.5 ) ) ;
2023-05-12 08:19:23 +00:00
let scrolledName = ( target . name || "" ) . substring ( nameIndex ) ;
2022-09-21 19:33:14 +00:00
if ( graphics . stringMetrics ( scrolledName ) . width > width ) {
nameIndex ++ ;
} else {
nameIndex = 0 ;
}
graphics . drawString ( scrolledName , x + 2 , y ) ;
let distanceString = loc . distance ( dist , 2 ) ;
matchFontSize ( graphics , distanceString + bearingString , height * 0.5 , width ) ;
graphics . drawString ( bearingString , x + 2 , y + ( height * 0.5 ) ) ;
graphics . setFontAlign ( 1 , - 1 ) ;
graphics . drawString ( distanceString , x + width , y + ( height * 0.5 ) ) ;
} else {
graphics . setFont ( "Vector" , Math . floor ( height * 1 ) ) ;
2023-05-27 11:34:30 +00:00
let bearingString = bearing ( start , target ) + "°" ;
2022-09-21 19:33:14 +00:00
let formattedDist = loc . distance ( dist , 2 ) ;
let distNum = ( formattedDist . match ( /[0-9\.]+/ ) || [ Infinity ] ) [ 0 ] ;
let size = 0.8 ;
let distNumMetrics ;
while ( graphics . stringMetrics ( bearingString ) . width + ( distNumMetrics = graphics . stringMetrics ( distNum ) ) . width > 0.90 * width ) {
size -= 0.05 ;
graphics . setFont ( "Vector" , Math . floor ( height * size ) ) ;
}
graphics . drawString ( bearingString , x + 2 , y + ( height - distNumMetrics . height ) / 2 ) ;
graphics . setFontAlign ( 1 , - 1 ) ;
graphics . drawString ( distNum , x + width , y + ( height - distNumMetrics . height ) / 2 ) ;
graphics . setFont ( "Vector" , Math . floor ( height * 0.25 ) ) ;
graphics . setFontAlign ( - 1 , 1 ) ;
if ( targetDataSource . getProgress ) {
graphics . drawString ( targetDataSource . getProgress ( ) , x + 2 , y + height ) ;
}
graphics . setFontAlign ( 1 , 1 ) ;
2023-05-27 21:54:24 +00:00
if ( isFinite ( distNum ) && distNum != Infinity )
2022-09-21 19:33:14 +00:00
graphics . drawString ( formattedDist . match ( /[a-zA-Z]+/ ) , x + width , y + height ) ;
}
}
} ;
2022-10-13 17:43:56 +00:00
} ;
2022-09-21 19:33:14 +00:00
2022-10-13 17:43:56 +00:00
let drawCompass = function ( graphics , x , y , height , width , increment , start ) {
2022-09-21 19:33:14 +00:00
graphics . setFont12x20 ( ) ;
graphics . setFontAlign ( 0 , - 1 ) ;
graphics . setColor ( graphics . theme . fg ) ;
let frag = 0 - start % 15 ;
if ( frag > 0 ) frag = 0 ;
let xpos = 0 + frag * increment ;
for ( let i = start ; i <= 720 ; i += 15 ) {
2022-10-21 15:04:53 +00:00
var res = i + frag ;
2022-09-21 19:33:14 +00:00
if ( res % 90 == 0 ) {
2023-05-19 15:56:54 +00:00
graphics . drawString ( labels [ Math . floor ( res / 45 ) % 8 ] , xpos , y + height * 0.1 ) ;
graphics . fillRect ( xpos - 1 , Math . floor ( y + height * 0.8 ) , xpos + 1 , Math . floor ( y + height ) ) ;
2022-09-21 19:33:14 +00:00
} else if ( res % 45 == 0 ) {
2023-05-19 15:56:54 +00:00
graphics . drawString ( labels [ Math . floor ( res / 45 ) % 8 ] , xpos , y + height * 0.1 ) ;
graphics . fillRect ( xpos - 1 , Math . floor ( y + height * 0.7 ) , xpos + 1 , Math . floor ( y + height ) ) ;
2022-09-21 19:33:14 +00:00
} else if ( res % 15 == 0 ) {
graphics . fillRect ( xpos , Math . floor ( y + height * 0.9 ) , xpos + 1 , Math . floor ( y + height ) ) ;
}
xpos += increment * 15 ;
if ( xpos > width + 20 ) break ;
}
2022-10-13 17:43:56 +00:00
} ;
2022-09-21 19:33:14 +00:00
2023-05-18 14:50:45 +00:00
let getCompassSlice = function ( ) {
let compassDataSource = {
getCourse : function ( ) {
2023-05-20 11:29:18 +00:00
if ( isGpsCourse ( ) ) return WIDGETS . gpstrek . getState ( ) . currentPos . course ;
2023-05-18 14:50:45 +00:00
return getAveragedCompass ( ) ;
} ,
getPoints : function ( ) {
let points = [ ] ;
let s = WIDGETS . gpstrek . getState ( ) ;
if ( s . currentPos && s . currentPos . lon && s . route ) {
points . push ( { bearing : bearing ( s . currentPos , getLast ( s . route ) ) , icon : finishIcon } ) ;
}
if ( s . currentPos && s . currentPos . lon && s . waypoint ) {
points . push ( { bearing : bearing ( s . currentPos , s . waypoint ) , icon : finishIcon } ) ;
}
2023-06-08 12:59:26 +00:00
if ( s . currentPos && s . currentPos . lon && s . route ) {
points . push ( { bearing : bearing ( s . currentPos , get ( s . route ) ) , color : "#0f0" } ) ;
2023-05-18 14:50:45 +00:00
}
return points ;
} ,
getMarkers : function ( ) {
return [ { xpos : 0.5 , width : 10 , height : 10 , linecolor : g . theme . fg , fillcolor : "#f00" } ] ;
}
} ;
2022-11-02 17:02:47 +00:00
let lastDrawnValue = 0 ;
2022-09-21 19:33:14 +00:00
const buffers = 4 ;
let buf = [ ] ;
return {
draw : function ( graphics , x , y , height , width ) {
const max = 180 ;
const increment = width / max ;
2023-05-20 11:29:18 +00:00
let s = WIDGETS . gpstrek . getState ( ) ;
2022-09-21 19:33:14 +00:00
2023-05-20 11:29:18 +00:00
let course = isGpsCourse ( ) ? s . currentPos . course : getAveragedCompass ( ) ;
2022-11-02 17:02:47 +00:00
2023-05-20 11:29:18 +00:00
if ( Math . abs ( lastDrawnValue - compassDataSource . getCourse ( ) ) < SETTINGS . minCourseChange ) return ;
lastDrawnValue = course ;
graphics . clearRect ( x , y , x + width , y + height ) ;
var start = course - 90 ;
if ( isNaN ( course ) ) start = - 90 ;
2022-09-21 19:33:14 +00:00
if ( start < 0 ) start += 360 ;
start = start % 360 ;
2023-05-20 11:29:18 +00:00
if ( s . acc && ! isGpsCourse ( ) ) {
2022-09-21 19:33:14 +00:00
drawCompass ( graphics , 0 , y + width * 0.05 , height - width * 0.05 , width , increment , start ) ;
} else {
drawCompass ( graphics , 0 , y , height , width , increment , start ) ;
}
if ( compassDataSource . getPoints ) {
2022-12-04 20:13:10 +00:00
let points = compassDataSource . getPoints ( ) ; //storing this in a variable works around a minifier bug causing a problem in the next line: for(let a of a.getPoints())
for ( let p of points ) {
2022-11-05 20:45:51 +00:00
g . reset ( ) ;
2023-05-20 11:29:18 +00:00
var bpos = p . bearing - course ;
2022-09-21 19:33:14 +00:00
if ( bpos > 180 ) bpos -= 360 ;
if ( bpos < - 180 ) bpos += 360 ;
bpos += 120 ;
let min = 0 ;
let max = 180 ;
if ( bpos <= min ) {
bpos = Math . floor ( width * 0.05 ) ;
} else if ( bpos >= max ) {
bpos = Math . ceil ( width * 0.95 ) ;
} else {
bpos = Math . round ( bpos * increment ) ;
}
2022-11-03 20:24:51 +00:00
if ( p . color ) {
graphics . setColor ( p . color ) ;
}
if ( p . icon ) {
graphics . drawImage ( p . icon , bpos , y + height - 12 , { rotate : 0 , scale : 2 } ) ;
} else {
graphics . fillCircle ( bpos , y + height - 12 , Math . floor ( width * 0.03 ) ) ;
}
2022-09-21 19:33:14 +00:00
}
}
if ( compassDataSource . getMarkers ) {
2022-12-04 20:13:10 +00:00
let markers = compassDataSource . getMarkers ( ) ; //storing this in a variable works around a minifier bug causing a problem in the next line: for(let a of a.getMarkers())
for ( let m of markers ) {
2022-11-05 20:45:51 +00:00
g . reset ( ) ;
2022-09-21 19:33:14 +00:00
g . setColor ( m . fillcolor ) ;
let mpos = m . xpos * width ;
if ( m . xpos < 0.05 ) mpos = Math . floor ( width * 0.05 ) ;
if ( m . xpos > 0.95 ) mpos = Math . ceil ( width * 0.95 ) ;
g . fillPoly ( triangle ( mpos , y + height - m . height , m . height , m . width ) ) ;
g . setColor ( m . linecolor ) ;
g . drawPoly ( triangle ( mpos , y + height - m . height , m . height , m . width ) , true ) ;
}
}
graphics . setColor ( g . theme . fg ) ;
graphics . fillRect ( x , y , Math . floor ( width * 0.05 ) , y + height ) ;
graphics . fillRect ( Math . ceil ( width * 0.95 ) , y , width , y + height ) ;
2023-05-20 11:29:18 +00:00
if ( s . acc && ! isGpsCourse ( ) ) {
let xh = E . clip ( width * 0.5 - height / 2 + ( ( ( s . acc . x + 1 ) / 2 ) * height ) , width * 0.5 - height / 2 , width * 0.5 + height / 2 ) ;
let yh = E . clip ( y + ( ( ( s . acc . y + 1 ) / 2 ) * height ) , y , y + height ) ;
2022-09-21 19:33:14 +00:00
graphics . fillRect ( width * 0.5 - height / 2 , y , width * 0.5 + height / 2 , y + Math . floor ( width * 0.05 ) ) ;
graphics . setColor ( g . theme . bg ) ;
graphics . drawLine ( width * 0.5 - 5 , y , width * 0.5 - 5 , y + Math . floor ( width * 0.05 ) ) ;
graphics . drawLine ( width * 0.5 + 5 , y , width * 0.5 + 5 , y + Math . floor ( width * 0.05 ) ) ;
graphics . fillRect ( xh - 1 , y , xh + 1 , y + Math . floor ( width * 0.05 ) ) ;
let left = Math . floor ( width * 0.05 ) ;
let right = Math . ceil ( width * 0.95 ) ;
graphics . drawLine ( 0 , y + height / 2 - 5 , left , y + height / 2 - 5 ) ;
graphics . drawLine ( right , y + height / 2 - 5 , x + width , y + height / 2 - 5 ) ;
graphics . drawLine ( 0 , y + height / 2 + 5 , left , y + height / 2 + 5 ) ;
graphics . drawLine ( right , y + height / 2 + 5 , x + width , y + height / 2 + 5 ) ;
graphics . fillRect ( 0 , yh - 1 , left , yh + 1 ) ;
graphics . fillRect ( right , yh - 1 , x + width , yh + 1 ) ;
}
graphics . setColor ( g . theme . fg ) ;
graphics . drawRect ( Math . floor ( width * 0.05 ) , y , Math . ceil ( width * 0.95 ) , y + height ) ;
}
} ;
2022-10-13 17:43:56 +00:00
} ;
2022-09-21 19:33:14 +00:00
2022-10-13 17:43:56 +00:00
let radians = function ( a ) {
2022-09-21 19:33:14 +00:00
return a * Math . PI / 180 ;
2022-10-13 17:43:56 +00:00
} ;
2022-09-21 19:33:14 +00:00
2022-10-13 17:43:56 +00:00
let degrees = function ( a ) {
let d = a * 180 / Math . PI ;
2022-09-21 19:33:14 +00:00
return ( d + 360 ) % 360 ;
2022-10-13 17:43:56 +00:00
} ;
2022-09-21 19:33:14 +00:00
2022-10-13 17:43:56 +00:00
let bearing = function ( a , b ) {
2022-09-21 19:33:14 +00:00
if ( ! a || ! b || ! a . lon || ! a . lat || ! b . lon || ! b . lat ) return Infinity ;
2022-10-13 17:43:56 +00:00
let delta = radians ( b . lon - a . lon ) ;
let alat = radians ( a . lat ) ;
let blat = radians ( b . lat ) ;
let y = Math . sin ( delta ) * Math . cos ( blat ) ;
let x = Math . cos ( alat ) * Math . sin ( blat ) -
2022-09-21 19:33:14 +00:00
Math . sin ( alat ) * Math . cos ( blat ) * Math . cos ( delta ) ;
return Math . round ( degrees ( Math . atan2 ( y , x ) ) ) ;
2022-10-13 17:43:56 +00:00
} ;
2022-09-21 19:33:14 +00:00
2022-10-13 17:43:56 +00:00
let distance = function ( a , b ) {
2022-09-21 19:33:14 +00:00
if ( ! a || ! b || ! a . lon || ! a . lat || ! b . lon || ! b . lat ) return Infinity ;
2022-10-13 17:43:56 +00:00
let x = radians ( a . lon - b . lon ) * Math . cos ( radians ( ( a . lat + b . lat ) / 2 ) ) ;
let y = radians ( b . lat - a . lat ) ;
2022-09-21 19:33:14 +00:00
return Math . round ( Math . sqrt ( x * x + y * y ) * 6371000 ) ;
2022-10-13 17:43:56 +00:00
} ;
2022-09-21 19:33:14 +00:00
2022-11-01 23:09:34 +00:00
let getAveragedCompass = function ( ) {
return Math . round ( WIDGETS . gpstrek . getState ( ) . avgComp ) ;
} ;
2022-10-13 17:43:56 +00:00
let triangle = function ( x , y , width , height ) {
2022-09-21 19:33:14 +00:00
return [
Math . round ( x ) , Math . round ( y ) ,
Math . round ( x + width * 0.5 ) , Math . round ( y + height ) ,
Math . round ( x - width * 0.5 ) , Math . round ( y + height )
] ;
2022-10-13 17:43:56 +00:00
} ;
2022-09-21 19:33:14 +00:00
2023-05-20 13:13:02 +00:00
mapOverviewX = g . getWidth ( ) / 2 ;
mapOverviewY = g . getHeight ( ) / 2 ;
mapOverviewScale = SETTINGS . overviewScale ;
2023-05-20 20:48:10 +00:00
mapLiveScale = SETTINGS . mapScale ;
2023-05-20 13:13:02 +00:00
let onAction = function ( _ , xy ) {
2023-05-27 11:52:49 +00:00
clearTaskQueue ( ) ;
2023-05-22 13:49:34 +00:00
forceMapRedraw = true ;
2023-06-08 12:59:26 +00:00
if ( WIDGETS . gpstrek . getState ( ) . mode == MODE _MAP ) {
2023-05-22 14:40:30 +00:00
stopDrawing ( ) ;
2023-05-20 14:12:54 +00:00
if ( xy && xy . y > Bangle . appRect . y + Bangle . appRect . h - g . getHeight ( ) * 0.2 && xy . y <= Bangle . appRect . y2 ) {
2023-05-20 13:13:02 +00:00
if ( xy . x < Bangle . appRect . x + Bangle . appRect . w / 2 )
2023-05-20 20:48:10 +00:00
if ( isMapOverview ) {
2023-06-10 11:20:49 +00:00
mapOverviewScale /= 1.33 ;
2023-05-20 20:48:10 +00:00
} else {
2023-06-10 11:20:49 +00:00
mapLiveScale /= 1.33 ;
2023-05-20 20:48:10 +00:00
}
else
if ( isMapOverview ) {
2023-06-10 11:20:49 +00:00
mapOverviewScale *= 1.33 ;
2023-05-20 20:48:10 +00:00
} else {
2023-06-10 11:20:49 +00:00
mapLiveScale *= 1.33 ;
2023-05-20 20:48:10 +00:00
}
2023-06-08 20:13:09 +00:00
} else if ( isMapOverview && xy && xy . y > Bangle . appRect . y ) {
scrolling = ! scrolling ;
2023-05-20 13:13:02 +00:00
}
2023-05-22 14:40:30 +00:00
startDrawing ( ) ;
2022-10-19 19:04:46 +00:00
}
2022-10-13 17:43:56 +00:00
} ;
2022-10-19 19:04:46 +00:00
2023-06-08 20:13:09 +00:00
let scrolling = false ;
2023-05-20 13:13:02 +00:00
let onSwipe = function ( dirLR , dirUD ) {
2023-06-08 12:59:26 +00:00
let s = WIDGETS . gpstrek . getState ( ) ;
2023-05-27 11:52:49 +00:00
clearTaskQueue ( ) ;
2023-05-22 13:49:34 +00:00
forceMapRedraw = true ;
2023-06-08 12:59:26 +00:00
if ( s . mode == MODE _MAP ) {
2023-06-08 20:13:09 +00:00
if ( ! scrolling ) {
if ( dirLR > 0 ) {
switchMode ( MODE _MENU ) ;
} else if ( dirLR < 0 ) {
switchMode ( MODE _SLICES ) ;
}
if ( dirUD ) {
isMapOverview = ! isMapOverview ;
if ( ! isMapOverview ) {
mapOverviewX = g . getWidth ( ) / 2 ;
mapOverviewY = g . getHeight ( ) / 2 ;
scrolling = false ;
}
startDrawing ( ) ;
}
} else {
mapOverviewX += dirLR * SETTINGS . overviewScroll ;
mapOverviewY += dirUD * SETTINGS . overviewScroll ;
2023-06-08 12:59:26 +00:00
}
} else if ( s . mode == MODE _SLICES ) {
if ( dirLR > 0 ) {
if ( s . route ) {
switchMode ( MODE _MAP ) ;
} else {
switchMode ( MODE _MENU ) ;
}
} else if ( dirLR < 0 ) {
switchMode ( MODE _MENU ) ;
}
if ( dirUD ) {
setSlicesPage ( dirUD ) ;
}
2023-06-11 08:31:22 +00:00
} else {
2023-06-08 12:59:26 +00:00
if ( dirLR > 0 ) {
switchMode ( MODE _SLICES ) ;
} else if ( dirLR < 0 ) {
if ( s . route ) {
switchMode ( MODE _MAP ) ;
} else {
switchMode ( MODE _SLICES ) ;
}
}
}
2023-05-20 13:13:02 +00:00
} ;
2022-10-13 17:43:56 +00:00
let setButtons = function ( ) {
2022-10-19 19:04:46 +00:00
let options = {
mode : "custom" ,
2022-10-21 15:04:53 +00:00
swipe : onSwipe ,
2023-05-20 13:13:02 +00:00
btn : onAction ,
2023-06-08 12:59:26 +00:00
touch : onAction ,
2022-10-19 19:04:46 +00:00
} ;
Bangle . setUI ( options ) ;
2022-10-13 17:43:56 +00:00
} ;
2022-09-21 19:33:14 +00:00
2023-05-27 13:57:23 +00:00
let getFileSize = function ( filename ) {
return STORAGE . readArrayBuffer ( filename ) . byteLength ;
} ;
let writeUint24 = function ( filename , number , offset , size ) {
let b = new ArrayBuffer ( 3 ) ;
let n = new Uint24Array ( b , 0 , 1 ) ;
n [ 0 ] = number ;
STORAGE . write ( filename , b , offset , size ) ;
} ;
let writeUint32 = function ( filename , number , offset , size ) {
let b = new ArrayBuffer ( 4 ) ;
let n = new Uint32Array ( b , 0 , 1 ) ;
n [ 0 ] = number ;
STORAGE . write ( filename , b , offset , size ) ;
2022-10-13 17:43:56 +00:00
} ;
2022-09-21 19:33:14 +00:00
2023-06-11 09:10:40 +00:00
let getRouteIndex = function ( route ) {
if ( ! route . indexToOffset ) loadIndex ( route ) ;
return route . indexToOffset ;
}
let getIndexFileName = function ( filename ) {
return filename . substring ( 0 , filename . length - 1 ) + "i" ;
}
let loadIndex = function ( routeInfo ) {
routeInfo . indexToOffset = new Uint24Array ( STORAGE . readArrayBuffer ( getIndexFileName ( routeInfo . filename ) ) , 4 , routeInfo . count ) ;
}
2023-05-27 13:57:23 +00:00
let loadRouteData = function ( filename , progressMonitor ) {
2022-09-21 19:33:14 +00:00
let routeInfo = { } ;
routeInfo . filename = filename ;
let c = { } ;
let scanOffset = 0 ;
routeInfo . length = 0 ;
routeInfo . mirror = false ;
let lastSeenWaypoint ;
let lastSeenAlt ;
let waypoint = { } ;
2023-05-27 21:54:24 +00:00
let count = 0 ;
2022-09-21 19:33:14 +00:00
routeInfo . up = 0 ;
routeInfo . down = 0 ;
2023-05-12 08:13:01 +00:00
2023-05-27 13:57:23 +00:00
let trfHash = E . CRC32 ( STORAGE . read ( filename ) ) ;
let size = getFileSize ( filename ) ;
let indexFileName = filename . substring ( 0 , filename . length - 1 ) + "i" ;
//shortest possible entry is 24 characters + linebreak, 3 bytes per entry, 4 bytes hash in front
let indexFileSize = Math . ceil ( size / 25 * 3 + 4 ) ;
let createIndexFile = ! STORAGE . read ( indexFileName ) ;
if ( ! createIndexFile ) {
let currentHash = new Uint32Array ( STORAGE . readArrayBuffer ( indexFileName ) , 0 , 1 ) [ 0 ] ;
if ( currentHash != trfHash ) {
createIndexFile = true ;
}
}
// write hash into index file, will be recreated by this
if ( createIndexFile )
writeUint32 ( indexFileName , trfHash , 0 , indexFileSize ) ;
2022-09-21 19:33:14 +00:00
2023-05-27 16:15:37 +00:00
while ( ( scanOffset = getEntry ( filename , scanOffset , waypoint , true ) ) > 0 ) {
2023-05-27 21:54:24 +00:00
if ( count % 5 == 0 ) progressMonitor ( scanOffset , "Loading" , size ) ;
2022-09-21 19:33:14 +00:00
if ( lastSeenWaypoint ) {
routeInfo . length += distance ( lastSeenWaypoint , waypoint ) ;
let diff = waypoint . alt - lastSeenAlt ;
if ( waypoint . alt && lastSeenAlt && diff > 3 ) {
if ( lastSeenAlt < waypoint . alt ) {
routeInfo . up += diff ;
} else {
routeInfo . down += diff ;
}
}
}
2023-05-27 21:54:24 +00:00
if ( createIndexFile ) writeUint24 ( indexFileName , waypoint . fileOffset , 4 + count * 3 , indexFileSize ) ;
count ++ ;
2022-09-21 19:33:14 +00:00
lastSeenWaypoint = waypoint ;
2023-05-27 21:54:24 +00:00
if ( isFinite ( waypoint . alt ) ) lastSeenAlt = waypoint . alt ;
2022-09-21 19:33:14 +00:00
waypoint = { } ;
}
2023-06-11 09:10:40 +00:00
routeInfo . count = count - 1 ;
loadIndex ( routeInfo ) ;
2023-05-27 13:57:23 +00:00
2022-09-21 19:33:14 +00:00
set ( routeInfo , 0 ) ;
return routeInfo ;
2022-10-13 17:43:56 +00:00
} ;
2022-09-21 19:33:14 +00:00
2023-05-14 13:37:54 +00:00
let hasPrev = function ( route , index ) {
2023-06-08 19:52:50 +00:00
if ( isNaN ( index ) ) index = getWaypointIndex ( route ) ;
2023-05-20 08:52:31 +00:00
return index > 0 ;
2022-10-13 17:43:56 +00:00
} ;
2022-09-21 19:33:14 +00:00
2023-05-27 21:54:24 +00:00
let hasNext = function ( route , index , count ) {
if ( ! count ) count = 1 ;
2023-05-20 08:52:31 +00:00
if ( isNaN ( index ) ) index = route . index ;
2023-06-11 12:56:09 +00:00
return getWaypointIndex ( route , index ) + count < ( getRouteIndex ( route ) . length ) ;
2022-10-13 17:43:56 +00:00
} ;
2022-09-21 19:33:14 +00:00
2023-05-27 21:54:24 +00:00
let getNext = function ( route , index , count ) {
if ( ! count ) count = 1 ;
2023-06-08 19:52:50 +00:00
if ( isNaN ( index ) ) index = getWaypointIndex ( route ) ;
index += count ;
2023-06-11 12:56:09 +00:00
if ( index >= getRouteIndex ( route ) . length || index < 0 ) return ;
2023-05-14 13:37:54 +00:00
let result = { } ;
2023-06-11 09:10:40 +00:00
getEntry ( route . filename , getRouteIndex ( route ) [ getWaypointIndex ( route , index ) ] , result ) ;
2023-05-14 13:37:54 +00:00
return result ;
} ;
2023-05-27 21:54:24 +00:00
let get = function ( route , index ) {
2023-06-08 19:52:50 +00:00
if ( isNaN ( index ) ) index = getWaypointIndex ( route ) ;
2023-06-11 09:10:40 +00:00
if ( index >= getRouteIndex ( route ) . length || index < 0 ) return ;
2023-05-27 21:54:24 +00:00
let result = { } ;
2023-06-11 09:10:40 +00:00
getEntry ( route . filename , getRouteIndex ( route ) [ getWaypointIndex ( route , index ) ] , result ) ;
2023-05-27 21:54:24 +00:00
return result ;
} ;
2023-06-08 19:52:50 +00:00
let getWaypointIndex = function ( route , index ) {
2023-06-08 12:59:26 +00:00
if ( isNaN ( index ) ) index = route . index ;
2023-06-11 09:10:40 +00:00
return route . mirror ? getRouteIndex ( route ) . length - 1 - index : index ;
2023-06-08 12:59:26 +00:00
} ;
2023-06-08 19:52:50 +00:00
let setWaypointIndex = function ( route , waypointIndex ) {
2023-06-08 12:59:26 +00:00
if ( route . mirror )
2023-06-11 09:10:40 +00:00
route . index = getRouteIndex ( route ) . length - 1 - waypointIndex ;
2023-06-08 12:59:26 +00:00
else
route . index = waypointIndex ;
2023-05-27 21:54:24 +00:00
} ;
2023-05-16 17:48:30 +00:00
let getPrev = function ( route , index ) {
2023-06-11 12:56:09 +00:00
return getNext ( route , index , - 1 ) ;
2023-05-16 17:48:30 +00:00
} ;
2022-10-13 17:43:56 +00:00
let next = function ( route ) {
2022-09-21 19:33:14 +00:00
if ( ! hasNext ( route ) ) return ;
2023-06-08 19:52:50 +00:00
set ( route , getWaypointIndex ( route ) + 1 ) ;
2022-10-13 17:43:56 +00:00
} ;
2022-09-21 19:33:14 +00:00
2022-10-13 17:43:56 +00:00
let set = function ( route , index ) {
2023-05-16 17:48:30 +00:00
if ( ! route ) return ;
2023-06-08 19:52:50 +00:00
route . index = getWaypointIndex ( route , index ) ;
2022-10-13 17:43:56 +00:00
} ;
2022-09-21 19:33:14 +00:00
2022-10-13 17:43:56 +00:00
let prev = function ( route ) {
2022-09-21 19:33:14 +00:00
if ( ! hasPrev ( route ) ) return ;
2023-06-11 12:56:09 +00:00
set ( route , getWaypointIndex ( route ) - 1 ) ;
2022-10-13 17:43:56 +00:00
} ;
2022-09-21 19:33:14 +00:00
2022-10-13 17:43:56 +00:00
let getLast = function ( route ) {
2023-06-11 09:10:40 +00:00
return get ( route , getRouteIndex ( route ) . length - 1 ) ;
2022-10-13 17:43:56 +00:00
} ;
2022-09-21 19:33:14 +00:00
2023-06-08 12:59:26 +00:00
let isLast = function ( route , index ) {
if ( isNaN ( index ) ) index = route . index ;
2023-06-10 16:29:14 +00:00
index = getWaypointIndex ( route , index ) ;
2023-06-11 09:10:40 +00:00
return getRouteIndex ( route ) . length - 1 == index ;
2023-06-08 12:59:26 +00:00
} ;
2022-10-13 17:43:56 +00:00
let removeMenu = function ( ) {
2023-06-11 12:56:44 +00:00
let s = WIDGETS . gpstrek . getState ( ) ;
2022-09-21 19:33:14 +00:00
E . showMenu ( ) ;
2023-06-11 12:56:44 +00:00
switch ( searchNeeded ) {
case 1 :
setClosestWaypoint ( s . route , getWaypointIndex ( s . route ) , showProgress ) ;
break ;
case 2 :
setClosestWaypoint ( s . route , 0 , showProgress )
break ;
}
searchNeeded = 0 ;
2023-06-08 12:59:26 +00:00
onSwipe ( - 1 , 0 ) ;
2022-10-13 17:43:56 +00:00
} ;
2022-09-21 19:33:14 +00:00
2022-10-13 17:43:56 +00:00
let showProgress = function ( progress , title , max ) {
2022-09-21 19:33:14 +00:00
let message = title ? title : "Loading" ;
if ( max ) {
message += " " + E . clip ( ( progress / max * 100 ) , 0 , 100 ) . toFixed ( 0 ) + "%" ;
} else {
let dots = progress % 4 ;
for ( let i = 0 ; i < dots ; i ++ ) message += "." ;
for ( let i = dots ; i < 4 ; i ++ ) message += " " ;
}
E . showMessage ( message ) ;
2022-10-13 17:43:56 +00:00
} ;
2022-09-21 19:33:14 +00:00
2022-10-13 17:43:56 +00:00
let handleLoading = function ( c ) {
2022-09-21 19:33:14 +00:00
E . showMenu ( ) ;
2023-05-20 11:29:18 +00:00
let s = WIDGETS . gpstrek . getState ( ) ;
2023-05-27 13:57:23 +00:00
s . route = loadRouteData ( c , showProgress ) ;
2023-06-11 12:56:44 +00:00
if ( SETTINGS . autosearch && searchNeeded < 2 ) searchNeeded = 2 ;
2023-05-20 11:29:18 +00:00
s . waypoint = null ;
2022-10-13 17:43:56 +00:00
} ;
2022-09-21 19:33:14 +00:00
2022-10-13 17:43:56 +00:00
let showRouteSelector = function ( ) {
2022-10-21 15:04:53 +00:00
var menu = {
2022-09-21 19:33:14 +00:00
"" : {
back : showRouteMenu ,
}
} ;
2023-05-22 19:07:13 +00:00
STORAGE . list ( /\.trf$/ ) . sort ( ) . forEach ( ( file ) => {
2023-06-11 12:56:44 +00:00
menu [ file ] = ( ) => { handleLoading ( file ) ; showRouteMenu ( ) } ;
2022-11-02 21:50:00 +00:00
} ) ;
2022-09-21 19:33:14 +00:00
E . showMenu ( menu ) ;
2022-10-13 17:43:56 +00:00
} ;
2022-09-21 19:33:14 +00:00
2023-06-11 12:56:44 +00:00
// 1 for complete search, 2 for starting at current waypoint
let searchNeeded = 0 ;
2022-10-13 17:43:56 +00:00
let showRouteMenu = function ( ) {
2022-10-21 15:04:53 +00:00
var menu = {
2022-09-21 19:33:14 +00:00
"" : {
"title" : "Route" ,
back : showMenu ,
} ,
"Select file" : showRouteSelector
} ;
2023-05-20 11:29:18 +00:00
let s = WIDGETS . gpstrek . getState ( ) ;
if ( s . route ) {
2022-09-21 19:33:14 +00:00
menu . Mirror = {
2023-05-20 11:29:18 +00:00
value : s && s . route && ! ! s . route . mirror || false ,
2022-09-21 19:33:14 +00:00
onchange : v => {
2023-05-27 21:54:24 +00:00
if ( s . route . mirror != v ) {
s . route . mirror = v ;
2023-06-11 12:56:44 +00:00
if ( SETTINGS . autosearch )
if ( searchNeeded < 2 ) searchNeeded = 2 ;
else
setWaypointIndex ( s . route , 0 ) ;
2023-05-27 21:54:24 +00:00
}
2022-09-21 19:33:14 +00:00
}
} ;
menu [ 'Select closest waypoint' ] = function ( ) {
2023-05-20 11:29:18 +00:00
if ( s . currentPos && s . currentPos . lat ) {
2023-06-11 12:56:44 +00:00
if ( searchNeeded < 2 ) searchNeeded = 2 ;
removeMenu ( ) ;
2022-09-21 19:33:14 +00:00
} else {
E . showAlert ( "No position" ) . then ( ( ) => { E . showMenu ( menu ) ; } ) ;
}
} ;
menu [ 'Select closest waypoint (not visited)' ] = function ( ) {
2023-05-20 11:29:18 +00:00
if ( s . currentPos && s . currentPos . lat ) {
2023-06-11 12:56:44 +00:00
if ( searchNeeded < 1 ) searchNeeded = 1 ;
removeMenu ( ) ;
2022-09-21 19:33:14 +00:00
} else {
E . showAlert ( "No position" ) . then ( ( ) => { E . showMenu ( menu ) ; } ) ;
}
} ;
menu [ 'Select waypoint' ] = {
2023-06-08 19:52:50 +00:00
value : getWaypointIndex ( s . route ) ,
2023-06-11 09:10:40 +00:00
min : 0 , max : getRouteIndex ( s . route ) . length - 1 , step : 1 ,
2023-06-08 19:52:50 +00:00
onchange : v => { setWaypointIndex ( s . route , v ) ; }
2022-09-21 19:33:14 +00:00
} ;
menu [ 'Select waypoint as current position' ] = function ( ) {
2023-06-08 12:59:26 +00:00
let c = get ( s . route ) ;
s . currentPos . lat = c . lat ;
s . currentPos . lon = c . lon ;
s . currentPos . alt = c . alt ;
2022-09-21 19:33:14 +00:00
removeMenu ( ) ;
} ;
}
2023-05-20 11:29:18 +00:00
if ( s . route && hasPrev ( s . route ) )
menu [ 'Previous waypoint' ] = function ( ) { prev ( s . route ) ; removeMenu ( ) ; } ;
if ( s . route && hasNext ( s . route ) )
menu [ 'Next waypoint' ] = function ( ) { next ( s . route ) ; removeMenu ( ) ; } ;
2022-09-21 19:33:14 +00:00
E . showMenu ( menu ) ;
2022-10-13 17:43:56 +00:00
} ;
2022-09-21 19:33:14 +00:00
2022-10-13 17:43:56 +00:00
let showWaypointSelector = function ( ) {
2022-09-21 19:33:14 +00:00
let waypoints = require ( "waypoints" ) . load ( ) ;
2022-10-21 15:04:53 +00:00
var menu = {
2022-09-21 19:33:14 +00:00
"" : {
back : showWaypointMenu ,
}
} ;
2022-11-02 21:23:37 +00:00
waypoints . forEach ( ( wp , c ) => {
2022-09-21 19:33:14 +00:00
menu [ waypoints [ c ] . name ] = function ( ) {
2023-05-20 11:29:18 +00:00
let s = WIDGETS . gpstrek . getState ( ) ;
s . waypoint = waypoints [ c ] ;
s . waypointIndex = c ;
s . route = null ;
2022-09-21 19:33:14 +00:00
removeMenu ( ) ;
} ;
2022-11-02 21:23:37 +00:00
} ) ;
2022-09-21 19:33:14 +00:00
E . showMenu ( menu ) ;
2022-10-13 17:43:56 +00:00
} ;
2022-09-21 19:33:14 +00:00
2022-10-13 17:43:56 +00:00
let showCalibrationMenu = function ( ) {
2022-09-21 19:33:14 +00:00
let menu = {
"" : {
"title" : "Calibration" ,
back : showMenu ,
} ,
"Barometer (GPS)" : ( ) => {
2023-05-20 11:29:18 +00:00
let s = WIDGETS . gpstrek . getState ( ) ;
if ( ! s . currentPos || isNaN ( s . currentPos . alt ) ) {
2022-09-21 19:33:14 +00:00
E . showAlert ( "No GPS altitude" ) . then ( ( ) => { E . showMenu ( menu ) ; } ) ;
} else {
2023-05-20 11:29:18 +00:00
s . calibAltDiff = s . altitude - s . currentPos . alt ;
E . showAlert ( "Calibrated Altitude Difference: " + s . calibAltDiff . toFixed ( 0 ) ) . then ( ( ) => { removeMenu ( ) ; } ) ;
2022-09-21 19:33:14 +00:00
}
} ,
"Barometer (Manual)" : {
2023-05-27 21:54:24 +00:00
value : Math . round ( WIDGETS . gpstrek . getState ( ) . currentPos && ( WIDGETS . gpstrek . getState ( ) . currentPos . alt != undefined && isFinite ( WIDGETS . gpstrek . getState ( ) . currentPos . alt ) ) ? WIDGETS . gpstrek . getState ( ) . currentPos . alt : WIDGETS . gpstrek . getState ( ) . altitude ) ,
2022-09-21 19:33:14 +00:00
min : - 2000 , max : 10000 , step : 1 ,
2022-10-21 15:04:53 +00:00
onchange : v => { WIDGETS . gpstrek . getState ( ) . calibAltDiff = WIDGETS . gpstrek . getState ( ) . altitude - v ; }
2022-09-21 19:33:14 +00:00
} ,
"Reset Compass" : ( ) => { Bangle . resetCompass ( ) ; removeMenu ( ) ; } ,
} ;
E . showMenu ( menu ) ;
2022-10-13 17:43:56 +00:00
} ;
2022-09-21 19:33:14 +00:00
2022-10-13 17:43:56 +00:00
let showWaypointMenu = function ( ) {
2022-09-21 19:33:14 +00:00
let menu = {
"" : {
"title" : "Waypoint" ,
back : showMenu ,
} ,
"Select waypoint" : showWaypointSelector ,
} ;
E . showMenu ( menu ) ;
2022-10-13 17:43:56 +00:00
} ;
2022-09-21 19:33:14 +00:00
2022-10-13 17:43:56 +00:00
let showBackgroundMenu = function ( ) {
2022-10-13 18:36:47 +00:00
let menu = {
"" : {
"title" : "Background" ,
back : showMenu ,
} ,
2023-06-11 12:56:44 +00:00
"Start" : ( ) => { E . showPrompt ( "Start?" ) . then ( ( v ) => { if ( v ) { WIDGETS . gpstrek . start ( true ) ; showMenu ( ) ; } else { showMenu ( ) ; } } ) . catch ( ( ) => { showMenu ( ) ; } ) ; } ,
"Stop" : ( ) => { E . showPrompt ( "Stop?" ) . then ( ( v ) => { if ( v ) { WIDGETS . gpstrek . stop ( true ) ; showMenu ( ) ; } else { showMenu ( ) ; } } ) . catch ( ( ) => { showMenu ( ) ; } ) ; } ,
2022-10-13 18:36:47 +00:00
} ;
E . showMenu ( menu ) ;
2022-10-13 17:43:56 +00:00
} ;
2022-10-13 18:36:47 +00:00
2022-10-13 17:43:56 +00:00
let showMenu = function ( ) {
2022-10-21 15:04:53 +00:00
var mainmenu = {
2022-09-21 19:33:14 +00:00
"" : {
"title" : "Main" ,
back : removeMenu ,
} ,
"Route" : showRouteMenu ,
"Waypoint" : showWaypointMenu ,
2022-10-13 18:36:47 +00:00
"Background" : showBackgroundMenu ,
2022-09-21 19:33:14 +00:00
"Calibration" : showCalibrationMenu ,
2022-10-21 15:04:53 +00:00
"Reset" : ( ) => { E . showPrompt ( "Do Reset?" ) . then ( ( v ) => { if ( v ) { WIDGETS . gpstrek . resetState ( ) ; removeMenu ( ) ; } else { E . showMenu ( mainmenu ) ; } } ) . catch ( ( ) => { E . showMenu ( mainmenu ) ; } ) ; } ,
2022-10-30 17:31:31 +00:00
"Info rows" : {
2022-10-13 17:43:56 +00:00
value : WIDGETS . gpstrek . getState ( ) . numberOfSlices ,
2022-09-21 19:33:14 +00:00
min : 1 , max : 6 , step : 1 ,
2022-10-21 15:04:53 +00:00
onchange : v => { WIDGETS . gpstrek . getState ( ) . numberOfSlices = v ; }
2022-09-21 19:33:14 +00:00
} ,
} ;
E . showMenu ( mainmenu ) ;
2022-10-13 17:43:56 +00:00
} ;
2022-09-21 19:33:14 +00:00
2022-10-13 17:43:56 +00:00
let switchMenu = function ( ) {
2022-10-21 15:04:53 +00:00
stopDrawing ( ) ;
2022-10-13 17:43:56 +00:00
showMenu ( ) ;
} ;
2022-09-21 19:33:14 +00:00
2022-10-21 15:04:53 +00:00
let stopDrawing = function ( ) {
2023-05-27 21:54:24 +00:00
if ( global . drawTimeout ) clearTimeout ( global . drawTimeout ) ;
global . drawTimeout = undefined ;
2022-10-21 15:04:53 +00:00
scheduleDraw = false ;
} ;
2023-05-22 14:40:30 +00:00
let startDrawing = function ( ) {
2023-06-08 12:59:26 +00:00
firstDraw = true ;
2023-05-22 14:40:30 +00:00
scheduleDraw = true ;
draw ( ) ;
drawInTimeout ( ) ;
} ;
2022-10-13 17:43:56 +00:00
let drawInTimeout = function ( ) {
2023-05-27 21:54:24 +00:00
if ( global . drawTimeout ) return ;
2022-10-13 17:43:56 +00:00
drawTimeout = setTimeout ( ( ) => {
drawTimeout = undefined ;
2022-09-21 19:33:14 +00:00
draw ( ) ;
2023-05-24 19:08:39 +00:00
} , Bangle . isLocked ( ) ? SETTINGS . refreshLocked : SETTINGS . refresh ) ;
2022-10-13 17:43:56 +00:00
} ;
2022-09-21 19:33:14 +00:00
2022-10-13 17:43:56 +00:00
let switchNav = function ( ) {
2022-09-21 19:33:14 +00:00
setButtons ( ) ;
2023-06-08 12:59:26 +00:00
startDrawing ( ) ;
2022-10-13 17:43:56 +00:00
} ;
2022-09-21 19:33:14 +00:00
2023-06-08 12:59:26 +00:00
let setSlicesPage = function ( change ) {
page _slices -= change ;
if ( page _slices >= maxSlicePages ) {
page _slices = 0 ;
}
if ( page _slices < 0 ) {
page _slices = maxSlicePages - 1 ;
2022-09-21 19:33:14 +00:00
}
2022-10-13 17:43:56 +00:00
drawInTimeout ( ) ;
} ;
2022-09-21 19:33:14 +00:00
2023-06-10 12:54:22 +00:00
let setClosestWaypoint = function ( route , startindex , progress , maxDist ) {
2023-05-20 11:29:18 +00:00
let s = WIDGETS . gpstrek . getState ( ) ;
2023-05-27 21:54:24 +00:00
2023-06-08 19:52:50 +00:00
let stopSearchAfterFirstMatch = ! isFinite ( startindex ) ;
2023-05-27 21:54:24 +00:00
if ( ! startindex ) startindex = 0 ;
2023-06-11 09:10:40 +00:00
if ( startindex >= getRouteIndex ( s . route ) . length ) startindex = getRouteIndex ( s . route ) . length - 1 ;
2023-05-27 21:54:24 +00:00
if ( startindex < 0 ) startindex = 0 ;
2023-06-08 19:52:50 +00:00
2023-05-20 11:29:18 +00:00
if ( ! s . currentPos . lat ) {
2022-09-21 19:33:14 +00:00
set ( route , startindex ) ;
return ;
}
2023-06-10 12:54:22 +00:00
if ( ! maxDist ) maxDist = Number . MAX _VALUE ;
let minDist = maxDist ;
2023-05-27 21:54:24 +00:00
let mincount = 0 ;
let currentPos = s . currentPos ;
let count = 0 ;
let wp ;
do {
2023-06-11 09:10:40 +00:00
if ( progress && ( count % 5 == 0 ) ) progress ( count + startindex , "Searching" , getRouteIndex ( route ) . length ) ;
2023-05-27 21:54:24 +00:00
wp = getNext ( route , startindex , count ) ;
if ( ! wp ) break ;
let curDist = distance ( currentPos , wp ) ;
2023-06-10 12:54:22 +00:00
if ( curDist > maxDist ) {
break ;
}
2022-09-21 19:33:14 +00:00
if ( curDist < minDist ) {
minDist = curDist ;
2023-05-27 21:54:24 +00:00
mincount = count ;
2022-09-21 19:33:14 +00:00
} else {
2023-06-08 19:52:50 +00:00
if ( stopSearchAfterFirstMatch ) break ;
2022-09-21 19:33:14 +00:00
}
2023-06-08 19:52:50 +00:00
count ++ ;
2023-05-27 21:54:24 +00:00
} while ( wp ) ;
2023-06-08 12:59:26 +00:00
set ( route , startindex + mincount ) ;
2022-10-13 17:43:56 +00:00
} ;
2022-09-21 19:33:14 +00:00
2022-11-03 20:24:51 +00:00
const finishIcon = atob ( "CggB//meZmeZ+Z5n/w==" ) ;
2022-09-21 19:33:14 +00:00
const waypointData = {
icon : atob ( "EBCBAAAAAAAAAAAAcIB+zg/uAe4AwACAAAAAAAAAAAAAAAAA" ) ,
getProgress : function ( ) {
2023-06-11 09:10:40 +00:00
return ( getWaypointIndex ( WIDGETS . gpstrek . getState ( ) . route ) + 1 ) + "/" + getRouteIndex ( WIDGETS . gpstrek . getState ( ) . route ) . length ;
2022-09-21 19:33:14 +00:00
} ,
getTarget : function ( ) {
2023-06-08 12:59:26 +00:00
return get ( WIDGETS . gpstrek . getState ( ) . route ) ;
2022-09-21 19:33:14 +00:00
} ,
getStart : function ( ) {
2022-10-13 17:43:56 +00:00
return WIDGETS . gpstrek . getState ( ) . currentPos ;
2022-09-21 19:33:14 +00:00
}
} ;
const finishData = {
icon : atob ( "EBABAAA/4DmgJmAmYDmgOaAmYD/gMAAwADAAMAAwAAAAAAA=" ) ,
getTarget : function ( ) {
2023-05-16 16:49:05 +00:00
let s = WIDGETS . gpstrek . getState ( ) ;
if ( s . route ) return getLast ( s . route ) ;
if ( s . waypoint ) return s . waypoint ;
2022-09-21 19:33:14 +00:00
} ,
getStart : function ( ) {
2022-10-13 17:43:56 +00:00
return WIDGETS . gpstrek . getState ( ) . currentPos ;
2022-09-21 19:33:14 +00:00
}
} ;
2022-10-13 17:43:56 +00:00
let getSliceHeight = function ( number ) {
return Math . floor ( Bangle . appRect . h / WIDGETS . gpstrek . getState ( ) . numberOfSlices ) ;
} ;
2022-09-21 19:33:14 +00:00
2023-05-18 14:50:45 +00:00
let compassSlice = getCompassSlice ( ) ;
2023-05-16 16:49:05 +00:00
let mapSlice = getMapSlice ( ) ;
2022-09-21 19:33:14 +00:00
let waypointSlice = getTargetSlice ( waypointData ) ;
let finishSlice = getTargetSlice ( finishData ) ;
let eleSlice = getDoubleLineSlice ( "Up" , "Down" , ( ) => {
2023-05-16 16:49:05 +00:00
let s = WIDGETS . gpstrek . getState ( ) ;
return loc . distance ( s . up , 3 ) + "/" + ( s . route ? loc . distance ( s . route . up , 3 ) : "---" ) ;
2022-09-21 19:33:14 +00:00
} , ( ) => {
2023-05-16 16:49:05 +00:00
let s = WIDGETS . gpstrek . getState ( ) ;
return loc . distance ( s . down , 3 ) + "/" + ( s . route ? loc . distance ( s . route . down , 3 ) : "---" ) ;
2022-09-21 19:33:14 +00:00
} ) ;
let statusSlice = getDoubleLineSlice ( "Speed" , "Alt" , ( ) => {
let speed = 0 ;
2023-05-16 16:49:05 +00:00
let s = WIDGETS . gpstrek . getState ( ) ;
if ( s . currentPos && s . currentPos . speed ) speed = s . currentPos . speed ;
2022-09-21 19:33:14 +00:00
return loc . speed ( speed , 2 ) ;
} , ( ) => {
let alt = Infinity ;
2023-05-16 16:49:05 +00:00
let s = WIDGETS . gpstrek . getState ( ) ;
2023-05-27 21:54:24 +00:00
if ( isFinite ( s . altitude ) ) {
2023-05-16 16:49:05 +00:00
alt = isNaN ( s . calibAltDiff ) ? s . altitude : ( s . altitude - s . calibAltDiff ) ;
2022-09-21 19:33:14 +00:00
}
2023-05-16 16:49:05 +00:00
if ( s . currentPos && s . currentPos . alt ) alt = s . currentPos . alt ;
2022-10-13 17:43:56 +00:00
if ( isNaN ( alt ) ) return "---" ;
2022-09-21 19:33:14 +00:00
return loc . distance ( alt , 3 ) ;
} ) ;
let status2Slice = getDoubleLineSlice ( "Compass" , "GPS" , ( ) => {
2023-05-27 11:34:30 +00:00
return getAveragedCompass ( ) + "°" ;
2022-09-21 19:33:14 +00:00
} , ( ) => {
2023-05-27 11:34:30 +00:00
let course = "---°" ;
2023-05-16 16:49:05 +00:00
let s = WIDGETS . gpstrek . getState ( ) ;
2023-05-27 11:34:30 +00:00
if ( s . currentPos && s . currentPos . course ) course = s . currentPos . course . toFixed ( 0 ) + "°" ;
2022-09-21 19:33:14 +00:00
return course ;
2023-05-16 16:15:52 +00:00
} ) ;
2022-09-21 19:33:14 +00:00
let healthSlice = getDoubleLineSlice ( "Heart" , "Steps" , ( ) => {
2022-10-13 17:43:56 +00:00
return WIDGETS . gpstrek . getState ( ) . bpm || "---" ;
2022-09-21 19:33:14 +00:00
} , ( ) => {
2023-05-27 21:54:24 +00:00
return isFinite ( WIDGETS . gpstrek . getState ( ) . steps ) ? WIDGETS . gpstrek . getState ( ) . steps : "---" ;
2022-09-21 19:33:14 +00:00
} ) ;
2023-05-20 10:47:11 +00:00
let system2Slice = getDoubleLineSlice ( "Bat" , "Storage" , ( ) => {
2022-11-01 23:31:06 +00:00
return ( Bangle . isCharging ( ) ? "+" : "" ) + E . getBattery ( ) . toFixed ( 0 ) + "% " + ( analogRead ( D3 ) * 4.2 / BAT _FULL ) . toFixed ( 2 ) + "V" ;
2022-09-21 19:33:14 +00:00
} , ( ) => {
2023-05-20 10:47:11 +00:00
return ( STORAGE . getFree ( ) / 1024 ) . toFixed ( 0 ) + "kB" ;
2022-09-21 19:33:14 +00:00
} ) ;
2023-05-20 10:47:11 +00:00
let systemSlice = getDoubleLineSlice ( "RAM" , "WP Cache" , ( ) => {
2022-09-21 19:33:14 +00:00
let ram = process . memory ( false ) ;
return ( ( ram . blocksize * ram . free ) / 1024 ) . toFixed ( 0 ) + "kB" ;
} , ( ) => {
2023-05-27 13:57:23 +00:00
return cachedOffsets . length ? cachedOffsets . length : 0 ;
2022-09-21 19:33:14 +00:00
} ) ;
2022-10-13 17:43:56 +00:00
let clear = function ( ) {
g . clearRect ( Bangle . appRect ) ;
} ;
2022-09-21 19:33:14 +00:00
2023-05-12 08:19:23 +00:00
let minimumDistance = Number . MAX _VALUE ;
let lastSearch = 0 ;
2023-06-10 11:19:11 +00:00
let autosearchCounter = 0 ;
2023-05-12 08:19:23 +00:00
let updateRouting = function ( ) {
2023-05-16 16:49:05 +00:00
let s = WIDGETS . gpstrek . getState ( ) ;
2023-06-10 09:07:21 +00:00
if ( s . mode != MODE _MENU && s . route && s . currentPos . lat ) {
2023-06-08 12:59:26 +00:00
let currentDistanceToTarget = distance ( s . currentPos , get ( s . route ) ) ;
2023-05-12 08:19:23 +00:00
if ( currentDistanceToTarget < minimumDistance ) {
minimumDistance = currentDistanceToTarget ;
}
2023-06-10 11:19:11 +00:00
if ( SETTINGS . autosearch && autosearchCounter < SETTINGS . autosearchLimit && ! isMapOverview && lastSearch + 15000 < Date . now ( ) && minimumDistance < currentDistanceToTarget - SETTINGS . waypointChangeDist ) {
2023-05-12 08:19:23 +00:00
Bangle . buzz ( 1000 ) ;
2023-06-10 12:54:22 +00:00
setClosestWaypoint ( s . route , getWaypointIndex ( s . route ) , null , SETTINGS . maxDistForAutosearch ) ;
2023-05-12 08:19:23 +00:00
minimumDistance = Number . MAX _VALUE ;
lastSearch = Date . now ( ) ;
2023-06-10 11:19:11 +00:00
autosearchCounter ++ ;
2023-06-10 09:07:21 +00:00
}
let counter = 0 ;
while ( hasNext ( s . route ) && distance ( s . currentPos , get ( s . route ) ) < SETTINGS . waypointChangeDist ) {
next ( s . route ) ;
minimumDistance = Number . MAX _VALUE ;
2023-06-10 11:19:11 +00:00
autosearchCounter = 0 ;
2023-05-12 08:19:23 +00:00
}
}
} ;
2023-05-18 19:40:20 +00:00
let updateSlices = function ( ) {
let s = WIDGETS . gpstrek . getState ( ) ;
2023-05-20 09:24:16 +00:00
slices = [ compassSlice ] ;
2023-06-08 12:59:26 +00:00
if ( s . currentPos && s . currentPos . lat && s . route && ! isLast ( s . route ) ) {
2023-05-18 19:40:20 +00:00
slices . push ( waypointSlice ) ;
}
if ( s . currentPos && s . currentPos . lat && ( s . route || s . waypoint ) ) {
slices . push ( finishSlice ) ;
}
2023-06-10 14:17:28 +00:00
slices . push ( eleSlice ) ;
2023-05-18 19:40:20 +00:00
slices . push ( statusSlice ) ;
slices . push ( status2Slice ) ;
slices . push ( healthSlice ) ;
slices . push ( systemSlice ) ;
slices . push ( system2Slice ) ;
2023-06-08 12:59:26 +00:00
maxSlicePages = Math . ceil ( slices . length / s . numberOfSlices ) ;
2023-05-18 19:40:20 +00:00
} ;
2023-06-08 12:59:26 +00:00
let page _slices = 0 ;
let switchMode = function ( targetMode ) {
switch ( targetMode ) {
case MODE _MENU :
WIDGETS . gpstrek . getState ( ) . mode = MODE _MENU ;
switchMenu ( ) ;
break ;
case MODE _MAP :
case MODE _SLICES :
WIDGETS . gpstrek . getState ( ) . mode = targetMode ;
E . showMenu ( ) ;
switchNav ( ) ;
break ;
}
}
let lastDrawnMode ;
let lastDrawnPage ;
let drawMap = function ( ) {
g . reset ( ) ;
mapSlice . draw ( g , Bangle . appRect . x , Bangle . appRect . y , Bangle . appRect . h , Bangle . appRect . w ) ;
}
let drawSlices = function ( ) {
2023-05-16 16:49:05 +00:00
let s = WIDGETS . gpstrek . getState ( ) ;
2023-06-08 12:59:26 +00:00
updateSlices ( ) ;
let ypos = Bangle . appRect . y ;
let force = lastDrawnPage != page _slices || firstDraw ;
2022-09-21 19:33:14 +00:00
if ( force ) {
clear ( ) ;
2022-11-05 22:45:40 +00:00
}
2023-06-08 12:59:26 +00:00
let firstSlice = page _slices * s . numberOfSlices ;
let sliceHeight = getSliceHeight ( ) ;
let slicesToDraw = slices . slice ( firstSlice , firstSlice + s . numberOfSlices ) ;
for ( let slice of slicesToDraw ) {
2023-05-18 21:36:43 +00:00
g . reset ( ) ;
2023-06-08 12:59:26 +00:00
if ( ! slice . refresh || slice . refresh ( ) || force )
slice . draw ( g , 0 , ypos , sliceHeight , g . getWidth ( ) ) ;
ypos += sliceHeight + 1 ;
g . drawLine ( 0 , ypos - 1 , g . getWidth ( ) , ypos - 1 ) ;
}
lastDrawnPage = page _slices ;
}
let draw = function ( ) {
let s = WIDGETS . gpstrek . getState ( ) ;
if ( s . mode == MODE _MENU ) return ;
if ( lastDrawnMode != s . mode )
firstDraw = true ;
if ( firstDraw ) {
g . clear ( ) ;
Bangle . drawWidgets ( ) ;
2022-09-21 19:33:14 +00:00
}
2023-05-12 08:13:01 +00:00
2023-06-08 12:59:26 +00:00
switch ( s . mode ) {
case MODE _MAP :
drawMap ( ) ;
break ;
case MODE _SLICES :
drawSlices ( ) ;
break ;
}
firstDraw = false ;
lastDrawnMode = s . mode ;
2022-11-05 22:45:40 +00:00
if ( scheduleDraw ) {
drawInTimeout ( ) ;
}
2022-10-13 17:43:56 +00:00
} ;
2022-09-21 19:33:14 +00:00
2023-06-08 12:59:26 +00:00
switchMode ( MODE _SLICES ) ;
2022-09-21 19:33:14 +00:00
2023-06-10 09:07:21 +00:00
setInterval ( updateRouting , 500 ) ;
2022-10-13 17:43:56 +00:00
clear ( ) ;
}