Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add example of coloring single 3D extrusion building onClick #1190

Closed
langsmith opened this issue Aug 29, 2019 · 7 comments
Closed

Add example of coloring single 3D extrusion building onClick #1190

langsmith opened this issue Aug 29, 2019 · 7 comments

Comments

@langsmith
Copy link
Contributor

If possible, could be cool to reproduce https://demos.mapbox.com/buildings-picker-3d/#16/40.712189/-74.00943/65.6/29 in this app

Screen Shot 2019-08-29 at 10 49 07 AM

Screen Shot 2019-08-29 at 10 49 27 AM

@langsmith
Copy link
Contributor Author

1137429494682002 QjWaYVrLHHsdlewoTYdo_height640

@langsmith
Copy link
Contributor Author

From @avi-c offline:

"It does indeed need buildings-plus being in the style. And it uses the Tilequery API against that dataset to figure out the feature ids that need the different styling."

@langsmith
Copy link
Contributor Author

JS code for https://demos.mapbox.com/buildings-picker-3d/#16/40.712189/-74.00943/65.6/29 is

const map = new mapboxgl.Map({
  style: "mapbox://styles/labs-sandbox/ck2aiw9cl37jr1co3qfrdazd4",
  center: [-74.0066, 40.7135],
  zoom: 16,
  pitch: 45,
  bearing: -17.6,
  container: "map",
  hash: true
});

// The 'building' layer in the mapbox-streets vector source contains building-height
// data from OpenStreetMap.
map.on("load", () => {
  // Insert the layer beneath any symbol layer.
  const layers = map.getStyle().layers;
  let labelLayerId;
  for (let i = 0; i < layers.length; i++) {
    if (layers[i].type === "symbol" && layers[i].layout["text-field"]) {
      labelLayerId = layers[i].id;
      break;
    }
  }

  map.addLayer(
    {
      id: "3d-buildings",
      source: "composite",
      "source-layer": "buildings_plus",
      filter: ["==", "extrude", "true"],
      type: "fill-extrusion",
      minzoom: 14,
      paint: {
        // color based on feature state
        "fill-extrusion-color": [
          "match",
          ["feature-state", "highlight"],
          "true",
          "#F0F",
          "#09F"
        ],
        // use an 'interpolate' expression to add a smooth transition effect to the
        // buildings as the user zooms in
        "fill-extrusion-height": [
          "interpolate",
          ["linear"],
          ["zoom"],
          15,
          0,
          15.05,
          ["get", "height"]
        ],
        "fill-extrusion-base": [
          "interpolate",
          ["linear"],
          ["zoom"],
          15,
          0,
          15.05,
          ["get", "min_height"]
        ],
        "fill-extrusion-opacity": 0.6
      }
    },
    labelLayerId
  );


  function findParent(features) {
    const clicked = features[0];
    if (clicked.properties.type === "building:part") {
      const all_features = map.queryRenderedFeatures({
        layers: ["3d-buildings"]
        // ,filter: ["==", "id", clicked.properties.parent]
      });
      let parent;
      all_features.every(feature => {
        if (feature.id === clicked.properties.parent) {
          parent = feature;
          return false;
        } else {
          return true;
        }
      });
      return parent ? parent : clicked;
    } else {
      return clicked;
    }
  }

  const selectFeatures = (() => {
    let previous;
    return ids => {
      if (ids !== previous && previous !== undefined) {
        previous.forEach(p_id => {
          map.setFeatureState(
            {
              source: "composite",
              sourceLayer: "buildings_plus",
              id: p_id
            },
            { highlight: "false" }
          );
        });
      }
      previous = ids;

      ids.forEach(id => {
        map.setFeatureState(
          { source: "composite", sourceLayer: "buildings_plus", id },
          { highlight: "true" }
        );
      });
    };
  })();

  map.on("click", e => {
    const features = map.queryRenderedFeatures(e.point, {
      layers: ["3d-buildings"]
    });

    if (features) {
      // find the parent
      const parent = findParent(features);

      let ids = [parent.id];
      if (parent.properties.parts) {
        ids = ids.concat(JSON.parse(parent.properties.parts));
      }

      selectFeatures(ids);
    }
  });
});

@langsmith
Copy link
Contributor Author

Kinda' close to figuring this out on Maps SDK Android. My work is happening on https://github.com/mapbox/mapbox-android-demo/pull/new/ls-single-building-highlight-example.

@gsavvid
Copy link

gsavvid commented Jun 16, 2020

@langsmith thanks for the solution. I'm trying to apply this in my map, too, but I was wondering how did you create buildings_plus layer? It'd be great if you could share some details about this because I get the following error:

Source layer "buildings_plus" does not exist on source "composite" as specified by style layer "3d-buildings"

@langsmith
Copy link
Contributor Author

Hey @gsavvid, thanks for your interest in this.

buildings_plus is a Mapbox tileset that used to be a product offering that had to be paid for. However, it's no longer maintained by Mapbox.

This building highlighting is tricky because:

  • a single building can be split by a line where two map tiles come together. This means that only a part of the building would be highlighted.
  • buildings have ids and sometimes there can be duplicated IDs across tiles. This can affect the filters and other logic that are required for determining and highlighting the correct extrusion.

All in all, Mapbox mobile and data engineers are still figuring out what type of building info is going to be supported in various upcoming Mapbox data sources and what's sustainable moving forward in order to pull off this highlighting UI.

@langsmith
Copy link
Contributor Author

Resolved by #1370

91744826-e8a0be80-eb6e-11ea-976b-01d64c46b0f2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants