mapbox / mapbox-gl-draw

Draw tools for mapbox-gl-js
https://www.mapbox.com/mapbox-gl-js/example/mapbox-gl-draw/
ISC License
947 stars 592 forks source link

How to change the initial custom styling of a feature after we draw it? #1298

Open alamenai opened 2 weeks ago

alamenai commented 2 weeks ago

Hi everyone,

In the image below I draw roofs on the the top of the building:

image

This how I draw the roof :


  const createRoofPolygonFeature = (roofSurface: RoofSurface): Feature => {
    const polygon = turf.polygon([roofSurface.coords])
    const roofCoordinates = polygon.geometry.coordinates

    if (roofSurface.holes) {
      roofSurface.holes.forEach((hole: Position[]) => {
        roofCoordinates.push(hole)
      })
    }

    const feature = {
      type: "Feature",
      properties: {
        id: roofSurface.id,
        user_color: "#16a34a", // Default color with 'user_' prefix
      },
      geometry: {
        type: "Polygon",
        coordinates: roofCoordinates,
      },
    }

    // Store the draw instance in the mapRef
    if (mapRef.current) {
      const current = mapRef.current as MapWithDraw
      current.draw.add(feature as Feature)

      // // Add a new layer for the holes
      // if (roofSurface.holes && roofSurface.holes.length > 0) {
      //   roofSurface.holes.forEach((hole, index) => {
      //     const holeFeature = {
      //       type: "Feature",
      //       properties: {},
      //       geometry: {
      //         type: "Polygon",
      //         coordinates: [hole],
      //       },
      //     }

      //     const holeId = `hole-${roofSurface.id}-${index}`

      //     current.addSource(holeId, {
      //       type: "geojson",
      //       data: holeFeature,
      //     })

      //     current.addLayer({
      //       id: holeId,
      //       type: "fill",
      //       source: holeId,
      //       paint: {
      //         "fill-color": "red",
      //         "fill-opacity": 0.3,
      //       },
      //     })
      //   })
      // }
    }

    return feature as Feature
  }

I created a map instance :

  const createDrawInstance = (modes: { [key: string]: DrawCustomMode }, styles: any): MapboxDraw => {
    return new MapboxDraw({
      displayControlsDefault: false,
      touchEnabled: true,
      modes,
      styles,
      boxSelect: true,
      userProperties: true,
    })
  }

I defined the styles using this function:


  const createDrawStyles = () => {
    return [
      // ACTIVE (being drawn)
      // line stroke
      {
        id: "gl-draw-line",
        type: "line",
        filter: ["all", ["==", "$type", "LineString"], ["!=", "mode", "static"]],
        layout: {
          "line-cap": "round",
          "line-join": "round",
        },
        paint: {
          "line-color": ["get", "color"],
          "line-width": 2,
        },
      },
      // polygon fill
      {
        id: "gl-draw-polygon-fill",
        type: "fill",
        filter: ["all", ["==", "$type", "Polygon"], ["!=", "mode", "static"]],
        paint: {
          "fill-color": "#84cc16",
          "fill-outline-color": "#84cc16",
          "fill-opacity": 0.1, // Semi-opaque fill
        },
      },

      // vertex point halos
      {
        id: "gl-draw-polygon-and-line-vertex-halo-active",
        type: "circle",
        filter: ["all", ["==", "meta", "vertex"], ["==", "$type", "Point"], ["!=", "mode", "static"]],
        paint: {
          "circle-radius": 10,
          "circle-color": "#FFF", // White color for vertex halo
        },
      },
      // vertex points
      {
        id: "gl-draw-polygon-and-line-vertex-active",
        type: "circle",
        filter: ["all", ["==", "meta", "vertex"], ["==", "$type", "Point"], ["!=", "mode", "static"]],
        paint: {
          "circle-radius": 8,
          "circle-color": "#84cc16", // Red color for vertex points
        },
      },
      // Inactive polygon fill
      {
        id: "gl-draw-polygon-fill-static",
        type: "fill",
        filter: ["all", ["==", "$type", "Polygon"], ["==", "mode", "static"]],
        paint: {
          "fill-color": "#3b82f6", // Blue color for inactive polygon fill
          "fill-outline-color": "#3b82f6", // Blue outline
          "fill-opacity": 0.1, // Semi-opaque fill
        },
      },
      // Inactive polygon outline stroke
      {
        id: "gl-draw-polygon-stroke-static",
        type: "line",
        filter: ["all", ["==", "$type", "Polygon"], ["==", "mode", "static"]],
        layout: {
          "line-cap": "round",
          "line-join": "round",
        },
        paint: {
          "line-color": "#16a34a", // inactive polygon outline
          "line-width": 3, // Line width
        },
      },
      // Inactive vertex points
      {
        id: "gl-draw-polygon-and-line-vertex-static",
        type: "circle",
        filter: ["all", ["==", "meta", "vertex"], ["==", "$type", "Point"], ["==", "mode", "static"]],
        paint: {
          "circle-radius": 3,
          "circle-color": "#3BB2D0", // Blue color for inactive vertex points
        },
      },
      // polygon outline stroke
      // This doesn't style the first edge of the polygon, which uses the line stroke styling instead
      {
        id: "gl-draw-polygon-stroke-active",
        type: "line",
        filter: ["all", ["==", "$type", "Polygon"], ["!=", "mode", "static"]],
        paint: {
          "line-color": ["get", "color"],
          "line-width": 2,
        },
      },
    ]
  }

