Smathermather's Weblog

Remote Sensing, GIS, Ecology, and Oddball Techniques

Fast Calculation of Voronoi Polygons in PovRay

Posted by smathermather on January 20, 2012

Yet further abuse to follow in the application of PovRay for spatial analyses– be forwarned… .

Calculating Voronoi polygons is a useful tool for calculating the initial approximation of a medial axis. We densify the vertices on the polygon for which we want a medial axis, and then calculate Voronoi polygons on said vertices. I’ll confess– I’ve been using ArcGIS for this step. There. I said it. My name is smathermather, and I use ArcGIS. It’s a darn useful tool, and, and, I’m not ashamed to say that I use it all the time.

But, I do like the idea of doing this with non-proprietary, open source tools. I’ve contemplated using the approach that Regina Obe blogs about using PL/R + PostGIS to calculate them, but the installation of PL/R on my system failed my initial Yak Shaving Test.

So, with some google-fu, I stumbled upon articles on using 3D approaches to calculate 2D Voronoi diagrams, and my brain went into high gear. Right cones of equal size, viewed orthogonally generate lovely Voronoi diagrams, the only caveat being you need to know what your maximum distance you want to calculate in your diagram. For my use cases, this isn’t a limitation at all.

And so we abuse PovRay, yet again, to do our dirty work in analyses. I’ll undoubtedly have some follow up posts on this, but in the mean time, some diagrams and really dirty code:


global_settings {
	ambient_light rgb <0.723364, 0.723368, 0.723364>
}

camera {
	orthographic 
	location < 0.0, 5, 0>
	right x * 3
	up y * 3
	look_at < 0.0, 0.0, 0.0>
}

light_source {
	< 0.0, 100, 0>
	rgb <0.999996, 1.000000, 0.999996>
	parallel
	point_at < 0.0, 0.0, 0.0>
}


light_source {
	< 0.0, 100, 100>
	rgb <0.999996, 1.000000, 0.999996>
	parallel
	point_at < 0.0, 0.0, 0.0>
}

light_source {
	< 100, 100, 0>
	rgb <0.999996, 1.000000, 0.999996>
	parallel
	point_at < 0.0, 0.0, 0.0>
}

cone {
	<0.0, -0.5, 0.0>, 0.5,  <0.0, 0.5, 0.0>, 0.0
	hollow
	material {
		texture {
			pigment {
				rgb <0.089677, 0.000000, 1.000000>
			}
			finish {
				diffuse 0.6
				brilliance 1.0
			}
		}
		interior {
			ior 1.3
		}
	}
}

cone {
	<0.0, -0.5, 0.0>, 0.5,  <0.0, 0.5, 0.0>, 0.0
	hollow
	material {
		texture {
			pigment {
				rgb <1, 0.000000, 1.000000>
			}
			finish {
				diffuse 0.6
				brilliance 1.0
			}
		}
		interior {
			ior 1.3
		}
	}
	translate <-0.5,0,-0.5>
}

cone {
	<0.0, -0.5, 0.0>, 0.5,  <0.0, 0.5, 0.0>, 0.0
	hollow
	material {
		texture {
			pigment {
				rgb <1, 0.000000, 000000>
			}
			finish {
				diffuse 0.6
				brilliance 1.0
			}
		}
		interior {
			ior 1.3
		}
	}
	translate <-0.5,0,0.5>
}

cone {
	<0.0, -0.5, 0.0>, 0.5,  <0.0, 0.5, 0.0>, 0.0
	hollow
	material {
		texture {
			pigment {
				rgb <1, 1.000000, 000000>
			}
			finish {
				diffuse 0.6
				brilliance 1.0
			}
		}
		interior {
			ior 1.3
		}
	}
	translate <0.3,0,0.3>
}

cone {
	<0.0, -0.5, 0.0>, 0.5,  <0.0, 0.5, 0.0>, 0.0
	hollow
	material {
		texture {
			pigment {
				rgb <0, 1.000000, 000000>
			}
			finish {
				diffuse 0.6
				brilliance 1.0
			}
		}
		interior {
			ior 1.3
		}
	}
	translate <0.4,0,-0.2>
}

cone {
	<0.0, -0.5, 0.0>, 0.5,  <0.0, 0.5, 0.0>, 0.0
	hollow
	material {
		texture {
			pigment {
				rgb <1, 1.000000, 1.000000>
			}
			finish {
				diffuse 0.6
				brilliance 1.0
			}
		}
		interior {
			ior 1.3
		}
	}
	translate <0,0,-.7>
}

Posted in Analysis, GIS, Optics, Other, POV-Ray | Tagged: , , , , | 1 Comment »

What is the center line of a complex polygon? New approach.

Posted by smathermather on January 17, 2012

Today, I just show pictures of my process for filtering the medial axis of extraneous elements. I’ll let the pics tell the story for now:

Original Geometry, with ST_Segmentize run to densify the vertices

Medial Axis (Calculated outside PostGIS... at this time)

Density of vertex points in Medial Axis-- proportional to how closely it matches the direction of the local geometry

Medial Axis filtered based on density of vertices. Red segments are the removed ones, blue are the retained. It's not perfect, but it's very cheap computationally... .

scale axis transform 3

scale axis transform 2
scale axis transform 1

previous post

original

Posted in Analysis, Database, GIS, PostGIS, PostgreSQL, SQL | Tagged: , , , , | Leave a Comment »

What is the center line of a complex polygon? (cont. again!)

Posted by smathermather on January 14, 2012

I didn’t remember that I had 4 previous post on this topic, but I’ve gotten a little obsessed with this problem– how to (with relatively little computational cost) kind the centerline of complex polygons, so we can extract flow lines from streams, label long complex polygons, easily generate the centerline parcel boundary line for river bound parcels, etc..  FYI, here are my 4 previous posts on this topic:

scale axis transform 2
scale axis transform 1

previous post

original

I’ll confess, I became somewhat enamored of the scale axis transform as a method for tackling this problem.  In short, the scale axis transform identifies the medial axis of the polygon using Amenta and Kolluri’s finite union of balls to identify the initial centerline of the polygon, and then tries to fix the deficiencies in the robustness of the medial axis.

The problem, of course, is the degree to which bumps in the shape result in extra little lines hanging off our medial axis.  This does not represent an intuitive medial axis.  This is not what we would (in this case) call the centerline of the stream.

The scale axis transform suggests expanding these balls by a multiplicative factor, and deriving the new axis from that scaled version:

This step can easily be done in PostGIS once you have the medial axis:


CREATE TABLE rr_expand_r3_12 AS

SELECT
ST_Buffer(b.the_geom, ST_Distance(b.the_geom, ST_Union(a.the_geom)) * 1.2 )
FROM medial_axis b, polyline a
GROUP BY <a href="http://a.id/" target="_blank">a.id</a>, b.the_geom
 ;

I will play a bit more with this, but the initial results I got around areas with islands (torus like topology) were disappointing.  Also, at least as I have this implemented in PostGIS, this is a computationally intensive process.  I think though that I have a good (enough) alternative, which will be the topic of my next post.

Posted in Analysis, Database, GIS, PostGIS, PostgreSQL, SQL | Tagged: , , , , | 1 Comment »

PostGIS Cartographic Effects– Cartoonify Nearly Coincident Lines

Posted by smathermather on January 10, 2012

In my previous post, a long 24-hours ago, I proposed some automatic modification of line for cartographic reasons. I had some flaws in my code. The points were over-rotated by 45 degrees. Can you spot why? Tip: it’s a basic trigonometric mistake. Here’s the corrected code (though there may be a better way):

CREATE TABLE movedRoad AS
SELECT
    ST_Translate(b.the_geom,
        0.5 * ST_Distance(b.the_geom, ST_Union(a.the_geom)) * (cos(ST_Azimuth(b.the_geom, ST_ClosestPoint(ST_Union(a.the_geom), b.the_geom))) - 3.14159/4),
        0.5 * ST_Distance(b.the_geom, ST_Union(a.the_geom)) * (sin(ST_Azimuth(b.the_geom, ST_ClosestPoint(ST_Union(a.the_geom), b.the_geom))) - 3.14159/4) )
            AS the_geom
    FROM road2_points b, road1 a
        GROUP BY a.id, b.the_geom
    ;

An alternate approach is to only move those points that are too close, ala:

CREATE TABLE movedRoadAgain AS
	SELECT
        ST_Translate(b.the_geom,
	        (100 - ST_Distance(b.the_geom, ST_Union(a.the_geom))) * (cos(ST_Azimuth(b.the_geom, ST_ClosestPoint(ST_Union(a.the_geom), b.the_geom))) - 3.14159/4) ,
	        (100 - ST_Distance(b.the_geom, ST_Union(a.the_geom)))* (sin(ST_Azimuth(b.the_geom, ST_ClosestPoint(ST_Union(a.the_geom), b.the_geom))) - 3.14159/4) ) AS the_geom
	    FROM road2_points b, road1 a
	        GROUP BY a.id, b.the_geom
		HAVING ST_Distance(b.the_geom, ST_Union(a.the_geom)) < 100

	UNION ALL

	SELECT
		b.the_geom AS the_geom
	    FROM bridle_points b, apt a
	        GROUP BY a.id, b.the_geom
		HAVING ST_Distance(b.the_geom, ST_Union(a.the_geom)) >= 100
	    ;

