Initial commit

Co-Authored-By: kotte <14197736+mrtamagotchi@users.noreply.github.com>
Co-Authored-By: mikaeltellhed <2311083+mikaeltellhed@users.noreply.github.com>
Co-Authored-By: Tore Knudsen <18231882+torekndsn@users.noreply.github.com>
Co-Authored-By: Michael Cartner <32543275+michaelcartner@users.noreply.github.com>
This commit is contained in:
Eric Tuvesson
2023-09-05 12:08:55 +02:00
commit 53f0d6320e
2704 changed files with 76354 additions and 0 deletions

View File

@@ -0,0 +1,109 @@
https://docs.mapbox.com/mapbox-gl-js/example/add-3d-model/
```js
const map = Inputs.map;
// parameters to ensure the model is georeferenced correctly on the map
const modelOrigin = [148.9819, -35.39847];
const modelAltitude = 0;
const modelRotate = [Math.PI / 2, 0, 0];
const modelAsMercatorCoordinate = mapboxgl.MercatorCoordinate.fromLngLat(
modelOrigin,
modelAltitude
);
// transformation parameters to position, rotate and scale the 3D model onto the map
const modelTransform = {
translateX: modelAsMercatorCoordinate.x,
translateY: modelAsMercatorCoordinate.y,
translateZ: modelAsMercatorCoordinate.z,
rotateX: modelRotate[0],
rotateY: modelRotate[1],
rotateZ: modelRotate[2],
/* Since the 3D model is in real world meters, a scale transform needs to be
* applied since the CustomLayerInterface expects units in MercatorCoordinates.
*/
scale: modelAsMercatorCoordinate.meterInMercatorCoordinateUnits()
};
const THREE = window.THREE;
// configuration of the custom layer for a 3D model per the CustomLayerInterface
const customLayer = {
id: '3d-model',
type: 'custom',
renderingMode: '3d',
onAdd: function (map, gl) {
this.camera = new THREE.Camera();
this.scene = new THREE.Scene();
// create two three.js lights to illuminate the model
const directionalLight = new THREE.DirectionalLight(0xffffff);
directionalLight.position.set(0, -70, 100).normalize();
this.scene.add(directionalLight);
const directionalLight2 = new THREE.DirectionalLight(0xffffff);
directionalLight2.position.set(0, 70, 100).normalize();
this.scene.add(directionalLight2);
// use the three.js GLTF loader to add the 3D model to the three.js scene
const loader = new THREE.GLTFLoader();
loader.load(
'https://docs.mapbox.com/mapbox-gl-js/assets/34M_17/34M_17.gltf',
(gltf) => {
this.scene.add(gltf.scene);
}
);
this.map = map;
// use the Mapbox GL JS map canvas for three.js
this.renderer = new THREE.WebGLRenderer({
canvas: map.getCanvas(),
context: gl,
antialias: true
});
this.renderer.autoClear = false;
},
render: function (gl, matrix) {
const rotationX = new THREE.Matrix4().makeRotationAxis(
new THREE.Vector3(1, 0, 0),
modelTransform.rotateX
);
const rotationY = new THREE.Matrix4().makeRotationAxis(
new THREE.Vector3(0, 1, 0),
modelTransform.rotateY
);
const rotationZ = new THREE.Matrix4().makeRotationAxis(
new THREE.Vector3(0, 0, 1),
modelTransform.rotateZ
);
const m = new THREE.Matrix4().fromArray(matrix);
const l = new THREE.Matrix4()
.makeTranslation(
modelTransform.translateX,
modelTransform.translateY,
modelTransform.translateZ
)
.scale(
new THREE.Vector3(
modelTransform.scale,
-modelTransform.scale,
modelTransform.scale
)
)
.multiply(rotationX)
.multiply(rotationY)
.multiply(rotationZ);
this.camera.projectionMatrix = m.multiply(l);
this.renderer.resetState();
this.renderer.render(this.scene, this.camera);
this.map.triggerRepaint();
}
};
map.addLayer(customLayer, 'waterway-label');
```

View File

@@ -0,0 +1,21 @@
# Camera
## Fly to location
```js
const mapboxObject = Inputs.MapboxObject;
if (!mapboxObject) return;
// Mapbox example
// https://docs.mapbox.com/mapbox-gl-js/example/flyto/
// flyTo doc:
// https://docs.mapbox.com/mapbox-gl-js/api/map/#map#flyto
mapboxObject.flyTo({
center: [(Math.random() - 0.5) * 360, (Math.random() - 0.5) * 100],
zoom: 10,
// this animation is considered essential
// with respect to prefers-reduced-motion
essential: true,
});
```

View File

