Hello,
I'm running the map in SvelteKit.
I have an array of items like this:
const myArray = [
{
x: someThing.coordinates.longitude,
y: someThing.coordinates.latitude,
icon: { url: `URL_OF_ICON.svg`, w: '24px', h: '32px' },
highlightedIcon: {
url: `URL_OF_HIGHLIGHTED_ICON.svg`,
w: '32px',
h: '48px',
backgroundColor: 'rgba(0, 255, 0, 0.3)',
borderColor: 'rgba(0, 255, 0, 0.9)',
circleR: '64px',
circleBorderW: 2
},
someDetails: someThing
},
...
];
That I'm rendering to the map. I'm looping through this array and creating graphics onto the map:
Object.keys(points).forEach((point) => {
try {
const attributes = {
someId: points[point].someDetails?.someId,
someDetails: points[point].someDetails,
isHighlighted: points[point].isHighlighted
};
if (points[point].isHighlighted) {
const circleSymbol = new SimpleMarkerSymbol({
style: "circle",
color: points[point].highlightedIcon?.backgroundColor,
size: points[point].highlightedIcon?.circleR,
...(points[point].highlightedIcon?.borderColor ?
{
outline: {
color: points[point].highlightedIcon?.borderColor,
width: points[point].highlightedIcon?.circleBorderW || 2
}
} : {} )
});
const pictureSymbol = new PictureMarkerSymbol({
url: points[point].highlightedIcon?.url,
width: points[point].highlightedIcon?.w,
height: points[point].highlightedIcon?.h
});
const pointGeometry = new Point({
longitude: points[point].x,
latitude: points[point].y
});
const circleGraphic = new Graphic({
geometry: pointGeometry,
symbol: circleSymbol,
attributes
});
const pictureGraphic = new Graphic({
geometry: pointGeometry,
symbol: pictureSymbol,
attributes
});
graphicsToAdd.push(circleGraphic);
graphicsToAdd.push(pictureGraphic);
} else {
const graphic = new Graphic({
geometry: new Point({
longitude: points[point].x,
latitude: points[point].y
}),
symbol: new PictureMarkerSymbol({
url: points[point].icon.url,
width: points[point].icon.w,
height: points[point].icon.h
}),
attributes
});
graphicsToAdd.push(graphic);
}
} catch (err) {
console.log(`Error: `, err);
}
});
graphicsToAdd is essentially being added to a GraphicsLayer that is a layer on the map.
I have another section of code that essentially detects if the mouse is hovering over one of the graphics, and changes the array so that isHighlighted is true for that item, and it'll render the highlighted icon, with the background graphic, and also show a popup.
The problem comes when I want to cluster these points so that when the user zooms out they clump together. I'm not sure what to do for the FeatureLayer:
const iconLayer = new FeatureLayer({
source: [],
fields: [
{
name: 'objectId',
alias: 'objectId',
type: 'oid'
},
{
name: 'someId',
alias: 'someId',
type: 'string'
}
],
objectIdField: 'objectId',
geometryType: 'point',
renderer: {
type: "simple",
symbol: {
type: "picture-marker",
// What goes here?
}
}
});
It seems there's no way to get the FeatureLayer to render the graphics being attached to it via applyEdits(). I can make them show up as dots on the map, and can get them to cluster. But I can't get them to use their own icons. I haven't tried getting the mouse hover to work yet. I know that I can set a URL under renderer.symbol.type, but I want each graphic to render its own symbol.
Off topic:
Also, is there a way to switch to markdown mode when posting here? I can't edit my code snippets once they are inserted.
Solved! Go to Solution.
If I'm understanding this correctly, I think the problem is that FeatureLayer ignores the symbol property of its graphics. If you want each graphic to be rendered uniquely, I think your best bet is to create and populate a UniqueValueRenderer object, and set it to the layer's renderer property. You could add the url to the graphic's attributes, and use that as the field for your renderer.
Also, if you've created a code block in one of your posts, you can double-click it to re-open and edit.
If I'm understanding this correctly, I think the problem is that FeatureLayer ignores the symbol property of its graphics. If you want each graphic to be rendered uniquely, I think your best bet is to create and populate a UniqueValueRenderer object, and set it to the layer's renderer property. You could add the url to the graphic's attributes, and use that as the field for your renderer.
Also, if you've created a code block in one of your posts, you can double-click it to re-open and edit.
Hey Joel,
Thanks for the reply.
I'm unsure what you mean by `populate a UniqueValueRenderer object, and set it to the layer's renderer property`.
Are there any examples of using a Layer that has a custom graphic for each icon, and updating the icon dynamically (either by mouseover or otherwise)? I essentially want to be able to cluster a set of Graphics.
I dig into it a little more and had ChatGPT help out. I modified this code it gave as an example. Seems to work wonders. Thanks for pointing me in the right direction!
<script>
import { onMount } from 'svelte';
import { loadModules } from 'esri-loader';
let map;
onMount(async () => {
const [Map, MapView, FeatureLayer, UniqueValueRenderer, PictureMarkerSymbol] = await loadModules([
'esri/Map',
'esri/views/MapView',
'esri/layers/FeatureLayer',
'esri/renderers/UniqueValueRenderer',
'esri/symbols/PictureMarkerSymbol'
]);
// Create the map
map = new Map({
basemap: 'topo-vector'
});
// Create the view
const view = new MapView({
container: 'viewDiv',
map: map,
center: [-100.33, 25.69], // Longitude, latitude
zoom: 10
});
// Define the UniqueValueRenderer
const renderer = new UniqueValueRenderer({
field: 'type',
uniqueValueInfos: [
{
value: 'type1',
symbol: new PictureMarkerSymbol({
url: 'path/to/image1.png',
width: '24px',
height: '24px'
})
},
{
value: 'type2',
symbol: new PictureMarkerSymbol({
url: 'path/to/image2.png',
width: '24px',
height: '24px'
})
}
// Add more unique values as needed
]
});
// Create a FeatureLayer with inline features
const featureLayer = new FeatureLayer({
source: [
{
geometry: {
type: 'point',
longitude: -100.33,
latitude: 25.69
},
attributes: {
type: 'type1'
}
},
{
geometry: {
type: 'point',
longitude: -100.35,
latitude: 25.70
},
attributes: {
type: 'type2'
}
}
// Add more features as needed
],
objectIdField: 'ObjectID', // This must be defined for FeatureLayer
fields: [
{
name: 'ObjectID',
alias: 'ObjectID',
type: 'oid'
},
{
name: 'type',
alias: 'Type',
type: 'string'
}
],
renderer: renderer
});
// Add the layer to the map
map.add(featureLayer);
});
</script>
<style>
#viewDiv {
height: 100%;
width: 100%;
}
</style>
<div id="viewDiv"></div>