Posted in PostGIS, GIS, SQL, Database, Trails, Trail Curation, Analysis, PostgreSQL | Tagged: , , , , , , , , | Leave a Comment »

PostGIS Cartographic Effects– Cartoonify Nearly Coincident Lines

Posted by smathermather on January 9, 2012

I’m still working on this query, but I thought I’d post what I’ve done so far. My intent is to produce scale-dependent exaggeration of the distances between quasi-parallel lines. The reason for this is so that lines such as street lines which are nearly coincident at a particular viewing scale can be spread from each other, much in the same way great cartography lies a little to display a relatively correct map. The nice thing about digital cartography is that as we zoom in, we can make the lie a smaller and smaller, until it is just barely a fib :) .

My thought was to start with the simplest case– move one line away from another (stationary) line. First step is to find the closest point on the stationary line, determine the distance and angle to that point, and then translate the point outward by some scale factor along that angle. Don’t take my code as gospel, though, I think there’s some deeper logical error, but see what you think:

ST_ShortestLine gives us the shortest line from a given point to the road from which we are trying to offset. This is just to help visualize the problem:

CREATE TABLE shortLine AS

SELECT
	ST_ShortestLine(b.the_geom, ST_Union(a.the_geom)) AS the_geom
    FROM road2_points b, road1 a
    GROUP BY a.id, b.the_geom
    ;

Now, we try to translate the point outward at the same angle, by half again the distance to the closest point:

CREATE TABLE movedRoad AS
SELECT
	ST_Translate(b.the_geom,
		0.5 * ST_Distance(b.the_geom, ST_Union(a.the_geom)) * cos(ST_Azimuth(b.the_geom, ST_ClosestPoint(ST_Union(a.the_geom), b.the_geom))),
		0.5 * ST_Distance(b.the_geom, ST_Union(a.the_geom)) * sin(ST_Azimuth(b.the_geom, ST_ClosestPoint(ST_Union(a.the_geom), b.the_geom))) )
			AS the_geom
    FROM road2_points b, road1 a
        GROUP BY a.id, b.the_geom
    ;

Resulting in something looking like this (red dashed line is the road we are moving away from, the dotted blue are the points making up the line we are moving, and the dotted brown are the newly transformed points, with ST_ShortestLine also visualized in thin brown):

/>

Posted in PostGIS, GIS, SQL, Database, Trails, Trail Curation, Analysis, PostgreSQL | Tagged: , , , , , , , , | 1 Comment »

There’s an App for That, Alternate

Posted by smathermather on January 2, 2012

codinggeekette, or Sarah Dutkiewicz and her husband introduced me to Linux and Open Source many years ago.  Running counter-culture is the genius that is @sadukie, so now she’s a Microsoft MVP.  We’ll forgive her that, since she does crazy stuff like setting up .net environments on Linux and other fun stuff.  Anyway, Sarah has a post on phone apps she likes and has proposed in her post (quasi) simu-blogging on the topic.  I’ll bite.  I’ll focus mine on the limited mapping apps I have on my iPod, since my smartphone solution is an iPod touch and a Motorola flip phone.

App number one: Tiltmeter.

Tiltmeter is simply that– it logs at a specified interval the 2D axis tilt of your iPod or iPhone, and can show that visually or e-mail the log to you.  Oh, where were you Tiltmeter, when I was building camera arrays, putting bandpass filters on them, and launching them on a 12-foot balloon.

It was a low cost, few band, hyperspectral attempt at calculating red-edge effects for productivity estimations.  A very early attempt at something similar to Grassroots Mapping.  Let’s just say, a tiltmeter would have really made the georeferencing easier… .

App number B: Ride the City.

Ride the City (Desktop web interface)

Ride the city is a very nice point-to-point routing solution for a desktop or mobile client.  It’s a discreet app on the mobile side.  Other than not doing looped (recreational) mapping support, it’s a pretty cool application.  Great for commuter bicycling.

App Gamma: PDF Maps

PDF Maps is a tool from Avenza Systems for viewing Geo PDFs.  I’ll confess, since I have an iPod touch, and therefore no GPS, I haven’t run this one through the ringer (and there are other likely choices), but it could be a really useful tool in concert, e.g., with the Geo PDFs from the USGS.

