Toutes les pages
Propulsé par GitBook
1 sur 13

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Examples

The following pages contain source code examples that you can run or modifiy in Codepen.io.

In all examples, you must import the JMap NG core or app library from our CDN with a command like this:

<script defer type="text/javascript" src="https://cdn.jsdelivr.net/npm/jmap-core-js@7_Kathmandu_HF3"></script>

or

<script defer type="text/javascript" src="https://cdn.jsdelivr.net/npm/jmap-app-js@7_Kathmandu_HF3"></script>

The specific version of the library that you import must match the version of your backend. For JMap Cloud, always use version 'jmapcloud'. For JMap Server, use the version that matches your setup (for instance, 7_Jakarta_HF6 or 7_Kathmandu_HF3). New versions of JMap NG libraries are published for each release of JMap Server.

Start the JMap NG App

You can start JMap NG App in a div of your website, or as a full page application.

JMap NG App

Import library and application from CDN

You must import App js files from our CDN links (it will automatically load the JMap Core dependency).

  <script defer type="text/javascript" src="https://cdn.jsdelivr.net/npm/jmap-app-js@7_Kathmandu_HF3"></script>

Make sure the version of the library matches the version of your JMap backend (JMap Cloud or JMap Server).

Set library and application options

You have to provide required and optional parameters to the library and the app:

More information about startup options here

Display the app in a div

Try it out in

Display the map full screen

Try it out in

Add attributions on the map

In this example, we will show how to add one or multiple attributions on the map in JMap NG.

Note that attributions are available in both NG Core and NG App.

Add a single attribution example

In the example bellow, we add a single attribution composed of one hyperlink forwarding to k2geospatial website.

Try it out in

Locate and select features by attribute query

In this example, we will locate and select features on the map, for a given postal code.

In the example dataset, we will use the layer "Places", id=6. We will search on the postal code attribute (CODE_POSTA), and return all places having the given postal code.

Example

Try it out in

Start the JMap NG Core library

You can start JMap NG Core in a div of your website, or as a full page application.

Import the library from CDN

To use the JMap NG Core library, simply import the JMap NG Core Javascript library from our CDN link.

Make sure the version of the library matches the version of your JMap Server instance.

Add a layer to display custom data from GeoJSON file

In this example, we will create a custom layer that fetches GeoJSON data from a file. It will display on top of all other layers.

The GeoJSON file for this example is located at this URL: .

Example

Try it out in

Toggle a JMap layer visibility

This example shows how to toggle the visibility of a JMap layer on the map.

Try it out in

Codepen.io
Codepen.io
Codepen.io
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
    <meta charset="UTF-8">
    <style>
      #my-custom-map {
        width: 600px;
        height: 600px;
        border: 1px solid grey;
        margin-top: 1rem;
      }
    </style>
  </head>
  <body class="jmap_wrapper">
    <div id="my-custom-map"></div>
    <script type="text/javascript">
      window.JMAP_OPTIONS = {
        projectId: 1,
        restBaseUrl: "https://jmapdoc.jmaponline.net/services/rest/v2.0",
        anonymous: true,
        map: {
          containerId: "my-custom-map",
          zoom: 9.573700695830425,
          center: {
            x: -73.7219880403544,
            y: 45.53690235213574
          }
        },
        onReady: () => {
          JMap.Event.Map.on.mapLoad("my-listener", params => {
            const map = params.map;
            map.addSource("my-custom-source", {
              type: "geojson",
              data: "https://data.montreal.ca/dataset/ab112a84-0661-4360-9573-652eed16beeb/resource/a90678ac-7ea7-464c-b615-e8f5cb9f527b/download/rsqa_secteurs.geojson"
            });
            map.addLayer({
              id: "my-custom-layer",
              type: "fill",
              source: "my-custom-source",
              paint: {
                "fill-color": "#0080ff",
                "fill-opacity": 0.5
              }
            });
          });
        }
      };
    </script>
    <script defer type="text/javascript" src="https://cdn.jsdelivr.net/npm/jmap-core-js@7_Kathmandu_HF3"></script>  
  </body>
</html>
link
Codepen.io
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
    <meta charset="UTF-8">
    <style>
      #toggle-button {
        margin-bottom: 1rem;
      }
      #my-custom-map {
        width: 600px;
        height: 600px;
        border: 1px solid grey;
      }
    </style>
  </head>
  <body class="jmap_wrapper">
     <div>
       <span>Toggle layer visibility: </span>
       <button id="toggle-button">Wait the map is loading</button>
    </div>
  	<div id="my-custom-map"></div>
    <script type="text/javascript">
      window.JMAP_OPTIONS = {
        projectId: 1,
        restBaseUrl: "https://jmapdoc.jmaponline.net/services/rest/v2.0",
        anonymous: true,
        map: {
          onStartupMapReadyFn: function() {
            var button = document.getElementById("toggle-button");
            button.onclick = function () {
              const layerId = 4
              const currentLayerVisibility = JMap.Layer.isVisible(layerId);
              JMap.Layer.setVisible(layerId, !currentLayerVisibility);
            };
            button.innerHTML = "Toggle";
          },
          containerId: "my-custom-map",
          zoom: 8.794068040447577,
          center: {
            x: -73.66584749530894,
            y: 45.54275797936188
          },
          scaleControlVisible: true,
          scaleControlPosition: "top-right"
        }
      }
    </script>
    <script defer type="text/javascript" src="https://cdn.jsdelivr.net/npm/jmap-core-js@7_Kathmandu_HF3"></script>  
  </body>
