Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
You can start JMap NG Core in a div of your website, or as a full page application.
To use the JMap NG Core library, simply import the JMap NG Core Javascript library from our CDN link.
<script defer type="text/javascript" src="https://cdn.jsdelivr.net/npm/jmap-core-js@7_Kathmandu_HF3"></script>
Make sure the version of the library matches the version of your JMap Server instance.
You have to specify options to the library:
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"
}
}
More information about startup options here
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 {
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>
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
<!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>
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.
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>Postal code: </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>
This example shows how to toggle the visibility of a JMap layer on the map.
Try it out in 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>
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.
In this example, we will show how to add an event on move end in JMap NG.
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 Codepen.io
<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: </b></label>
<br/>
<label>Latitude: </label>
<span id="latitude"></span>
<br/>
<label>Longitude: </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>
You can start JMap NG App in a div of your website, or as a full page application.
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).
You have to provide required and optional parameters to the library and the app:
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"
}
}
}
More information about startup options here
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-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>
Try it out in Codepen.io
<!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>Latitude: </span>
<input placeholder="Enter latitude" id="latitude" value="-73.49440489999999" />
<span>Longitude: </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: </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>
<!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>
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.
The example bellow shows a simple extension that creates a panel and displays some text on it.
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>
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>
There is another way to load your extension, after the JMap NG App has been loaded.
You can register the previous extension like this:
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()
}
}
})
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 Codepen.io
<!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>
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
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;
}
</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>
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.
In the example bellow, we add a single attribution composed of one hyperlink forwarding to k2geospatial website.
Try it out in
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
<!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>
<!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>
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.
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>