diff --git a/.gitignore b/.gitignore index d432aa94..1217dc76 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ .vscode/launch.json .vscode/ipch .ipynb_checkpoints +/node_modules diff --git a/1-getting-started/lessons/1-introduction-to-iot/README.md b/1-getting-started/lessons/1-introduction-to-iot/README.md index 44335cb8..4af59048 100644 --- a/1-getting-started/lessons/1-introduction-to-iot/README.md +++ b/1-getting-started/lessons/1-introduction-to-iot/README.md @@ -135,6 +135,8 @@ Work through the relevant guide to set your device up and complete a 'Hello Worl * [Single-board computer - Raspberry Pi](pi.md) * [Single-board computer - Virtual device](virtual-device.md) +✅ You will be using VS Code for both Arduino and Single-board computers. If you haven't used this before, read more about it on the [VS Code site](https://code.visualstudio.com?WT.mc_id=academic-17441-jabenn) + ## Applications of IoT IoT covers a huge range of use cases, across a few broad groups: diff --git a/3-transport/README.md b/3-transport/README.md index f7058a26..61bc84a2 100644 --- a/3-transport/README.md +++ b/3-transport/README.md @@ -21,4 +21,4 @@ In these 4 lessons, you'll learn how to apply the Internet of Things to improve ## Credits -All the lessons were written with ♥️ by [Jim Bennett](https://GitHub.com/JimBobBennett) +All the lessons were written with ♥️ by [Jen Looper](https://github.com/jlooper) and [Jim Bennett](https://GitHub.com/JimBobBennett) diff --git a/3-transport/lessons/3-visualize-location-data/README.md b/3-transport/lessons/3-visualize-location-data/README.md index 34c5bd45..2921e310 100644 --- a/3-transport/lessons/3-visualize-location-data/README.md +++ b/3-transport/lessons/3-visualize-location-data/README.md @@ -34,24 +34,41 @@ You can access Azure Maps APIs by leveraging its [REST API](https://docs.microso In this lesson, you will use the web SDK to draw a map and display your sensor's GPS location's path. ## Create an Azure Maps resource -Your first step is to create an Azure Maps account. The easiest way to do this is in the [Azure portal](https://portal.azure.com?WT.mc_id=academic-17441-jabenn). +Your first step is to create an Azure Maps account. You can do this using the CLI or in the [Azure portal](https://portal.azure.com?WT.mc_id=academic-17441-jabenn). -1. After logging in to the portal, click the "Create a resource" button and in the search box that appears, type "Azure Maps". +Using the CLI, create a maps account: -1. Select "Azure Maps" and click 'Create'. +``` +az maps account create --name + --resource-group + [--accept-tos] + [--sku {S0, S1}] + [--subscription] + [--tags] +``` + +Use the `gps-service` resource group you've used in previous lessons. You can use the S0 subscription for this small task. -1. On the Create Azure Maps Account page, enter: +A sample call would look like this: + +``` +az maps account create --name MyMapsAccount --resource-group MyResourceGroup --sku S0 --subscription MySubscription +``` +The service will deploy. Next, you need to get your Subscription Key. There are two ways to authenticate your maps in a web app: using Active Directory (AD) or 'Shared Key Authentication', also known as Subscription Key. We'll use the latter, for simplicity. - - Your subscription in the dropdown box. - - A Resource group to use (use 'gps-sensor' as you have done throughout these lessons) - - Add a name for your account - - Choose a Pricing tier. The Pricing tier for this account. S0 will work for this small project. +In the CLI, find your keys: -1. Read and accept the terms of service, and click the 'Create' button. - -1. The service will deploy and you can visit it by clicking 'Go to resource' in the next screen. +``` +az maps account keys list --name + --resource-group + [--query-examples] + [--subscription] +``` +A sample call would look like this: -2. Navigate to your new Azure Maps Account Authentication screen. Here, you discover that you have two ways to authenticate your maps in a web app: using Active Directory (AD) or 'Shared Key Authentication', also known as Subscription Key. We'll use the latter, for simplicity. Copy the Primary Key value and make a note of it! +``` +az maps account keys list --name MyMapsAccount --resource-group MyResourceGroup +``` ✅ You will be able to rotate and swap keys at will using the Shared Keys; switch your app to use the Secondary Key while rotating the Primary Key if needed. @@ -94,7 +111,7 @@ The map will load in the 'myMap' `div`. A few styles allow it so span the width function init() { var map = new atlas.Map('myMap', { - center: [-122.33, 47.6], + center: [-122.26473, 47.73444], zoom: 12, authOptions: { authType: "subscriptionKey", @@ -105,14 +122,13 @@ The map will load in the 'myMap' `div`. A few styles allow it so span the width } ``` -If you open your index.html page in a web browser, you should see a map loaded, and focused on Seattle: +If you open your index.html page in a web browser, you should see a map loaded, and focused on the Seattle area. ![map image](images/map-image.png) -✅ Experiment with the zoom and center parameters to change your map display. +✅ Experiment with the zoom and center parameters to change your map display. You can add different coordinates corresponding to your data's latitude and longitude to re-center the map. > A better way to work with web apps locally is to install [http-server](https://www.npmjs.com/package/http-server). You will need [node.js](https://nodejs.org/) and [npm](https://www.npmjs.com/) installed before using this tool. Once those tools are installed, you can navigate to the location of your `index.html` file and type `http-server`. The web app will open on a local webserver http://127.0.0.1:8080/. - ## The GeoJSON format Now that you have your web app in place with the map displaying, you need to extract GPS data from your storage and display it in a layer of markers on top of the map. Before we do that, let's look at the [GeoJSON](https://wikipedia.org/wiki/GeoJSON) format that is required by Azure Maps. @@ -139,12 +155,16 @@ Sample GeoJSON data looks like this: } ``` -Of particular interest is the way the data is nested as a 'FeatureCollection'. Within that object can be found 'geometry' with the 'coordinates' indicating latitude and longitude. Geometry can have different 'types' designated to that a polygon could be drawn to a map; in this case, a point is drawn with two coordinates designated. +Of particular interest is the way the data is nested as a 'Feature' within a 'FeatureCollection'. Within that object can be found 'geometry' with the 'coordinates' indicating latitude and longitude. + +✅ When building your geoJSON, pay attention to the order of 'latitude' and 'longitude' in the object, or your points will not appear where they should! + +`Geometry` can have different 'types' designated to that a polygon could be drawn to a map; in this case, a point is drawn with two coordinates designated. ✅ Azure Maps supports standard GeoJSON plus some [enhanced features](https://docs.microsoft.com/azure/azure-maps/extend-geojson?WT.mc_id=academic-17441-jabenn) including the ability to draw circles and other geometries. ## Plot GPS data on a Map using GeoJSON -Now you are ready to consume data from the storage that you built in the previous lesson. As a reminder, it is stored as a number of files in blob storage so you will need to retrieve the files and parse them so that Azure Maps can use the data. +Now you are ready to consume data from the storage that you built in the previous lesson. As a reminder, it is stored as a number of files in blob storage so you will need to retrieve the files and parse them so that Azure Maps can use the data. If you make a call to your storage to fetch the data you might be surprised to see errors occurring in your browser's console. That's because you need to set permissions for [CORS](https://developer.mozilla.org/docs/Web/HTTP/CORS) on this storage to allow external web apps to read its data. CORS stands for "Cross-Origin Resource Sharing" and usually needs to be set explicitly in Azure for security reasons. Do this using the Azure CLI, adding the name of your storage container and its key. We only need to 'GET' data from this container: @@ -156,12 +176,88 @@ az storage cors add --methods GET \ --account-key ``` -TODO - fetch call explanation +1. First, get the endpoint of your storage container. Using the Azure CLI, you can show its information: + +``` +az storage account blob-service-properties show --account-name + [--query-examples] + [--resource-group] + [--subscription] +``` + +A typical query would look like: + +``` +az storage account blob-service-properties show -n mystorageaccount -g MyResourceGroup +``` + +1. Use that endpoint to build up your init() function. Overwrite the previous function by adding the ability to fetch data: + +```javascript + + fetch("https://.blob.core.windows.net/gps-data/?restype=container&comp=list") + .then(response => response.text()) + .then(str => new window.DOMParser().parseFromString(str, "text/xml")) + .then(xml => { + let blobList = Array.from(xml.querySelectorAll("Url")); + blobList.forEach(async blobUrl => { + loadJSON(blobUrl.innerHTML) + }); + }) + .then( response => { + map = new atlas.Map('myMap', { + center: [-122.26473, 47.73444], + zoom: 14, + authOptions: { + authType: "subscriptionKey", + subscriptionKey: "", + + } + }); + map.events.add('ready', function () { + var source = new atlas.source.DataSource(); + map.sources.add(source); + map.layers.add(new atlas.layer.BubbleLayer(source)); + source.add(features); + }) + }) +``` + +There are several things happening here. First, you fetch your data from your container using the endpoint you found using the Azure CLI. You parse each file in that blog storage to extract latitude and longitude. Then you initialize a map, adding a bubble layer with the data fetched and saved as source. + +1. Add a loadJSON() function to your script block: + +```javascript +var map, features; + + function loadJSON(file) { + var xhr = new XMLHttpRequest(); + features = []; + xhr.onreadystatechange = function () { + if (xhr.readyState === XMLHttpRequest.DONE) { + if (xhr.status === 200) { + gps = JSON.parse(xhr.responseText) + features.push( + new atlas.data.Feature(new atlas.data.Point([parseFloat(gps.gps.lon), parseFloat(gps.gps.lat)])) + ) + } + } + }; + xhr.open("GET", file, true); + xhr.send(); + } + +``` +This function is called by the fetch routine to parse through the JSON data and convert it to be read as longitude and latitude coordinates as geoJSON. +Once parsed, the data is set as part of a geoJSON `Feature`. The map will be initialized and little bubbles will appear around the path your data is plotting: + +![data path](images/path.png) + --- ## 🚀 Challenge -It's nice to be able to display static data on a map as markers. Can you enhance this web app to add animation and show the path of the markers over time, using the timestamped json files? Here are some samples of using animation +It's nice to be able to display static data on a map as markers. Can you enhance this web app to add animation and show the path of the markers over time, using the timestamped json files? Here are [some samples](https://azuremapscodesamples.azurewebsites.net/) of using animation within maps. ## Post-lecture quiz @@ -169,6 +265,8 @@ It's nice to be able to display static data on a map as markers. Can you enhance ## Review & Self Study +Azure Maps is particularly useful for working with IoT devices. Research some of the uses in the [documentation](https://docs.microsoft.com/en-us/azure/azure-maps/tutorial-iot-hub-maps?WT.mc_id=academic-17441-jabenn). Deepen your knowledge of mapmaking and waypoints [with this Learn module](https://docs.microsoft.com/en-us/learn/modules/create-your-first-app-with-azure-maps/?WT.mc_id=academic-17441-jabenn). + ## Assignment [Deploy your app](assignment.md) diff --git a/3-transport/lessons/3-visualize-location-data/code/index.html b/3-transport/lessons/3-visualize-location-data/code/index.html index da2c3043..e69ef107 100644 --- a/3-transport/lessons/3-visualize-location-data/code/index.html +++ b/3-transport/lessons/3-visualize-location-data/code/index.html @@ -6,27 +6,59 @@