Posted in GIS, Other | Tagged: , | 1 Comment »

2011 in review — Laziest post possible

Posted by smathermather on December 31, 2011

Happy New Year everyone!  In a beautifully global fashion, the first New Year wishes I received this year were out of Africa from two folks I follow on twitter.  In that same vein, my blog is comfortably global, with readers on all the continents, Antarctica notwithstanding.  As I used to do polar research, I suppose I should change that 6 out of 7 to 7 out of 7, but it could be that it might be hard to get decent numbers down there.  WordPress may also not tabulate them.  Regardless, hello to summer residents of Antarctica, all 5-thousand or so of you.  I love polar stereographic.  UTM wouldn’t be the same without its partner UPS.

Anyway, I am humbled by my readership, people who keep me in line with smart comments, and guide my growth in the geospatial world.  Report excerpt and link as follows:

“The WordPress.com stats helper monkeys prepared a 2011 annual report for this blog.

Here’s an excerpt:

The concert hall at the Syndey Opera House holds 2,700 people. This blog was viewed about 14,000 times in 2011. If it were a concert at Sydney Opera House, it would take about 5 sold-out performances for that many people to see it.

Click here to see the complete report.

Posted in Other | Leave a Comment »

gdal_warp, cutlines, and cwhere– simple tip for use on Linux

Posted by smathermather on December 17, 2011

Mini GDAL tip of the day:

gdalwarp, especially in combination with gdal_merge, is a powerful tool for doing all sorts on nice aggregation (read: mosaic’ing) of spatial raster data.  Unfortunately, at least with a google search, there’s very little to be found on demonstrating the use of queries in conjunction with cutlines, probably because in general these queries are not difficult to figure out.

In a previous post, I demonstrated using this to stitch together USGS quads.  I found out when I tried to run this code on a Linux machine, the command like code was a little different.  Instead of:

gdal_merge -o usgs_merge.tif -createonly input1.tif input2.tif ...
gdalwarp -cutline quadrangle_index_24k.shp -cwhere "quadname_ = West_View" West_View.tif usgs_merge.tif

I found I needed some extra single quotes to get my string literal to not be interpreted as a field:


gdalwarp -cutline quadrangle_index_24k.shp -cwhere "quadname_ = 'West_View'" West_View.tif usgs_merge.tif

Ah, that’s better.  Now I have a nice big mosaic of DEMs for my region with perfect cuts where the overlaps used to be.  (The change is hard to see– look for single quotes around West_View in “quadname_ = ‘West_View’”)

Posted in GDAL, GIS | Tagged: , , , , | Leave a Comment »

GDAL Slopes– Local Hydrologic Slope vs. the Standard Approach

Posted by smathermather on December 15, 2011

Open Source software is not, of course just about warm and fuzzies, great support, rapid development cycles, shared costs, etc., it’s also about getting your hands dirty with someone else’s code and implementing stuff more quickly and more intelligently because of it, and hopefully learning something in the process.  You don’t have to poke under the hood to drive the car, but sometimes it’s nice to anyway.

The two normal approaches to calculating slope in GIS raster tools (used in applications from ESRI’s Spatial Analyst and GDAL’s gdaldem) are Horn’s formula and Zevenbergen & Thorne’s formula, both integrated calculations of slope that do a bit of generalization in the process of calculating slope.  This is particularly useful if you are using slope for more than a localized calculations, for example in doing estimates of local light conditions.  That said, if you want to match local hydrologic slope (something which will more closely approximate a field measurement of slope), you are better off using a different formula altogether.  An article a colleague sent me: “When GIS Slope is Not What You Think” in this month’s The Forestry Source tipped me off to this, and I thought I’d do some quick and dirty digging in the GDAL code to see if I could implement something a little different.

Here is the standard Horn approach as currently implemented in GDAL:

float GDALSlopeHornAlg (float* afWin, float fDstNoDataValue, void* pData)
{
    const double radiansToDegrees = 180.0 / M_PI;
    GDALSlopeAlgData* psData = (GDALSlopeAlgData*)pData;
    double dx, dy, key;

    using std::max;
    dx = (max((afWin[0], afWin[3], afWin[3], afWin[6])) -
          max((afWin[2], afWin[5], afWin[5], afWin[8])))/psData->ewres;

    dy = ((afWin[6], afWin[7], afWin[7], afWin[8]) -
          max((afWin[0], afWin[1], afWin[1], afWin[2])))/psData->nsres;

    key = (dx * dx + dy * dy);

    if (psData->slopeFormat == 1)
        return (float) (atan(sqrt(key) / (8*psData->scale)) * radiansToDegrees);
    else
        return (float) (100*(sqrt(key) / (8*psData->scale)));
}