</html>
Codepen.io
Add multiple attributions example

You can add multiple attributions in one call.

In this example, we add two attributions, one composed of a hyperlink forwarding to stackoverflow website and the other composed of a random image forwading to an example domain.

Try it out in Codepen.io

Codepen.io
window.JMAP_OPTIONS = {
  projectId: 1,
  restBaseUrl: "https://jmapdoc.jmaponline.net/services/rest/v2.0",
  anonymous: true,
  map: {
    zoom: 8.468052241302663,
    center: {
      x: -74.049048172276,
      y: 45.53487085859891
    }
  },
  hideMainLayout: true,
  application: {
    containerId: "my-custom-app"
  }
}
}
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
    <meta charset="UTF-8">
    <style>
      #my-custom-app {
        position: relative;
        overflow: hidden;
        margin-left: 50px;
        margin-top: 50px;
        width: 850px;
        height: 700px;
        border: 1px solid grey;
      }
    </style>
    <title>JMap NG</title>
  </head>
  <body>
  	<div id="my-custom-app"></div>
    <script type="text/javascript">
      window.JMAP_OPTIONS = {
        projectId: 1,
        restBaseUrl: "https://jmapdoc.jmaponline.net/services/rest/v2.0",
        anonymous: true,
        map: {
          zoom: 8.468052241302663,
          center: {
            x: -74.049048172276,
            y: 45.53583011032552
          }
        },
        hideMainLayout: true,
        application: {
          containerId: "my-custom-app"
        }
      }
    </script>
    <script defer type="text/javascript" src="https://cdn.jsdelivr.net/npm/jmap-app-js@7_Kathmandu_HF3"></script>
  </body>
</html>
<!DOCTYPE html>
<html class="jmap_wrapper">
  <head>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
    <meta charset="UTF-8">
    <style>
      html,
      body {
        height: 100%;
        width: 100%;
        margin: 0px;
        padding: 0px;
      }
    </style>
    <title>JMap NG</title>
  </head>
  <body>
    <script type="text/javascript">
      window.JMAP_OPTIONS = {
        projectId: 1,
        restBaseUrl: "https://jmapdoc.jmaponline.net/services/rest/v2.0",
        anonymous: true,
        map: {
          zoom: 8.035870457975536,
          center: {
            x: -74.18791345871016,
            y: 45.55343159305562
          }
        },
        hideMainLayout: true,
      }
    </script>
    <script defer type="text/javascript" src="https://cdn.jsdelivr.net/npm/jmap-app-js@7_Kathmandu_HF3"></script>
  </body>
</html
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
    <meta charset="UTF-8">
    <style>
      #my-custom-map {
        width: 600px;
        height: 600px;
        border: 1px solid grey;
        margin-top: 1rem;
      }
    </style>
  </head>
  <body class="jmap_wrapper">
    <span>Postal code:&nbsp;</span>
    <input placeholder="Enter postal code" id="postalCode" value="H1A 1T9"/>
    <button
      id="searchButton"
      onclick="alert('The map is loading, try again when the map is displayed on screen')">
      Search features
    </button>
    <div id="my-custom-map"></div>
    <script type="text/javascript">
      const PLACE_LAYER_ID = 6;
      let isSearching = false;
      window.search = () => {
        if (isSearching) {
          return alert("Search in progress, please wait");
        }
        const postalCode = document.getElementById("postalCode").value;
        if (!postalCode) {
          return alert("Please enter a postal code");
        }
        isSearching = true;
        if (!JMap.Layer.isSelectableById(PLACE_LAYER_ID)) {
          JMap.Layer.setSelectabilityById(PLACE_LAYER_ID, true);
        }
        JMap.Layer.Search
          .byAttribute({
            layerId: PLACE_LAYER_ID,
            attributeName: "CODE_POSTA",
            attributeValue: postalCode
          })
          .then(features => {
            console.log("Features", features);
            isSearching = false;
            if (features.length === 0) {
              JMap.Map.Selection.clearLayersSelection([PLACE_LAYER_ID]);
              return alert("No feature found !");
            }
            JMap.Map.Selection.setLayerSelection(PLACE_LAYER_ID, features);
            JMap.Map.fitFeatures(features);
          })
          .catch(error => {
            isSearching = false;
            console.error("An error occured", error);
            alert("An error occured");
          });
      };
      window.JMAP_OPTIONS = {
        projectId: 1,
        restBaseUrl: "https://jmapdoc.jmaponline.net/services/rest/v2.0",
        anonymous: true,
        map: {
          containerId: "my-custom-map",
          zoom: 13.797865986918877,
          center: {
            x: -73.48063889179525,
            y: 45.664231577062765
          }
        },
        onReady: () => {
          JMap.Event.Map.on.mapLoad("my-listener", () => {
            document.getElementById("searchButton").onclick = window.search;
          });
        }
      };
    </script>
    <script defer type="text/javascript" src="https://cdn.jsdelivr.net/npm/jmap-core-js@7_Kathmandu_HF3"></script>  
  </body>
