Over the rest of this course, we’ll be working with version 4.x of the ArcGIS API for JavaScript. This version represents a major change from the previous one in terms of both coding syntax and what you can do with the API. Probably the biggest upgrade in the API is in the ability to develop 3D apps that run directly in the browser without the need for a plugin. One of the best resources for ArcGIS JS API developers is the API’s software development kit (SDK) found at ArcGIS API for JavaScript. It provides a slew of sample pages that demonstrate how to accomplish various tasks with the API. A good method for learning the API, especially for novice developers, is to find a sample that does something close to what you’d like and modify it to your liking. So we’ll develop our first simple apps by taking that approach.
At the successful completion of this lesson, you should be able to:
If you have any questions now or at any point during this week, please feel free to post them to the Lesson 4 Discussion Forum. (That forum can be accessed at any time by clicking on the Discussions tab.)
Lesson 4 is one week in length. (See the Calendar in Canvas for specific due dates.) To finish this lesson, you must complete the activities listed below. You may find it useful to print this page out first so that you can follow along with the directions.
Step | Activity | Access/Directions |
---|---|---|
1 | Work through Lesson 4. | Lesson 4 |
2 | Complete the two-part "Plot Your Hometown" project. On your e-portfolio index page:
|
Follow the directions throughout the lesson and on the "Plot Your Hometown" page. |
3 | Take Quiz 4 after you read the online content. | Click on "Lesson 4 Quiz" to begin the quiz. |
As mentioned earlier, modifying existing sample code is a common way to begin learning a new programming language or API. Thankfully, Esri offers a "sandbox" environment for experimenting with the samples found in their SDK. Let's use this sandbox to start our work with the API.
An exciting feature of version 4 of Esri's JavaScript API is its support for 3D scenes. Let’s have a look at a basic 3D sample.
Note: Most of you have probably explored a 3D map like this one, though you may not be aware of all of the navigation options available to you. Read about the various mouse and keyboard controls and their associated effects in the documentation of the SceneView class, and be sure to try them out!
Also note that in addition to the Open in CodePen option, the sample pages also offer an Explore in the sandbox option. This is Esri's own sandbox for experimenting with their samples, much like CodePen.
The samples in the SDK are written with the CSS and JavaScript code embedded within the HTML code. This is fine for simple apps, but for more complex apps it is recommended that you write the CSS and JavaScript in separate files. Among the benefits to this approach are:
Let's revisit the Intro to MapView (2D) sample in CodePen, looking to follow a code separation approach.
Playgrounds like Esri’s JS SDK sandbox and CodePen are great tools for experimenting with samples and developing your own apps from scratch, but when you want to “go live” with an app so that others can use it, you’ll want to host your code on a web server. You used a web hosting service in the previous lesson and we’re going to use that service again now.
When building apps, a convention that many developers follow is to create a folder on the web server for their site/app, and into that folder place an HTML file called index.html (or default.html). For example, in building my “app_to_end_all_apps” site, I’d create a sub-folder by that name in my www folder, then upload my app’s HTML markup in a file named index.html to that sub-folder. I could then view my app using the URL: http://detwilergeog863.000webhostapp.com/app_to_end_all_apps/. (Note: This app doesn't actually exist, so you should expect a File Not Found error if you follow the link.) I could omit the index.html from the URL since browsers are built to look for a file by that name when the URL ends in a folder.
<link rel="stylesheet" href="main.css"></link>
<script src="main.js"></script>Note: These references use relative paths to locate the files; the browser will look for the files in the same folder as the HTML file since no folders were included in the paths. Because the architecture of this app is not particularly complicated, I’m directing you to store all of the files together. But you should keep in mind that many developers prefer to store their files in sub-folders (e.g., css/, js/ or styles/, scripts/) for organizational reasons.
For extra practice, go through the process of copying the 3D sample code to CodePen and then create a site called hello_scene in your web space.
Now that you’ve had a chance to dive right into building your own map apps, it’s time to learn more about what’s happening in those apps before you can go further with the API. Let's examine these start-up pages and discuss their HTML, CSS and JS pieces, beginning with the HTML of the 2D sample. Be sure to open up each of the three files so you can follow along with the discussion.
After completing the previous lesson, you should recognize the <html>, <head> and <body> tags. You should also recognize that the <!DOCTYPE> directive declares that the document is written as HTML 5.
The <head> of the document contains two <meta> elements. The second of these is concerned with rendering the document in a user-friendly way across a variety of devices (part of the larger CSS topic of responsive web design). This is not critical for you to worry about now, but you may want to return to this topic if you are developing for mobile phones or tablets. The first meta element specifies that your document is encoded in the UTF-8 character set, one that supports all of the world's major languages. It's a good idea to include this line verbatim in all of your map pages. If you're an insomniac, you may want to read more about character encoding.
The <head> also contains the page’s title, links to two external stylesheets (your own main.css file and Esri’s main.css file), and two script elements (one referencing the Esri JS API and the other your local main.js file). We’ll discuss the CSS and JS source code in detail shortly.
Next in the HTML file comes the body of the document, in which the only thing you'll find is a div element. This element is used to create a division, or section, in a document. In a web mapping context, a div is typically used as the container for the page's map. Note that this div is assigned an id (viewDiv).
Looking at your main.css file, there are three elements being styled: the html element, its child body element, and the element having the id of viewDiv (indicated by the pound sign). Each element has four property settings applied to it. The height and width settings specify that the element should take up all of the space of its parent container. The padding and margin settings specify that there should be no white space inserted on the inside or outside of the element, respectively. Basically, these styles produce a map filling the entire browser window.
At this point, you should familiarize yourself with the syntax of JavaScript (JS). The w3schools site offers a good hands-on JavaScript tutorial that should do the job. Take a couple of hours to work through the topics listed under the JS Tutorial heading and pay particular attention to the following points:
Now that you've learned a bit about JavaScript, we can examine what’s happening in the main.js file.
The first thing you should notice is that all of the code is wrapped inside a require() function. This is a result of the fact that Esri’s API is built around a JS toolkit called Dojo. Dojo uses a design pattern called Asynchronous Module Definition (AMD) to load JS resources into memory. By defining their API’s object classes in modules, Esri’s made those classes easier to maintain and re-use. Loading the modules asynchronously (in parallel rather than one at a time) means that apps built on the API will load faster than they would in a non-AMD framework. One of your recurring tasks as an Esri JS developer will be to identify the modules containing the object classes needed by your app.
In the case of the 2D sample, two Esri classes are needed: Map and MapView. These classes are accessed by loading the esri/Map and esri/views/MapView modules, respectively. The list of modules at the beginning of the require() function is then followed up by an anonymous callback function. The function is anonymous, in that it has no name. It’s a “callback” function because Dojo's module loader will call back to the function when it's done loading the modules, passing module references to the function.
In order to work with the classes held in the modules being loaded, the callback function contains a list of parameters that corresponds to the list of modules. In the 2D example, only the esri/Map and esri/views/MapView modules are required, so the only parameters defined in the callback function are Map and MapView. We’ll see other parameters associated with other modules in more complex examples later. Some notes on the callback function parameters:
In the 2021-22 time frame, Esri's samples switched to a "fat arrow" syntax for function declarations. For example:
require(["esri/Map", "esri/views/MapView"], (Map, MapView) => { }
This syntax is a bit more concise than the previously used syntax:
require(["esri/Map", "esri/views/MapView"], function (Map, MapView) { }
I've attempted to update my own examples to be consistent with Esri's change, but you may still see the old syntax in places. If so, no need to worry. They are equivalent syntaxes for declaring a function. You'll also see some of Esri's samples using the old style too.
An important web browser technology relied upon by Esri's JS API is the Document Object Model, or DOM. This is a tree-like representation of a web page that the browser creates whenever it loads that page. The DOM provides the means for JS developers to manipulate a page’s contents and behavior programmatically. In the case of developing an Esri JS app, the DOM is needed to insert a map or scene onto the page. SInce the require() callback function references DOM objects, it is critical that the DOM is loaded before the callback executes. However, we don't have to worry about this because require() has built-in functionality that waits for the DOM to finish loading before executing the callback function.
Code samples in older incarnations of the API documentation included another module called domReady! This is a Dojo module that is used to ensure that none of the code that follows in the callback function is executed until the DOM has finished loading. The fact that Esri removed references to domReady! in their samples implies that there's no longer -- or never was -- a need to worry about the callback function code executing before the DOM was done loading. In any case, I mention it here in case you run into it either in Esri's or this class's samples. Since the domReady! module -- also referred to as a plugin in Dojo's documentation -- doesn't have a meaningful return value, you'll note that it doesn't have a parameter to go with it in the callback function parameter list.
Once again, the w3schools site offers a good JavaScript HTML DOM tutorial. This short tutorial should only take you 30-60 minutes to complete.
Earlier in the lesson, we visited the Esri JS API SDK to locate sample code that could be used to create our first apps. Now, let’s have a look at an equally important part of the SDK: the API Reference. The API Reference contains a list of all the API modules down the left side of the page. Each module heading can be expanded to see the classes defined in that module. For example, we’d find the Map class in the esri module and the MapView class in the esri/views module.
On the right side of the API Reference index page is a list of commonly used classes, generally organized from top to bottom in order of importance. We’ll cover most of these classes over the course of the term.
The first line of JS code in our 2D sample creates an object of the Map class (new Map). We can learn about that class by clicking the Map link on the API Reference index page or by entering Map into the Find page box.
Like the other class description pages in the API Reference, the page for the Map class conveys a lot of useful information:
The Properties section of the page includes an Overview, listing all of the properties in alphabetical order along with their type (e.g., String, Number, or some object class). The property type is important to know as it tells you what you can expect to get back if you read the property (or conversely, what you need to supply if you’re setting the property). After the Overview, you’ll find a section of Details, providing more specifics on the class’s properties. For example, the details on the basemap property provide a list of acceptable values, along with a thumbnail of each.
The Methods section likewise includes both an Overview and Details section. Method parameters (pieces of information the method uses in performing its action) are listed in parentheses after the name in the Details section. If a parameter is optional, it will be listed with a question mark next to it. If a method returns a value when it is called, the method parameters will be followed by an arrow symbol pointing to the return type. Looking at the Map class methods, the add() method has a required parameter (layer), an optional parameter (index, specifying the position in the Map’s layer collection where you want the layer to appear), and no return value. The findLayerById() method has a required layerId parameter and it returns a reference to a Layer object. Note that when a parameter or return value is an object, it will appear in the documentation as a link. This helps you to navigate through the SDK as you write your code.
The part of the class documentation that we glossed over was the Constructor section. This section is intended to provide guidance on how to create a new object of the class. This is done by using the word new followed by the name of the class and a set of parentheses. In some APIs (including version 3.x of Esri’s JS API), it is possible to set just certain properties of the object you’re creating as part of the constructor statement, making it important to consult the Constructor section of the class’s documentation. However, in version 4.x, any of the class’s properties can be set as part of the constructor, which is why you’ll note that the Map class constructor says simply properties? within the parentheses.
Whatever properties you decide to set in the constructor should be expressed as a JS object literal, a term that may seem intimidating, but is actually not too difficult to grasp. Object literals are basically a list of property-value pairs, in which the list is enclosed in curly braces and the property-value pairs are separated by commas. In the case of the 2D sample, the Map was constructed with just a single property-value setting (the basemap), whereas in the 3D sample, it was constructed with two property-value settings (the basemap and the ground).
Note that you need not set all properties as part of the constructor. For example, I could rewrite the 2D sample as follows:
const map = new Map(); map.basemap = "streets";
Or the 3D sample as follows:
const map = new Map({ basemap: "streets" }); map.ground = "world-elevation";
One last word on constructors... When creating an object, you’re typically doing so because you need it later in your script. Thus, you will usually store a reference to the object you’re creating in a variable. In the samples, the new Map object was stored in a variable called map. That variable was later used to set the map property of a MapView object. Having multiple entities in a script with the same name can be confusing, especially for beginners, so I like to assign variable names that don’t duplicate the name of a class or property. In this case, I might use myMap or theMap. Keep in mind though that this means updating other statements in which that variable is referenced:
require([ "esri/Map", "esri/views/MapView" ], (Map, MapView) => { const myMap = new Map({ basemap: "streets" }); const myView = new MapView({ container: "viewDiv", map: myMap, zoom: 4, center: [15, 65] }); });
And finally, one more aspect of the Esri samples that has changed over time is in the way references to objects are stored. In the past, the samples created variables to store references to objects using the var keyword. Today, you'll see that the samples now typically employ the const keyword in place of var. As you might have guessed, const is short for constant. It is called for when you want to store an object (or primitive value, such as a number or string) in memory for use later in your code, with the important caveat that a constant can never be reassigned a new object or value. That's in contrast to variables, which as the name implies, can be reassigned a new object or value (i.e., they're allowed to vary). In the short snippet above, for example, the const myMap and const myView statements were previously written as var myMap and var myView. Using var still works and is not wrong, but if myMap is always going to refer to the same Map object, it's more appropriate to declare it as a constant.
In thinking about the difference between const and var, it's important to note that while you can't assign a new object to a constant after you've declared it, you are allowed to set properties of the object to new values. In other words, if you were adding code to the snippet above, you would not be able to do this:
myMap = new Map({...}); //or const myMap = new Map({...});
but you could do this:
myMap.basemap = "terrain" //same object, just altering a property
If you really wanted to create a new Map object and store it in myMap -- recycling that space in memory, if you will -- you could certainly do that. You'd just need to declare myMap using var rather than const. Similarly, if you want to store the number 2 in x and later change x to 3, you would need to declare x using var rather than const.
Lastly, you'll see that the course text generally refers to all named spaces in memory as variables, even those declared using const. Technically, anything declared using const should be referred to as a constant, but the term variable is so commonly used in programming that I hope you will excuse the imprecise language. Just keep in mind that most of these named spaces are actually constants, not variables.
We’ve already been exposed to the Map, MapView and SceneView classes in experimenting with and discussing the Esri samples. Now, let’s learn some more about these fundamental classes.
In version 4.x of Esri’s API, the Map class is used to store a basemap and layers. The actual rendering of the Map on the page is done through the use of a View: either a MapView (2D) or a SceneView (3D). If you look back at the two samples we’ve been working with, you’ll note that a Map object is created and its basemap property set in both cases. The only difference is that in the 3D example, the Map’s ground property is set in addition to its basemap. Setting the ground property renders the map such that the underlying terrain can be seen. The property can be set to “world-elevation” to use Esri’s default world elevation service, but it is also possible to use other elevation layers. (Setting the ground property in a Map that is displayed in a MapView has no effect since the MapView class only renders Maps in 2D.)
The other commonly used property of the Map class is its layers property. We’ll look at adding layers to a map in depth later in the course.
Looking at the documentation of the MapView class, you’ll see that it contains a whole host of properties. The most commonly set MapView properties include:
Note that while center and zoom are commonly used to specify the part of the Map that’s currently visible, it’s also possible to do so using the extent and/or scale properties. Take care that you’re setting these properties logically. As explained in the documentation, if both the zoom and scale are set, then the scale setting will override the zoom setting. Similarly, an extent setting will override any center, zoom or scale setting.
3D SceneViews have the same set of properties as listed above, with the exception of rotation.
While it’s typical for a page to contain just a single View, you should keep in mind that you’re not limited to that sort of layout. It is sometimes useful to employ multiple views on the page, as in this sample that contains both a MapView and a SceneView.
The maps we’ve created so far have been pretty boring, so let's spice it up a tiny bit by plotting a point. We’ll see later in the course how to display features stored in one of Esri’s vector data formats. What I’m talking about doing right now is how you’d display a small number of geometries (points, lines or polygons) whose coordinates you’ve hard coded into the app, or perhaps acquired through user input.
const pt = new Point({ latitude: 40.792, longitude: -77.871 });We’ve created a Point geometry for our Graphic, but have we forgotten something? Yes, if we want to work with the Point class, we need to include a reference to its module in our require() declaration.
const sym = new SimpleMarkerSymbol({ color: "blue", style: "square", size: 12 });
const ptGraphic = new Graphic({ geometry:pt, symbol:sym });
view.graphics.add(ptGraphic);
With that, you've reached the end of this lesson's content. Move on to the next page to access the assignment associated with this lesson.
The coding assignment for this lesson is in three parts. Here are some instructions for completing the assignment.
In an earlier course in our curriculum, you may have learned about the United States Geological Survey's Geographic Names Information System and the Getty Thesaurus of Geographic Names (for places outside the U.S.). Use those tools or any other resources at your disposal to locate the latitude/longitude coordinates of your hometown. (One handy way to get a location's coordinates is to pull it up in Google Maps, right-click on the location and select What's Here?)
Modify the lesson4 app code from earlier in the lesson so that the map is centered on and a marker points to your hometown. The easiest approach to this problem would be to insert your hometown coordinates in both lines of the script where coordinates are found. However, I hope you'll recognize that repeating values in different parts of a program is generally a bad practice because of situations like this where you end up having to make revisions in multiple places. So, while you're changing the location displayed on the map, revise the code so that it is as efficient as possible.
As you know, one way that web maps convey information to the user is through popup windows. We saw in Lesson 1 that layers can have popups configured using GUI-based tools in ArcGIS Online. In the next lesson, we'll see how the API makes it possible to easily incorporate layers and maps configured in ArcGIS Online into an app. And later we'll see how to configure layer popups programmatically. In this lesson, we've dealt with graphics, which don't have an attribute table associated with them. However, it's still possible to code the display of popups when the user clicks on a graphic.
For Part II, I'd like you to create a copy of the app from Part I and modify it so that clicking on a town marker will display some information about it (e.g., its name, population, when it was established, etc.). We haven't covered how to do this, but it is something that's demonstrated in an Esri code sample. You might be tempted to search the Sample Code area of the SDK for "popups," but all of the samples with that tag involve data layers rather than graphics. The sample I'm thinking of can be found under Graphics in the list of Sample Code categories.
Create a pen in CodePen that contains your code from either Part I or Part II (your choice).
This project is one week in length. Please refer to the Canvas course Calendar for the due date.
Note: The examples you've seen so far are all likely to have shown the div element used to display the map/scene filling the whole browser window. You could add your reflection text beneath that div, but the map would still fill the whole window and it may not be immediately clear to the viewer that there was content below the map. It's probably a better idea to shrink the height of the map div to something less than 100%. If you're unsure how to go about this, here is an example that demonstrates one approach to including text on the page with the map.
In this lesson, you built your first apps using Esri's JavaScript API. Even the simple map that you created involves a number of different technologies (HTML, CSS, JavaScript, the Document Object Model, and the Dojo JavaScript framework) and it is important that you have a firm understanding of this material before moving on. If you skipped over the w3schools tutorials referenced in the lesson, be sure to go back and complete them. You might also use any free study time to work through other JavaScript references such as the ones recommended in the syllabus.
In Lesson 5, you'll see how to spice up the rather dull product of Lesson 4 by adding data layers. You'll also learn about a number of coding tools and strategies that will help you as a JavaScript developer.