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

Make query methods return raster features/sources #1404

Open
samanpwbb opened this issue Jul 31, 2015 · 20 comments
Open

Make query methods return raster features/sources #1404

samanpwbb opened this issue Jul 31, 2015 · 20 comments

Comments

@samanpwbb
Copy link
Contributor

It would be useful if raster layers were included in featuresAt. Especially for raster layers with sources with limited bounds.

I see here that this is a noop:

callback(null, []);

@samanpwbb samanpwbb changed the title featuresAt for raster layers? featuresAt should include raster layers Jul 31, 2015
@scothis scothis self-assigned this Jul 31, 2015
@scothis
Copy link
Contributor

scothis commented Jul 31, 2015

Looks to already be working.

screen shot 2015-07-31 at 1 40 01 pm

@scothis scothis closed this as completed Jul 31, 2015
@samanpwbb samanpwbb reopened this Aug 3, 2015
@samanpwbb
Copy link
Contributor Author

@scothis in that example, the raster layers are hacked in to show up across the whole map. Ideally the raster layer only appears in the popup if it exists where user clicked.

@scothis scothis removed their assignment Apr 21, 2016
@lucaswoj lucaswoj changed the title featuresAt should include raster layers Make query methods respect raster layer bounds Jul 28, 2016
@lbud lbud changed the title Make query methods respect raster layer bounds Make query methods return raster features/sources Dec 8, 2016
@lbud
Copy link
Contributor

lbud commented Dec 8, 2016

At this time we don't return raster sources or features from map.query*Features at all. We should do this.

This will be dependent on #3186: querying searches for features in a tile, but since we shoehorn features into a tile, querying a point on a visible raster layer may search for points past a tile (the source cache's tile)'s "extent."

The equivalent of the noop mentioned in #1404 (comment) is now query_features.js#L12: raster layers don't have feature indices. querySourceFeatures also only queries geojson and vector data.

This was brought up in #3745. Indeed for something like a canvas-based raster layer it would be helpful to return an [x,y] coordinate mapped back to the original raster data.

@lbud lbud mentioned this issue Dec 8, 2016
5 tasks
@mollymerp
Copy link
Contributor

This could be particularly useful for raster layers like the terrain rgb where users could query the pixel values and calculate elevation at a point based on the raster data 💭

@jucor
Copy link

jucor commented Oct 11, 2017

Any luck on this? This would very useful.

@jfirebaugh
Copy link
Contributor

#5916 notes that raster layers with a canvas source should be included with this feature.

@shawnd
Copy link

shawnd commented Sep 28, 2019

This could be particularly useful for raster layers like the terrain rgb where users could query the pixel values and calculate elevation at a point based on the raster data 💭

Exactly the use case I have. Layer with rgb data that I want to be able to query with a click event point.

@felix-ht
Copy link

felix-ht commented Dec 3, 2019

Any updates on this?

@ansarikhurshid786
Copy link

any updates or solution ?

1 similar comment
@dongmingwang2198881
Copy link

any updates or solution ?

@gagecarto
Copy link

gagecarto commented May 22, 2020

Oh gosh.. This would be so helpful especially for querying via RGB values.. fingers crossed on this one

@nextstopsun
Copy link
Contributor

Any news here? I'd like to query RGB values of underlying raster pixel too.

@mprove
Copy link

mprove commented Nov 8, 2021

While this is not working, you can add a note to the documentation that it does not work for raster layers. This would reduce confusion.
https://docs.mapbox.com/mapbox-gl-js/api/map/#map#on

@nm2501
Copy link

nm2501 commented Feb 2, 2022

I'm interested in a solution. Adding my upvote here...
👍

@jbeuckm
Copy link

jbeuckm commented Mar 30, 2022

Yes, and it would be great to get a distribution of raster color values within a given polygon.

@jbeuckm
Copy link

jbeuckm commented Apr 7, 2022

Well, I worked a new method through to the tile.js layer here . The idea is that you could ask for the value from a raster source at a given location:

map.on("mousemove", (event) => {
    map.queryRasterSource(<my raster sourceId>, event.point);
});

