This sample demonstrates how to add map notes to a MapNotesLayer using the SketchViewModel. Map notes can be added, edited, and saved to a new WebMap.
Map notes are part of web maps, and are typically created with the Map Viewer. This is useful for scenarios when someone needs to quickly add comments to a map, or sketch notes related to action items, or map edits. The MapNotesLayer lets you display and modify map notes (features sketched on a web map) from the Map Viewer. A new MapNotesLayer can also be created using the SketchViewModel.
Use the top right toolbar to select a sketch tool or delete all sketched graphics. Selecting a sketched feature will allow you to move the feature, and modify vertices (polylines and polygons). If the feature is text, you will be able to move the feature and edit the text with the editor in the lower-left. Once a text feature is selected to move or modify, the text input will expand. Once the moves or modifications are complete, the text input will collapse.
Expanding the LayerList widget on the left will display all the types
of MapNotesLayer, with the individual features listed as sublayers for each type. In this sample, we add a title
attribute to each graphic so that they can have unique identifiers.
When you are ready to save, click on the Save button to save as a new WebMap. Note that if a feature is actively being modified (the feature will be highlighted), saving is disabled.
How it works
First, create a new MapNotesLayer and add it to the view.
// add a MapNotesLayer for the sketches and map notes
let sketchLayer = new MapNotesLayer();
view.map.add(sketchLayer);
Second, create four new SketchViewModels. We need to use a different SketchViewModel for each layer (e.g. point, polyline, polygon, text) so that each layer can be added and modified independent of the other layers. Because the SketchViewModel requires a GraphicsLayer, we cannot just use a MapNotesLayer here. We need to use the appropriate MapNotesLayer layer type.
const pointLayer = sketchLayer.pointLayer;
const polylineLayer = sketchLayer.polylineLayer;
const polygonLayer = sketchLayer.polygonLayer;
const textLayer = sketchLayer.textLayer;
// create a new sketch view model for each different map note type
// this allows both the creation and modification of each different map note
const pointSketchViewModel = createSketchViewModels(pointLayer);
const polylineSketchViewModel = createSketchViewModels(polylineLayer);
const polygonSketchViewModel = createSketchViewModels(polygonLayer);
const textSketchViewModel = createSketchViewModels(textLayer);
function createSketchViewModels(layer) {
const sketchVM = new SketchViewModel({
view,
layer,
updateOnGraphicClick: true
});
sketchVM.on("create", addGraphic);
if (layer !== textLayer) {
sketchVM.on("update", updateMapNotes);
}
return sketchVM;
}
Third, we add an add
function with a switch statement to define the added graphic symbology based on geometry type.
However, since both point and text graphics have a point
geometry, we need to add more logic based on the button the user selected.
We also add a title
attribute to each graphic so that they can have unique identifiers in the LayerList widget. We could also define
a popupTemplate here if we wished. These will be persisted when saved as a WebMap. Here we see the add
function with a switch
statement for points and text.
// called when sketchViewModel's create-complete event is fired
function addGraphic(event) {
if (event.state === "complete") {
switch (event.tool) {
case "point":
count++;
let elemental = document.getElementsByClassName("active");
if (elemental[0].id == "pointButton") {
pointLayer.remove(event.graphic);
const newPointGraphic = new Graphic({
geometry: event.graphic.geometry,
symbol: {
type: "simple-marker",
style: "circle",
size: 10,
color: [255, 255, 255, 0.8],
outline: {
color: [155, 89, 182],
size: 10,
width: 2
}
},
attributes: {
title: "point map note #" + count
}
});
pointLayer.add(newPointGraphic);
} else if (elemental[0].id == "textButton") {
count++;
textLayer.remove(event.graphic);
const newTextGraphic = new Graphic({
geometry: event.graphic.geometry,
symbol: {
type: "text",
text: "new text alert",
color: [255, 255, 255],
haloColor: [1, 68, 33],
haloSize: 2,
font: {
family: "Arial Unicode MS",
size: 14
}
},
attributes: {
title: "text map note #" + count
}
});
textLayer.add(newTextGraphic);
} else {
console.log("point logic error occurred");
break;
}
setActiveButton();
break;
For modifying text, it's a bit more complicated as we want to allow the user to modify both the location of the text point, and also the text of the text point. We use the same logic for moving text as we do for point, but for modifying the text we need another UI option. We have created a new Expand widget to contain a text input which is automatically expanded when the user selects a sketched text. After the user inputs the new text (or finishes the move), this Expand widget automatically collapses to obscure it from view.
// Listen to update event to modify a graphic to view
textSketchViewModel.on("update", (event) => {
// prevent saving if the feature is still being modified
save.disabled = true;
textInput.addEventListener("keyup", completeTextEdit);
textExpand.expand();
let currentGraphic = event.graphics[0];
let currentText = textInput.nodeValue;
const eventInfo = event.toolEventInfo;
if (event.state === "complete") {
currentGraphic.symbol = new TextSymbol({
text: textInput.value,
color: [255, 255, 255],
haloColor: [1, 68, 33],
haloSize: 2,
font: {
family: "Arial Unicode MS",
size: 14
}
});
// once modifications are complete, enable saving again
save.disabled = false;
// once modifications are complete, collapse text input
textExpand.collapse();
}
// populate the textInput with the selected
// graphics's symbol text
if (event.state === "start") {
if (currentGraphic.symbol.type === "text") {
let newTextSymbol = currentGraphic.symbol;
textInput.value = newTextSymbol.text;
}
}
function completeTextEdit(event) {
if (event.defaultPrevented) {
return;
}
if (event.key !== undefined) {
if (event.key === "Enter") {
event.preventDefault();
textSketchViewModel.complete();
// once modifications are complete, collapse text input
textExpand.collapse();
}
}
}
});
Lastly, you can save your newly created MapNotesLayer to a new WebMap.