</html>
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
    <meta charset="UTF-8">
    <style>
      #my-custom-map {
        width: 600px;
        height: 600px;
        border: 1px solid grey;
        margin-top: 1rem;
      }
    </style>
  </head>
  <body>
    <div id="my-custom-map" class="jmap_wrapper"></div>
    <script type="text/javascript">
      window.JMAP_OPTIONS = {
        projectId: 1,
        restBaseUrl: "https://jmapdoc.jmaponline.net/services/rest/v2.0",
        anonymous: true,
        map: {
          containerId: "my-custom-map",
          zoom: 13.797865986918877,
          center: {
            x: -75.48063889179525,
            y: 45.664231577062765
          },
          onStartupMapReadyFn: () => {
            JMap.Map.Attribution.addSingle({
              id: "custom-attribution",
              text: "© My custom attribution",
              href: "https://k2geospatial.com/jmap-en/"
            })
          }
        },
      };
    </script>
    <script defer type="text/javascript" src="https://cdn.jsdelivr.net/npm/jmap-core-js@jmap-ng-doc"></script>  
  </body>
</html>
<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
  <meta charset="UTF-8">
</head>
<body class="jmap_wrapper">
  <script type="text/javascript">
    window.JMAP_OPTIONS = {
      projectId: 1,
      restBaseUrl: "https://jmapdoc.jmaponline.net/services/rest/v2.0",
      anonymous: true,
      map: {
        zoom: 9.573700695830425,
        center: {
          x: -75.48063889179525,
          y: 45.53690235213574
        },
        onStartupMapReadyFn: () => {
          JMap.Map.Attribution.addMultiple([{
              id: "custom-attribution-0",
              text: "© StackOverflow",
              href: "https://stackoverflow.com/"
            },
            {
              id: "custom-attribution-1",
              imageUrl: "https://picsum.photos/180/90",
              href: "https://example.com/"
            }
          ])
        }
      },
    };
  </script>
  <script defer type="text/javascript" src="https://cdn.jsdelivr.net/npm/jmap-core-js@jmap-ng-doc"></script>
</body>
</html>
Set library options

You have to specify options to the library:

More information about startup options here

Display the map in a div

Try it out in Codepen.io

Display the map full page

Instead of running JMap NG Core inside a DIV, you can start it directly in the body of the web page for a full page experience.

Try it out in Codepen.io

<script defer type="text/javascript" src="https://cdn.jsdelivr.net/npm/jmap-core-js@7_Kathmandu_HF3"></script>
window.JMAP_OPTIONS = {
  projectId: 1,
  restBaseUrl: "https://jmapdoc.jmaponline.net/services/rest/v2.0",
  anonymous: true,
  map: {
    containerId: "my-custom-map",
    zoom: 9.757829447748511,
    center: {
      x: -73.60243569463414,
      y: 45.504533166207324
    },
    scaleControlVisible: true,
    scaleControlPosition: "top-right"
  }
}
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
    <meta charset="UTF-8">
    <style>
      #my-custom-map {
        margin-left: 50px;
        margin-top: 50px;
        width: 400px;
        height: 400px;
        border: 1px solid grey;
      }
    </style>
    <title>JMap Core</title>
  </head>
  <body class="jmap_wrapper">
  	<div id="my-custom-map"></div>
    <script type="text/javascript">
      window.JMAP_OPTIONS = {
        projectId: 1,
        restBaseUrl: "https://jmapdoc.jmaponline.net/services/rest/v2.0",
        anonymous: true,
        map: {
          containerId: "my-custom-map",
          zoom: 8.702330187924481,
          center: {
            x: -73.71123535973672,
            y: 45.565083749787306
          },
          scaleControlVisible: true,
          scaleControlPosition: "top-right"
        }
      }
    </script>
    <script defer type="text/javascript" src="https://cdn.jsdelivr.net/npm/jmap-core-js@7_Kathmandu_HF3"></script>
  </body>
</html>
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
    <meta charset="UTF-8">
    <title>JMap Core</title>
  </head>
  <body>
    <script type="text/javascript">
      window.JMAP_OPTIONS = {
        projectId: 1,
        restBaseUrl: "https://jmapdoc.jmaponline.net/services/rest/v2.0",
        anonymous: true,
        map: {
          zoom: 9.01995956737214,
          center: {
            x: -73.69369778619411,
            y: 45.50946387970188
          }
        }
      }
    </script>
    <script defer type="text/javascript" src="https://cdn.jsdelivr.net/npm/jmap-core-js@7_Kathmandu_HF3"></script>
  </body>
</html>

Locate and select feature by id

In this example, we will locate and select a feature on the map, for a given feature id.

In the example dataset, we will use the layer "Places", id=6, and locate feature with id=33.

We expect to find this feature, select it, then recenter the map around it.

Example