Everything works as expected but when I click on the roof ID ( circle ID), I want to change the color of that selected roof (feature) to red but Mapbox gl does not influence the change:

c0c91e36-12f5-43ea-8ed6-f3ce538f93c3.webm

This is the method I use:

 // Update roof line color
    if (mapRef.current) {
      const current = mapRef.current as MapWithDraw
      const feature = current.draw.get(roofSurface.id as string) as Feature
      if (feature) {
        feature.properties = {
          ...feature.properties,
          user_color: selectStatus ? "#16a34a" : "#ef4444", // Green when selected, red when deselected
        }
        current.draw.add(feature)
      }
    }

I did not understand how to use the property userProperties Is it possible to change the color of each feature?

I appreciate any example that you can provide.

Example: drawing to polygons in the first rendering and when I click on one of them, it will change its color.

Thank you

Quietly-20201113 commented 1 week ago

I also want to achieve the color change problem, but I change it when drawing a new line

Quietly-20201113 commented 1 week ago

Hi everyone,

In the image below I draw roofs on the the top of the building:

image

This how I draw the roof :

  const createRoofPolygonFeature = (roofSurface: RoofSurface): Feature => {
    const polygon = turf.polygon([roofSurface.coords])
    const roofCoordinates = polygon.geometry.coordinates

    if (roofSurface.holes) {
      roofSurface.holes.forEach((hole: Position[]) => {
        roofCoordinates.push(hole)
      })
    }

    const feature = {
      type: "Feature",
      properties: {
        id: roofSurface.id,
        user_color: "#16a34a", // Default color with 'user_' prefix
      },
      geometry: {
        type: "Polygon",
        coordinates: roofCoordinates,
      },
    }

    // Store the draw instance in the mapRef
    if (mapRef.current) {
      const current = mapRef.current as MapWithDraw
      current.draw.add(feature as Feature)

      // // Add a new layer for the holes
      // if (roofSurface.holes && roofSurface.holes.length > 0) {
      //   roofSurface.holes.forEach((hole, index) => {
      //     const holeFeature = {
      //       type: "Feature",
      //       properties: {},
      //       geometry: {
      //         type: "Polygon",
      //         coordinates: [hole],
      //       },
      //     }

      //     const holeId = `hole-${roofSurface.id}-${index}`

      //     current.addSource(holeId, {
      //       type: "geojson",
      //       data: holeFeature,
      //     })

      //     current.addLayer({
      //       id: holeId,
      //       type: "fill",
      //       source: holeId,
      //       paint: {
      //         "fill-color": "red",
      //         "fill-opacity": 0.3,
      //       },
      //     })
      //   })
      // }
    }

    return feature as Feature
  }

I created a map instance :

  const createDrawInstance = (modes: { [key: string]: DrawCustomMode }, styles: any): MapboxDraw => {
    return new MapboxDraw({
      displayControlsDefault: false,
      touchEnabled: true,
      modes,
      styles,
      boxSelect: true,
      userProperties: true,
    })
  }

