Start with a Map - Building a Geospacial App with SvelteKit, Deck.gl, and Mapbox - Part 1
Tags: svelte, sveltekit, deckgl, mapbox
Published: 2022-07-13 21:31:00

---
series: Building a Geospacial App with SvelteKit, Deck.gl, and Mapbox
---

The tutorial titled [Building a Geospacial App](http://vis.academy/#/building-a-geospatial-app/setup) from Vis Academy is written using React. I wanted to use Deck.gl in a Svelte app (specifically [SvelteKit](https://kit.svelte.dev/)), 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](https://dev.to/localeai/large-scale-geospatial-visualization-with-deck-gl-mapbox-gl-and-vue-js-54im). 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](https://vis.gl/) opensource visualization framework suite.

# Setup
## SvelteKit
Create your SvelteKit project following the instructions on their website: [SvelteKit](https://kit.svelte.dev/).

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
```

## 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](https://docs.mapbox.com/mapbox-gl-js/api/). Mapbox also has a pretty extensive examples page [here] (https://docs.mapbox.com/mapbox-gl-js/example/) where you can learn to do a lot of cool stuff.

You need a free [Mapbox](https://www.mapbox.com/) 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
```

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

```vue
// 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>
```

**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:

```vue
// 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>
```

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:

```vue
<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>
```

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](https://www.mapbox.com/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.