But this still needs the function to pull a pixel value out of the raster source:

    // Find raster value at a given location
    queryRasterValues(
        layers: { [_: string]: StyleLayer },
        serializedLayers: { [string]: Object },
        sourceFeatureState: SourceFeatureState,
        tileResult: TilespaceQueryGeometry,
        params: {
            filter: FilterSpecification,
            layers: Array<string>,
            availableImages: Array<string>,
        },
        transform: Transform,
        pixelPosMatrix: Float32Array,
        visualizeQueryGeometry: boolean
    ): Color {
        // const texturePos = pixelPosMatrix.getTexturePos()
        // const color = new ArrayBuffer()
        // tileResult.tile.texture.readPixels(texturePos.x, texturePos.y, 1, 1, type, &color)
        // return color
    }

It might be that we need to use the Painter to render the source/layer for a viewport that is one pixel square? That seems like a lot of code for one pixel. Maybe there is a way to query the screen x,y for the already-rendered layer? Would a new painter function work?

painter.renderLayerPixel(layerId, mousePos) {
        this.context.viewport.set([mouseX, mouseY, 1, 1]);
        this.renderLayer(this, sourceCache, layer, coords);
        // how to get the pixel value???
}

I'm hoping someone who knows the internals well can jump in.

@jbeuckm
Copy link

jbeuckm commented Apr 8, 2022

Here is something that is starting to kind of work. I started with the Painter.render() function and tried to pare down to rendering just the requested layer. Then I read the requested pixel value. When I try this with multi-layer maps, it interferes with the main map render and the result is not stable. Someone who understands the gl buffers and how mapbox uses them should be able to fix that and maybe improve this to just render the pixel rather than the entire viewport.

colorForLayerPixel

    colorForLayerPoint(layerId: string, point: PointLike) {

        const layer = this.style._layers[layerId];
        const sourceCache = this.style._getLayerSourceCache(layer);

        sourceCache.prepare(this.context);

        const coordsAscending: Array<OverscaledTileID> = sourceCache.getVisibleCoordinates();
        const coordsDescending: Array<OverscaledTileID> = coordsAscending.slice().reverse();

        // Rebind the main framebuffer now that all offscreen layers have been rendered:
        // this.context.bindFramebuffer.set(null);
        this.context.viewport.set([0, 0, this.width, this.height]);

        this.context.clear({
            color: 'rgba(0,0,0,0)', //options.showOverdrawInspector ? Color.black : clearColor,
            depth: 1,
        });
        this.clearStencil();

        this._showOverdrawInspector = false; //options.showOverdrawInspector;

        this.renderPass = "translucent";


        // For symbol layers in the translucent pass, we add extra tiles to the renderable set
        // for cross-tile symbol fading. Symbol layers don't use tile clipping, so no need to render
        // separate clipping masks
        const coords = sourceCache
            ? coordsDescending
            : undefined;

        this._renderTileClippingMasks(
            layer,
            sourceCache,
            sourceCache ? coordsAscending : undefined
        );
        this.renderLayer(this, sourceCache, layer, coords);

        const gl = this.context.gl;
        var pixel = new Uint8Array(4);
        gl.readPixels(
            point.x * window.devicePixelRatio,
            this.height - point.y * window.devicePixelRatio,
            1,
            1,
            gl.RGBA,
            gl.UNSIGNED_BYTE,
            pixel
        );

        return pixel;
    }

@Carlos-Carreno-Berlanga

This would be very useful, for generating interactive tooltip information for raster sources. My use case is generating a tooltip for a weather map.

@mkaskov
Copy link

mkaskov commented Jun 1, 2023

any updates?

@tredpath
Copy link

You can force the map to repaint after querying the color and it will render correctly. I didn't see any flickering or stuttering but it's not great performance wise when dragging the cursor over the map. For my use it was good enough to only query the color when the cursor has stopped moving to improve that.

let timeout
map.on("mousemove", e -> {
    clearTimeout(timeout)
    timeout = setTimeout(() => {
        //query the color for a layer created elsewhere
        const clr = map.colorForLayerPoint("layer_id", e.point)
        //draw the map to fix anything getting the color broke
        map.triggerRepaint()
        //use clr to do something interesting
        useColor(clr)
    }, 150)
})

If you want to use the color as a lookup as I did you also need to be careful how you add the layer to the map. Raster layers need resampling set to nearest and you need the latest version with the fix from #12577 to disable interpolation as best as possible.

const layer = {
    id: "layer_id",
    type: "raster",
    source: "source_id",
    paint: {
        "raster-resampling": "nearest"
    }
}

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