|
POST
|
This probably has what you need to accomplish what you're trying to do, or at least get you closer to it anyways: <html lang="en">
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="initial-scale=1,maximum-scale=1,user-scalable=no"
/>
<title>
Highlight point features | Sample | ArcGIS Maps SDK for JavaScript 4.29
</title>
<link rel="stylesheet" href="https://js.arcgis.com/4.29/esri/themes/light/main.css" />
<script src="https://js.arcgis.com/4.29/"></script>
<style>
html,
body,
#viewDiv {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
}
#paneDiv {
position: absolute;
bottom: 40px;
width: 100%;
text-align: center;
background-color: transparent;
color: white;
}
.esri-button-overwrite {
width:auto;
display: table-cell;
margin: 4px;
background-color: white;
color: #0079c1;
}
</style>
<script>
require(["esri/Map", "esri/WebScene", "esri/views/SceneView"], (
Map,
WebScene,
SceneView
) => {
// load webscene from portal item
const webScene = new WebScene({
portalItem: {
// autocasts as new PortalItem()
id: "475a7161ed194460b5b52654e29581a2"
}
});
const view = new SceneView({
map: webScene,
container: "viewDiv",
// set highlightOptions like color and fillOpacity
highlightOptions: {
color: [255, 241, 58],
fillOpacity: 0.4
}
});
// these two highlight handlers are used for selection and hovering over features
let highlightSelect;
webScene.when(() => {
// get layer from webScene
const stationLayer = webScene.layers.getItemAt(1);
// highlight is set on the layerView, so we need to detect when the layerView is ready
view.whenLayerView(stationLayer).then((layerView) => {
// creates the query that will be used to obtain the features needed for the highlight
const queryStations = stationLayer.createQuery();
// features that are passed in the highlight function need to have an `objectID`
// if the query is built using `new Query()` then `queryStations.outFields = ["objectID"]` should be set
const buttons = document.querySelectorAll("button");
for (let i = 0, button = null; (button = buttons[i]); i++) {
button.addEventListener("mouseover", onClick);
button.addEventListener("mouseout", onMouseOut);
}
function onClick(event) {
queryStations.where = `nom='${event.target.innerText}'`;
stationLayer.queryFeatures(queryStations).then((result) => {
if (typeof view.popup?.clear == "function") {
view.popup.close();
view.popup.clear();
}
// if a feature is already highlighted, then remove the highlight
if (highlightSelect) {
highlightSelect.remove();
highlightSelect = null;
}
// the feature to be highlighted
const feature = result.features[0];
view.openPopup({ features: [feature] });
// use the objectID to highlight the feature
highlightSelect = layerView.highlight(
feature.attributes["OBJECTID"]
);
/*
// center the feature
view
.goTo(
{
target: feature.geometry,
tilt: 70,
zoom: 16
},
{
duration: 2000,
easing: "in-out-expo"
}
)
.catch((error) => {
if (error.name != "AbortError") {
console.error(error);
}
});
*/
});
}
function onMouseOut(evt) {
if (typeof view.popup?.clear == "function") {
view.popup.close();
view.popup.clear();
}
if (highlightSelect) {
highlightSelect.remove();
highlightSelect = null;
}
}
});
});
});
</script>
</head>
<body>
<div id="viewDiv"></div>
<div id="paneDiv" class="esri-widget">
<h3>Subway stations</h3>
<button class="esri-button esri-button-overwrite">Valmy</button>
<button class="esri-button esri-button-overwrite">
St-Jean Vieux Lyon
</button>
<button class="esri-button esri-button-overwrite">Charpennes</button>
<button class="esri-button esri-button-overwrite">Sans souci</button>
<button class="esri-button esri-button-overwrite">Hôtel de Ville</button>
<button class="esri-button esri-button-overwrite">Garibaldi</button>
</div>
</body>
</html> Note that adding and removing a highlight is redundant because the popup adds its own highlight as well. Therefore, in order to get rid of the popup's highlight, I added code to close the popup as well. I also commented out the part where the view zooms to the selected feature...otherwise, the popup would always appear over top of the feature, effectively hiding it.
... View more
02-29-2024
09:53 AM
|
1
|
1
|
1855
|
|
POST
|
In that case, can you supply the code where you create the PopupTemplate and its actions?
... View more
02-20-2024
10:31 AM
|
0
|
0
|
1206
|
|
POST
|
There isn't native support for this in the SDK since polygon graphics only support horizontal labeling (see the LabelClass.labelPlacement property). That's not to say you can't pull this off with a little extra work though. One idea would be to maintain a separate polyline layer where the polygons have been converted to polylines, and those polylines labeled instead, since the SDK does support drawing labels alone lines. If you make those polylines transparent, it will appear as if the polygon edge is labeled. Below is a basic example: <html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
<title>Sketch widget | Sample | ArcGIS Maps SDK for JavaScript 4.28</title>
<link rel="stylesheet" href="https://js.arcgis.com/4.28/esri/themes/light/main.css" />
<script src="https://js.arcgis.com/4.28/"></script>
<style>
html,
body,
#viewDiv {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
}
</style>
<script>
require([
"esri/Color",
"esri/Graphic",
"esri/Map",
"esri/geometry/Polygon",
"esri/geometry/Polyline",
"esri/layers/FeatureLayer",
"esri/renderers/SimpleRenderer",
"esri/symbols/Font",
"esri/symbols/SimpleFillSymbol",
"esri/symbols/SimpleLineSymbol",
"esri/symbols/TextSymbol",
"esri/views/MapView"
], (
Color,
Graphic,
Map,
Polygon,
Polyline,
FeatureLayer,
SimpleRenderer,
Font,
SimpleFillSymbol,
SimpleLineSymbol,
TextSymbol,
MapView
) => {
const map = new Map({
basemap: "topo-vector"
});
const view = new MapView({
container: "viewDiv",
map: map,
zoom: 5,
center: [90, 45]
});
view.when(() => {
var polygon = Polygon.fromJSON({
spatialReference: { latestWkid: 3857, wkid: 102100 },
rings: [
[
[9201708.294980831, 6103380.512501691],
[9199505.916630479, 6061356.630160495],
[9192922.9112975, 6019793.170268292],
[9182031.40376591, 5979145.510789373],
[9166950.723671414, 5939858.9959870605],
[9147846.098100968, 5902364.05713876],
[9124926.841330219, 5867071.496640812],
[9098444.06153251, 5834367.987171478],
[9068687.909585183, 5804611.835224151],
[9035984.400115848, 5778129.055426442],
[9000691.8396179, 5755209.7986556925],
[8963196.9007696, 5736105.173085245],
[8923910.385967288, 5721024.492990751],
[8883262.726488369, 5710132.98545916],
[8841699.266596166, 5703549.980126182],
[8799675.38425497, 5701347.601775828],
[8757651.501913773, 5703549.980126182],
[8716088.04202157, 5710132.98545916],
[8675440.382542651, 5721024.492990751],
[8636153.867740339, 5736105.173085245],
[8598658.928892039, 5755209.7986556925],
[8563366.368394092, 5778129.055426442],
[8530662.858924756, 5804611.835224151],
[8500906.70697743, 5834367.987171478],
[8474423.92717972, 5867071.496640812],
[8451504.670408972, 5902364.057138759],
[8432400.044838525, 5939858.9959870605],
[8417319.36474403, 5979145.510789373],
[8406427.85721244, 6019793.170268291],
[8399844.85187946, 6061356.630160495],
[8397642.473529108, 6103380.512501691],
[8399844.85187946, 6145404.394842886],
[8406427.85721244, 6186967.8547350895],
[8417319.36474403, 6227615.514214008],
[8432400.044838525, 6266902.029016321],
[8451504.670408972, 6304396.967864621],
[8474423.92717972, 6339689.5283625685],
[8500906.706977429, 6372393.037831903],
[8530662.858924756, 6402149.18977923],
[8563366.368394092, 6428631.969576939],
[8598658.928892039, 6451551.226347689],
[8636153.867740339, 6470655.851918136],
[8675440.382542651, 6485736.53201263],
[8716088.04202157, 6496628.039544221],
[8757651.501913773, 6503211.044877199],
[8799675.38425497, 6505413.423227553],
[8841699.266596166, 6503211.044877199],
[8883262.726488369, 6496628.039544221],
[8923910.385967286, 6485736.53201263],
[8963196.900769599, 6470655.851918136],
[9000691.8396179, 6451551.226347689],
[9035984.400115848, 6428631.96957694],
[9068687.909585183, 6402149.18977923],
[9098444.061532509, 6372393.037831904],
[9124926.841330219, 6339689.528362569],
[9147846.098100968, 6304396.967864622],
[9166950.723671414, 6266902.029016321],
[9182031.40376591, 6227615.514214008],
[9192922.9112975, 6186967.85473509],
[9199505.916630479, 6145404.394842887],
[9201708.294980831, 6103380.512501691]
]
]
});
var polygonGraphic = new Graphic({
geometry: polygon,
symbol: new SimpleFillSymbol({
color: new Color([192, 192, 192, 0.5]),
outline: new SimpleLineSymbol({
color: new Color([255, 0, 0, 1]),
style: "solid",
width: "2px"
}),
style: "solid"
})
});
view.graphics.add(polygonGraphic);
var polylineJson = polygon.toJSON();
polylineJson.paths = polylineJson.rings;
delete polylineJson.rings;
var polyline = Polyline.fromJSON(polylineJson);
var polylineGraphic = new Graphic({
geometry: polyline,
attributes: { OBJECTID: 1, LABELFIELD: "My Label Text" }
});
var layer = new FeatureLayer({
source: [polylineGraphic],
objectIdField: "OBJECTID",
fields: [
{
name: "OBJECTID",
type: "oid"
},
{
name: "LABELFIELD",
type: "string"
}
],
renderer: new SimpleRenderer({
symbol: new SimpleLineSymbol({
color: new Color([0, 0, 0, 0]),
style: "solid",
width: "2px"
})
}),
labelingInfo: [
{
labelExpressionInfo: { expression: "$feature.LABELFIELD" },
labelPlacement: "center-along",
repeatLabel: false,
symbol: new TextSymbol({
color: new Color([255, 255, 255, 1]),
font: new Font({
family: "Arial",
size: "12pt",
style: "normal",
weight: "bold"
}),
haloColor: new Color([0, 0, 0, 1]),
haloSize: 2
})
}
]
});
view.map.add(layer);
});
});
</script>
</head>
<body>
<div id="viewDiv"></div>
</body>
</html>
... View more
02-20-2024
10:22 AM
|
1
|
1
|
1740
|
|
POST
|
Perhaps simplifying the syntax of your definition expressions might help. For example: Amicaexp = oidField3 + " NOT IN(" + Amicaoids.join(",") + ")";
... View more
02-20-2024
09:21 AM
|
0
|
2
|
1214
|
|
POST
|
The problem here is that you're trying to highlight graphics in a FeatureLayer by assigning a new symbol to them. That would've worked in 3.x, but not in 4.x. To highlight FeatureLayer graphics in 4.x, you need to get a reference to the layer's associated FeatureLayerView, and then use its highlight method.
... View more
02-16-2024
09:19 AM
|
2
|
0
|
2166
|
|
POST
|
This is a bug in the current version; please see this thread for further information.
... View more
02-14-2024
09:36 AM
|
0
|
0
|
958
|
|
POST
|
Yes, you can use a RequestInterceptor to examine the results from the server before they get delivered to the layer. In that process, you can remove any features with an invalid geometry from the array. Some other threads where this intercept-and-modify process is discussed can be found here and here.
... View more
02-07-2024
02:36 PM
|
0
|
0
|
906
|
|
POST
|
The value of that heading should be the same as the title property for the layer containing the graphics. If your layer doesn't have a title, that could explain it. Or, if you're not using a layer and adding your graphics directly to the MapView's graphics collection instead, that might explain it as well.
... View more
02-05-2024
02:40 PM
|
1
|
2
|
2020
|
|
POST
|
Have you tried using the registerToken method of the IdentityManager module? I've used it to do this same kind of thing. For example: esriId.registerToken({
expires: tokenResponse.expires,
server: "https://myServer/arcgis/rest/services",
token: tokenResponse.token
});
... View more
02-05-2024
09:45 AM
|
0
|
1
|
1770
|
|
POST
|
With the following changes I got it to highlight on mouseover: 1) Line 215 should be: const senateLayer = map.layers.items[3]; 2) Line 242 should be: highlight = layerView.highlight([newObjectId]); 3) Technically not necessary, but I also changed line 227 to: return result.graphic.layer == layerView.layer;
... View more
02-02-2024
01:49 PM
|
0
|
0
|
2932
|
|
POST
|
You would include the id in the parameters passed to the layer's constructor, for example: var layer = new FeatureLayer({
id: "myLayerID",
url: "https://myServer/arcgis/rest/services/MyService/MapServer/0"
});
... View more
02-01-2024
03:12 PM
|
0
|
0
|
1031
|
|
POST
|
My apologies, I sent you in the wrong direction due to a misunderstanding of the refresh event. I thought it would fire every time the layer refreshed itself, but that's not the case. Instead, the trigger we're looking for is the associated FeatureLayerView's updating property; when it changes from true to false, the layer has just finished refreshing. It is then that we should query the FeatureLayerView to see if it has any features, in which case I've also found it's best to use the view's extent (simply getting the count of the FeatureLayerView's available features can include features that aren't visible on screen). I've modified the code for the sample mentioned in the other thread, and is reproduced in its fullness below: <html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no" />
<title>Legend widget | Sample | ArcGIS Maps SDK for JavaScript 4.28</title>
<link rel="stylesheet" href="https://js.arcgis.com/4.28/esri/themes/light/main.css" />
<style>
html,
body,
#viewDiv {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
overflow: hidden;
}
</style>
<script src="https://js.arcgis.com/4.28/"></script>
<script>
function addLegendForLayer(Legend, view, container, layer) {
var childElement = document.createElement("DIV");
container.appendChild(childElement);
const legend = new Legend({
view: view,
layerInfos: [
{
layer: layer,
title: "NY Educational Attainment"
}
],
container: childElement
});
return {legend:legend,layer:layer};
}
require(["esri/views/MapView", "esri/widgets/Legend", "esri/WebMap"], (
MapView,
Legend,
WebMap
) => {
const webmap = new WebMap({
portalItem: {
// autocasts as new PortalItem()
id: "05e015c5f0314db9a487a9b46cb37eca"
}
});
const view = new MapView({
container: "viewDiv",
map: webmap
});
view.when(() => {
var container = document.createElement("DIV");
container.style.backgroundColor = "#FFFFFF";
// Add widget to the bottom right corner of the view
view.ui.add(container, "bottom-right");
addLegendForLayer(Legend, view, container, webmap.layers.getItemAt(0));
var item = addLegendForLayer(Legend, view, container, webmap.layers.getItemAt(0));
var legend2 = item.legend;
var layer2 = item.layer;
view.whenLayerView(layer2).then(function(layerView2) {
layerView2.watch("updating", function(newValue, oldValue, propertyName, target) {
if ((oldValue) && (!newValue)) {
var query = layerView2.createQuery();
query.geometry = view.extent;
layerView2.queryFeatures(query).then(function(featureSet) {
legend2.visible = (featureSet.features.length > 0);
});
}
});
});
});
});
</script>
</head>
<body class="calcite">
<div id="viewDiv"></div>
</body>
</html> To view this in action, go to the sample's sandbox, replace the code in the pane on the left with what's above (you can copy and paste), and click the "Refresh" button near the top-right of the page. After it's loaded, zoom to an area where there are no features, and the 2nd legend item disappears. Zoom back out to where there are features, and the legend reappears.
... View more
01-31-2024
09:53 AM
|
0
|
0
|
2114
|
|
POST
|
Although this is not possible with the out of the box Legend widget, it is still possible for you to accomplish this behavior in your application, assuming the layers you're working with are client-side layers (e.g. FeatureLayers). This post shows how to use multiple Legend widgets, although having the appearance of being a single legend to the end user. If you're using FeatureLayers, you could add one Legend per layer. For each layer, you would get a reference to its associated FeatureLayerView, and then whenever it refreshes it, you would query the FeatureLayerView for its features. This query runs client-side, so is instantaneous. If it returns no features, you would hide the associated Legend instance, or if there are features returned, you would show it instead.
... View more
01-30-2024
09:39 AM
|
2
|
2
|
2141
|
|
POST
|
I adjusted the "Intro to popups" sample to the following, which works although there's some explanation further below: <html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
<title>Intro to popups | Sample | ArcGIS Maps SDK for JavaScript 4.28</title>
<style>
html,
body,
#viewDiv {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
}
#instruction {
z-index: 99;
position: absolute;
top: 15px;
left: 50%;
padding: 5px;
margin-left: -175px;
height: 30px;
width: 355px;
background: rgba(25, 25, 25, 0.8);
color: white;
}
</style>
<link rel="stylesheet" href="https://js.arcgis.com/4.28/esri/themes/light/main.css" />
<script src="https://js.arcgis.com/4.28/"></script>
<script>
function adjustViewForPopup(view, popup) {
if (popup.visible) {
var contentFeatureElement = popup.container.querySelector(".esri-features__content-feature");
var headerElement = contentFeatureElement?.shadowRoot.querySelector("calcite-panel")?.shadowRoot?.querySelector("div.header-container");
if (headerElement) {
var popupRect = popup.container.getBoundingClientRect();
var viewRect = view.container.getBoundingClientRect();
var xDiff = 0, yDiff = 0;
if (popupRect.right > viewRect.right)
xDiff = (viewRect.right - popupRect.right) - 5;
else if (popupRect.left < viewRect.left)
xDiff = (viewRect.left - popupRect.left) + 5;
if (popupRect.bottom > viewRect.bottom)
yDiff = (viewRect.bottom - popupRect.bottom) - 5;
else if (popupRect.top < viewRect.top)
yDiff = (viewRect.top - popupRect.top) + 5;
if ((xDiff !== 0) || (yDiff !== 0)) {
var screenPoint = view.toScreen(view.extent.center);
view.goTo(view.toMap({x:screenPoint.x - xDiff, y:screenPoint.y - yDiff}));
}
} else
window.setTimeout(adjustViewForPopup.bind(window, view, popup), 100);
}
}
require(["esri/rest/locator", "esri/Map", "esri/views/MapView", "esri/widgets/Popup"], (locator, Map, MapView, Popup) => {
// Set up a locator url using the world geocoding service
const locatorUrl = "https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer";
// Create the Map
const map = new Map({
basemap: "streets-navigation-vector"
});
var popup = new Popup();
popup.alignment = "top-center";
// Create the MapView
const view = new MapView({
container: "viewDiv",
map: map,
center: [-71.6899, 43.7598],
zoom: 12,
popup: popup
});
new MutationObserver(adjustViewForPopup.bind(window, view, popup)).observe(popup.container, {childList:true,subtree:true});
popup.watch(["content","selectedFeatureWidget"], adjustViewForPopup.bind(window, view, popup));
/*******************************************************************
* This click event sets generic content on the popup not tied to
* a layer, graphic, or popupTemplate. The location of the point is
* used as input to a reverse geocode method and the resulting
* address is printed to the popup content.
*******************************************************************/
view.popupEnabled = false;
view.on("click", (event) => {
// Get the coordinates of the click on the view
const lat = Math.round(event.mapPoint.latitude * 1000) / 1000;
const lon = Math.round(event.mapPoint.longitude * 1000) / 1000;
view.openPopup({
// Set the popup's title to the coordinates of the location
title: "Reverse geocode: [" + lon + ", " + lat + "]",
location: event.mapPoint // Set the location of the popup to the clicked location
});
const params = {
location: event.mapPoint
};
// Display the popup
// Execute a reverse geocode using the clicked location
locator
.locationToAddress(locatorUrl, params)
.then((response) => {
// If an address is successfully found, show it in the popup's content
view.popup.content = response.address;
})
.catch(() => {
// If the promise fails and no result is found, show a generic message
view.popup.content = "No address was found for this location";
});
});
});
</script>
</head>
<body>
<div id="viewDiv"></div>
<div id="instruction" class="esri-widget">Click any location on the map to see its street address</div>
</body>
</html> The main part is the function "adjustViewForPopup" added on lines 35-63. I also had to explicitly require the Popup widget on line 65, instantiate one on line 74, and assign it to the view on line 83 in order to get around an issue where it didn't fully pan on the first click. I chalk those first-click issues up to the modules being lazy loaded, coupled with the use of a shadow DOM starting in 4.28. The MutationObserver of line 86 doesn't appear to be notified when elements within a shadow root are altered, thus the additional use of "watch" two lines below it for good measure. I used a fixed alignment on line 75, because without it, the alignment will change anchors when the map is panned, thus making it seem like the panning went too far. It seems the popup's contents are fully present when the headerElement of line 40 is present, which is when the domRect has its expected values.
... View more
01-29-2024
10:45 AM
|
0
|
0
|
2168
|
|
POST
|
If your goal is simply to keep the popup from getting cut off, you might consider the solution here. It doesn't pan the map, but instead forces to the popup to open towards the direction of the map where there's the most space.
... View more
01-26-2024
09:34 AM
|
0
|
2
|
2223
|
| Title | Kudos | Posted |
|---|---|---|
| 1 | 03-19-2024 10:37 AM | |
| 1 | 03-31-2026 02:34 PM | |
| 1 | 12-09-2025 09:35 AM | |
| 2 | 12-09-2025 09:06 AM | |
| 1 | 11-26-2025 12:29 PM |