Try it out in Codepen.io

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
    <meta charset="UTF-8">
    <style>
      #my-custom-map {
        width: 600px;
        height: 600px;
        border: 1px solid grey;
        margin-top: 1rem;
      }
    </style>
  </head>
  <body class="jmap_wrapper">
    <button
      id="selectFeature"
      onclick="alert('The map is loading, try again when the map is displayed on screen')">
      Find, select, and display feature
    </button>
    <button
      id="unSelectFeature"
      onclick="alert('The map is loading, try again when the map is displayed on screen')">
      Unselect feature
    </button>
    <div id="my-custom-map"></div>
    <script type="text/javascript">
      const PLACE_LAYER_ID = 6;
      const FEATURE_ID = 33;
      let isLoading = false;
      window.selectFeature = () => {
        if (isLoading) {
          return alert("Locating the feature, please wait");
        }
        isLoading = true;
        if (!JMap.Layer.isSelectableById(PLACE_LAYER_ID)) {
          JMap.Layer.setSelectabilityById(PLACE_LAYER_ID, true);
        }
        JMap.Feature
          .getById(PLACE_LAYER_ID, FEATURE_ID)
          .then(feature => {
            isLoading = false;
            JMap.Map.Selection.setLayerSelection(PLACE_LAYER_ID, feature);
            JMap.Map.fitFeatures([feature]);
          })
          .catch(error => {
            isLoading = false;
            console.error("An error occured", error);
            alert("Cannot get the feature !");
          });
      };
      window.unSelectFeature = () => {
        JMap.Map.Selection.clearLayersSelection([PLACE_LAYER_ID]);
      };
      window.JMAP_OPTIONS = {
        projectId: 1,
        restBaseUrl: "https://jmapdoc.jmaponline.net/services/rest/v2.0",
        anonymous: true,
        map: {
          containerId: "my-custom-map",
          zoom: 13.797865986918877,
          center: {
            x: -73.48063889179525,
            y: 45.664231577062765
          }
        },
        onReady: () => {
          JMap.Event.Map.on.mapLoad("my-listener", () => {
            document.getElementById("selectFeature").onclick = window.selectFeature;
            document.getElementById("unSelectFeature").onclick = window.unSelectFeature;
          });
        }
      };
    </script>
    <script defer type="text/javascript" src="https://cdn.jsdelivr.net/npm/jmap-core-js@7_Kathmandu_HF3"></script>  
  </body>
</html>

Custom mouseover on a layer

The mouseover is a popup window that is displayed after clicking on features on the map.

If no mouseover content is set in the layer configuration, no popup is displayed when clicking on a feature of that layer.

You can set your own mouseover content programatically in Javascript. For instance, you can add a mouseover on a layer that has no mousoever content in its configuration.

In the following example, the layer "Hydrography", id=3, has no mouseover defined.

We will set a custom mouseover content that:

  • Always displays a custom header

  • Displays the mouseover content for a clicked hydrography feature

  • Avoids displaying a mouseover for the "Event" layer (blue dots on the map)

  • Always displays a custom footer

Set the custom mouseover

Try it out in

Add an event on move end

In this example, we will show how to add an event on move end in JMap NG.

Example

We add an event to display the current center of the map. The value is updated each time the move ends.

Try it out in

<html>
<head>
  <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
  <meta charset="UTF-8">
  <style>
    #my-custom-map {
      width: 600px;
      height: 600px;
      border: 1px solid grey;
      margin-top: 1rem;
    }
  </style>
</head>
<body>
  <label><b>Center of the displayed map:&nbsp;</b></label>
  <br/>
  <label>Latitude:&nbsp;</label>
  <span id="latitude"></span>
  <br/>
  <label>Longitude:&nbsp;</label>
  <span id="longitude"></span>
  <div id="my-custom-map" class="jmap_wrapper"></div>
  <script type="text/javascript">
    window.JMAP_OPTIONS = {
      projectId: 1,
      restBaseUrl: "https://jmapdoc.jmaponline.net/services/rest/v2.0",
      anonymous: true,
      map: {
        containerId: "my-custom-map",
        zoom: 13.797865986918877,
        center: {
          x: -73.48063889179525,
          y: 45.664231577062765
        },
        onStartupMapReadyFn: () => {
          const initialCenter = JMap.Map.getMap().getCenter()
          document.getElementById("latitude").textContent = initialCenter.lat
          document.getElementById("longitude").textContent = initialCenter.lng
          JMap.Event.Map.on.moveEnd("move-end", (params) => {
            const center = params.map.getCenter()
            document.getElementById("latitude").textContent = center.lat
            document.getElementById("longitude").textContent = center.lng
          })
        }
      },
    };
  </script>
  <script defer type="text/javascript" src="https://cdn.jsdelivr.net/npm/jmap-core-js@jmap-ng-doc"></script>
