agentscript icon indicating copy to clipboard operation
agentscript copied to clipboard

slope on droplets off

Open m0ose opened this issue 2 years ago • 19 comments

I was looking at the slopes on the droplets model https://code.agentscript.org/views2/droplets.html , and it occurred to me that the slopes look wrong. If you look at model.slopes they are all values like 1.4 which is like 80 degrees. I was not expecting slopes bigger than like 10 degrees for that area.

I think it has to do with the way dzdx and dzdy are calculated. They are treating the z values in meters, but the x,y values in terms of pixel dimensions. I think that the x and y should be in terms of meters too.

I wrote some helper functions that seem to work. My question is wether you would be interested in me putting those into a pull request? would we be interested in another class for GeoDatasets?

        /**
         * utils
         * 
         * */
         function geoDzdx(elevation, widthInMeters) {
            const pixelScale = widthInMeters / elevation.width
            const dzdx = elevation.dzdx(2, (1/8) * (1/pixelScale)) // (1/8) for the kernel and 1/pixelscale to get units right
            return dzdx
        }

        function geoDzdy(elevation, heightInMeters) {
            const pixelScale = heightInMeters / elevation.height
            const dzdy = elevation.dzdy(2, (1/8) * (1/pixelScale))
            return dzdy
        }

        function slope(dzdx, dzdy) {
            const slopes = dzdx.map((x, i) => {
                const y = dzdy.data[i]
                const a = Math.hypot(-x, -y)
                const sl = (Math.PI / 2) - Math.atan2(1, a)
                return sl
            })
            return slopes
        }

        /**
         * Calculate the slope of elevation Dataset in meters
         * @param {DataSet} elevationDS
         * @param {number} widthMeters The width in meters of the elevation dataset
         * @param {number} heightMeters The height in meters of the elevation dataset
         * @return {DataSet} slopes The slopes in radians
         * */
        function slopeFromElevationDS(elevationDS, widthMeters, heightMeters) {
            const dzdx = geoDzdx(elevationDS, widthMeters)
            const dzdy = geoDzdy(elevationDS, heightMeters)
            const slopes = slope(dzdx, dzdy)
            return slopes
        }

        /**
         * Calculate the slope of elevation Dataset in meters
         * @param {DataSet} elevationDS
         * @param {[west, south, east, north]} bounds
         * @return {DataSet} slopes The slopes in radians
         * */
        function slopeFromElevationDS_bounds(elevationDS, bounds) {
            const sizeMeters = bboxMetricSize(bounds)
            return slopeFromElevationDS(elevationDS, sizeMeters[0], sizeMeters[1])
        }

m0ose avatar Aug 12 '22 17:08 m0ose

Sounds possible. That would be an issue in DataSet.js and it's convolutions, we could fix those. Alas, I'm stuck with medical stuff today so can't get to that right now.

backspaces avatar Aug 12 '22 17:08 backspaces

Here's the code: https://github.com/backspaces/agentscript/blob/master/src/DataSet.js#L352

backspaces avatar Aug 12 '22 18:08 backspaces

This is an important distinction that we've talked about in AnyHazard over 8 years. For some model usecases, like sculpting sand and visualizing the general flows on watesheds, we do want dZ/dPixels. As a hack, I'm always trying to find the right delta time when I'm sculpting sand with water because I believe it's written as dZ/dX (meters). I recognized there's a difference of opinion of how this should be.

The current use of the plume model (hazmat and smoke) in AnyHazard is also probably better dealt with in dZ/dPixel.

For cases where you're doing a physical sim and care about time of arrival or distance, you do want dZ / dX (meters). Your avalanche model is probably a good case.

stephenguerin avatar Aug 12 '22 19:08 stephenguerin

@stephenguerin Thanks for the insight.

So does this suggest a few new methods in the DataSet.js module? The current methods are simple convolutions without units (meters etc)

backspaces avatar Aug 13 '22 15:08 backspaces

I was thinking about making a class that inherits from dataset , maybe geodataset, that overrides the slope, aspect, dzdx, etc methods to take bounds.

