I've gone back to trying to figure out how to generate topographical data with contour maps.
I found a youtube that ended up pointing me to contours.axismaps.com, where I could generate a topographical map of an area, and download it as SVG.
On inspecting the SVG output, it was pretty simple. It was constructed as an <svg> tag followed by a number of individual <path> tags. Each <path> has a "d" attribute that contains all the Move and Line (M x,y L x,y L x,y L x,y L x,y L x,y...) definitions. A "d" attribute in SVG can have all sorts of other things in it, like cubic Beziers, but this particular app doesn't generate them.
The "d" attribute actually also could contain multiple MLLLL values, so really, there were subpaths within the path. The <path> tag has an "id" attribute that declares the elevation for that path (and all its subpaths).
The "d" attribute also is highly granular. If you look at it, you'll find that it has tons of L movements that are only 1 numeric value apart.
If you generate a contour that, for example, has 100 foot elevation separations, and you're rendering a somewhat hilly region, you'll end up with a very large output file. Mine was 57 MB.
As well, with that many segments, it makes Inkscape crawl.
Here's what it looks like when rendered.
What the picture above doesn't show is how slow it is to read and render. It's really slow.
I had a goal to clip the contours to a geometry, perhaps to use the lines for laser etching. But due to the level of detail in the contour lines, it would still be slow when clipped.
Clipping
My initial attempt at clipping the lines with my own code was just to use a circle, and do a really crappy approach. Since it seemed the segments were really small, I made the code include segments where both start and end points were within a given radius of a center point, and exclude others. That isn't really "clipping". But it's close enough for what I'm doing.
In addition, to address the contour line detail level, I chose to output only a subset of the segments.
The overall sequence was like this:
- Read the file using svgelements.SVG.parse.
- Emit the <svg> element
- For each <path> element, find its "d" attribute, and break the MLLLL values into subpaths.
- For each subpath, clip the subpath according to the circle. The result will result in zero, one, or multiple subsubpaths.
- For each subsubpath, emit a different set of MLLLL values. In the end, I chose to keep the first 8 and last 8 vectors, and only every 8th segment in between. This made it so that small islands would be preserved, but long ones would drop to about 1/8th their precision (but keep precise points at the ends).
- Close off the paths
- Add an extra SVG element to show the clipping object.
Here's the result of the crude, circular clip.
Later, I changed the "clip" to extend clipped segments to the circle edge but still in a cheap way, so it was not mathematically correct, but would yield segments that would truly join to the circle.
The next thing I did was to do rectangular clip. Again, I did a cheap clipping mechanism, just checking to see if both endpoints of a segment were within the (x1,y1) to (x2,y2) definition of the rectangle. Still, not mathematically clipping.
Here's the rectangle clip result:
Finally, I read that there is another system called shapely that provides a native mechanism for clipping. It could do rectangle clipping, or arbitrary polygon clipping. For rectangle clipping, it provides shapely.clip_by_rect, which does an efficient clip. This probably uses the classic computer graphics algorithm for intersection detection, edge crossing, and real clip computation.
To use this, I had to switch the coordinate system to be shapely-friendly. One of the really odd things about the svgelements library is that (x,y) values are represented as a single, complex number (e.g., (100,200) is 100.0+200.0j).
shapely, on the other hand, depends on having LineString, MultiLineString, and Polygon geometries. Each of those has coordinates, but the coordinates are in a more typical format of an array of (x,y) values.
Here's the result.
The difference is negligible, both in computation time and result, but the shapely approach seems much more reliable.
Later, I also tried the arbitrary polygon clipping mechanism, using a manually defined set of coordinates. Here's the result.
I'm now trying to move to reading an existing SVG polygon to use for clipping, but handling holes as "interior" vs "exterior" polygon geometries is proving to be difficult.
No comments:
Post a Comment