</body>
</html>
Codepen.io
Codepen.io
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
    <meta charset="UTF-8">
    <style>
      #my-custom-map {
        width: 600px;
        height: 600px;
        border: 1px solid grey;
      }
    </style>
  </head>
  <body class="jmap_wrapper">
    <div id="my-custom-map"></div>
    <script type="text/javascript">
      const HYDRO_LAYER_ID = 3;
      const PLACE_LAYER_ID = 6;
      window.JMAP_OPTIONS = {
        projectId: 1,
        restBaseUrl: "https://jmapdoc.jmaponline.net/services/rest/v2.0",
        anonymous: true,
        map: {
          containerId: "my-custom-map",
          zoom: 13.797865986918877,
          center: {
            x: -73.48063889179525,
            y: 45.664231577062765
          }
        },
        extensions: [{
          id: "custom-mouseover-extension",
          initFn: () => {
            JMap.Event.MouseOver.on.beforeContentProcessed("My-custom-mouse-over", params => {
              const {
                selection,
                getFeaturesByLayerId,
                addHtmlContentAtTheBeginning,
                addHtmlContentAtTheEnd,
                removeFeaturesFromLayerSelection
              } = params;
              /* this method is processed each time we click on features on the map, even if no mouse-over is defined for a layer */
              /* add a message in the top of the mouseover popup */
              addHtmlContentAtTheBeginning(`<span style="font-size: 1rem">Custom popup header visible for all layers</span>`);
              /* add a message in the bottom of the mouseover popup */
              addHtmlContentAtTheEnd(`<span style="font-size: 1rem">Custom popup footer visible for all layers</span>`);
              console.info("All selected features", { ...selection });
              /* if some place feature has been selected, remove it from the mouseover */
              removeFeaturesFromLayerSelection(PLACE_LAYER_ID, getFeaturesByLayerId(PLACE_LAYER_ID).map(f => f.id));
              console.info("Selected hydrography features", getFeaturesByLayerId(HYDRO_LAYER_ID));
            })
          },
          renderMouseOver: (layer, feature) => {
            /* this method is processed for each feature that has been clicked */
            if (layer.id !== HYDRO_LAYER_ID) {
              return;
            }
            return {
              html: `
                <div style="display: flex; flex-direction: column; align-items: center;">
                  <span>
                    Hydro feature id="${feature.id}" clicked !
                  </span>
                  <button
                    onclick="alert('Feature id=${feature.id} clicked !')"
                    style="cursor: pointer; border: 1px solid grey; margin: .5rem;">
                    Click me !
                  </button>
                </div>`
            };
          }
        }]
      }
    </script>
    <script defer type="text/javascript" src="https://cdn.jsdelivr.net/npm/jmap-core-js@7_Kathmandu_HF3"></script>  
  </body>
</html>

Locate and select feature(s) by location

In this example, we will locate and select a feature on the map for a given location and on any selectable layer.

The default coordinates will select a feature on layer "Places", id=6.

Example

Try it out in Codepen.io

<!DOCTYPE html>
<html>

<head>
  <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
  <meta charset="UTF-8">
  <style>
    #my-custom-map {
      width: 600px;
      height: 600px;
      border: 1px solid grey;
      margin-top: 1rem;
    }
  </style>
</head>

<body class="jmap_wrapper">
  <span>Latitude:&nbsp;</span>
  <input placeholder="Enter latitude" id="latitude" value="-73.49440489999999" />
  <span>Longitude:&nbsp;</span>
  <input placeholder="Enter longitude" id="longitude" value="45.667717299999985" />
  <button id="selectFeature" onclick="alert('The map is loading, try again when the map is displayed on screen')">Select</button>
  <button id="unSelectFeature" onclick="alert('The map is loading, try again when the map is displayed on screen')">
    <span>Unselect</span>
  </button>
  <br />
  <br />
  <span>You can also click on the map to choose a location:&nbsp;</span>
  <div id="my-custom-map">
  </div>
  <script type="text/javascript">
    window.selectFeature = () => {
      const latitude = Number(document.getElementById("latitude").value);
      const longitude = Number(document.getElementById("longitude").value);
      if (!latitude || isNaN(latitude)) {
        return alert("Please enter a valid latitude");
      }
      if (!longitude || isNaN(latitude)) {
        return alert("Please enter a valid longitude");
      }
      JMap.Map.Selection.selectOnAllLayersAtLocation({
        x: latitude,
        y: longitude
      });
    };
    window.unSelectFeature = () => {
      JMap.Map.Selection.clearSelection();
    };
    window.JMAP_OPTIONS = {
      projectId: 1,
      restBaseUrl: "https://jmapdoc.jmaponline.net/services/rest/v2.0",
      anonymous: true,
      map: {
        containerId: "my-custom-map",
        zoom: 13.797865986918877,
        center: {
          x: -73.49440489999999,
          y: 45.667717299999985
        },
      },
      onReady: () => {
        JMap.Event.Map.on.mapLoad("my-listener", () => {
          document.getElementById("selectFeature").onclick = window.selectFeature;
          document.getElementById("unSelectFeature").onclick = window.unSelectFeature;
        });
        JMap.Event.Map.on.click("selectLocation", (res) => {
          document.getElementById("latitude").value = res.location.x;
          document.getElementById("longitude").value = res.location.y;
        });
      }
    };
  </script>
  <script defer type="text/javascript" src="https://cdn.jsdelivr.net/npm/jmap-core-js@jmap-ng-doc"></script>
