From 0fc4ed6c8214f02bf238b3ebe7eb21253319b99e Mon Sep 17 00:00:00 2001 From: Paul Willems Date: Wed, 18 Dec 2024 22:25:07 +0100 Subject: [PATCH] Missed files --- plugin/activecaptain_client.js | 89 +++++++++++++ plugin/handlebars_utilities.js | 170 +++++++++++++++++++++++++ plugin/partials/amenities.hbsp | 25 ++++ plugin/position_utilities.js | 58 +++++++++ plugin/templates/point_of_interest.hbs | 7 + 5 files changed, 349 insertions(+) create mode 100644 plugin/activecaptain_client.js create mode 100644 plugin/handlebars_utilities.js create mode 100644 plugin/partials/amenities.hbsp create mode 100644 plugin/position_utilities.js create mode 100644 plugin/templates/point_of_interest.hbs diff --git a/plugin/activecaptain_client.js b/plugin/activecaptain_client.js new file mode 100644 index 0000000..5430b70 --- /dev/null +++ b/plugin/activecaptain_client.js @@ -0,0 +1,89 @@ +/* + * MIT License + * + * Copyright (c) 2024 Paul Willems + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +const axios = require('axios') + +const baseUrl = 'https://activecaptain.garmin.com' +const userAgent = 'Signal K Active Captain Plugin' + +axios.interceptors.request.use(request => { + // console.log('Starting Request', JSON.stringify(request, null, 2)) + return request +}, error => { + Promise.reject(error) +}) + +axios.interceptors.response.use(response => { + // console.log('Received Response', JSON.stringify(response.data, null, 2)) + return response +}, error => { + Promise.reject(error) +}) + +module.exports = { + listPointsOfInterest: function (app, x1, y1, x2, y2) { + const url = `${baseUrl}/community/api/v1/points-of-interest/bbox` + + return axios.post(url, { + north: y1, + west: x1, + south: y2, + east: x2, + zoomLevel: 17 + }, { + headers: { + 'User-Agent': userAgent, + Accept: 'application/json' + } + }).then(response => { + return response.data.pointsOfInterest.map(p => { + return { + id: p.id, + type: p.poiType, + position: { + longitude: p.mapLocation.longitude, + latitude: p.mapLocation.latitude + }, + name: p.name + } + }) + }).catch(error => { + app.debug(`ERROR fetching points of interest list ${x1}, ${y1}, ${x2}, ${y2} - ${error}`) + }) + }, + pointOfInterestDetails: function (app, id) { + const url = `${baseUrl}/community/api/v1/points-of-interest/${id}/summary` + + return axios.get(url, { + headers: { + 'User-Agent': userAgent, + Accept: 'application/json' + } + }).then(response => { + return response.data + }).catch(error => { + app.debug(`ERROR fetching point of interest ${id} - ${error}`) + }) + } +} diff --git a/plugin/handlebars_utilities.js b/plugin/handlebars_utilities.js new file mode 100644 index 0000000..1a3fdf7 --- /dev/null +++ b/plugin/handlebars_utilities.js @@ -0,0 +1,170 @@ +/** + * MIT License + * + * Copyright (c) 2024 Paul Willems + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +const handlebars = require('handlebars') +const helpersForHandlebars = require('helpers-for-handlebars') +const fs = require('node:fs') +const moment = require('moment') + +const EMPTY = '' +const UNKNOWN = 'Unknown' + +const partialsDir = './node_modules/signalk-activecaptain-resources/plugin/partials/' + +handlebars.registerPartial('header', fs.readFileSync(`${partialsDir}/header.hbsp`, 'utf-8')) +handlebars.registerPartial('business', fs.readFileSync(`${partialsDir}/business.hbsp`, 'utf-8')) +handlebars.registerPartial('dockage', fs.readFileSync(`${partialsDir}/dockage.hbsp`, 'utf-8')) +handlebars.registerPartial('fuel', fs.readFileSync(`${partialsDir}/fuel.hbsp`, 'utf-8')) +handlebars.registerPartial('amenities', fs.readFileSync(`${partialsDir}/amenities.hbsp`, 'utf-8')) +handlebars.registerPartial('contact', fs.readFileSync(`${partialsDir}/contact.hbsp`, 'utf-8')) +handlebars.registerPartial('review', fs.readFileSync(`${partialsDir}/review.hbsp`, 'utf-8')) + +const templatesDir = './node_modules/signalk-activecaptain-resources/plugin/templates/' + +const pointOfInterestTemplate = handlebars.compile(fs.readFileSync(`${templatesDir}/point_of_interest.hbs`, 'utf-8')) + +module.exports = { + helpers: function () { + helpersForHandlebars.comparison() + helpersForHandlebars.array() + + handlebars.registerHelper('fromNow', function (context, _) { + return moment(new Date(context)).fromNow() + }) + + handlebars.registerHelper('hasFuel', function (options) { + if (this.data.fuel) { + if (this.data.fuel.diesel !== UNKNOWN || + this.data.fuel.ethanolFree !== UNKNOWN || + this.data.fuel.gas !== UNKNOWN || + this.data.fuel.propane !== UNKNOWN || + this.data.fuel.electric !== UNKNOWN || + this.data.fuel.notes.length > 0) { + return options.fn(this) + } + } + return options.inverse(this) + }) + + handlebars.registerHelper('hasDockage', function (options) { + if (this.data.dockage) { + if (this.data.dockage.liveaboard !== UNKNOWN || + this.data.dockage.secureAccess !== UNKNOWN || + this.data.dockage.securityPatrol !== UNKNOWN || + this.data.dockage.notes.length > 0) { + return options.fn(this) + } + } + return options.inverse(this) + }) + + handlebars.registerHelper('hasContact', function (options) { + if (this.data.contact) { + if (this.data.contact.vhfChannel !== EMPTY || + this.data.contact.phone !== EMPTY || + this.data.contact.afterHourContact !== EMPTY || + this.data.contact.email !== EMPTY || + this.data.contact.website !== EMPTY) { + return options.fn(this) + } + } + return options.inverse(this) + }) + + handlebars.registerHelper('hasAmenities', function (options) { + if (this.data.amenity) { + if (this.data.amenity.bar !== UNKNOWN || + this.data.amenity.boatRamp !== UNKNOWN || + this.data.amenity.cellReception !== UNKNOWN || + this.data.amenity.courtesyCar !== UNKNOWN || + this.data.amenity.laundry !== UNKNOWN || + this.data.amenity.lodging !== UNKNOWN || + this.data.amenity.pets !== UNKNOWN || + this.data.amenity.restaurant !== UNKNOWN || + this.data.amenity.restroom !== UNKNOWN || + this.data.amenity.shower !== UNKNOWN || + this.data.amenity.transportation !== UNKNOWN || + this.data.amenity.trash !== UNKNOWN || + this.data.amenity.water !== UNKNOWN || + this.data.amenity.wifi !== UNKNOWN) { + return options.fn(this) + } + } + return options.inverse(this) + }) + + handlebars.registerHelper('hasBusiness', function (options) { + if (this.data.business) { + if (this.data.business.cash !== UNKNOWN || + this.data.business.check !== UNKNOWN || + this.data.business.credit !== UNKNOWN || + this.data.business.public !== UNKNOWN || + this.data.business.seasonal !== UNKNOWN || + this.data.business.notes.length > 0) { + return options.fn(this) + } + } + return options.inverse(this) + }) + }, + anchorage: function (data) { + return pointOfInterestTemplate({ data }) + }, + hazard: function (data) { + return pointOfInterestTemplate({ data }) + }, + marina: function (data) { + return pointOfInterestTemplate({ data }) + }, + localKnowledge: function (data) { + return pointOfInterestTemplate({ data }) + }, + navigational: function (data) { + return pointOfInterestTemplate({ data }) + }, + boatRamp: function (data) { + return pointOfInterestTemplate({ data }) + }, + business: function (data) { + return pointOfInterestTemplate({ data }) + }, + inlet: function (data) { + return pointOfInterestTemplate({ data }) + }, + lock: function (data) { + return pointOfInterestTemplate({ data }) + }, + dam: function (data) { + return pointOfInterestTemplate({ data }) + }, + ferry: function (data) { + return pointOfInterestTemplate({ data }) + }, + airport: function (data) { + return pointOfInterestTemplate({ data }) + }, + bridge: function (data) { + return pointOfInterestTemplate({ data }) + } +} diff --git a/plugin/partials/amenities.hbsp b/plugin/partials/amenities.hbsp new file mode 100644 index 0000000..7d07471 --- /dev/null +++ b/plugin/partials/amenities.hbsp @@ -0,0 +1,25 @@ +
+
+

🏨 Amenities

+ {{#if bar}}{{#if (eq bar "Yes")}}✅{{else}}❌{{/if}} Bar
{{/if}} + {{#if cellReception}}{{#if (eq cellReception "Yes")}}✅{{else}}❌{{/if}} Cell reception
{{/if}} + {{#if boatRamp}}{{#if (eq boatRamp "Yes")}}✅{{else}}❌{{/if}} Boat ramp
{{/if}} + {{#if laundry}}{{#if (eq laundry "Yes")}}✅{{else}}❌{{/if}} Laundry
{{/if}} + {{#if courtesyCar}}{{#if (eq courtesyCar "Yes")}}✅{{else}}❌{{/if}} Courtesy car
{{/if}} + {{#if pets}}{{#if (eq pets "Yes")}}✅{{else}}❌{{/if}} Pets allowed
{{/if}} + {{#if lodging}}{{#if (eq lodging "Yes")}}✅{{else}}❌{{/if}} Lodging
{{/if}} + {{#if restroom}}{{#if (eq restroom "Yes")}}✅{{else}}❌{{/if}} Restrooms
{{/if}} + {{#if restaurant}}{{#if (eq restaurant "Yes")}}✅{{else}}❌{{/if}} Restaurant
{{/if}} + {{#if transportation}}{{#if (eq transportation "Yes")}}✅{{else}}❌{{/if}} Transportation
{{/if}} + {{#if shower}}{{#if (eq shower "Yes")}}✅{{else}}❌{{/if}} Showers
{{/if}} + {{#if water}}{{#if (eq water "Yes")}}✅{{else}}❌{{/if}} Water
{{/if}} + {{#if trash}}{{#if (eq trash "Yes")}}✅{{else}}❌{{/if}} Rubbish disposal
{{/if}} + {{#if wifi}}{{#if (eq wifi "Yes")}}✅{{else}}❌{{/if}} Wifi
{{/if}} +
+{{#if notes}} +
+{{#each notes}} +

{{this.field}}: {{this.value}}

+{{/each}} +
+{{/if}} \ No newline at end of file diff --git a/plugin/position_utilities.js b/plugin/position_utilities.js new file mode 100644 index 0000000..9377bd0 --- /dev/null +++ b/plugin/position_utilities.js @@ -0,0 +1,58 @@ +/* + * MIT License + * + * Copyright (c) 2024 Paul Willems + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +module.exports = { + positionToBbox: function (position, distanceMeters) { + const nwCoords = calculateNewPosition(position[0], position[1], -45, distanceMeters / 1000) + const seCoords = calculateNewPosition(position[0], position[1], 135, distanceMeters / 1000) + + return [nwCoords.longitude, nwCoords.latitude, seCoords.longitude, seCoords.latitude] + } +} + +function calculateNewPosition (longitude, latitude, bearing, distanceKms) { + const earthRadius = 6371 // Radius of the Earth in kilometers + const latitudeRad = toRadians(latitude) + const longitudeRad = toRadians(longitude) + const bearingRad = toRadians(bearing) + + const newLatitudeRad = Math.asin(Math.sin(latitudeRad) * Math.cos(distanceKms / earthRadius) + + Math.cos(latitudeRad) * Math.sin(distanceKms / earthRadius) * Math.cos(bearingRad)) + + const newLongitudeRad = longitudeRad + Math.atan2(Math.sin(bearingRad) * Math.sin(distanceKms / earthRadius) * Math.cos(latitudeRad), + Math.cos(distanceKms / earthRadius) - Math.sin(latitudeRad) * Math.sin(newLatitudeRad)) + + const newLatitude = toDegrees(newLatitudeRad) + const newLongitude = toDegrees(newLongitudeRad) + + return { latitude: newLatitude, longitude: newLongitude } +} + +function toRadians (degrees) { + return degrees * Math.PI / 180 +} + +function toDegrees (radians) { + return radians * 180 / Math.PI +} diff --git a/plugin/templates/point_of_interest.hbs b/plugin/templates/point_of_interest.hbs new file mode 100644 index 0000000..6c6b9f6 --- /dev/null +++ b/plugin/templates/point_of_interest.hbs @@ -0,0 +1,7 @@ +{{> header}} +{{#hasDockage}}{{> dockage data.dockage}}{{/hasDockage}} +{{#hasContact}}{{> contact data.contact}}{{/hasContact}} +{{#hasFuel}}{{> fuel data.fuel}}{{/hasFuel}} +{{#hasAmenities}}{{> amenities data.amenity}}{{/hasAmenities}} +{{#hasBusiness}}{{> business data.business}}{{/hasBusiness}} + \ No newline at end of file