DEV Community

Samuel Earl
Samuel Earl

Posted on • Updated on

Start with a Map - Building a Geospacial App with SvelteKit, Deck.gl, and Mapbox - Part 1

The tutorial titled Building a Geospacial App from Vis Academy is written using React. I wanted to use Deck.gl in a Svelte app (specifically SvelteKit), so I am rewriting that tutorial using SvelteKit. I also borrowed some ideas from this tutorial: Large Scale Geospatial Visualization with Deck.gl, Mapbox-gl and Vue.js. Here it goes.

What is Deck.gl?

Deck.gl is an opensource visualization framework created by Uber. It allows you to create things like maps with data layers over the top of the maps. Deck.gl is built to handle large datasets while providing high-performance visualizations on the web. Deck.gl is part of the vis.gl opensource visualization framework suite.

Setup

SvelteKit

Create your SvelteKit project following the instructions on their website: SvelteKit.

In your root folder create a .env file and add this entry:

VITE_MAPBOX_API_ACCESS_TOKEN=you will paste your Mapbox API token here
Enter fullscreen mode Exit fullscreen mode

Get a Mapbox API token

Mapbox GL JS is a client-side JavaScript library for building web maps and web applications with Mapbox's modern mapping technology. You can reference the API documentation here. Mapbox also has a pretty extensive examples page here where you can learn to do a lot of cool stuff.

You need a free Mapbox token in order to get the map to load. Head over to Mapbox and create an account (or sign in if you already have an account). Once you are signed in, click the "Account" navigation link. You should see a section in your account labelled "Default public token". Copy that token and paste it in your .env file.

Create the Map

The mapbox-gl npm package is the MapboxGL mapping library. This package makes it super easy to drop a mapping component into your application. Install mapbox-gl with this command:

npm install mapbox-gl
Enter fullscreen mode Exit fullscreen mode

I will show you the map code first and then I will explain it afterwards.

// src/routes/index.svelte

<div id="map" bind:this={mapElement}></div>

<script>
  import { onMount } from "svelte";
  import mapboxgl from "mapbox-gl";

  let mapElement;
  let map = null;
  let accessToken = import.meta.env.VITE_MAPBOX_API_ACCESS_TOKEN;
  let mapStyle = "mapbox://styles/mapbox/light-v9";
  let viewState = {
    longitude: -118.2443409,
    latitude: 34.0544779,
    zoom: 2,
    pitch: 0,
    bearing: 0,
  };

  onMount(() => {
    createMap();
  });

  function createMap() {
    map = new mapboxgl.Map({
      accessToken: accessToken,
      container: mapElement,
      interactive: true,
      style: mapStyle,
      center: [viewState.longitude, viewState.latitude],
      zoom: viewState.zoom,
      pitch: viewState.pitch,
      bearing: viewState.bearing,
    });
  }
</script>

<style>
  #map {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: #e5e9ec;
    overflow: hidden;
  }
</style>
Enter fullscreen mode Exit fullscreen mode

In the HTML
As you can see we created an element that will be used to display our map: <div id="map" bind:this={mapElement}></div>.

In the <script> tag
There is nothing too complex here. We simply set some configuration variables and wrote a function that creates a new map with those configuration variables. You can read through the code to get an idea of what it all does.

In the <style> tag
These styles are simply setting the position and size of the map that is rendered in the browser.

Changing Map Styles

I will show you how to add a select box that will be populated with some additional styles and which can be used to change the appearance of the map.

Create a components folder inside your src directory. Then create a MapStylePicker.svelte file inside your components folder and paste this code inside:

// src/components/MapStylePicker.svelte

<select
  class="map-style-picker"
  bind:value={currentStyle}
  on:change
