noodl-docs icon indicating copy to clipboard operation
noodl-docs copied to clipboard

Module(Mapbox): Custom Geocoder component

Open erictuvesson opened this issue 2 years ago • 0 comments

Improve documentation

Link

https://docs.noodl.net/2.9/library/modules/mapbox/guides/geocoder/

Describe the improvement

We should create a page descripting how to create a custom Geocoder / Address search component.

Additional context

Here is an example: https://mapbox-module-test.sandbox.noodl.app/geocoding-api

image

Search:

// 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: [Script.Inputs.lng, Script.Inputs.lat].join(','), // 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
    language: 'en-GB'
  };

  // 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();
  },
};

Center to the clicked item:

const items = Inputs.Items;
const itemId = Inputs.ItemId;

const item = items.find((x) => x.id === itemId);
if (!item) throw new Error("Cannot find clicked item.");

// The center geopoint of the clicked item.
const geopoint = item.center;

const mapboxObject = Inputs.MapboxObject;
if (!mapboxObject) throw new Error("Mapbox Object is invalid.");

mapboxObject.flyTo({
    center: [geopoint.latitude, geopoint.longitude],
    essential: true,
    zoom: 15
});

The repeater item is just a button that is outputting a Click signal.

erictuvesson avatar Sep 20 '23 10:09 erictuvesson