Borrowing from gdaldem roughness we implement this:

float GDALSlopeHydroAlg (float* afWin, float fDstNoDataValue, void* pData)

{
    // Hydrologic Slope is the max
    //  local slope btw center cell and adjacent cells

    const double radiansToDegrees = 180.0 / M_PI;
    GDALSlopeAlgData* psData = (GDALSlopeAlgData*)pData;
                double key;

    float pafLocalMin = afWin[0];
     float pafLocalMax = afWin[0];

    for ( int k = 1; k < 9; k++)
    {
        if (afWin[k] < pafLocalMin)
        {
            pafLocalMin=afWin[k];
        }

        if (afWin[k] > pafLocalMax)
        {
            pafLocalMax=afWin[k];
        }
    }

    key = afWin[4] - pafLocalMin;

    if (psData->slopeFormat == 1)
        return (float) (atan(sqrt(key) / (2*psData->scale)) * radiansToDegrees);
    else
        return (float) (100*(sqrt(key) / (2*psData->scale)));
}

Output is rougher than the original, as expected:

Local Hydrologic Slope

Zevenbergen & Thorne’s Slope

And looking at the stats we see a larger standard deviation for local hydrologic slope:


Z:\>gdalinfo Hydrologic_slope.tif
Driver: GTiff/GeoTIFF
Files: Hydrologic_slope.tif
...

Metadata:
STATISTICS_MINIMUM=0
STATISTICS_MAXIMUM=46.168109893799
STATISTICS_MEAN=10.206419514979
STATISTICS_MEDIAN=2.8041767686283e-266
STATISTICS_MODE=1.4463930749435e-307
STATISTICS_STDDEV=5.3794018830967
LAYER_TYPE=athematic

Z:\>gdalinfo Zevenbergen_Thorne.tif
Driver: GTiff/GeoTIFF
Files: Zevenbergen_Thorne.tif
...
Metadata:
STATISTICS_MINIMUM=0
STATISTICS_MAXIMUM=52.424499511719
STATISTICS_MEAN=3.0666069784504
STATISTICS_MEDIAN=4.331070843283e-184
STATISTICS_MODE=-4
STATISTICS_STDDEV=3.567882216098
LAYER_TYPE=athematic

A couple of additional notes.  First, this is 2.5ft resolution data.  At this resolution, the differences in algorithms may be somewhat academic.  Also, of note– the range for the Zevenbergen-Thorn slope algorithm is greater than that for the local hydrologic slope.  This is an outcome I did not anticipate.

 

Update:

Hmmm, looks like I have more revisions and questions from the GDAL dev board. May have to revisit this… .  Forgot about the corners of my 3×3 window… .

Posted in Analysis, Ecology, GDAL, GIS | Tagged: , , , , , | Leave a Comment »

Landscape Position: Conclusion? (part 2)

Posted by smathermather on December 7, 2011

From earlier post:

“I’ve managed to pilot most of a fast high resolution landscape position workflow with PovRay as my magic tool. The final steps I hope to pipe through PostGIS Raster. In the meantime a screenshot and description: blues are riparian, raw ocre, etc upland categories, grey is mostly flat lake plain and mid slopes, all derived from just a high res DEM input (no hydro lines as input, so it works on areas where you don’t know where the streams may be). There will be more categories in the final, in the meantime, welcome to December.”

I didn’t use PostGIS Raster, but the incredibly useful gdal_calc.py to finish out my landscape position calculations.  I will endeavor to post my code to github soon, but the parts and pieces include using gdal and PovRay.  PovRay helps with the sampling (really!) of nearby neighbors in the raster DEM, and gdal does the averaging and differencing of those to get relative landscape position.  I spent some time yesterday scratching my head over how to show all the new landscape position information on a readable and useable map, and after discussion with collegues, decided to use it to divide the world into two categories– riparian & lowland + headwaters & upland (not reflected yet in the labels).  To find out more about landscape position, follow this link, or better yet this one.  (BTW, the green is park land, blue is riparian/lowland/stream networks, purple is the basin boundary).

Riparian map based on landscape position, calculated using PovRay and GDAL.

Posted in Analysis, Ecology, GIS, Landscape Position, Other, POV-Ray | Tagged: , , , , , , , , , , , , | Leave a Comment »

 
Follow

Get every new post delivered to your Inbox.

Join 105 other followers