I defined the styles using this function:

  const createDrawStyles = () => {
    return [
      // ACTIVE (being drawn)
      // line stroke
      {
        id: "gl-draw-line",
        type: "line",
        filter: ["all", ["==", "$type", "LineString"], ["!=", "mode", "static"]],
        layout: {
          "line-cap": "round",
          "line-join": "round",
        },
        paint: {
          "line-color": ["get", "color"],
          "line-width": 2,
        },
      },
      // polygon fill
      {
        id: "gl-draw-polygon-fill",
        type: "fill",
        filter: ["all", ["==", "$type", "Polygon"], ["!=", "mode", "static"]],
        paint: {
          "fill-color": "#84cc16",
          "fill-outline-color": "#84cc16",
          "fill-opacity": 0.1, // Semi-opaque fill
        },
      },

      // vertex point halos
      {
        id: "gl-draw-polygon-and-line-vertex-halo-active",
        type: "circle",
        filter: ["all", ["==", "meta", "vertex"], ["==", "$type", "Point"], ["!=", "mode", "static"]],
        paint: {
          "circle-radius": 10,
          "circle-color": "#FFF", // White color for vertex halo
        },
      },
      // vertex points
      {
        id: "gl-draw-polygon-and-line-vertex-active",
        type: "circle",
        filter: ["all", ["==", "meta", "vertex"], ["==", "$type", "Point"], ["!=", "mode", "static"]],
        paint: {
          "circle-radius": 8,
          "circle-color": "#84cc16", // Red color for vertex points
        },
      },
      // Inactive polygon fill
      {
        id: "gl-draw-polygon-fill-static",
        type: "fill",
        filter: ["all", ["==", "$type", "Polygon"], ["==", "mode", "static"]],
        paint: {
          "fill-color": "#3b82f6", // Blue color for inactive polygon fill
          "fill-outline-color": "#3b82f6", // Blue outline
          "fill-opacity": 0.1, // Semi-opaque fill
        },
      },
      // Inactive polygon outline stroke
      {
        id: "gl-draw-polygon-stroke-static",
        type: "line",
        filter: ["all", ["==", "$type", "Polygon"], ["==", "mode", "static"]],
        layout: {
          "line-cap": "round",
          "line-join": "round",
        },
        paint: {
          "line-color": "#16a34a", // inactive polygon outline
          "line-width": 3, // Line width
        },
      },
      // Inactive vertex points
      {
        id: "gl-draw-polygon-and-line-vertex-static",
        type: "circle",
        filter: ["all", ["==", "meta", "vertex"], ["==", "$type", "Point"], ["==", "mode", "static"]],
        paint: {
          "circle-radius": 3,
          "circle-color": "#3BB2D0", // Blue color for inactive vertex points
        },
      },
      // polygon outline stroke
      // This doesn't style the first edge of the polygon, which uses the line stroke styling instead
      {
        id: "gl-draw-polygon-stroke-active",
        type: "line",
        filter: ["all", ["==", "$type", "Polygon"], ["!=", "mode", "static"]],
        paint: {
          "line-color": ["get", "color"],
          "line-width": 2,
        },
      },
    ]
  }

Everything works as expected but when I click on the roof ID ( circle ID), I want to change the color of that selected roof (feature) to red but Mapbox gl does not influence the change:

c0c91e36-12f5-43ea-8ed6-f3ce538f93c3.webm This is the method I use:

 // Update roof line color
    if (mapRef.current) {
      const current = mapRef.current as MapWithDraw
      const feature = current.draw.get(roofSurface.id as string) as Feature
      if (feature) {
        feature.properties = {
          ...feature.properties,
          user_color: selectStatus ? "#16a34a" : "#ef4444", // Green when selected, red when deselected
        }
        current.draw.add(feature)
      }
    }

I did not understand how to use the property userProperties Is it possible to change the color of each feature?

I appreciate any example that you can provide.

Example: drawing to polygons in the first rendering and when I click on one of them, it will change its color.

Thank you

I also have a requirement, which is to add serial numbers to the drawing points, such as 123 in your video. I would like to ask whether you use external labels to implement it or implement it in draw. If you implement it in the draw plug-in, how to implement it?

Quietly-20201113 commented 1 week ago

I found the following solution

sameerabit answer Dynamic color change You can refer to