<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>iknuth &#187; postgresql</title>
	<atom:link href="http://iknuth.com/tag/postgresql/feed/" rel="self" type="application/rss+xml" />
	<link>http://iknuth.com</link>
	<description>gis - database - systems - web programming</description>
	<lastBuildDate>Fri, 20 Jan 2012 03:55:30 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>making seatmate aware of locations with postgis</title>
		<link>http://iknuth.com/2012/01/making-seatmate-aware-of-locations-with-postgis/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=making-seatmate-aware-of-locations-with-postgis</link>
		<comments>http://iknuth.com/2012/01/making-seatmate-aware-of-locations-with-postgis/#comments</comments>
		<pubDate>Sat, 14 Jan 2012 17:49:00 +0000</pubDate>
		<dc:creator>edwin</dc:creator>
				<category><![CDATA[technology]]></category>
		<category><![CDATA[location based service]]></category>
		<category><![CDATA[mockup]]></category>
		<category><![CDATA[postgis]]></category>
		<category><![CDATA[postgresql]]></category>
		<category><![CDATA[seatMate]]></category>
		<category><![CDATA[sql]]></category>
		<category><![CDATA[trimet]]></category>

		<guid isPermaLink="false">http://iknuth.com/?p=515</guid>
		<description><![CDATA[Now that I&#8217;ve got the comment view more or less working, it&#8217;s time to link it up with actual trimet routes.  I think we need an initial screen to show to users when they first bring up the app.  I &#8230; <a href="http://iknuth.com/2012/01/making-seatmate-aware-of-locations-with-postgis/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<img style='float: left; margin-right: 10px; border: none;' src='http://www.gravatar.com/avatar.php?gravatar_id=9e27ba264dccbe5149fd08b17f5bdeb1&amp;default=http://use.perl.org/images/pix.gif' alt='No Gravatar' width=40 height=40/><p>Now that I&#8217;ve got the comment view more or less working, it&#8217;s time to link it up with actual trimet routes.  I think we need an initial screen to show to users when they first bring up the app.  I imagine that people will be either riding the bus/max, or they will be waiting at the stop.  Seems like we need to start by showing routes that are closest the user&#8217;s current location.</p>
<p><a href="http://iknuth.com/wp-content/uploads/2012/01/start-screen.png"><img class="aligncenter size-medium wp-image-516" title="start-screen" src="http://iknuth.com/wp-content/uploads/2012/01/start-screen-300x258.png" alt="start screen for seatmate app" width="300" height="258" /></a></p>
<h2>performing spatial queries in postgis</h2>
<p>Our backend needs to support a method that takes a latitude and longitude in the commonly used WGS84 projection and return the routes that are closest to that point.  Just getting the nearest stops isn&#8217;t useful because the rider may be actually travelling the route and not anywhere near a stop.  Luckily postgis makes it easy to select lines based on proximity to a point.  Please see the note from Regina in the comments for updated postgis functions.</p>
<div id="gist-1612154" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'><span class="k">SELECT</span> <span class="ss">&quot;RTE&quot;</span> <span class="k">as</span> <span class="n">route</span><span class="p">,</span><span class="ss">&quot;RTE_DESC&quot;</span> <span class="k">as</span> <span class="n">description</span><span class="p">,</span><span class="ss">&quot;DIR_DESC&quot;</span> <span class="k">as</span> <span class="n">direction</span><span class="p">,</span></div><div class='line' id='LC2'>	<span class="n">distance</span><span class="p">(</span><span class="n">PointFromText</span><span class="p">(</span><span class="s1">&#39;POINT(-122.613639 45.499541)&#39;</span><span class="p">,</span> <span class="mi">4326</span><span class="p">),</span> <span class="n">the_geom</span><span class="p">)</span> <span class="k">as</span> <span class="n">distance</span></div><div class='line' id='LC3'><span class="k">from</span> <span class="n">tm_routes</span> <span class="k">order</span> <span class="k">by</span> <span class="n">distance</span> <span class="k">limit</span> <span class="mi">10</span><span class="p">;</span></div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/1612154/c3c7211ae353a4240ad8789ce01c10c38f80a97c/spatial.sql" style="float:right;">view raw</a>
            <a href="https://gist.github.com/1612154#file_spatial.sql" style="float:right;margin-right:10px;color:#666">spatial.sql</a>
            <a href="https://gist.github.com/1612154">This Gist</a> brought to you by <a href="http://github.com">GitHub</a>.
          </div>
        </div>
</div>

<p>This query returns the following results when applied against the postgis table I created from the trimet shapefile projected to WGS84. Note that postgres requires that we double quote the column names because they are capitalized.</p>
<div id="gist-1612154" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'>&nbsp;route |      description       |           direction            |      distance       </div><div class='line' id='LC2'>-------+------------------------+--------------------------------+---------------------</div><div class='line' id='LC3'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;9 | Powell/Broadway        | To Powell &amp; 98th or Gresham TC | 0.00200889736828729</div><div class='line' id='LC4'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;9 | Powell/Broadway        | To Saratoga &amp; 27th             | 0.00200890251737265</div><div class='line' id='LC5'>&nbsp;&nbsp;&nbsp;&nbsp;14 | Hawthorne              | To Foster &amp; 94th               | 0.00229043846550735</div><div class='line' id='LC6'>&nbsp;&nbsp;&nbsp;&nbsp;14 | Hawthorne              | To Portland City Center        | 0.00229044244641688</div><div class='line' id='LC7'>&nbsp;&nbsp;&nbsp;&nbsp;71 | 60th Ave/122nd Ave     | To Foster &amp; 94th               | 0.00460586966268184</div><div class='line' id='LC8'>&nbsp;&nbsp;&nbsp;&nbsp;71 | 60th Ave/122nd Ave     | To Clackamas Town Center       | 0.00460588437600594</div><div class='line' id='LC9'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 | Division/Fessenden     | To Gresham TC                  | 0.00575370636240205</div><div class='line' id='LC10'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 | Division/Fessenden     | To St Johns                    | 0.00575370636240205</div><div class='line' id='LC11'>&nbsp;&nbsp;&nbsp;&nbsp;75 | Cesar E Chavez/Lombard | To St. Johns                   | 0.00898150226029743</div><div class='line' id='LC12'>&nbsp;&nbsp;&nbsp;&nbsp;75 | Cesar E Chavez/Lombard | To Milwaukie                   | 0.00898150543661629</div><div class='line' id='LC13'>(10 rows)</div><div class='line' id='LC14'><br/></div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/1612154/21f8c8d7568fcc110e4ac8a390d93d6343f4e056/results.txt" style="float:right;">view raw</a>
            <a href="https://gist.github.com/1612154#file_results.txt" style="float:right;margin-right:10px;color:#666">results.txt</a>
            <a href="https://gist.github.com/1612154">This Gist</a> brought to you by <a href="http://github.com">GitHub</a>.
          </div>
        </div>
</div>

<h2>grouping results in sql</h2>
<p>The routes returned look great for my current location. The data is accurate enough to differentiate between routes that run on the same street in different directions. If I were waiting at a bus stop, it would be a safe bet to assume that the bus I want is on the same side of the street as me. I still want to return other routes, but at this point I don&#8217;t really care what direction they are going. I want to group the results and just return a single row for each route. We can achieve that goal by tweaking the sql. We just need to apply an aggregate to the distance and group the results by route. We could filter the results by distance, but there isn&#8217;t really a need at this point.</p>
<p><div id="gist-1612154" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'><span class="k">SELECT</span> <span class="ss">&quot;RTE&quot;</span> <span class="k">as</span> <span class="n">route</span><span class="p">,</span> <span class="ss">&quot;RTE_DESC&quot;</span> <span class="k">as</span> <span class="n">description</span><span class="p">,</span></div><div class='line' id='LC2'>	<span class="k">min</span><span class="p">(</span><span class="n">distance</span><span class="p">(</span><span class="n">PointFromText</span><span class="p">(</span><span class="s1">&#39;POINT(-122.613639 45.499541)&#39;</span><span class="p">,</span> <span class="mi">4326</span><span class="p">),</span> <span class="n">the_geom</span><span class="p">))</span> <span class="k">as</span> <span class="n">distance</span></div><div class='line' id='LC3'><span class="k">from</span> <span class="n">tm_routes</span> <span class="k">group</span> <span class="k">by</span> <span class="n">route</span><span class="p">,</span> <span class="n">description</span></div><div class='line' id='LC4'><span class="k">order</span> <span class="k">by</span> <span class="n">distance</span> <span class="k">limit</span> <span class="mi">10</span><span class="p">;</span></div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/1612154/c2102181c0c79cd267b35a948ac45f6f3aa710c9/improved.sql" style="float:right;">view raw</a>
            <a href="https://gist.github.com/1612154#file_improved.sql" style="float:right;margin-right:10px;color:#666">improved.sql</a>
            <a href="https://gist.github.com/1612154">This Gist</a> brought to you by <a href="http://github.com">GitHub</a>.
          </div>
        </div>
</div>
<br />
<div id="gist-1612154" class="gist">

        <div class="gist-file">
          <div class="gist-data gist-syntax">
              <div class="highlight"><pre><div class='line' id='LC1'>route |      description       |      distance       </div><div class='line' id='LC2'>-------+------------------------+---------------------</div><div class='line' id='LC3'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;9 | Powell/Broadway        | 0.00200889736828729</div><div class='line' id='LC4'>&nbsp;&nbsp;&nbsp;&nbsp;14 | Hawthorne              | 0.00229043846550735</div><div class='line' id='LC5'>&nbsp;&nbsp;&nbsp;&nbsp;71 | 60th Ave/122nd Ave     | 0.00460586966268184</div><div class='line' id='LC6'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4 | Division/Fessenden     | 0.00575370636240205</div><div class='line' id='LC7'>&nbsp;&nbsp;&nbsp;&nbsp;75 | Cesar E Chavez/Lombard | 0.00898150226029743</div><div class='line' id='LC8'>&nbsp;&nbsp;&nbsp;&nbsp;66 | Marquam Hill/Hollywood | 0.00898150861891694</div><div class='line' id='LC9'>&nbsp;&nbsp;&nbsp;&nbsp;17 | Holgate/NW 21st        | 0.00924074516961275</div><div class='line' id='LC10'>&nbsp;&nbsp;&nbsp;&nbsp;10 | Harold St              |  0.0148568363291083</div><div class='line' id='LC11'>&nbsp;&nbsp;&nbsp;&nbsp;15 | Belmont/NW 23rd        |  0.0170180326549672</div><div class='line' id='LC12'>&nbsp;&nbsp;&nbsp;&nbsp;19 | Woodstock/Glisan       |   0.020370095002805</div><div class='line' id='LC13'>(10 rows)</div></pre></div>
          </div>

          <div class="gist-meta">
            <a href="https://gist.github.com/raw/1612154/54366ad6ed0c9e58c8709bea65d064d585bb3321/improved_results.txt" style="float:right;">view raw</a>
            <a href="https://gist.github.com/1612154#file_improved_results.txt" style="float:right;margin-right:10px;color:#666">improved_results.txt</a>
            <a href="https://gist.github.com/1612154">This Gist</a> brought to you by <a href="http://github.com">GitHub</a>.
          </div>
        </div>
</div>
</p>
<h2>putting it all together</h2>
<p>I&#8217;m happy with these results. The next step is to wrap it in a method and expose it on our backend. Then I&#8217;ll write a new Sencha touch view to request the data based on the coordinate returned from the geolocation api in the browser. Should be a snap.</p>
]]></content:encoded>
			<wfw:commentRss>http://iknuth.com/2012/01/making-seatmate-aware-of-locations-with-postgis/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>pdxhash and working with spatial data using sql</title>
		<link>http://iknuth.com/2010/06/validating-pdxhash-working-with-spatial-data-with-sq/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=validating-pdxhash-working-with-spatial-data-with-sq</link>
		<comments>http://iknuth.com/2010/06/validating-pdxhash-working-with-spatial-data-with-sq/#comments</comments>
		<pubDate>Wed, 02 Jun 2010 05:57:33 +0000</pubDate>
		<dc:creator>edwin</dc:creator>
				<category><![CDATA[technology]]></category>
		<category><![CDATA[geodatabase]]></category>
		<category><![CDATA[gis]]></category>
		<category><![CDATA[location based service]]></category>
		<category><![CDATA[pdxhash]]></category>
		<category><![CDATA[postgis]]></category>
		<category><![CDATA[postgresql]]></category>
		<category><![CDATA[spatial database]]></category>

		<guid isPermaLink="false">http://iknuth.com/?p=284</guid>
		<description><![CDATA[Overview After analyzing the data, it would appear that we can use the geohash algorithm to geographically locate things in the Portland area by a short combination of letters and numbers. Because we only care about locations in and around &#8230; <a href="http://iknuth.com/2010/06/validating-pdxhash-working-with-spatial-data-with-sq/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<img style='float: left; margin-right: 10px; border: none;' src='http://www.gravatar.com/avatar.php?gravatar_id=9e27ba264dccbe5149fd08b17f5bdeb1&amp;default=http://use.perl.org/images/pix.gif' alt='No Gravatar' width=40 height=40/><p><a href="http://iknuth.com/wp-content/uploads/2010/06/Screen-shot-2010-06-01-at-10.49.13-PM.png"><img class="aligncenter size-full wp-image-305" title="Screen shot 2010-06-01 at 10.49.13 PM" src="http://iknuth.com/wp-content/uploads/2010/06/Screen-shot-2010-06-01-at-10.49.13-PM.png" alt="" width="882" height="374" /></a></p>
<h3>Overview</h3>
<p>After analyzing the data, it would appear that we can use the geohash algorithm to geographically locate things in the Portland area by a short combination of letters and numbers. Because we only care about locations in and around Portland, we can throw away the majority of the geohash.  What is left of this string can be used to geotag locations and should fulfill that requirement of the #pdxtags civicapps challenge idea.  It would appear that we can identify areas in Portland  as small as individual houses with just 5 characters, like 06ytd or 03qrf.</p>
<p>The interesting thing about this approach is that it eliminates the need for PostGIS or other spatial database when building applications with the civicapps data.  Using a pdxhash to store locations would allow a programmer to do fast geographic lookups and basic spatial queries in Portland area, without a geodatabase or GIS software.  It would also allow the use of non traditional application servers like the Google App Engine or NOSQL databases like Mongo or CouchDB.</p>
<h3>Massaging the Data</h3>
<p>Now that we have the Portland metro area address points loaded into a postgis table, we can begin working with the data.  We will be doing gis operations at the SQL level.  People commonly use ArcGIS Desktop to do analysis and data management tasks like these, but the open source tools work just as well.  There are more GUI based open source methods to do what we are doing, but if you are interested in automating tasks and scripting, the command line rules.</p>
<p>The table schema in postgis matches the csv file which we loaded with ogr2ogr.  The import added a column named wkb_geometry that contains the point in postgis&#8217; binary representation of a coordinate.  The table contains 316,133 records that look a little like this:<br />
<code lang="sql"><br />
select wkb_geometry, street_name, zip_code from address_data limit 10;<br />
wkb_geometry                    | street_name | zip_code<br />
0101000020E6100000D1C25823EAC262C045CEAEC8C8C04340 | LAMBERT     |<br />
0101000020E6100000D1C25823EAC262C045CEAEC8C8C04340 | ASHBY       | 97229<br />
0101000020E6100000D1C25823EAC262C045CEAEC8C8C04340 | 2ND         | 97080<br />
0101000020E6100000D1C25823EAC262C045CEAEC8C8C04340 | CERVANTES   | 97035<br />
0101000020E6100000D1C25823EAC262C045CEAEC8C8C04340 | 40TH        | 97123<br />
0101000020E6100000D1C25823EAC262C045CEAEC8C8C04340 | PARK        | 97201<br />
0101000020E6100000D1C25823EAC262C045CEAEC8C8C04340 | DOLPH       | 97219<br />
0101000020E6100000D1C25823EAC262C045CEAEC8C8C04340 | 15TH        | 97030<br />
0101000020E6100000D1C25823EAC262C045CEAEC8C8C04340 | 48TH        | 97213<br />
0101000020E6100000D1C25823EAC262C045CEAEC8C8C04340 | 182ND       | 97233<br />
(10 rows)<br />
</code></p>
<h3>Putting the Data on a Map</h3>
<p>If we fire up qgis and add this postgis layer, we can view all of the points on a map.  We can symbolize the points based on the attributes in the table.  For example we could make points have different colors based on their county.  I&#8217;d like to visually differentiate the points based on their #pdxhash, which is a masked version of a geohash.  Unfortunately the querybuilder in qgis doesn&#8217;t allow you to get too fancy with sql functions, so we have to create a view.</p>
<p>A view is a little like a layer definition in ArcGIS.  It allows us to create a virtual table that hides our more complicated queries behind what looks like a simple table.  In our psql or pgadmin window, we can define the following view.<br />
<code lang="sql"><br />
create view pdx_hash_address_view as<br />
select substring(trim(ST_Geohash(wkb_geometry)) from 0 for 5) as pdx_hash_4,                                         substring(trim(ST_Geohash(wkb_geometry)) from 0 for 6) as pdx_hash_5,                                       substring(trim(ST_Geohash(wkb_geometry)) from 0 for 7) as pdx_hash_6,                                       substring(trim(ST_Geohash(wkb_geometry)) from 0 for 8) as pdx_hash_7,                                       wkb_geometry, ogc_fid<br />
from address_data;<br />
</code><br />
<a href="http://iknuth.com/wp-content/uploads/2010/06/Screen-shot-2010-06-01-at-10.39.21-AM.png"><img class="alignright size-medium wp-image-289" title="add postgis layer screenshot" src="http://iknuth.com/wp-content/uploads/2010/06/Screen-shot-2010-06-01-at-10.39.21-AM-300x201.png" alt="qgis postgis layers" width="300" height="201" /></a>We need the wkb_geometry and ogc_fid data to provide our actually features and a primary key to keep things sorted out.  Now we can go to postgis and pull up this layer just like we did with our original table.  However it becomes obvious that the view is much slower than our original table.  When we select our view we are doing multiple complicated operations on each row in our table.  Fortunately postgresql offers us a way to easily &#8220;materialize&#8221; this view into it&#8217;s own table.<br />
<code lang="sql"><br />
select * into pdx_hash_address_data_table<br />
from pdx_hash_address_view;<br />
create INDEX pdx_hash__address_data_table_geom_idx on pdx_hash_address_data_table using gist (wkb_geometry);<br />
</code></p>
<h3>Creating New Layers</h3>
<p>When we use SELECT INTO, our new table is created automatically from the schema of our old table or view.  Now instead of complicated function calls, our pdx_hash columns are  simple text fields and our query is much faster.  We also need to add a spatial index to this new table, so our operations on the points are speedy as well.</p>
<p><a href="http://iknuth.com/wp-content/uploads/2010/06/Screen-shot-2010-06-01-at-11.06.26-AM.png"><img class="alignleft size-medium wp-image-290" title="Screen shot 2010-06-01 at 11.06.26 AM" src="http://iknuth.com/wp-content/uploads/2010/06/Screen-shot-2010-06-01-at-11.06.26-AM-300x148.png" alt="" width="300" height="148" /></a>Now when we bring this layer into qgis, we can see the pdxhash attributes we have added and it is fast enough.  We can symbolize the layer based on the pdx_hash_4 field.  Immediately we can see that the main part of Portland is really represented by four of the pdx_hash_4 classes.  <a href="http://iknuth.com/wp-content/uploads/2010/06/Screen-shot-2010-06-01-at-11.08.34-AM.png"><img class="aligncenter size-medium wp-image-292" title="Screen shot 2010-06-01 at 11.08.34 AM" src="http://iknuth.com/wp-content/uploads/2010/06/Screen-shot-2010-06-01-at-11.08.34-AM-300x84.png" alt="pdx_hash_4" width="300" height="84" /></a><br />
If we symbolize the data based on 108 pdx_hash_5 classes, we get a more interesting picture.  Unfortunately when dealing with all of these points, it gets a little jumbled.  However the map seems to validate using a 5 character pdxhash to break down the Portland area.<br />
<a href="http://iknuth.com/wp-content/uploads/2010/06/Screen-shot-2010-06-01-at-11.13.10-AM.png"><img class="size-medium wp-image-294 alignright" title="Screen shot 2010-06-01 at 11.13.10 AM" src="http://iknuth.com/wp-content/uploads/2010/06/Screen-shot-2010-06-01-at-11.13.10-AM-300x181.png" alt="pdx_hash_5" width="300" height="181" /></a><br />
It would be nice if we could convert our points into polygons that represent each pdx_hash zone.  It would make our map a little more visually effective.  Right now it is hard to see the forest for the trees.</p>
<h3>Common Table Expressions and Spatial Aggregates</h3>
<p>One problem with working with a large dataset is that some operation can take a long time, especially when you make mistakes.  In order to convert our point fields into polygons, we have to use postgis aggregate functions which behave like standard sql aggregates like max, sum, average only for geographic features.</p>
<p>It would be nice to work on just a subset of our data while we get our statement correct.   We could create a new table with a limited subset of the address data records, but more recent sql implementations support something called a common table expression which will do that for us.</p>
<h3>Creating Polygons and exporting Shapefiles</h3>
<p>Using the WITH statement, we create a virtual table with only 10,000 records on which to perform our query.  This is much faster for some operations than working on our entire dataset.  When the query is solid, you just remove the &#8220;LIMIT 10000&#8243; from the common table expression and you are ready to go.  As it turns out postgis can generate polygons from our point fields very quickly when the query is correct.<br />
<code lang="sql"><br />
with pdx_hash as (select * from pdx_hash_address_data_table limit 10000)<br />
select pdx_hash_5, ST_Envelope(ST_Collect(wkb_geometry)) as geom, count(*)                                                                               into pdx_hash_polygons from pdx_hash group by pdx_hash_5;<br />
</code><br />
This query takes all the points which have the same pdx_hash_5 field and returns them on a single row using ST_Collect.  The ST_Envelope function creates a polygon from the edge of the field of points.  As you can see our, polygons enclose the regions defined by the pdxhash points in our previous queries.  It also would appear that all these blocks begin with a pdx_hash of c2, which means that we can uniquely identify each zone with just three characters.<br />
I&#8217;ve made some shapefiles for you to play with.  The highest precision shapefile reveals the limitations of basing the polygons off of the address data.  However, I don&#8217;t beleive it invalidates the concept.  I created the shapefiles using ogr2ogr.   I did this example on a mac with postgis/postgresql, but you could execute basically the same queries with Microsoft SQL Server 2008.<br />
<code lang="bash"><br />
ogr2ogr -skipfailures -nlt POLYGON  -f "ESRI Shapefile" pdx_hash_polygon PG:"host=localhost dbname=civicapps" pdx_hash_polygon<br />
</code></p>
<p><a href="http://iknuth.com/files/pdx_hash_polygon.zip">pdx_hash_polygon.zip</a></p>
<p style="text-align: center;"><a href="http://iknuth.com/files/pdx_hash_polygon.zip"></a><br />
<a href="http://iknuth.com/wp-content/uploads/2010/06/Screen-shot-2010-06-01-at-10.36.07-PM.png"><img class="aligncenter size-full wp-image-304" title="Screen shot 2010-06-01 at 10.36.07 PM" src="http://iknuth.com/wp-content/uploads/2010/06/Screen-shot-2010-06-01-at-10.36.07-PM.png" alt="" width="399" height="273" /></a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://iknuth.com/2010/06/validating-pdxhash-working-with-spatial-data-with-sq/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>bulk loading shapefiles into postgis</title>
		<link>http://iknuth.com/2010/05/bulk-loading-shapefiles-into-postgis/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=bulk-loading-shapefiles-into-postgis</link>
		<comments>http://iknuth.com/2010/05/bulk-loading-shapefiles-into-postgis/#comments</comments>
		<pubDate>Fri, 28 May 2010 21:29:04 +0000</pubDate>
		<dc:creator>edwin</dc:creator>
				<category><![CDATA[technology]]></category>
		<category><![CDATA[civicapps]]></category>
		<category><![CDATA[geodatabase]]></category>
		<category><![CDATA[gis]]></category>
		<category><![CDATA[postgis]]></category>
		<category><![CDATA[postgresql]]></category>
		<category><![CDATA[spatial database]]></category>

		<guid isPermaLink="false">http://iknuth.com/?p=212</guid>
		<description><![CDATA[Civicapps datasets are made available in a format called a shapefile. ESRI shapefiles are the de facto standard for distributing spatial data, but they can be tricky for those who are new to GIS.  Fortunately shapefiles are an open standard with a variety of useful tools available. <a href="http://iknuth.com/2010/05/bulk-loading-shapefiles-into-postgis/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<img style='float: left; margin-right: 10px; border: none;' src='http://www.gravatar.com/avatar.php?gravatar_id=9e27ba264dccbe5149fd08b17f5bdeb1&amp;default=http://use.perl.org/images/pix.gif' alt='No Gravatar' width=40 height=40/><p><a href="http://civicapps.org"><img class="alignleft" src="http://civicapps.org/sites/civicapps.org/themes/civicapps/images/logo.png" alt="civicapps" width="246" height="84" />Civicapps</a> datasets are made available in a format called a shapefile.  ESRI shapefiles are the de facto standard for distributing spatial data, but they can be tricky for those who are new to GIS.  Fortunately shapefiles are an open standard with a variety of useful tools available.</p>
<h1>Shapefiles and Spatial Databases</h1>
<p>One approach to using a shapefile in an application is to load it into a relational database that supports spatial features like PostgreSQL/PostGIS, SQL Server 2008 (with or without ArcSDE), SQLite/Spatialite, Oracle and even MySQL.  These products are sometimes referred to as spatial or geodatabases.  Some implementations are more mature and useful than others.</p>
<p><img class="alignright" title="postgis" src="http://postgis.refractions.net/download/logo_suite/stock_elephant/stock_elephant_060.gif" alt="" width="58" height="86" />I&#8217;m using <a href="http://postgis.refractions.net/">PostGIS</a> for this example and the ogr tools which ship with the ubuntu gdal-bin package.  These tools are being developed as part of the <a href="http://www.opengeospatial.org/">Open Geospatial Consortium</a> and are capable of doing incredible things, especially when compared to the commercial offerings.</p>
<h1>Getting at the data</h1>
<p><a href="http://iknuth.com/wp-content/uploads/2010/05/bridges.png"><img class="alignleft size-medium wp-image-223" title="bridges" src="http://iknuth.com/wp-content/uploads/2010/05/bridges-300x227.png" alt="bridges" width="300" height="227" /></a>After you unzip the archive you are left with several different files including a .shp file, a .prj file and a .dbf file.  The geographic features are not stored as longitude and latitude, but a coordinate system specific to Northern Oregon called State Plane with units in Feet.  The projection information is in the .prj file.</p>
<h2>Spatial Transformations</h2>
<p>First we have to project the data from State Plane which is commonly used by state and local governments to the standard web mapping coordinate system, WGS1984.  This projection works very well with the google maps api and uses latitude and longitude.  We will employ a tool called ogr2ogr to do the reprojection.  It will create a new shapefile called Bridges_pdx_4326.shp in the WGS84 coordinate system which is designated by a spatial reference id of 4326.<br />
<code lang="bash"><br />
ogr2ogr -t_srs EPSG:4326 -a_srs EPSG:4326 -f "ESRI Shapefile" Bridges_pdx_4326.shp Bridges_pdx.shp<br />
</code></p>
<h2>Creating the Database</h2>
<p>Now, we need to create a postgis database and load it from the shapefile.  We&#8217;ll be using the command shp2pgsql which comes with postgis and psql the command line interface to postgres to do the dirty work.  It&#8217;s really not that bad and lends itself to being automated with a shell script.<br />
<code lang="bash"><br />
edwin@iknuth.com:~/civicapps$ createdb civicapps<br />
edwin@iknuth.com:~/civicapps$ createlang plpgsql civicapps<br />
edwin@iknuth.com:~/civicapps$ psql civicapps -f /usr/share/postgresql/8.4/contrib/postgis.sql<br />
edwin@iknuth.com:~/civicapps$ psql civicapps -f /usr/share/postgresql/8.4/contrib/spatial_ref_sys.sql<br />
</code></p>
<h2>Loading the Data</h2>
<p>Now we load our projected data into postgres.  We are using a few command line options to specify the name of the table as &#8220;bridges_pdx&#8221; and the name of the column storing the features as &#8220;the_geom&#8221;.  You can use whatever naming conventions you like.<br />
<code lang="bash"><br />
shp2pgsql -s 4326 -d -g the_geom Bridges_pdx_4326.shp bridges_pdx |psql civicapps<br />
</code></p>
<p>If you run the command without piping it to psql, you see that it outputs the sql necessary to create a table and insert the data to populate it.  This whole process is highly scriptable and can be used to bulk load many shapefiles at once.</p>
<h2>Working with PostGIS</h2>
<p>To look at the data, we can connect to the database and run some queries.  PostGIS includes many functions that implement gis operations in sql.  We can use this command to generate kml directly from our database.<br />
<code lang="sql"><br />
select ST_AsKML(the_geom) from bridges_pdx;<br />
</code></p>
<p>Most spatial databases implement at least some of the <a href="http://www.opengeospatial.org/standards/sfs">OpenGIS Simple Features Implementation Specification for SQL</a>.  Using these functions, we can make queries and relate tables at the database level based on the spatial location of the features using SQL or object-relational mapping.  This is a very powerful tool for developing GIS applications.</p>
<p>I hope that helps people get started with shapefiles and postgis.  I&#8217;ll be writing more about civicapps and working with spatial data in the future  I appreciate your interest.<br />
[ad]</p>
]]></content:encoded>
			<wfw:commentRss>http://iknuth.com/2010/05/bulk-loading-shapefiles-into-postgis/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>rubyrep and postgresql replication roundup</title>
		<link>http://iknuth.com/2010/05/rubyrep-and-postgresql-replication-roundup/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=rubyrep-and-postgresql-replication-roundup</link>
		<comments>http://iknuth.com/2010/05/rubyrep-and-postgresql-replication-roundup/#comments</comments>
		<pubDate>Wed, 19 May 2010 21:32:03 +0000</pubDate>
		<dc:creator>edwin</dc:creator>
				<category><![CDATA[technology]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[postgresql]]></category>
		<category><![CDATA[replication]]></category>
		<category><![CDATA[rubyrep]]></category>

		<guid isPermaLink="false">http://iknuth.com/?p=204</guid>
		<description><![CDATA[While replication might not be tightly integrated into postgresql at the moment, we are lucky to have a variety of tools.  Each one has different qualities and meets different needs. SlonyII is mature and easy enough to implement, but it &#8230; <a href="http://iknuth.com/2010/05/rubyrep-and-postgresql-replication-roundup/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<img style='float: left; margin-right: 10px; border: none;' src='http://www.gravatar.com/avatar.php?gravatar_id=9e27ba264dccbe5149fd08b17f5bdeb1&amp;default=http://use.perl.org/images/pix.gif' alt='No Gravatar' width=40 height=40/><p>While replication might not be tightly integrated into postgresql at the moment, we are lucky to have a variety of tools.  Each one has different qualities and meets different needs.</p>
<p>SlonyII is mature and easy enough to implement, but it does not support multi master replication.  Bucardo and rubyrep handle that requirement and definitely have different personalities.  Bucardo is written in perl and rubyrep uses ruby, so you might pick the language with which you are more comfortable.</p>
<p>I had been impressed by how easy it was to get rubyrep up and running, but for some reason I missed it&#8217;s ability to handle continuous replication using triggers.  Luckily rubyrep&#8217;s author Arndt Lehmann set me straight with a comment to my misguided post.  We are currently implementing rubyrep to replicate a very large django database and I have been very pleased with the results.</p>
<p>I have noticed one interesting quick with rubyrep that we did not see with bucardo.  I&#8217;m still analyzing the situation and will submit a bug report to rubyrep or postgres.  I am using the same database backup to load the two databases to different servers.  I noticed that rubyrep was finding a huge amount of record mismatches regarding time/data data.  The only that was different between the two servers was the timezone.  One was set to UTC and the other Pacific.  Fixing the tz mismatch and restarting postgres has taken care of the problem.</p>
<p>While this would a problem going into production, rubyrep actually was able to sync these records very easily.  It painless synced the hundreds of thousands of records without breaking a sweat.  I was very impressed.</p>
]]></content:encoded>
			<wfw:commentRss>http://iknuth.com/2010/05/rubyrep-and-postgresql-replication-roundup/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>deploying bucardo and solving a schema mismatch problem</title>
		<link>http://iknuth.com/2010/04/bucardo-in-the-real-world/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=bucardo-in-the-real-world</link>
		<comments>http://iknuth.com/2010/04/bucardo-in-the-real-world/#comments</comments>
		<pubDate>Wed, 28 Apr 2010 06:59:21 +0000</pubDate>
		<dc:creator>edwin</dc:creator>
				<category><![CDATA[technology]]></category>
		<category><![CDATA[bucardo]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[postgresql]]></category>
		<category><![CDATA[replication]]></category>

		<guid isPermaLink="false">http://iknuth.com/?p=155</guid>
		<description><![CDATA[In the process of deploying bucardo 4.4.0 to replicate a fairly large django database using postgresql 8.4, I ran into a tricky issue.  Although I knew for certain that the database schemas were identical, bucardo still complained about a mismatch in the default value for the first table's primary key column. <a href="http://iknuth.com/2010/04/bucardo-in-the-real-world/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<img style='float: left; margin-right: 10px; border: none;' src='http://www.gravatar.com/avatar.php?gravatar_id=9e27ba264dccbe5149fd08b17f5bdeb1&amp;default=http://use.perl.org/images/pix.gif' alt='No Gravatar' width=40 height=40/><p>In the process of deploying bucardo 4.4.0 to replicate a fairly large django database using postgresql 8.4, I ran into a tricky issue.  Although I knew for certain that the database schemas were identical, bucardo still complained about a mismatch in the default value for the first table&#8217;s primary key column.</p>
<p>For some reason bucardo was identifying the reference to the sequence with the &#8220;public&#8221; schema explicitly named on one database and without &#8220;public&#8221; on the other.  In this case, schema refers to a namespace and not the database layout sense of the word.  The goat validation was throwing the following error.<br />
<code lang="bash"><br />
...<br />
MCP Warning: Source database for sync "databaseA" has column "id" of table "public.auth_group" with a DEFAULT of "nextval('auth_group_id_seq'::regclass)", but target database "databaseB" has a DEFAULT of "nextval('public.auth_group_id_seq'::regclass)"<br />
...<br />
</code></p>
<p>I was sure that the databases were identical because they were both restored from the same backup file.  I did find that I was running 8.4.1 on one postgresql server and 8.4.3 on the other. I synced up the postgresql versions, but I did not track down any of the relevant driver versions, because I was able to find a simple work around.</p>
<p>I searched for the error string in the bucardo perl source code and found the problematic section.  Since I knew the tables were identical, I felt that skipping this part of the validation would be harmless, so I made the following change to Bucardo.pm<br />
<code lang="perl"><br />
2032     #if ($scol->{atthasdef} and $fcol->{atthasdef} and $scol->{def} ne $fcol->{def}) {<br />
2033     if (1 == 2) {<br />
</code></p>
<p>After I installed my updated bucardo, I kicked off replication again with a successful result.  </p>
]]></content:encoded>
			<wfw:commentRss>http://iknuth.com/2010/04/bucardo-in-the-real-world/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>displaying a google maps api v3 map in a django application with geodjango and postgis</title>
		<link>http://iknuth.com/2010/04/displaying-a-google-maps-api-v3-map-in-a-django-application-with-geodjango-and-postgis/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=displaying-a-google-maps-api-v3-map-in-a-django-application-with-geodjango-and-postgis</link>
		<comments>http://iknuth.com/2010/04/displaying-a-google-maps-api-v3-map-in-a-django-application-with-geodjango-and-postgis/#comments</comments>
		<pubDate>Tue, 20 Apr 2010 07:10:18 +0000</pubDate>
		<dc:creator>edwin</dc:creator>
				<category><![CDATA[technology]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[geodatabase]]></category>
		<category><![CDATA[geodjango]]></category>
		<category><![CDATA[gis]]></category>
		<category><![CDATA[google maps api]]></category>
		<category><![CDATA[location based service]]></category>
		<category><![CDATA[postgis]]></category>
		<category><![CDATA[postgresql]]></category>
		<category><![CDATA[spatial database]]></category>

		<guid isPermaLink="false">http://iknuth.com/?p=130</guid>
		<description><![CDATA[In order to create a Google Maps API v3 map with django templates and views you can use the python modules django.contrib.gis.maps and specifically django.contrib.gis.maps.google. <a href="http://iknuth.com/2010/04/displaying-a-google-maps-api-v3-map-in-a-django-application-with-geodjango-and-postgis/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<img style='float: left; margin-right: 10px; border: none;' src='http://www.gravatar.com/avatar.php?gravatar_id=9e27ba264dccbe5149fd08b17f5bdeb1&amp;default=http://use.perl.org/images/pix.gif' alt='No Gravatar' width=40 height=40/><p><a href="http://iknuth.com/wp-content/uploads/2010/04/Screen-shot-2010-04-20-at-1.01.39-AM.png"><img src="http://iknuth.com/wp-content/uploads/2010/04/Screen-shot-2010-04-20-at-1.01.39-AM.png" alt="" title="Screen shot 2010-04-20 at 1.01.39 AM" width="227" height="140" class="alignleft size-full wp-image-143" /></a>In order to create a <a href="http://code.google.com/apis/maps/documentation/v3/">Google Maps API v3</a> map with django templates and views you can use the python modules <a href="http://code.djangoproject.com/browser/django/trunk/django/contrib/gis/map">django.contrib.gis.maps</a> and specifically <a href="http://code.djangoproject.com/browser/django/trunk/django/contrib/gis/maps/google">django.contrib.gis.maps.google</a>.</p>
<p>We will create a view called show_map that takes a request object and a query string that we will use to retrieve polygons from postgis, our spatial database.</p>
<p>If we have a database of time zones and we would like to display a map with a specific time zone, we first need to select it from the database.  For this query, we are using the standard django ORM to pull out the desired polygon.  We have defined the Zone object in our app&#8217;s models.py file.</p>
<h3>querying the geodatabase using django ORM</h3>
<p><code lang="python"><br />
In[24]: results = Time_Zone.objects.filter(timezone_id=query_string)<br />
</code></p>
<p>timezone_id is the criteria we are using to select all objects where the time zone id matches the query string exactly.</p>
<p>The geometry or polygon shapes of the records that have been returned are available in the polygon field you have specified in the model definition.  Because we are using the filter method of Zone&#8217;s object manager, we can pull multiple records that match our search criteria.  We can get at the polygon data for each record using some properties that are available on the geometry columns.</p>
<h3>geojson</h3>
<p><code lang="python"><br />
In[24]:  print results[0].geom.geojson<br />
Out[24]:  '{ "type": "MultiPolygon", "coordinates": [ [ [ [ -6.091862, 4.991835 ], [ -6.089009, 4.990883 ], [ -6.079963, 4.988334 ], [ -6.074483, 4.985070 ], [ -6.071546, 4.979528 ], [ -6.072025, 4.977002 ], [ -6.076686, 4.968072 ], [ -6.083112, 4.967894 ], [ -6.100063, 4.971250 ], [ -6.103525, 4.968749 ], [ -6.107572, 4.978899 ], [ -6.113694, 4.987916 ], [ -6.113905, 4.990215 ], [ -6.112429, 4.991211 ], [ -6.105550, 4.991084 ], [ -6.099033, 4.994057 ], [ -6.091862, 4.991835 ] ] ] ] }'<br />
</code></p>
<h3>kml</h3>
<p><code lang="python"><br />
In [25]: results[0].geom.kml<br />
Out[25]: '</p>
<p>-6.09186172485,4.99183511734,0 -6.08900928497,4.99088335037,0 -6.07996273041,4.98833370209,0 -6.07448291779,4.98507022858,0 -6.07154560089,4.97952795029,0 -6.07202482224,4.97700214386,0 -6.07668590546,4.9680724144,0 -6.08311223984,4.9678940773,0 -6.10006284714,4.97125005722,0 -6.10352468491,4.96874904633,0 -6.1075720787,4.97889947891,0 -6.11369419098,4.98791646957,0 -6.113904953,4.99021482468,0 -6.11242866516,4.9912109375,0 -6.10554981232,4.99108362198,0 -6.09903335571,4.9940571785,0 -6.09186172485,4.99183511734,0'<br />
</code></p>
<h3>google maps api</h3>
<p>In order to display the polygons on our google map, we will need to pass them to our code written in javascript.  We will then use methods from the google maps api v3 to construct and display our polygons on the map.  We will also need a little more information about the polygons we will be displaying in order to adjust the center and zoom level of our map.</p>
<p>First we will need to instantiate a google zoom object.  There are probably easier ways to do this, perhaps subclassing the geodjango admin views, but this is quick and effective.  And then render our template with the appropriate context.<br />
<code lang="python"><br />
gz = GoogleZoom()<br />
return render_to_response('map.html', {<br />
'all_tz': all_tz,<br />
'zoom': gz.get_zoom(all_tz.unionagg())<br />
})<br />
</code></p>
<h3>template javascript</h3>
<p>To display our maps using google&#8217;s javascript mapping API, we need to initialize our map and the add our polygons to it.  We will iterate through our list of polygons use the gpoly method of our Time_Zone object to definte the points that make up each polygon.  We&#8217;ll talk about the gpoly method in a bit.<br />
<code lang="javascript"><br />
function initialize() {<br />
var latlng = new google.maps.LatLng({{ results.unionagg.centroid.y}},<br />
{{ results.unionagg.centroid.x}});<br />
var myOptions = {<br />
zoom: {{ zoom }},<br />
center: latlng,<br />
mapTypeId: google.maps.MapTypeId.TERRAIN<br />
};<br />
var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions)<br />
{% for result in results %}<br />
polygon = new google.maps.Polygon({<br />
paths: {{ result.gpoly }},<br />
strokeColor: "#FF0000",<br />
strokeOpacity: 0.8,<br />
strokeWeight: 2,<br />
fillColor: "#FF0000",<br />
fillOpacity: 0.35<br />
});<br />
polygon.setMap(map);<br />
{% endfor %}<br />
</code></p>
<h3>google.maps.Polygon</h3>
<p>A google.maps.Polygon object is slightly different from a geojson representation.  They are both a string containing a list of points that represent the vertices of the polygon.  The geojson polygon that is displayed above needs to be transformed into the following string to be interpretable by the google Polygon constructor.<br />
<code lang="python"><br />
'[new google.maps.LatLng(4.99183511734,-6.09186172485),new google.maps.LatLng(4.99088335037,-6.08900928497),new google.maps.LatLng(4.98833370209,-6.07996273041),new google.maps.LatLng(4.98507022858,-6.07448291779),new google.maps.LatLng(4.97952795029,-6.07154560089),new google.maps.LatLng(4.97700214386,-6.07202482224),new google.maps.LatLng(4.9680724144,-6.07668590546),new google.maps.LatLng(4.9678940773,-6.08311223984),new google.maps.LatLng(4.97125005722,-6.10006284714),new google.maps.LatLng(4.96874904633,-6.10352468491),new google.maps.LatLng(4.97889947891,-6.1075720787),new google.maps.LatLng(4.98791646957,-6.11369419098),new google.maps.LatLng(4.99021482468,-6.113904953),new google.maps.LatLng(4.9912109375,-6.11242866516),new google.maps.LatLng(4.99108362198,-6.10554981232),new google.maps.LatLng(4.9940571785,-6.09903335571),new google.maps.LatLng(4.99183511734,-6.09186172485)]'<br />
</code></p>
<p>To do that with django, I import the GPolygon object from django.contrib.gis.maps.google.overlays.  It takes a geodjango multipolygon object and returns a string which is acceptable to the google maps api polygon constructor paths argument.  I&#8217;ve added a gpoly method to my geodjango object that wraps the GPolygon object and returns the correct string.</p>
<h3>supporting google maps api v3</h3>
<p>Unfortunately, geodjango seems to be written with v2 of the google maps api in mind, at least in the current version.  In order to transfer the output of the GPolygon method, we simply do a replace in the string.  The gpoly method looks like the following.<br />
<code lang="python"><br />
def gpoly(self):<br />
gpoly = GPolygon(self.geom[0])<br />
return gpoly.points.replace('GLatLng', 'google.maps.LatLng')</code></p>
]]></content:encoded>
			<wfw:commentRss>http://iknuth.com/2010/04/displaying-a-google-maps-api-v3-map-in-a-django-application-with-geodjango-and-postgis/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>installing bucardo the hard way</title>
		<link>http://iknuth.com/2010/04/installing-bucardo-the-hard-way/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=installing-bucardo-the-hard-way</link>
		<comments>http://iknuth.com/2010/04/installing-bucardo-the-hard-way/#comments</comments>
		<pubDate>Thu, 01 Apr 2010 22:55:31 +0000</pubDate>
		<dc:creator>edwin</dc:creator>
				<category><![CDATA[technology]]></category>
		<category><![CDATA[bucardo]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[postgresql]]></category>
		<category><![CDATA[replication]]></category>
		<category><![CDATA[ubuntu]]></category>

		<guid isPermaLink="false">http://iknuth.com/?p=107</guid>
		<description><![CDATA[It would seem that the bucardo_ctl installer script for version 4.4.0 has some problems. I spent a little time trying to fix the script, but it was too much of a pain. I found a great write up by Richard &#8230; <a href="http://iknuth.com/2010/04/installing-bucardo-the-hard-way/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<img style='float: left; margin-right: 10px; border: none;' src='http://www.gravatar.com/avatar.php?gravatar_id=9e27ba264dccbe5149fd08b17f5bdeb1&amp;default=http://use.perl.org/images/pix.gif' alt='No Gravatar' width=40 height=40/><p><a href="http://iknuth.com/wp-content/uploads/2010/04/extinct-ibex-resurrected.jpg"><img class="alignleft size-medium wp-image-119" title="extinct-ibex-resurrected" src="http://iknuth.com/wp-content/uploads/2010/04/extinct-ibex-resurrected-300x187.jpg" alt="" width="300" height="187" /></a>It would seem that the bucardo_ctl installer script for version 4.4.0 has some problems.  I spent a little time trying to fix the script, but it was too much of a pain.</p>
<p>I found a great <a href="http://www.richardmartin.org/2009/01/bucardo-postgresql/">write up</a> by Richard Martin that describes getting bucardo running without bucardo_ctl.  I simplified his procedure to replicate the three tables from the pgbench data that have primary keys.   From there you should be able to adopt it for any database.  You can use bucardo to replicate tables that do not have primary keys, but primary keys are generally a good thing to have.</p>
<p>I did modify the bucardo.schema file to remove the account creation stuff.  The script tries to switch to the bucardo db as the bucardo user after it creates it and then can cause a problem with most default postgres installs that use ident auth.</p>
<p>Feel free to download my modified <a href="http://iknuth.com/files/mod_bucardo.schema">bucardo.schema</a>.  For this example, I have used the database names &#8216;db_left&#8217; and &#8216;db_right&#8217;.  They are both on localhost, so we have to worry less about getting across a network.</p>
<p>Be sure to use better passwords if you are setting this up for reals.  Please do things like use ssl/ssh tunnels.</p>
<p>The first step is to create the databases and populate them with pgbench data.  For ubuntu 9.10 with postgresql 8.4, pgbench is in /usr/lib/postgresql/8.4/bin/<br />
<code lang="bash"><br />
createdb db_left<br />
createdb db_right<br />
pgbench -i db_left<br />
pgbench -i db_right<br />
/usr/lib/postgresql/8.4/bin/pgbench -i db_left<br />
/usr/lib/postgresql/8.4/bin/pgbench -i db_right<br />
</code></p>
<p>Next, run the following script in psql or your postgresql client of choice.<br />
<code lang="sql"><br />
create user bucardo SUPERUSER;<br />
create database bucardo owner bucardo;<br />
alter user bucardo with password 'bucardo';</p>
<p>\connect db_left;<br />
create language plpgsql;</p>
<p>\connect db_right;<br />
create language plpgsql;</p>
<p>\connect bucardo;<br />
create language plpgsql;<br />
create language plperlu;<br />
\i mod_bucardo.schema<br />
INSERT INTO db(name, dbname, dbhost, dbuser, dbpass)<br />
VALUES ('left','db_left','127.0.0.1','bucardo', 'bucardo');<br />
INSERT INTO db(name, dbname, dbhost, dbuser, dbpass)<br />
VALUES ('right','db_right','127.0.0.1','bucardo', 'bucardo');<br />
INSERT INTO dbgroup(name) VALUES ('remote_dbs');<br />
INSERT INTO dbmap(db,dbgroup) VALUES ('left','remote_dbs');<br />
INSERT INTO dbmap(db,dbgroup) VALUES ('right','remote_dbs');</p>
<p>INSERT INTO goat(db, schemaname, tablename, standard_conflict) VALUES('left', 'public', 'pgbench_accounts', 'source');<br />
INSERT INTO goat(db, schemaname, tablename, standard_conflict) VALUES('left', 'public', 'pgbench_branches', 'source');<br />
INSERT INTO goat(db, schemaname, tablename, standard_conflict) VALUES('left', 'public', 'pgbench_tellers',  'source');</p>
<p>INSERT INTO herd(name) VALUES ('HERD_NAME');<br />
insert into herdmap(herd,goat) select 'HERD_NAME', id from goat;</p>
<p>INSERT INTO sync(name,source,targetdb,synctype,checktime) VALUES ('left','HERD_NAME','right','swap','10 minutes');</p>
<p>update sync set status='active' where name ='left';</p>
<p></code></p>
<p>This will set up basic replication between db_left and db_right.  To kick off the bucardo process, use the following command:<br />
<code lang="bash"><br />
sudo bucardo_ctl --dbuser='bucardo' --dbhost='localhost' --dbpass='bucardo' start "full_copy"<br />
</code><br />
And to check the status to make sure it is running:<br />
<code lang="bash"><br />
sudo bucardo_ctl --dbuser='bucardo' --dbhost='localhost' --dbpass='bucardo' status<br />
Days back: 3  User: bucardo  Database: bucardo  Host: localhost  PID of Bucardo MCP: 7771<br />
Name Type  State PID  Last_good Time  I/U/D Last_bad Time<br />
====+=====+=====+====+=========+=====+=====+========+====<br />
left| S   |idle |7775|4m27s    |0s   |0/0/0|unknown | </code></p>
<p>Sorry about the backticks in those last commands.  I think that is the code snippet wordpress plugin :(</p>
<p>To test the replication insert a record into one of the database and look for it to be replicated.<br />
<code lang="sql"><br />
\c db_left<br />
insert into pgbench_branches (bid, bbalance, filler) values (2, 10, 'test');<br />
\c db_right<br />
select * from pgbench_branches;<br />
insert into pgbench_branches (bid, bbalance, filler) values (3, 10, 'the other way');<br />
\c db_left<br />
select * from bgpench_branches;<br />
bid | bbalance |                                          filler<br />
-----+----------+------------------------------------------------------------------------------------------<br />
1 |        0 |<br />
2 |       10 | test<br />
3 |       10 | the other way<br />
(3 rows)</code></p>
<p>It may take a few seconds for the new record to show up.  There should also be a logfile in your current working directory that will have a lot of diagnostic info.</p>
<p>And that is multimaster replication.</p>
]]></content:encoded>
			<wfw:commentRss>http://iknuth.com/2010/04/installing-bucardo-the-hard-way/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>geocoding and spatial database queries using python with django and postgis</title>
		<link>http://iknuth.com/2010/03/geocoding-and-spatial-queries-using-python-with-django-and-postgis/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=geocoding-and-spatial-queries-using-python-with-django-and-postgis</link>
		<comments>http://iknuth.com/2010/03/geocoding-and-spatial-queries-using-python-with-django-and-postgis/#comments</comments>
		<pubDate>Mon, 29 Mar 2010 06:12:49 +0000</pubDate>
		<dc:creator>edwin</dc:creator>
				<category><![CDATA[technology]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[geodatabase]]></category>
		<category><![CDATA[geodjango]]></category>
		<category><![CDATA[gis]]></category>
		<category><![CDATA[google maps api]]></category>
		<category><![CDATA[location based service]]></category>
		<category><![CDATA[postgis]]></category>
		<category><![CDATA[postgresql]]></category>
		<category><![CDATA[spatial database]]></category>

		<guid isPermaLink="false">http://iknuth.com/?p=85</guid>
		<description><![CDATA[Python is an excellent language for developing GIS applications.  It&#8217;s very easy to create a web service that queries a spatial database and returns a result.  Sometimes that sort of thing is called a location based service.  They are common &#8230; <a href="http://iknuth.com/2010/03/geocoding-and-spatial-queries-using-python-with-django-and-postgis/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<img style='float: left; margin-right: 10px; border: none;' src='http://www.gravatar.com/avatar.php?gravatar_id=9e27ba264dccbe5149fd08b17f5bdeb1&amp;default=http://use.perl.org/images/pix.gif' alt='No Gravatar' width=40 height=40/><p>Python is an excellent language for developing GIS applications.  It&#8217;s very easy to create a web service that <a href="http://iknuth.com/wp-content/uploads/2010/03/stock_elephant_060.gif"><img class="alignright size-full wp-image-86" title="stock_elephant_060" src="http://iknuth.com/wp-content/uploads/2010/03/stock_elephant_060.gif" alt="postgis" width="100"  /></a>queries a spatial database and returns a result.  Sometimes that sort of thing is called a location based service.  They are common in mobile applications, including iphone apps.  For our purposes, latitude and longitude are just inputs to a function.</p>
<p>Geocoding is the act of turning street addresses into coordinates on a map.  A geocoding service takes an address or a more generic reference to streets and returns a latitude and longitude that can be plotted on a map.  Reverse geocoding takes a point and returns an address, but we won&#8217;t be going into that here.</p>
<p>Google provides an excellent geocoding service that has a flexible address matching algorithm and provides  accurate results.  It can be used in python by way of the<a href="http://code.google.com/p/geopy/"> geopy</a> module.  The module can also query other geocoding services like yahoo and microsoft, but google does a good job and works well with the <a href="http://code.google.com/apis/maps/">Google Maps API</a> and base layer.  The syntax to look up a location by the street address is simple.</p>
<p><a href="http://iknuth.com/wp-content/uploads/2010/03/stock_elephant_060.gif"></a>This example also includes using that geocoded address to look up the Portland neighborhood in which the point is located.  To do this we must have a <a href="http://geodjango.org/">geodjango</a> model loaded in a <a href="http://postgis.refractions.net/">postgis</a> or other spatial database or geodatabase as they are sometimes called.</p>
<p><code lang="python"><br />
from django.contrib.gis.geos import Point<br />
from models import Neighborhood<br />
from geopy import geocoders<br />
api_key='AAJJDJDJSJ....'</p>
<p>g = geocoders.Google(api_key)<br />
place, (lat, lon) = g.geocode(address)<br />
pnt = Point(lon,lat)<br />
n = Neighborhood.objects.get(poly__intersects=pnt)<br />
</code></p>
]]></content:encoded>
			<wfw:commentRss>http://iknuth.com/2010/03/geocoding-and-spatial-queries-using-python-with-django-and-postgis/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>installing bucardo for multi-master postgres database replication</title>
		<link>http://iknuth.com/2010/03/installing-bucardo-for-multi-master-postgres-database-replication/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=installing-bucardo-for-multi-master-postgres-database-replication</link>
		<comments>http://iknuth.com/2010/03/installing-bucardo-for-multi-master-postgres-database-replication/#comments</comments>
		<pubDate>Tue, 23 Mar 2010 22:37:21 +0000</pubDate>
		<dc:creator>edwin</dc:creator>
				<category><![CDATA[technology]]></category>
		<category><![CDATA[bucardo]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[postgresql]]></category>
		<category><![CDATA[replication]]></category>
		<category><![CDATA[rubyrep]]></category>
		<category><![CDATA[ubuntu]]></category>

		<guid isPermaLink="false">http://iknuth.com/?p=43</guid>
		<description><![CDATA[In my quest for postgres replication/clustering nirvana I&#8217;ve played with pgpool, slony, rubyrep and now bucardo. Our requirements are painless bidirectional synchronization between two postgresql databases that serve a django application. Bucardo seems awesome, but there are some problems with &#8230; <a href="http://iknuth.com/2010/03/installing-bucardo-for-multi-master-postgres-database-replication/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<img style='float: left; margin-right: 10px; border: none;' src='http://www.gravatar.com/avatar.php?gravatar_id=9e27ba264dccbe5149fd08b17f5bdeb1&amp;default=http://use.perl.org/images/pix.gif' alt='No Gravatar' width=40 height=40/><p>In my quest for postgres replication/clustering nirvana I&#8217;ve played with pgpool, slony, rubyrep and now bucardo. Our requirements are painless bidirectional synchronization between two postgresql databases that serve a django application.</p>
<p><a href="http://iknuth.com/wp-content/uploads/2010/03/ecologiablog_Ibex.jpg"><img class="size-full wp-image-80 alignright" style="border: 0px initial initial;" title="ecologiablog_Ibex" src="http://iknuth.com/wp-content/uploads/2010/03/ecologiablog_Ibex.jpg" alt="bucardo" width="432" height="324" /></a></p>
<p>Bucardo seems awesome, but there are some problems with the install scripts.  I hope to work them out in a future post. In the meantime here are some notes about installing bucardo on ubuntu 9.10.</p>
<p style="text-align: center;">
<p>Bucardo is written in perl and seems to be a little more complicated than rubyrep to get up and running. I&#8217;ll be testing it out on a virtualbox vm running ubuntu 9.10 karmic koala. This is postgres 8.4, for the record.</p>
<p>The first prerequisite for bucard is the DBIx::Safe perl module. Unfortunately there isn&#8217;t a package available in the default 9.10 repo&#8217;s, but it does look like it&#8217;s out for lucid.</p>
<p>I&#8217;m just going to install DBIx::Safe from source. It and bucardo require the DBD::Pg perl module, which is available from the ubuntu repo. I&#8217;ll install that with:<br />
<code lang="bash">sudo aptitude install libdbd-pg-perl</code><br />
The <a href="http://bucardo.org/wiki/Bucardo/Installation">bucardo directions</a> have instructions for installing DBIx::Safe from source, so I&#8217;ll leave that up to them. The directions also state that DBIx::Sage is a rereq, but I couldn&#8217;t find that. The bucardo build did not complain about lacking that module, so I&#8217;m hoping we&#8217;re good to go.</p>
<p>After getting and building bucardo, the next step is to add the new functions to your database. Bucardo uses stored procedures written in perl to handle the replication. The instructions use yum to install this from a package on redhat/centos style servers. For ubuntu, we&#8217;ll do:<br />
<code lang="bash">sudo aptitude install postgresql-plperl-8.4</code><br />
The next step is run the bucardo_ctl install script. It prompts you for database settings, which is kind of a hassle. rubyrep was much more straightforward. It also creates a bucardo superuser on the database. The script prompts you for the the password for this user, but there doesn&#8217;t seem to be a step to set the password. I opened up a psql session and ran the following:<br />
<code lang="sql">alter user bucardo with password 'CHGME';</code><br />
Definitely change that password to something a little more secure. While you are at it, make sure you are being secure with your db connections. You don&#8217;t want port 5432 open to the world, and please use SSL.</p>
<p>The bucardo_ctl script gave me an error about not having the plperl language available. I tried various things and got tired of running the script. I installed the bucardo functions to the database with the following command:<br />
<code lang="bash">psql DBNAME -h localhost -f /usr/local/share/bucardo/bucardo.schema</code><br />
The next step is to add the database to be replicated to bucardo. bucardo_ctl had a little trouble authenticating. I needed to specify the hostname. You can do it like this:<br />
<code lang="bash">bucardo_ctl --dbhost=localhost --dbpass=BUCARDO_PASSWORD add database DBNAME</code><br />
For some reason bucardo had a lot of trouble installing on Ubuntu. I kept getting an error about missing plperlu, even though the package was installed on the server and I could manually add the language to a database.</p>
<p>There seem to be a few errors in the bucardo_ctl perl script. I had to make two changes to get it to work. You need to comment the exit command after the bucardo_schema file is installed, and a secret hardcoded db password at the end of the install:<br />
<code lang="perl">5465 if ($res !~ m{Pl/PerlU was successfully installed}) {</code></p>
<p><code lang="perl">5466 warn "\nINSTALLATION FAILED! ($res)\n\n";</p>
<p>5467 warn "Installation cannot proceed unless the Pl/PerlU language is available\n";</p>
<p>5468 warn "This is usually available as a separate package\n";</p>
<p>5469 warn "For example, you might try: yum install postgresql-plperl\n";</p>
<p>5470 warn "If compiling from source, add the --with-perl option to your ./configure comma 5470 nd\n\n";</p>
<p>5471 # exit 1;</p>
<p>5472 }</p>
<p>.......</p>
<p>5481 $port and $BDSN .= ";port=$port";</p>
<p>5482 my $default_bucardo_password = 'replicate';</p>
<p></code></p>
<p><code lang="perl">5483 $dbh = DBI-&gt;connect($BDSN, 'bucardo', $default_bucardo_password, {AutoCommit=&gt;0,RaiseError=&gt; 5483 1,PrintError=&gt;0});</code><br />
Now, let us create a few databases, populate and then sync them with bucardo. We&#8217;ll be using pgbench to generate some test data.<br />
<code lang="bash">createdb db_left<br />
createdb db_right<br />
pgbench -i db_left<br />
pgbench -i db_right</code><br />
We&#8217;ll be starting with two identical databases, populated with some data. One of the tables does not have a primary key defined, but bucardo can handle that. I&#8217;m not really interested in that functionality, so I&#8217;m not going to worry about it here.</p>
<p>To add the databases to replication, execute the following commands:<br />
<code lang="bash">bucardo_ctl add database db_left --dbhost=localhost --dbpass=PASS<br />
bucardo_ctl add database db_right --dbhost=localhost --dbpass=PASS<br />
bucardo_ctl add all tables<br />
bucardo_ctl add all sequences<br />
</code></p>
]]></content:encoded>
			<wfw:commentRss>http://iknuth.com/2010/03/installing-bucardo-for-multi-master-postgres-database-replication/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>using rubyrep to replicate a postgresql database on ubuntu 9.10 (karmic koala)</title>
		<link>http://iknuth.com/2010/03/installing-rubyrep-for-multi-master-postgresql-replication/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=installing-rubyrep-for-multi-master-postgresql-replication</link>
		<comments>http://iknuth.com/2010/03/installing-rubyrep-for-multi-master-postgresql-replication/#comments</comments>
		<pubDate>Wed, 17 Mar 2010 22:03:37 +0000</pubDate>
		<dc:creator>edwin</dc:creator>
				<category><![CDATA[technology]]></category>
		<category><![CDATA[bucardo]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[postgresql]]></category>
		<category><![CDATA[rubyrep]]></category>

		<guid isPermaLink="false">http://iknuth.com/?p=34</guid>
		<description><![CDATA[Update: I was mistaken about rubyrep&#8217;s ability to support continuous replication.  See my new post for an update. For my current project, we need to replicate a postgresql 8.4 database between several sites.  We are looking at bucardo and rubyrep. &#8230; <a href="http://iknuth.com/2010/03/installing-rubyrep-for-multi-master-postgresql-replication/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<img style='float: left; margin-right: 10px; border: none;' src='http://www.gravatar.com/avatar.php?gravatar_id=9e27ba264dccbe5149fd08b17f5bdeb1&amp;default=http://use.perl.org/images/pix.gif' alt='No Gravatar' width=40 height=40/><p><a href="http://www.rubyrep.org"><img class="alignleft" title="rubyrep" src="http://iknuth.com/images/rubyrep.png" alt="rubyrep" width="107" height="79" /></a> <strong>Update: </strong>I was mistaken about rubyrep&#8217;s ability to support continuous replication.  <a href="http://iknuth.com/2010/05/rubyrep-and-postgresql-replication-roundup/">See my new post for an update</a>.<strong> </strong></p>
<p><strong></strong>For my current project, we need to replicate a <a href="http://www.postgresql.org/">postgresql</a> 8.4 database between several sites.  We are looking at <a title="bucardo" href="http://bucardo.org/">bucardo</a> and <a href="http://www.rubyrep.org">rubyrep</a>.  Both projects support a multiple master architecture.  I&#8217;m going to give rubyrep a shot, and then try bucardo.  I&#8217;ll be using <a href="http://ubuntu.com">ubuntu</a> 9.10 karmic koala, but we&#8217;ll be using debian in production.  We&#8217;ll be replicating a fairly large <a href="http://www.djangoproject.com/">django</a> database between two sites.</p>
<h3>Slony-I</h3>
<p><a href="http://www.slony.info/">Slony</a> isn&#8217;t the only technology out there for doing replication of a postgres database.  Looks like two relatively new projects are being developed  that might be better for certain instances.  We couldn&#8217;t use slony because it is does not support bidirectional synchronization.  Slony only allows you to have a single master and multiple slaves.  For our purposes we have to have multiple masters.</p>
<h3>Setup</h3>
<p>rubyrep is obviously written in ruby, and you can choose from standard and jruby version.  The java ruby variant is supposed to be a bit faster, but I just want to see how it works.  To test, I&#8217;ll be installing it on for two databases on a single server.<br />
Assuming you have ruby installed, you can install rubyrep with the following command:</p>
<blockquote><p>sudo gem install rubyrep</p></blockquote>
<p>This installed version 1.1.1 of rubyrep.  The next step is to generate a stub config file using the rubyrep command.  The <a href="http://www.rubyrep.org/scanning.html">tutorial</a> does not specify the path, and gem didn&#8217;t add it to the default path.  For my setup the rubyrep script can be called like:</p>
<blockquote><p>/var/lib/gems/1.8/bin/rubyrep generate bh_rubyrep.conf</p></blockquote>
<p>This will generate a sample config file that you can edit to specify your database and server settings.  My databases are both on localhost for this test.  I uncommented the following to specify syncing all tables:</p>
<blockquote>
<div id="_mcePaste">config.include_tables /./ # regexp matching all tables in the database</div>
</blockquote>
<div>The /./ is just a regex to match table names.</div>
<h3>Execution</h3>
<p>The first step is to do a scan of the databases:</p>
<blockquote>
<div>/var/lib/gems/1.8/bin/rubyrep scan -c bh_rubyrep.conf</div>
</blockquote>
<div>I haven&#8217;t created the right hand database, so I got a fatal error stating it didn&#8217;t exist.  I created the second database and ran the command again, but it gave no output.  It should list all the tables with a count of mismatched records.  Unfortunately my right hand database is empty.  I dumped the left hand database and restored to the the right.</div>
<div>I ran the scan again and got a nice list of tables with zero mismatches.  We are going to be replicating the database of a relatively large postgres application.  With both databases running under a virtualbox vm on a macbook pro, it took 1 min 24 seconds to scan an 18mb database.  All tables have primary keys defined, which helps our cause.</div>
<blockquote>
<div>
<div>eknuth@eknuth-vubuntu:~/rubyrep$ time /var/lib/gems/1.8/bin/rubyrep scan -c bh_rubyrep.conf</div>
<div>purchasing_purchaseancillary 100% &#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;.   0</div>
<div>orders_orderline 100% &#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;.   0</div>
<div>orders_orderstatus 100% &#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;.   0</div>
<div>&#8230;</div>
<div>
<div>auth_user 100% &#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;.   0</div>
<div>navigation_pinnedtab 100% &#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;.   0</div>
<div>navigation_tab 100% &#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;.   0</div>
<div>real<span style="white-space: pre;"> </span>1m23.323s</div>
<div>user<span style="white-space: pre;"> </span>1m4.708s</div>
<div>sys<span style="white-space: pre;"> </span>0m8.533s</div>
</div>
</div>
</blockquote>
<div>To test the syncronization, I deleted all the records from one of the tables and ran the sync command:</div>
<blockquote>
<div>time /var/lib/gems/1.8/bin/rubyrep sync -c bh_rubyrep.conf</div>
</blockquote>
<div>rubyrep picked up the missing 8000 records and recreated them in the rh database.  Very slick and it did not take measurably longer than the scan.</div>
<h3>Conclusions</h3>
<p>I&#8217;m very impressed with the ease of setup.  It really was incredibly painless, in keeping with rubyrep&#8217;s slogan.  There are more complicated sync and conflict resolution rules you can set up, but the basic config was done very quickly.</p>
<div>Unlike slony, which uses triggers to sync the data, rubyrep would be the sort of thing you run periodically out of a cron job.  I&#8217;m not sure if that will work for us.</div>
<div>rubyrep seems like an excellent way to do postgres replication.  It would also allow you to replicate data between mysql and postgres, which is kinda cool.</div>
<div>I also think that rubyrep is probably one of the best postgres data comparison tools available.  It seems well suited for that type of use.</div>
]]></content:encoded>
			<wfw:commentRss>http://iknuth.com/2010/03/installing-rubyrep-for-multi-master-postgresql-replication/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>

