Plug-In: Add Static Map Apple Vision Pro

Requires OmniFocus 4

DISCLAIMER: Mention of third-party websites and products is for informational purposes only and constitutes neither an endorsement nor a recommendation. OMNI-AUTOMATION.COM assumes no responsibility with regard to the selection, performance or use of information or products found at third-party websites. OMNI-AUTOMATION.COM provides this only as a convenience to our users. OMNI-AUTOMATION.COM has not tested the information found on these sites and makes no representations regarding its accuracy or reliability. There are risks inherent in the use of any information or products found on the Internet, and OMNI-AUTOMATION.COM assumes no responsibility in this regard. Please understand that a third-party site is independent from OMNI-AUTOMATION.COM and that OMNI-AUTOMATION.COM has no control over the content on that website. Please contact the vendor for additional information.

This plug-in uses the online Mapbox service to generate and add a static map image using the first map URL stored in the notes of the selected project or task.

When the plug-in is launched for the first time, you will be prompted to enter your Mapbox customer API key. This API key is securely stored in the system keychain, and will not be displayed during the execution of plug-in which uses HTTPS to encrypt the Mapbox URL during transmission.

To reset the stored API key, hold down the Control key when launching the plug-in.

TIP: The Shortcut workflow shown in the following video run on an Watch, can be installed with this link.

(⬇ see below ) Shortcuts workflow for creating an OmniFocus task whose note contains a link to the current location.

Shortcuts workflow for creating an OmniFocus task whose note contains a link to the current location.
Video 1: Text Objects, Style Attributes, and Inline Attachments
Example: Adding static map to a note

Return to: OmniFocus Plug-In Collection

Add Static Map for URL
   