</body>
</html>

Add a JMap NG App extension

JMap NG extensions are plug-in modules taht can exetend the functionalities present in JMap NG App. You can develop and add your own extensions.

For more information about JMap NG extensions, refer to this section.

The JMap NG App extension API documentation is available here.

A simple example

The example bellow shows a simple extension that creates a panel and displays some text on it.

Try it out in Codepen.io

There is another way to load your extension, after the JMap NG App has been loaded.

You can register the previous extension like this:

A more complete example

The example bellow shows you a more sophisticated extension named "Favourite locations".

It adds points on the map where you click, and displays a list of your "favourite" locations. You can also toggle the visibility of the point layer.

Try it out in

Create a custom form in a div

This example shows how to create a custom form. It instantiates a JMap NG App extension that creates a custom form in a panel.

Form UI is only available in JMap NG App, and not in JMap NG Core.

Example

Try it out in

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
    <meta charset="UTF-8">
    <style>
      html,
      body {
        padding: 0px;
        margin: 0px;
        height: 100%;
        width: 100%;
      }
    </style>
  </head>
  <body>
  	<script type="text/javascript">
      const extensionId = "my-extension"
      window.JMAP_OPTIONS = {
        restBaseUrl: "https://jmapdoc.jmaponline.net/services/rest/v2.0",
        anonymous: true,
        projectId: 1,
        map: {
          center: {
            x: -74.17355888315548,
            y: 45.504030591808856
          },
          zoom: 8.04872607654608
        },
        hideMainLayout: true,
        application: {
          panel: `${extensionId}-panel`,
          extensions: [{
            id: extensionId,
            panelIcon: "fa-unlock-alt", // this is a font-awesome icon, but you can pass an image url
            panelTitle: "My custom extension",
            panelTooltip: "My custom extension",
            onPanelCreation: containerId => {
              const container = document.getElementById(containerId)
              const span = document.createElement("span")
              span.id = "my-text"
              span.innerHTML = "This is a custom panel"
              container.append(span)
            },
            onPanelDestroy: (containerId) => {
              document.getElementById(containerId)
              const text = document.getElementById("my-text")
              if (text) {
                text.remove()
              }
            }
          }]
        }
      }
    </script>
    <script defer type="text/javascript" src="https://cdn.jsdelivr.net/npm/jmap-app-js@7_Kathmandu_HF3"></script>
  </body>
</html>
Codepen.io
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
    <meta charset="UTF-8">
    <style>
      #my-custom-app {
        position: relative;
        overflow: hidden;
        width: 850px;
        height: 700px;
        border: 1px solid grey;
      }
    </style>
  </head>
  <body>
    <div id="my-custom-app" class="jmap_wrapper"></div>
    <script type="text/javascript">
      window.JMAP_OPTIONS = {
        projectId: 1,
        restBaseUrl: "https://jmapdoc.jmaponline.net/services/rest/v2.0",
        anonymous: true,
        map: {
          zoom: 9.573700695830425,
          center: {
            x: -73.7219880403544,
            y: 45.53690235213574
          }
        },
        application: {
          containerId: "my-custom-app",
          panel: "test-form-panel",
          extensions: [{
            id: "test-form",
            panelTitle: "Test of form component API",
            initFn: () => {/* nothing to do */},
            onPanelCreation: panelContainerId => {
              document.getElementById(panelContainerId).style.padding = "1rem"
              JMap.Application.Form.render(panelContainerId, {
                id: "search-form",
                schema: {
                  properties: {
                    name: {
                      title: "Office Name",
                      type: "string",
                      isRequired: true,
                      maxLength: 255
                    },
                    type: {
                      title: "Office type",
                      type: "number",
                      // default: 2,
                      enum: [1, 2, 3],
                      enumNames: ["Local", "External", "Mixte"]
                    }
                  }
                },
                uiSchema: [
                  {
                    type: "Tab",
                    controls: [
                      {
                        id: "name",
                        label: "Office name",
                        widget: "input",
                        scope: "#/properties/name"
                      },
                      {
                        id: "type",
                        label: "Office type",
                        widget: "select",
                        scope: "#/properties/type"
                      }
                    ]
                  }
                ],
                defaultValueById: { // defaultValueById is optional
                  name: "default value",
                  type: 2
                },
                validate: (values, formMetaData) => JMap.Form.validateData(formMetaData, JMap.Form.getPreparedData(formMetaData, values)),
                onSubmit: values => {
                  // Here you process/persist the data. This function can:
                  //  - returns nothing: means onSubmit success
                  //  - returns a string: the string is an error that will be displayed
                  //  - returns a promise: the form will display a loading button until the promise resolved
                  JMap.Application.Message.info(`Submitted values: ${JSON.stringify(values)}`)
                }
              })
            },
            onPanelDestroy: panelContainerId => {
              JMap.Application.Form.destroyByContainerId(panelContainerId)
            }
          }]
        }
      };
    </script>
    <script defer type="text/javascript" src="https://cdn.jsdelivr.net/npm/jmap-app-js@7_Kathmandu_HF3"></script>  
  </body>