m0ose avatar Aug 13 '22 15:08 m0ose

That makes sense. By bounds you mean bbox? I.e. [west, south, east, north]?

backspaces avatar Aug 13 '22 15:08 backspaces

Yeah.

m0ose avatar Aug 13 '22 16:08 m0ose

thinking about it, I can see going with delta physical units height / delta physical distance by default and then leave it to modeler to simulate/visualize delta height / deltaPixels

On Sat, Aug 13, 2022, 9:28 AM Owen Densmore @.***> wrote:

@stephenguerin https://github.com/stephenguerin Thanks for the insight.

So does this suggest a few new methods in the DataSet.js module? The current methods are simple convolutions without units (meters etc)

— Reply to this email directly, view it on GitHub https://github.com/backspaces/agentscript/issues/67#issuecomment-1214176913, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAIHKJ6N7CY2OS6QXNZGZZ3VY65JJANCNFSM56MJTMAQ . You are receiving this because you were mentioned.Message ID: @.***>

stephenguerin avatar Aug 13 '22 16:08 stephenguerin

Does GeoDataSet resolve this? I think it does. I have to really dig in and understand GeoDataSet, touches on a lot of geo concepts I currently lack! Time for a beer.

backspaces avatar Aug 15 '22 18:08 backspaces

It's kinda like World.js & GeoWorld.js.

backspaces avatar Aug 15 '22 18:08 backspaces

Yeah. When I dug in, I started seeing more and more methods I needed to change.

m0ose avatar Aug 15 '22 19:08 m0ose

It really is like the World/GeoWorld: it has coord xfms that go from/to bbox <-> dataset .. i.e. a lonlat for each item in the dataset. Very nice!

One minor request: lat, lng -> lng, lat i.e have "x" first. I say that because other gis-y methods in AS do this. gis.js sez:

  • Current gis.js and others use [lon, lat] coords, i.e. x,y.
  • gis.latlon(lonlat) converts [lon, lat] to [lat, lon], i.e. y,x as used by leaflet

So: getLatLng(lat, lng) would go to getLngLat(lng, lat), x,y order.

A less important naming convention:

  • gis.js and other use "lon" rather than "lng". Might be useful to rename lng to lon.

Why? To disambiguate between leaflet (lat, lng) and more common (currently) use of "lon". I actually researched this, and https://macwright.com/lonlat/ helped

No big at all deal however .. x,y is the high order bit.

backspaces avatar Aug 15 '22 21:08 backspaces

Oh, and geojson coords:

      [
        -105.914164,
        35.7241074
      ],

.. put lon first.

backspaces avatar Aug 15 '22 21:08 backspaces

I need to look through geoworld. There is some good stuff in there.

m0ose avatar Aug 15 '22 21:08 m0ose

Sorry to be too chatty, but it occurred to me to ask:

  • Should LeafletDataSet, and the newer BBoxDataSet use GeoDataSet?
  • Are there other models that should convert as well?

backspaces avatar Aug 15 '22 22:08 backspaces

It looks like LeafletDataSet will be easy to change over https://github.com/backspaces/agentscript/pull/68/commits/5d85db73c109bb0cb75c879131a9e7a354e8b578. I converted the droplets model, but I am not sure what other models could use it.

m0ose avatar Aug 15 '22 22:08 m0ose

Actually looking at it, since zxyToDataSet returns a GeoDataSet most of the demos are already using it.

m0ose avatar Aug 15 '22 22:08 m0ose

Cool!

backspaces avatar Aug 15 '22 23:08 backspaces

I note you have import BBoxTransform from './World.js' in GeoDataSet. I don't think you need it. And the reason it's not exported in World.js is because it's exposed indirectly via world.bboxTransform(minX, minY, maxX, maxY)

And GeoWorld uses it via inheritance. this.xfm = this.bboxTransform(...bbox)

That is a bit convoluted, I realize! But it keeps is "private" and only exposed to World and it's subclasses.

This is kinda fun, thanks for your patience!

backspaces avatar Aug 15 '22 23:08 backspaces