>
  {#each MAPBOX_DEFAULT_MAPSTYLES as style (style.value)}
    <option value={style.value}>
      {style.label}
    </option>
  {/each}
</select>

<script>
  export let currentStyle;

  const MAPBOX_DEFAULT_MAPSTYLES = [
    { label: "Streets V10", value: "mapbox://styles/mapbox/streets-v10" },
    { label: "Outdoors V10", value: "mapbox://styles/mapbox/outdoors-v10" },
    { label: "Light V9", value: "mapbox://styles/mapbox/light-v9" },
    { label: "Dark V9", value: "mapbox://styles/mapbox/dark-v9" },
    { label: "Satellite V9", value: "mapbox://styles/mapbox/satellite-v9" },
    {
      label: "Satellite Streets V10",
      value: "mapbox://styles/mapbox/satellite-streets-v10"
    },
    {
      label: "Navigation Preview Day V4",
      value: "mapbox://styles/mapbox/navigation-preview-day-v4"
    },
    {
      label: "Navitation Preview Night V4",
      value: "mapbox://styles/mapbox/navigation-preview-night-v4"
    },
    {
      label: "Navigation Guidance Day V4",
      value: "mapbox://styles/mapbox/navigation-guidance-day-v4"
    },
    {
      label: "Navigation Guidance Night V4",
      value: "mapbox://styles/mapbox/navigation-guidance-night-v4"
    }
  ];
</script>

<style>
  .map-style-picker {
    background-color: #fff;
    position: absolute;
    top: 20px;
    left: 20px;
    z-index: 100;
  }
</style>
Enter fullscreen mode Exit fullscreen mode

The MapStylePicker component is just a <select> element with some style options hard coded inside.

Now import MapStylePicker into your index file. Here is the updated code:

<MapStylePicker currentStyle={mapStyle} on:change={handleStyleChange}/>
<div id="map" bind:this={mapElement}></div>

<script>
  import { onMount } from "svelte";
  import mapboxgl from "mapbox-gl";
  import MapStylePicker from "$/components/MapStylePicker.svelte";

  let mapElement;
  let map = null;
  let accessToken = import.meta.env.VITE_MAPBOX_API_ACCESS_TOKEN;
  let mapStyle = "mapbox://styles/mapbox/light-v9";
  let viewState = {
    longitude: -118.2443409,
    latitude: 34.0544779,
    zoom: 2,
    pitch: 0,
    bearing: 0,
  };

  onMount(() => {
    createMap();
  });

  function createMap() {
    map = new mapboxgl.Map({
      accessToken: accessToken,
      container: mapElement,
      interactive: true,
      style: mapStyle,
      center: [viewState.longitude, viewState.latitude],
      zoom: viewState.zoom,
      pitch: viewState.pitch,
      bearing: viewState.bearing,
    });
  }

  function handleStyleChange(event) {
    map.setStyle(event.target.value);
  }
</script>

<style>
  #map {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: #e5e9ec;
    overflow: hidden;
  }
</style>
Enter fullscreen mode Exit fullscreen mode

The main thing to notice here is the handleStyleChange() function. It uses the setStyle function from mapbox-gl to update the map style. (See https://docs.mapbox.com/mapbox-gl-js/example/setstyle/.)

Now you should be able to change the styles of the map.

If you feel creative, you can also create your own styles in Mapbox Studio and put their url (starting with http://api.mapbox.com/styles/) in the state instead.

onViewportChange Callback

The "Building a Geospacial App" tutorial has a section that shows how you can add an onViewportChange callback to the map to enable user interactions (e.g. panning, zooming, rotating, etc). However, that interactivity code appears to be enabled by default now (or at least in non-React environments it is enabled by default). So I am going to skip to the next section.

Adding Polish

This section of the "Building a Geospacial App" tutorial also seems unnecessary. That tutorial specifies the width and height of the map with window.innerWidth and window.innerHeight, which would require a callback in the JavaScript code to update the dimensions of the map. But I am using CSS to set the dimensions of the map, so there is no need for a callback function.

Next

That's it for this part of the tutorial. In the next tutorial I will show you how to add a data layer on top of your map.

Top comments (0)