@@ -0,0 +1,253 @@
---
title: Directions API with the Mapbox Module
hide_title: true
---
# Directions API with the Mapbox Module
Here are some code heavy example of how to use the Directions API in Noodl.
The code in this example is using [Getting started with the Mapbox Directions API
](https://docs.mapbox.com/help/tutorials/getting-started-directions-api/) guide.
## Listen to on click events on Mapbox
Script node:
```js
Script.Outputs = {
["Mapbox Object"]: "*",
["Clicked"]: "signal",
["Clicked Longitude"]: "number",
["Clicked Latitute"]: "number"
};
Script.Signals["Mount Click Event"] = function () {
// Listen to the Mapbox on click event
// NOTE: This might cause some memory leaks,
// Not sure how Mapbox is handling it.
Script.Inputs["Mapbox Object"].on("click", (event) => {
// Get the coordinates that the user pressed on
const coords = Object.keys(event.lngLat).map((key) => event.lngLat[key]);
// Set the Noodl outputs
Script.Outputs["Clicked Longitude"] = coords[0];
Script.Outputs["Clicked Latitute"] = coords[1];
// Send the click signal
Script.Outputs["Clicked"]();
});
};
```
## Create a route path on Mapbox
Script node:
```js
// This function makes a Mapbox Directions API request
async function getRoute(map, start, end) {
const accessToken = Noodl.getProjectSettings().mapboxAccessToken;
// make a directions request using cycling profile
// an arbitrary start will always be the same
// only the end or destination will change
const query = await fetch(
`https://api.mapbox.com/directions/v5/mapbox/cycling/${start[0]},${start[1]};${end[0]},${end[1]}?steps=true&geometries=geojson&access_token=${accessToken}`,
{ method: "GET" }
);
const json = await query.json();
const data = json.routes[0];
const route = data.geometry.coordinates;
const geojson = {
type: "Feature",
properties: {},
geometry: {
type: "LineString",
coordinates: route,
},
};
// if the route already exists on the map, we'll reset it using setData
if (map.getSource("route")) {
map.getSource("route").setData(geojson);
}
// otherwise, we'll make a new request
else {
map.addLayer({
id: "route",
type: "line",
source: {
type: "geojson",
data: geojson,
},
layout: {
"line-join": "round",
"line-cap": "round",
},
paint: {
"line-color": "#3887be",
"line-width": 5,
"line-opacity": 0.75,
},
});
}
return data;
}
//
// Start of Noodl Script
//
Script.Inputs = {
map: {
displayName: "Mapbox Object",
type: "*",
},
startLongitude: {
displayName: "Start Longitude",
type: "number",
group: "Coordinates",
default: 0,
},
startLatitute: {
displayName: "Start Latitude",
type: "number",
group: "Coordinates",
default: 0,
},
endLongitude: {
displayName: "End Longitude",
type: "number",
group: "Coordinates",
default: 0,
},
endLatitute: {
displayName: "End Latitude",
type: "number",
group: "Coordinates",
default: 0,
},
// You can read more about Routing profiles here:
// https://docs.mapbox.com/api/navigation/directions/#routing-profiles
routingProfile: {
displayName: "Routing Profile",
type: {
name: "enum",
enums: [
{
label: "Driving Traffic",
value: "mapbox/driving-traffic",
},
{
label: "Driving",
value: "mapbox/driving",
},
{
label: "Walking",
value: "mapbox/walking",
},
{
label: "Cycling",
value: "mapbox/cycling",
},
],
},
default: "mapbox/cycling",
},
};
Script.Outputs = {
Steps: "array",
Duration: "number",
};
Script.Signals.CreateRoute = function () {
const map = Script.Inputs.Map;
const startCoords = [
Script.Inputs.startLongitude,
Script.Inputs.startLatitute,
];
const endCoords = [Script.Inputs.endLongitude, Script.Inputs.endLatitute];
// Add starting point to the map
map.addLayer({
id: "point",
type: "circle",
source: {
type: "geojson",
data: {
type: "FeatureCollection",
features: [
{
type: "Feature",
properties: {},
geometry: {
type: "Point",
coordinates: startCoords,
},
},
],
},
},
paint: {
"circle-radius": 10,
"circle-color": "#3887be",
},
});
const endPoint = {
type: "FeatureCollection",
features: [
{
type: "Feature",
properties: {},
geometry: {
type: "Point",
coordinates: endCoords,
},
},
],
};
if (map.getLayer("end")) {
map.getSource("end").setData(endPoint);
} else {
map.addLayer({
id: "end",
type: "circle",
source: {
type: "geojson",
data: {
type: "FeatureCollection",
features: [
{
type: "Feature",
properties: {},
geometry: {
type: "Point",
coordinates: endCoords,
},
},
],
},
},
paint: {
"circle-radius": 10,
"circle-color": "#f30",
},
});
}
// make an directions request that
getRoute(map, startCoords, endCoords).then((data) => {
// Extract the steps / instructions
const steps = data.legs[0].steps;
Script.Outputs.Steps = steps;
Script.Outputs.Duration = Math.floor(data.duration / 60);
});
};
```

View File

@@ -0,0 +1,131 @@
# Directions
## Create a request to Directions API
TODO: Add more info about "overview" (full, simplified, or false)
```js
// Get the Mapbox access token from Noodl project settings
const access_token = Noodl.getProjectSettings().mapboxAccessToken;
// For using the API to handle min/max road width, we have to use the "driving" profile.
// https://docs.mapbox.com/api/navigation/directions#optional-parameters-for-the-mapboxdriving-profile
const routingProfile = "driving";
// Encode the coordinates to be URL-safe
const coordinates = encodeURIComponent([fromCoordinate, toCoordinate].join(";"));
// Define query parameters for the API request
//
// Playground by Mapbox to test out all the features:
// https://docs.mapbox.com/playground/directions/
const queryParams = {
access_token, // Provide the access token
alternatives: true,
continue_straight: true,
geometries: "geojson",
language: "en",
overview: "simplified",
steps: true,
// The max vehicle height, in meters. If this parameter is provided, the
// Directions API will compute a route that includes only roads with a height
// limit greater than or equal to the max vehicle height. max_height must be
// between 0 and 10 meters. The default value is 1.6 meters. Coverage for road
// height restriction may vary by region.
max_height,
// The max vehicle width, in meters. If this parameter is provided, the
// Directions API will compute a route that includes only roads with a width
// limit greater than or equal to the max vehicle width. max_width must be
// between 0 and 10 meters. The default value is 1.9 meters. Coverage for road
// width restriction may vary by region.
max_width,
};
// Construct the query string from the query parameters
const query = Object.keys(queryParams)
.filter((key) => !!queryParams[key]) // Filter out empty values
.map((key) => `${encodeURIComponent(key)}=${encodeURIComponent(queryParams[key])}`)
.join('&');
// Set the endpoint URL for the Mapbox directions API
const ENDPOINT = 'https://api.mapbox.com/directions/v5/mapbox';
// Make the API request and get the response as JSON
const response = await fetch(`${ENDPOINT}/${routingProfile}/${coordinates}?${query}`);
const json = await response.json();
if (json.code === "Ok") {
Outputs.Routes = jsonroutes;
Outputs.Success();
} else {
Outputs.Failure();
}
```
Draw the route:
```js
Script.Inputs = {
MapboxObject: "object",
Routes: "array"
};
Script.Outputs = {
Done: "signal",
};
Script.Signals = {
Update() {
const map = Script.Inputs.MapboxObject;
const route = Script.Inputs.Routes[0];
function createOrUpdateSource(id, newSource) {
const source = map.getSource(id);
if (source) {
source.setData(newSource.data);
} else {
map.addSource(id, newSource);
}
}
function createOrUpdateLayer(newLayer) {
const layer = map.getLayer(newLayer.id);
if (layer) {
if (newLayer.paint) {
Object.keys(newLayer.paint).forEach((key) => {
layer.setPaintProperty(key, newLayer.paint[key]);
});
}
} else {
map.addLayer(newLayer);
}
}
createOrUpdateSource('route', {
'type': 'geojson',
'data': {
'type': 'Feature',
'properties': {},
'geometry': route.geometry,
}
});
createOrUpdateLayer({
'id': 'route',
'type': 'line',
'source': 'route',
'layout': {
'line-join': 'round',
'line-cap': 'round'
},
'paint': {
'line-color': '#888',
'line-width': 8
}
});
},
};
```

View File

@@ -0,0 +1,111 @@
---
title: Geocoder
hide_title: true
---
# Geocoder
Mapbox Geocoder API is a geocoding service that allows developers to convert human-readable addresses into geographic coordinates (latitude and longitude) and vice versa. It is a RESTful API that provides geocoding and reverse geocoding services for worldwide locations. The API uses machine learning and other advanced algorithms to provide accurate and relevant results for address searches. Mapbox Geocoder API can be used in a variety of applications, such as location-based services, logistics and delivery management, real estate, and more. It also offers various features such as autocomplete suggestions, batch geocoding, and custom matching parameters, making it a powerful tool for developers to build location-based applications.
## Create a request to Geocoder API
```js
// Define the expected inputs for the script
Script.Inputs = {
Query: "string",
};
// Define the expected outputs for the script
Script.Outputs = {
Items: "array",
Searched: "signal",
};
// Set the endpoint URL for the Mapbox geocoding API
const ENDPOINT = "https://api.mapbox.com/geocoding/v5/mapbox.places";
// Define an asynchronous function to make the API request
async function makeRequest() {
// Get the Mapbox access token from Noodl project settings
const access_token = Noodl.getProjectSettings().mapboxAccessToken;
// Encode the search query to be URL-safe
const search_text = encodeURIComponent(Script.Inputs.Query);
// Define query parameters for the API request
//
// Playground by Mapbox to test out all the features:
// https://docs.mapbox.com/playground/geocoding
//
// Here is a list of all the different possible types:
// - address
// - country
// - region
// - postcode
// - district
// - place
// - neighborhood
// - locality
// - poi
const queryParams = {
access_token, // Provide the access token
proximity: "ip", // Bias results towards user's location
limit: 5, // Maximum number of results to return
types: ["address"].join(","), // Filter results to include only addresses
fuzzyMatch: true, // Enable approximate matching
};
// Construct the query string from the query parameters
const query = Object.keys(queryParams)
.filter((key) => !!queryParams[key]) // Filter out empty values
.map(
(key) =>
`${encodeURIComponent(key)}=${encodeURIComponent(queryParams[key])}`
)
.join("&");
// Make the API request and get the response as JSON
const response = await fetch(`${ENDPOINT}/${search_text}.json?${query}`);
const json = await response.json();
// Map the API response to an array of search results
const items = json.features.map((x) =>
Noodl.Object.create({
text: x.text,
place_name: x.place_name,
// Convert the array of [latitude, longitude] to a Geopoint
center: { latitude: x.center[0], longitude: x.center[1] },
})
);
Script.Outputs.Items = items;
Script.Outputs.Searched();
}
Script.Signals = {
Search() {
makeRequest();
},
};
```
### Improve the search result
While comparing the built in Geocoder in Mapbox and using this Script.
There are a few differences and the result from the Mapbox Geocoder is getting better result.
The reason why the results are better is that the parameters are a little different, but in the end they are using the same API endpoints.
To make the parameters match, we can make a few changes in this code:
```js
const queryParams = {
access_token, // Provide the access token
proximity: [Script.Inputs.lng, Script.Inputs.lat].join(','), // Bias results towards user's location
limit: 5, // Maximum number of results to return
language: 'en-GB'
};
```
The main change here is the `proximity` that we changed to a Geopoint instead of `ip`.
This requires 2 new inputs to the Script node, `lng` and `lat`, connected from the Mapbox node center lng and lat position.

View File

@@ -0,0 +1,111 @@
---
title: Interacting with the Mapbox Module
hide_title: true
---
# Interacting with the Mapbox Module
## What you will learn in this guide
This guide will teach you how to implement basic interactions with your Mapbox map using the Mapbox Module.
## Overview
If you haven't setup Mapbox, it is recommended to read [setting up Mapbox guide](/library/modules/mapbox/guides/setting-up) before continuing this guide.
We will go though the following steps:
- Getting the latitude and longitude of the position a user clicked on the map.
- Detecting when a user moves the map.
## Getting the position that the user clicked
If we start out with the same super simple app as in the previous guide ([Setting up](/library/modules/mapbox/guides/setting-up) guide) we can start adding some interactions to it.
<div className="ndl-image-with-background l">
![](/library/modules/mapbox/guides/interacting/initial-state.png)
</div>
Let's first remove the "Hello World"-text. Instead we replace it with a panel. We will build it using a [Group](/nodes/basic-elements/group) node. So add a **Group** node at the bottom of the node hierarchy. Name it "Popup Panel" for readability.
<div className="ndl-image-with-background">
![](/library/modules/mapbox/guides/interacting/nodes-1.png)
</div>
We want it to float on top of the Mapbox Map so change it's **Position** to **Absolute**. Center it and dock it to the bottom using the **Layout** controls. Set a size of 300 pixels wide and 100 pixels high for now. Give it a bottom margin of 20 pixels to give it some space.
<div className="ndl-image-with-background s">
![](/library/modules/mapbox/guides/interacting/popup-panel-1.png)
</div>
Also make it white, with rounded corners of 10 pixels and a 2 pixel outline of a dark grey color. Also tick `Shadow Enabled` to make it a little nicer visually.
<div className="ndl-image-with-background s">
![](/library/modules/mapbox/guides/interacting/popup-panel-2.png)
</div>
Now you should have a panel floating on top of the map.
<div className="ndl-image-with-background l">
![](/library/modules/mapbox/guides/interacting/screen-1.png)
</div>
We want to print out the geographical location in the panel, i.e. the latitude and longitude of the position the user clicks.
Add two text nodes in the panel. Adjust the padding (horizontally and vertically) of the "Popup Panel" to 10 pixels to give the text some space.
<div className="ndl-image-with-background l">
![](/library/modules/mapbox/guides/interacting/state-2.png)
</div>
Then connect the two outputs `Longitude` and `Latitude` to the respective text.
_Make sure it's the one under "Mapped Clicked" Section_ since there are two other similarly named outputs that holds the current panning position of the map.
<div className="ndl-image-with-background l">
![](/library/modules/mapbox/guides/interacting/connection-1.png)
</div>
Now try clicking around in the map. You should see the two texts updating whenever you click.
<div className="ndl-image-with-background l">
![](/library/modules/mapbox/guides/interacting/screen-2.png)
</div>
## Tracking Map Movement
Now let's add some behavior to the panel. We want it to appear from the bottom when the user clicks on the map. Then, when the user starts panning or zooming in the map. To do this we are going to use the `Click` and `Map Moved` output signals.
First add a [States](/nodes/utilities/logic/states) node that will hold the current state of the panel. Add the states "Off Screen" and "On Screen". Create a value to control in the state, lets call it "Y Position". This is the value that will control the Y position of the panel. It will be 120 when the panel is "Off Screen" (i.e. it will be below the screen). Then it will be 0 when the panel is "On Screen", i.e. it will be back at the bottom of the screen.
<div className="ndl-image-with-background">
![](/library/modules/mapbox/guides/interacting/states-panel-1.png)
</div>
Finally hook up the outputs `Click` and `Map Moved` signal to change the state between "On Screen" and "Off Screen". Feed back the output "Y Position" to the **Pos Y** of the panel.
<div className="ndl-image-with-background l">
![](/library/modules/mapbox/guides/interacting/nodes-2.png)
</div>
Now you should be able to move around and click on the map, showing and hiding the panel printing the longitude and latitude.

View File

@@ -0,0 +1,49 @@
WIP, would be nice to work with the nodes.
```js
const mapbox = Inputs.MapboxObject;
const draw = new MapboxDraw({
displayControlsDefault: false,
// Select which mapbox-gl-draw control buttons to add to the map.
controls: {
polygon: true,
trash: true
},
// Set mapbox-gl-draw to draw by default.
// The user does not have to click the polygon control button first.
defaultMode: 'draw_polygon'
});
mapbox.addControl(draw);
function updateArea(e) {
const geometry = e.features[0].geometry;
Outputs.Geometry = JSON.parse(JSON.stringify(geometry));
Outputs.Coordinates = geometry.coordinates;
// const data = draw.getAll();
// const answer = document.getElementById('calculated-area') = Outputs.calculatedarea;
// if (data.features.length > 0) {
// const area = turf.area(data);
// // Restrict the area to 2 decimal points.
// const rounded_area = Math.round(area * 100) / 100;
// answer.innerHTML = `<p><strong>${rounded_area}</strong></p><p>square meters</p>`;
// } else {
// answer.innerHTML = '';
// if (e.type !== 'draw.delete')
// alert('Click the map to draw a polygon.');
// }
}
mapbox.on('draw.create', updateArea);
mapbox.on('draw.delete', updateArea);
mapbox.on('draw.update', updateArea);
// Add keydown event listener to the mapbox container
mapbox.getCanvas().addEventListener('keydown', (event) => {
if (event.keyCode === 13 || event.keyCode === 3) { // 13 is the keycode for the Enter key
draw.changeMode('simple_select');
}
});
```

View File

@@ -0,0 +1,45 @@
---
title: Getting Screen Coordinates of Markers
hide_title: true
---
# Getting Screen Coordinates of Markers
:::caution
With the new Mapbox module you only need to place your nodes as children to the Mapbox Marker, and they will be visible instead.
:::
This snippet is useful if you want to position something, for example a Noodl Component, on top of a Marker on the Mapbox Map.
<div className="ndl-image-with-background l">
![](/library/modules/mapbox/guides/screen-coordinates/meteor.png)
</div>
## How it works
Create a [Function](/nodes/javascript/function) node and paste in the code below
```javascript
const markers = document.querySelectorAll(".mapboxgl-marker");
if (!markers) return;
for (let i = 0; i < markers.length; i++) {
let m = markers[i];
let markerId = m.getAttribute("data-ndl-marker-id");
let rect = m.getBoundingClientRect();
Noodl.Object.get(markerId).setAll({
posX: rect.left,
posY: rect.top,
});
}
Outputs.done();
```
When calling **Run** on the **Function** node the screen coordinates will be written to the Marker array, in the two properties `posX` and `posY`. After the operation is finished the **Done** output signal will be triggered.

View File

@@ -0,0 +1,58 @@
---
title: Take a screenshot
hide_title: true
---
# Take a screenshot
Taking a screenshot of the map can be useful in many different scenarios,
for example saving navigation, directions, planning, record-keeping, and research purposes.
It can help visualize routes, distances, specific locations, and patterns in data.
:::info
This will not include Markers, to include Markers we have to take the screenshot in a different way.
:::
## Results
When taking the screenshot we will get an image the same size as the Mapbox element.
The output will be an image blob which can be passed into an Image node, upload as a file, or saved in the database.
Example of the screenshot/image data format:
```
data:image/png;base64,iVBORw0KGgoA...
```
This is what the image looks like:
<div className="ndl-image-with-background">
![](/library/modules/mapbox/guides/screenshot/map-screenshot.png)
</div>
## Steps
1. Create a Function node, with this code:
```js
const mapboxObject = Inputs.MapboxObject;
if (!mapboxObject) return;
Outputs.ImageDataUrl = mapboxObject.getCanvas().toDataURL();
```
2. Connect the Mapbox Output, `Mapbox Object` to the Function node.
3. Connect the `ImageDataUrl` output on the Function node to either an image or where you want to save it.
This image can be saved in the database too, but keep in mind that it is not recommended to save a lot of data in a column.
Here is an example of how it might look in the end:
<div className="ndl-image-with-background">
![](/library/modules/mapbox/guides/screenshot/nodeGraph.png)
</div>

View File

@@ -0,0 +1,66 @@
---
title: Setting up the Mapbox Module
hide_title: true
---
# Setting up the Mapbox Module
## What you will learn in this guide
This guide will show you how to include the Mapbox module in a project and set up an Mapbox API key to use for your application.
## Overview
We will go though the following steps:
- Install the Mapbox Module in a Noodl Project.
- Create an Access Token on the Mapbox account
- Create a minimal Mapbox App in Noodl
## Install the Mapbox Module
When in the project, open the "Module" tab in the Node Picker. Find the "Mapbox" module and click "Install".
<div className="ndl-image-with-background s">
![](/library/modules/mapbox/guides/setting-up/module-1.png)
</div>
After the module is imported, you should now be able to find the [Mapbox Map](library/modules/mapbox/nodes/v2/mapbox-map) node in the node picker. Right click in the node editor area to bring up the node picker. Look under "External libraries" to find the **Mapbox Map** node.
<div className="ndl-image-with-background s">
![](/library/modules/mapbox/guides/setting-up/nodepicker-1.png)
</div>
Add the node. Then put it as a child to your main App group. As you will see, you will immedieatly get a warning: _"No access token. Please specify one in the Project Settings and reload"_.
<div className="ndl-image-with-background">
![](/library/modules/mapbox/guides/setting-up/no-token.png)
</div>
So to use the Mapbox Module you need an access token from Mapbox. You can read more about Access tokens [here](https://docs.mapbox.com/help/getting-started/access-tokens/) and also follow instructions on how to create one. Among other things you will have to set up an account on Mapbox unless you already have one.
## Entering the Mapbox Access Token
Once you got the token (which will look like a long password) you open up the "Project Settings" panel by clicking the cogwheel in the main panel to the left. Copy/Paste the token into the field called "Mapbox Access Token"
<div className="ndl-image-with-background">
![](/library/modules/mapbox/guides/setting-up/token-1.png)
</div>
Once you've added in the token, reload the viewer, and you should now have a Mapbox map showing in your App, something like below.
<div className="ndl-image-with-background">
![](/library/modules/mapbox/guides/setting-up/screen-1.png)
</div>
You can play a little with the map. Pan and zoom.

View File

@@ -0,0 +1,134 @@
---
title: Mapbox Styles
hide_title: true
---
# Mapbox Styles
Mapbox styles are a collection of rules that define how a map is displayed. They include information about the map's layout, colors, labels, and symbols. Mapbox styles are used to create maps that are visually appealing and easy to read.
They can be customized to meet the needs of different users and applications, and can be used with Mapbox's suite of mapping tools to create interactive maps for web and mobile applications. Mapbox styles are based on the Mapbox Style Specification, which is an open-source specification for designing and sharing map styles.
<div className="ndl-image-with-background">
![](/library/modules/mapbox/guides/styles/preview.png)
</div>
## Prebuilt Styles
Mapbox have a few prebuilt styles, here are a few of them:
- Streets: A classic style that emphasizes roads, parks, and other features of urban areas.
- Outdoors: Designed for outdoor enthusiasts, this style includes topographic lines, hiking trails, and other natural features.
- Light: A minimalist style that focuses on the essentials of a map, with a light color scheme that's easy on the eyes.
- Dark: A more dramatic version of the Light style, with a dark color scheme that's perfect for nighttime maps.
- Satellite: This style uses satellite imagery to show real-world views of the Earth's surface, and is often used for mapping remote areas or monitoring changes in the environment.
- Navigation: A style optimized for driving and navigation, with a focus on road networks and landmarks.
You can find them [here](https://docs.mapbox.com/api/maps/styles/#mapbox-styles). Here is the same list with the styles:
<div className="ndl-table-35-65">
| Style | Source |
| ----------------- | -------------------------------------------- |
| Streets | mapbox://styles/mapbox/streets-v12 |
| Outdoors | mapbox://styles/mapbox/outdoors-v12 |
| Light | mapbox://styles/mapbox/light-v11 |
| Dark | mapbox://styles/mapbox/dark-v11 |
| Satellite | mapbox://styles/mapbox/satellite-v9 |
| Satellite Streets | mapbox://styles/mapbox/satellite-streets-v12 |
| Navigation Day | mapbox://styles/mapbox/navigation-day-v1 |
| Navigation Night | mapbox://styles/mapbox/navigation-night-v1 |
</div>
## Custom Styles
You can also use [Mapbox Studio](https://www.mapbox.com/mapbox-studio) to create your own custom styles.
In Mapbox Studio you will get a link which should be passed into `Style`, and you will have the custom style.
## Style Object
Looking at the Mapbox "Add a video" example, which can be [here](https://docs.mapbox.com/mapbox-gl-js/example/video-on-a-map/).
In this example a Javascript object is used to create the style.
Here is how we can recreate this example in Noodl.
1. Create a Function node, with this code:
```js
Outputs.MapStyle = JSON.stringify({
version: 8,
sources: {
satellite: {
type: "raster",
url: "mapbox://mapbox.satellite",
tileSize: 256,
},
video: {
type: "video",
urls: [
"https://static-assets.mapbox.com/mapbox-gl-js/drone.mp4",
"https://static-assets.mapbox.com/mapbox-gl-js/drone.webm",
],
coordinates: [
[-122.51596391201019, 37.56238816766053],
[-122.51467645168304, 37.56410183312965],
[-122.51309394836426, 37.563391708549425],
[-122.51423120498657, 37.56161849366671],
],
},
},
layers: [
{
id: "background",
type: "background",
paint: {
"background-color": "rgb(4,7,14)",
},
},
{
id: "satellite",
type: "raster",
source: "satellite",
},
{
id: "video",
type: "raster",
source: "video",
},
],
});
```
2. Connect the `MapStyle` output to the Mapbox `Style` input. And you will now have the style with a video.
3. To pause and play the video, like in the example.
We create another Function node with this code:
```js
const mapboxObject = Inputs.Map;
const playOrPause = Inputs.PlayOrPause;
if (!mapboxObject) return;
if (playOrPause) {
mapboxObject.getSource("video").play();
} else {
mapboxObject.getSource("video").pause();
}
```
And connect it to a Switch node, like in this picture:
<div className="ndl-image-with-background">
![](/library/modules/mapbox/guides/styles/video-nodegraph.png)
</div>
4. Now we have replicated the example by Mapbox.

View File

@@ -0,0 +1,189 @@
---
title: Using Markers
hide_title: true
---
# Using Markers in the Mapbox Module
## What you will learn in this guide
This guide will teach you how to add and remove markers to your Mapbox Map in Noodl.
## Overview
This guide will walk you through the following steps
- Adding markers to the map
- Capturing when the user clicks a marker
- Removing markers from the map
The guide will build on the two previous Mapbox guides, [Setting up Mapbox module](/library/modules/mapbox/guides/setting-up) and [Interacting with the Mapbox Module](/library/modules/mapbox/guides/interacting). It's recommended that you read those guides first.
## Markers
Markers are used to mark points in the map. They can be anywhere on the map.
<div className="ndl-image-with-background s">
![](/library/modules/mapbox/guides/using-markers/markers-1.png)
</div>
A marker can either be represented by the default icon, or any of the Noodl visual nodes.
In this guide we will go with the default icon. The default icon can also have a color of your choice.
Markers are placed as children to the Mapbox Map node.
To change the marker to any of the Noodl visual nodes, just place them as a children in the marker.
<div className="ndl-image-with-background">
![](/library/modules/mapbox/guides/using-markers/add-marker-1.png)
</div>
In our case we would like to create the markers dynamically when you click somewhere on the map.
To achieve this we will create a new component that can be used in a Repeater.
Since this component is going to used in a Repeater, lets also create the Component Inputs node with a few parameters.
<div className="ndl-image-with-background">
![](/library/modules/mapbox/guides/using-markers/component-1.png)
</div>
Next we will create an array that is going to contain the information about each marker.
Each [Object](/nodes/data/object/object-node) in the **Array** should be of the format:
```
{
"Lat":<the latitude>,
"Lon":<the longitude>,
"Color":<the color, e.g, "White" or as hex-string "#ffffff". Only used with the default icon.>
}
```
So it matches the properties we added in the component.
First we add an **Array** node. Give it the **id** `Map Markers`.
<div className="ndl-image-with-background">
![](/library/modules/mapbox/guides/using-markers/panel-1.png)
</div>
This will feed the markers to a Repeater that is using our new marker component, so connect its output **Items** to the **Markers** input on the **Repeater** node.
<div className="ndl-image-with-background l">
![](/library/modules/mapbox/guides/using-markers/nodes-1.png)
</div>
Now we need to add new **Objects** to the **Array** whenever the user clicks on the map. Add a [Create New Object](/nodes/data/object/create-new-object) node and make sure you can set the three properties `Lon` (Number), `Lat` (Number) and `Color` (String) when you create it.
<div className="ndl-image-with-background l">
![](/library/modules/mapbox/guides/using-markers/nodes-2.png)
</div>
<div className="ndl-image-with-background s">
![](/library/modules/mapbox/guides/using-markers/panel-2.png)
</div>
We want to set the `Lat` and `Lon` to the position the user clicks, so connect the `Latitude` and `Longitude` outputs in the group "Map Clicked" from the Mapbox to the them. We can hard code the color to `Black` for now, so just enter `Black` directly in the properties panel of the **Create New Object** node. Then we connect the **Click** event from the **Mapbox Map** node to the **Do** signal of the **Create New Object** node.
<div className="ndl-image-with-background l">
![](/library/modules/mapbox/guides/using-markers/nodes-3.png)
</div>
The newly created **Object** need to be added to the **Array** that holds the markers. So by adding a [Insert Object Into Array](/nodes/data/array/insert-into-array) node, connecting the **Done** signal from the **Create new Object Node** to **Do** and also connecting the **Id** to **Object Id** we are almost done. We just need to make sure the insert happens in the correct **Array**. So either hardcode the **Array Id** to `Map Markers`, or connect the **Id** of the **Array** to the **Array Id** of the **Insert Object Into Array** node.
<div className="ndl-image-with-background l">
![](/library/modules/mapbox/guides/using-markers/nodes-4.png)
</div>
If everything was done correctly, you should now be able to place black markers on the map.
<div className="ndl-image-with-background">
![](/library/modules/mapbox/guides/using-markers/screen-2.png)
</div>
## Capturing when the user clicks a marker
Next step is to be able to do something when the user clicks a marker. So we need to differentiate between clicking on the map and clicking on a marker.
Conveniently there is an additional outgoing signal on the **Mapbox Marker** node called **Click**.
Lets connect the click signal to Component Outputs so we can use it in our main component.
<div className="ndl-image-with-background l">
![](/library/modules/mapbox/guides/using-markers/component-2.png)
</div>
So let's change up the logic slightly, so the panel either shows the marker that was clicked, or the newly created marker if the user didn't click the marker but on the map instead.
Using [Variable](/nodes/data/variable/variable-node) and [Set Variable](/nodes/data/variable/set-variable) node and some logic around that, we make sure a **Variable** holds the current marker. See the [guide](/docs/guides/data/variables) on using **Variables** if you want to know more about how to use them.
Also, the **Marker Click** signal now makes the panel visible as well. The full node design can be seen below.
<div className="ndl-image-with-background l">
![](/library/modules/mapbox/guides/using-markers/nodes-5.png)
</div>
Finally, let's update the panel logic. We want to make sure the latitude/longitude values comes from the marker and not from where you clicked on the map. It won't make a difference now, but we might want to capture more information in the marker to show in the panel, so this is a better design.
The `Current Marker` Variable holds the **Id** of the current marker, so just connect it to an **Object** and take out the `Lon` and `Lat` properties from it. The updated design is shown below.
<div className="ndl-image-with-background l">
![](/library/modules/mapbox/guides/using-markers/nodes-6.png)
</div>
## Removing Markers
Finally we want to be able to remove markers. It's very easy, just remove them from the Marker **Array**.
So let's add a [Button](/nodes/ui-controls/button) to the panel. Remove its label and instead add an icon that represents "delete". Also make it smaller by adapting its padding and size. Finally make it red so it's clear it's a destructive action.
<div className="ndl-image-with-background s">
![](/library/modules/mapbox/guides/using-markers/button-1.png)
</div>
<div className="ndl-image-with-background l">
![](/library/modules/mapbox/guides/using-markers/nodes-7.png)
</div>
<div className="ndl-image-with-background s">
![](/library/modules/mapbox/guides/using-markers/button-panel-1.png)
</div>
<div className="ndl-image-with-background">
![](/library/modules/mapbox/guides/using-markers/screen-3.png)
</div>
We connect the functionality for the button. We add a [Remove Object From Array](/nodes/data/array/remove-from-array) node and make sure the **Object Id** is what's stored in the `Current Marker` **Variable** and the **Array Id** comes the same **Id** as the `Map Markers` **Array**.
The **Click** signal from the **Button** is connected to **Do** on the **Remove Object From Array** node. Finally we also close the panel when the removal is **Done**.
This is what the final node construct looks like. If you want to import the final project click the "Import" button: