Drawing Points, Tile clipping issue.

Topics: SharpMap Project, SharpMap v0.9 / v1.x
Mar 4, 2008 at 9:38 PM
My setup is as follows.
Google Maps API using SharpMap to generate overlay tiles.
The problem I am having is with the points. Currently all points are shown on the map by a image png file via the use of Symbols.

These symbol images are clipping at the tile borders.

Basically since Points have zero extent, the adjacent tile does not know to render half of the point image.

Let me know if I am not making any sense. I can't seem to figure out a way around this problem.

-Matt
Developer
Mar 4, 2008 at 10:03 PM
Edited Mar 4, 2008 at 10:12 PM
The tilecache.py people solve this by requesting a tile that is somewhat bigger than what they need. Afterwards you remove the extra margin. A symbol with its center in the margin will have its center removed but its edges will still be visible. Maybe something like this is possible in your solution.

Is this what you are doing?
http://www.sharpgis.net/post/2006/04/09/Using-SharpMap-as-a-Google-Earth-Network-Link.aspx
if so, you could request a bigger image and cut the edges away from the Bitmap that is returned by the GetMap.
Mar 4, 2008 at 10:11 PM
Sounds promising. How does one clip the resulting bitmap?


pauldendulk wrote:
The tilecache.py people solve this by requesting a tile that is somewhat bigger than what they need. Afterwards you remove the extra margin. A symbol with its center in the margin will have its center removed but its edges will still be visible. Maybe something like this is possible in your solution.

Is this what you are doing?
http://www.sharpgis.net/post/2006/04/09/Using-SharpMap-as-a-Google-Earth-Network-Link.aspx
if so, you could request a bigger image and cut the edges away from the Bitmap that is returned by the GetMap.

Mar 4, 2008 at 10:11 PM
Edited Mar 4, 2008 at 10:18 PM

pauldendulk wrote:
The tilecache.py people solve this by requesting a tile that is somewhat bigger than what they need. Afterwards you remove the extra margin. A symbol with its center in the margin will have its center removed but its edges will still be visible. Maybe something like this is possible in your solution.

Is this what you are doing?
http://www.sharpgis.net/post/2006/04/09/Using-SharpMap-as-a-Google-Earth-Network-Link.aspx
if so, you could request a bigger image and cut the edges away from the Bitmap that is returned by the GetMap.

Developer
Mar 4, 2008 at 10:23 PM
Edited Mar 4, 2008 at 10:50 PM
well that is c# System.Drawing stuff.

something like this:

Bitmap b1 = new Bitmap("images/chupacabra.png");
Bitmap b2 = new Bitmap(b1.Width - 20, b1.Height - 20);
Graphics graphic = Graphics.FromImage(b2);
graphic.DrawImage(b1, -10, -10, b1.Width, b1.Height);
b2.Save("out.png", System.Drawing.Imaging.ImageFormat.Png);

Mar 5, 2008 at 8:29 PM
Edited Mar 5, 2008 at 8:29 PM
Well I got it to work...sort of. The tiles just don't seem to match up right.

It appears that SharpMap renders points differently depending on the presence of points around it. I see shifting of points going on.
Any thoughts?

Best,
-Matt
Mar 5, 2008 at 9:41 PM
Nevermind, it was a problem with my projection transformation. All is well!

Thanks!
-Matt
Sep 26, 2014 at 12:45 PM
Hi, I was having the same problem while testing the WMS server on the last code base.
For some reason, modifying the GetMap handler as per your suggestion did not work out.
I suppose it has to do with missing transformations as well.
Here are my edits for reference: If you know how to fix it, thanks for letting me know.

In SharpMap.Web.Wms.Server.Handlers, SharpMap.Web, public override IHandlerResponse Handle(Map map, IContextRequest request):

First resizing the size in params with extra pixels, to account for overlapping clipped symbols, and computing the resulting ratio.
 Size size = GetSize(@params);
            
 //Begin Edit
 var extraPixels = 50;
 Size tempSize = new Size(size.Width + extraPixels, size.Height + extraPixels);
 var widthExtraPct = (tempSize.Width / size.Width) - 1;
 var heightExtraPct = (tempSize.Height / size.Height) - 1;
 size = tempSize;
 //End Edit
            
 map.Size = size;
Then resizing the bbox in params with the same ratio as the image resizing.
I guess this is where the bug is, and I should do something about transformations.
Envelope bbox = @params.BBOX;

//Edit
double deltaX = (bbox.Width * widthExtraPct) / 2;
double deltaY = (bbox.Height * heightExtraPct) / 2;
Envelope tempBbox = new Envelope(bbox.MinX - deltaX, bbox.MaxX + deltaX, bbox.MinY - deltaY, bbox.MaxY + deltaY);
bbox = tempBbox;
//End Edit
Finally, after the image rendering, clipping the oversized image to the original dimensions, while centering the clipped rectangle relatively to the oversized image to draw:
// render map
Image img = map.GetMap();

//Edit
Image cropped = new Bitmap(img.Width - extraPixels, img.Height - extraPixels);
Graphics graphic = Graphics.FromImage(cropped);
graphic.DrawImage(img, 0-(extraPixels / 2), 0-(extraPixels / 2), img.Width, img.Height);
img = cropped;
//End Edit
The positions are changed, which means there is certainly a bug.

Now while rolling back, another quick change in the codebase nicely fixed the clipping issue:

In SharpMap.Web.Wms.Server.Handlers, SharpMap.Web, public override IHandlerResponse Handle(Map map, IContextRequest request):
In SharpMap.Layers.VectorLayer, SharpMap, protected void RenderInternal(Graphics g, Map map, Envelope envelope, ITheme theme)
//Edit
var newEnvelope = new Envelope(envelope.MinX - envelope.Width/5, envelope.MaxX + envelope.Width/5, envelope.MinY - envelope.Height/5, envelope.MaxY + envelope.Height/5);
envelope = newEnvelope;
//End Edit
DataSource.ExecuteIntersectionQuery(envelope, ds);
It only took increasing the intersection query to include objects just outside of the target bounding box, for the tiles to correctly draw recomposed images of the overlapping objects.

What do you guys think of it? Shouldn't such a fix make sense?
I mean I can undestand that you can do the overlapping drawing and clipping in the Wms server, and I'm sure my above code probably has a dumb bug that can be fixed, but shouldn't the layer rendering logic offer the opportunity to correctly include overlapping symbols in the first place, at a much lower performance cost?
Coordinator
Sep 26, 2014 at 10:42 PM
Edited Sep 29, 2014 at 9:55 AM
Your bug is, that you calculate the widthExtraPct and heightExtraPct to integer values.
I'd assume that you'd get better results if you change the code in the first block to
var widthExtraPct = (tempSize.Width / (double)size.Width) - 1;
var heightExtraPct = (tempSize.Height / (double)size.Height) - 1;
Nonetheless, you may have a point, that enlarging the image to render offers benefits. You can achieve that using a proxy class.
Sep 29, 2014 at 3:05 PM
FObermaier wrote:
Your bug is, that you calculate the widthExtraPct and heightExtraPct to integer values.
I'd assume that you'd get better results if you change the code in the first block to
var widthExtraPct = (tempSize.Width / (double)size.Width) - 1;
var heightExtraPct = (tempSize.Height / (double)size.Height) - 1;
Bingo ! That was the fix, thanks a lot.

FObermaier wrote:
Nonetheless, you may have a point, that enlarging the image to render offers benefits.
I'm not sure what your meant here. My point was that enlarging the image then clipping seemed to be much less efficient than enlarging the intersection query.
I just did a profiling comparison of both solutions with the above code.
The result is quite interesting (left is the intersection solution, right is the enlarge and clip solution):

results

You can see that because my extra margin was larger with the intersection query (1/5 length * 2) than with the "enlarge and clip" solution (25 px * 2), there are significantly more geometry being rendered in the process (1443 vs 1081), but still, because a lot of those geometry end up being rendered outside of the viewport, the overall vector layer rendering time is shorter (1079 ms / 105 calls vs 1152 ms / 105 calls).
And that's before the costly clip redrawing operation, which kills the 2nd solution perfs (overall 1262 ms vs 1851 ms).

Now assuming that for no-tile rendering you may not want to include the overlapping exterior symbols, whereas with regular tiling that would be the common usage, could you consider either:
  • adding a property to a vector layer to add a margin to the intersection query? (simpler solution, with best performances)
  • Add a hook to the wms server logic, so that one implement the enlarge and clip solution (less desirable I think)
Hope that's clearer, and thanks in advance for looking that up.