My First Mapbox Project: Building a Locator

A while I ago, I went to a Map meetup, where I was introduced to Mapbox – I have always been kind of curious about GIS mapping since I interned at SF Bicycle Coalition, where they had another intern worked with GIS maps.

Recently, I finally got back to the codebase that I started during that meetup. The tutorial I did was a map of parks. I wanted to do a locator, and eventually replace the geojson file with park location data with bike shop data – turns out mapping APIs typically uses json-like file call geojson.

Well, not just bike shop – SFBike members get discounts at specific bike shop and even general stores like groceries and book store. I want to do a map of SFBike-related business. That means I would be writing a scraper for the SFBike member page, and then processing Yelp’s API for store hours so the map will display only opened stores.

Lo and behold, Mapbox have a locator tutorial! And while I do that, let put it on Github so I can keep track of my progress!

However, as I code, I came across 2 major problem: how to get a dataset that I already uploaded on Mapbox (most tutorials seems to use local files?) and how to protect my API key in a simple HTML/CSS/JS site without JS framework?

Here’s what I learned and did to troubleshoot:

Retrieving GeoJson Dataset

I tried reading the tutorials first, but it seems to mostly using local files that is already there or add Sources before extracting the sources – that’s whether convoluted…

At one point, I tried queryRenderedFeatures(), but nope, not working. I think the features gets rendered later, so even though I know there is features created from the mapbox tileset, I can’t grab them.

By the way, the flow of mapbox is

  1. Often time, the data are in geoJSON format. GeoJSON can contain FeatureCollection object, which is an object that contain a geometry object and additional properties called features.
  2. In Mapbox, a Dataset is needed, and it is often uploaded GeoJSON.
  3. Dataset creates what’s called a Tileset.
  4. The Tileset can be styled with Style, which is documents that defines the visual appearance of a map.

But first – back to getting the data.

I started searching in the documentation, when I started wondering why there is so many curl when the tutorial never mentions it – only to realize there is a selector for different programming languages on top, JavaScript included.

I resisted the urge to hit the table. The answer was right there!

Here’s what I did to get the uploaded code from Mapbox Studio:

1
2
3
4
5
6
7
8
9
10
11
12
13
map.on('load', function() {
// Retrieve the geoson dataset uploaded in Mapbox Studio
const mapboxClient = mapboxSdk({ accessToken: mapboxgl.accessToken });
mapboxClient.datasets
.listFeatures({
datasetId: 'some datasetId'
})
.send()
.then(
response => { builLocatioList(response.body) },
error => console.log(error)
)
})

API Key in Working .env File, No JS Framework Needed

Since I wanted to keep it to JavaScript, I thought I would use a .env to store the key – but don’t I usually use framework with that? I don’t want a framework for now, so let’s search for ways to use .env without framework!

… Damn, all the express and node solutions online looks ways too complicated for such simple needs (I just need an .env file working!). There seems to be a simple one. But once I try to use process.env.APIKEY, it says that process is not defined.

Maybe I need to require the dotenv package? I mean, I did it in server.js, but maybe they need it in index.js?

Now it is saying require is not defined.

*Flips table, or at least thinks about it*

After some search, I learned that require is for the server side of the app, not the client side. After reading about multiple options, including an hour tweaking with RequireJS, I decided to go with webpack. Basically, I just need add a simple plugin code in my webpack config file:

1
2
3
4
node: { fs: 'empty' },
plugins: [
new Dotenv()
]

The fs: ’empty’ was added because the error code keep saying Can’t resolve fs.

So here are my final file directory structure:
maptime-mapbox-park
-dist
-bundle.js
-node_modules
-public
-index.html
-index.js
-stylesheet.css
-.env
-.gitignore
-package-lock.json
-package.json
-README.md
-webpack.config.js

The steps to create a simple framework-less node app is as follow:

    1. In your project directory root, enter command: touch .env .gitignore README.MD
    2. Inside .gitignore, make sure to include .env and dist. If not, when you push it up to Github, everyone will know your API key! Also add node_modules and any files or directories that you don’t want to upload to Github.
    3. Create a directory named public (some people like app). Inside, create files you needed. You will want index.html and index.js at least.
    4. Now do an npm init -y, which will create a package.json file with default content. You can always change the content later.
    5. Do an npm install express express path. Then npm install -D dotenv-webpack webpack webpack-cli.
    6. Now, time to create your own webpack.config.js file. The output variable will determine what the bundled file output to. In this case, it would be dist/bundle.js.
    7. Since the output bundle file will be at dist/bundle.js, make sure the index.html will use that file instead of index.js. Replace what would normally be <script src=index.js> to <script src = [new bundle file path]>.
    8. Now in index.js, I can use require(‘dotenv’).config();!
    9. Make sure your .env contains your API keys!
    10. When the code is set up, run npm run build to create the bundle file. Everthing looks good? No error? Then go and do a npm run start.
    11. Everything’s setup. You should see your working page in the index.html page.