ArcGIS Maps SDK for JavaScript
Note: This tutorial was written for those with some experience coding. As such I won’t be explaining basic concepts or terms but should still be pretty understandable for beginners.
You will also need to have a local dev environment set up along with accounts with GitHub, Esri, and Netlify.
To get the key go to developers.arcgis.com and sign up for a developers account. Once you’ve got that signed in go to your dashboard and you’ll see an API keys tab.
**Note: I’ve had a few Esri accounts over the years, from school to personal, and depending on what log in I use I am not able to change the account to a developers one for some reason. If you’ve had an account with them before and you don’t see the API keys tab, you may need a new one.
Step One - Setting up the base map
Feel free to simply clone my getting started branch from my repo to get the basics ready while you follow along.
For this map we aren’t going to use any local data and instead are going to rely on a layer already hosted by ArcGIS server. However because I like to be prepared and have the ability to change that later we are still going to set up using Webpack like I’ve done in the past before. It will also allow us to keep our HTML, CSS, and JavaScript separate, to be bundled together later when we publish our map using Netlify.
If you’ve cloned the getting started branch you should see the three Webpack config files, you’ll note in the common one I left in but commented out the CopyPlugin for data and an ArcGIS Plugin - you don’t need either for this tutorial but if you expand on it you may need them. Checkout the Mapbox GL JS Tutorial Part 1 and Part 2 for a more in-depth discussion of Webpack. There is a README file with starting instructions, a package.json and git ignore file.
In the package.json you’ll see we loaded in @arcgis/core which will give us all the features we’ll need for our map; everything else supports the Webpack loading and bundling.
In the src folder you’ll see our base HTML, CSS, and JavaScript files. Our index.html is pretty simple with just a div with the id and class “map” and our main.css has some attributes for that to give it height and width on the page.
Our index.js file imports the CSS file and three items from the @arcgis/core package. For this API to keep it small and only load what you need we will be adding to this import list as we add features. From there you’ve got your API key and your definition of the basemap. Then a view that tells it where to place the map(the div with the id “map”), where to center the map, and what zoom to start at.
//index.html ... <body> <div id="map" class="map"></div> </body> ... //main.css ... .map { padding: 0; margin: 0; height: 100%; width: 100%; position: absolute; } //index.js import './main.css'; import Map from "@arcgis/core/Map.js"; import MapView from "@arcgis/core/views/MapView.js" import esriConfig from "@arcgis/core/config.js"; esriConfig.apiKey = 'XXXX-XXXX-XXXX'; const map = new Map({ basemap: "arcgis-topographic" // Basemap layer service }); const view = new MapView({ map: map, center: [-94.5, 38.7951], // Longitude, latitude zoom: 4, // Zoom level container: "map" // Div element });
Go ahead and do npm install and npm run start and see what you get. You should see a basic map centered on North America - since that will the subject of our layers today.
Note: Unlike some other map APIs Esri “loads” a map even if you don’t have the API key correct or available. If you forgot to put in your unique key and did npm run start you’ll still see something on the page, a very basic hillshade in this case. If you open the console log you’ll see there was a failure to load the layers.
Step Two - Adding a Data Layer
For this map our main center of focus is going to be data from the Commission of Environmental Cooperation on North American Terrestrial Species of Common Conservation Concern which is available in Esri’s Living Atlas. You may find it useful to open the data up in the Map Viewer so you can can do some exploring before we start adding it to our own map. You can also view the service so you can see more details on the layer including the fields.
On the item’s page on the right hand side you’ll see a URL for the service on the server: ‘https://services7.arcgis.com/oF9CDB4lUYF7Um9q/arcgis/rest/services/NA_Terrestrial_Species_of_Common_Conservation_Concern/FeatureServer’
In order to use this we need to add the Feature Layer from the @arcgis/core, then create a new FeatureLayer and add it to the map.
//other imports import FeatureLayer from "@arcgis/core/layers/FeatureLayer.js"; . . . const terrestrialSCCCLayer = new FeatureLayer({ url: "https://services7.arcgis.com/oF9CDB4lUYF7Um9q/arcgis/rest/services/NA_Terrestrial_Species_of_Common_Conservation_Concern/FeatureServer" }); map.add(terrestrialSCCCLayer);
You should see some colored polygons added to the map using the pre-existing definitions provided by the service. Great but that’s not very helpful for our user - plus you definitely can’t see all the polygons or the map below them. Let’s add some attributes to our feature layer starting with opacity. We’ll make the opacity 0.7 which allows the base map below to show through a bit for some added context.
It would also be nice if we can add popups. This is a great feature of the FeatureLayer. First we need to look at the fields available to us and pick what we want outputted for us to access. Let’s choose the English name, the scientific name and the family.
const terrestrialSCCCLayer = new FeatureLayer({ url: "https://services7.arcgis.com/oF9CDB4lUYF7Um9q/arcgis/rest/services/NA_Terrestrial_Species_of_Common_Conservation_Concern/FeatureServer", opacity: 0.7, outFields: ["ENGL_NAME", "SCI_NAME", "FAMILY"], });
Then we need to create the popup template to add to the feature layer. Above the feature layer add a constant of popupTerrestrialSCCC which is going to contain all our information. We will start with the title being the English name (i.e. American black bear) and then an array of content which we will make as a fields content element which will give us a small table of the details.
const popupTerrestrialSCCC = { "title": "{ENGL_NAME}", "content": [{ "type": "fields", "fieldInfos": [ { "fieldName": "SCI_NAME", "label": "Scientific/Latin Name", "isEditable": false, "tooltip": "", "visible": true, "format": null, "stringFieldOption": "text-box" }, { "fieldName": "FAMILY", "label": "Family", "isEditable": false, "tooltip": "", "visible": true, "format": null, "stringFieldOption": "text-box" }, ] }] };
Now when you open your map you should be able to click on the map and see a popup. Esri does some extra work for us here by checking if there are features on top of each other at your click spot and giving you a popup with multiple selections and highlighting the corresponding polygons.
If you want to style your popup differently checkout the API documentation for Popup Content for your options.
Step Three - Adding a Filter to the Data
The popup looks good and it helps a user visualize the range of the species but I don’t really like that the Monarch Butterfly range takes up most of North America. I’d like for a user to be able to filter the data to see just a single species range at a time. To do this we can use an Esri widget to create a drop down filter.
First we need to come up the list of queries we want to give the user, in our simple example it’s just going to be a list of species. You can then use document.createElement to create the select filter drop down and using the list of queries populate the filter choices. Then using the map view ui I add the element to my screen.
//SQL query array const terrestrialSCCCSQL = ["Choose a species","All species","American black bear","Black-tailed prairie dog","Burrowing owl","California condor", "Ferruginous hawk","Southern long-nosed bat", "Peregrine falcon","Gray wolf","Golden-cheeked warbler","Loggerhead shrike", "Mexican long-nosed bat","Monarch butterfly", "Mountain plover", "Piping plover", "Sonoran pronghorn", "Spotted owl"]; //Add SQL UI const selectFilter = document.createElement("select"); selectFilter.setAttribute("class", "esri-widget esri-select"); selectFilter.setAttribute("style", "width: 250px; font-family: 'Avenir Next'; font-size: 1em"); terrestrialSCCCSQL.forEach(function(query){ let option = document.createElement("option"); option.innerHTML = query; if(query==="Choose a species"){ option.value = "1=0" } else if(query==="All species"){ option.value = "1=1" } else { option.value = "ENGL_NAME='"+query+"'"; } selectFilter.appendChild(option); }); view.ui.add(selectFilter, "top-right");
You’ll notice that I’ve add some if’s to my forEach function. First I’d like the top “query” to be the users prompt - if they select that it will clear the map and if they select all species it will take them back to the original map load. The final option is for the query itself - to create the query for the species range in question.
Now we will add a function that creates a definition expression based on an expression query we send to it. This is a simple attribute update for the layer. As you can see below you can also add this directly to the feature layer on load if you wanted to load queried layer first. This new function needs to be called every time a user makes a change in the drop down. So we add an event listener to listen for the change and pass on our event target’s value, which we’ve already made to equal the query we want.
//Update the definition expression const setFeatureLayerFilter = (expression) => { terrestrialSCCCLayer.definitionExpression = expression; } // Listen for changes selectFilter.addEventListener('change', (event) => { setFeatureLayerFilter(event.target.value); }); const terrestrialSCCCLayer = new FeatureLayer({ url: "https://services7.arcgis.com/oF9CDB4lUYF7Um9q/arcgis/rest/services/NA_Terrestrial_Species_of_Common_Conservation_Concern/FeatureServer", ... definitionExpression: "1=1" //optional });
Now our users can see exactly where the range of the Sonoran pronghorn (or any of the awesome species listed) is without any other species being displayed on the map.
Step Four - Adding a legend and basemap selections
The final step for this data layer I would like to see is adding a legend and options to change the basemap. Luckily Esri makes this part very simple. By importing the Legend widget from @arcgis/core we can quickly add it to our view and it will appear on our map with the title of the layer and all the species colors listed.
import Legend from "@arcgis/core/widgets/Legend.js"; let legend = new Legend({ view: view }); view.ui.add(legend, "bottom-left");
Adding the basemap toggle is also just as simple as importing some new widgets and and loading them up. We’ll choose the imagery options as our other basemap choice. You should see the toggle in the bottom right and being able to click between the two without losing the data on the map.
import BasemapToggle from "@arcgis/core/widgets/BasemapToggle"; import BasemapGallery from "@arcgis/core/widgets/BasemapGallery.js"; const basemapToggle = new BasemapToggle({ view: view, nextBasemap: "arcgis-imagery" }); view.ui.add(basemapToggle,"bottom-right"); const basemapGallery = new BasemapGallery({ view: view, source: { query: { title: '"World Basemaps for Developers" AND owner:esri' } } });
Step Five - Publishing to Netlify
After you’ve finished your own polishing touches you should have a pretty functional map, but it’s only local right now- you want to share your creation with others. There are a number of ways to get your code out on the web (for free) and I recommend you look into all your options, but for this tutorial we are going to use Netlify as it’s a very simple process. Hopefully as you’ve been following along you’ve been committing your code and pushing it to your GitHub repo. This is one of the ways you can publish to Netlify.
One of the most important items is having a build command available. By default Netlify is looking for ‘npm run build’ which we conveniently have in our package.json already.
Note: If you are planning on running your code from a public GitHub repo remember that your ArcGIS Maps API Access token will be accessible from there. You could run it from a private repo or you can, and should for good measure anyways, also go to your API key settings and make it only accessible to a specific website URL (look for the Referrers section).
Go to Netlify and create an account if you haven’t already. Click the Deploy to Netlify button and connect it to your GitHub account and your updated repository. You may need to include the build command (npm run build) and the publish directory (dist) if it doesn’t automatically include it. Click Save and Deploy and wait for the build to finish. To start Netlify will assign a random production URL to your site. For example when I first deployed this it was assigned the url ‘golden-wisp-b82cf6’. You can leave as is or you can change it in the Settings for Domain management.
If you do change the site name be sure to add it to the API key referrers list to protect your token.
Great job! Hopefully your deploy worked and you have a functioning site. You can visit the link below to see my working tutorial published via Netlify.