/*{ "type": "action", "targets": ["omnifocus"], "author": "Otto Automator", "identifier": "com.omni-automation.of.add-mapbox-image", "version": "1.1", "description": "This plug-in uses the online Mapbox service (www.mapbox.com) to generate and add a static map image using the first map URL stored in the notes of the selected project or task.", "label": "Add Map from URL", "shortLabel": "Add Map", "paletteLabel": "Add Map", "image": "mappin.and.ellipse" }*/ (() => { var preferences = new Preferences() var credentials = new Credentials() var serviceTitle = "Mapbox" var shouldLog = false const requestCredentials = async () => { try { OSname = app.platformName // CREATE FORM FOR GATHERING ACCOUNT API KEY credentialsForm = new Form() // CREATE TEXT FIELD keyInputField = new Form.Field.String( "APIKey", null, null ) if(OSname !== "macOS"){ keyInputField.autocapitalizationType = TextAutocapitalizationType.None } credentialsForm.addField(keyInputField) // VALIDATE THE USER INPUT credentialsForm.validate = formObject => { APIKey = formObject.values["APIKey"] return (APIKey && APIKey.length > 0) ? true:false } // PRESENT THE FORM TO THE USER formPrompt = "Enter Mapbox API Key:" formObject = await credentialsForm.show(formPrompt, "Continue") // RETRIEVE FORM VALUES APIKey = formObject.values["APIKey"] // STORE THE VALUES credentials.write(serviceTitle, "Mapbox API Key", APIKey) // RETRIEVE CREDENTIALS OBJECT alertMsg = "The API key has been stored in the system keychain." alertTitle = "Confirmation" new Alert(alertTitle, alertMsg).show() } catch(err){ if(!err.causedByUserCancelling){ new Alert(err.name, err.message).show() } } } const fetchData = async mapboxURL => { try { if(shouldLog){console.log("FETCH", mapboxURL)} item = document.windows[0].selection.databaseObjects[0] noteObj = item.noteText nStyle = noteObj.style targetURLString = mapboxURL request = URL.FetchRequest.fromString(targetURLString) request.headers = {"Content-Type":"application/json"} request.method = 'GET' response = await request.fetch() responseCode = response.statusCode if (responseCode >= 200 && responseCode < 300){ wrapper = FileWrapper.withContents("static-map.png", response.bodyData) attachmentObj = Text.makeFileAttachment(wrapper, noteObj.style) if(noteObj.range.isEmpty){ noteObj.insert(noteObj.start, attachmentObj) } else { newLineObj = new Text('\n', noteObj.style) noteObj.append(newLineObj, noteObj.style) noteObj.append(newLineObj, noteObj.style) noteObj.append(attachmentObj, noteObj.style) } node = document.windows[0].content.selectedNodes[0] node.expandNote(false) } else { throw { name: "Server Response Code", message: `The server responded with code: ${responseCode}` } } } catch(err){ new Alert(err.name, err.message).show() } } const action = new PlugIn.Action(async function(selection, sender){ try { // HOLD DOWN CONTROL KEY TO RESET VALUES if (app.controlKeyDown){ credentialsObj = credentials.read(serviceTitle) if(!credentialsObj){ throw { name: "Missing Credentials", message: "There is no stored Mapbox API key to remove." } } else { alertMessage = "Remove the stored Mapbox API key?" alert = new Alert("Confirmation Required", alertMessage) alert.addOption("Reset") alert.addOption("Cancel") alert.show(buttonIndex => { if (buttonIndex === 0){ if(shouldLog){console.log(`Removing Service “${serviceTitle}”`)} credentials.remove(serviceTitle) if(shouldLog){console.log(`Service “${serviceTitle}” Removed`)} } }) } } else { credentialsObj = credentials.read(serviceTitle) if(shouldLog){console.log("# CREDENTIALS READ")} if (credentialsObj){ APIToken = credentialsObj["password"] // LOCATE FIRST MAP URL IN NOTE item = selection.databaseObjects[0] noteObj = item.noteText runRanges = noteObj.ranges(TextComponent.AttributeRuns) mapLinkStr = null for (rangeIndx in runRanges){ aRange = runRanges[rangeIndx] runStyle = noteObj.styleForRange(aRange) linkStr = runStyle.get(Style.Attribute.Link).string if(linkStr.length > 0 && linkStr.includes("&ll=")){ if(shouldLog){console.log("Map URL", linkStr)} mapLinkStr = linkStr break } } if(!mapLinkStr){ throw { name: "Missing URL", message: "The note contains no map links." } } // ETRACT THE COORDS FROM THE MAP LINK urlComps = URL.Components.fromString(mapLinkStr) queryItems = urlComps.queryItems coordsStr = null for (itemIndex in queryItems){ queryItem = queryItems[itemIndex] if(queryItem.name === "ll"){ coordsStr = queryItem.value if(shouldLog){console.log("Latitude and Longitude", coordsStr)} break } } coords = coordsStr.split(",") aLatitude = coords[0] if(shouldLog){console.log("Latitude", aLatitude)} aLongitude = coords[1] if(shouldLog){console.log("Longitude", aLongitude)} // CONSTRUCT THE USER FORM mapStyleIndex = preferences.readNumber("mapStyleIndex") if(!mapStyleIndex){ mapStyleIndex = 1 } if(shouldLog){console.log("mapStyleIndex", mapStyleIndex)} mapStyles = ["Mapbox Light", "Mapbox Dark", "Mapbox Streets", "Mapbox Outdoors", "Mapbox Satellite", "Mapbox Satellite Streets"] mapURLStyleValues = ["light-v11", "dark-v11", "streets-v12", "outdoors-v12", "satellite-v9", "satellite-streets-v12"] mapStylesIndexes = [0,1,2,3,4,5] mapStyleMenu = new Form.Field.Option( "mapStyleIndex", "Style", mapStylesIndexes, mapStyles, mapStyleIndex ) mapSizeIndex = preferences.readNumber("mapSizeIndex") if(!mapSizeIndex){ mapSizeIndex = 0 } if(shouldLog){console.log("mapSizeIndex", mapSizeIndex)} mapSizes = ["640x360", "360x640", "360x360", "480x480", "640x640"] mapDisplaySizes = ["640 x 360", "360 x 640", "360 x 360", "480 x 480", "640 x 640"] mapSizesIndexes = [0,1,2,3,4] mapSizeMenu = new Form.Field.Option( "mapSizeIndex", "Size", mapSizesIndexes, mapDisplaySizes, mapSizeIndex ) mapZoomIndex = preferences.readNumber("mapZoomIndex") if(!mapZoomIndex){ mapZoomIndex = 2 } if(shouldLog){console.log("mapZoomIndex", mapZoomIndex)} mapZoomFactors = ["13", "14", "15", "16", "17"] mapZoomIndexes = [0,1,2,3,4] mapZoomMenu = new Form.Field.Option( "mapZoomIndex", "Zoom", mapZoomIndexes, mapZoomFactors, mapZoomIndex ) shouldUseHighRes = preferences.readBoolean("shouldUseHighRes") if(!shouldUseHighRes){ shouldUseHighRes = false } if(shouldLog){console.log("shouldUseHighRes", shouldUseHighRes)} highResCheckbox = new Form.Field.Checkbox( "shouldUseHighRes", "High-Resolution (@2x)", shouldUseHighRes ) shouldUsePin = preferences.readBoolean("shouldUsePin") if(!shouldUsePin){ shouldUsePin = true } if(shouldLog){console.log("shouldUsePin", shouldUsePin)} pinStatusCheckbox = new Form.Field.Checkbox( "shouldUsePin", "Include Location Pin", shouldUsePin ) pinColorIndex = preferences.readNumber("pinColorIndex") if(!pinColorIndex){ pinColorIndex = 0 } if(shouldLog){console.log("mapZoomIndex", mapZoomIndex)} pinColorTitles = ["Red", "Green", "Blue", "Black", "White", "Gray", "Dark Gray", "Light Gray"] pinColorValues = ["f00", "0f0", "00f", "000", "fff", "808080", "a9a9a9", "d3d3d3"] pinColorIndexes = [0, 1, 2, 3, 4, 5, 6, 7] pinColorMenu = new Form.Field.Option( "pinColorIndex", "Pin Color", pinColorIndexes, pinColorTitles, pinColorIndex ) inputForm = new Form() inputForm.addField(mapStyleMenu) inputForm.addField(mapSizeMenu) inputForm.addField(mapZoomMenu) inputForm.addField(pinStatusCheckbox) inputForm.addField(pinColorMenu) inputForm.addField(highResCheckbox) inputForm.validate = function(formObject){ return true } formPrompt = "Select Mapbox map parameters:" buttonTitle = "Continue" // PRESENT FORM AND WAIT FOR RESULTS formObject = await inputForm.show(formPrompt, buttonTitle) mapStyleIndex = formObject.values['mapStyleIndex'] if(shouldLog){console.log('mapStyleIndex', mapStyleIndex)} mapStyleValue = mapURLStyleValues[mapStyleIndex] if(shouldLog){console.log('mapStyleValue', mapStyleValue)} mapSizeIndex = formObject.values['mapSizeIndex'] if(shouldLog){console.log('mapSizeIndex', mapSizeIndex)} mapSizeValue = mapSizes[mapSizeIndex] if(shouldLog){console.log('mapSizeValue', mapSizeValue)} mapZoomIndex = formObject.values['mapZoomIndex'] if(shouldLog){console.log('mapZoomIndex', mapZoomIndex)} mapZoomValue = mapZoomFactors[mapZoomIndex] if(shouldLog){console.log('mapZoomValue', mapZoomValue)} shouldUsePin = formObject.values['shouldUsePin'] if(shouldLog){console.log('shouldUsePin', shouldUsePin)} pinColorIndex = formObject.values['pinColorIndex'] if(shouldLog){console.log('pinColorIndex', pinColorIndex)} pinColorValue = pinColorValues[pinColorIndex] if(shouldLog){console.log('pinColorValue', pinColorValue)} shouldUseHighRes = formObject.values['shouldUseHighRes'] if(shouldLog){console.log('shouldUseHighRes', shouldUseHighRes)} resolutionValue = (shouldUseHighRes) ? "@2x":"" // CONSTRUCT MAPBOX URL if(shouldUsePin){ var urlStr = `https://api.mapbox.com/styles/v1/mapbox/${mapStyleValue}/static/pin-l+${pinColorValue}(${aLongitude},${aLatitude})/${aLongitude},${aLatitude},${mapZoomValue}/${mapSizeValue}${resolutionValue}?access_token=${APIToken}` } else { var urlStr = `https://api.mapbox.com/styles/v1/mapbox/${mapStyleValue}/static/${aLongitude},${aLatitude},${mapZoomValue}/${mapSizeValue}${resolutionValue}?access_token=${APIToken}` } if(shouldLog){console.log("Mapbox URL", urlStr)} // STORE THE VALUES preferences.write("mapStyleIndex", mapStyleIndex) preferences.write("mapSizeIndex", mapSizeIndex) preferences.write("mapZoomIndex", mapZoomIndex) preferences.write("pinColorIndex", pinColorIndex) preferences.write("shouldUsePin", shouldUsePin) preferences.write("shouldUseHighRes", shouldUseHighRes) if(shouldLog){console.log("# FETCHING DATA")} fetchData(urlStr) } else { if(shouldLog){console.log("# REQUESTING CREDENTIALS")} requestCredentials() } } } catch(err){ if(!err.causedByUserCancelling){ console.error(err.name, err.message) new Alert(err.name, err.message).show() } } }); action.validate = function(selection, sender){ singleItemIsSelected = ( selection.databaseObjects.length === 1 && selection.projects.length === 1 || selection.tasks.length === 1 ) return singleItemIsSelected }; return action; })();