Select to view content in your preferred language

FeatureLayer use symbol for render

370
3
Jump to solution
08-07-2024 07:32 AM
Slyke
by
Emerging Contributor

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.

0 Kudos
1 Solution

Accepted Solutions
JoelBennett
MVP Regular Contributor

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.

View solution in original post

3 Replies
JoelBennett
MVP Regular Contributor

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.

Slyke
by
Emerging Contributor

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.

0 Kudos
Slyke
by
Emerging Contributor

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>