OpenMap-java / openmap

OpenMap is an Open Source JavaBeans-based programmer's toolkit. Using OpenMap, you can quickly build applications and applets that access data from legacy databases and applications.
http://openmap-java.org
Other
73 stars 43 forks source link

Rotated OMScalingRaster rendered wrong when partially off edge of window #42

Closed ianrenton closed 7 years ago

ianrenton commented 7 years ago

I have come across an issue when using an OMScalingRaster object which has a rotation applied. The object sits on an OMGraphicHandlerLayer. When the image is fully in view, it renders correctly. However, when the image is partially off the left or right side of the window (Mercator projection), the shape of the image is distorted - it appears shrunk in one direction and enlarged in the other. If I do not rotate the OMScalingRaster, it works fine when placed partially off-screen, but unfortunately for my application the image needs to be rotated.

I notice in OMScalingRaster.java that there is a "clever bit" in the scaleTo(Projection) method which checks if some of the image is off-screen, and if so, draws only the visible subset of the image. In order to make this rotated image work properly for me, I have had to subclass OMScalingRaster and remove these lines:

        if (corners == null || corners.size() <= 2) {
            iRect = winRect.intersection(projRect);
        }

and also remove the if block that starts with if (!winRect.contains(projRect)) {.

This fixes the problem for me, but is obviously not ideal as it is removing a useful feature.

Do you know if this functionality has been tried before when using a rotated OMScalingRaster?

I'm not able to share an example direct from our code, but if necessary I will try to create a minimal example to demonstrate this problem.

dfdietrick commented 7 years ago

Hi Ian,

I understand that the API will allow you to rotate an OMScalingRaster, but I don't quite get what it represents on the map - it's kind of unusual to anchor a projected image to two points on the map and then rotate it. What does the image represent? Is it an Icon image centered over location and you're trying to resize it for different zoom levels, or are you really trying to rotate a georeferenced image?

ianrenton commented 7 years ago

I should probably explain what I'm doing, as I may be going about it wrong.

I have an image which is created and updated internally within the software. Currently I implement this as an AWT BufferedImage. The image is rectangular but corresponds to a geographical rectangle that could be at any angle relative to North.

(The maths that generates this image is complex, and I would rather keep the image internally as a plain rectangle and just rotate it for display, rather than internally handling an image that is orthogonal to North but with large transparent areas around a diagonal rectangle.)

When setting up my OMScalingRaster, I give it the BufferedImage, specify the rotation angle I would like, then supply the lat/lon of two corners of where I would like the image to display using setULLat() etc.

This does appear to work, and OpenMap renders my rectangular image in the right place at the right angle - unless the image would be partially off screen.

Is this a real bug, or is there another way of achieving what I'm trying to do that I should be using instead?

Thanks

dfdietrick commented 7 years ago

I have to think about this a bit - so you’re generating a geographically referenced image that may not be north-up. You use the upper left / lower right coordinates of your geographic rectangle to set the OMScalingRaster so it scales it properly, and then it gets rotated by setting the OMScalingRaster rotation setting.

If this looks at all accurate, I think you’re getting lucky with the projection type and/or the scale you’re working with. It’ll work with a cartesian projection or maybe an equidistant projection like LLC or UTM where the delta-lat and delta-lon per pixel is constant relative to the center of the image and it’s location, or at a scale where those deltas are almost imperceptible. Based on that, I know the OMScalingRaster wasn’t written or tested to handle rotating images after scaling, and it’s a happy coincidence you’re getting results even this good.

If you need precision, I’d use an OMWarpingImage - but you’d have to write a transform that handles the rotation so it knows how to build up the resulting image. The transform allows the OMWarpingImage to figure out what the footprint of the result will be, then grab the appropriate source pixel values from the source for each resulting destination pixel.

If the precision you’re seeing is OK for your needs, the other option is to use an OMScalingIcon, which is an OMScalingRaster that doesn’t try to do that clipping thing - it just draws the entire rotated image. The trick with using the OMScalingIcon is to figure out what the base scale of your image’s projection is in OpenMap projection terms.

You can use ProjMath.getScale(…) for that, like the com.bbn.openmap.event.NavMouseMode2 does to change the projection scale to a rectangle drawn on the map.

On Oct 11, 2017, at 8:46 AM, Ian Renton notifications@github.com wrote:

I should probably explain what I'm doing, as I may be going about it wrong.

I have an image which is created and updated internally within the software. Currently I implement this as an AWT BufferedImage. The image is rectangular but corresponds to a geographical rectangle that could be at any angle relative to North.

The maths that generates this image is complex, and I would rather keep the image internally as a plain rectangle rather than creating an image that is orthogonal to North but with large transparent areas.

When setting up my OMScalingRaster, I give it the BufferedImage, specify the rotation angle I would like, then supply the lat/lon of two corners of where I would like the image to display using setULLat() etc.

This does appear to work, and OpenMap renders my rectangular image in the right place at the right angle - unless the image would be partially off screen.

Is this a real bug, or is there another way of achieving what I'm trying to do that I should be using instead?

Thanks

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/OpenMap-java/openmap/issues/42#issuecomment-335797836, or mute the thread https://github.com/notifications/unsubscribe-auth/AB08k68r7whc1gbBhuRZZzaIn1a1wsiyks5srLjBgaJpZM4Pycbp.

ianrenton commented 7 years ago

I suspect we're just lucky that the image looks OK and is small enough that the projection doesn't show it distorted. I will look into OMWarpingImage, is there a guide / example anywhere of how to use this?

dfdietrick commented 7 years ago

The best example of using the OMWarpingImage is in the com.bbn.openmap.dataAccess.mapTile.StandardMapTileFactory.getTileNotMatchingProjectionType(…) method. it’s using a MapTileCoordinateTransform (there are two implementing flavors) that knows how to provide a com.bbn.openmap.proj.coords.GeoCoordTransformation for the zoom level, that allows the OMWarpingImage to use the DataBounds projected image coordinates to get lat/lon coordinates and vice-versa. This might be useful to see how to write a custom GeoCoordTransformation class for a unique use case.

If you do a reference search on OMWarpingImage in openmap, you’ll find a couple of other image layer components that use it, too (RpfLayer, DTEDLayer), but they are using coordinate transforms provided in the coords package.

On Oct 12, 2017, at 11:41 AM, Ian Renton notifications@github.com wrote:

I suspect we're just lucky that the image looks OK and is small enough that the projection doesn't show it distorted. I will look into OMWarpingImage, is there a guide / example anywhere of how to use this?

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/OpenMap-java/openmap/issues/42#issuecomment-336177619, or mute the thread https://github.com/notifications/unsubscribe-auth/AB08kwX96Gt5ao70pAW7NCgLR5wO2jOlks5srjMkgaJpZM4Pycbp.

ianrenton commented 7 years ago

Thanks for your help! I now have an OMWarpingImage set up with the correct coordinate transforms to make it appear in the right place.

I have one more question, if I may:

I am updating my BufferedImage regularly and need to force the layer to show the new image. I am updating my OMWarpingImage with the new BufferedImage contents by calling the setWarp() method - so far so good. However, my layer isn't picking up that it needs to regenerate. So the user does not see any updates on screen. Changing the projection (e.g. zooming or panning the map) forces an update and the latest image data is shown.

What's the best way of forcing a repaint of the layer every time the OMWarpingImage behind it changes?

dfdietrick commented 7 years ago

If your layer extends OMGraphicHandlerLayer, call doPrepare() on your layer. That launches a thread that calls your prepare() method and the repaint() method. That’s what a projection change basically does. Good to hear it's working.

On Oct 13, 2017, at 7:12 AM, Ian Renton notifications@github.com wrote:

Thanks for your help! I now have an OMWarpingImage set up with the correct coordinate transforms to make it appear in the right place.

I have one more question, if I may:

I am updating my BufferedImage regularly and need to force the layer to show the new image. I am updating my OMWarpingImage with the new BufferedImage contents by calling the setWarp() method - so far so good. However, my layer isn't picking up that it needs to regenerate. So the user does not see any updates on screen. Changing the projection (e.g. zooming or panning the map) forces an update and the latest image data is shown.

What's the best way of forcing a repaint of the image/layer every time the data behind it changes?

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/OpenMap-java/openmap/issues/42#issuecomment-336422855, or mute the thread https://github.com/notifications/unsubscribe-auth/AB08k88X1pprR9IvPkrBWZphM6_OVMhxks5sr0WBgaJpZM4Pycbp.

ianrenton commented 7 years ago

That's working great, thanks for your help!