One place for hosting & domains

      How to Dynamically Import JavaScript with Import Maps


      The author selected Creative Commons to receive a donation as part of the Write for DOnations program.

      Introduction

      External libraries can add complexity to a new JavaScript project. To be able to install and use external code libraries, you’ll need a build tool that can parse the code and bundle the libraries that you import into a final format. After the build is set up, you can add and integrate new code with ease, but there are still some problems.

      For example, you may need a library in just one part of your application, a part of the application most users may never need, like an admin page. But by default most build systems will bundle all the code into one large file. The user will load the code regardless of whether they ever need to execute it. The build systems are flexible enough that they can be configured to load code as needed, but the process takes some work.

      Build tools are an important part of the development experience, but a spec called import maps will allow you to both import external code into your project without a build tool and it will only load the code when it is needed at runtime. Import maps won’t completely replace build tools that perform many other valuable actions like building style sheets and handling images, but they will allow you to bootstrap new JavaScript applications quickly and easily using only the native browser functionality.

      In this tutorial, you’ll use import maps and JavaScript modules to import code without build tools. You’ll create a basic application that will display a message and you’ll create an import map that will tell your browser where to locate external code. Next, you’ll integrate the imported code into your JavaScript and will use the third-party code without any need to download the code locally or run it through a build step. Finally, you’ll learn about current tools that implement many aspects of import maps and work on all modern browsers.

      Prerequisites

      Step 1 — Creating an HTML Page and Inserting JavaScript

      In this step, you will create an HTML page, use JavaScript for dynamic activity, and start a local development server to track your changes.

      To start, in a new directory, create a blank HTML document.

      Open a file called index.html in a text editor:

      Inside of the file, add a short, blank HTML page:

      index.html

      <!DOCTYPE html>
      <html lang="en-US">
        <head>
          <meta charset="utf-8">
          <meta http-equiv="X-UA-Compatible" content="IE=edge">
          <title>Hello World</title>
          <meta name="viewport" content="width=device-width, initial-scale=1">
        </head>
        <body>
        </body>
      </html>
      

      This document has a few standard <meta> tags and an empty <body> element.

      Next add a <script> tag. The src attribute for the script tag will be a new JavaScript file you are about to create called hello.js:

      index.html

      <!DOCTYPE html>
      <html lang="en-US">
      <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <title>Hello World</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <script defer src="https://www.digitalocean.com/community/tutorials/./hello.js"></script>
      </head>
      <body>
      </body>
      

      Notice that you are adding a defer attribute to the <script> tag. This will delay execution of the script tag until after the document is parsed. If you don’t defer, you may receive an error that says body is not found when you try to add to the element.

      Next, create a JavaScript file named hello.js in the same directory asindex.html:

      Inside of the file, write some JavaScript to create a new text element with the text "Hello, World":

      hello.js

      const el = document.createElement('h1');
      const words = "Hello, World!"
      const text = document.createTextNode(words);
      el.appendChild(text);
      
      document.body.appendChild(el);
      

      Now that you have your script, you can open the index.html file in a browser. In the same directory as your index.html file, run npx serve. This will run the serve package locally without downloading into your node_modules. The serve package runs a simple webserver that will serve your code locally.

      npx serve
      

      The command will ask you if you want to install a package. Type y to agree:

      Need to install the following packages:
        serve
      Ok to proceed? (y) y
      

      When you run the command you will see some output like this:

      npx: installed 88 in 15.187s
      
         ┌────────────────────────────────────────┐
         │                                        │
         │   Serving!                             │
         │                                        │
         │   Local:  http://localhost:5000        │
         │                                        │
         │   Copied local address to clipboard!   │
         │                                        │
         └────────────────────────────────────────┘
      
      

      When you open your web browser to http://localhost:5000, you’ll see your code. You can either leave the server running in a separate tab or window or close it with CTRL+C after previewing your code.

      Hello, World in a browser

      Now you are displaying a basic page in your browser, but you are not yet able to take advantage of third-party code and JavaScript packages. In the next step, you’ll dynamically import code and import the functions into your script without a build tool.

      Step 2 — Writing a Hello World Script Using ES6 Modules

      In this step, you’ll write code that uses external packages. You’ll modify your code to import JavaScript code using ES imports. Finally, you’ll load the code in your browser using the module type so the browser will know to dynamically load the code.

      To begin, open up hello.js:

      You are going to import some code from lodash to dynamically change your text.

      Inside of the file, change the text from Hello World to all lower case: hello world. Then at the top of the file, import the startCase function from lodash using the standard ES6 import syntax:

      hello.js

      import startCase from '@lodash/startCase';
      
      const el = document.createElement('h1');
      const words = "hello, world";
      const text = document.createTextNode(words);
      el.appendChild(text);
      
      document.body.appendChild(el);
      

      Finally, call startCase with the words variable as an argument inside of document.createTextNode:

      hello.js

      import startCase from '@lodash/startCase';
      
      const el = document.createElement('h1');
      const words = "hello, world";
      const text = document.createTextNode(startCase(words));
      el.appendChild(text);
      
      document.body.appendChild(el);
      

      If you closed your webserver, open a new terminal window or tab and run npx serve to restart the server. Then navigate to http://localhost:5000 in a web browser to view the changes.

      When you preview the code in a browser, open the developer console. When you do, you’ll see an error:

      Output

      Uncaught SyntaxError: Cannot use import statement outside a module

      Module Error

      Since the code is using import statements, you’ll need to modify the <script> tag inside of index.html to handle JavaScript that is now split between multiple files. One file is the original code you wrote. The other file is the code imported from lodash. JavaScript code that imports other code are called modules.

      Close hello.js and open index.html:

      To run the script as a module, change the value of the type attribute on the <script> tag to module:

      hello.js

      <!DOCTYPE html>
      <html lang="en-US">
      <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <title>Hello World</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <script type="module" src="https://www.digitalocean.com/community/tutorials/./hello.js"></script>
      </head>
      <body>
      </body>
      </html>
      

      Notice, you also removed the defer attribute. JavaScript modules will not execute until the page is parsed.

      Open your browser and you’ll still see an error`:

      Output

      Uncaught TypeError: Failed to resolve module specifier "@lodash/startCase". Relative references must start with either "/", "./", or "../"

      Unknown specifier

      Now the problem is not the import statement. The problem is the browser doesn’t know what the import statement means. It’s expecting to find code on the webserver, so it looks for a file relative to the current file. To solve that problem, you’ll need a new tool called import maps.

      In this step, you learned how to modify your JavaScript code to load external libraries using ES imports. You also modified the HTML script tag to handle JavaScript modules.

      In the next step, you’ll tell the browser how to find code using import maps.

      Step 3 — Loading External Code with Import Maps

      In this step, you’ll learn how to create import maps to tell your browser where to find external code. You’ll also learn how to import module code and see how code is lazy-loaded in a browser.

      An import map is a JavaScript object where the key is the name of the import (@lodash/startCase) and the value is the location of the code.

      Inside of index.html add a new script tag with a type of importmap. Inside of the script tag, create a JavaScript object with a key of imports. The value will be another object:

      hello.js

      <!DOCTYPE html>
      <html lang="en-US">
      <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <title>Hello World</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <script type="importmap">
          {
            "imports": {
            }
          }
        </script>
        <script type="module" src="https://www.digitalocean.com/community/tutorials/./hello.js"></script>
      </head>
      <body>
      </body>
      </html>
      

      Be sure that you do not add any trailing commas in the object. The browser will not know how to handle them.

      Now that you have your basic object, you can add the location of your code. The structure of an import map will be like this:

      {
          "imports": {
              "nameOfImport": "locationOfCode",
              "nameOfSecondImport": "secondLocation"
          }
      }
      

      You already know the name of your import @lodash/startCase, but now you need to find where a location to point the import map to.

      A great resource is unpkg. Unpkg is a content delivery network (CDN) for any package in npm. If you can npm install a package, you should be able to load it via unpkg. Unpkg also includes a browsing option that can help you find the specific file you need.

      To find the startCase code, open https://unpkg.com/browse/[email protected]/ in a browser. Notice the word browse in the URL. This gives you a graphical way to look through the directory, but you should not add the path to your import map since it serves up an HTML page and not the raw JavaScript file.

      Also, note that you are browsing lodash-es and not lodash. This is the lodash library exported as ES modules, which is what you will need in this case.

      Browse the code and you’ll notice there is a file called startCase.js. This code imports other functions and uses them to convert the first letter of each word to upper case:

      import createCompounder from './_createCompounder.js';
      import upperFirst from './upperFirst.js';
      
      /**
       * Converts `string` to
       * [start case](https://en.wikipedia.org/wiki/Letter_case#Stylistic_or_specialised_usage).
       *
       * @static
       * @memberOf _
       * @since 3.1.0
       * @category String
       * @param {string} [string=''] The string to convert.
       * @returns {string} Returns the start cased string.
       * @example
       *
       * _.startCase('--foo-bar--');
       * // => 'Foo Bar'
       *
       * _.startCase('fooBar');
       * // => 'Foo Bar'
       *
       * _.startCase('__FOO_BAR__');
       * // => 'FOO BAR'
       */
      var startCase = createCompounder(function(result, word, index) {
        return result + (index ? ' ' : '') + upperFirst(word);
      });
      
      export default startCase;
      

      The browser will follow the import statements and import every file necessary.

      Now that you have a location for your import map, update the file import map with the new URL. Inside of index.html, add @lodash/startCase along with the URL. Once again, be sure to remove browse:

      index.html

      <!DOCTYPE html>
      <html lang="en-US">
      <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <title>Hello World</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <script type="importmap">
          {
            "imports": {
              "@lodash/startCase": "https://unpkg.com/[email protected]/startCase.js"
            }
          }
        </script>
        <script type="module" src="https://www.digitalocean.com/community/tutorials/./hello.js"></script>
      </head>
      <body>
      </body>
      </html>
      

      Save the file. Refresh your browser and you will see Hello World.

      Working

      NOTE: Import maps are not yet widely supported. Open the code in the latest version of Edge or Chrome or check out the latest supported browsers.

      Your browser shows “Hello World”, but now something much more interesting is happening. Open the browser console in your browser, select Inspect Elements, and switch to the Network tab.

      After opening the Network tab, refresh the page and you’ll see that the browser is loading the code dynamically. Whenever it finds a new import statement, it imports the relevant code:

      Network Tab

      More importantly, notice that all of the code is loaded lazily. That means that the browser does not import any code until it is specifically needed. For example, even though startCase is in the import map and the import map is defined before the script for hello.js, it is not loaded until after hello.js loads and imports the code.

      If you were to add other entries in your import map, the browser would not load them at all since they are never imported into code. The import map is a map of locations, and doesn’t import any code itself.

      One major problem is that import maps are not yet fully supported by all browsers. And even when they are supported, some users may not use a supported browser. Fortunately, there are different projects that use the import map syntax while adding full browser support.

      In this step you created an import map. You also learned how to import module code and how the code will be lazy loaded in the browser. In the next step, you’ll import code using SystemJS.

      Step 4 — Building Cross-Browser Support with SystemJS

      In this step, you’ll use import maps across all browsers using SystemJS. You’ll export code as a SystemJS build and how to set the import map type to use SystemJS format. By the end of this step, you’ll be able to take advantage of import maps in any browser and will have a foundation for building more complex applications and microfrontends.

      Import maps will remove many of the complicated build steps from an application, but they are not yet widely supported. Further, not all libraries are built as ES modules so there is some limitation to how you can use them in your code.

      Fortunately, there is a project called SystemJS that can use create import maps for cross-browser support and use a variety of package builds.

      The lodash library is convenient because it is compiled in an ES format, but that’s not the case for most libraries. Many libraries are exported in other formats. One of the most common is the Universal Module Definition or UMD. This format works in both browsers and node modules.

      A major difference is that unlike the ES imports, a UMD build typically combines all of the code into a single file. The file will be a lot larger and you’ll end up with more code then you’ll probably execute.

      To update your project to use SystemJS and a UMD build of lodash, first open hello.js:

      Change the import statement to import the startCase function directly from lodash.

      hello.js

      import { startCase } from 'lodash';
      
      const el = document.createElement('h1');
      const words = "hello, world";
      const text = document.createTextNode(startCase(words));
      el.appendChild(text);
      
      document.body.appendChild(el);
      

      Save and close the file.

      Next, to build the file as a SystemJS build, you will need a simple build step. You can use another build tool such as webpack, but in this example you’ll use rollup.

      First, initialize the project to create a package.json file. Add the -y flag to accept all of the defaults:

      npm init -y
      

      After the command runs you’ll see a success output:

      {
        "name": "hello",
        "version": "1.0.0",
        "description": "",
        "main": "index.js",
        "devDependencies": {},
        "scripts": {
          "test": "echo "Error: no test specified" && exit 1"
        },
        "keywords": [],
        "author": "",
        "license": "ISC",
        "homepage": ""
      }
      

      Note: Your output may be slightly different depending on the version of npm you are using.

      Next, install rollup as a devDepenceny:

      npm install --save-dev rollup
      

      After a moment, you will see a success message:

      + [email protected]
      added 1 package from 1 contributor and audited 2 packages in 6.85s
      found 0 vulnerabilities
      

      Next, create a simple build configuration. Open a new file called rollup.config.js:

      Then add a configuration that will output the code in SystemJS format:

      rollup.config.js

      export default {
        external: ["lodash"],
        input: ["hello.js"],
        output: [
          {
            dir: "public",
            format: "system",
            sourcemap: true
          }
        ]
      };
      

      The external key tells rollup not to include any of the lodash code in the final build. SystemJS will load that code dynamically when it is imported.

      The input is the location of the root file. The output tells rollup where to put the final code and the format it should use which in this case is system.

      Save and close the file.

      Now that you have a build step, you’ll need to add a task to run it. Open package.json:

      In the scripts object, add a script called build that will run rollup -c. Change the main key to hello.js:

      package.json

      {
        "name": "hello",
        "version": "1.0.0",
        "description": "",
        "main": "hello.js",
        "devDependencies": {
          "rollup": "^2.56.2"
        },
        "scripts": {
          "build": "rollup -c"
        },
        "keywords": [],
        "author": "",
        "license": "ISC",
        "homepage": ""
      }
      

      Save and close the file, and run the build command:

      npm run build
      

      The command will run briefly, then you will see a success message:

      > rollup -c
      
      
      hello.js → public...
      created public in 21ms
      
      

      You will also see a new directory called public that will contain the built file. If you open public/hello.js you’ll see your project compiled in a system format.

      The file will look like this. It’s similar to hello.js with a surrounding System.register method. In addtion, lodash is in an array. This will tell SystemJS to load the external library during run time. One of the maintainers created a video that further explains the module format.

      public/hello.js

      System.register(['lodash'], function () {
          'use strict';
          var startCase;
          return {
              setters: [function (module) {
                  startCase = module.startCase;
              }],
              execute: function () {
      
                  const el = document.createElement('h1');
                  const words = "hello, world";
                  const text = document.createTextNode(startCase(words));
                  el.appendChild(text);
      
                  document.body.appendChild(el);
      
              }
          };
      });
      //# sourceMappingURL=hello.js.map
      

      Save and close the file.

      The final step is to update your index.html to handle the new file:

      Open index.html

      First, you’ll need to import the SystemJS code. Use a regular <script> tag with the src attribute pointing to a CDN distribution.

      Put the <script> tag right below the import map:

      index.html

      <!DOCTYPE html>
      <html lang="en-US">
      <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <title>Hello World</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <script type="importmap">
          {
            "imports": {
              "@lodash/startCase": "https://unpkg.com/[email protected]/startCase.js
            }
          }
        </script>
        <script src="https://cdn.jsdelivr.net/npm/systemjs/dist/system.js"></script>
        <script type="module" src="https://www.digitalocean.com/community/tutorials/./hello.js"></script>
      </head>
      <body>
      </body>
      </html>
      

      Next, you’ll need to update your import map. The format is similar to what you completed in Step 3, but there are three changes:

      • Update the import map type.
      • Update the reference to lodash.
      • Add a reference to the hello.js script.

      First, update the type. Since this is not the native browser version of an import map, but a systemjs version, change the type to systemjs-importmap:

      index.html

      <!DOCTYPE html>
      <html lang="en-US">
      <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <title>Hello World</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <script type="systemjs-importmap">
          {
            "imports": {
              "@lodash/startCase": "https://unpkg.com/[email protected]/startCase.js
            }
          }
        </script>
        <script src="https://cdn.jsdelivr.net/npm/systemjs/dist/system.js"></script>
        <script type="module" src="https://www.digitalocean.com/community/tutorials/./hello.js"></script>
      </head>
      <body>
      </body>
      </html>
      

      Next, update the references. Change @lodash/startCase to lodash. You’ll be importing the full library. Then change the location to the UMD build at unpkg.

      Then add a new entry for hello and point that to the compiled version in the public directory:

      index.html

      ...
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <script type="systemjs-importmap">
          {
            "imports": {
              "hello": "./public/hello.js",
              "lodash": "https://unpkg.com/[email protected]/lodash.js"
            }
          }
        </script>
      ...
      

      Now that you are importing systemJS and have updated the import maps, all that’s left is to load the module.

      Change the type attribute on the script tag for the module to systemjs-module. Then change the src to import:hello. This will tell systemjs to load the hello script and execute:

      hello.js

      ...
        <script type="systemjs-importmap">
          {
            "imports": {
              "hello": "./public/hello.js",
              "lodash": "https://unpkg.com/[email protected]/lodash.js"
            }
          }
        </script>
        <script src="https://cdn.jsdelivr.net/npm/systemjs/dist/system.js"></script>
        <script type="systemjs-module" src="https://www.digitalocean.com/community/tutorials/import:hello"></script>
      </head>
      ...
      

      Save and close the file.

      When you do, the browser will refresh and you’ll see Hello World.

      Unlike native import maps, this will work in any browser. Here’s the result in FireFox:

      Hello in firefox

      If you look at the Network tab. You’ll see that as with import maps, the code is lazy loaded as needed:

      Firefox network tab

      In this step, you used import maps across browsers with SystemJS. You changed your script to use the UMD build of lodash, created a rollup build to output the code in system format, and changed the import map and module types to work with SystemJS

      Conclusion

      In this tutorial you used import maps to dynamically load JavaScript code. You rendered an application that dynamically loaded an external library without any build step. Then, you created a build process to generate your code in SystemJS format so that you can use import maps across all browsers.

      Import maps give you opportunities to start breaking large projects into smaller independent pieces called microfronentends. You also do not need to limit yourself to statically defined maps as you learned in this tutorial; you can also create dynamic import maps that can load from other scripts. You also can use a single import map for multiple projects by using scopes to define different versions of a dependency for different scripts that import them.

      There are new features in progress and you can follow them on the official spec.



      Source link

      How to Create a GIS Application using Flask, Stadia Maps, and MongoDB


      Updated by Linode Contributed by Leslie Salazar

      Geographic Information system (GIS) based applications require a beautiful mapping experience for users. Stadia Maps provides digital mapping that you can easily and affordably integrate into your web or mobile applications. They offer hosted map tiles, offline map tiles, static maps, and a few other core products. If you would like to test their services, you can use a local development environment along with their free tier plan. For more details on pricing and service limits, see their pricing plans.

      In this Guide

      This guide will use Stadia Maps free tier plan and hosted vector map tiles to create a GIS web app using Flask. Your Flask application will use a MongoDB database to store GeoJSON data to display as markers on your Stadia Map powered map. GeoJson is a format for encoding a variety of geographic data structures based on JavaScript Object Notation (JSON).

      The GeoJSON data that you will use is a subset of Philadelphia’s Street Tree Inventory dataset. Since the entire dataset is very large, a subset was used to keep this example simple and to reduce MongoDB storage requirements. When you are finished with this guide, you will have a Stadia Maps powered map with markers displaying the location of the trees that surround Linode’s headquarters in Philadelphia, USA.

      While the example in this guide is simple, its components can be adopted to build a GIS app that maps any data you would like to display to your users.

      The sections in this guide will cover the following topics:

      Before you Begin

      Note

      This guide was written using Python version 3.7.

      1. You can optionally create an account with Stadia maps. When developing locally, you are not required to create an account with Stadia Maps. Once you are ready to deploy your app, you will be required to sign up and select an appropriate service plan.

      2. Install MongoDB following the link’s instructions. This installation will also give you access to the mongoimport command line tool.

      3. We recommend Installing Conda, an open source package and environment management system. Conda lets you easily switch between development environments that have different Python versions and packages installed. While this isn’t a strict requirement for setting up your GIS application, it is a great way to isolate your development environment and keep your system’s global Python version and packages untouched.

      Setup Your Development Environment

      In this section, you will prepare your development environment by creating your project directories, activating your conda environment, installing the required Python packages, and importing your GeoJSON data to your MongoDB database.

      1. Create a project folder to store your Flask app files and move into that folder:

        mkdir ~/stadia-maps && cd ~/stadia-maps
        
      2. If you are using conda, create a new environment running Python 3.7:

        conda create --name py37 python=3.7
        
      3. Activate your new conda environment:

        conda activate py37
        
      4. Install pip if it is not already available with your Python installation. A Python 3 installation (>=3.4) should automatically include pip. Follow the instructions in pip’s official documentation to install pip, if needed.

      5. Install the required Python packages:

        pip install flask flask-session geojson pymongo Flask-PyMongo
        

      Import your GeoJSON File to MongoDB

      Before creating your Flask App, you will set up your MongoDB database to store the example data set. This data set was created from the Philadelphia Street Tree inventory GeoJSON data set.

      1. Run your local MongoDB instance. The instance will need to run so that your Flask app can connect to your project’s database. Follow the steps in MongoDB’s official documentation. These steps vary depending on your computer’s operating system.

      2. In your stadia-maps project directory, create a file named linodeStreetTrees.geojson to store your GeoJSON data. Using the text editor of your choice, copy and paste the data located in this linked file. Your file’s final locations should be ~/stadia-maps/linodeStreetTrees.geojson. You will use the data stored in your local file in the next step.

      3. Open a new terminal window and use the mongoimport command line tool to import your GeoJSON data to your database. The import will create a database and collection named linodeStreetTrees and will use the data stored in the linodeStreetTrees.geojson file to create your collection’s documents. In MongoDB, databases hold collections of documents. Collections are analogous to tables in relational databases. Documents store data records of field-value pairs in BSON format, a binary representation of JSON.

        mongoimport --db linodeStreetTrees --collection linodeStreetTrees --file ~/stadia-maps/linodeStreetTrees.geojson
        
      4. Connect to your MongoDB database to verify that all the data was imported as expected. By default, MongoDB will use port 27017 for database connections.

        mongo mongodb://localhost:27017/linodeStreetTrees
        

        From the MongoDB prompt, use the find() collection method to query your database.

        db.linodeStreetTrees.find()
        

        You should see all the data from your imported GeoJSON file returned.

          
        { "_id" : ObjectId("5e2a0c20b4d6fb3be09261c0"), "type" : "FeatureCollection", "features" : [ { "type" : "Feature", "properties" : { "OBJECTID" : 36391, "SPECIES" : null, "STATUS" : null, "DBH" : null }, "geometry" : { "type" : "Point", "coordinates" : [ -75.14457809997165, 39.95216128175947 ] } }, { "type" : "Feature", "properties" : { "OBJECTID" : 36392, "SPECIES" : null, "STATUS" : null, "DBH" : null }, "geometry" : { "type" : "Point", "coordinates" : [ -75.14446882894926, 39.95213340459382 ] } }, { "type" : "Feature", "properties" : { "OBJECTID" : 36388, "SPECIES" : null, "STATUS" : null, "DBH" : null }, "geometry" : { "type" : "Point", "coordinates" : [ -75.14436203227342, 39.95213350981046 ] } }, { "type" : "Feature", "properties" : { "OBJECTID" : 36387, "SPECIES" : null, "STATUS" : null, "DBH" : null }, "geometry" : { "type" : "Point", "coordinates" : [ -75.14391916318196, 39.95207850652248 ] } }, { "type" : "Feature", "properties" : { "OBJECTID" : 36385, "SPECIES" : null, "STATUS" : null, "DBH" : null }, "geometry" : { "type" : "Point", "coordinates" : [ -75.14371036845057, 39.95207848246513 ] } }, { "type" : "Feature", "properties" : { "OBJECTID" : 34552, "SPECIES" : null, "STATUS" : null, "DBH" : null }, "geometry" : { "type" : "Point", "coordinates" : [ -75.14525481375121, 39.952402827669786 ] } }, { "type" : "Feature", "properties" : { "OBJECTID" : 34553, "SPECIES" : null, "STATUS" : null, "DBH" : null }, "geometry" : { "type" : "Point", "coordinates" : [ -75.14522060144313, 39.95249257200717 ] } }, { "type" : "Feature", "properties" : { "OBJECTID" : 34554, "SPECIES" : null, "STATUS" : null, "DBH" : null }, "geometry" : { "type" : "Point", "coordinates" : [ -75.14521115802674, 39.95254397352744 ] } }, { "type" : "Feature", "properties" : { "OBJECTID" : 34556, "SPECIES" : null, "STATUS" : null, "DBH" : null }, "geometry" : { "type" : "Point", "coordinates" : [ -75.14507538603768, 39.952630343085694 ] } } ] }
                
        

        Now that your MongoDB database is set up, you can move on to creating your Flask app.

      Create your Flask App

      Now that your development environment is set up and your MongoDB data is stored in a database collection, you will create a simple GIS Flask application that will include a single Python file, app.py, a template file base.html, and some css styling stored in a map.css file. At the end of this section, you will be able to render a running Stadia Maps powered map in a browser window.

      Create your Project Layout

      1. Ensure you are in your stadia-maps project directory:

        cd ~/stadia-maps
        
      2. Create your Flask App’s project layout. The styles directory will store your app’s stylesheets, while the templates directory will store any Flask templates.

        mkdir -p static/styles && mkdir templates
        

      Create your app.py File

      In this section, you will write the code for your Flask application. Since this is a simple Flask app example, all your app code will be located in a file named app.py. If you are interested in viewing a more in-depth example of a Flask project layout, you can refer to Flask’s project layout documentation.

      1. In the root of your stadia-maps directory create a file named app.py and add the following import statements. This will ensure that your app has access to all the necessary Python packages and their methods.

        ~/stadia-maps/app.py
        1
        2
        3
        4
        5
        6
        7
        
        from flask import Flask, request, render_template
        from flask_session import Session
        from geojson import Point
        from flask_pymongo import PyMongo
        from bson.json_util import dumps
        import json
            
      2. Below your import statements, add the Python code to set up the Flask app and connect to your MongoDB database. The code creates an instance of the Flask class, connects to your MongoDB server and linodeStreetTrees database running on port 27017. Finally, an instance of the PyMongo class is created, which manages connections from MongoDB to your Flask app.

        ~/stadia-maps/app.py
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        
        from flask import Flask, request, render_template
        from flask_session import Session
        from geojson import Point
        from flask_pymongo import PyMongo
        from bson.json_util import dumps
        import json
        
        app=Flask(__name__)
        app.config["MONGO_URI"] = "mongodb://localhost:27017/linodeStreetTrees"
        mongo = PyMongo(app)
            
      3. Register your view function for your app’s index page by adding the example’s remaining lines to your app.py file. The route() decorator signals to Flask which URL should trigger the defined function def index(). When a user visits your app’s index (i.e. http://127.0.0.1:5000/), the code defined in the index() function will execute. This code retrieves all the data in your MongoDB linodeStreetTress collection and makes the data available to the base.html template in the street_trees_points template variable. The base.html template will be created in the next section.

        The final block of code provides a way for Python to handle both script execution and importing. Finally, if the conditional evaluates to true, it will execute Flask’s run() method to run your app.

        ~/stadia-maps/app.py
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        
        from flask import Flask, request, render_template
        from flask_session import Session
        from geojson import Point
        from flask_pymongo import PyMongo
        from bson.json_util import dumps
        import json
        
        app=Flask(__name__)
        app.config["MONGO_URI"] = "mongodb://localhost:27017/linodeStreetTrees"
        mongo = PyMongo(app)
        
        @app.route('/')
        def index():
            street_trees_points_query = dumps(mongo.db.linodeStreetTrees.find({}, {'_id': False}))
            street_trees_points = json.loads(street_trees_points_query)
            return render_template('base.html', street_trees_points=street_trees_points)
        
        if __name__ == '__main__':
            app.debug=True
            app.run()
        
            

      Create your Template File

      In this section you will create your Flask app’s template. Flask templates are used to render the front end portion of your app. You can make use of the Jinja templating language for Python to add additional functionality to your templates.

      In this example, the base.html template connects to Stadia Maps to retrieve their vector map tiles and renders them on your site’s index page. Stadia Map’s implementation relies on the Mapbox GL JavaScript library. The template accesses your database’s data and uses it to render marker’s on your map with the help of Mapbox GL and Stadia Maps. This example utilizes Stadia Map’s boiler plate vector maps example as a foundation. The example file is heavily commented, which you can use to better understand each section of the file.

      1. Create a file named base.html in your root project’s templates directory with the example file content.

        ~/stadia-maps/templates/base.html
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        46
        47
        
        <html>
        <head>
            <title>Stadia Maps + Flask + MongoDB Demo</title>
            <meta charset="utf-8">
            <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
            <script src="//cdnjs.cloudflare.com/ajax/libs/mapbox-gl/0.52.0/mapbox-gl.js"></script>
            <link href="//cdnjs.cloudflare.com/ajax/libs/mapbox-gl/0.52.0/mapbox-gl.css" rel="stylesheet" />
            <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='../static/styles/map.css') }}" />
            <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
        </head>
        <body>
            <h1>
                Stadia Maps + Flask + MongoDB Demo
            </h1>
            <div id="map"></div>
            <script>
             var map = new mapboxgl.Map({
               container: 'map',
               style: 'https://tiles.stadiamaps.com/styles/alidade_smooth.json',  // Theme URL; see our themes documentation for more options
               center: [-75.144867, 39.952278],  // Initial focus coordinate
               zoom: 16
             });
        
             mapboxgl.setRTLTextPlugin('https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-rtl-text/v0.2.1/mapbox-gl-rtl-text.js');
        
             // Add zoom and rotation controls to the map.
             map.addControl(new mapboxgl.NavigationControl());
        
             var mongoData = JSON.parse('{{ street_trees_points| tojson | safe }}');
             var markerCollection = mongoData[0];
        
                // Next, we can add markers to the map
                markerCollection.features.forEach(function(point) {
                    var elem = document.createElement('div');
                    elem.className = 'marker';
        
                    // Now, we construct a marker and set it's coordinates from the GeoJSON. Note the coordinate order.
                    var marker = new mapboxgl.Marker(elem);
                    marker.setLngLat(point.geometry.coordinates);
        
                    // Finally, we add the marker to the map.
                    marker.addTo(map);
                });
            </script>
        </body>
        </html>
              

        Key portions of the file that you should take note of are the following:

        • The <head> element contains a call to the Mapbox GL JavaScript file that the rest of the template makes use of. Other needed external scripts and files, like jQuery, are called in this section.
        • The call to the app’s stylesheet contains an href attribute (line 8) whose value makes use of the Jinja templating language. The code "{{ url_for('static', filename='../static/styles/map.css') }" allows Flask to generate URLs for any static files.
        • Line 15 (<div id="map"></div>) creates the div element that will contain the rendered map.
        • The script beginning on line 16 creates a new mapboxgl instance whose methods will be used throughout the script to construct your map. Notice that upon instantiation, Stadia Map’s theme URL is called to retrieve their Alidade Smooth theme. For details on all the MapBox GL methods that are used throughout the template, see the MapBox GL reference.
        • Line 29 is responsible for grabbing your databases data, using Jinja, from the street_trees_points variable that was exposed in the app.py file. The data is serialized and passed to the markerCollection variable. The forEach() method is then used to create markers for each collection document (from your database’s serialized data) and render them on your map.

      Create your App’s Styling

      In the previous section, the base.html template file calls your app’s style sheet. You are now ready to create the stylesheet.

      1. Create a file named map.css in your project’s ~/stadia-maps/static/styles directory and add the example file’s content. The marker that will be rendered on your map is provided by Stadia Map.

        ~/stadia-maps/static/styles/map.css
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        
        body {
          margin: 0;
          padding: 0;
        }
        
        #map {
          position: absolute;
          top: 0;
          bottom: 0;
          width: 100%;
        }
        
        /* We recommend using an icon that is 2x the intended display size, so that it renders nicely on retina displays.
        * The icon used in the background-image property is owned by Stadia Maps. While you maintain an account with us, we grant you royalty-free use of
        * this image when displayed on our maps.
        */
        .marker {
        background-image: url('data:image/png;base64,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');
        background-size: cover;
        width: 27px;
        height: 42px;
        cursor: pointer;
        }
            

      Run your Flask App

      You are now ready to run your Flask app locally to view your rendered Stadia Map.

      1. Open a new terminal window and navigate to your stadia-maps root directory.

        cd ~/stadia-maps
        
      2. Run your Flask application with the following command:

        python3 app.py
        

        You will see that your Flask server runs with the following output:

          
        * Serving Flask app "app" (lazy loading)
        * Environment: production
          WARNING: This is a development server. Do not use it in a production deployment.
          Use a production WSGI server instead.
        * Debug mode: on
        * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
        * Restarting with stat
        * Debugger is active!
        * Debugger PIN: 140-560-688
            
        
      3. Open a browser and navigate to the local URL. In our example, this is: http://127.0.0.1:5000/. You should see a Stadia Map render that displays the location of Linode’s headquarters and the location of some of the neighborhood’s surrounding trees.

        "Linode Trees"

      Next Steps

      This guide is published under a CC BY-ND 4.0 license.





      Source link

      Understanding Maps in Go


      Most modern programming languages have the concept of a dictionary or a hash type. These types are commonly used to store data in pairs with a key that maps to a value.

      In Go, the map data type is what most programmers would think of as the dictionary type. It maps keys to values, making key-value pairs that are a useful way to store data in Go. A map is constructed by using the keyword map followed by the key data type in square brackets [ ], followed by the value data type. The key-value pairs are then placed inside curly braces on either side { }:

      map[key]value{}
      

      You typically use maps in Go to hold related data, such as the information contained in an ID. A map with data looks like this:

      map[string]string{"name": "Sammy", "animal": "shark", "color": "blue", "location": "ocean"}
      

      In addition to the curly braces, there are also colons throughout the map that connect the key-value pairs. The words to the left of the colons are the keys. Keys can be any comparable type in Go, like strings, ints, and so on.

      The keys in the example map are:

      • "name"
      • "animal"
      • "color"
      • "location"

      The words to the right of the colons are the values. Values can be any data type. The values in the example map are:

      • "Sammy"
      • "shark"
      • "blue"
      • "ocean"

      Like the other data types, you can store the map inside a variable, and print it out:

      sammy := map[string]string{"name": "Sammy", "animal": "shark", "color": "blue", "location": "ocean"}
      fmt.Println(sammy)
      

      This would be your output:

      Output

      map[animal:shark color:blue location:ocean name:Sammy]

      The order of the key-value pairs may have shifted. In Go, the map data type is unordered. Regardless of the order, the key-value pairs will remain intact, enabling you to access data based on their relational meaning.

      Accessing Map Items

      You can call the values of a map by referencing the related keys. Since maps offer key-value pairs for storing data, they can be important and useful items in your Go program.

      If you want to isolate Sammy’s username, you can do so by calling sammy["name"]; the variable holding your map and the related key. Let’s print that out:

      fmt.Println(sammy["name"])
      

      And receive the value as output:

      Output

      Sammy

      Maps behave like a database; instead of calling an integer to get a particular index value as you would with a slice, you assign a value to a key and call that key to get its related value.

      By invoking the key "name" you receive the value of that key, which is "Sammy".

      Similarly you can call the remaining values in the sammy map using the same format:

      fmt.Println(sammy["animal"])
      // returns shark
      
      fmt.Println(sammy["color"])
      // returns blue
      
      fmt.Println(sammy["location"])
      // returns ocean
      

      By making use of the key-value pairs in map data types, you can reference keys to retrieve values.

      Keys and Values

      Unlike some programming languages, Go does not have any convenience functions to list out the keys or values of a map. An example of this would be Python’s .keys() method for dictionaries. It does, however, allow for iteration by using the range operator:

      for key, value := range sammy {
          fmt.Printf("%q is the key for the value %qn", key, value)
      }
      

      When ranging through a map in Go, it’ll return two values. The first value will be the key, and the second value will be the value. Go will create these variables with the correct data type. In this case, the map key was a string so key will also be a string. The value is also a string:

      Output

      "animal" is the key for the value "shark" "color" is the key for the value "blue" "location" is the key for the value "ocean" "name" is the key for the value "Sammy"

      To get a list of just the keys, you can use the range operator again. You can declare just one variable to only access the keys:

      keys := []string{}
      
      for key := range sammy {
          keys = append(keys, key)
      }
      fmt.Printf("%q", keys)
      

      The program begins by declaring a slice to store your keys in.

      The output will show only the keys of your map:

      Output

      ["color" "location" "name" "animal"]

      Again, the keys are not sorted. If you want to sort them, you use the sort.Strings function from the sort package:

      sort.Strings(keys)
      

      With this function, you’ll receive the following output:

      Output

      ["animal" "color" "location" "name"]

      You can use the same pattern to retrieve just the values in a map. In the next example, you pre-allocate the slice to avoid allocations, thus making the program more efficient:

      sammy := map[string]string{"name": "Sammy", "animal": "shark", "color": "blue", "location": "ocean"}
      
      items := make([]string, len(sammy))
      
      var i int
      
      for _, v := range sammy {
          items[i] = v
          i++
      }
      fmt.Printf("%q", items)
      

      First you declare a slice to store your keys in; since you know how many items you need, you can avoid potential memory allocations by defining the slice at the exact same size. You then declare your index variable. As you don’t want the key, you use the _ operator, when starting your loop, to ignore the key’s value. Your output would be the following:

      Output

      ["ocean" "Sammy" "shark" "blue"]

      To determine the number of items in a map, you can use the built-in len function:

      sammy := map[string]string{"name": "Sammy", "animal": "shark", "color": "blue", "location": "ocean"}
      fmt.Println(len(sammy))
      

      The output displays the number of items in your map:

      Output

      4

      Even though Go doesn’t ship with convenience functions to get keys and values, it only takes a few lines of code to retrieve the keys and values when needed.

      Checking Existence

      Maps in Go will return the zero value for the value type of the map when the requested key is missing. Because of this, you need an alternative way to differentiate a stored zero, versus a missing key.

      Let’s look up a value in a map that you know doesn’t exist and look at the value returned:

      counts := map[string]int{}
      fmt.Println(counts["sammy"])
      

      You’ll see the following output:

      Output

      0

      Even though the key sammy was not in the map, Go still returned the value of 0. This is because the value data type is an int, and because Go has a zero value for all variables, it returns the zero value of 0.

      In many cases, this is undesirable and would lead to a bug in your program. When looking up the value in a map, Go can return a second, optional value. This second value is a bool and will be true if the key was found, or false if the key was not found. In Go, this is referred to as the ok idiom. Even though you could name the variable that captures the second argument anything you want, in Go, you always name it ok:

      count, ok := counts["sammy"]
      

      If the key sammy exists in the counts map, then ok will be true. Otherwise ok will be false.

      You can use the ok variable to decide what to do in your program:

      if ok {
          fmt.Printf("Sammy has a count of %dn", count)
      } else {
          fmt.Println("Sammy was not found")
      }
      

      This would result in the following output:

      Output

      Sammy was not found

      In Go, you can combine variable declaration and conditional checking with an if/else block. This allows you to use a single statement for this check:

      if count, ok := counts["sammy"]; ok {
          fmt.Printf("Sammy has a count of %dn", count)
      } else {
          fmt.Println("Sammy was not found")
      }
      

      When retrieving a value from a map in Go, it’s always good practice to check for its existence as well to avoid bugs in your program.

      Modifying Maps

      Maps are a mutable data structure, so you can modify them. Let’s look at adding and deleting map items in this section.

      Adding and Changing Map Items

      Without using a method or function, you can add key-value pairs to maps. You do this using the maps variable name, followed by the key value in square brackets [ ], and using the equal = operator to set a new value:

      map[key] = value
      

      In practice, you can see this work by adding a key-value pair to a map called usernames:

      usernames := map[string]string{"Sammy": "sammy-shark", "Jamie": "mantisshrimp54"}
      
      usernames["Drew"] = "squidly"
      fmt.Println(usernames)
      

      The output will display the new Drew:squidly key-value pair in the map:

      Output

      map[Drew:squidly Jamie:mantisshrimp54 Sammy:sammy-shark]

      Because maps are returned unordered, this pair may occur anywhere in the map output. If you use the usernames map later in your program file, it will include the additional key-value pair.

      You can also use this syntax for modifying the value assigned to a key. In this case, you reference an existing key and pass a different value to it.

      Consider a map called followers that tracks followers of users on a given network. The user "drew" had a bump in followers today, so you need to update the integer value passed to the "drew" key. You’ll use the Println() function to check that the map was modified:

      followers := map[string]int{"drew": 305, "mary": 428, "cindy": 918}
      followers["drew"] = 342
      fmt.Println(followers)
      

      Your output will show the updated value for drew:

      Output

      map[cindy:918 drew:342 mary:428]

      You see that the number of followers jumped from the integer value of 305 to 342.

      You can use this method for adding key-value pairs to maps with user input. Let’s write a quick program called usernames.go that runs on the command line and allows input from the user to add more names and associated usernames:

      usernames.go

      package main
      
      import (
          "fmt"
          "strings"
      )
      
      func main() {
          usernames := map[string]string{"Sammy": "sammy-shark", "Jamie": "mantisshrimp54"}
      
          for {
              fmt.Println("Enter a name:")
      
              var name string
              _, err := fmt.Scanln(&name)
      
              if err != nil {
                  panic(err)
              }
      
              name = strings.TrimSpace(name)
      
              if u, ok := usernames[name]; ok {
                  fmt.Printf("%q is the username of %qn", u, name)
                  continue
              }
      
              fmt.Printf("I don't have %v's username, what is it?n", name)
      
              var username string
              _, err = fmt.Scanln(&username)
      
              if err != nil {
                  panic(err)
              }
      
              username = strings.TrimSpace(username)
      
              usernames[name] = username
      
              fmt.Println("Data updated.")
          }
      }
      

      In usernames.go you first define the original map. You then set up a loop to iterate over the names. You request your user to enter a name and declare a variable to store it in. Next, you check to see if you had an error; if so, the program will exit with a panic. Because Scanln captures the entire input, including the carriage return, you need to remove any space from the input; you do this with the strings.TrimSpace function.

      The if block checks whether the name is present in the map and prints feedback. If the name is present it then continues back to the top of the loop. If the name is not in the map, it provides feedback to the user and then will ask for a new username for the associated name. The program checks again to see if there is an error. With no error, it trims off the carriage return, assigns the username value to the name key, and then prints feedback that the data was updated.

      Let’s run the program on the command line:

      You'll see the following output:

      Output

      Enter a name: Sammy "sammy-shark" is the username of "Sammy" Enter a name: Jesse I don't have Jesse's username, what is it? JOctopus Data updated. Enter a name:

      When you're done testing, press CTRL + C to escape the program.

      This shows how you can modify maps interactively. With this particular program, as soon as you exit the program with CTRL + C you’ll lose all your data unless you implement a way to handle reading and writing files.

      To summarize, you can add items to maps or modify values with the map[key] = value syntax.

      Deleting Map Items

      Just as you can add key-value pairs and change values within the map data type, you can also delete items within a map.

      To remove a key-value pair from a map, you can use the built-in function delete(). The first argument is the map you are deleting from. The second argument is the key you are deleting:

      delete(map, key)
      

      Let's define a map of permissions:

      permissions := map[int]string{1: "read", 2: "write", 4: "delete", 8: "create", 16:"modify"}
      

      You no longer need the modify permission, so you'll remove it from your map. Then you'll print out the map to confirm it was removed:

      permissions := map[int]string{1: "read", 2: "write", 4: "delete", 8: "create", 16: "modify"}
      delete(permissions, 16)
      fmt.Println(permissions)
      

      The output will confirm the deletion:

      Output

      map[1:read 2:write 4:delete 8:create]

      The line delete(permissions, 16) removes the key-value pair 16:"modify" from the permissions map.

      If you would like to clear a map of all of its values, you can do so by setting it equal to an empty map of the same type. This will create a new empty map to use, and the old map will be cleared from memory by the garbage collector.

      Let’s remove all the items within the permissions map:

      permissions = map[int]string{}
      fmt.Println(permissions)
      

      The output shows that you now have an empty map devoid of key-value pairs:

      Output

      map[]

      Because maps are mutable data types, they can be added to, modified, and have items removed and cleared.

      Conclusion

      This tutorial explored the map data structure in Go. Maps are made up of key-value pairs and provide a way to store data without relying on indexing. This allows us to retrieve values based on their meaning and relation to other data types.



      Source link