</html>
Codepen.io
JMap.Application.Extension.register({
  id: "my-extension",
  panelIcon: "fas fa-wrench",
  panelTitle: "My custom extension",
  panelTooltip: "My custom extension",
  onPanelCreation: containerId => {
    const container = document.getElementById(containerId)
    const span = document.createElement("span")
    span.id = "my-text"
    span.innerHTML = "This is a custom panel"
    container.append(span)
  },
  onPanelDestroy: (containerId) => {
    document.getElementById(containerId)
    const text = document.getElementById("my-text")
    if (text) {
      text.remove()
    }
  }
})
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
    <meta charset="UTF-8" />
    <style>
      html,
      body {
        padding: 0px;
        margin: 0px;
        height: 100%;
        width: 100%;
      }
    </style>
  </head>

  <body>
    <script type="text/javascript">
      function getFavouriteExtension(extensionId) {
        const LAYER_ID = "favourite-custom-layer"
        const CLICK_LISTENER_ID = "favourite-click-listener"
        const ACTION_ADD = "FAVOURITE_ADD_LOCATION"
        const ACTION_DEL = "FAVOURITE_DEL_LOCATION"
        const ACTION_SET_VISIBILITY = "FAVOURITE_SET_LAYER_VISIBILITY"
        let panelContainerId
        let visibilityContainerId
        let locationsContainerId
        let reduxUnsubscribe
        let map
        let previousLocationCount = 0
        let previousLayerVisibility = true
        // return the JMap application current redux state
        function getAppState() {
          return JMap.getDataStore().getState().external["jmap_app"]
        }
        // return the favourite extension current redux state
        function getFavouriteState() {
          return JMap.getDataStore().getState().external[extensionId]
        }
        // return the primary color in use by JMap Application
        function getPrimaryColor() {
          return getAppState().ui.theme.palette.text.primary
        }
        // return the secondary color in use by JMap Application
        function getSecondaryColor() {
          return getAppState().ui.theme.palette.text.secondary
        }
        /**
         * This is the favourite service.
         * It is where we change the redux store values,
         * then the reduxChangeHandler function will react to state changes.
         **/
        const favouriteSVC = {
          // returns all existing locations
          getLocations: () => getFavouriteState().locations.slice(),
          // return true if the layer is displayed on the map
          getLayerVisibility: () => getFavouriteState().isLayerVisible,
          // add a location in the redux store
          add: location =>
            JMap.getDataStore().dispatch({
              type: ACTION_ADD,
              location: location
            }),
          // remove a location in the redux store at the index
          del: index =>
            JMap.getDataStore().dispatch({
              type: ACTION_DEL,
              index: index
            }),
          // if false, hide the layer on the map, else show it
          setLayerVisibility: visibility =>
            JMap.getDataStore().dispatch({
              type: ACTION_SET_VISIBILITY,
              visibility: visibility
            })
        }
        /**
         * This is the redux reducer, a pure javascript function.
         * It reacts to the actions you dispatch, and changes the data in the store.
         **/
        function reduxReducer(currentFavouriteState, action) {
          if (!currentFavouriteState) {
            currentFavouriteState = {
              locations: [],
              isLayerVisible: previousLayerVisibility
            }
          }
          switch (action.type) {
            case ACTION_ADD: {
              const newFavouriteState = { ...currentFavouriteState, locations: currentFavouriteState.locations.slice() }
              newFavouriteState.locations.push(action.location)
              return newFavouriteState
            }
            case ACTION_DEL: {
              const newFavouriteState = { ...currentFavouriteState, locations: currentFavouriteState.locations.slice() }
              newFavouriteState.locations.splice(action.index, 1)
              return newFavouriteState
            }
            case ACTION_SET_VISIBILITY: {
              return { ...currentFavouriteState, isLayerVisible: action.visibility }
            }
          }
          return currentFavouriteState
        }
        // Create the dom elements to display the layer visibility checkbox
        function displayLayerVisibility() {
          if (!visibilityContainerId) {
            visibilityContainerId = `${panelContainerId}-visibility`
          }
          const panelContainer = document.getElementById(panelContainerId)
          panelContainer.style.color = getPrimaryColor()
          panelContainer.style.padding = "1rem"
          const container = document.createElement("div")
          container.id = visibilityContainerId
          const span = document.createElement("span")
          span.innerHTML = "Display locations on map"
          container.append(span)
          const input = document.createElement("input")
          input.type = "checkbox"
          input.style.cursor = "pointer"
          input.style.marginLeft = "1rem"
          input.checked = favouriteSVC.getLayerVisibility()
          input.onclick = () => favouriteSVC.setLayerVisibility(!favouriteSVC.getLayerVisibility())
          container.append(input)
          panelContainer.append(container)
        }
        // Create the dom elements to display the locations list
        function displayLocations() {
          if (!locationsContainerId) {
            locationsContainerId = `${panelContainerId}-locations`
          }
          const panelContainer = document.getElementById(panelContainerId)
          const container = document.createElement("div")
          container.id = locationsContainerId
          container.style.marginTop = "1rem"
          container.style.display = "flex"
          container.style.flexDirection = "column"
          const locations = favouriteSVC.getLocations()
          if (locations.length === 0) {
            const span = document.createElement("span")
            span.innerHTML = "Click on the map to add your favourite locations"
            span.style.color = getSecondaryColor()
            container.append(span)
          } else {
            for (var i = 0; i < locations.length; i++) {
              const location = locations[i]
              const index = i
              const locationContainer = document.createElement("div")
              locationContainer.style.marginTop = "1rem"
              locationContainer.style.display = "flex"
              locationContainer.style.alignItems = "center"
              locationContainer.style.justifyContent = "space-between"
              const locationSpan = document.createElement("span")
              locationSpan.innerHTML = `LNG = ${location.x.toFixed(5)} | LAT = ${location.y.toFixed(5)}`
              locationContainer.append(locationSpan)
              const button = document.createElement("button")
              button.innerHTML = "X"
              button.title = "Click to remove the location"
              button.style.cursor = "pointer"
              button.onclick = () => favouriteSVC.del(index)
              locationContainer.append(button)
              container.append(locationContainer)
            }
          }
          panelContainer.append(container)
        }
        // Remove in the dom the container, given the container id
        function removeContainer(containerId) {
          const container = document.getElementById(containerId)
          if (container) {
            container.remove()
          }
        }
        function refreshLocations() {
          removeContainer(locationsContainerId)
          displayLocations()
        }
        // Locations have changed in the store, we add or remove it in the mapbox source.
        function refreshLayer() {
          map.getSource(LAYER_ID).setData({
            type: "FeatureCollection",
            features: getFavouriteState().locations.map(l => ({
              type: "Feature",
              geometry: { type: "Point", coordinates: [l.x, l.y] }
            }))
          })
        }
        /**
         * The redux function that is called when the redux state changed.
         * If a favourite value has changed it refreshes the ui or the map.
         **/
        function reduxChangeHandler() {
          const state = getFavouriteState()
          if (previousLocationCount !== state.locations.length) {
            previousLocationCount = state.locations.length
            refreshLocations()
            refreshLayer()
          }
          if (previousLayerVisibility !== state.isLayerVisible) {
            previousLayerVisibility = state.isLayerVisible
            map.setLayoutProperty(LAYER_ID, "visibility", state.isLayerVisible ? "visible": "none")
          }
        }
        /**
         * The map interactor, more details on:
         * https://k2geospatial.github.io/jmap-app/latest/modules/jmap.map.interaction.html
         **/
        const mapInteractor = {
          init: mapboxMap => {
            map = mapboxMap
            map.addSource(LAYER_ID, {
              type: "geojson",
              data: { type: "FeatureCollection", features: [] }
            })
            map.addLayer({
              id: LAYER_ID,
              type: "circle",
              source: LAYER_ID,
              paint: {
                "circle-radius": 10,
                "circle-color": "#0066ff"
              }
            })
            JMap.Event.Map.on.click(CLICK_LISTENER_ID, params => {
              favouriteSVC.add(params.location)
            })
            JMap.Event.Map.deactivate(CLICK_LISTENER_ID)
          },
          activate: () => {
            JMap.Map.getMap().doubleClickZoom.disable()
            JMap.Event.Map.activate(CLICK_LISTENER_ID)
          },
          deactivate: () => {
            JMap.Map.getMap().doubleClickZoom.enable()
            JMap.Event.Map.deactivate(CLICK_LISTENER_ID)
          },
          terminate: () => {
            JMap.Event.Map.remove(CLICK_LISTENER_ID)
            map.removeLayer(LAYER_ID)
            map.removeSource(LAYER_ID)
          }
        }
        // Returns the extension object that will be registered by the JMap Application
        return {
          id: extensionId,
          panelIcon: "fa-thumbtack",
          panelTitle: "My favourite locations",
          panelTooltip: "My favourite locations",
          onPanelCreation: containerId => {
            if (!panelContainerId) {
              panelContainerId = containerId
            }
            displayLayerVisibility()
            displayLocations()
            reduxUnsubscribe = JMap.getDataStore().subscribe(reduxChangeHandler)
          },
          onPanelDestroy: () => {
            removeContainer(visibilityContainerId)
            removeContainer(locationsContainerId)
            reduxUnsubscribe()
          },
          interactor: mapInteractor,
          serviceToExpose: favouriteSVC,
          storeReducer: reduxReducer
        }
      }
      const extensionId = "favourite"
      window.JMAP_OPTIONS = {
        restBaseUrl: "https://jmapdoc.jmaponline.net/services/rest/v2.0",
        anonymous: true,
        projectId: 1,
        map: {
          center: {
            x: -73.92251114687645,
            y: 45.52559621002098
          },
          zoom: 9.07038517536697
        },
        hideMainLayout: true,
        application: {
          panel: `${extensionId}-panel`, // will display the favourite panel when application starts
          extensions: [getFavouriteExtension(extensionId)] // will register the favourite extension
        }
      }
    </script>
    <script defer type="text/javascript" src="https://cdn.jsdelivr.net/npm/jmap-app-js@7_Kathmandu_HF3"></script>
  </body>
</html>