Get trips for stops that haven't *finished* not those that haven't started.
Get trips for stops that haven't *finished* not those that haven't started.

file:a/about.php -> file:b/about.php
<?php <?php
include ('include/common.inc.php'); include ('include/common.inc.php');
include_header("About", "about") include_header("About", "about")
?> ?>
<p> <p>
Busness Time - An ACT bus timetable webapp<br /> Busness Time - An ACT bus timetable webapp<br />
Based on the maxious-canberra-transit-feed (<a Based on the maxious-canberra-transit-feed (<a
href="http://s3-ap-southeast-1.amazonaws.com/busresources/cbrfeed.zip">download</a>, href="http://s3-ap-southeast-1.amazonaws.com/busresources/cbrfeed.zip">download</a>,
last updated <?php last updated <?php
echo date("F d Y.", @filemtime('cbrfeed.zip')); ?>)<br /> echo date("F d Y.", @filemtime('cbrfeed.zip')); ?>)<br />
Source code for the <a Source code for the <a
href="https://github.com/maxious/ACTBus-data">transit href="https://github.com/maxious/ACTBus-data">transit
feed</a> and <a href="https://github.com/maxious/ACTBus-ui">this feed</a> and <a href="https://github.com/maxious/ACTBus-ui">this
site</a> available from github.<br /> site</a> available from github.<br />
Uses jQuery Mobile, PHP, Ruby, Python, Google Transit Feed Specification tools, OpenTripPlanner, OpenLayers, OpenStreetMap, Cloudmade Geocoder and Tile Service<br /> Uses jQuery Mobile, PHP, PostgreSQL, OpenTripPlanner, OpenLayers, OpenStreetMap, Cloudmade Geocoder and Tile Service<br />
<br /> <br />
Feedback encouraged; contact maxious@lambdacomplex.org<br /> Feedback encouraged; contact maxious@lambdacomplex.org<br />
<br /> <br />
Some icons by Joseph Wain / glyphish.com<br /> Some icons by Joseph Wain / glyphish.com<br />
<br /> <br />
<small>Disclaimer: The content of this website is of a general and informative nature. Please check with printed timetables or those available on http://action.act.gov.au before your trip. <small>Disclaimer: The content of this website is of a general and informative nature. Please check with printed timetables or those available on http://action.act.gov.au before your trip.
Whilst every effort has been made to ensure the high quality and accuracy of the Site, the Author makes no warranty, Whilst every effort has been made to ensure the high quality and accuracy of the Site, the Author makes no warranty,
express or implied concerning the topicality, correctness, completeness or quality of the information, which is provided express or implied concerning the topicality, correctness, completeness or quality of the information, which is provided
"as is". The Author expressly disclaims all warranties, including but not limited to warranties of fitness for a particular purpose and warranties of merchantability. "as is". The Author expressly disclaims all warranties, including but not limited to warranties of fitness for a particular purpose and warranties of merchantability.
All offers are not binding and without obligation. The Author expressly reserves the right, in his discretion, to suspend, All offers are not binding and without obligation. The Author expressly reserves the right, in his discretion, to suspend,
change, modify, add or remove portions of the Site and to restrict or terminate the use and accessibility of the Site change, modify, add or remove portions of the Site and to restrict or terminate the use and accessibility of the Site
without prior notice. </small> without prior notice. </small>
<? <?
include_footer(); include_footer();
?> ?>
   
#!/bin/bash #!/bin/bash
#this script should be run from a fresh git checkout from http://maxious.lambdacomplex.org #this script should be run from a fresh git checkout from github
#ami base must have yum install lighttpd-fastcgi, git, tomcat6 #ami base must have yum install lighttpd-fastcgi, git, tomcat6
#screen php-cli php-gd tomcat6-webapps tomcat6-admin-webapps svn maven2 #php-cli php-gd tomcat6-webapps tomcat6-admin-webapps svn maven2
  #postgres postgres-server php-pg
#http://www.how2forge.org/installing-lighttpd-with-php5-and-mysql-support-on-fedora-12 #http://www.how2forge.org/installing-lighttpd-with-php5-and-mysql-support-on-fedora-12
   
cp /root/aws.php /tmp/ cp /root/aws.php /tmp/
mkdir /var/www/lib/staticmaplite/cache mkdir /var/www/lib/staticmaplite/cache
chcon -h system_u:object_r:httpd_sys_content_t /var/www chcon -h system_u:object_r:httpd_sys_content_t /var/www
chcon -R -h root:object_r:httpd_sys_content_t /var/www/* chcon -R -h root:object_r:httpd_sys_content_t /var/www/*
chcon -R -t httpd_sys_content_rw_t /var/www/lib/staticmaplite/cache chcon -R -t httpd_sys_content_rw_t /var/www/lib/staticmaplite/cache
chmod -R 777 /var/www/lib/staticmaplite/cache chmod -R 777 /var/www/lib/staticmaplite/cache
wget http://s3-ap-southeast-1.amazonaws.com/busresources/cbrfeed.zip \ wget http://s3-ap-southeast-1.amazonaws.com/busresources/cbrfeed.zip \
-O /var/www/cbrfeed.zip -O /var/www/cbrfeed.zip
easy_install transitfeed  
easy_install simplejson createdb transitdata
screen -S viewsh -X quit createlang -d transitdata plpgsql
screen -S viewsh -d -m /var/www/view.sh psql -d transitdata -f /var/www/lib/postgis.sql
  # curl https://github.com/maxious/ACTBus-ui/raw/master/transitdata.cbrfeed.sql.gz -o transitdata.cbrfeed.sql.gz
  #made with pg_dump transitdata | gzip -c > transitdata.cbrfeed.sql.gz
  gunzip /var/www/transitdata.cbrfeed.sql.gz
  psql -d transitdata -f /var/www/transitdata.cbrfeed.sql
  #createuser transitdata -SDRP
  #password transitdata
  #psql -d transitdata -c \"GRANT SELECT ON TABLE agency,calendar,calendar_dates,routes,stop_times,stops,trips TO transitdata;\"
  php /var/www/updatedb.php
   
wget http://s3-ap-southeast-1.amazonaws.com/busresources/Graph.obj \ wget http://s3-ap-southeast-1.amazonaws.com/busresources/Graph.obj \
-O /tmp/Graph.obj -O /tmp/Graph.obj
rm -rfv /usr/share/tomcat6/webapps/opentripplanner* rm -rfv /usr/share/tomcat6/webapps/opentripplanner*
wget http://s3-ap-southeast-1.amazonaws.com/busresources/opentripplanner-webapp.war \ wget http://s3-ap-southeast-1.amazonaws.com/busresources/opentripplanner-webapp.war \
-O /usr/share/tomcat6/webapps/opentripplanner-webapp.war -O /usr/share/tomcat6/webapps/opentripplanner-webapp.war
wget http://s3-ap-southeast-1.amazonaws.com/busresources/opentripplanner-api-webapp.war \ wget http://s3-ap-southeast-1.amazonaws.com/busresources/opentripplanner-api-webapp.war \
-O /usr/share/tomcat6/webapps/opentripplanner-api-webapp.war -O /usr/share/tomcat6/webapps/opentripplanner-api-webapp.war
/etc/init.d/tomcat6 restart /etc/init.d/tomcat6 restart
   
   
file:b/aws/pg_hba.conf (new)
  # PostgreSQL Client Authentication Configuration File
  # ===================================================
  #
  # Refer to the "Client Authentication" section in the
  # PostgreSQL documentation for a complete description
  # of this file. A short synopsis follows.
  #
  # This file controls: which hosts are allowed to connect, how clients
  # are authenticated, which PostgreSQL user names they can use, which
  # databases they can access. Records take one of these forms:
  #
  # local DATABASE USER METHOD [OPTIONS]
  # host DATABASE USER CIDR-ADDRESS METHOD [OPTIONS]
  # hostssl DATABASE USER CIDR-ADDRESS METHOD [OPTIONS]
  # hostnossl DATABASE USER CIDR-ADDRESS METHOD [OPTIONS]
  #
  # (The uppercase items must be replaced by actual values.)
  #
  # The first field is the connection type: "local" is a Unix-domain socket,
  # "host" is either a plain or SSL-encrypted TCP/IP socket, "hostssl" is an
  # SSL-encrypted TCP/IP socket, and "hostnossl" is a plain TCP/IP socket.
  #
  # DATABASE can be "all", "sameuser", "samerole", a database name, or
  # a comma-separated list thereof.
  #
  # USER can be "all", a user name, a group name prefixed with "+", or
  # a comma-separated list thereof. In both the DATABASE and USER fields
  # you can also write a file name prefixed with "@" to include names from
  # a separate file.
  #
  # CIDR-ADDRESS specifies the set of hosts the record matches.
  # It is made up of an IP address and a CIDR mask that is an integer
  # (between 0 and 32 (IPv4) or 128 (IPv6) inclusive) that specifies
  # the number of significant bits in the mask. Alternatively, you can write
  # an IP address and netmask in separate columns to specify the set of hosts.
  #
  # METHOD can be "trust", "reject", "md5", "password", "gss", "sspi", "krb5",
  # "ident", "pam", "ldap" or "cert". Note that "password" sends passwords
  # in clear text; "md5" is preferred since it sends encrypted passwords.
  #
  # OPTIONS are a set of options for the authentication in the format
  # NAME=VALUE. The available options depend on the different authentication
  # methods - refer to the "Client Authentication" section in the documentation
  # for a list of which options are available for which authentication methods.
  #
  # Database and user names containing spaces, commas, quotes and other special
  # characters must be quoted. Quoting one of the keywords "all", "sameuser" or
  # "samerole" makes the name lose its special character, and just match a
  # database or username with that name.
  #
  # This file is read on server startup and when the postmaster receives
  # a SIGHUP signal. If you edit the file on a running system, you have
  # to SIGHUP the postmaster for the changes to take effect. You can use
  # "pg_ctl reload" to do that.
 
  # Put your actual configuration here
  # ----------------------------------
  #
  # If you want to allow non-local connections, you need to add more
  # "host" records. In that case you will also need to make PostgreSQL listen
  # on a non-local interface via the listen_addresses configuration parameter,
  # or via the -i or -h command line switches.
  #
 
 
 
  # TYPE DATABASE USER CIDR-ADDRESS METHOD
 
  # "local" is for Unix domain socket connections only
  local all all trust
  # IPv4 local connections:
  host all all 127.0.0.1/32 trust
  # IPv6 local connections:
  host all all ::1/128 trust
 
 Binary files a/css/images/01-refresh.png and /dev/null differ
file:a/css/images/02-redo.png (deleted)
 Binary files a/css/images/02-redo.png and /dev/null differ
 Binary files a/css/images/06-magnify.png and /dev/null differ
 Binary files a/css/images/07-map-marker.png and /dev/null differ
 Binary files a/css/images/101-gameplan.png and /dev/null differ
file:a/css/images/102-walk.png (deleted)
 Binary files a/css/images/102-walk.png and /dev/null differ
file:a/css/images/103-map.png (deleted)
 Binary files a/css/images/103-map.png and /dev/null differ
 Binary files a/css/images/121-landscape.png and /dev/null differ
 Binary files a/css/images/13-target.png and /dev/null differ
 Binary files a/css/images/139-flags.png and /dev/null differ
 Binary files a/css/images/145-persondot.png and /dev/null differ
 Binary files a/css/images/184-warning.png and /dev/null differ
 Binary files a/css/images/193-location-arrow.png and /dev/null differ
file:a/css/images/28-star.png (deleted)
 Binary files a/css/images/28-star.png and /dev/null differ
file:a/css/images/53-house.png (deleted)
 Binary files a/css/images/53-house.png and /dev/null differ
 Binary files a/css/images/55-network.png and /dev/null differ
 Binary files a/css/images/57-download.png and /dev/null differ
 Binary files a/css/images/58-bookmark.png and /dev/null differ
file:a/css/images/59-flag.png (deleted)
 Binary files a/css/images/59-flag.png and /dev/null differ
 Binary files a/css/images/60-signpost.png and /dev/null differ
file:a/css/images/73-radar.png (deleted)
 Binary files a/css/images/73-radar.png and /dev/null differ
 Binary files a/css/images/74-location.png and /dev/null differ
 Binary files a/css/images/83-calendar.png and /dev/null differ
 Binary files /dev/null and b/css/images/91-beaker-2.png differ
  <?php
  if (php_uname('n') == "actbus-www") {
  $conn = pg_connect("dbname=transitdata user=transitdata password=transitdata host=db.actbus.dotcloud.com port=2242");
  } else if (isDebugServer()) {
  $conn = pg_connect("dbname=transitdata user=postgres password=snmc");
  } else {
  $conn = pg_connect("dbname=transitdata user=transitdata password=transitdata ");
  }
  if (!$conn) {
  die("A database error occurred.\n");
  }
 
  function databaseError($errMsg) {
  die($errMsg);
  }
 
  include('db/route-dao.inc.php');
  include('db/trip-dao.inc.php');
  include('db/stop-dao.inc.php');
  ?>
 
<?php <?php
// SELECT array_to_string(array(SELECT REPLACE(name_2006, ',', '\,') as name FROM suburbs order by name), ',') // SELECT array_to_string(array(SELECT REPLACE(name_2006, ',', '\,') as name FROM suburbs order by name), ',')
$suburbs = explode(",", "Acton,Ainslie,Amaroo,Aranda,Banks,Barton,Belconnen,Bonner,Bonython,Braddon,Bruce,Calwell,Campbell,Chapman,Charnwood,Chifley,Chisholm,City,Conder,Cook,Curtin,Deakin,Dickson,Downer,Duffy,Dunlop,Evatt,Fadden,Farrer,Fisher,Florey,Flynn,Forrest,Franklin,Fraser,Fyshwick,Garran,Gilmore,Giralang,Gordon,Gowrie,Greenway,Griffith,Gungahlin,Hackett,Hall,Harrison,Hawker,Higgins,Holder,Holt,Hughes,Hume,Isaacs,Isabella Plains,Kaleen,Kambah,Kingston,Latham,Lawson,Lyneham,Lyons,Macarthur,Macgregor,Macquarie,Mawson,McKellar,Melba,Mitchell,Monash,Narrabundah,Ngunnawal,Nicholls,Oaks Estate,O'Connor,O'Malley,Oxley,Page,Palmerston,Parkes,Pearce,Phillip,Pialligo,Red Hill,Reid,Richardson,Rivett,Russell,Scullin,Spence,Stirling,Symonston,Tharwa,Theodore,Torrens,Turner,Wanniassa,Waramanga,Watson,Weetangera,Weston,Yarralumla"); $suburbs = explode(",", "Acton,Ainslie,Amaroo,Aranda,Banks,Barton,Belconnen,Bonner,Bonython,Braddon,Bruce,Calwell,Campbell,Chapman,Charnwood,Chifley,Chisholm,City,Conder,Cook,Curtin,Deakin,Dickson,Downer,Duffy,Dunlop,Evatt,Fadden,Farrer,Fisher,Florey,Flynn,Forrest,Franklin,Fraser,Fyshwick,Garran,Gilmore,Giralang,Gordon,Gowrie,Greenway,Griffith,Gungahlin,Hackett,Hall,Harrison,Hawker,Higgins,Holder,Holt,Hughes,Hume,Isaacs,Isabella Plains,Kaleen,Kambah,Kingston,Latham,Lawson,Lyneham,Lyons,Macarthur,Macgregor,Macquarie,Mawson,McKellar,Melba,Mitchell,Monash,Narrabundah,Ngunnawal,Nicholls,Oaks Estate,O'Connor,O'Malley,Oxley,Page,Palmerston,Parkes,Pearce,Phillip,Pialligo,Red Hill,Reid,Richardson,Rivett,Russell,Scullin,Spence,Stirling,Symonston,Tharwa,Theodore,Torrens,Turner,Wanniassa,Waramanga,Watson,Weetangera,Weston,Yarralumla");
function staticmap($mapPoints, $zoom = 0, $markerImage = "iconb", $collapsible = true) function staticmap($mapPoints, $zoom = 0, $markerImage = "iconb", $collapsible = true)
{ {
$width = 300; $width = 300;
$height = 300; $height = 300;
$metersperpixel[9] = 305.492 * $width; $metersperpixel[9] = 305.492 * $width;
$metersperpixel[10] = 152.746 * $width; $metersperpixel[10] = 152.746 * $width;
$metersperpixel[11] = 76.373 * $width; $metersperpixel[11] = 76.373 * $width;
$metersperpixel[12] = 38.187 * $width; $metersperpixel[12] = 38.187 * $width;
$metersperpixel[13] = 19.093 * $width; $metersperpixel[13] = 19.093 * $width;
$metersperpixel[14] = 9.547 * $width; $metersperpixel[14] = 9.547 * $width;
$metersperpixel[15] = 4.773 * $width; $metersperpixel[15] = 4.773 * $width;
$metersperpixel[16] = 2.387 * $width; $metersperpixel[16] = 2.387 * $width;
// $metersperpixel[17]=1.193*$width; // $metersperpixel[17]=1.193*$width;
$center = ""; $center = "";
$markers = ""; $markers = "";
$minlat = 999; $minlat = 999;
$minlon = 999; $minlon = 999;
$maxlat = 0; $maxlat = 0;
$maxlon = 0; $maxlon = 0;
if (sizeof($mapPoints) < 1) return "map error"; if (sizeof($mapPoints) < 1) return "map error";
if (sizeof($mapPoints) === 1) { if (sizeof($mapPoints) === 1) {
if ($zoom == 0) $zoom = 14; if ($zoom == 0) $zoom = 14;
$markers.= "{$mapPoints[0][0]},{$mapPoints[0][1]},$markerimage"; $markers.= "{$mapPoints[0][0]},{$mapPoints[0][1]},$markerimage";
$center = "{$mapPoints[0][0]},{$mapPoints[0][1]}"; $center = "{$mapPoints[0][0]},{$mapPoints[0][1]}";
} }
else { else {
foreach ($mapPoints as $index => $mapPoint) { foreach ($mapPoints as $index => $mapPoint) {
$markers.= $mapPoint[0] . "," . $mapPoint[1] . "," . $markerImage . ($index + 1); $markers.= $mapPoint[0] . "," . $mapPoint[1] . "," . $markerImage . ($index + 1);
if ($index + 1 != sizeof($mapPoints)) $markers.= "|"; if ($index + 1 != sizeof($mapPoints)) $markers.= "|";
if ($mapPoint[0] < $minlat) $minlat = $mapPoint[0]; if ($mapPoint[0] < $minlat) $minlat = $mapPoint[0];
if ($mapPoint[0] > $maxlat) $maxlat = $mapPoint[0]; if ($mapPoint[0] > $maxlat) $maxlat = $mapPoint[0];
if ($mapPoint[1] < $minlon) $minlon = $mapPoint[1]; if ($mapPoint[1] < $minlon) $minlon = $mapPoint[1];
if ($mapPoint[1] > $maxlon) $maxlon = $mapPoint[1]; if ($mapPoint[1] > $maxlon) $maxlon = $mapPoint[1];
$totalLat+= $mapPoint[0]; $totalLat+= $mapPoint[0];
$totalLon+= $mapPoint[1]; $totalLon+= $mapPoint[1];
} }
if ($zoom == 0) { if ($zoom == 0) {
$mapwidthinmeters = distance($minlat, $minlon, $minlat, $maxlon); $mapwidthinmeters = distance($minlat, $minlon, $minlat, $maxlon);
foreach (array_reverse($metersperpixel, true) as $zoomLevel => $maxdistance) { foreach (array_reverse($metersperpixel, true) as $zoomLevel => $maxdistance) {
if ($zoom == 0 && $mapwidthinmeters < ($maxdistance + 50)) $zoom = $zoomLevel; if ($zoom == 0 && $mapwidthinmeters < ($maxdistance + 50)) $zoom = $zoomLevel;
} }
} }
$center = $totalLat / sizeof($mapPoints) . "," . $totalLon / sizeof($mapPoints); $center = $totalLat / sizeof($mapPoints) . "," . $totalLon / sizeof($mapPoints);
} }
$output = ""; $output = "";
if ($collapsible) $output.= '<div data-role="collapsible" data-collapsed="true"><h3>Open Map...</h3>'; if ($collapsible) $output.= '<div data-role="collapsible" data-collapsed="true"><h3>Open Map...</h3>';
$output.= '<center><img src="' . curPageURL() . '/lib/staticmaplite/staticmap.php?center=' . $center . '&zoom=' . $zoom . '&size=' . $width . 'x' . $height . '&markers=' . $output.= '<center><img src="' . curPageURL() . '/lib/staticmaplite/staticmap.php?center=' . $center . '&zoom=' . $zoom . '&size=' . $width . 'x' . $height . '&markers=' .
$markers . '" width=' . $width . ' height=' . $height . '></center>'; $markers . '" width=' . $width . ' height=' . $height . '></center>';
if ($collapsible) $output.= '</div>'; if ($collapsible) $output.= '</div>';
return $output; return $output;
} }
function distance($lat1, $lng1, $lat2, $lng2, $roundLargeValues = false) function distance($lat1, $lng1, $lat2, $lng2, $roundLargeValues = false)
{ {
$pi80 = M_PI / 180; $pi80 = M_PI / 180;
$lat1*= $pi80; $lat1*= $pi80;
$lng1*= $pi80; $lng1*= $pi80;
$lat2*= $pi80; $lat2*= $pi80;
$lng2*= $pi80; $lng2*= $pi80;
$r = 6372.797; // mean radius of Earth in km $r = 6372.797; // mean radius of Earth in km
$dlat = $lat2 - $lat1; $dlat = $lat2 - $lat1;
$dlng = $lng2 - $lng1; $dlng = $lng2 - $lng1;
$a = sin($dlat / 2) * sin($dlat / 2) + cos($lat1) * cos($lat2) * sin($dlng / 2) * sin($dlng / 2); $a = sin($dlat / 2) * sin($dlat / 2) + cos($lat1) * cos($lat2) * sin($dlng / 2) * sin($dlng / 2);
$c = 2 * atan2(sqrt($a) , sqrt(1 - $a)); $c = 2 * atan2(sqrt($a) , sqrt(1 - $a));
$km = $r * $c; $km = $r * $c;
if ($roundLargeValues) { if ($roundLargeValues) {
if ($km < 1) return floor($km * 1000); if ($km < 1) return floor($km * 1000);
else return round($km,2)."k"; else return round($km,2)."k";
} else return floor($km * 1000); } else return floor($km * 1000);
} }
   
function decodePolylineToArray($encoded) function decodePolylineToArray($encoded)
{ {
// source: http://latlongeeks.com/forum/viewtopic.php?f=4&t=5 // source: http://latlongeeks.com/forum/viewtopic.php?f=4&t=5
$length = strlen($encoded); $length = strlen($encoded);
$index = 0; $index = 0;
$points = array(); $points = array();
$lat = 0; $lat = 0;
$lng = 0; $lng = 0;
while ($index < $length) { while ($index < $length) {
// Temporary variable to hold each ASCII byte. // Temporary variable to hold each ASCII byte.
$b = 0; $b = 0;
// The encoded polyline consists of a latitude value followed by a // The encoded polyline consists of a latitude value followed by a
// longitude value. They should always come in pairs. Read the // longitude value. They should always come in pairs. Read the
// latitude value first. // latitude value first.
$shift = 0; $shift = 0;
$result = 0; $result = 0;
do { do {
// The `ord(substr($encoded, $index++))` statement returns the ASCII // The `ord(substr($encoded, $index++))` statement returns the ASCII
// code for the character at $index. Subtract 63 to get the original // code for the character at $index. Subtract 63 to get the original
// value. (63 was added to ensure proper ASCII characters are displayed // value. (63 was added to ensure proper ASCII characters are displayed
// in the encoded polyline string, which is `human` readable) // in the encoded polyline string, which is `human` readable)
$b = ord(substr($encoded, $index++)) - 63; $b = ord(substr($encoded, $index++)) - 63;
// AND the bits of the byte with 0x1f to get the original 5-bit `chunk. // AND the bits of the byte with 0x1f to get the original 5-bit `chunk.
// Then left shift the bits by the required amount, which increases // Then left shift the bits by the required amount, which increases
// by 5 bits each time. // by 5 bits each time.
// OR the value into $results, which sums up the individual 5-bit chunks // OR the value into $results, which sums up the individual 5-bit chunks
// into the original value. Since the 5-bit chunks were reversed in // into the original value. Since the 5-bit chunks were reversed in
// order during encoding, reading them in this way ensures proper // order during encoding, reading them in this way ensures proper
// summation. // summation.
$result|= ($b & 0x1f) << $shift; $result|= ($b & 0x1f) << $shift;
$shift+= 5; $shift+= 5;
} }
// Continue while the read byte is >= 0x20 since the last `chunk` // Continue while the read byte is >= 0x20 since the last `chunk`
// was not OR'd with 0x20 during the conversion process. (Signals the end) // was not OR'd with 0x20 during the conversion process. (Signals the end)
while ($b >= 0x20); while ($b >= 0x20);
// Check if negative, and convert. (All negative values have the last bit // Check if negative, and convert. (All negative values have the last bit
// set) // set)
$dlat = (($result & 1) ? ~($result >> 1) : ($result >> 1)); $dlat = (($result & 1) ? ~($result >> 1) : ($result >> 1));
// Compute actual latitude since value is offset from previous value. // Compute actual latitude since value is offset from previous value.
$lat+= $dlat; $lat+= $dlat;
// The next values will correspond to the longitude for this point. // The next values will correspond to the longitude for this point.
$shift = 0; $shift = 0;
$result = 0; $result = 0;
do { do {
$b = ord(substr($encoded, $index++)) - 63; $b = ord(substr($encoded, $index++)) - 63;
$result|= ($b & 0x1f) << $shift; $result|= ($b & 0x1f) << $shift;
$shift+= 5; $shift+= 5;
} while ($b >= 0x20); } while ($b >= 0x20);
$dlng = (($result & 1) ? ~($result >> 1) : ($result >> 1)); $dlng = (($result & 1) ? ~($result >> 1) : ($result >> 1));
$lng+= $dlng; $lng+= $dlng;
// The actual latitude and longitude values were multiplied by // The actual latitude and longitude values were multiplied by
// 1e5 before encoding so that they could be converted to a 32-bit // 1e5 before encoding so that they could be converted to a 32-bit
// integer representation. (With a decimal accuracy of 5 places) // integer representation. (With a decimal accuracy of 5 places)
// Convert back to original values. // Convert back to original values.
$points[] = array( $points[] = array(
$lat * 1e-5, $lat * 1e-5,
$lng * 1e-5 $lng * 1e-5
); );
} }
return $points; return $points;
} }
function geocode($query, $giveOptions) function geocode($query, $giveOptions)
{ {
global $cloudmadeAPIkey; global $cloudmadeAPIkey;
$url = "http://geocoding.cloudmade.com/$cloudmadeAPIkey/geocoding/v2/find.js?query=" . urlencode($query) . "&bbox=-35.5,149.00,-35.15,149.1930&return_location=true&bbox_only=true"; $url = "http://geocoding.cloudmade.com/$cloudmadeAPIkey/geocoding/v2/find.js?query=" . urlencode($query) . "&bbox=-35.5,149.00,-35.15,149.1930&return_location=true&bbox_only=true";
$contents = json_decode(getPage($url)); $contents = json_decode(getPage($url));
if ($giveOptions) return $contents->features; if ($giveOptions) return $contents->features;
elseif (isset($contents->features[0]->centroid)) return $contents->features[0]->centroid->coordinates[0] . "," . $contents->features[0]->centroid->coordinates[1]; elseif (isset($contents->features[0]->centroid)) return $contents->features[0]->centroid->coordinates[0] . "," . $contents->features[0]->centroid->coordinates[1];
else return ""; else return "";
} }
function reverseGeocode($lat, $lng) function reverseGeocode($lat, $lng)
{ {
global $cloudmadeAPIkey; global $cloudmadeAPIkey;
$url = "http://geocoding.cloudmade.com/$cloudmadeAPIkey/geocoding/v2/find.js?around=" . $lat . "," . $lng . "&distance=closest&object_type=road"; $url = "http://geocoding.cloudmade.com/$cloudmadeAPIkey/geocoding/v2/find.js?around=" . $lat . "," . $lng . "&distance=closest&object_type=road";
$contents = json_decode(getPage($url)); $contents = json_decode(getPage($url));
return $contents->features[0]->properties->name; return $contents->features[0]->properties->name;
} }
?> ?>
   
<?php <?php
// you have to open the session to be able to modify or remove it // you have to open the session to be able to modify or remove it
session_start(); session_start();
if (isset($_REQUEST['service_period'])) { if (isset($_REQUEST['service_period'])) {
$_SESSION['service_period'] = filter_var($_REQUEST['service_period'], FILTER_SANITIZE_STRING); $_SESSION['service_period'] = filter_var($_REQUEST['service_period'], FILTER_SANITIZE_STRING);
sessionUpdated(); sessionUpdated();
} }
if (isset($_REQUEST['time'])) { if (isset($_REQUEST['time'])) {
$_SESSION['time'] = filter_var($_REQUEST['time'], FILTER_SANITIZE_STRING); $_SESSION['time'] = filter_var($_REQUEST['time'], FILTER_SANITIZE_STRING);
sessionUpdated(); sessionUpdated();
} }
if (isset($_REQUEST['geolocate'])) { if (isset($_REQUEST['geolocate']) && $_REQUEST['geolocate'] != "Enter co-ordinates or address here") {
$geocoded = false; $geocoded = false;
if (isset($_REQUEST['lat']) && isset($_REQUEST['lon'])) { if (isset($_REQUEST['lat']) && isset($_REQUEST['lon'])) {
$_SESSION['lat'] = trim(filter_var($_REQUEST['lat'], FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION)); $_SESSION['lat'] = trim(filter_var($_REQUEST['lat'], FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION));
$_SESSION['lon'] = trim(filter_var($_REQUEST['lon'], FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION)); $_SESSION['lon'] = trim(filter_var($_REQUEST['lon'], FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION));
} }
else { else {
$geolocate = filter_var($_REQUEST['geolocate'], FILTER_SANITIZE_URL); $geolocate = filter_var($_REQUEST['geolocate'], FILTER_SANITIZE_URL);
echo $_REQUEST['geolocate'];  
if (startsWith($geolocate, "-")) { if (startsWith($geolocate, "-")) {
$locateparts = explode(",", $geolocate); $locateparts = explode(",", $geolocate);
$_SESSION['lat'] = $locateparts[0]; $_SESSION['lat'] = $locateparts[0];
$_SESSION['lon'] = $locateparts[1]; $_SESSION['lon'] = $locateparts[1];
} }
else { else {
$contents = geocode($geolocate, true); $contents = geocode($geolocate, true);
print_r($contents); print_r($contents);
if (isset($contents[0]->centroid)) { if (isset($contents[0]->centroid)) {
$geocoded = true; $geocoded = true;
$_SESSION['lat'] = $contents[0]->centroid->coordinates[0]; $_SESSION['lat'] = $contents[0]->centroid->coordinates[0];
$_SESSION['lon'] = $contents[0]->centroid->coordinates[1]; $_SESSION['lon'] = $contents[0]->centroid->coordinates[1];
} }
else { else {
$_SESSION['lat'] = ""; $_SESSION['lat'] = "";
$_SESSION['lon'] = ""; $_SESSION['lon'] = "";
} }
} }
} }
if ($_SESSION['lat'] != "" && isAnalyticsOn()) { if ($_SESSION['lat'] != "" && isAnalyticsOn()) {
trackEvent("Geolocation","Updated Location", "Geocoded - ".($geocoded ? "Yes" : "No")); trackEvent("Geolocation","Updated Location", "Geocoded - ".($geocoded ? "Yes" : "No"));
} }
sessionUpdated(); sessionUpdated();
} }
function sessionUpdated() { function sessionUpdated() {
$_SESSION['lastUpdated'] = time(); $_SESSION['lastUpdated'] = time();
} }
// timeoutSession // timeoutSession
$TIMEOUT_LIMIT = 60*5; // 5 minutes $TIMEOUT_LIMIT = 60*5; // 5 minutes
if (isset($_SESSION['lastUpdated']) && $_SESSION['lastUpdated']+$TIMEOUT_LIMIT < time()) { if (isset($_SESSION['lastUpdated']) && $_SESSION['lastUpdated']+$TIMEOUT_LIMIT < time()) {
debug ("Session timeout ".($_SESSION['lastUpdated']+$TIMEOUT_LIMIT).">".time(),"session"); debug ("Session timeout ".($_SESSION['lastUpdated']+$TIMEOUT_LIMIT).">".time(),"session");
session_destroy(); session_destroy();
session_start(); session_start();
} }
debug(print_r($_SESSION, true) , "session"); debug(print_r($_SESSION, true) , "session");
   
  function current_time() {
  return ($_SESSION['time']? $_SESSION['time'] : date("H:i:s"));
  }
?> ?>
<?php <?php
// Copyright 2009 Google Inc. All Rights Reserved. // Copyright 2009 Google Inc. All Rights Reserved.
$GA_ACCOUNT = "MO-22173039-1"; $GA_ACCOUNT = "MO-22173039-1";
$GA_PIXEL = "/lib/ga.php"; $GA_PIXEL = "/lib/ga.php";
  function googleAnalyticsGetImageUrl()
function googleAnalyticsGetImageUrl() { {
global $GA_ACCOUNT, $GA_PIXEL; global $GA_ACCOUNT, $GA_PIXEL;
$url = ""; $url = "";
$url .= $GA_PIXEL . "?"; $url.= $GA_PIXEL . "?";
$url .= "utmac=" . $GA_ACCOUNT; $url.= "utmac=" . $GA_ACCOUNT;
$url .= "&utmn=" . rand(0, 0x7fffffff); $url.= "&utmn=" . rand(0, 0x7fffffff);
$referer = $_SERVER["HTTP_REFERER"]; $referer = $_SERVER["HTTP_REFERER"];
$query = $_SERVER["QUERY_STRING"]; $query = $_SERVER["QUERY_STRING"];
$path = $_SERVER["REQUEST_URI"]; $path = $_SERVER["REQUEST_URI"];
if (empty($referer)) { if (empty($referer)) {
$referer = "-"; $referer = "-";
} }
$url .= "&utmr=" . urlencode($referer); $url.= "&utmr=" . urlencode($referer);
if (!empty($path)) { if (!empty($path)) {
$url .= "&utmp=" . urlencode($path); $url.= "&utmp=" . urlencode($path);
} }
$url .= "&guid=ON"; $url.= "&guid=ON";
return str_replace("&", "&amp;", $url); return str_replace("&", "&amp;", $url);
} }
   
function include_header($pageTitle, $pageType, $opendiv = true, $geolocate = false, $datepicker = false) function include_header($pageTitle, $pageType, $opendiv = true, $geolocate = false, $datepicker = false)
{ {
echo ' echo '
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>' . $pageTitle . '</title> <title>' . $pageTitle . '</title>
<meta name="google-site-verification" <meta name="google-site-verification"
content="-53T5Qn4TB_de1NyfR_ZZkEVdUNcNFSaYKSFkWKx-sY" />'; content="-53T5Qn4TB_de1NyfR_ZZkEVdUNcNFSaYKSFkWKx-sY" />';
if ($datepicker) echo '<link rel="stylesheet" href="css/jquery.ui.datepicker.mobile.css" />'; if ($datepicker) echo '<link rel="stylesheet" href="css/jquery.ui.datepicker.mobile.css" />';
if (isDebugServer()) echo '<link rel="stylesheet" href="css/jquery.mobile-1.0a4.css" /> if (isDebugServer()) {
  echo '<link rel="stylesheet" href="css/jquery.mobile-1.0a4.css" />
   
<script type="text/javascript" src="js/jquery-1.5.js"></script> <script type="text/javascript" src="js/jquery-1.5.js"></script>
<script>$(document).bind("mobileinit", function(){ <script>$(document).bind("mobileinit", function(){
$.mobile.ajaxEnabled = false; $.mobile.ajaxEnabled = false;
}); });
</script> </script>
<script type="text/javascript" src="js/jquery.mobile-1.0a4.js"></script>'; <script type="text/javascript" src="js/jquery.mobile-1.0a4.js"></script>';
else echo '<link rel="stylesheet" href="http://code.jquery.com/mobile/1.0a4/jquery.mobile-1.0a4.min.css" /> }
  else {
  echo '<link rel="stylesheet" href="http://code.jquery.com/mobile/1.0a4.1/jquery.mobile-1.0a4.1.min.css" />
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js"></script> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js"></script>
<script>$(document).bind("mobileinit", function(){ <script>$(document).bind("mobileinit", function(){
$.mobile.ajaxEnabled = false; $.mobile.ajaxEnabled = false;
}); });
</script> </script>
<script type="text/javascript" src="http://code.jquery.com/mobile/1.0a4/jquery.mobile-1.0a4.min.js"></script>'; <script type="text/javascript" src="http://code.jquery.com/mobile/1.0a4.1/jquery.mobile-1.0a4.1.min.js"></script>';
if ($datepicker) echo '<script> }
  if ($datepicker) {
  echo '<script>
//reset type=date inputs to text //reset type=date inputs to text
$( document ).bind( "mobileinit", function(){ $( document ).bind( "mobileinit", function(){
$.mobile.page.prototype.options.degradeInputs.date = true; $.mobile.page.prototype.options.degradeInputs.date = true;
}); });
</script> </script>
<script src="js/jQuery.ui.datepicker.js"></script>'; <script src="js/jQuery.ui.datepicker.js"></script>';
  }
echo '<style type="text/css"> echo '<style type="text/css">
.ui-navbar { .ui-navbar {
width: 100%; width: 100%;
} }
.ui-btn-inner { .ui-btn-inner {
white-space: normal !important; white-space: normal !important;
} }
.ui-li-heading { .ui-li-heading {
white-space: normal !important; white-space: normal !important;
} }
.ui-listview-filter { .ui-listview-filter {
margin: 0 !important; margin: 0 !important;
} }
.ui-icon-navigation { .ui-icon-navigation {
background-image: url(css/images/113-navigation.png); background-image: url(css/images/113-navigation.png);
  background-position: 1px 0;
  }
  .ui-icon-beaker {
  background-image: url(css/images/91-beaker-2.png);
background-position: 1px 0; background-position: 1px 0;
} }
#footer { #footer {
text-size: 0.75em; text-size: 0.75em;
text-align: center; text-align: center;
} }
body { body {
background-color: #F0F0F0; background-color: #F0F0F0;
} }
#jqm-homeheader { #jqm-homeheader {
text-align: center; text-align: center;
} }
.viaPoints { .viaPoints {
display: none; display: none;
text-size: 0.2em; text-size: 0.2em;
} }
.min-width-480px .viaPoints { .min-width-480px .viaPoints {
display: inline; display: inline;
} }
#extrainfo { #extrainfo {
visibility: hidden; visibility: hidden;
display: none; display: none;
} }
// source http://webaim.org/techniques/skipnav/ // source http://webaim.org/techniques/skipnav/
#skip a, #skip a:hover, #skip a:visited #skip a, #skip a:hover, #skip a:visited
{ {
position:absolute; position:absolute;
left:0px; left:0px;
top:-500px; top:-500px;
width:1px; width:1px;
height:1px; height:1px;
overflow:hidden; overflow:hidden;
} }
   
#skip a:active, #skip a:focus #skip a:active, #skip a:focus
{ {
position:static; position:static;
width:auto; width:auto;
height:auto; height:auto;
} }
</style>'; </style>';
if (strstr($_SERVER['HTTP_USER_AGENT'], 'iPhone') || strstr($_SERVER['HTTP_USER_AGENT'], 'iPod')) { if (strstr($_SERVER['HTTP_USER_AGENT'], 'iPhone') || strstr($_SERVER['HTTP_USER_AGENT'], 'iPod')) {
echo '<meta name="apple-mobile-web-app-capable" content="yes" /> echo '<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" /> <meta name="apple-mobile-web-app-status-bar-style" content="black" />
<link rel="apple-touch-startup-image" href="startup.png" /> <link rel="apple-touch-startup-image" href="startup.png" />
<link rel="apple-touch-icon" href="apple-touch-icon.png" />'; <link rel="apple-touch-icon" href="apple-touch-icon.png" />';
} }
if ($geolocate) { if ($geolocate) {
echo "<script> echo "<script>
   
function success(position) { function success(position) {
$('#error').val('Location now detected. Please wait for data to load.'); $('#error').val('Location now detected. Please wait for data to load.');
$('#geolocate').val(position.coords.latitude+','+position.coords.longitude); $('#geolocate').val(position.coords.latitude+','+position.coords.longitude);
$.ajax({ url: \"include/common.inc.php?geolocate=yes&lat=\"+position.coords.latitude+\"&lon=\"+position.coords.longitude }); $.ajax({ url: \"include/common.inc.php?geolocate=yes&lat=\"+position.coords.latitude+\"&lon=\"+position.coords.longitude });
location.reload(true); location.reload(true);
} }
function error(msg) { function error(msg) {
$('#error').val('Error: '+msg); $('#error').val('Error: '+msg);
} }
   
function geolocate() { function geolocate() {
if (navigator.geolocation) { if (navigator.geolocation) {
var options = { var options = {
enableHighAccuracy: true, enableHighAccuracy: true,
timeout: 60000, timeout: 60000,
maximumAge: 10000 maximumAge: 10000
} }
navigator.geolocation.getCurrentPosition(success, error, options); navigator.geolocation.getCurrentPosition(success, error, options);
} }
} }
$(document).ready(function() { $(document).ready(function() {
$('#here').click(function(event) { $('#geolocate').val(geolocate()); return false;}); $('#here').click(function(event) { $('#geolocate').val(geolocate()); return false;});
$('#here').show(); $('#here').show();
}); });
"; ";
if (!isset($_SESSION['lat']) || $_SESSION['lat'] == "") echo "geolocate();"; if (!isset($_SESSION['lat']) || $_SESSION['lat'] == "") echo "geolocate();";
echo "</script> "; echo "</script> ";
} }
if (isAnalyticsOn()) echo ' if (isAnalyticsOn()) echo '
<script type="text/javascript">'." <script type="text/javascript">' . "
   
var _gaq = _gaq || []; var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-22173039-1']); _gaq.push(['_setAccount', 'UA-22173039-1']);
_gaq.push(['_trackPageview']); _gaq.push(['_trackPageview']);
</script>"; </script>";
echo '</head> echo '</head>
<body> <body>
<div id="skip"> <div id="skip">
<a href="#maincontent">Skip to content</a> <a href="#maincontent">Skip to content</a>
</div> </div>
'; ';
if ($opendiv) { if ($opendiv) {
echo '<div data-role="page"> echo '<div data-role="page">
<script>  
$(document).ready(function ()  
{  
document.title = "' . $pageTitle . '";  
});  
</script>  
<div data-role="header" data-position="inline"> <div data-role="header" data-position="inline">
<a href="'.$_SERVER["HTTP_REFERER"].'" data-icon="arrow-l" data-rel="back" class="ui-btn-left">Back</a> <a href="' . $_SERVER["HTTP_REFERER"] . '" data-icon="arrow-l" data-rel="back" class="ui-btn-left">Back</a>
<h1>' . $pageTitle . '</h1> <h1>' . $pageTitle . '</h1>
<a href="/index.php" data-icon="home" class="ui-btn-right">Home</a> <a href="/index.php" data-icon="home" class="ui-btn-right">Home</a>
</div><!-- /header --> </div><!-- /header -->
<a name="maincontent" id="maincontent"></a> <a name="maincontent" id="maincontent"></a>
<div data-role="content"> '; <div data-role="content"> ';
} }
} }
function include_footer() function include_footer()
{ {
echo '<div id="footer"><a href="about.php">About/Contact Us</a>&nbsp;<a href="feedback.php">Feedback/Bug Report</a></a>'; echo '<div id="footer"><a href="about.php">About/Contact Us</a>&nbsp;<a href="feedback.php">Feedback/Bug Report</a></a>';
echo '</div>'; echo '</div>';
if (isAnalyticsOn()) { if (isAnalyticsOn()) {
echo "<script> (function() { echo "<script> (function() {
var ga = document.createElement('script'); ga.type = var ga = document.createElement('script'); ga.type =
'text/javascript'; ga.async = true; 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? ga.src = ('https:' == document.location.protocol ?
'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(ga, s); s.parentNode.insertBefore(ga, s);
})();</script>"; })();</script>";
$googleAnalyticsImageUrl = googleAnalyticsGetImageUrl(); $googleAnalyticsImageUrl = googleAnalyticsGetImageUrl();
echo '<noscript><img src="' . $googleAnalyticsImageUrl . '" /></noscript>'; echo '<noscript><img src="' . $googleAnalyticsImageUrl . '" /></noscript>';
} }
} }
function timePlaceSettings($geolocate = false) function timePlaceSettings($geolocate = false)
{ {
global $service_periods; global $service_periods;
$geoerror = false; $geoerror = false;
if ($geolocate == true) { if ($geolocate == true) {
$geoerror = !isset($_SESSION['lat']) || !isset($_SESSION['lat']) || $_SESSION['lat'] == "" || $_SESSION['lon'] == ""; $geoerror = !isset($_SESSION['lat']) || !isset($_SESSION['lat']) || $_SESSION['lat'] == "" || $_SESSION['lon'] == "";
} }
if ($geoerror) { if ($geoerror) {
echo '<div id="error">Sorry, but your location could not currently be detected. echo '<div id="error">Sorry, but your location could not currently be detected.
Please allow location permission, wait for your location to be detected, Please allow location permission, wait for your location to be detected,
or enter an address/co-ordinates in the box below.</div>'; or enter an address/co-ordinates in the box below.</div>';
} }
echo '<div data-role="collapsible" data-collapsed="' . !$geoerror . '"> echo '<div data-role="collapsible" data-collapsed="' . !$geoerror . '">
<h3>Change Time/Place (' . (isset($_SESSION['time']) ? $_SESSION['time'] : "Current Time,") . ' ' . ucwords(service_period()) . ')...</h3> <h3>Change Time/Place (' . (isset($_SESSION['time']) ? $_SESSION['time'] : "Current Time,") . ' ' . ucwords(service_period()) . ')...</h3>
<form action="'.basename($_SERVER['PHP_SELF'])."?".$_SERVER['QUERY_STRING'].'" method="post"> <form action="' . basename($_SERVER['PHP_SELF']) . "?" . $_SERVER['QUERY_STRING'] . '" method="post">
<div class="ui-body"> <div class="ui-body">
<div data-role="fieldcontain"> <div data-role="fieldcontain">
<label for="geolocate"> Current Location: </label> <label for="geolocate"> Current Location: </label>
<input type="text" id="geolocate" name="geolocate" value="' . (isset($_SESSION['lat']) && isset($_SESSION['lon']) ? $_SESSION['lat'] . "," . $_SESSION['lon'] : "Enter co-ordinates or address here") . '"/> <a href="#" style="display:none" name="here" id="here">Here?</a> <input type="text" id="geolocate" name="geolocate" value="' . (isset($_SESSION['lat']) && isset($_SESSION['lon']) ? $_SESSION['lat'] . "," . $_SESSION['lon'] : "Enter co-ordinates or address here") . '"/> <a href="#" style="display:none" name="here" id="here">Here?</a>
</div> </div>
<div data-role="fieldcontain"> <div data-role="fieldcontain">
<label for="time"> Time: </label> <label for="time"> Time: </label>
<input type="time" name="time" id="time" value="' . (isset($_SESSION['time']) ? $_SESSION['time'] : date("H:i")) . '"/> <input type="time" name="time" id="time" value="' . (isset($_SESSION['time']) ? $_SESSION['time'] : date("H:i")) . '"/>
<a href="#" name="currentTime" id="currentTime" onClick="var d = new Date();'. "$('#time').val(d.getHours() +':'+ (d.getMinutes().toString().length = 1 ? '0'+ d.getMinutes(): d.getMinutes()));".'">Current Time?</a> <a href="#" name="currentTime" id="currentTime" onClick="var d = new Date();' . "$('#time').val(d.getHours() +':'+ (d.getMinutes().toString().length == 1 ? '0'+ d.getMinutes(): d.getMinutes()));" . '">Current Time?</a>
</div> </div>
<div data-role="fieldcontain"> <div data-role="fieldcontain">
<label for="service_period"> Service Period: </label> <label for="service_period"> Service Period: </label>
<select name="service_period" id="service_period">'; <select name="service_period" id="service_period">';
foreach ($service_periods as $service_period) { foreach ($service_periods as $service_period) {
echo "<option value=\"$service_period\"" . (service_period() === $service_period ? " SELECTED" : "") . '>' . ucwords($service_period) . '</option>'; echo "<option value=\"$service_period\"" . (service_period() === $service_period ? " SELECTED" : "") . '>' . ucwords($service_period) . '</option>';
} }
echo '</select> echo '</select>
<a href="#" style="display:none" name="currentPeriod" id="currentPeriod"/>Current Period?</a> <a href="#" style="display:none" name="currentPeriod" id="currentPeriod"/>Current Period?</a>
</div> </div>
<input type="submit" value="Update"/> <input type="submit" value="Update"/>
</form> </form>
</div></div>'; </div></div>';
} }
function trackEvent($category, $action, $label = "", $value = -1) { function trackEvent($category, $action, $label = "", $value = - 1)
if (isAnalyticsOn()) { {
echo "\n<script> _gaq.push(['_trackEvent', '$category', '$action'".($label != "" ? ", '$label'" : "").($value != -1 ? ", $value" : "")."]);</script>"; if (isAnalyticsOn()) {
} echo "\n<script> _gaq.push(['_trackEvent', '$category', '$action'" . ($label != "" ? ", '$label'" : "") . ($value != - 1 ? ", $value" : "") . "]);</script>";
  }
} }
?> ?>
   
<?php <?php
$service_periods = Array( $service_periods = Array(
'sunday', 'sunday',
'saturday', 'saturday',
'weekday' 'weekday'
); );
function service_period() function service_period()
{ {
if (isset($_SESSION['service_period'])) return $_SESSION['service_period']; if (isset($_SESSION['service_period'])) return $_SESSION['service_period'];
switch (date('w')) { switch (date('w')) {
case 0: case 0:
return 'sunday'; return 'sunday';
case 6: case 6:
return 'saturday'; return 'saturday';
default: default:
return 'weekday'; return 'weekday';
} }
} }
function midnight_seconds()  
{  
// from http://www.perturb.org/display/Perlfunc__Seconds_Since_Midnight.html  
if (isset($_SESSION['time'])) {  
$time = strtotime($_SESSION['time']);  
return (date("G", $time) * 3600) + (date("i", $time) * 60) + date("s", $time);  
}  
return (date("G") * 3600) + (date("i") * 60) + date("s");  
}  
function midnight_seconds_to_time($seconds)  
{  
if ($seconds > 0) {  
$midnight = mktime(0, 0, 0, date("n") , date("j") , date("Y"));  
return date("h:ia", $midnight + $seconds);  
}  
else {  
return "";  
}  
}  
function viaPoints($tripid, $stopid, $timingPointsOnly = false)  
{  
global $APIurl;  
$url = $APIurl . "/json/tripstoptimes?trip=" . $tripid;  
$json = json_decode(getPage($url));  
debug(print_r($json, true));  
$stops = $json[0];  
$times = $json[1];  
$foundStop = false;  
$viaPoints = Array();  
foreach ($stops as $key => $row) {  
if ($foundStop) {  
if (!$timingPointsOnly || !startsWith($row[5], "Wj")) {  
$viaPoints[] = Array(  
"id" => $row[0],  
"name" => $row[1],  
"time" => $times[$key]  
);  
}  
}  
else {  
if ($row[0] == $stopid) $foundStop = true;  
}  
}  
return $viaPoints;  
}  
function viaPointNames($tripid, $stopid)  
{  
$points = viaPoints($tripid, $stopid, true);  
$pointNames = Array();  
foreach ($points as $point) {  
$pointNames[] = $point['name'];  
}  
return implode(", ", $pointNames);  
}  
?> ?>
<?php <?php
date_default_timezone_set('Australia/ACT'); date_default_timezone_set('Australia/ACT');
$APIurl = "http://localhost:8765";  
$debugOkay = Array( $debugOkay = Array(
"session", "session",
"json", "json",
"phperror", "phperror",
"awsgtfs",  
"awsotp", "awsotp",
//"squallotp", //"squallotp",
//"vanilleotp", //"vanilleotp",
  "database",
"other" "other"
); );
if (isDebug("awsgtfs")) {  
$APIurl = "http://bus-main.lambdacomplex.org:8765";  
}  
$cloudmadeAPIkey = "daa03470bb8740298d4b10e3f03d63e6"; $cloudmadeAPIkey = "daa03470bb8740298d4b10e3f03d63e6";
$googleMapsAPIkey = "ABQIAAAA95XYXN0cki3Yj_Sb71CFvBTPaLd08ONybQDjcH_VdYtHHLgZvRTw2INzI_m17_IoOUqH3RNNmlTk1Q"; $googleMapsAPIkey = "ABQIAAAA95XYXN0cki3Yj_Sb71CFvBTPaLd08ONybQDjcH_VdYtHHLgZvRTw2INzI_m17_IoOUqH3RNNmlTk1Q";
$otpAPIurl = 'http://localhost:8080/opentripplanner-api-webapp/'; $otpAPIurl = 'http://localhost:8080/opentripplanner-api-webapp/';
if (isDebug("awsotp") || php_uname('n') == "maxious.xen.prgmr.com") { if (isDebug("awsotp") || php_uname('n') == "maxious.xen.prgmr.com") {
$otpAPIurl = 'http://bus-main.lambdacomplex.org:8080/opentripplanner-api-webapp/'; $otpAPIurl = 'http://bus-main.lambdacomplex.org:8080/opentripplanner-api-webapp/';
} }
if (isDebug("squallotp")) { if (isDebug("squallotp")) {
$otpAPIurl = 'http://10.0.1.108:5080/opentripplanner-api-webapp/'; $otpAPIurl = 'http://10.0.1.108:5080/opentripplanner-api-webapp/';
} }
if (isDebug("vanilleotp")) { if (isDebug("vanilleotp")) {
$otpAPIurl = 'http://10.0.1.135:8080/opentripplanner-api-webapp/'; $otpAPIurl = 'http://10.0.1.135:8080/opentripplanner-api-webapp/';
} }
if (isDebug("phperror")) error_reporting(E_ALL ^ E_NOTICE); if (isDebug("phperror")) error_reporting(E_ALL ^ E_NOTICE);
   
include_once ("common-geo.inc.php"); include_once ("common-geo.inc.php");
include_once ("common-net.inc.php"); include_once ("common-net.inc.php");
include_once ("common-transit.inc.php"); include_once ("common-transit.inc.php");
   
include_once ("common-session.inc.php"); include_once ("common-session.inc.php");
  include_once ("common-db.inc.php");
include_once ("common-template.inc.php"); include_once ("common-template.inc.php");
   
function isDebugServer() function isDebugServer()
{ {
return $_SERVER['SERVER_NAME'] == "10.0.1.154" || $_SERVER['SERVER_NAME'] == "localhost" || $_SERVER['SERVER_NAME'] == "127.0.0.1" || !$_SERVER['SERVER_NAME']; return $_SERVER['SERVER_NAME'] == "10.0.1.154" || $_SERVER['SERVER_NAME'] == "localhost" || $_SERVER['SERVER_NAME'] == "127.0.0.1" || !$_SERVER['SERVER_NAME'];
} }
function isAnalyticsOn() function isAnalyticsOn()
{ {
return !isDebugServer(); return !isDebugServer();
} }
function isDebug($debugReason = "other") function isDebug($debugReason = "other")
{ {
global $debugOkay; global $debugOkay;
return in_array($debugReason, $debugOkay, false) && isDebugServer(); return in_array($debugReason, $debugOkay, false) && isDebugServer();
} }
function debug($msg, $debugReason = "other") function debug($msg, $debugReason = "other")
{ {
if (isDebug($debugReason)) echo "\n<!-- " . date(DATE_RFC822) . "\n $msg -->\n"; if (isDebug($debugReason)) echo "\n<!-- " . date(DATE_RFC822) . "\n $msg -->\n";
} }
function isJQueryMobileDevice() function isJQueryMobileDevice()
{ {
// http://forum.jquery.com/topic/what-is-the-best-way-to-detect-all-useragents-which-can-handle-jquery-mobile#14737000002087897 // http://forum.jquery.com/topic/what-is-the-best-way-to-detect-all-useragents-which-can-handle-jquery-mobile#14737000002087897
$user_agent = $_SERVER['HTTP_USER_AGENT']; $user_agent = $_SERVER['HTTP_USER_AGENT'];
return preg_match('/iphone/i', $user_agent) || preg_match('/android/i', $user_agent) || preg_match('/webos/i', $user_agent) || preg_match('/ios/i', $user_agent) || preg_match('/bada/i', $user_agent) || preg_match('/maemo/i', $user_agent) || preg_match('/meego/i', $user_agent) || preg_match('/fennec/i', $user_agent) || (preg_match('/symbian/i', $user_agent) && preg_match('/s60/i', $user_agent) && $browser['majorver'] >= 5) || (preg_match('/symbian/i', $user_agent) && preg_match('/platform/i', $user_agent) && $browser['majorver'] >= 3) || (preg_match('/blackberry/i', $user_agent) && $browser['majorver'] >= 5) || (preg_match('/opera mobile/i', $user_agent) && $browser['majorver'] >= 10) || (preg_match('/opera mini/i', $user_agent) && $browser['majorver'] >= 5); return preg_match('/iphone/i', $user_agent) || preg_match('/android/i', $user_agent) || preg_match('/webos/i', $user_agent) || preg_match('/ios/i', $user_agent) || preg_match('/bada/i', $user_agent) || preg_match('/maemo/i', $user_agent) || preg_match('/meego/i', $user_agent) || preg_match('/fennec/i', $user_agent) || (preg_match('/symbian/i', $user_agent) && preg_match('/s60/i', $user_agent) && $browser['majorver'] >= 5) || (preg_match('/symbian/i', $user_agent) && preg_match('/platform/i', $user_agent) && $browser['majorver'] >= 3) || (preg_match('/blackberry/i', $user_agent) && $browser['majorver'] >= 5) || (preg_match('/opera mobile/i', $user_agent) && $browser['majorver'] >= 10) || (preg_match('/opera mini/i', $user_agent) && $browser['majorver'] >= 5);
} }
function isFastDevice() function isFastDevice()
{ {
$ua = $_SERVER['HTTP_USER_AGENT']; $ua = $_SERVER['HTTP_USER_AGENT'];
$fastDevices = Array( $fastDevices = Array(
"Mozilla/5.0 (X11;", "Mozilla/5.0 (X11;",
"Mozilla/5.0 (Windows;", "Mozilla/5.0 (Windows;",
"Mozilla/5.0 (iP", "Mozilla/5.0 (iP",
"Mozilla/5.0 (Linux; U; Android", "Mozilla/5.0 (Linux; U; Android",
"Mozilla/4.0 (compatible; MSIE" "Mozilla/4.0 (compatible; MSIE"
); );
$slowDevices = Array( $slowDevices = Array(
"J2ME", "J2ME",
"MIDP", "MIDP",
"Opera/", "Opera/",
"Mozilla/2.0 (compatible;", "Mozilla/2.0 (compatible;",
"Mozilla/3.0 (compatible;" "Mozilla/3.0 (compatible;"
); );
return true; return true;
} }
function array_flatten($a, $f = array()) function array_flatten($a, $f = array())
{ {
if (!$a || !is_array($a)) return ''; if (!$a || !is_array($a)) return '';
foreach ($a as $k => $v) { foreach ($a as $k => $v) {
if (is_array($v)) $f = array_flatten($v, $f); if (is_array($v)) $f = array_flatten($v, $f);
else $f[$k] = $v; else $f[$k] = $v;
} }
return $f; return $f;
} }
function remove_spaces($string) function remove_spaces($string)
{ {
return str_replace(' ', '', $string); return str_replace(' ', '', $string);
} }
function object2array($object) function object2array($object)
{ {
if (is_object($object)) { if (is_object($object)) {
foreach ($object as $key => $value) { foreach ($object as $key => $value) {
$array[$key] = $value; $array[$key] = $value;
} }
} }
else { else {
$array = $object; $array = $object;
} }
return $array; return $array;
} }
function startsWith($haystack, $needle, $case = true) function startsWith($haystack, $needle, $case = true)
{ {
if ($case) { if ($case) {
return (strcmp(substr($haystack, 0, strlen($needle)) , $needle) === 0); return (strcmp(substr($haystack, 0, strlen($needle)) , $needle) === 0);
} }
return (strcasecmp(substr($haystack, 0, strlen($needle)) , $needle) === 0); return (strcasecmp(substr($haystack, 0, strlen($needle)) , $needle) === 0);
} }
   
function endsWith($haystack, $needle, $case = true) function endsWith($haystack, $needle, $case = true)
{ {
if ($case) { if ($case) {
return (strcmp(substr($haystack, strlen($haystack) - strlen($needle)) , $needle) === 0); return (strcmp(substr($haystack, strlen($haystack) - strlen($needle)) , $needle) === 0);
} }
return (strcasecmp(substr($haystack, strlen($haystack) - strlen($needle)) , $needle) === 0); return (strcasecmp(substr($haystack, strlen($haystack) - strlen($needle)) , $needle) === 0);
} }
function bracketsMeanNewLine($input) function bracketsMeanNewLine($input)
{ {
return str_replace(")", "</small>", str_replace("(", "<br><small>", $input)); return str_replace(")", "</small>", str_replace("(", "<br><small>", $input));
} }
function sksort(&$array, $subkey = "id", $sort_ascending = false) function sksort(&$array, $subkey = "id", $sort_ascending = false)
{ {
if (count($array)) $temp_array[key($array) ] = array_shift($array); if (count($array)) $temp_array[key($array) ] = array_shift($array);
foreach ($array as $key => $val) { foreach ($array as $key => $val) {
$offset = 0; $offset = 0;
$found = false; $found = false;
foreach ($temp_array as $tmp_key => $tmp_val) { foreach ($temp_array as $tmp_key => $tmp_val) {
if (!$found and strtolower($val[$subkey]) > strtolower($tmp_val[$subkey])) { if (!$found and strtolower($val[$subkey]) > strtolower($tmp_val[$subkey])) {
$temp_array = array_merge((array)array_slice($temp_array, 0, $offset) , array( $temp_array = array_merge((array)array_slice($temp_array, 0, $offset) , array(
$key => $val $key => $val
) , array_slice($temp_array, $offset)); ) , array_slice($temp_array, $offset));
$found = true; $found = true;
} }
$offset++; $offset++;
} }
if (!$found) $temp_array = array_merge($temp_array, array( if (!$found) $temp_array = array_merge($temp_array, array(
$key => $val $key => $val
)); ));
} }
if ($sort_ascending) $array = array_reverse($temp_array); if ($sort_ascending) $array = array_reverse($temp_array);
else $array = $temp_array; else $array = $temp_array;
} }
  function sktimesort(&$array, $subkey = "id", $sort_ascending = false)
  {
  if (count($array)) $temp_array[key($array) ] = array_shift($array);
  foreach ($array as $key => $val) {
  $offset = 0;
  $found = false;
  foreach ($temp_array as $tmp_key => $tmp_val) {
  if (!$found and strtotime($val[$subkey]) > strtotime($tmp_val[$subkey])) {
  $temp_array = array_merge((array)array_slice($temp_array, 0, $offset) , array(
  $key => $val
  ) , array_slice($temp_array, $offset));
  $found = true;
  }
  $offset++;
  }
  if (!$found) $temp_array = array_merge($temp_array, array(
  $key => $val
  ));
  }
  if ($sort_ascending) $array = array_reverse($temp_array);
  else $array = $temp_array;
  }
  function r_implode( $glue, $pieces )
  {
  foreach( $pieces as $r_pieces )
  {
  if( is_array( $r_pieces ) )
  {
  $retVal[] = r_implode( $glue, $r_pieces );
  }
  else
  {
  $retVal[] = $r_pieces;
  }
  }
  return implode( $glue, $retVal );
  }
?> ?>
   
  <?php
 
  function getRoute($routeID) {
  $query = "Select * from routes where route_id = '$routeID' LIMIT 1";
  debug($query,"database");
  $result = pg_query($conn, $query);
  if (!$result) {
  databaseError(pg_result_error($result));
  return Array();
  }
  return pg_fetch_assoc($result);
  }
  function getRoutes() {
  global $conn;
  $query = "Select * from routes order by route_short_name;";
  debug($query,"database");
  $result = pg_query($conn, $query);
  if (!$result) {
  databaseError(pg_result_error($result));
  return Array();
  }
  return pg_fetch_all($result);
  }
 
  function getRoutesByNumber($routeNumber = "") {
  global $conn;
  if ($routeNumber != "") {
  $query = "Select distinct routes.route_id,routes.route_short_name,routes.route_long_name,service_id from routes join trips on trips.route_id =
  routes.route_id join stop_times on stop_times.trip_id = trips.trip_id where route_short_name = '$routeNumber' order by route_short_name;";
  } else {
  $query = "SELECT DISTINCT route_short_name from routes order by route_short_name";
  }
  debug($query,"database");
  $result = pg_query($conn, $query);
  if (!$result) {
  databaseError(pg_result_error($result));
  return Array();
  }
  return pg_fetch_all($result);
  }
 
  function getRouteNextTrip($routeID) {
  global $conn;
  $query = "select * from routes join trips on trips.route_id = routes.route_id
  join stop_times on stop_times.trip_id = trips.trip_id where
  arrival_time > '".current_time()."' and routes.route_id = '$routeID' order by
  arrival_time limit 1";
  debug($query,"database");
  $result = pg_query($conn, $query);
  if (!$result) {
  databaseError(pg_result_error($result));
  return Array();
  }
  return pg_fetch_assoc($result);
  }
 
  function getTimeInterpolatedRouteAtStop($routeID, $stop_id)
  {
  $nextTrip = getRouteNextTrip($routeID);
  if ($nextTrip['trip_id']){
  foreach (getTimeInterpolatedTrip($nextTrip['trip_id']) as $tripStop) {
  if ($tripStop['stop_id'] == $stop_id) return $tripStop;
  }
  }
  return Array();
  }
 
  function getRouteTrips($routeID) {
  global $conn;
  $query = "select * from routes join trips on trips.route_id = routes.route_id
  join stop_times on stop_times.trip_id = trips.trip_id where routes.route_id = '$routeID' order by
  arrival_time ";
  debug($query,"database");
  $result = pg_query($conn, $query);
  if (!$result) {
  databaseError(pg_result_error($result));
  return Array();
  }
  return pg_fetch_all($result);
  }
  function getRoutesByDestination($destination = "", $service_period = "") {
  global $conn;
  if ($service_period == "") $service_period = service_period();
  if ($destination != "") {
  $query = "SELECT DISTINCT trips.route_id,route_short_name,route_long_name, service_id
  FROM stop_times join trips on trips.trip_id =
  stop_times.trip_id join routes on trips.route_id = routes.route_id
  WHERE route_long_name = '$destination' AND service_id='$service_period' order by route_short_name";
  } else {
  $query = "SELECT DISTINCT route_long_name
  FROM stop_times join trips on trips.trip_id =
  stop_times.trip_id join routes on trips.route_id = routes.route_id
  WHERE service_id='$service_period' order by route_long_name";
  }
  debug($query,"database");
  $result = pg_query($conn, $query);
  if (!$result) {
  databaseError(pg_result_error($result));
  return Array();
  }
  return pg_fetch_all($result);
  }
 
  function getRoutesBySuburb($suburb, $service_period = "") {
  if ($service_period == "") $service_period = service_period();
  global $conn;
  $query = "SELECT DISTINCT service_id,trips.route_id,route_short_name,route_long_name
  FROM stop_times join trips on trips.trip_id = stop_times.trip_id
  join routes on trips.route_id = routes.route_id
  join stops on stops.stop_id = stop_times.stop_id
  WHERE zone_id LIKE '%$suburb;%' AND service_id='$service_period' ORDER BY route_short_name";
  debug($query,"database");
  $result = pg_query($conn, $query);
  if (!$result) {
  databaseError(pg_result_error($result));
  return Array();
  }
  return pg_fetch_all($result);
  }
 
  function getRoutesNearby($lat, $lng, $limit = "", $distance = 500) {
 
 
  if ($service_period == "") $service_period = service_period();
  if ($limit != "") $limit = " LIMIT $limit ";
  global $conn;
  $query = "SELECT service_id,trips.route_id,route_short_name,route_long_name,min(stops.stop_id) as stop_id,
  min(ST_Distance(position, ST_GeographyFromText('SRID=4326;POINT($lng $lat)'), FALSE)) as distance
  FROM stop_times
  join trips on trips.trip_id = stop_times.trip_id
  join routes on trips.route_id = routes.route_id
  join stops on stops.stop_id = stop_times.stop_id
  WHERE service_id='$service_period'
  AND ST_DWithin(position, ST_GeographyFromText('SRID=4326;POINT($lng $lat)'), $distance, FALSE)
  group by service_id,trips.route_id,route_short_name,route_long_name
  order by distance $limit";
  debug($query,"database");
  $result = pg_query($conn, $query);
  if (!$result) {
  databaseError(pg_result_error($result));
  return Array();
  }
  return pg_fetch_all($result);
  }
  ?>
  <?php
  function getStop($stopID)
  {
  global $conn;
  $query = "Select * from stops where stop_id = '$stopID' LIMIT 1";
  debug($query, "database");
  $result = pg_query($conn, $query);
  if (!$result) {
  databaseError(pg_result_error($result));
  return Array();
  }
  return pg_fetch_assoc($result);
  }
  function getStops($timingPointsOnly = false, $firstLetter = "")
  {
  global $conn;
  $conditions = Array();
  if ($timingPointsOnly) $conditions[] = "substr(stop_code,1,2) != 'Wj'";
  if ($firstLetter != "") $conditions[] = "substr(stop_name,1,1) = '$firstLetter'";
  $query = "Select * from stops";
  if (sizeof($conditions) > 0) {
  if (sizeof($conditions) > 1) {
  $query.= " Where " . implode(" AND ", $conditions) . " ";
  }
  else {
  $query.= " Where " . $conditions[0] . " ";
  }
  }
  $query.= " order by stop_name;";
  debug($query, "database");
  $result = pg_query($conn, $query);
  if (!$result) {
  databaseError(pg_result_error($result));
  return Array();
  }
  return pg_fetch_all($result);
  }
  function getNearbyStops($lat, $lng, $limit = "", $distance = 1000)
  {
  if ($lat == null || $lng == null) return Array();
  if ($limit != "") $limit = " LIMIT $limit ";
  global $conn;
  $query = "Select *, ST_Distance(position, ST_GeographyFromText('SRID=4326;POINT($lng $lat)'), FALSE) as distance
  from stops WHERE ST_DWithin(position, ST_GeographyFromText('SRID=4326;POINT($lng $lat)'), $distance, FALSE)
  order by distance $limit;";
  debug($query, "database");
  $result = pg_query($conn, $query);
  if (!$result) {
  databaseError(pg_result_error($result));
  return Array();
  }
  return pg_fetch_all($result);
  }
  function getStopsBySuburb($suburb)
  {
  global $conn;
  $query = "Select * from stops where zone_id LIKE '%$suburb;%' order by stop_name;";
  debug($query, "database");
  $result = pg_query($conn, $query);
  if (!$result) {
  databaseError(pg_result_error($result));
  return Array();
  }
  return pg_fetch_all($result);
  }
  function getStopRoutes($stopID, $service_period)
  {
  if ($service_period == "") $service_period = service_period();
  global $conn;
  $query = "SELECT service_id,trips.route_id,route_short_name,route_long_name
  FROM stop_times join trips on trips.trip_id =
  stop_times.trip_id join routes on trips.route_id = routes.route_id WHERE stop_id = '$stopID' AND service_id='$service_period'";
  debug($query, "database");
  $result = pg_query($conn, $query);
  if (!$result) {
  databaseError(pg_result_error($result));
  return Array();
  }
  return pg_fetch_all($result);
  }
  function getStopTrips($stopID, $service_period = "", $afterTime = "")
  {
  if ($service_period == "") $service_period = service_period();
  $afterCondition = "AND arrival_time > '$afterTime'";
  global $conn;
  if ($afterTime != "") {
  $query = " SELECT stop_times.trip_id,stop_times.arrival_time,stop_times.stop_id,stop_sequence,service_id,trips.route_id,route_short_name,route_long_name, end_times.arrival_time as end_time
  FROM stop_times
  join trips on trips.trip_id =
  stop_times.trip_id
  join routes on trips.route_id = routes.route_id , (SELECT trip_id,max(arrival_time) as arrival_time from stop_times
  WHERE stop_times.arrival_time IS NOT NULL group by trip_id) as end_times
  WHERE stop_times.stop_id = '$stopID'
  AND stop_times.trip_id = end_times.trip_id
  AND service_id='$service_period'
  AND end_times.arrival_time > '$afterTime'
  ORDER BY end_time";
  }
  else {
  $query = "SELECT stop_times.trip_id,arrival_time,stop_times.stop_id,stop_sequence,service_id,trips.route_id,route_short_name,route_long_name
  FROM stop_times
  join trips on trips.trip_id =
  stop_times.trip_id
  join routes on trips.route_id = routes.route_id
  WHERE stop_times.stop_id = '$stopID'
  AND service_id='$service_period'
  ORDER BY arrival_time";
  }
  debug($query, "database");
  $result = pg_query($conn, $query);
  if (!$result) {
  databaseError(pg_result_error($result));
  return Array();
  }
  return pg_fetch_all($result);
  }
  function getStopTripsWithTimes($stopID, $time = "", $service_period = "", $time_range = "", $limit = "")
  {
  if ($service_period == "") $service_period = service_period();
  if ($time_range == "") $time_range = (24 * 60 * 60);
  if ($time == "") $time = current_time();
  if ($limit == "") $limit = 10;
  $trips = getStopTrips($stopID, $service_period, $time);
  $timedTrips = Array();
  if ($trips && sizeof($trips) > 0) {
  foreach ($trips as $trip) {
  if ($trip['arrival_time'] != "") {
  if (strtotime($trip['arrival_time']) > strtotime($time) and strtotime($trip['arrival_time']) < (strtotime($time) + $time_range)) {
  $timedTrips[] = $trip;
  }
  }
  else {
  $timedTrip = getTimeInterpolatedTripAtStop($trip['trip_id'], $trip['stop_sequence']);
  if ($timedTrip['arrival_time'] > $time and strtotime($timedTrip['arrival_time']) < (strtotime($time) + $time_range)) {
  $timedTrips[] = $timedTrip;
  }
  }
  if (sizeof($timedTrips) > $limit) break;
  }
  sktimesort($timedTrips, "arrival_time", true);
  }
  return $timedTrips;
  }
  ?>
  <?php
  function getTrip($tripID)
  {
  global $conn;
  $query = "Select * from trips
  join routes on trips.route_id = routes.route_id
  where trip_id = '$tripID'
  LIMIT 1";
  debug($query, "database");
  $result = pg_query($conn, $query);
  if (!$result) {
  databaseError(pg_result_error($result));
  return Array();
  }
  return pg_fetch_assoc($result);
  }
  function getTripShape()
  {
  /* def handle_json_GET_tripstopTimes(self, params):
  schedule = self.server.schedule
  try:
  trip = schedule.GetTrip(params.get('trip'))
  except KeyError:
  # if a non-existent trip is searched for, the return nothing
  return
  time_stops = trip.GetTimeInterpolatedStops()
  stops = []
  times = []
  for arr,ts,is_timingpoint in time_stops:
  stops.append(StopToTuple(ts.stop))
  times.append(arr)
  return [stops, times]
 
  def handle_json_GET_tripshape(self, params):
  schedule = self.server.schedule
  try:
  trip = schedule.GetTrip(params.get('trip'))
  except KeyError:
  # if a non-existent trip is searched for, the return nothing
  return
  points = []
  if trip.shape_id:
  shape = schedule.GetShape(trip.shape_id)
  for (lat, lon, dist) in shape.points:
  points.append((lat, lon))
  else:
  time_stops = trip.GetTimeStops()
  for arr,dep,stop in time_stops:
  points.append((stop.stop_lat, stop.stop_lon))
  return points*/
  }
  function getTimeInterpolatedTrip($tripID, $range = "")
  {
  global $conn;
  $query = "SELECT stop_times.trip_id,arrival_time,stop_times.stop_id,stop_lat,stop_lon,stop_name,stop_code,
  stop_sequence,service_id,trips.route_id,route_short_name,route_long_name
  FROM stop_times
  join trips on trips.trip_id = stop_times.trip_id
  join routes on trips.route_id = routes.route_id
  join stops on stops.stop_id = stop_times.stop_id
  WHERE trips.trip_id = '$tripID' $range ORDER BY stop_sequence";
  debug($query, "database");
  $result = pg_query($conn, $query);
  if (!$result) {
  databaseError(pg_result_error($result));
  return Array();
  }
  $stopTimes = pg_fetch_all($result);
  $cur_timepoint = Array();
  $next_timepoint = Array();
  $distance_between_timepoints = 0.0;
  $distance_traveled_between_timepoints = 0.0;
  $rv = Array();
  foreach ($stopTimes as $i => $stopTime) {
  if ($stopTime['arrival_time'] != "") {
  // is timepoint
  $cur_timepoint = $stopTime;
  $distance_between_timepoints = 0.0;
  $distance_traveled_between_timepoints = 0.0;
  if ($i + 1 < sizeof($stopTimes)) {
  $k = $i + 1;
  $distance_between_timepoints += distance($stopTimes[$k - 1]["stop_lat"], $stopTimes[$k - 1]["stop_lon"], $stopTimes[$k]["stop_lat"], $stopTimes[$k]["stop_lon"]);
  while ($stopTimes[$k]["arrival_time"] == "" && $k + 1 < sizeof($stopTimes)) {
  $k += 1;
  //echo "k".$k;
  $distance_between_timepoints += distance($stopTimes[$k - 1]["stop_lat"], $stopTimes[$k - 1]["stop_lon"], $stopTimes[$k]["stop_lat"], $stopTimes[$k]["stop_lon"]);
  }
  $next_timepoint = $stopTimes[$k];
  $rv[] = $stopTime;
  }
  }
  else {
  // is untimed point
  //echo "i".$i;
  $distance_traveled_between_timepoints += distance($stopTimes[$i - 1]["stop_lat"], $stopTimes[$i - 1]["stop_lon"], $stopTimes[$i]["stop_lat"], $stopTimes[$i]["stop_lon"]);
  //echo "$distance_traveled_between_timepoints / $distance_between_timepoints<br>";
  $distance_percent = $distance_traveled_between_timepoints / $distance_between_timepoints;
  if ($next_timepoint["arrival_time"] != "") {
  $total_time = strtotime($next_timepoint["arrival_time"]) - strtotime($cur_timepoint["arrival_time"]);
  //echo strtotime($next_timepoint["arrival_time"])." - ".strtotime($cur_timepoint["arrival_time"])."<br>";
  $time_estimate = ($distance_percent * $total_time) + strtotime($cur_timepoint["arrival_time"]);
  $stopTime["arrival_time"] = date("H:i:s", $time_estimate);
  } else {
  $stopTime["arrival_time"] = $cur_timepoint["arrival_time"];
  }
  $rv[] = $stopTime;
  //var_dump($rv);
  }
  }
  return $rv;
  }
  function getTimeInterpolatedTripAtStop($tripID, $stop_sequence)
  {
  global $conn;
  // limit interpolation to between nearest actual points.
  $prevTimePoint = pg_fetch_assoc(pg_query($conn," SELECT trip_id,stop_id,
  stop_sequence
  FROM stop_times
  WHERE trip_id = '$tripID' and stop_sequence < $stop_sequence and stop_times.arrival_time IS NOT NULL ORDER BY stop_sequence DESC LIMIT 1"));
  $nextTimePoint = pg_fetch_assoc(pg_query($conn," SELECT trip_id,stop_id,
  stop_sequence
  FROM stop_times
  WHERE trip_id = '$tripID' and stop_sequence > $stop_sequence and stop_times.arrival_time IS NOT NULL ORDER BY stop_sequence LIMIT 1"));
  $range = "AND stop_sequence >= '{$prevTimePoint['stop_sequence']}' AND stop_sequence <= '{$nextTimePoint['stop_sequence']}'";
  foreach (getTimeInterpolatedTrip($tripID,$range) as $tripStop) {
  if ($tripStop['stop_sequence'] == $stop_sequence) return $tripStop;
  }
  return Array();
  }
  function getTripStartTime($tripID)
  {
  global $conn;
  $query = "Select * from stop_times
  where trip_id = '$tripID'
  AND arrival_time IS NOT NULL
  AND stop_sequence = '1'";
  debug($query, "database");
  $result = pg_query($conn, $query);
  if (!$result) {
  databaseError(pg_result_error($result));
  return Array();
  }
  $r = pg_fetch_assoc($result);
  return $r['arrival_time'];
  }
  function getActiveTrips($time)
  {
  global $conn;
  if ($time == "") $time = current_time();
  $query = "Select distinct stop_times.trip_id, start_times.arrival_time as start_time, end_times.arrival_time as end_time from stop_times, (SELECT trip_id,arrival_time from stop_times WHERE stop_times.arrival_time IS NOT NULL
  AND stop_sequence = '1') as start_times, (SELECT trip_id,max(arrival_time) as arrival_time from stop_times WHERE stop_times.arrival_time IS NOT NULL group by trip_id) as end_times
  WHERE start_times.trip_id = end_times.trip_id AND stop_times.trip_id = end_times.trip_id AND $time > start_times.arrival_time AND $time < end_times.arrival_time";
  debug($query, "database");
  $result = pg_query($conn, $query);
  if (!$result) {
  databaseError(pg_result_error($result));
  return Array();
  }
  return pg_fetch_all($result);
  }
 
  function viaPointNames($tripid, $stop_sequence = "")
  {
  global $conn;
  $query = "SELECT stop_name
  FROM stop_times join stops on stops.stop_id = stop_times.stop_id
  WHERE stop_times.trip_id = '$tripid'
  ".($stop_sequence != "" ? "AND stop_sequence > '$stop_sequence'" : "").
  "AND substr(stop_code,1,2) != 'Wj' ORDER BY stop_sequence";
  debug($query, "database");
  $result = pg_query($conn, $query);
  if (!$result) {
  databaseError(pg_result_error($result));
  return Array();
  }
  $pointNames = pg_fetch_all($result);
  return r_implode(", ", $pointNames);
  }
  ?>
file:a/index.php -> file:b/index.php
<?php <?php
include ('include/common.inc.php'); include ('include/common.inc.php');
include_header("bus.lambdacomplex.org", "index", false) include_header("bus.lambdacomplex.org", "index", false)
?> ?>
<div data-role="page"> <div data-role="page">
<div data-role="content"> <div data-role="content">
<div id="jqm-homeheader"> <div id="jqm-homeheader">
<h1>busness time</h1><br><small>Canberra Bus Timetables and Trip Planner</small> <h1>busness time</h1><br><small>Canberra Bus Timetables and Trip Planner</small>
</div> </div>
<a name="maincontent" id="maincontent"></a> <a name="maincontent" id="maincontent"></a>
<a href="tripPlanner.php" data-role="button" data-icon="navigation">Launch Trip Planner...</a> <a href="tripPlanner.php" data-role="button" data-icon="navigation">Launch Trip Planner...</a>
<ul data-role="listview" data-inset="true" data-theme="c" data-dividertheme="b"> <ul data-role="listview" data-inset="true" data-theme="c" data-dividertheme="b">
<li data-role="list-divider">Timetables - Stops</li> <li data-role="list-divider">Timetables - Stops</li>
<li><a href="stopList.php">Major (Timing Point) Stops</a></li> <li><a href="stopList.php">Major (Timing Point) Stops</a></li>
<li><a href="stopList.php?allstops=yes">All Stops</a></li> <li><a href="stopList.php?allstops=yes">All Stops</a></li>
<li><a href="stopList.php?suburbs=yes">Stops By Suburb</a></li> <li><a href="stopList.php?suburbs=yes">Stops By Suburb</a></li>
<li><a class="nearby" href="stopList.php?nearby=yes">Nearby Stops</a></li> <li><a class="nearby" href="stopList.php?nearby=yes">Nearby Stops</a></li>
</ul> </ul>
<ul data-role="listview" data-inset="true" data-theme="c" data-dividertheme="b"> <ul data-role="listview" data-inset="true" data-theme="c" data-dividertheme="b">
<li data-role="list-divider">Timetables - Routes</li> <li data-role="list-divider">Timetables - Routes</li>
<li><a href="routeList.php">Routes By Final Destination</a></li> <li><a href="routeList.php">Routes By Final Destination</a></li>
<li><a href="routeList.php?bynumber=yes">Routes By Number</a></li> <li><a href="routeList.php?bynumber=yes">Routes By Number</a></li>
<li><a href="routeList.php?bysuburb=yes">Routes By Suburb</a></li> <li><a href="routeList.php?bysuburb=yes">Routes By Suburb</a></li>
<li><a class="nearby" href="routeList.php?nearby=yes">Nearby Routes</a></li> <li><a class="nearby" href="routeList.php?nearby=yes">Nearby Routes</a></li>
</ul> </ul>
<?php <?php
echo timePlaceSettings(); echo timePlaceSettings();
  echo ' <a href="labs/index.php" data-role="button" data-icon="beaker">Busness R&D</a>';
include_footer(true) include_footer(true)
?> ?>
   
symlink:b/labs/css (new)
  ../css/
file:b/labs/index.php (new)
  <?php
  include ('../include/common.inc.php');
  include_header("Busness R&D", "index")
  ?>
  <ul data-role="listview" data-theme="e" data-groupingtheme="e">
  <li data-role="list-divider" > Experimental Features </li>
  <li><a href="mywaybalance.php"><h3>MyWay Balance for mobile</h3>
  <p>Mobile viewer for MyWay balance. Warning! No HTTPS security.</p></a></li>
  <li>More coming soon!</li>
  </ul>
  </div>
  <?php
  include_footer()
  ?>
 
 
symlink:b/labs/js (new)
  ../js
symlink:b/labs/lib (new)
  ../lib
<?php <?php
function cleanString($subject) function cleanString($subject)
{ {
$subject = str_replace("&nbsp;", " ", $subject); $subject = str_replace("&nbsp;", " ", $subject);
$subject = str_replace("&", "&amp;", $subject); $subject = str_replace("&", "&amp;", $subject);
$subject = preg_replace('/[^\r\n\t\x20-\x7E\xA0-\xFF]/', '', $subject); $subject = preg_replace('/[^\r\n\t\x20-\x7E\xA0-\xFF]/', '', $subject);
$subject = str_replace(" ", " ", $subject); $subject = str_replace(" ", " ", $subject);
return trim($subject); return trim($subject);
} }
$return = Array(); $return = Array();
/*if (file_exists("mywayresponse.txt")) { /*if (file_exists("mywayresponse.txt")) {
@$fh = fopen("mywayresponse.txt", 'r'); @$fh = fopen("mywayresponse.txt", 'r');
if ($fh) { if ($fh) {
$pageHTML = fread($fh, filesize("mywayresponse.txt")); $pageHTML = fread($fh, filesize("mywayresponse.txt"));
fclose($fh); fclose($fh);
} }
}*/ }*/
//set POST variables //set POST variables
$url = 'https://www.action.act.gov.au/ARTS/use_Funcs.asp'; $url = 'https://www.action.act.gov.au/ARTS/use_Funcs.asp';
$field_mapping = Array( $field_mapping = Array(
"card_number" => "SRNO", "card_number" => "SRNO",
"DOBmonth" => "month", "DOBmonth" => "month",
"DOBday" => "day", "DOBday" => "day",
"DOByear" => "year", "DOByear" => "year",
"secret_answer" => "pwrd", "secret_answer" => "pwrd",
"button" => "button" "button" => "button"
); );
foreach (Array( foreach (Array(
"card_number", "card_number",
"DOBday", "DOBday",
"DOBmonth", "DOBmonth",
"DOByear" "DOByear"
) as $field_name) { ) as $field_name) {
if (isset($_REQUEST[$field_name])) { if (isset($_REQUEST[$field_name])) {
$fields[$field_name] = filter_var($_REQUEST[$field_name], FILTER_SANITIZE_NUMBER_INT); $fields[$field_name] = filter_var($_REQUEST[$field_name], FILTER_SANITIZE_NUMBER_INT);
} }
else { else {
$return["error"][] = $field_name. " parameter invalid or unspecified"; $return["error"][] = $field_name. " parameter invalid or unspecified";
} }
} }
if (isset($_REQUEST['secret_answer'])) { if (isset($_REQUEST['secret_answer'])) {
$fields['secret_answer'] = filter_var($_REQUEST['secret_answer'], FILTER_SANITIZE_STRING, Array( $fields['secret_answer'] = filter_var($_REQUEST['secret_answer'], FILTER_SANITIZE_STRING, Array(
FILTER_FLAG_NO_ENCODE_QUOTES, FILTER_FLAG_NO_ENCODE_QUOTES,
FILTER_FLAG_STRIP_HIGH, FILTER_FLAG_STRIP_HIGH,
FILTER_FLAG_STRIP_LOW FILTER_FLAG_STRIP_LOW
)); ));
} }
else { else {
$return["error"][] = "secret_answer parameter invalid or unspecified"; $return["error"][] = "secret_answer parameter invalid or unspecified";
} }
$fields['button'] = 'Submit'; $fields['button'] = 'Submit';
$fields_string = ""; $fields_string = "";
//url-ify the data for the POST //url-ify the data for the POST
foreach ($fields as $key => $value) { foreach ($fields as $key => $value) {
if (sizeof($value) === 0) $return['error'][] = $key . " parameter invalid or unspecified"; if (sizeof($value) === 0) $return['error'][] = $key . " parameter invalid or unspecified";
$fields_string.= $field_mapping[$key] . '=' . $value . '&'; $fields_string.= $field_mapping[$key] . '=' . $value . '&';
} }
$fields_string = rtrim($fields_string, '&'); $fields_string = rtrim($fields_string, '&');
if (!isset($return['error'])) { if (!isset($return['error'])) {
//open connection //open connection
$ch = curl_init(); $ch = curl_init();
//set the url, number of POST vars, POST data //set the url, number of POST vars, POST data
curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, count($fields)); curl_setopt($ch, CURLOPT_POST, count($fields));
curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string); curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_TIMEOUT, 30); curl_setopt($ch, CURLOPT_TIMEOUT, 30);
//execute post //execute post
$pageHTML = curl_exec($ch); $pageHTML = curl_exec($ch);
if (curl_errno($ch)) $return["error"][] = "Network error " . curl_errno($ch) . " " . curl_error($ch) . " " . $url . $fields_string; if (curl_errno($ch)) $return["error"][] = "Network error " . curl_errno($ch) . " " . curl_error($ch) . " " . $url . $fields_string;
//close connection //close connection
curl_close($ch); curl_close($ch);
} }
   
if (!isset($return['error'])) { if (!isset($return['error'])) {
include_once ('simple_html_dom.php'); include_once ('lib/simple_html_dom.php');
$page = str_get_html($pageHTML); $page = str_get_html($pageHTML);
$pageAlerts = $page->find(".smartCardAlert"); $pageAlerts = $page->find(".smartCardAlert");
if (sizeof($pageAlerts) > 0) { if (sizeof($pageAlerts) > 0) {
$return['error'][] = $pageAlerts[0]->plaintext; $return['error'][] = $pageAlerts[0]->plaintext;
} }
if (!isset($return['error'])) { if (!isset($return['error'])) {
$tableNum = 0; $tableNum = 0;
$tableName = Array( $tableName = Array(
1 => "myway_carddetails", 1 => "myway_carddetails",
2 => "myway_transactions" 2 => "myway_transactions"
); );
foreach ($page->find("table") as $table) { foreach ($page->find("table") as $table) {
$tableNum++; $tableNum++;
$tableColumns = Array(); $tableColumns = Array();
$tableColumnNum = 0; $tableColumnNum = 0;
foreach ($table->find("th") as $th) { foreach ($table->find("th") as $th) {
$tableColumns[$tableColumnNum] = cleanString($th->plaintext); $tableColumns[$tableColumnNum] = cleanString($th->plaintext);
$tableColumnNum++; $tableColumnNum++;
} }
$tableRowNum = 0; $tableRowNum = 0;
foreach ($table->find("tr") as $tr) { foreach ($table->find("tr") as $tr) {
$tableColumnNum = 0; $tableColumnNum = 0;
foreach ($tr->find("td") as $td) { foreach ($tr->find("td") as $td) {
if ($tableNum == 1) $return[$tableName[$tableNum]][$tableColumns[$tableColumnNum]] = cleanString($td->plaintext); if ($tableNum == 1) $return[$tableName[$tableNum]][$tableColumns[$tableColumnNum]] = cleanString($td->plaintext);
else $return[$tableName[$tableNum]][$tableRowNum][$tableColumns[$tableColumnNum]] = cleanString($td->plaintext); else $return[$tableName[$tableNum]][$tableRowNum][$tableColumns[$tableColumnNum]] = cleanString($td->plaintext);
$tableColumnNum++; $tableColumnNum++;
} }
$tableRowNum++; $tableRowNum++;
} }
} }
} }
} }
if (sizeof($return) == 0) { if (sizeof($return) == 0) {
$return['error'][] = "No data extracted from MyWay website - API may be out of date"; $return['error'][] = "No data extracted from MyWay website - API may be out of date";
} }
   
header('Content-Type: text/javascript; charset=utf8'); header('Content-Type: text/javascript; charset=utf8');
// header('Access-Control-Allow-Origin: http://bus.lambdacomplex.org/'); // header('Access-Control-Allow-Origin: http://bus.lambdacomplex.org/');
header('Access-Control-Max-Age: 3628800'); header('Access-Control-Max-Age: 3628800');
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE'); header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE');
if (isset($_GET['callback'])) { if (isset($_GET['callback'])) {
$json = '(' . json_encode($return) . ');'; //must wrap in parens and end with semicolon $json = '(' . json_encode($return) . ');'; //must wrap in parens and end with semicolon
print_r($_GET['callback'] . $json); //callback is prepended for json-p print_r($_GET['callback'] . $json); //callback is prepended for json-p
} }
else echo json_encode($return); else echo json_encode($return);
?> ?>
   
<?php <?php
include ('../include/common.inc.php'); include ('../include/common.inc.php');
include_header("MyWay Balance", "mywayBalance", true, false, true); include_header("MyWay Balance", "mywayBalance", false, false, true);
  echo '<div data-role="page">
  <div data-role="header" data-position="inline">
  <a href="' . $_SERVER["HTTP_REFERER"] . '" data-icon="arrow-l" data-rel="back" class="ui-btn-left">Back</a>
  <h1>MyWay Balance</h1>
  <a href="mywaybalance.php?logout=yes" data-icon="delete" class="ui-btn-right">Logout</a>
  </div><!-- /header -->
  <a name="maincontent" id="maincontent"></a>
  <div data-role="content"> ';
   
$return = Array(); $return = Array();
  function logout() {
  setcookie("card_number", "", time() - 60 * 60 * 24 * 100, "/");
  setcookie("date", "", time() - 60 * 60 * 24 * 100, "/");
  setcookie("secret_answer", "", time() - 60 * 60 * 24 * 100, "/");
  }
function printBalance($cardNumber, $date, $pwrd) function printBalance($cardNumber, $date, $pwrd)
{ {
global $return; global $return;
$return = json_decode(getPage(curPageURL() . "/myway_api.json.php?card_number=$cardNumber&DOBday={$date[0]}&DOBmonth={$date[1]}&DOByear={$date[2]}&secret_answer=$pwrd"), true); $return = json_decode(getPage(curPageURL() . "/myway_api.json.php?card_number=$cardNumber&DOBday={$date[0]}&DOBmonth={$date[1]}&DOByear={$date[2]}&secret_answer=$pwrd") , true);
  if (isset($return['error'])) {
if (isset($return['error'])) { logout();
echo "<font color=red>" . var_dump($return['error']) . "</font>"; echo '<h3><font color="red">' . $return['error'][0] . "</font></h3>";
} else { }
  else {
echo "<h2>Balance: " . $return['myway_carddetails']['Card Balance'] . "</h2>"; echo "<h2>Balance: " . $return['myway_carddetails']['Card Balance'] . "</h2>";
echo '<ul data-role="listview" data-inset="true"><li data-role="list-divider"> Recent Transactions </li>'; echo '<ul data-role="listview" data-inset="true"><li data-role="list-divider"> Recent Transactions </li>';
foreach ($return['myway_transactions'] as $transaction) { foreach ($return['myway_transactions'] as $transaction) {
echo "<li><b>" . $transaction["Date / Time"] . "</b>"; echo "<li><b>" . $transaction["Date / Time"] . "</b>";
echo "<br><small>" . $transaction["TX Reference No / Type"]. "</small>"; echo "<br><small>" . $transaction["TX Reference No / Type"] . "</small>";
echo '<p class="ui-li-aside">'.$transaction["TX Amount"].'</p>'; echo '<p class="ui-li-aside">' . $transaction["TX Amount"] . '</p>';
echo "</li>"; echo "</li>";
} }
echo "</ul>"; echo "</ul>";
} }
} }
if (isset($_REQUEST['card_number']) && isset($_REQUEST['date']) && isset($_REQUEST['secret_answer'])) { if (isset($_REQUEST['card_number']) && isset($_REQUEST['date']) && isset($_REQUEST['secret_answer'])) {
$cardNumber = $_REQUEST['card_number']; $cardNumber = $_REQUEST['card_number'];
$date = explode("/", $_REQUEST['date']); $date = explode("/", $_REQUEST['date']);
$pwrd = $_REQUEST['secret_answer']; $pwrd = $_REQUEST['secret_answer'];
if ($_REQUEST['remember'] == true) { if ($_REQUEST['remember'] == "on") {
$_COOKIE['card_number'] = $cardNumber; setcookie("card_number", $cardNumber, time() + 60 * 60 * 24 * 100, "/");
$_COOKIE['date'] = $date; setcookie("date", $_REQUEST['date'], time() + 60 * 60 * 24 * 100, "/");
$_COOKIE['secret_answer'] = $pwrd; setcookie("secret_answer", $pwrd, time() + 60 * 60 * 24 * 100, "/");
} }
printBalance($cardNumber, $date, $pwrd); printBalance($cardNumber, $date, $pwrd);
  }
  else if (isset($_REQUEST['logout'])) {
  echo '<center><h3> Logged out of MyWay balance </h3><a href="/index.php">Back to main menu...</a><center>';
} }
else if (isset($_COOKIE['card_number']) && isset($_COOKIE['date']) && isset($_COOKIE['secret_answer'])) { else if (isset($_COOKIE['card_number']) && isset($_COOKIE['date']) && isset($_COOKIE['secret_answer'])) {
$cardNumber = $_COOKIE['card_number']; $cardNumber = $_COOKIE['card_number'];
$date = explode("/", $_COOKIE['date']); $date = explode("/", $_COOKIE['date']);
$pwrd = $_COOKIE['secret_answer']; $pwrd = $_COOKIE['secret_answer'];
printBalance($cardNumber, $date, $pwrd); printBalance($cardNumber, $date, $pwrd);
} }
else { else {
$date = (isset($_REQUEST['date']) ? filter_var($_REQUEST['date'], FILTER_SANITIZE_STRING) : date("m/d/Y")); $date = (isset($_REQUEST['date']) ? filter_var($_REQUEST['date'], FILTER_SANITIZE_STRING) : date("m/d/Y"));
echo '<form action="" method="post"> echo '<form action="" method="post">
<div data-role="fieldcontain"> <div data-role="fieldcontain">
<label for="card_number">Card number</label> <label for="card_number">Card number</label>
<input type="text" name="card_number" id="card_number" value="' . $card_number . '" /> <input type="text" name="card_number" id="card_number" value="' . $card_number . '" />
</div> </div>
<div data-role="fieldcontain"> <div data-role="fieldcontain">
<label for="date"> Date of birth </label> <label for="date"> Date of birth </label>
<input type="text" name="date" id="date" value="' . $date . '" /> <input type="text" name="date" id="date" value="' . $date . '" />
</div> </div>
<div data-role="fieldcontain"> <div data-role="fieldcontain">
<label for="secret_answer"> Secret question answer </label> <label for="secret_answer"> Secret question answer </label>
<input type="text" name="secret_answer" id="secret_answer" value="' . $secret_answer . '" /> <input type="text" name="secret_answer" id="secret_answer" value="' . $secret_answer . '" />
</div> </div>
<div data-role="fieldcontain"> <div data-role="fieldcontain">
<label for="remember"> Remember these details? </label> <label for="remember"> Remember these details? </label>
<input type="checkbox" name="remember" id="remember" checked="yes" /> <input type="checkbox" name="remember" id="remember" checked="yes" />
</div> </div>
<input type="submit" value="Go!"></form>'; <input type="submit" value="Go!"></form>';
} }
include_footer(); include_footer();
?> ?>
   
<?php <?php
// http://www.herethere.net/~samson/php/color_gradient/color_gradient_generator.php.txt // http://www.herethere.net/~samson/php/color_gradient/color_gradient_generator.php.txt
// return the interpolated value between pBegin and pEnd // return the interpolated value between pBegin and pEnd
function interpolate($pBegin, $pEnd, $pStep, $pMax) function interpolate($pBegin, $pEnd, $pStep, $pMax)
{ {
if ($pBegin < $pEnd) { if ($pBegin < $pEnd) {
return (($pEnd - $pBegin) * ($pStep / $pMax)) + $pBegin; return (($pEnd - $pBegin) * ($pStep / $pMax)) + $pBegin;
} }
else { else {
return (($pBegin - $pEnd) * (1 - ($pStep / $pMax))) + $pEnd; return (($pBegin - $pEnd) * (1 - ($pStep / $pMax))) + $pEnd;
  }
  }
  require ("../lib/rolling-curl/RollingCurl.php");
  function processResult_cb($response, $info, $request)
  {
  global $testRegions, $regionTimes,$csv,$kml, $latdeltasize,$londeltasize;
  $md = $request->metadata;
  $tripplan = json_decode($response);
  $plans = Array();
  //var_dump(Array($info, $request));
  if (is_array($tripplan->plan->itineraries->itinerary)) {
  foreach ($tripplan->plan->itineraries->itinerary as $itineraryNumber => $itinerary) {
  $plans[floor($itinerary->duration / 60000) ] = $itinerary;
  }
  }
  else {
  $plans[floor($tripplan->plan->itineraries->itinerary->duration / 60000) ] = $tripplan->plan->itineraries->itinerary;
  }
  if ($csv) echo "{$md['i']},{$md['j']}," . min(array_keys($plans)) . ",$latdeltasize, $londeltasize,{$md['key']}\n";
  if ($kml) {
  $time = min(array_keys($plans));
  $plan = "";
  if (is_array($plans[min(array_keys($plans)) ]->legs->leg)) {
  foreach ($plans[min(array_keys($plans)) ]->legs->leg as $legNumber => $leg) {
  $plan.= processLeg($legNumber, $leg) . ",";
  }
  }
  else {
  $plan.= processLeg(0, $plans[min(array_keys($plans)) ]->legs->leg);
  }
  if (isset($tripplan->error) && $tripplan->error->id == 404) {
  $time = 999;
  $plan = "Trip not possible without excessive walking from nearest bus stop";
  }
  $testRegions[] = Array(
  "lat" => $md['i'],
  "lon" => $md['j'],
  "time" => $time,
  "latdeltasize" => $latdeltasize,
  "londeltasize" => $londeltasize,
  "regionname" => $md['key'],
  "plan" => $plan . "<br/><a href='" . htmlspecialchars($url) . "'>original plan</a>"
  );
  $regionTimes[] = $time;
} }
} }
function Gradient($HexFrom, $HexTo, $ColorSteps) function Gradient($HexFrom, $HexTo, $ColorSteps)
{ {
$theColorBegin = hexdec($HexFrom); $theColorBegin = hexdec($HexFrom);
$theColorEnd = hexdec($HexTo); $theColorEnd = hexdec($HexTo);
$theNumSteps = intval($ColorSteps); $theNumSteps = intval($ColorSteps);
$theR0 = ($theColorBegin & 0xff0000) >> 16; $theR0 = ($theColorBegin & 0xff0000) >> 16;
$theG0 = ($theColorBegin & 0x00ff00) >> 8; $theG0 = ($theColorBegin & 0x00ff00) >> 8;
$theB0 = ($theColorBegin & 0x0000ff) >> 0; $theB0 = ($theColorBegin & 0x0000ff) >> 0;
$theR1 = ($theColorEnd & 0xff0000) >> 16; $theR1 = ($theColorEnd & 0xff0000) >> 16;
$theG1 = ($theColorEnd & 0x00ff00) >> 8; $theG1 = ($theColorEnd & 0x00ff00) >> 8;
$theB1 = ($theColorEnd & 0x0000ff) >> 0; $theB1 = ($theColorEnd & 0x0000ff) >> 0;
$GradientColors = array(); $GradientColors = array();
// generate gradient swathe now // generate gradient swathe now
for ($i = 0; $i <= $theNumSteps; $i++) { for ($i = 0; $i <= $theNumSteps; $i++) {
$theR = interpolate($theR0, $theR1, $i, $theNumSteps); $theR = interpolate($theR0, $theR1, $i, $theNumSteps);
$theG = interpolate($theG0, $theG1, $i, $theNumSteps); $theG = interpolate($theG0, $theG1, $i, $theNumSteps);
$theB = interpolate($theB0, $theB1, $i, $theNumSteps); $theB = interpolate($theB0, $theB1, $i, $theNumSteps);
$theVal = ((($theR << 8) | $theG) << 8) | $theB; $theVal = ((($theR << 8) | $theG) << 8) | $theB;
$GradientColors[] = sprintf("%06X", $theVal); $GradientColors[] = sprintf("%06X", $theVal);
} }
return $GradientColors; return $GradientColors;
} }
function processLeg($legNumber, $leg) function processLeg($legNumber, $leg)
{ {
$legArray = object2array($leg); $legArray = object2array($leg);
if ($legArray["@mode"] === "BUS") { if ($legArray["@mode"] === "BUS") {
return "bus {$legArray['@route']} " . str_replace("To", "towards", $legArray['@headsign']); return "bus {$legArray['@route']} " . str_replace("To", "towards", $legArray['@headsign']);
} }
else { else {
return "walk"; return "walk";
//$walkingstep = "walk "; //$walkingstep = "walk ";
//if (strpos($step->streetName, "from") !== false && strpos($step->streetName, "way") !== false) { //if (strpos($step->streetName, "from") !== false && strpos($step->streetName, "way") !== false) {
// $walkingstep.= "footpath"; // $walkingstep.= "footpath";
//} //}
//else { //else {
// $walkingstep.= $step->streetName; // $walkingstep.= $step->streetName;
//} //}
//$walkingstep.= floor($step->distance) . "m"; //$walkingstep.= floor($step->distance) . "m";
//return $walkingstep; //return $walkingstep;
   
} }
} }
$csv = false; $csv = false;
$kml = true; $kml = true;
if ($kml) { if ($kml) {
//header('Content-Type: application/vnd.google-earth.kml+xml'); header('Content-Type: application/vnd.google-earth.kml+xml');
echo '<?xml version="1.0" encoding="UTF-8"?> echo '<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2"><Document>'; <kml xmlns="http://www.opengis.net/kml/2.2"><Document>';
} }
include ('../include/common.inc.php'); include ('../include/common.inc.php');
//Test code to grab transit times  
// make sure to sleep(10);  
$boundingBoxes = Array( $boundingBoxes = Array(
"belconnen" => Array( "belconnen" => Array(
"startlat" => - 35.1928, "startlat" => - 35.1928,
"startlon" => 149.006, "startlon" => 149.006,
"finishlat" => - 35.2630, "finishlat" => - 35.2630,
"finishlon" => 149.1045, "finishlon" => 149.1045,
) , ) ,
"north gungahlin civic" => Array( "north gungahlin civic" => Array(
"startlat" => - 35.1828, "startlat" => - 35.1828,
"startlon" => 149.1045, "startlon" => 149.1045,
"finishlat" => - 35.2955, "finishlat" => - 35.2955,
"finishlon" => 149.1559, "finishlon" => 149.1559,
) , ) ,
"west duffy" => Array( "west duffy" => Array(
"startlat" => - 35.3252, "startlat" => - 35.3252,
"startlon" => 149.0240, "startlon" => 149.0240,
"finishlat" => - 35.3997, "finishlat" => - 35.3997,
"finishlon" => 149.0676, "finishlon" => 149.0676,
) , ) ,
"central south" => Array( "central south" => Array(
"startlat" => - 35.3042, "startlat" => - 35.3042,
"startlon" => 149.0762, "startlon" => 149.0762,
"finishlat" => - 35.3370, "finishlat" => - 35.3370,
"finishlon" => 149.1806, "finishlon" => 149.1806,
) , ) ,
"south" => Array( "south" => Array(
"startlat" => - 35.3403, "startlat" => - 35.3403,
"startlon" => 149.0714, "startlon" => 149.0714,
"finishlat" => - 35.4607, "finishlat" => - 35.4607,
"finishlon" => 149.1243, "finishlon" => 149.1243,
) )
); );
$latdeltasize = 0.01; $latdeltasize = 0.01;
$londeltasize = 0.01; $londeltasize = 0.01;
$from = "Wattle Street"; $from = "Wattle Street";
$fromPlace = (startsWith($from, "-") ? $from : geocode($from, false)); $fromPlace = (startsWith($from, "-") ? $from : geocode($from, false));
$startTime = "9:00 am"; $startTime = "9:00 am";
$startDate = "03/21/2011"; // american dates, OTP does not validate! $startDate = "03/21/2011"; // american dates, OTP does not validate!
$counter = 0; $counter = 0;
$regionTimes = Array(); $regionTimes = Array();
$testRegions = Array(); $testRegions = Array();
$useragent = "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.1) Gecko/20061204 Firefox/2.0.0.1"; $useragent = "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.1) Gecko/20061204 Firefox/2.0.0.1";
if ($csv) echo "<pre>"; if ($csv) echo "<pre>";
if ($csv) echo "lat,lon,time,latdeltasize, londeltasize, region key name\n"; if ($csv) echo "lat,lon,time,latdeltasize, londeltasize, region key name\n";
  $rc = new RollingCurl("processResult_cb");
  $rc->window_size = 3;
foreach ($boundingBoxes as $key => $boundingBox) { foreach ($boundingBoxes as $key => $boundingBox) {
for ($i = $boundingBox['startlat']; $i >= $boundingBox['finishlat']; $i-= $latdeltasize) { for ($i = $boundingBox['startlat']; $i >= $boundingBox['finishlat']; $i-= $latdeltasize) {
for ($j = $boundingBox['startlon']; $j <= $boundingBox['finishlon']; $j+= $londeltasize) { for ($j = $boundingBox['startlon']; $j <= $boundingBox['finishlon']; $j+= $londeltasize) {
$url = $otpAPIurl . "ws/plan?date=" . urlencode($startDate) . "&time=" . urlencode($startTime) . "&mode=TRANSIT%2CWALK&optimize=QUICK&maxWalkDistance=440&wheelchair=false&toPlace=" . $i . "," . $j . "&fromPlace=$fromPlace"; $url = $otpAPIurl . "ws/plan?date=" . urlencode($startDate) . "&time=" . urlencode($startTime) . "&mode=TRANSIT%2CWALK&optimize=QUICK&maxWalkDistance=440&wheelchair=false&toPlace=" . $i . "," . $j . "&fromPlace=$fromPlace";
$ch = curl_init($url); $request = new RollingCurlRequest($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $request->headers = Array(
curl_setopt($ch, CURLOPT_HEADER, 0);  
curl_setopt($ch, CURLOPT_HTTPHEADER, array(  
"Accept: application/json" "Accept: application/json"
)); );
curl_setopt($ch, CURLOPT_TIMEOUT, 5); $request->metadata = Array( "i" => $i, "j" => $j, "key" => $key);
$page = curl_exec($ch); $rc->add($request);
if (curl_errno($ch)) { }
if ($csv) echo "Trip planner temporarily unavailable: " . curl_errno($ch) . " " . curl_error($ch); }
} }
else { $rc->execute();
$tripplan = json_decode($page);  
$plans = Array();  
if (is_array($tripplan->plan->itineraries->itinerary)) {  
foreach ($tripplan->plan->itineraries->itinerary as $itineraryNumber => $itinerary) {  
$plans[floor($itinerary->duration / 60000) ] = $itinerary;  
}  
}  
else {  
$plans[floor($tripplan->plan->itineraries->itinerary->duration / 60000) ] = $tripplan->plan->itineraries->itinerary;  
}  
if ($csv) echo "$i,$j," . min(array_keys($plans)) . ",$latdeltasize, $londeltasize,$key\n";  
if ($kml) {  
$time = min(array_keys($plans));  
$plan = "";  
if (is_array($plans[min(array_keys($plans)) ]->legs->leg)) {  
foreach ($plans[min(array_keys($plans)) ]->legs->leg as $legNumber => $leg) {  
$plan .= processLeg($legNumber, $leg).",";  
}  
}  
else {  
$plan .= processLeg(0, $plans[min(array_keys($plans)) ]->legs->leg);  
}  
if (isset($tripplan->error) && $tripplan->error->id == 404) {  
$time = 999;  
$plan = "Trip not possible without excessive walking from nearest bus stop";  
}  
$testRegions[] = Array(  
"lat" => $i,  
"lon" => $j,  
"time" => $time,  
"latdeltasize" => $latdeltasize,  
"londeltasize" => $londeltasize,  
"regionname" => $key,  
"plan" => $plan . "<br/><a href='". htmlspecialchars($url)."'>original plan</a>"  
);  
$regionTimes[] = $time;  
}  
}  
flush(); @ob_flush();  
curl_close($ch);  
}  
}  
}  
if ($kml) { if ($kml) {
$colorSteps = 9; $colorSteps = 9;
//$minTime = min($regionTimes); //$minTime = min($regionTimes);
//$maxTime = max($regionTimes); //$maxTime = max($regionTimes);
//$rangeTime = $maxTime - $minTime; //$rangeTime = $maxTime - $minTime;
//$deltaTime = $rangeTime / $colorSteps; //$deltaTime = $rangeTime / $colorSteps;
$Gradients = Gradient(strrev("66FF00"), strrev("FF0000"), $colorSteps); // KML is BGR not RGB so strrev $Gradients = Gradient(strrev("66FF00") , strrev("FF0000") , $colorSteps); // KML is BGR not RGB so strrev
foreach ($testRegions as $testRegion) { foreach ($testRegions as $testRegion) {
//$band = (floor(($testRegion[time] - $minTime) / $deltaTime)); //$band = (floor(($testRegion[time] - $minTime) / $deltaTime));
$band = (floor($testRegion[time] / 10)); $band = (floor($testRegion[time] / 10));
if ($band > $colorSteps) $band = $colorSteps; if ($band > $colorSteps) $band = $colorSteps;
echo "<Placemark> echo "<Placemark>
<name>" . $testRegion['regionname'] . " time {$testRegion['time']} band $band</name> <name>" . $testRegion['regionname'] . " time {$testRegion['time']} band $band</name>
<description> {$testRegion['plan']} </description> <description> {$testRegion['plan']} </description>
<Style> <Style>
<PolyStyle> <PolyStyle>
<color>c7" . $Gradients[$band] . "</color>" . // 7f = 50% alpha, c7=78% <color>c7" . $Gradients[$band] . "</color>" . // 7f = 50% alpha, c7=78%
"</PolyStyle> "</PolyStyle>
<LineStyle> <LineStyle>
<color>c7" . $Gradients[$band] . "</color>" . "</LineStyle> <color>c7" . $Gradients[$band] . "</color>" . "</LineStyle>
</Style> </Style>
<Polygon> <Polygon>
<altitudeMode>relativeToGround</altitudeMode> <altitudeMode>relativeToGround</altitudeMode>
<outerBoundaryIs> <outerBoundaryIs>
<LinearRing> <LinearRing>
<coordinates> <coordinates>
" . ($testRegion['lon'] - ($testRegion['londeltasize'] / 2)) . "," . ($testRegion['lat'] - ($testRegion['latdeltasize'] / 2)) . ",500\n" . ($testRegion['lon'] - ($testRegion['londeltasize'] / 2)) . "," . ($testRegion['lat'] + ($testRegion['latdeltasize'] / 2)) . ",500\n" . ($testRegion['lon'] + ($testRegion['londeltasize'] / 2)) . "," . ($testRegion['lat'] + ($testRegion['latdeltasize'] / 2)) . ",500\n" . ($testRegion['lon'] + ($testRegion['londeltasize'] / 2)) . "," . ($testRegion['lat'] - ($testRegion['latdeltasize'] / 2)) . ",500\n" . ($testRegion['lon'] - ($testRegion['londeltasize'] / 2)) . "," . ($testRegion['lat'] - ($testRegion['latdeltasize'] / 2)) . ",500\n" . " " . ($testRegion['lon'] - ($testRegion['londeltasize'] / 2)) . "," . ($testRegion['lat'] - ($testRegion['latdeltasize'] / 2)) . ",500\n" . ($testRegion['lon'] - ($testRegion['londeltasize'] / 2)) . "," . ($testRegion['lat'] + ($testRegion['latdeltasize'] / 2)) . ",500\n" . ($testRegion['lon'] + ($testRegion['londeltasize'] / 2)) . "," . ($testRegion['lat'] + ($testRegion['latdeltasize'] / 2)) . ",500\n" . ($testRegion['lon'] + ($testRegion['londeltasize'] / 2)) . "," . ($testRegion['lat'] - ($testRegion['latdeltasize'] / 2)) . ",500\n" . ($testRegion['lon'] - ($testRegion['londeltasize'] / 2)) . "," . ($testRegion['lat'] - ($testRegion['latdeltasize'] / 2)) . ",500\n" . "
</coordinates> </coordinates>
</LinearRing> </LinearRing>
</outerBoundaryIs> </outerBoundaryIs>
</Polygon> </Polygon>
</Placemark>"; </Placemark>";
} }
echo "\n</Document></kml>\n"; echo "\n</Document></kml>\n";
} }
if ($csv) echo "</pre>"; if ($csv) echo "</pre>";
?> ?>
   
<?php <?php
include ('include/common.inc.php'); include ('include/common.inc.php');
$output = Array(); $output = Array();
$output['hotspots'] = Array(); $output['hotspots'] = Array();
$output['layer'] = "canberrabusstops"; $output['layer'] = "canberrabusstops";
$max_page = 10; $max_page = 10;
$max_results = 50; $max_results = 50;
$page_start = 0 + filter_var($_REQUEST['pageKey'], FILTER_SANITIZE_NUMBER_INT); $page_start = 0 + filter_var($_REQUEST['pageKey'], FILTER_SANITIZE_NUMBER_INT);
$page_end = $max_page + filter_var($_REQUEST['pageKey'], FILTER_SANITIZE_NUMBER_INT); $page_end = $max_page + filter_var($_REQUEST['pageKey'], FILTER_SANITIZE_NUMBER_INT);
$lat = filter_var($_REQUEST['lat'], FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION); $lat = filter_var($_REQUEST['lat'], FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);
$lon = filter_var($_REQUEST['lon'], FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION); $lon = filter_var($_REQUEST['lon'], FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION);
$url = $APIurl . "/json/neareststops?lat=$lat&lon=$lon&limit=50"; $max_distance = filter_var($_REQUEST['radius'], FILTER_SANITIZE_NUMBER_INT);
$contents = json_decode(getPage($url)); $contents = getNearbyStops($lat, $lon, 50, $max_distance);
debug(print_r($contents, true));  
$stopNum = 0; $stopNum = 0;
foreach ($contents as $row) { foreach ($contents as $stop) {
$stopNum++; $stopNum++;
if ($stopNum > $page_start && $stopNum <= $page_end) { if ($stopNum > $page_start && $stopNum <= $page_end) {
$hotspot = Array(); $hotspot = Array();
$hotspot['id'] = $row[0]; $hotspot['id'] = $stop['stop_id'];
$hotspot['title'] = $row[1]; $hotspot['title'] = $stop['stop_name'];
$hotspot['type'] = 0; $hotspot['type'] = 0;
$hotspot['lat'] = floor($row[2] * 1000000); $hotspot['lat'] = floor($stop['stop_lat'] * 1000000);
$hotspot['lon'] = floor($row[3] * 1000000); $hotspot['lon'] = floor($stop['stop_lon'] * 1000000);
$hotspot['distance'] = distance($row[2], $row[3], $_REQUEST['lat'], $_REQUEST['lon']); $hotspot['distance'] = floor($stop['distance']);
  $hotspot['attribution'] = "ACTION Buses";
$hotspot['actions'] = Array( $hotspot['actions'] = Array(
Array( Array(
"label" => 'View more trips/information', "label" => 'View more trips/information',
'uri' => 'http://bus.lambdacomplex.org/' . 'stop.php?stopid=' . $row[0] 'uri' => 'http://bus.lambdacomplex.org/' . 'stop.php?stopid=' . $stop['stop_id']
) )
); );
$url = $APIurl . "/json/stoptrips?stop=" . $row[0] . "&time=" . midnight_seconds() . "&service_period=" . service_period() . "&limit=4&time_range=" . strval(90 * 60); $trips = getStopTripsWithTimes($stop['stop_id'], "", "", "", 3);
$trips = json_decode(getPage($url));  
debug(print_r($trips, true));  
foreach ($trips as $key => $row) { foreach ($trips as $key => $row) {
if ($key < 3) { if ($key < 3) {
$hotspot['line' . strval($key + 2) ] = $row[1][1] . ' @ ' . midnight_seconds_to_time($row[0]); $hotspot['line' . strval($key + 2) ] = $row['route_short_name'] . ' ' . $row['route_long_name'] . ' @ ' . $row['arrival_time'];
} }
} }
if (sizeof($trips) == 0) $hotspot['line2'] = 'No trips in the near future.'; if (sizeof($trips) == 0) $hotspot['line2'] = 'No trips in the near future.';
$output['hotspots'][] = $hotspot; $output['hotspots'][] = $hotspot;
} }
} }
if (sizeof($hotspot) > 0) { if (sizeof($hotspot) > 0) {
$output['errorString'] = 'ok'; $output['errorString'] = 'ok';
$output['errorCode'] = 0; $output['errorCode'] = 0;
} }
else { else {
$output['errorString'] = 'no results, try increasing range'; $output['errorString'] = 'no results, try increasing range';
$output['errorCode'] = 21; $output['errorCode'] = 21;
} }
if ($page_end >= $max_results || sizeof($hotspot) < $max_page) { if ($page_end >= $max_results || sizeof($contents) < $page_start+$max_page) {
$output["morePages"] = false; $output["morePages"] = false;
$output["nextPageKey"] = null; $output["nextPageKey"] = null;
} }
else { else {
$output["morePages"] = true; $output["morePages"] = true;
$output["nextPageKey"] = $page_end; $output["nextPageKey"] = $page_end;
} }
echo json_encode($output); echo json_encode($output);
?> ?>
   
file:b/lib/postgis.sh (new)
  createlang -d dbname plpgsql
  psql -d transitdata -f postgis.sql
 
file:b/lib/postgis.sql (new)
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  --
  -- $Id: postgis.sql.in.c 5385 2010-03-09 00:22:41Z pramsey $
  --
  -- PostGIS - Spatial Types for PostgreSQL
  -- http://postgis.refractions.net
  -- Copyright 2001-2003 Refractions Research Inc.
  --
  -- This is free software; you can redistribute and/or modify it under
  -- the terms of the GNU General Public Licence. See the COPYING file.
  --
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  --
  -- WARNING: Any change in this file must be evaluated for compatibility.
  -- Changes cleanly handled by postgis_upgrade.sql are fine,
  -- other changes will require a bump in Major version.
  -- Currently only function replaceble by CREATE OR REPLACE
  -- are cleanly handled.
  --
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
  -- INSTALL VERSION: 1.5.1
 
  SET client_min_messages TO warning;
 
  BEGIN;
 
  -------------------------------------------------------------------
  -- SPHEROID TYPE
  -------------------------------------------------------------------
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION st_spheroid_in(cstring)
  RETURNS spheroid
  AS '$libdir/postgis-1.5','ellipsoid_in'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION st_spheroid_out(spheroid)
  RETURNS cstring
  AS '$libdir/postgis-1.5','ellipsoid_out'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION spheroid_in(cstring)
  RETURNS spheroid
  AS '$libdir/postgis-1.5','ellipsoid_in'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION spheroid_out(spheroid)
  RETURNS cstring
  AS '$libdir/postgis-1.5','ellipsoid_out'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE TYPE spheroid (
  alignment = double,
  internallength = 65,
  input = spheroid_in,
  output = spheroid_out
  );
 
  -------------------------------------------------------------------
  -- GEOMETRY TYPE (lwgeom)
  -------------------------------------------------------------------
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION st_geometry_in(cstring)
  RETURNS geometry
  AS '$libdir/postgis-1.5','LWGEOM_in'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION st_geometry_out(geometry)
  RETURNS cstring
  AS '$libdir/postgis-1.5','LWGEOM_out'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION st_geometry_analyze(internal)
  RETURNS bool
  AS '$libdir/postgis-1.5', 'LWGEOM_analyze'
  LANGUAGE 'C' VOLATILE STRICT;
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION st_geometry_recv(internal)
  RETURNS geometry
  AS '$libdir/postgis-1.5','LWGEOM_recv'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION st_geometry_send(geometry)
  RETURNS bytea
  AS '$libdir/postgis-1.5','LWGEOM_send'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION geometry_in(cstring)
  RETURNS geometry
  AS '$libdir/postgis-1.5','LWGEOM_in'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION geometry_out(geometry)
  RETURNS cstring
  AS '$libdir/postgis-1.5','LWGEOM_out'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION geometry_analyze(internal)
  RETURNS bool
  AS '$libdir/postgis-1.5', 'LWGEOM_analyze'
  LANGUAGE 'C' VOLATILE STRICT;
 
  CREATE OR REPLACE FUNCTION geometry_recv(internal)
  RETURNS geometry
  AS '$libdir/postgis-1.5','LWGEOM_recv'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION geometry_send(geometry)
  RETURNS bytea
  AS '$libdir/postgis-1.5','LWGEOM_send'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE TYPE geometry (
  internallength = variable,
  input = geometry_in,
  output = geometry_out,
  send = geometry_send,
  receive = geometry_recv,
  delimiter = ':',
  analyze = geometry_analyze,
  storage = main
  );
 
  -------------------------------------------
  -- Affine transforms
  -------------------------------------------
 
  -- Availability: 1.1.2
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION Affine(geometry,float8,float8,float8,float8,float8,float8,float8,float8,float8,float8,float8,float8)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_affine'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_Affine(geometry,float8,float8,float8,float8,float8,float8,float8,float8,float8,float8,float8,float8)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_affine'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.1.2
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION Affine(geometry,float8,float8,float8,float8,float8,float8)
  RETURNS geometry
  AS 'SELECT affine($1, $2, $3, 0, $4, $5, 0, 0, 0, 1, $6, $7, 0)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_Affine(geometry,float8,float8,float8,float8,float8,float8)
  RETURNS geometry
  AS 'SELECT affine($1, $2, $3, 0, $4, $5, 0, 0, 0, 1, $6, $7, 0)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.1.2
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION RotateZ(geometry,float8)
  RETURNS geometry
  AS 'SELECT affine($1, cos($2), -sin($2), 0, sin($2), cos($2), 0, 0, 0, 1, 0, 0, 0)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_RotateZ(geometry,float8)
  RETURNS geometry
  AS 'SELECT affine($1, cos($2), -sin($2), 0, sin($2), cos($2), 0, 0, 0, 1, 0, 0, 0)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.1.2
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION Rotate(geometry,float8)
  RETURNS geometry
  AS 'SELECT rotateZ($1, $2)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_Rotate(geometry,float8)
  RETURNS geometry
  AS 'SELECT rotateZ($1, $2)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.1.2
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION RotateX(geometry,float8)
  RETURNS geometry
  AS 'SELECT affine($1, 1, 0, 0, 0, cos($2), -sin($2), 0, sin($2), cos($2), 0, 0, 0)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_RotateX(geometry,float8)
  RETURNS geometry
  AS 'SELECT affine($1, 1, 0, 0, 0, cos($2), -sin($2), 0, sin($2), cos($2), 0, 0, 0)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.1.2
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION RotateY(geometry,float8)
  RETURNS geometry
  AS 'SELECT affine($1, cos($2), 0, sin($2), 0, 1, 0, -sin($2), 0, cos($2), 0, 0, 0)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_RotateY(geometry,float8)
  RETURNS geometry
  AS 'SELECT affine($1, cos($2), 0, sin($2), 0, 1, 0, -sin($2), 0, cos($2), 0, 0, 0)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION Translate(geometry,float8,float8,float8)
  RETURNS geometry
  AS 'SELECT affine($1, 1, 0, 0, 0, 1, 0, 0, 0, 1, $2, $3, $4)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_Translate(geometry,float8,float8,float8)
  RETURNS geometry
  AS 'SELECT affine($1, 1, 0, 0, 0, 1, 0, 0, 0, 1, $2, $3, $4)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION Translate(geometry,float8,float8)
  RETURNS geometry
  AS 'SELECT translate($1, $2, $3, 0)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_Translate(geometry,float8,float8)
  RETURNS geometry
  AS 'SELECT translate($1, $2, $3, 0)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.1.0
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION Scale(geometry,float8,float8,float8)
  RETURNS geometry
  AS 'SELECT affine($1, $2, 0, 0, 0, $3, 0, 0, 0, $4, 0, 0, 0)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_Scale(geometry,float8,float8,float8)
  RETURNS geometry
  AS 'SELECT affine($1, $2, 0, 0, 0, $3, 0, 0, 0, $4, 0, 0, 0)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.1.0
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION Scale(geometry,float8,float8)
  RETURNS geometry
  AS 'SELECT scale($1, $2, $3, 1)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_Scale(geometry,float8,float8)
  RETURNS geometry
  AS 'SELECT scale($1, $2, $3, 1)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.1.0
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION transscale(geometry,float8,float8,float8,float8)
  RETURNS geometry
  AS 'SELECT affine($1, $4, 0, 0, 0, $5, 0,
  0, 0, 1, $2 * $4, $3 * $5, 0)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_transscale(geometry,float8,float8,float8,float8)
  RETURNS geometry
  AS 'SELECT affine($1, $4, 0, 0, 0, $5, 0,
  0, 0, 1, $2 * $4, $3 * $5, 0)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.1.0
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION shift_longitude(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_longitude_shift'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_shift_longitude(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_longitude_shift'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -------------------------------------------------------------------
  -- BOX3D TYPE
  -------------------------------------------------------------------
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION st_box3d_in(cstring)
  RETURNS box3d
  AS '$libdir/postgis-1.5', 'BOX3D_in'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION st_box3d_out(box3d)
  RETURNS cstring
  AS '$libdir/postgis-1.5', 'BOX3D_out'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION box3d_in(cstring)
  RETURNS box3d
  AS '$libdir/postgis-1.5', 'BOX3D_in'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION box3d_out(box3d)
  RETURNS cstring
  AS '$libdir/postgis-1.5', 'BOX3D_out'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE TYPE box3d (
  alignment = double,
  internallength = 48,
  input = box3d_in,
  output = box3d_out
  );
 
  -- Temporary box3d aggregate type to retain full double precision
  -- for ST_Extent(). Should be removed when we change the output
  -- type of ST_Extent() to return something other than BOX2DFLOAT4.
  CREATE OR REPLACE FUNCTION box3d_extent_in(cstring)
  RETURNS box3d_extent
  AS '$libdir/postgis-1.5', 'BOX3D_in'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION box3d_extent_out(box3d_extent)
  RETURNS cstring
  AS '$libdir/postgis-1.5', 'BOX3D_extent_out'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE TYPE box3d_extent (
  alignment = double,
  internallength = 48,
  input = box3d_extent_in,
  output = box3d_extent_out
  );
 
  -- Availability: 1.4.0
  CREATE OR REPLACE FUNCTION box3d_extent(box3d_extent)
  RETURNS box3d
  AS '$libdir/postgis-1.5', 'BOX3D_extent_to_BOX3D'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION box2d(box3d_extent)
  RETURNS box2d
  AS '$libdir/postgis-1.5', 'BOX3D_to_BOX2DFLOAT4'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION geometry(box3d_extent)
  RETURNS geometry
  AS '$libdir/postgis-1.5','BOX3D_to_LWGEOM'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- End of temporary hack
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION xmin(box3d)
  RETURNS FLOAT8
  AS '$libdir/postgis-1.5','BOX3D_xmin'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_XMin(box3d)
  RETURNS FLOAT8
  AS '$libdir/postgis-1.5','BOX3D_xmin'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION ymin(box3d)
  RETURNS FLOAT8
  AS '$libdir/postgis-1.5','BOX3D_ymin'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_YMin(box3d)
  RETURNS FLOAT8
  AS '$libdir/postgis-1.5','BOX3D_ymin'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION zmin(box3d)
  RETURNS FLOAT8
  AS '$libdir/postgis-1.5','BOX3D_zmin'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_ZMin(box3d)
  RETURNS FLOAT8
  AS '$libdir/postgis-1.5','BOX3D_zmin'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION xmax(box3d)
  RETURNS FLOAT8
  AS '$libdir/postgis-1.5','BOX3D_xmax'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_XMax(box3d)
  RETURNS FLOAT8
  AS '$libdir/postgis-1.5','BOX3D_xmax'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION ymax(box3d)
  RETURNS FLOAT8
  AS '$libdir/postgis-1.5','BOX3D_ymax'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_YMax(box3d)
  RETURNS FLOAT8
  AS '$libdir/postgis-1.5','BOX3D_ymax'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION zmax(box3d)
  RETURNS FLOAT8
  AS '$libdir/postgis-1.5','BOX3D_zmax'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_ZMax(box3d)
  RETURNS FLOAT8
  AS '$libdir/postgis-1.5','BOX3D_zmax'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -------------------------------------------------------------------
  -- CHIP TYPE
  -------------------------------------------------------------------
 
  CREATE OR REPLACE FUNCTION chip_in(cstring)
  RETURNS chip
  AS '$libdir/postgis-1.5','CHIP_in'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION chip_out(chip)
  RETURNS cstring
  AS '$libdir/postgis-1.5','CHIP_out'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION ST_chip_in(cstring)
  RETURNS chip
  AS '$libdir/postgis-1.5','CHIP_in'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION ST_chip_out(chip)
  RETURNS cstring
  AS '$libdir/postgis-1.5','CHIP_out'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE TYPE chip (
  alignment = double,
  internallength = variable,
  input = chip_in,
  output = chip_out,
  storage = extended
  );
 
  -----------------------------------------------------------------------
  -- BOX2D
  -----------------------------------------------------------------------
 
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION st_box2d_in(cstring)
  RETURNS box2d
  AS '$libdir/postgis-1.5','BOX2DFLOAT4_in'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION st_box2d_out(box2d)
  RETURNS cstring
  AS '$libdir/postgis-1.5','BOX2DFLOAT4_out'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION box2d_in(cstring)
  RETURNS box2d
  AS '$libdir/postgis-1.5','BOX2DFLOAT4_in'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION box2d_out(box2d)
  RETURNS cstring
  AS '$libdir/postgis-1.5','BOX2DFLOAT4_out'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE TYPE box2d (
  internallength = 16,
  input = box2d_in,
  output = box2d_out,
  storage = plain
  );
 
 
  -------------------------------------------------------------------
  -- BTREE indexes
  -------------------------------------------------------------------
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION st_geometry_lt(geometry, geometry)
  RETURNS bool
  AS '$libdir/postgis-1.5', 'lwgeom_lt'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION st_geometry_le(geometry, geometry)
  RETURNS bool
  AS '$libdir/postgis-1.5', 'lwgeom_le'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION st_geometry_gt(geometry, geometry)
  RETURNS bool
  AS '$libdir/postgis-1.5', 'lwgeom_gt'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION st_geometry_ge(geometry, geometry)
  RETURNS bool
  AS '$libdir/postgis-1.5', 'lwgeom_ge'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION st_geometry_eq(geometry, geometry)
  RETURNS bool
  AS '$libdir/postgis-1.5', 'lwgeom_eq'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION st_geometry_cmp(geometry, geometry)
  RETURNS integer
  AS '$libdir/postgis-1.5', 'lwgeom_cmp'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION geometry_lt(geometry, geometry)
  RETURNS bool
  AS '$libdir/postgis-1.5', 'lwgeom_lt'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION geometry_le(geometry, geometry)
  RETURNS bool
  AS '$libdir/postgis-1.5', 'lwgeom_le'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION geometry_gt(geometry, geometry)
  RETURNS bool
  AS '$libdir/postgis-1.5', 'lwgeom_gt'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION geometry_ge(geometry, geometry)
  RETURNS bool
  AS '$libdir/postgis-1.5', 'lwgeom_ge'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION geometry_eq(geometry, geometry)
  RETURNS bool
  AS '$libdir/postgis-1.5', 'lwgeom_eq'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION geometry_cmp(geometry, geometry)
  RETURNS integer
  AS '$libdir/postgis-1.5', 'lwgeom_cmp'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  --
  -- Sorting operators for Btree
  --
 
  CREATE OPERATOR < (
  LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_lt,
  COMMUTATOR = '>', NEGATOR = '>=',
  RESTRICT = contsel, JOIN = contjoinsel
  );
 
  CREATE OPERATOR <= (
  LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_le,
  COMMUTATOR = '>=', NEGATOR = '>',
  RESTRICT = contsel, JOIN = contjoinsel
  );
 
  CREATE OPERATOR = (
  LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_eq,
  COMMUTATOR = '=', -- we might implement a faster negator here
  RESTRICT = contsel, JOIN = contjoinsel
  );
 
  CREATE OPERATOR >= (
  LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_ge,
  COMMUTATOR = '<=', NEGATOR = '<',
  RESTRICT = contsel, JOIN = contjoinsel
  );
  CREATE OPERATOR > (
  LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_gt,
  COMMUTATOR = '<', NEGATOR = '<=',
  RESTRICT = contsel, JOIN = contjoinsel
  );
 
 
  CREATE OPERATOR CLASS btree_geometry_ops
  DEFAULT FOR TYPE geometry USING btree AS
  OPERATOR 1 < ,
  OPERATOR 2 <= ,
  OPERATOR 3 = ,
  OPERATOR 4 >= ,
  OPERATOR 5 > ,
  FUNCTION 1 geometry_cmp (geometry, geometry);
 
 
 
  -------------------------------------------------------------------
  -- GiST indexes
  -------------------------------------------------------------------
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION postgis_gist_sel (internal, oid, internal, int4)
  RETURNS float8
  AS '$libdir/postgis-1.5', 'LWGEOM_gist_sel'
  LANGUAGE 'C';
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION postgis_gist_joinsel(internal, oid, internal, smallint)
  RETURNS float8
  AS '$libdir/postgis-1.5', 'LWGEOM_gist_joinsel'
  LANGUAGE 'C';
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION st_postgis_gist_sel (internal, oid, internal, int4)
  RETURNS float8
  AS '$libdir/postgis-1.5', 'LWGEOM_gist_sel'
  LANGUAGE 'C';
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION st_postgis_gist_joinsel(internal, oid, internal, smallint)
  RETURNS float8
  AS '$libdir/postgis-1.5', 'LWGEOM_gist_joinsel'
  LANGUAGE 'C';
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION st_geometry_overleft(geometry, geometry)
  RETURNS bool
  AS '$libdir/postgis-1.5', 'LWGEOM_overleft'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION st_geometry_overright(geometry, geometry)
  RETURNS bool
  AS '$libdir/postgis-1.5', 'LWGEOM_overright'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION st_geometry_overabove(geometry, geometry)
  RETURNS bool
  AS '$libdir/postgis-1.5', 'LWGEOM_overabove'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION st_geometry_overbelow(geometry, geometry)
  RETURNS bool
  AS '$libdir/postgis-1.5', 'LWGEOM_overbelow'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION st_geometry_left(geometry, geometry)
  RETURNS bool
  AS '$libdir/postgis-1.5', 'LWGEOM_left'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION st_geometry_right(geometry, geometry)
  RETURNS bool
  AS '$libdir/postgis-1.5', 'LWGEOM_right'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION st_geometry_above(geometry, geometry)
  RETURNS bool
  AS '$libdir/postgis-1.5', 'LWGEOM_above'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION st_geometry_below(geometry, geometry)
  RETURNS bool
  AS '$libdir/postgis-1.5', 'LWGEOM_below'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION st_geometry_contain(geometry, geometry)
  RETURNS bool
  AS '$libdir/postgis-1.5', 'LWGEOM_contain'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION st_geometry_contained(geometry, geometry)
  RETURNS bool
  AS '$libdir/postgis-1.5', 'LWGEOM_contained'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION st_geometry_overlap(geometry, geometry)
  RETURNS bool
  AS '$libdir/postgis-1.5', 'LWGEOM_overlap'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION st_geometry_same(geometry, geometry)
  RETURNS bool
  AS '$libdir/postgis-1.5', 'LWGEOM_samebox'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION geometry_same(geometry, geometry)
  RETURNS bool
  AS '$libdir/postgis-1.5', 'LWGEOM_samebox'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION geometry_gist_sel (internal, oid, internal, int4)
  RETURNS float8
  AS '$libdir/postgis-1.5', 'LWGEOM_gist_sel'
  LANGUAGE 'C';
 
  CREATE OR REPLACE FUNCTION geometry_gist_joinsel(internal, oid, internal, smallint)
  RETURNS float8
  AS '$libdir/postgis-1.5', 'LWGEOM_gist_joinsel'
  LANGUAGE 'C';
 
  CREATE OR REPLACE FUNCTION geometry_overleft(geometry, geometry)
  RETURNS bool
  AS '$libdir/postgis-1.5', 'LWGEOM_overleft'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION geometry_overright(geometry, geometry)
  RETURNS bool
  AS '$libdir/postgis-1.5', 'LWGEOM_overright'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION geometry_overabove(geometry, geometry)
  RETURNS bool
  AS '$libdir/postgis-1.5', 'LWGEOM_overabove'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION geometry_overbelow(geometry, geometry)
  RETURNS bool
  AS '$libdir/postgis-1.5', 'LWGEOM_overbelow'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION geometry_left(geometry, geometry)
  RETURNS bool
  AS '$libdir/postgis-1.5', 'LWGEOM_left'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION geometry_right(geometry, geometry)
  RETURNS bool
  AS '$libdir/postgis-1.5', 'LWGEOM_right'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION geometry_above(geometry, geometry)
  RETURNS bool
  AS '$libdir/postgis-1.5', 'LWGEOM_above'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION geometry_below(geometry, geometry)
  RETURNS bool
  AS '$libdir/postgis-1.5', 'LWGEOM_below'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION geometry_contain(geometry, geometry)
  RETURNS bool
  AS '$libdir/postgis-1.5', 'LWGEOM_contain'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION geometry_contained(geometry, geometry)
  RETURNS bool
  AS '$libdir/postgis-1.5', 'LWGEOM_contained'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION geometry_overlap(geometry, geometry)
  RETURNS bool
  AS '$libdir/postgis-1.5', 'LWGEOM_overlap'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION geometry_samebox(geometry, geometry)
  RETURNS bool
  AS '$libdir/postgis-1.5', 'LWGEOM_samebox'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OPERATOR << (
  LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_left,
  COMMUTATOR = '>>',
  RESTRICT = positionsel, JOIN = positionjoinsel
  );
 
  CREATE OPERATOR &< (
  LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_overleft,
  COMMUTATOR = '&>',
  RESTRICT = positionsel, JOIN = positionjoinsel
  );
 
  CREATE OPERATOR <<| (
  LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_below,
  COMMUTATOR = '|>>',
  RESTRICT = positionsel, JOIN = positionjoinsel
  );
 
  CREATE OPERATOR &<| (
  LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_overbelow,
  COMMUTATOR = '|&>',
  RESTRICT = positionsel, JOIN = positionjoinsel
  );
 
  CREATE OPERATOR && (
  LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_overlap,
  COMMUTATOR = '&&',
  RESTRICT = geometry_gist_sel, JOIN = geometry_gist_joinsel
  );
 
  CREATE OPERATOR &> (
  LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_overright,
  COMMUTATOR = '&<',
  RESTRICT = positionsel, JOIN = positionjoinsel
  );
 
  CREATE OPERATOR >> (
  LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_right,
  COMMUTATOR = '<<',
  RESTRICT = positionsel, JOIN = positionjoinsel
  );
 
  CREATE OPERATOR |&> (
  LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_overabove,
  COMMUTATOR = '&<|',
  RESTRICT = positionsel, JOIN = positionjoinsel
  );
 
  CREATE OPERATOR |>> (
  LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_above,
  COMMUTATOR = '<<|',
  RESTRICT = positionsel, JOIN = positionjoinsel
  );
 
  CREATE OPERATOR ~= (
  LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_samebox,
  COMMUTATOR = '~=',
  RESTRICT = eqsel, JOIN = eqjoinsel
  );
 
  CREATE OPERATOR @ (
  LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_contained,
  COMMUTATOR = '~',
  RESTRICT = contsel, JOIN = contjoinsel
  );
 
  CREATE OPERATOR ~ (
  LEFTARG = geometry, RIGHTARG = geometry, PROCEDURE = geometry_contain,
  COMMUTATOR = '@',
  RESTRICT = contsel, JOIN = contjoinsel
  );
 
  -- gist support functions
 
  CREATE OR REPLACE FUNCTION LWGEOM_gist_consistent(internal,geometry,int4)
  RETURNS bool
  AS '$libdir/postgis-1.5' ,'LWGEOM_gist_consistent'
  LANGUAGE 'C';
 
  CREATE OR REPLACE FUNCTION LWGEOM_gist_compress(internal)
  RETURNS internal
  AS '$libdir/postgis-1.5','LWGEOM_gist_compress'
  LANGUAGE 'C';
 
  CREATE OR REPLACE FUNCTION LWGEOM_gist_penalty(internal,internal,internal)
  RETURNS internal
  AS '$libdir/postgis-1.5' ,'LWGEOM_gist_penalty'
  LANGUAGE 'C';
 
  CREATE OR REPLACE FUNCTION LWGEOM_gist_picksplit(internal, internal)
  RETURNS internal
  AS '$libdir/postgis-1.5' ,'LWGEOM_gist_picksplit'
  LANGUAGE 'C';
 
  CREATE OR REPLACE FUNCTION LWGEOM_gist_union(bytea, internal)
  RETURNS internal
  AS '$libdir/postgis-1.5' ,'LWGEOM_gist_union'
  LANGUAGE 'C';
 
  CREATE OR REPLACE FUNCTION LWGEOM_gist_same(box2d, box2d, internal)
  RETURNS internal
  AS '$libdir/postgis-1.5' ,'LWGEOM_gist_same'
  LANGUAGE 'C';
 
  CREATE OR REPLACE FUNCTION LWGEOM_gist_decompress(internal)
  RETURNS internal
  AS '$libdir/postgis-1.5' ,'LWGEOM_gist_decompress'
  LANGUAGE 'C';
 
  -------------------------------------------
  -- GIST opclass index binding entries.
  -------------------------------------------
  --
  -- Create opclass index bindings for PG>=73
  --
 
  CREATE OPERATOR CLASS gist_geometry_ops
  DEFAULT FOR TYPE geometry USING gist AS
  STORAGE box2d,
  OPERATOR 1 << ,
  OPERATOR 2 &< ,
  OPERATOR 3 && ,
  OPERATOR 4 &> ,
  OPERATOR 5 >> ,
  OPERATOR 6 ~= ,
  OPERATOR 7 ~ ,
  OPERATOR 8 @ ,
  OPERATOR 9 &<| ,
  OPERATOR 10 <<| ,
  OPERATOR 11 |>> ,
  OPERATOR 12 |&> ,
  FUNCTION 1 LWGEOM_gist_consistent (internal, geometry, int4),
  FUNCTION 2 LWGEOM_gist_union (bytea, internal),
  FUNCTION 3 LWGEOM_gist_compress (internal),
  FUNCTION 4 LWGEOM_gist_decompress (internal),
  FUNCTION 5 LWGEOM_gist_penalty (internal, internal, internal),
  FUNCTION 6 LWGEOM_gist_picksplit (internal, internal),
  FUNCTION 7 LWGEOM_gist_same (box2d, box2d, internal);
 
  -------------------------------------------
  -- other lwgeom functions
  -------------------------------------------
 
  CREATE OR REPLACE FUNCTION addbbox(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5','LWGEOM_addBBOX'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION postgis_addbbox(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5','LWGEOM_addBBOX'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION dropbbox(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5','LWGEOM_dropBBOX'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION postgis_dropbbox(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5','LWGEOM_dropBBOX'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION getsrid(geometry)
  RETURNS int4
  AS '$libdir/postgis-1.5','LWGEOM_getSRID'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION getbbox(geometry)
  RETURNS box2d
  AS '$libdir/postgis-1.5','LWGEOM_to_BOX2DFLOAT4'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION postgis_getbbox(geometry)
  RETURNS box2d
  AS '$libdir/postgis-1.5','LWGEOM_to_BOX2DFLOAT4'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION hasbbox(geometry)
  RETURNS bool
  AS '$libdir/postgis-1.5', 'LWGEOM_hasBBOX'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION postgis_hasbbox(geometry)
  RETURNS bool
  AS '$libdir/postgis-1.5', 'LWGEOM_hasBBOX'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -------------------------------------------
  --- CHIP functions
  -------------------------------------------
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION srid(chip)
  RETURNS int4
  AS '$libdir/postgis-1.5','CHIP_getSRID'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_srid(chip)
  RETURNS int4
  AS '$libdir/postgis-1.5','CHIP_getSRID'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION height(chip)
  RETURNS int4
  AS '$libdir/postgis-1.5','CHIP_getHeight'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_height(chip)
  RETURNS int4
  AS '$libdir/postgis-1.5','CHIP_getHeight'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION factor(chip)
  RETURNS FLOAT4
  AS '$libdir/postgis-1.5','CHIP_getFactor'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_factor(chip)
  RETURNS FLOAT4
  AS '$libdir/postgis-1.5','CHIP_getFactor'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION width(chip)
  RETURNS int4
  AS '$libdir/postgis-1.5','CHIP_getWidth'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_width(chip)
  RETURNS int4
  AS '$libdir/postgis-1.5','CHIP_getWidth'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION datatype(chip)
  RETURNS int4
  AS '$libdir/postgis-1.5','CHIP_getDatatype'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_datatype(chip)
  RETURNS int4
  AS '$libdir/postgis-1.5','CHIP_getDatatype'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION compression(chip)
  RETURNS int4
  AS '$libdir/postgis-1.5','CHIP_getCompression'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_compression(chip)
  RETURNS int4
  AS '$libdir/postgis-1.5','CHIP_getCompression'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION setSRID(chip,int4)
  RETURNS chip
  AS '$libdir/postgis-1.5','CHIP_setSRID'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION setFactor(chip,float4)
  RETURNS chip
  AS '$libdir/postgis-1.5','CHIP_setFactor'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_setFactor(chip,float4)
  RETURNS chip
  AS '$libdir/postgis-1.5','CHIP_setFactor'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  ------------------------------------------------------------------------
  -- DEBUG
  ------------------------------------------------------------------------
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION mem_size(geometry)
  RETURNS int4
  AS '$libdir/postgis-1.5', 'LWGEOM_mem_size'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_mem_size(geometry)
  RETURNS int4
  AS '$libdir/postgis-1.5', 'LWGEOM_mem_size'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION summary(geometry)
  RETURNS text
  AS '$libdir/postgis-1.5', 'LWGEOM_summary'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_summary(geometry)
  RETURNS text
  AS '$libdir/postgis-1.5', 'LWGEOM_summary'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION npoints(geometry)
  RETURNS int4
  AS '$libdir/postgis-1.5', 'LWGEOM_npoints'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_npoints(geometry)
  RETURNS int4
  AS '$libdir/postgis-1.5', 'LWGEOM_npoints'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION nrings(geometry)
  RETURNS int4
  AS '$libdir/postgis-1.5', 'LWGEOM_nrings'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_nrings(geometry)
  RETURNS int4
  AS '$libdir/postgis-1.5', 'LWGEOM_nrings'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  ------------------------------------------------------------------------
  -- Misures
  ------------------------------------------------------------------------
 
  -- this is a fake (for back-compatibility)
  -- uses 3d if 3d is available, 2d otherwise
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION length3d(geometry)
  RETURNS FLOAT8
  AS '$libdir/postgis-1.5', 'LWGEOM_length_linestring'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_length3d(geometry)
  RETURNS FLOAT8
  AS '$libdir/postgis-1.5', 'LWGEOM_length_linestring'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION length2d(geometry)
  RETURNS FLOAT8
  AS '$libdir/postgis-1.5', 'LWGEOM_length2d_linestring'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_length2d(geometry)
  RETURNS FLOAT8
  AS '$libdir/postgis-1.5', 'LWGEOM_length2d_linestring'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION length(geometry)
  RETURNS FLOAT8
  AS '$libdir/postgis-1.5', 'LWGEOM_length_linestring'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: length2d(geometry)
  CREATE OR REPLACE FUNCTION ST_Length(geometry)
  RETURNS FLOAT8
  AS '$libdir/postgis-1.5', 'LWGEOM_length2d_linestring'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- this is a fake (for back-compatibility)
  -- uses 3d if 3d is available, 2d otherwise
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION length3d_spheroid(geometry, spheroid)
  RETURNS FLOAT8
  AS '$libdir/postgis-1.5','LWGEOM_length_ellipsoid_linestring'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_length3d_spheroid(geometry, spheroid)
  RETURNS FLOAT8
  AS '$libdir/postgis-1.5','LWGEOM_length_ellipsoid_linestring'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION length_spheroid(geometry, spheroid)
  RETURNS FLOAT8
  AS '$libdir/postgis-1.5','LWGEOM_length_ellipsoid_linestring'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_length_spheroid(geometry, spheroid)
  RETURNS FLOAT8
  AS '$libdir/postgis-1.5','LWGEOM_length_ellipsoid_linestring'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION length2d_spheroid(geometry, spheroid)
  RETURNS FLOAT8
  AS '$libdir/postgis-1.5','LWGEOM_length2d_ellipsoid'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_length2d_spheroid(geometry, spheroid)
  RETURNS FLOAT8
  AS '$libdir/postgis-1.5','LWGEOM_length2d_ellipsoid'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
  -- this is a fake (for back-compatibility)
  -- uses 3d if 3d is available, 2d otherwise
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION perimeter3d(geometry)
  RETURNS FLOAT8
  AS '$libdir/postgis-1.5', 'LWGEOM_perimeter_poly'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_perimeter3d(geometry)
  RETURNS FLOAT8
  AS '$libdir/postgis-1.5', 'LWGEOM_perimeter_poly'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION perimeter2d(geometry)
  RETURNS FLOAT8
  AS '$libdir/postgis-1.5', 'LWGEOM_perimeter2d_poly'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_perimeter2d(geometry)
  RETURNS FLOAT8
  AS '$libdir/postgis-1.5', 'LWGEOM_perimeter2d_poly'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION perimeter(geometry)
  RETURNS FLOAT8
  AS '$libdir/postgis-1.5', 'LWGEOM_perimeter_poly'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: perimeter2d(geometry)
  CREATE OR REPLACE FUNCTION ST_Perimeter(geometry)
  RETURNS FLOAT8
  AS '$libdir/postgis-1.5', 'LWGEOM_perimeter2d_poly'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- this is an alias for 'area(geometry)'
  -- there is nothing such an 'area3d'...
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION area2d(geometry)
  RETURNS FLOAT8
  AS '$libdir/postgis-1.5', 'LWGEOM_area_polygon'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  -- Deprecation in 1.3.4
  CREATE OR REPLACE FUNCTION ST_area2d(geometry)
  RETURNS FLOAT8
  AS '$libdir/postgis-1.5', 'LWGEOM_area_polygon'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION area(geometry)
  RETURNS FLOAT8
  AS '$libdir/postgis-1.5','LWGEOM_area_polygon'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: area(geometry)
  CREATE OR REPLACE FUNCTION ST_Area(geometry)
  RETURNS FLOAT8
  AS '$libdir/postgis-1.5','LWGEOM_area_polygon'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION distance_spheroid(geometry,geometry,spheroid)
  RETURNS FLOAT8
  AS '$libdir/postgis-1.5','LWGEOM_distance_ellipsoid'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_distance_spheroid(geometry,geometry,spheroid)
  RETURNS FLOAT8
  AS '$libdir/postgis-1.5','LWGEOM_distance_ellipsoid'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION distance_sphere(geometry,geometry)
  RETURNS FLOAT8
  AS '$libdir/postgis-1.5','LWGEOM_distance_sphere'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_distance_sphere(geometry,geometry)
  RETURNS FLOAT8
  AS '$libdir/postgis-1.5','LWGEOM_distance_sphere'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
  -- Minimum distance. 2d only.
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION distance(geometry,geometry)
  RETURNS float8
  AS '$libdir/postgis-1.5', 'LWGEOM_mindistance2d'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
  -- PostGIS equivalent function: distance(geometry,geometry)
  CREATE OR REPLACE FUNCTION ST_Distance(geometry,geometry)
  RETURNS float8
  AS '$libdir/postgis-1.5', 'LWGEOM_mindistance2d'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION point_inside_circle(geometry,float8,float8,float8)
  RETURNS bool
  AS '$libdir/postgis-1.5', 'LWGEOM_inside_circle_point'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_point_inside_circle(geometry,float8,float8,float8)
  RETURNS bool
  AS '$libdir/postgis-1.5', 'LWGEOM_inside_circle_point'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION azimuth(geometry,geometry)
  RETURNS float8
  AS '$libdir/postgis-1.5', 'LWGEOM_azimuth'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_azimuth(geometry,geometry)
  RETURNS float8
  AS '$libdir/postgis-1.5', 'LWGEOM_azimuth'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  ------------------------------------------------------------------------
  -- MISC
  ------------------------------------------------------------------------
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION force_2d(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_force_2d'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_force_2d(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_force_2d'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION force_3dz(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_force_3dz'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_force_3dz(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_force_3dz'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- an alias for force_3dz
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION force_3d(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_force_3dz'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_force_3d(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_force_3dz'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION force_3dm(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_force_3dm'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_force_3dm(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_force_3dm'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION force_4d(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_force_4d'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_force_4d(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_force_4d'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION force_collection(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_force_collection'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_force_collection(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_force_collection'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION ST_CollectionExtract(geometry, integer)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'ST_CollectionExtract'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION multi(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_force_multi'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_multi(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_force_multi'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION expand(box3d,float8)
  RETURNS box3d
  AS '$libdir/postgis-1.5', 'BOX3D_expand'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_Expand(box3d,float8)
  RETURNS box3d
  AS '$libdir/postgis-1.5', 'BOX3D_expand'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION expand(box2d,float8)
  RETURNS box2d
  AS '$libdir/postgis-1.5', 'BOX2DFLOAT4_expand'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_expand(box2d,float8)
  RETURNS box2d
  AS '$libdir/postgis-1.5', 'BOX2DFLOAT4_expand'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION expand(geometry,float8)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_expand'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_expand(geometry,float8)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_expand'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION envelope(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_envelope'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: envelope(geometry)
  CREATE OR REPLACE FUNCTION ST_Envelope(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_envelope'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION reverse(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_reverse'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_Reverse(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_reverse'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION ForceRHR(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_forceRHR_poly'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_ForceRHR(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_forceRHR_poly'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION noop(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_noop'
  LANGUAGE 'C' VOLATILE STRICT;
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION postgis_noop(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_noop'
  LANGUAGE 'C' VOLATILE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION zmflag(geometry)
  RETURNS smallint
  AS '$libdir/postgis-1.5', 'LWGEOM_zmflag'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION ST_zmflag(geometry)
  RETURNS smallint
  AS '$libdir/postgis-1.5', 'LWGEOM_zmflag'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION ndims(geometry)
  RETURNS smallint
  AS '$libdir/postgis-1.5', 'LWGEOM_ndims'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_NDims(geometry)
  RETURNS smallint
  AS '$libdir/postgis-1.5', 'LWGEOM_ndims'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION AsEWKT(geometry)
  RETURNS TEXT
  AS '$libdir/postgis-1.5','LWGEOM_asEWKT'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_AsEWKT(geometry)
  RETURNS TEXT
  AS '$libdir/postgis-1.5','LWGEOM_asEWKT'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION AsEWKB(geometry)
  RETURNS BYTEA
  AS '$libdir/postgis-1.5','WKBFromLWGEOM'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_AsEWKB(geometry)
  RETURNS BYTEA
  AS '$libdir/postgis-1.5','WKBFromLWGEOM'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION AsHEXEWKB(geometry)
  RETURNS TEXT
  AS '$libdir/postgis-1.5','LWGEOM_asHEXEWKB'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_AsHEXEWKB(geometry)
  RETURNS TEXT
  AS '$libdir/postgis-1.5','LWGEOM_asHEXEWKB'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION AsHEXEWKB(geometry, text)
  RETURNS TEXT
  AS '$libdir/postgis-1.5','LWGEOM_asHEXEWKB'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_AsHEXEWKB(geometry, text)
  RETURNS TEXT
  AS '$libdir/postgis-1.5','LWGEOM_asHEXEWKB'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION AsEWKB(geometry,text)
  RETURNS bytea
  AS '$libdir/postgis-1.5','WKBFromLWGEOM'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_AsEWKB(geometry,text)
  RETURNS bytea
  AS '$libdir/postgis-1.5','WKBFromLWGEOM'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION GeomFromEWKB(bytea)
  RETURNS geometry
  AS '$libdir/postgis-1.5','LWGEOMFromWKB'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_GeomFromEWKB(bytea)
  RETURNS geometry
  AS '$libdir/postgis-1.5','LWGEOMFromWKB'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION GeomFromEWKT(text)
  RETURNS geometry
  AS '$libdir/postgis-1.5','parse_WKT_lwgeom'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_GeomFromEWKT(text)
  RETURNS geometry
  AS '$libdir/postgis-1.5','parse_WKT_lwgeom'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION postgis_cache_bbox()
  RETURNS trigger
  AS '$libdir/postgis-1.5', 'cache_bbox'
  LANGUAGE 'C';
 
  ------------------------------------------------------------------------
  -- CONSTRUCTORS
  ------------------------------------------------------------------------
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION MakePoint(float8, float8)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_makepoint'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_MakePoint(float8, float8)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_makepoint'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION MakePoint(float8, float8, float8)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_makepoint'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_MakePoint(float8, float8, float8)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_makepoint'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION MakePoint(float8, float8, float8, float8)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_makepoint'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_MakePoint(float8, float8, float8, float8)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_makepoint'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION MakePointM(float8, float8, float8)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_makepoint3dm'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.3.4
  CREATE OR REPLACE FUNCTION ST_MakePointM(float8, float8, float8)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_makepoint3dm'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION MakeBox2d(geometry, geometry)
  RETURNS box2d
  AS '$libdir/postgis-1.5', 'BOX2DFLOAT4_construct'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_MakeBox2d(geometry, geometry)
  RETURNS box2d
  AS '$libdir/postgis-1.5', 'BOX2DFLOAT4_construct'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION MakeBox3d(geometry, geometry)
  RETURNS box3d
  AS '$libdir/postgis-1.5', 'BOX3D_construct'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_MakeBox3d(geometry, geometry)
  RETURNS box3d
  AS '$libdir/postgis-1.5', 'BOX3D_construct'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION makeline_garray (geometry[])
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_makeline_garray'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_MakeLine_GArray (geometry[])
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_makeline_garray'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.4.0
  CREATE OR REPLACE FUNCTION ST_MakeLine (geometry[])
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_makeline_garray'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION LineFromMultiPoint(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_line_from_mpoint'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_LineFromMultiPoint(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_line_from_mpoint'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION MakeLine(geometry, geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_makeline'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_MakeLine(geometry, geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_makeline'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION AddPoint(geometry, geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_addpoint'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_AddPoint(geometry, geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_addpoint'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION AddPoint(geometry, geometry, integer)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_addpoint'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_AddPoint(geometry, geometry, integer)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_addpoint'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION RemovePoint(geometry, integer)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_removepoint'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_RemovePoint(geometry, integer)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_removepoint'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION SetPoint(geometry, integer, geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_setpoint_linestring'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_SetPoint(geometry, integer, geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_setpoint_linestring'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION ST_MakeEnvelope(float8, float8, float8, float8, integer)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'ST_MakeEnvelope'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION MakePolygon(geometry, geometry[])
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_makepoly'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_MakePolygon(geometry, geometry[])
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_makepoly'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION MakePolygon(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_makepoly'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_MakePolygon(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_makepoly'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION BuildArea(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_buildarea'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_BuildArea(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_buildarea'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION Polygonize_GArray (geometry[])
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'polygonize_garray'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION ST_Polygonize_GArray (geometry[])
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'polygonize_garray'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
  -- Availability: 1.4.0
  CREATE OR REPLACE FUNCTION ST_Polygonize (geometry[])
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'polygonize_garray'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION LineMerge(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'linemerge'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_LineMerge(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'linemerge'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
 
  CREATE TYPE geometry_dump AS (path integer[], geom geometry);
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION Dump(geometry)
  RETURNS SETOF geometry_dump
  AS '$libdir/postgis-1.5', 'LWGEOM_dump'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_Dump(geometry)
  RETURNS SETOF geometry_dump
  AS '$libdir/postgis-1.5', 'LWGEOM_dump'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION DumpRings(geometry)
  RETURNS SETOF geometry_dump
  AS '$libdir/postgis-1.5', 'LWGEOM_dump_rings'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_DumpRings(geometry)
  RETURNS SETOF geometry_dump
  AS '$libdir/postgis-1.5', 'LWGEOM_dump_rings'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -----------------------------------------------------------------------
  -- _ST_DumpPoints()
  -----------------------------------------------------------------------
  -- A helper function for ST_DumpPoints(geom)
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION _ST_DumpPoints(the_geom geometry, cur_path integer[]) RETURNS SETOF geometry_dump AS $$
  DECLARE
  tmp geometry_dump;
  tmp2 geometry_dump;
  nb_points integer;
  nb_geom integer;
  i integer;
  j integer;
  g geometry;
 
  BEGIN
 
  RAISE DEBUG '%,%', cur_path, ST_GeometryType(the_geom);
 
  -- Special case (MULTI* OR GEOMETRYCOLLECTION) : iterate and return the DumpPoints of the geometries
  SELECT ST_NumGeometries(the_geom) INTO nb_geom;
 
  IF (nb_geom IS NOT NULL) THEN
 
  i = 1;
  FOR tmp2 IN SELECT (ST_Dump(the_geom)).* LOOP
 
  FOR tmp IN SELECT * FROM _ST_DumpPoints(tmp2.geom, cur_path || tmp2.path) LOOP
  RETURN NEXT tmp;
  END LOOP;
  i = i + 1;
 
  END LOOP;
 
  RETURN;
  END IF;
 
 
  -- Special case (POLYGON) : return the points of the rings of a polygon
  IF (ST_GeometryType(the_geom) = 'ST_Polygon') THEN
 
  FOR tmp IN SELECT * FROM _ST_DumpPoints(ST_ExteriorRing(the_geom), cur_path || ARRAY[1]) LOOP
  RETURN NEXT tmp;
  END LOOP;
 
  j := ST_NumInteriorRings(the_geom);
  FOR i IN 1..j LOOP
  FOR tmp IN SELECT * FROM _ST_DumpPoints(ST_InteriorRingN(the_geom, i), cur_path || ARRAY[i+1]) LOOP
  RETURN NEXT tmp;
  END LOOP;
  END LOOP;
 
  RETURN;
  END IF;
 
 
  -- Special case (POINT) : return the point
  IF (ST_GeometryType(the_geom) = 'ST_Point') THEN
 
  tmp.path = cur_path || ARRAY[1];
  tmp.geom = the_geom;
 
  RETURN NEXT tmp;
  RETURN;
 
  END IF;
 
 
  -- Use ST_NumPoints rather than ST_NPoints to have a NULL value if the_geom isn't
  -- a LINESTRING or CIRCULARSTRING.
  SELECT ST_NumPoints(the_geom) INTO nb_points;
 
  -- This should never happen
  IF (nb_points IS NULL) THEN
  RAISE EXCEPTION 'Unexpected error while dumping geometry %', ST_AsText(the_geom);
  END IF;
 
  FOR i IN 1..nb_points LOOP
  tmp.path = cur_path || ARRAY[i];
  tmp.geom := ST_PointN(the_geom, i);
  RETURN NEXT tmp;
  END LOOP;
 
  END
  $$ LANGUAGE plpgsql;
 
  -----------------------------------------------------------------------
  -- ST_DumpPoints()
  -----------------------------------------------------------------------
  -- This function mimicks that of ST_Dump for collections, but this function
  -- that returns a path and all the points that make up a particular geometry.
  -- This current implementation in plpgsql does not scale very well at all.
  -- and should be ported to C at some point.
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION ST_DumpPoints(geometry) RETURNS SETOF geometry_dump AS $$
  SELECT * FROM _ST_DumpPoints($1, NULL);
  $$ LANGUAGE SQL;
 
 
  ------------------------------------------------------------------------
 
  --
  -- Aggregate functions
  --
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION combine_bbox(box2d,geometry)
  RETURNS box2d
  AS '$libdir/postgis-1.5', 'BOX2DFLOAT4_combine'
  LANGUAGE 'C' IMMUTABLE;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_Combine_BBox(box2d,geometry)
  RETURNS box2d
  AS '$libdir/postgis-1.5', 'BOX2DFLOAT4_combine'
  LANGUAGE 'C' IMMUTABLE;
 
  -- Temporary hack function
  CREATE OR REPLACE FUNCTION combine_bbox(box3d_extent,geometry)
  RETURNS box3d_extent
  AS '$libdir/postgis-1.5', 'BOX3D_combine'
  LANGUAGE 'C' IMMUTABLE;
 
  -- Temporary hack function
  CREATE OR REPLACE FUNCTION ST_Combine_BBox(box3d_extent,geometry)
  RETURNS box3d_extent
  AS '$libdir/postgis-1.5', 'BOX3D_combine'
  LANGUAGE 'C' IMMUTABLE;
 
  -- Deprecation in 1.2.3
  CREATE AGGREGATE Extent(
  sfunc = ST_combine_bbox,
  basetype = geometry,
  stype = box3d_extent
  );
 
  -- Availability: 1.2.2
  CREATE AGGREGATE ST_Extent(
  sfunc = ST_combine_bbox,
  basetype = geometry,
  stype = box3d_extent
  );
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION combine_bbox(box3d,geometry)
  RETURNS box3d
  AS '$libdir/postgis-1.5', 'BOX3D_combine'
  LANGUAGE 'C' IMMUTABLE;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_Combine_BBox(box3d,geometry)
  RETURNS box3d
  AS '$libdir/postgis-1.5', 'BOX3D_combine'
  LANGUAGE 'C' IMMUTABLE;
 
  -- Deprecation in 1.2.3
  CREATE AGGREGATE Extent3d(
  sfunc = combine_bbox,
  basetype = geometry,
  stype = box3d
  );
 
  -- Availability: 1.2.2
  CREATE AGGREGATE ST_Extent3d(
  sfunc = ST_combine_bbox,
  basetype = geometry,
  stype = box3d
  );
 
  -----------------------------------------------------------------------
  -- ESTIMATED_EXTENT( <schema name>, <table name>, <column name> )
  -----------------------------------------------------------------------
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION estimated_extent(text,text,text) RETURNS box2d AS
  '$libdir/postgis-1.5', 'LWGEOM_estimated_extent'
  LANGUAGE 'C' IMMUTABLE STRICT SECURITY DEFINER;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_estimated_extent(text,text,text) RETURNS box2d AS
  '$libdir/postgis-1.5', 'LWGEOM_estimated_extent'
  LANGUAGE 'C' IMMUTABLE STRICT SECURITY DEFINER;
 
  -----------------------------------------------------------------------
  -- ESTIMATED_EXTENT( <table name>, <column name> )
  -----------------------------------------------------------------------
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION estimated_extent(text,text) RETURNS box2d AS
  '$libdir/postgis-1.5', 'LWGEOM_estimated_extent'
  LANGUAGE 'C' IMMUTABLE STRICT SECURITY DEFINER;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_estimated_extent(text,text) RETURNS box2d AS
  '$libdir/postgis-1.5', 'LWGEOM_estimated_extent'
  LANGUAGE 'C' IMMUTABLE STRICT SECURITY DEFINER;
 
  -----------------------------------------------------------------------
  -- FIND_EXTENT( <schema name>, <table name>, <column name> )
  -----------------------------------------------------------------------
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION find_extent(text,text,text) RETURNS box2d AS
  $$
  DECLARE
  schemaname alias for $1;
  tablename alias for $2;
  columnname alias for $3;
  myrec RECORD;
 
  BEGIN
  FOR myrec IN EXECUTE 'SELECT extent("' || columnname || '") FROM "' || schemaname || '"."' || tablename || '"' LOOP
  return myrec.extent;
  END LOOP;
  END;
  $$
  LANGUAGE 'plpgsql' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_find_extent(text,text,text) RETURNS box2d AS
  $$
  DECLARE
  schemaname alias for $1;
  tablename alias for $2;
  columnname alias for $3;
  myrec RECORD;
 
  BEGIN
  FOR myrec IN EXECUTE 'SELECT extent("' || columnname || '") FROM "' || schemaname || '"."' || tablename || '"' LOOP
  return myrec.extent;
  END LOOP;
  END;
  $$
  LANGUAGE 'plpgsql' IMMUTABLE STRICT;
 
 
  -----------------------------------------------------------------------
  -- FIND_EXTENT( <table name>, <column name> )
  -----------------------------------------------------------------------
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION find_extent(text,text) RETURNS box2d AS
  $$
  DECLARE
  tablename alias for $1;
  columnname alias for $2;
  myrec RECORD;
 
  BEGIN
  FOR myrec IN EXECUTE 'SELECT extent("' || columnname || '") FROM "' || tablename || '"' LOOP
  return myrec.extent;
  END LOOP;
  END;
  $$
  LANGUAGE 'plpgsql' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_find_extent(text,text) RETURNS box2d AS
  $$
  DECLARE
  tablename alias for $1;
  columnname alias for $2;
  myrec RECORD;
 
  BEGIN
  FOR myrec IN EXECUTE 'SELECT extent("' || columnname || '") FROM "' || tablename || '"' LOOP
  return myrec.extent;
  END LOOP;
  END;
  $$
  LANGUAGE 'plpgsql' IMMUTABLE STRICT;
 
  -------------------------------------------------------------------
  -- SPATIAL_REF_SYS
  -------------------------------------------------------------------
  CREATE TABLE spatial_ref_sys (
  srid integer not null primary key,
  auth_name varchar(256),
  auth_srid integer,
  srtext varchar(2048),
  proj4text varchar(2048)
  );
 
  -------------------------------------------------------------------
  -- GEOMETRY_COLUMNS
  -------------------------------------------------------------------
  CREATE TABLE geometry_columns (
  f_table_catalog varchar(256) not null,
  f_table_schema varchar(256) not null,
  f_table_name varchar(256) not null,
  f_geometry_column varchar(256) not null,
  coord_dimension integer not null,
  srid integer not null,
  type varchar(30) not null,
  CONSTRAINT geometry_columns_pk primary key (
  f_table_catalog,
  f_table_schema,
  f_table_name,
  f_geometry_column )
  ) WITH OIDS;
 
  -----------------------------------------------------------------------
  -- RENAME_GEOMETRY_TABLE_CONSTRAINTS()
  -----------------------------------------------------------------------
  -- This function has been obsoleted for the difficulty in
  -- finding attribute on which the constraint is applied.
  -- AddGeometryColumn will name the constraints in a meaningful
  -- way, but nobody can rely on it since old postgis versions did
  -- not do that.
  -----------------------------------------------------------------------
  CREATE OR REPLACE FUNCTION rename_geometry_table_constraints() RETURNS text
  AS
  $$
  SELECT 'rename_geometry_table_constraint() is obsoleted'::text
  $$
  LANGUAGE 'SQL' IMMUTABLE;
 
  -----------------------------------------------------------------------
  -- FIX_GEOMETRY_COLUMNS()
  -----------------------------------------------------------------------
  -- This function will:
  --
  -- o try to fix the schema of records with an integer one
  -- (for PG>=73)
  --
  -- o link records to system tables through attrelid and varattnum
  -- (for PG<75)
  --
  -- o delete all records for which no linking was possible
  -- (for PG<75)
  --
  --
  -----------------------------------------------------------------------
  CREATE OR REPLACE FUNCTION fix_geometry_columns() RETURNS text
  AS
  $$
  DECLARE
  mislinked record;
  result text;
  linked integer;
  deleted integer;
  foundschema integer;
  BEGIN
 
  -- Since 7.3 schema support has been added.
  -- Previous postgis versions used to put the database name in
  -- the schema column. This needs to be fixed, so we try to
  -- set the correct schema for each geometry_colums record
  -- looking at table, column, type and srid.
  UPDATE geometry_columns SET f_table_schema = n.nspname
  FROM pg_namespace n, pg_class c, pg_attribute a,
  pg_constraint sridcheck, pg_constraint typecheck
  WHERE ( f_table_schema is NULL
  OR f_table_schema = ''
  OR f_table_schema NOT IN (
  SELECT nspname::varchar
  FROM pg_namespace nn, pg_class cc, pg_attribute aa
  WHERE cc.relnamespace = nn.oid
  AND cc.relname = f_table_name::name
  AND aa.attrelid = cc.oid
  AND aa.attname = f_geometry_column::name))
  AND f_table_name::name = c.relname
  AND c.oid = a.attrelid
  AND c.relnamespace = n.oid
  AND f_geometry_column::name = a.attname
 
  AND sridcheck.conrelid = c.oid
  AND sridcheck.consrc LIKE '(srid(% = %)'
  AND sridcheck.consrc ~ textcat(' = ', srid::text)
 
  AND typecheck.conrelid = c.oid
  AND typecheck.consrc LIKE
  '((geometrytype(%) = ''%''::text) OR (% IS NULL))'
  AND typecheck.consrc ~ textcat(' = ''', type::text)
 
  AND NOT EXISTS (
  SELECT oid FROM geometry_columns gc
  WHERE c.relname::varchar = gc.f_table_name
  AND n.nspname::varchar = gc.f_table_schema
  AND a.attname::varchar = gc.f_geometry_column
  );
 
  GET DIAGNOSTICS foundschema = ROW_COUNT;
 
  -- no linkage to system table needed
  return 'fixed:'||foundschema::text;
 
  END;
  $$
  LANGUAGE 'plpgsql' VOLATILE;
 
  -----------------------------------------------------------------------
  -- POPULATE_GEOMETRY_COLUMNS()
  -----------------------------------------------------------------------
  -- Truncates and refills the geometry_columns table from all tables and
  -- views in the database that contain geometry columns. This function
  -- is a simple wrapper for populate_geometry_columns(oid). In essence,
  -- this function ensures every geometry column in the database has the
  -- appropriate spatial contraints (for tables) and exists in the
  -- geometry_columns table.
  -- Availability: 1.4.0
  -----------------------------------------------------------------------
  CREATE OR REPLACE FUNCTION populate_geometry_columns()
  RETURNS text AS
  $$
  DECLARE
  inserted integer;
  oldcount integer;
  probed integer;
  stale integer;
  gcs RECORD;
  gc RECORD;
  gsrid integer;
  gndims integer;
  gtype text;
  query text;
  gc_is_valid boolean;
 
  BEGIN
  SELECT count(*) INTO oldcount FROM geometry_columns;
  inserted := 0;
 
  EXECUTE 'TRUNCATE geometry_columns';
 
  -- Count the number of geometry columns in all tables and views
  SELECT count(DISTINCT c.oid) INTO probed
  FROM pg_class c,
  pg_attribute a,
  pg_type t,
  pg_namespace n
  WHERE (c.relkind = 'r' OR c.relkind = 'v')
  AND t.typname = 'geometry'
  AND a.attisdropped = false
  AND a.atttypid = t.oid
  AND a.attrelid = c.oid
  AND c.relnamespace = n.oid
  AND n.nspname NOT ILIKE 'pg_temp%';
 
  -- Iterate through all non-dropped geometry columns
  RAISE DEBUG 'Processing Tables.....';
 
  FOR gcs IN
  SELECT DISTINCT ON (c.oid) c.oid, n.nspname, c.relname
  FROM pg_class c,
  pg_attribute a,
  pg_type t,
  pg_namespace n
  WHERE c.relkind = 'r'
  AND t.typname = 'geometry'
  AND a.attisdropped = false
  AND a.atttypid = t.oid
  AND a.attrelid = c.oid
  AND c.relnamespace = n.oid
  AND n.nspname NOT ILIKE 'pg_temp%'
  LOOP
 
  inserted := inserted + populate_geometry_columns(gcs.oid);
  END LOOP;
 
  -- Add views to geometry columns table
  RAISE DEBUG 'Processing Views.....';
  FOR gcs IN
  SELECT DISTINCT ON (c.oid) c.oid, n.nspname, c.relname
  FROM pg_class c,
  pg_attribute a,
  pg_type t,
  pg_namespace n
  WHERE c.relkind = 'v'
  AND t.typname = 'geometry'
  AND a.attisdropped = false
  AND a.atttypid = t.oid
  AND a.attrelid = c.oid
  AND c.relnamespace = n.oid
  LOOP
 
  inserted := inserted + populate_geometry_columns(gcs.oid);
  END LOOP;
 
  IF oldcount > inserted THEN
  stale = oldcount-inserted;
  ELSE
  stale = 0;
  END IF;
 
  RETURN 'probed:' ||probed|| ' inserted:'||inserted|| ' conflicts:'||probed-inserted|| ' deleted:'||stale;
  END
 
  $$
  LANGUAGE 'plpgsql' VOLATILE;
 
  -----------------------------------------------------------------------
  -- POPULATE_GEOMETRY_COLUMNS(tbl_oid oid)
  -----------------------------------------------------------------------
  -- DELETEs from and reINSERTs into the geometry_columns table all entries
  -- associated with the oid of a particular table or view.
  --
  -- If the provided oid is for a table, this function tries to determine
  -- the srid, dimension, and geometry type of the all geometries
  -- in the table, adding contraints as necessary to the table. If
  -- successful, an appropriate row is inserted into the geometry_columns
  -- table, otherwise, the exception is caught and an error notice is
  -- raised describing the problem. (This is so the wrapper function
  -- populate_geometry_columns() can apply spatial constraints to all
  -- geometry columns across an entire database at once without erroring
  -- out)
  --
  -- If the provided oid is for a view, as with a table oid, this function
  -- tries to determine the srid, dimension, and type of all the geometries
  -- in the view, inserting appropriate entries into the geometry_columns
  -- table.
  -- Availability: 1.4.0
  -----------------------------------------------------------------------
  CREATE OR REPLACE FUNCTION populate_geometry_columns(tbl_oid oid)
  RETURNS integer AS
  $$
  DECLARE
  gcs RECORD;
  gc RECORD;
  gsrid integer;
  gndims integer;
  gtype text;
  query text;
  gc_is_valid boolean;
  inserted integer;
 
  BEGIN
  inserted := 0;
 
  -- Iterate through all geometry columns in this table
  FOR gcs IN
  SELECT n.nspname, c.relname, a.attname
  FROM pg_class c,
  pg_attribute a,
  pg_type t,
  pg_namespace n
  WHERE c.relkind = 'r'
  AND t.typname = 'geometry'
  AND a.attisdropped = false
  AND a.atttypid = t.oid
  AND a.attrelid = c.oid
  AND c.relnamespace = n.oid
  AND n.nspname NOT ILIKE 'pg_temp%'
  AND c.oid = tbl_oid
  LOOP
 
  RAISE DEBUG 'Processing table %.%.%', gcs.nspname, gcs.relname, gcs.attname;
 
  DELETE FROM geometry_columns
  WHERE f_table_schema = quote_ident(gcs.nspname)
  AND f_table_name = quote_ident(gcs.relname)
  AND f_geometry_column = quote_ident(gcs.attname);
 
  gc_is_valid := true;
 
  -- Try to find srid check from system tables (pg_constraint)
  gsrid :=
  (SELECT replace(replace(split_part(s.consrc, ' = ', 2), ')', ''), '(', '')
  FROM pg_class c, pg_namespace n, pg_attribute a, pg_constraint s
  WHERE n.nspname = gcs.nspname
  AND c.relname = gcs.relname
  AND a.attname = gcs.attname
  AND a.attrelid = c.oid
  AND s.connamespace = n.oid
  AND s.conrelid = c.oid
  AND a.attnum = ANY (s.conkey)
  AND s.consrc LIKE '%srid(% = %');
  IF (gsrid IS NULL) THEN
  -- Try to find srid from the geometry itself
  EXECUTE 'SELECT srid(' || quote_ident(gcs.attname) || ')
  FROM ONLY ' || quote_ident(gcs.nspname) || '.' || quote_ident(gcs.relname) || '
  WHERE ' || quote_ident(gcs.attname) || ' IS NOT NULL LIMIT 1'
  INTO gc;
  gsrid := gc.srid;
 
  -- Try to apply srid check to column
  IF (gsrid IS NOT NULL) THEN
  BEGIN
  EXECUTE 'ALTER TABLE ONLY ' || quote_ident(gcs.nspname) || '.' || quote_ident(gcs.relname) || '
  ADD CONSTRAINT ' || quote_ident('enforce_srid_' || gcs.attname) || '
  CHECK (srid(' || quote_ident(gcs.attname) || ') = ' || gsrid || ')';
  EXCEPTION
  WHEN check_violation THEN
  RAISE WARNING 'Not inserting ''%'' in ''%.%'' into geometry_columns: could not apply constraint CHECK (srid(%) = %)', quote_ident(gcs.attname), quote_ident(gcs.nspname), quote_ident(gcs.relname), quote_ident(gcs.attname), gsrid;
  gc_is_valid := false;
  END;
  END IF;
  END IF;
 
  -- Try to find ndims check from system tables (pg_constraint)
  gndims :=
  (SELECT replace(split_part(s.consrc, ' = ', 2), ')', '')
  FROM pg_class c, pg_namespace n, pg_attribute a, pg_constraint s
  WHERE n.nspname = gcs.nspname
  AND c.relname = gcs.relname
  AND a.attname = gcs.attname
  AND a.attrelid = c.oid
  AND s.connamespace = n.oid
  AND s.conrelid = c.oid
  AND a.attnum = ANY (s.conkey)
  AND s.consrc LIKE '%ndims(% = %');
  IF (gndims IS NULL) THEN
  -- Try to find ndims from the geometry itself
  EXECUTE 'SELECT ndims(' || quote_ident(gcs.attname) || ')
  FROM ONLY ' || quote_ident(gcs.nspname) || '.' || quote_ident(gcs.relname) || '
  WHERE ' || quote_ident(gcs.attname) || ' IS NOT NULL LIMIT 1'
  INTO gc;
  gndims := gc.ndims;
 
  -- Try to apply ndims check to column
  IF (gndims IS NOT NULL) THEN
  BEGIN
  EXECUTE 'ALTER TABLE ONLY ' || quote_ident(gcs.nspname) || '.' || quote_ident(gcs.relname) || '
  ADD CONSTRAINT ' || quote_ident('enforce_dims_' || gcs.attname) || '
  CHECK (ndims(' || quote_ident(gcs.attname) || ') = '||gndims||')';
  EXCEPTION
  WHEN check_violation THEN
  RAISE WARNING 'Not inserting ''%'' in ''%.%'' into geometry_columns: could not apply constraint CHECK (ndims(%) = %)', quote_ident(gcs.attname), quote_ident(gcs.nspname), quote_ident(gcs.relname), quote_ident(gcs.attname), gndims;
  gc_is_valid := false;
  END;
  END IF;
  END IF;
 
  -- Try to find geotype check from system tables (pg_constraint)
  gtype :=
  (SELECT replace(split_part(s.consrc, '''', 2), ')', '')
  FROM pg_class c, pg_namespace n, pg_attribute a, pg_constraint s
  WHERE n.nspname = gcs.nspname
  AND c.relname = gcs.relname
  AND a.attname = gcs.attname
  AND a.attrelid = c.oid
  AND s.connamespace = n.oid
  AND s.conrelid = c.oid
  AND a.attnum = ANY (s.conkey)
  AND s.consrc LIKE '%geometrytype(% = %');
  IF (gtype IS NULL) THEN
  -- Try to find geotype from the geometry itself
  EXECUTE 'SELECT geometrytype(' || quote_ident(gcs.attname) || ')
  FROM ONLY ' || quote_ident(gcs.nspname) || '.' || quote_ident(gcs.relname) || '
  WHERE ' || quote_ident(gcs.attname) || ' IS NOT NULL LIMIT 1'
  INTO gc;
  gtype := gc.geometrytype;
  --IF (gtype IS NULL) THEN
  -- gtype := 'GEOMETRY';
  --END IF;
 
  -- Try to apply geometrytype check to column
  IF (gtype IS NOT NULL) THEN
  BEGIN
  EXECUTE 'ALTER TABLE ONLY ' || quote_ident(gcs.nspname) || '.' || quote_ident(gcs.relname) || '
  ADD CONSTRAINT ' || quote_ident('enforce_geotype_' || gcs.attname) || '
  CHECK ((geometrytype(' || quote_ident(gcs.attname) || ') = ' || quote_literal(gtype) || ') OR (' || quote_ident(gcs.attname) || ' IS NULL))';
  EXCEPTION
  WHEN check_violation THEN
  -- No geometry check can be applied. This column contains a number of geometry types.
  RAISE WARNING 'Could not add geometry type check (%) to table column: %.%.%', gtype, quote_ident(gcs.nspname),quote_ident(gcs.relname),quote_ident(gcs.attname);
  END;
  END IF;
  END IF;
 
  IF (gsrid IS NULL) THEN
  RAISE WARNING 'Not inserting ''%'' in ''%.%'' into geometry_columns: could not determine the srid', quote_ident(gcs.attname), quote_ident(gcs.nspname), quote_ident(gcs.relname);
  ELSIF (gndims IS NULL) THEN
  RAISE WARNING 'Not inserting ''%'' in ''%.%'' into geometry_columns: could not determine the number of dimensions', quote_ident(gcs.attname), quote_ident(gcs.nspname), quote_ident(gcs.relname);
  ELSIF (gtype IS NULL) THEN
  RAISE WARNING 'Not inserting ''%'' in ''%.%'' into geometry_columns: could not determine the geometry type', quote_ident(gcs.attname), quote_ident(gcs.nspname), quote_ident(gcs.relname);
  ELSE
  -- Only insert into geometry_columns if table constraints could be applied.
  IF (gc_is_valid) THEN
  INSERT INTO geometry_columns (f_table_catalog,f_table_schema, f_table_name, f_geometry_column, coord_dimension, srid, type)
  VALUES ('', gcs.nspname, gcs.relname, gcs.attname, gndims, gsrid, gtype);
  inserted := inserted + 1;
  END IF;
  END IF;
  END LOOP;
 
  -- Add views to geometry columns table
  FOR gcs IN
  SELECT n.nspname, c.relname, a.attname
  FROM pg_class c,
  pg_attribute a,
  pg_type t,
  pg_namespace n
  WHERE c.relkind = 'v'
  AND t.typname = 'geometry'
  AND a.attisdropped = false
  AND a.atttypid = t.oid
  AND a.attrelid = c.oid
  AND c.relnamespace = n.oid
  AND n.nspname NOT ILIKE 'pg_temp%'
  AND c.oid = tbl_oid
  LOOP
  RAISE DEBUG 'Processing view %.%.%', gcs.nspname, gcs.relname, gcs.attname;
 
  EXECUTE 'SELECT ndims(' || quote_ident(gcs.attname) || ')
  FROM ' || quote_ident(gcs.nspname) || '.' || quote_ident(gcs.relname) || '
  WHERE ' || quote_ident(gcs.attname) || ' IS NOT NULL LIMIT 1'
  INTO gc;
  gndims := gc.ndims;
 
  EXECUTE 'SELECT srid(' || quote_ident(gcs.attname) || ')
  FROM ' || quote_ident(gcs.nspname) || '.' || quote_ident(gcs.relname) || '
  WHERE ' || quote_ident(gcs.attname) || ' IS NOT NULL LIMIT 1'
  INTO gc;
  gsrid := gc.srid;
 
  EXECUTE 'SELECT geometrytype(' || quote_ident(gcs.attname) || ')
  FROM ' || quote_ident(gcs.nspname) || '.' || quote_ident(gcs.relname) || '
  WHERE ' || quote_ident(gcs.attname) || ' IS NOT NULL LIMIT 1'
  INTO gc;
  gtype := gc.geometrytype;
 
  IF (gndims IS NULL) THEN
  RAISE WARNING 'Not inserting ''%'' in ''%.%'' into geometry_columns: could not determine ndims', quote_ident(gcs.attname), quote_ident(gcs.nspname), quote_ident(gcs.relname);
  ELSIF (gsrid IS NULL) THEN
  RAISE WARNING 'Not inserting ''%'' in ''%.%'' into geometry_columns: could not determine srid', quote_ident(gcs.attname), quote_ident(gcs.nspname), quote_ident(gcs.relname);
  ELSIF (gtype IS NULL) THEN
  RAISE WARNING 'Not inserting ''%'' in ''%.%'' into geometry_columns: could not determine gtype', quote_ident(gcs.attname), quote_ident(gcs.nspname), quote_ident(gcs.relname);
  ELSE
  query := 'INSERT INTO geometry_columns (f_table_catalog,f_table_schema, f_table_name, f_geometry_column, coord_dimension, srid, type) ' ||
  'VALUES ('''', ' || quote_literal(gcs.nspname) || ',' || quote_literal(gcs.relname) || ',' || quote_literal(gcs.attname) || ',' || gndims || ',' || gsrid || ',' || quote_literal(gtype) || ')';
  EXECUTE query;
  inserted := inserted + 1;
  END IF;
  END LOOP;
 
  RETURN inserted;
  END
 
  $$
  LANGUAGE 'plpgsql' VOLATILE;
 
 
  -----------------------------------------------------------------------
  -- PROBE_GEOMETRY_COLUMNS()
  -----------------------------------------------------------------------
  -- Fill the geometry_columns table with values probed from the system
  -- catalogues. This is done by simply looking up constraints previously
  -- added to a geometry column. If geometry constraints are missing, no
  -- attempt is made to add the necessary constraints to the geometry
  -- column, nor is it recorded in the geometry_columns table.
  -- 3d flag cannot be probed, it defaults to 2
  --
  -- Note that bogus records already in geometry_columns are not
  -- overridden (a check for schema.table.column is performed), so
  -- to have a fresh probe backup your geometry_columns, delete from
  -- it and probe.
  -----------------------------------------------------------------------
  CREATE OR REPLACE FUNCTION probe_geometry_columns() RETURNS text AS
  $$
  DECLARE
  inserted integer;
  oldcount integer;
  probed integer;
  stale integer;
  BEGIN
 
  SELECT count(*) INTO oldcount FROM geometry_columns;
 
  SELECT count(*) INTO probed
  FROM pg_class c, pg_attribute a, pg_type t,
  pg_namespace n,
  pg_constraint sridcheck, pg_constraint typecheck
 
  WHERE t.typname = 'geometry'
  AND a.atttypid = t.oid
  AND a.attrelid = c.oid
  AND c.relnamespace = n.oid
  AND sridcheck.connamespace = n.oid
  AND typecheck.connamespace = n.oid
  AND sridcheck.conrelid = c.oid
  AND sridcheck.consrc LIKE '(srid('||a.attname||') = %)'
  AND typecheck.conrelid = c.oid
  AND typecheck.consrc LIKE
  '((geometrytype('||a.attname||') = ''%''::text) OR (% IS NULL))'
  ;
 
  INSERT INTO geometry_columns SELECT
  ''::varchar as f_table_catalogue,
  n.nspname::varchar as f_table_schema,
  c.relname::varchar as f_table_name,
  a.attname::varchar as f_geometry_column,
  2 as coord_dimension,
  trim(both ' =)' from
  replace(replace(split_part(
  sridcheck.consrc, ' = ', 2), ')', ''), '(', ''))::integer AS srid,
  trim(both ' =)''' from substr(typecheck.consrc,
  strpos(typecheck.consrc, '='),
  strpos(typecheck.consrc, '::')-
  strpos(typecheck.consrc, '=')
  ))::varchar as type
  FROM pg_class c, pg_attribute a, pg_type t,
  pg_namespace n,
  pg_constraint sridcheck, pg_constraint typecheck
  WHERE t.typname = 'geometry'
  AND a.atttypid = t.oid
  AND a.attrelid = c.oid
  AND c.relnamespace = n.oid
  AND sridcheck.connamespace = n.oid
  AND typecheck.connamespace = n.oid
  AND sridcheck.conrelid = c.oid
  AND sridcheck.consrc LIKE '(st_srid('||a.attname||') = %)'
  AND typecheck.conrelid = c.oid
  AND typecheck.consrc LIKE
  '((geometrytype('||a.attname||') = ''%''::text) OR (% IS NULL))'
 
  AND NOT EXISTS (
  SELECT oid FROM geometry_columns gc
  WHERE c.relname::varchar = gc.f_table_name
  AND n.nspname::varchar = gc.f_table_schema
  AND a.attname::varchar = gc.f_geometry_column
  );
 
  GET DIAGNOSTICS inserted = ROW_COUNT;
 
  IF oldcount > probed THEN
  stale = oldcount-probed;
  ELSE
  stale = 0;
  END IF;
 
  RETURN 'probed:'||probed::text||
  ' inserted:'||inserted::text||
  ' conflicts:'||(probed-inserted)::text||
  ' stale:'||stale::text;
  END
 
  $$
  LANGUAGE 'plpgsql' VOLATILE;
 
  -----------------------------------------------------------------------
  -- ADDGEOMETRYCOLUMN
  -- <catalogue>, <schema>, <table>, <column>, <srid>, <type>, <dim>
  -----------------------------------------------------------------------
  --
  -- Type can be one of GEOMETRY, GEOMETRYCOLLECTION, POINT, MULTIPOINT, POLYGON,
  -- MULTIPOLYGON, LINESTRING, or MULTILINESTRING.
  --
  -- Geometry types (except GEOMETRY) are checked for consistency using a CHECK constraint.
  -- Uses an ALTER TABLE command to add the geometry column to the table.
  -- Addes a row to geometry_columns.
  -- Addes a constraint on the table that all the geometries MUST have the same
  -- SRID. Checks the coord_dimension to make sure its between 0 and 3.
  -- Should also check the precision grid (future expansion).
  --
  -----------------------------------------------------------------------
  CREATE OR REPLACE FUNCTION AddGeometryColumn(varchar,varchar,varchar,varchar,integer,varchar,integer)
  RETURNS text
  AS
  $$
  DECLARE
  catalog_name alias for $1;
  schema_name alias for $2;
  table_name alias for $3;
  column_name alias for $4;
  new_srid alias for $5;
  new_type alias for $6;
  new_dim alias for $7;
  rec RECORD;
  sr varchar;
  real_schema name;
  sql text;
 
  BEGIN
 
  -- Verify geometry type
  IF ( NOT ( (new_type = 'GEOMETRY') OR
  (new_type = 'GEOMETRYCOLLECTION') OR
  (new_type = 'POINT') OR
  (new_type = 'MULTIPOINT') OR
  (new_type = 'POLYGON') OR
  (new_type = 'MULTIPOLYGON') OR
  (new_type = 'LINESTRING') OR
  (new_type = 'MULTILINESTRING') OR
  (new_type = 'GEOMETRYCOLLECTIONM') OR
  (new_type = 'POINTM') OR
  (new_type = 'MULTIPOINTM') OR
  (new_type = 'POLYGONM') OR
  (new_type = 'MULTIPOLYGONM') OR
  (new_type = 'LINESTRINGM') OR
  (new_type = 'MULTILINESTRINGM') OR
  (new_type = 'CIRCULARSTRING') OR
  (new_type = 'CIRCULARSTRINGM') OR
  (new_type = 'COMPOUNDCURVE') OR
  (new_type = 'COMPOUNDCURVEM') OR
  (new_type = 'CURVEPOLYGON') OR
  (new_type = 'CURVEPOLYGONM') OR
  (new_type = 'MULTICURVE') OR
  (new_type = 'MULTICURVEM') OR
  (new_type = 'MULTISURFACE') OR
  (new_type = 'MULTISURFACEM')) )
  THEN
  RAISE EXCEPTION 'Invalid type name - valid ones are:
  POINT, MULTIPOINT,
  LINESTRING, MULTILINESTRING,
  POLYGON, MULTIPOLYGON,
  CIRCULARSTRING, COMPOUNDCURVE, MULTICURVE,
  CURVEPOLYGON, MULTISURFACE,
  GEOMETRY, GEOMETRYCOLLECTION,
  POINTM, MULTIPOINTM,
  LINESTRINGM, MULTILINESTRINGM,
  POLYGONM, MULTIPOLYGONM,
  CIRCULARSTRINGM, COMPOUNDCURVEM, MULTICURVEM
  CURVEPOLYGONM, MULTISURFACEM,
  or GEOMETRYCOLLECTIONM';
  RETURN 'fail';
  END IF;
 
 
  -- Verify dimension
  IF ( (new_dim >4) OR (new_dim <0) ) THEN
  RAISE EXCEPTION 'invalid dimension';
  RETURN 'fail';
  END IF;
 
  IF ( (new_type LIKE '%M') AND (new_dim!=3) ) THEN
  RAISE EXCEPTION 'TypeM needs 3 dimensions';
  RETURN 'fail';
  END IF;
 
 
  -- Verify SRID
  IF ( new_srid != -1 ) THEN
  SELECT SRID INTO sr FROM spatial_ref_sys WHERE SRID = new_srid;
  IF NOT FOUND THEN
  RAISE EXCEPTION 'AddGeometryColumns() - invalid SRID';
  RETURN 'fail';
  END IF;
  END IF;
 
 
  -- Verify schema
  IF ( schema_name IS NOT NULL AND schema_name != '' ) THEN
  sql := 'SELECT nspname FROM pg_namespace ' ||
  'WHERE text(nspname) = ' || quote_literal(schema_name) ||
  'LIMIT 1';
  RAISE DEBUG '%', sql;
  EXECUTE sql INTO real_schema;
 
  IF ( real_schema IS NULL ) THEN
  RAISE EXCEPTION 'Schema % is not a valid schemaname', quote_literal(schema_name);
  RETURN 'fail';
  END IF;
  END IF;
 
  IF ( real_schema IS NULL ) THEN
  RAISE DEBUG 'Detecting schema';
  sql := 'SELECT n.nspname AS schemaname ' ||
  'FROM pg_catalog.pg_class c ' ||
  'JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace ' ||
  'WHERE c.relkind = ' || quote_literal('r') ||
  ' AND n.nspname NOT IN (' || quote_literal('pg_catalog') || ', ' || quote_literal('pg_toast') || ')' ||
  ' AND pg_catalog.pg_table_is_visible(c.oid)' ||
  ' AND c.relname = ' || quote_literal(table_name);
  RAISE DEBUG '%', sql;
  EXECUTE sql INTO real_schema;
 
  IF ( real_schema IS NULL ) THEN
  RAISE EXCEPTION 'Table % does not occur in the search_path', quote_literal(table_name);
  RETURN 'fail';
  END IF;
  END IF;
 
 
  -- Add geometry column to table
  sql := 'ALTER TABLE ' ||
  quote_ident(real_schema) || '.' || quote_ident(table_name)
  || ' ADD COLUMN ' || quote_ident(column_name) ||
  ' geometry ';
  RAISE DEBUG '%', sql;
  EXECUTE sql;
 
 
  -- Delete stale record in geometry_columns (if any)
  sql := 'DELETE FROM geometry_columns WHERE
  f_table_catalog = ' || quote_literal('') ||
  ' AND f_table_schema = ' ||
  quote_literal(real_schema) ||
  ' AND f_table_name = ' || quote_literal(table_name) ||
  ' AND f_geometry_column = ' || quote_literal(column_name);
  RAISE DEBUG '%', sql;
  EXECUTE sql;
 
 
  -- Add record in geometry_columns
  sql := 'INSERT INTO geometry_columns (f_table_catalog,f_table_schema,f_table_name,' ||
  'f_geometry_column,coord_dimension,srid,type)' ||
  ' VALUES (' ||
  quote_literal('') || ',' ||
  quote_literal(real_schema) || ',' ||
  quote_literal(table_name) || ',' ||
  quote_literal(column_name) || ',' ||
  new_dim::text || ',' ||
  new_srid::text || ',' ||
  quote_literal(new_type) || ')';
  RAISE DEBUG '%', sql;
  EXECUTE sql;
 
 
  -- Add table CHECKs
  sql := 'ALTER TABLE ' ||
  quote_ident(real_schema) || '.' || quote_ident(table_name)
  || ' ADD CONSTRAINT '
  || quote_ident('enforce_srid_' || column_name)
  || ' CHECK (ST_SRID(' || quote_ident(column_name) ||
  ') = ' || new_srid::text || ')' ;
  RAISE DEBUG '%', sql;
  EXECUTE sql;
 
  sql := 'ALTER TABLE ' ||
  quote_ident(real_schema) || '.' || quote_ident(table_name)
  || ' ADD CONSTRAINT '
  || quote_ident('enforce_dims_' || column_name)
  || ' CHECK (ST_NDims(' || quote_ident(column_name) ||
  ') = ' || new_dim::text || ')' ;
  RAISE DEBUG '%', sql;
  EXECUTE sql;
 
  IF ( NOT (new_type = 'GEOMETRY')) THEN
  sql := 'ALTER TABLE ' ||
  quote_ident(real_schema) || '.' || quote_ident(table_name) || ' ADD CONSTRAINT ' ||
  quote_ident('enforce_geotype_' || column_name) ||
  ' CHECK (GeometryType(' ||
  quote_ident(column_name) || ')=' ||
  quote_literal(new_type) || ' OR (' ||
  quote_ident(column_name) || ') is null)';
  RAISE DEBUG '%', sql;
  EXECUTE sql;
  END IF;
 
  RETURN
  real_schema || '.' ||
  table_name || '.' || column_name ||
  ' SRID:' || new_srid::text ||
  ' TYPE:' || new_type ||
  ' DIMS:' || new_dim::text || ' ';
  END;
  $$
  LANGUAGE 'plpgsql' VOLATILE STRICT;
 
  ----------------------------------------------------------------------------
  -- ADDGEOMETRYCOLUMN ( <schema>, <table>, <column>, <srid>, <type>, <dim> )
  ----------------------------------------------------------------------------
  --
  -- This is a wrapper to the real AddGeometryColumn, for use
  -- when catalogue is undefined
  --
  ----------------------------------------------------------------------------
  CREATE OR REPLACE FUNCTION AddGeometryColumn(varchar,varchar,varchar,integer,varchar,integer) RETURNS text AS $$
  DECLARE
  ret text;
  BEGIN
  SELECT AddGeometryColumn('',$1,$2,$3,$4,$5,$6) into ret;
  RETURN ret;
  END;
  $$
  LANGUAGE 'plpgsql' STABLE STRICT;
 
  ----------------------------------------------------------------------------
  -- ADDGEOMETRYCOLUMN ( <table>, <column>, <srid>, <type>, <dim> )
  ----------------------------------------------------------------------------
  --
  -- This is a wrapper to the real AddGeometryColumn, for use
  -- when catalogue and schema are undefined
  --
  ----------------------------------------------------------------------------
  CREATE OR REPLACE FUNCTION AddGeometryColumn(varchar,varchar,integer,varchar,integer) RETURNS text AS $$
  DECLARE
  ret text;
  BEGIN
  SELECT AddGeometryColumn('','',$1,$2,$3,$4,$5) into ret;
  RETURN ret;
  END;
  $$
  LANGUAGE 'plpgsql' VOLATILE STRICT;
 
  -----------------------------------------------------------------------
  -- DROPGEOMETRYCOLUMN
  -- <catalogue>, <schema>, <table>, <column>
  -----------------------------------------------------------------------
  --
  -- Removes geometry column reference from geometry_columns table.
  -- Drops the column with pgsql >= 73.
  -- Make some silly enforcements on it for pgsql < 73
  --
  -----------------------------------------------------------------------
  CREATE OR REPLACE FUNCTION DropGeometryColumn(varchar, varchar,varchar,varchar)
  RETURNS text
  AS
  $$
  DECLARE
  catalog_name alias for $1;
  schema_name alias for $2;
  table_name alias for $3;
  column_name alias for $4;
  myrec RECORD;
  okay boolean;
  real_schema name;
 
  BEGIN
 
 
  -- Find, check or fix schema_name
  IF ( schema_name != '' ) THEN
  okay = 'f';
 
  FOR myrec IN SELECT nspname FROM pg_namespace WHERE text(nspname) = schema_name LOOP
  okay := 't';
  END LOOP;
 
  IF ( okay <> 't' ) THEN
  RAISE NOTICE 'Invalid schema name - using current_schema()';
  SELECT current_schema() into real_schema;
  ELSE
  real_schema = schema_name;
  END IF;
  ELSE
  SELECT current_schema() into real_schema;
  END IF;
 
  -- Find out if the column is in the geometry_columns table
  okay = 'f';
  FOR myrec IN SELECT * from geometry_columns where f_table_schema = text(real_schema) and f_table_name = table_name and f_geometry_column = column_name LOOP
  okay := 't';
  END LOOP;
  IF (okay <> 't') THEN
  RAISE EXCEPTION 'column not found in geometry_columns table';
  RETURN 'f';
  END IF;
 
  -- Remove ref from geometry_columns table
  EXECUTE 'delete from geometry_columns where f_table_schema = ' ||
  quote_literal(real_schema) || ' and f_table_name = ' ||
  quote_literal(table_name) || ' and f_geometry_column = ' ||
  quote_literal(column_name);
 
  -- Remove table column
  EXECUTE 'ALTER TABLE ' || quote_ident(real_schema) || '.' ||
  quote_ident(table_name) || ' DROP COLUMN ' ||
  quote_ident(column_name);
 
  RETURN real_schema || '.' || table_name || '.' || column_name ||' effectively removed.';
 
  END;
  $$
  LANGUAGE 'plpgsql' VOLATILE STRICT;
 
  -----------------------------------------------------------------------
  -- DROPGEOMETRYCOLUMN
  -- <schema>, <table>, <column>
  -----------------------------------------------------------------------
  --
  -- This is a wrapper to the real DropGeometryColumn, for use
  -- when catalogue is undefined
  --
  -----------------------------------------------------------------------
  CREATE OR REPLACE FUNCTION DropGeometryColumn(varchar,varchar,varchar)
  RETURNS text
  AS
  $$
  DECLARE
  ret text;
  BEGIN
  SELECT DropGeometryColumn('',$1,$2,$3) into ret;
  RETURN ret;
  END;
  $$
  LANGUAGE 'plpgsql' VOLATILE STRICT;
 
  -----------------------------------------------------------------------
  -- DROPGEOMETRYCOLUMN
  -- <table>, <column>
  -----------------------------------------------------------------------
  --
  -- This is a wrapper to the real DropGeometryColumn, for use
  -- when catalogue and schema is undefined.
  --
  -----------------------------------------------------------------------
  CREATE OR REPLACE FUNCTION DropGeometryColumn(varchar,varchar)
  RETURNS text
  AS
  $$
  DECLARE
  ret text;
  BEGIN
  SELECT DropGeometryColumn('','',$1,$2) into ret;
  RETURN ret;
  END;
  $$
  LANGUAGE 'plpgsql' VOLATILE STRICT;
 
  -----------------------------------------------------------------------
  -- DROPGEOMETRYTABLE
  -- <catalogue>, <schema>, <table>
  -----------------------------------------------------------------------
  --
  -- Drop a table and all its references in geometry_columns
  --
  -----------------------------------------------------------------------
  CREATE OR REPLACE FUNCTION DropGeometryTable(varchar, varchar,varchar)
  RETURNS text
  AS
  $$
  DECLARE
  catalog_name alias for $1;
  schema_name alias for $2;
  table_name alias for $3;
  real_schema name;
 
  BEGIN
 
  IF ( schema_name = '' ) THEN
  SELECT current_schema() into real_schema;
  ELSE
  real_schema = schema_name;
  END IF;
 
  -- Remove refs from geometry_columns table
  EXECUTE 'DELETE FROM geometry_columns WHERE ' ||
  'f_table_schema = ' || quote_literal(real_schema) ||
  ' AND ' ||
  ' f_table_name = ' || quote_literal(table_name);
 
  -- Remove table
  EXECUTE 'DROP TABLE '
  || quote_ident(real_schema) || '.' ||
  quote_ident(table_name);
 
  RETURN
  real_schema || '.' ||
  table_name ||' dropped.';
 
  END;
  $$
  LANGUAGE 'plpgsql' VOLATILE STRICT;
 
  -----------------------------------------------------------------------
  -- DROPGEOMETRYTABLE
  -- <schema>, <table>
  -----------------------------------------------------------------------
  --
  -- Drop a table and all its references in geometry_columns
  --
  -----------------------------------------------------------------------
  CREATE OR REPLACE FUNCTION DropGeometryTable(varchar,varchar) RETURNS text AS
  $$ SELECT DropGeometryTable('',$1,$2) $$
  LANGUAGE 'sql' WITH (isstrict);
 
  -----------------------------------------------------------------------
  -- DROPGEOMETRYTABLE
  -- <table>
  -----------------------------------------------------------------------
  --
  -- Drop a table and all its references in geometry_columns
  -- For PG>=73 use current_schema()
  --
  -----------------------------------------------------------------------
  CREATE OR REPLACE FUNCTION DropGeometryTable(varchar) RETURNS text AS
  $$ SELECT DropGeometryTable('','',$1) $$
  LANGUAGE 'sql' VOLATILE STRICT;
 
  -----------------------------------------------------------------------
  -- UPDATEGEOMETRYSRID
  -- <catalogue>, <schema>, <table>, <column>, <srid>
  -----------------------------------------------------------------------
  --
  -- Change SRID of all features in a spatially-enabled table
  --
  -----------------------------------------------------------------------
  CREATE OR REPLACE FUNCTION UpdateGeometrySRID(varchar,varchar,varchar,varchar,integer)
  RETURNS text
  AS
  $$
  DECLARE
  catalog_name alias for $1;
  schema_name alias for $2;
  table_name alias for $3;
  column_name alias for $4;
  new_srid alias for $5;
  myrec RECORD;
  okay boolean;
  cname varchar;
  real_schema name;
 
  BEGIN
 
 
  -- Find, check or fix schema_name
  IF ( schema_name != '' ) THEN
  okay = 'f';
 
  FOR myrec IN SELECT nspname FROM pg_namespace WHERE text(nspname) = schema_name LOOP
  okay := 't';
  END LOOP;
 
  IF ( okay <> 't' ) THEN
  RAISE EXCEPTION 'Invalid schema name';
  ELSE
  real_schema = schema_name;
  END IF;
  ELSE
  SELECT INTO real_schema current_schema()::text;
  END IF;
 
  -- Find out if the column is in the geometry_columns table
  okay = 'f';
  FOR myrec IN SELECT * from geometry_columns where f_table_schema = text(real_schema) and f_table_name = table_name and f_geometry_column = column_name LOOP
  okay := 't';
  END LOOP;
  IF (okay <> 't') THEN
  RAISE EXCEPTION 'column not found in geometry_columns table';
  RETURN 'f';
  END IF;
 
  -- Update ref from geometry_columns table
  EXECUTE 'UPDATE geometry_columns SET SRID = ' || new_srid::text ||
  ' where f_table_schema = ' ||
  quote_literal(real_schema) || ' and f_table_name = ' ||
  quote_literal(table_name) || ' and f_geometry_column = ' ||
  quote_literal(column_name);
 
  -- Make up constraint name
  cname = 'enforce_srid_' || column_name;
 
  -- Drop enforce_srid constraint
  EXECUTE 'ALTER TABLE ' || quote_ident(real_schema) ||
  '.' || quote_ident(table_name) ||
  ' DROP constraint ' || quote_ident(cname);
 
  -- Update geometries SRID
  EXECUTE 'UPDATE ' || quote_ident(real_schema) ||
  '.' || quote_ident(table_name) ||
  ' SET ' || quote_ident(column_name) ||
  ' = setSRID(' || quote_ident(column_name) ||
  ', ' || new_srid::text || ')';
 
  -- Reset enforce_srid constraint
  EXECUTE 'ALTER TABLE ' || quote_ident(real_schema) ||
  '.' || quote_ident(table_name) ||
  ' ADD constraint ' || quote_ident(cname) ||
  ' CHECK (srid(' || quote_ident(column_name) ||
  ') = ' || new_srid::text || ')';
 
  RETURN real_schema || '.' || table_name || '.' || column_name ||' SRID changed to ' || new_srid::text;
 
  END;
  $$
  LANGUAGE 'plpgsql' VOLATILE STRICT;
 
  -----------------------------------------------------------------------
  -- UPDATEGEOMETRYSRID
  -- <schema>, <table>, <column>, <srid>
  -----------------------------------------------------------------------
  CREATE OR REPLACE FUNCTION UpdateGeometrySRID(varchar,varchar,varchar,integer)
  RETURNS text
  AS $$
  DECLARE
  ret text;
  BEGIN
  SELECT UpdateGeometrySRID('',$1,$2,$3,$4) into ret;
  RETURN ret;
  END;
  $$
  LANGUAGE 'plpgsql' VOLATILE STRICT;
 
  -----------------------------------------------------------------------
  -- UPDATEGEOMETRYSRID
  -- <table>, <column>, <srid>
  -----------------------------------------------------------------------
  CREATE OR REPLACE FUNCTION UpdateGeometrySRID(varchar,varchar,integer)
  RETURNS text
  AS $$
  DECLARE
  ret text;
  BEGIN
  SELECT UpdateGeometrySRID('','',$1,$2,$3) into ret;
  RETURN ret;
  END;
  $$
  LANGUAGE 'plpgsql' VOLATILE STRICT;
 
  -----------------------------------------------------------------------
  -- FIND_SRID( <schema>, <table>, <geom col> )
  -----------------------------------------------------------------------
  CREATE OR REPLACE FUNCTION find_srid(varchar,varchar,varchar) RETURNS int4 AS
  $$
  DECLARE
  schem text;
  tabl text;
  sr int4;
  BEGIN
  IF $1 IS NULL THEN
  RAISE EXCEPTION 'find_srid() - schema is NULL!';
  END IF;
  IF $2 IS NULL THEN
  RAISE EXCEPTION 'find_srid() - table name is NULL!';
  END IF;
  IF $3 IS NULL THEN
  RAISE EXCEPTION 'find_srid() - column name is NULL!';
  END IF;
  schem = $1;
  tabl = $2;
  -- if the table contains a . and the schema is empty
  -- split the table into a schema and a table
  -- otherwise drop through to default behavior
  IF ( schem = '' and tabl LIKE '%.%' ) THEN
  schem = substr(tabl,1,strpos(tabl,'.')-1);
  tabl = substr(tabl,length(schem)+2);
  ELSE
  schem = schem || '%';
  END IF;
 
  select SRID into sr from geometry_columns where f_table_schema like schem and f_table_name = tabl and f_geometry_column = $3;
  IF NOT FOUND THEN
  RAISE EXCEPTION 'find_srid() - couldnt find the corresponding SRID - is the geometry registered in the GEOMETRY_COLUMNS table? Is there an uppercase/lowercase missmatch?';
  END IF;
  return sr;
  END;
  $$
  LANGUAGE 'plpgsql' IMMUTABLE STRICT;
 
 
  ---------------------------------------------------------------
  -- PROJ support
  ---------------------------------------------------------------
 
  CREATE OR REPLACE FUNCTION get_proj4_from_srid(integer) RETURNS text AS
  $$
  BEGIN
  RETURN proj4text::text FROM spatial_ref_sys WHERE srid= $1;
  END;
  $$
  LANGUAGE 'plpgsql' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION postgis_transform_geometry(geometry,text,text,int)
  RETURNS geometry
  AS '$libdir/postgis-1.5','transform_geom'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION transform(geometry,integer)
  RETURNS geometry
  AS '$libdir/postgis-1.5','transform'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: transform(geometry,integer)
  CREATE OR REPLACE FUNCTION ST_Transform(geometry,integer)
  RETURNS geometry
  AS '$libdir/postgis-1.5','transform'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
 
  -----------------------------------------------------------------------
  -- POSTGIS_VERSION()
  -----------------------------------------------------------------------
 
  CREATE OR REPLACE FUNCTION postgis_version() RETURNS text
  AS '$libdir/postgis-1.5'
  LANGUAGE 'C' IMMUTABLE;
 
  CREATE OR REPLACE FUNCTION postgis_proj_version() RETURNS text
  AS '$libdir/postgis-1.5'
  LANGUAGE 'C' IMMUTABLE;
 
  --
  -- IMPORTANT:
  -- Starting at 1.1.0 this function is used by postgis_proc_upgrade.pl
  -- to extract version of postgis being installed.
  -- Do not modify this w/out also changing postgis_proc_upgrade.pl
  --
  CREATE OR REPLACE FUNCTION postgis_scripts_installed() RETURNS text
  AS 'SELECT ''1.5 r5385''::text AS version'
  LANGUAGE 'sql' IMMUTABLE;
 
  CREATE OR REPLACE FUNCTION postgis_lib_version() RETURNS text
  AS '$libdir/postgis-1.5'
  LANGUAGE 'C' IMMUTABLE; -- a new lib will require a new session
 
  -- NOTE: starting at 1.1.0 this is the same of postgis_lib_version()
  CREATE OR REPLACE FUNCTION postgis_scripts_released() RETURNS text
  AS '$libdir/postgis-1.5'
  LANGUAGE 'C' IMMUTABLE;
 
  CREATE OR REPLACE FUNCTION postgis_uses_stats() RETURNS bool
  AS '$libdir/postgis-1.5'
  LANGUAGE 'C' IMMUTABLE;
 
  CREATE OR REPLACE FUNCTION postgis_geos_version() RETURNS text
  AS '$libdir/postgis-1.5'
  LANGUAGE 'C' IMMUTABLE;
 
  CREATE OR REPLACE FUNCTION postgis_libxml_version() RETURNS text
  AS '$libdir/postgis-1.5'
  LANGUAGE 'C' IMMUTABLE;
 
  CREATE OR REPLACE FUNCTION postgis_scripts_build_date() RETURNS text
  AS 'SELECT ''2010-03-11 19:15:17''::text AS version'
  LANGUAGE 'sql' IMMUTABLE;
 
  CREATE OR REPLACE FUNCTION postgis_lib_build_date() RETURNS text
  AS '$libdir/postgis-1.5'
  LANGUAGE 'C' IMMUTABLE;
 
 
 
  CREATE OR REPLACE FUNCTION postgis_full_version() RETURNS text
  AS $$
  DECLARE
  libver text;
  projver text;
  geosver text;
  libxmlver text;
  usestats bool;
  dbproc text;
  relproc text;
  fullver text;
  BEGIN
  SELECT postgis_lib_version() INTO libver;
  SELECT postgis_proj_version() INTO projver;
  SELECT postgis_geos_version() INTO geosver;
  SELECT postgis_libxml_version() INTO libxmlver;
  SELECT postgis_uses_stats() INTO usestats;
  SELECT postgis_scripts_installed() INTO dbproc;
  SELECT postgis_scripts_released() INTO relproc;
 
  fullver = 'POSTGIS="' || libver || '"';
 
  IF geosver IS NOT NULL THEN
  fullver = fullver || ' GEOS="' || geosver || '"';
  END IF;
 
  IF projver IS NOT NULL THEN
  fullver = fullver || ' PROJ="' || projver || '"';
  END IF;
 
  IF libxmlver IS NOT NULL THEN
  fullver = fullver || ' LIBXML="' || libxmlver || '"';
  END IF;
 
  IF usestats THEN
  fullver = fullver || ' USE_STATS';
  END IF;
 
  -- fullver = fullver || ' DBPROC="' || dbproc || '"';
  -- fullver = fullver || ' RELPROC="' || relproc || '"';
 
  IF dbproc != relproc THEN
  fullver = fullver || ' (procs from ' || dbproc || ' need upgrade)';
  END IF;
 
  RETURN fullver;
  END
  $$
  LANGUAGE 'plpgsql' IMMUTABLE;
 
  ---------------------------------------------------------------
  -- CASTS
  ---------------------------------------------------------------
 
  -- Legacy ST_ variants of casts, to be removed in 2.0
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION st_box2d(geometry)
  RETURNS box2d
  AS '$libdir/postgis-1.5','LWGEOM_to_BOX2DFLOAT4'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION st_box3d(geometry)
  RETURNS box3d
  AS '$libdir/postgis-1.5','LWGEOM_to_BOX3D'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION st_box(geometry)
  RETURNS box
  AS '$libdir/postgis-1.5','LWGEOM_to_BOX'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION st_box2d(box3d)
  RETURNS box2d
  AS '$libdir/postgis-1.5','BOX3D_to_BOX2DFLOAT4'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION st_box3d(box2d)
  RETURNS box3d
  AS '$libdir/postgis-1.5','BOX2DFLOAT4_to_BOX3D'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION st_box(box3d)
  RETURNS box
  AS '$libdir/postgis-1.5','BOX3D_to_BOX'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION st_text(geometry)
  RETURNS text
  AS '$libdir/postgis-1.5','LWGEOM_to_text'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION st_geometry(box2d)
  RETURNS geometry
  AS '$libdir/postgis-1.5','BOX2DFLOAT4_to_LWGEOM'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION st_geometry(box3d)
  RETURNS geometry
  AS '$libdir/postgis-1.5','BOX3D_to_LWGEOM'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION st_geometry(text)
  RETURNS geometry
  AS '$libdir/postgis-1.5','parse_WKT_lwgeom'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION st_geometry(chip)
  RETURNS geometry
  AS '$libdir/postgis-1.5','CHIP_to_LWGEOM'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION st_geometry(bytea)
  RETURNS geometry
  AS '$libdir/postgis-1.5','LWGEOM_from_bytea'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION st_bytea(geometry)
  RETURNS bytea
  AS '$libdir/postgis-1.5','LWGEOM_to_bytea'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION st_box3d_extent(box3d_extent)
  RETURNS box3d
  AS '$libdir/postgis-1.5', 'BOX3D_extent_to_BOX3D'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION st_box2d(box3d_extent)
  RETURNS box2d
  AS '$libdir/postgis-1.5', 'BOX3D_to_BOX2DFLOAT4'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.5.0
  CREATE OR REPLACE FUNCTION st_geometry(box3d_extent)
  RETURNS geometry
  AS '$libdir/postgis-1.5','BOX3D_to_LWGEOM'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
 
  CREATE OR REPLACE FUNCTION box2d(geometry)
  RETURNS box2d
  AS '$libdir/postgis-1.5','LWGEOM_to_BOX2DFLOAT4'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION box3d(geometry)
  RETURNS box3d
  AS '$libdir/postgis-1.5','LWGEOM_to_BOX3D'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION box(geometry)
  RETURNS box
  AS '$libdir/postgis-1.5','LWGEOM_to_BOX'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION box2d(box3d)
  RETURNS box2d
  AS '$libdir/postgis-1.5','BOX3D_to_BOX2DFLOAT4'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION box3d(box2d)
  RETURNS box3d
  AS '$libdir/postgis-1.5','BOX2DFLOAT4_to_BOX3D'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION box(box3d)
  RETURNS box
  AS '$libdir/postgis-1.5','BOX3D_to_BOX'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION text(geometry)
  RETURNS text
  AS '$libdir/postgis-1.5','LWGEOM_to_text'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- this is kept for backward-compatibility
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION box3dtobox(box3d)
  RETURNS box
  AS 'SELECT box($1)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION geometry(box2d)
  RETURNS geometry
  AS '$libdir/postgis-1.5','BOX2DFLOAT4_to_LWGEOM'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION geometry(box3d)
  RETURNS geometry
  AS '$libdir/postgis-1.5','BOX3D_to_LWGEOM'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION geometry(text)
  RETURNS geometry
  AS '$libdir/postgis-1.5','parse_WKT_lwgeom'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION geometry(chip)
  RETURNS geometry
  AS '$libdir/postgis-1.5','CHIP_to_LWGEOM'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION geometry(bytea)
  RETURNS geometry
  AS '$libdir/postgis-1.5','LWGEOM_from_bytea'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION bytea(geometry)
  RETURNS bytea
  AS '$libdir/postgis-1.5','LWGEOM_to_bytea'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- 7.3+ explicit casting definitions
  CREATE CAST (geometry AS box2d) WITH FUNCTION box2d(geometry) AS IMPLICIT;
  CREATE CAST (geometry AS box3d) WITH FUNCTION box3d(geometry) AS IMPLICIT;
  CREATE CAST (geometry AS box) WITH FUNCTION box(geometry) AS IMPLICIT;
  CREATE CAST (box3d AS box2d) WITH FUNCTION box2d(box3d) AS IMPLICIT;
  CREATE CAST (box2d AS box3d) WITH FUNCTION box3d(box2d) AS IMPLICIT;
  CREATE CAST (box2d AS geometry) WITH FUNCTION geometry(box2d) AS IMPLICIT;
  CREATE CAST (box3d AS box) WITH FUNCTION box(box3d) AS IMPLICIT;
  CREATE CAST (box3d AS geometry) WITH FUNCTION geometry(box3d) AS IMPLICIT;
  CREATE CAST (text AS geometry) WITH FUNCTION geometry(text) AS IMPLICIT;
  CREATE CAST (geometry AS text) WITH FUNCTION text(geometry) AS IMPLICIT;
  CREATE CAST (chip AS geometry) WITH FUNCTION geometry(chip) AS IMPLICIT;
  CREATE CAST (bytea AS geometry) WITH FUNCTION geometry(bytea) AS IMPLICIT;
  CREATE CAST (geometry AS bytea) WITH FUNCTION bytea(geometry) AS IMPLICIT;
 
  -- Casts to allow the box3d_extent type to automatically cast to box3d/box2d in queries
  CREATE CAST (box3d_extent AS box3d) WITH FUNCTION box3d_extent(box3d_extent) AS IMPLICIT;
  CREATE CAST (box3d_extent AS box2d) WITH FUNCTION box2d(box3d_extent) AS IMPLICIT;
  CREATE CAST (box3d_extent AS geometry) WITH FUNCTION geometry(box3d_extent) AS IMPLICIT;
 
  ---------------------------------------------------------------
  -- Algorithms
  ---------------------------------------------------------------
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION Simplify(geometry, float8)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_simplify2d'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_Simplify(geometry, float8)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_simplify2d'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- SnapToGrid(input, xoff, yoff, xsize, ysize)
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION SnapToGrid(geometry, float8, float8, float8, float8)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_snaptogrid'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_SnapToGrid(geometry, float8, float8, float8, float8)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_snaptogrid'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- SnapToGrid(input, xsize, ysize) # offsets=0
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION SnapToGrid(geometry, float8, float8)
  RETURNS geometry
  AS 'SELECT SnapToGrid($1, 0, 0, $2, $3)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_SnapToGrid(geometry, float8, float8)
  RETURNS geometry
  AS 'SELECT ST_SnapToGrid($1, 0, 0, $2, $3)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- SnapToGrid(input, size) # xsize=ysize=size, offsets=0
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION SnapToGrid(geometry, float8)
  RETURNS geometry
  AS 'SELECT SnapToGrid($1, 0, 0, $2, $2)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_SnapToGrid(geometry, float8)
  RETURNS geometry
  AS 'SELECT ST_SnapToGrid($1, 0, 0, $2, $2)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- SnapToGrid(input, point_offsets, xsize, ysize, zsize, msize)
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION SnapToGrid(geometry, geometry, float8, float8, float8, float8)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_snaptogrid_pointoff'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_SnapToGrid(geometry, geometry, float8, float8, float8, float8)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_snaptogrid_pointoff'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION Segmentize(geometry, float8)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_segmentize2d'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_Segmentize(geometry, float8)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_segmentize2d'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  ---------------------------------------------------------------
  -- LRS
  ---------------------------------------------------------------
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION line_interpolate_point(geometry, float8)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_line_interpolate_point'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_line_interpolate_point(geometry, float8)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_line_interpolate_point'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION line_substring(geometry, float8, float8)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_line_substring'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_line_substring(geometry, float8, float8)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_line_substring'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION line_locate_point(geometry, geometry)
  RETURNS float8
  AS '$libdir/postgis-1.5', 'LWGEOM_line_locate_point'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_line_locate_point(geometry, geometry)
  RETURNS float8
  AS '$libdir/postgis-1.5', 'LWGEOM_line_locate_point'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION locate_between_measures(geometry, float8, float8)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_locate_between_m'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_locate_between_measures(geometry, float8, float8)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_locate_between_m'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION locate_along_measure(geometry, float8)
  RETURNS geometry
  AS $$ SELECT locate_between_measures($1, $2, $2) $$
  LANGUAGE 'sql' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_locate_along_measure(geometry, float8)
  RETURNS geometry
  AS $$ SELECT locate_between_measures($1, $2, $2) $$
  LANGUAGE 'sql' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION ST_AddMeasure(geometry, float8, float8)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'ST_AddMeasure'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  ---------------------------------------------------------------
  -- GEOS
  ---------------------------------------------------------------
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION intersection(geometry,geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5','intersection'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: intersection(geometry,geometry)
  CREATE OR REPLACE FUNCTION ST_Intersection(geometry,geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5','intersection'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION buffer(geometry,float8)
  RETURNS geometry
  AS '$libdir/postgis-1.5','buffer'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
  -- PostGIS equivalent function: buffer(geometry,float8)
  CREATE OR REPLACE FUNCTION ST_Buffer(geometry,float8)
  RETURNS geometry
  AS '$libdir/postgis-1.5','buffer'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
  -- Availability: 1.5.0 - requires GEOS-3.2 or higher
  CREATE OR REPLACE FUNCTION _ST_Buffer(geometry,float8,cstring)
  RETURNS geometry
  AS '$libdir/postgis-1.5','buffer'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_Buffer(geometry,float8,integer)
  RETURNS geometry
  AS $$ SELECT _ST_Buffer($1, $2,
  CAST('quad_segs='||CAST($3 AS text) as cstring))
  $$
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION ST_buffer(geometry,float8,text)
  RETURNS geometry
  AS $$ SELECT _ST_Buffer($1, $2,
  CAST( regexp_replace($3, '^[0123456789]+$',
  'quad_segs='||$3) AS cstring)
  )
  $$
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION buffer(geometry,float8,integer)
  RETURNS geometry
  AS 'SELECT ST_Buffer($1, $2, $3)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION convexhull(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5','convexhull'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
  -- PostGIS equivalent function: convexhull(geometry)
  CREATE OR REPLACE FUNCTION ST_ConvexHull(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5','convexhull'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
  -- Only accepts LINESTRING as parameters.
  -- Availability: 1.4.0
  CREATE OR REPLACE FUNCTION _ST_LineCrossingDirection(geometry, geometry)
  RETURNS integer
  AS '$libdir/postgis-1.5', 'ST_LineCrossingDirection'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
  -- Availability: 1.4.0
  CREATE OR REPLACE FUNCTION ST_LineCrossingDirection(geometry, geometry)
  RETURNS integer AS
  $$ SELECT CASE WHEN NOT $1 && $2 THEN 0 ELSE _ST_LineCrossingDirection($1,$2) END $$
  LANGUAGE 'sql' IMMUTABLE;
 
 
  -- Only accepts LINESTRING as parameters.
  -- Availability: 1.4.0
  CREATE OR REPLACE FUNCTION ST_LocateBetweenElevations(geometry, float8, float8)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'ST_LocateBetweenElevations'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Requires GEOS >= 3.0.0
  -- Availability: 1.3.3
  CREATE OR REPLACE FUNCTION ST_SimplifyPreserveTopology(geometry, float8)
  RETURNS geometry
  AS '$libdir/postgis-1.5','topologypreservesimplify'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
  -- Requires GEOS >= 3.1.0
  -- Availability: 1.4.0
  CREATE OR REPLACE FUNCTION ST_IsValidReason(geometry)
  RETURNS text
  AS '$libdir/postgis-1.5', 'isvalidreason'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
 
  -- Requires GEOS >= 3.2.0
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION ST_HausdorffDistance(geometry, geometry)
  RETURNS FLOAT8
  AS '$libdir/postgis-1.5', 'hausdorffdistance'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
 
 
  -- Requires GEOS >= 3.2.0
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION ST_HausdorffDistance(geometry, geometry, float8)
  RETURNS FLOAT8
  AS '$libdir/postgis-1.5', 'hausdorffdistancedensify'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION difference(geometry,geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5','difference'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: difference(geometry,geometry)
  CREATE OR REPLACE FUNCTION ST_Difference(geometry,geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5','difference'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION boundary(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5','boundary'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: boundary(geometry)
  CREATE OR REPLACE FUNCTION ST_Boundary(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5','boundary'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION symdifference(geometry,geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5','symdifference'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: symdifference(geometry,geometry)
  CREATE OR REPLACE FUNCTION ST_SymDifference(geometry,geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5','symdifference'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION symmetricdifference(geometry,geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5','symdifference'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_symmetricdifference(geometry,geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5','symdifference'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION GeomUnion(geometry,geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5','geomunion'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: GeomUnion(geometry,geometry)
  CREATE OR REPLACE FUNCTION ST_Union(geometry,geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5','geomunion'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  --------------------------------------------------------------------------------
  -- Aggregates and their supporting functions
  --------------------------------------------------------------------------------
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION collect(geometry, geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_collect'
  LANGUAGE 'C' IMMUTABLE;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_collect(geometry, geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_collect'
  LANGUAGE 'C' IMMUTABLE;
 
  -- Deprecation in 1.2.3
  CREATE AGGREGATE memcollect(
  sfunc = ST_collect,
  basetype = geometry,
  stype = geometry
  );
 
  -- Availability: 1.2.2
  CREATE AGGREGATE ST_memcollect(
  sfunc = ST_collect,
  basetype = geometry,
  stype = geometry
  );
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_collect (geometry[])
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_collect_garray'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE AGGREGATE MemGeomUnion (
  basetype = geometry,
  sfunc = geomunion,
  stype = geometry
  );
 
  -- Availability: 1.2.2
  CREATE AGGREGATE ST_MemUnion (
  basetype = geometry,
  sfunc = ST_Union,
  stype = geometry
  );
 
  --
  -- pgis_abs
  -- Container type to hold the ArrayBuildState pointer as it passes through
  -- the geometry array accumulation aggregate.
  --
  CREATE OR REPLACE FUNCTION pgis_abs_in(cstring)
  RETURNS pgis_abs
  AS '$libdir/postgis-1.5'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION pgis_abs_out(pgis_abs)
  RETURNS cstring
  AS '$libdir/postgis-1.5'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE TYPE pgis_abs (
  internallength = 8,
  input = pgis_abs_in,
  output = pgis_abs_out,
  alignment = double
  );
 
  -- Availability: 1.4.0
  CREATE OR REPLACE FUNCTION pgis_geometry_accum_transfn(pgis_abs, geometry)
  RETURNS pgis_abs
  AS '$libdir/postgis-1.5'
  LANGUAGE 'C';
 
  -- Availability: 1.4.0
  CREATE OR REPLACE FUNCTION pgis_geometry_accum_finalfn(pgis_abs)
  RETURNS geometry[]
  AS '$libdir/postgis-1.5'
  LANGUAGE 'C';
 
  -- Availability: 1.4.0
  CREATE OR REPLACE FUNCTION pgis_geometry_union_finalfn(pgis_abs)
  RETURNS geometry
  AS '$libdir/postgis-1.5'
  LANGUAGE 'C';
 
  -- Availability: 1.4.0
  CREATE OR REPLACE FUNCTION pgis_geometry_collect_finalfn(pgis_abs)
  RETURNS geometry
  AS '$libdir/postgis-1.5'
  LANGUAGE 'C';
 
  -- Availability: 1.4.0
  CREATE OR REPLACE FUNCTION pgis_geometry_polygonize_finalfn(pgis_abs)
  RETURNS geometry
  AS '$libdir/postgis-1.5'
  LANGUAGE 'C';
 
  -- Availability: 1.4.0
  CREATE OR REPLACE FUNCTION pgis_geometry_makeline_finalfn(pgis_abs)
  RETURNS geometry
  AS '$libdir/postgis-1.5'
  LANGUAGE 'C';
 
  -- Deprecation in: 1.2.3
  CREATE AGGREGATE accum (
  sfunc = pgis_geometry_accum_transfn,
  basetype = geometry,
  stype = pgis_abs,
  finalfunc = pgis_geometry_accum_finalfn
  );
 
  -- Availability: 1.2.2
  CREATE AGGREGATE ST_Accum (
  sfunc = pgis_geometry_accum_transfn,
  basetype = geometry,
  stype = pgis_abs,
  finalfunc = pgis_geometry_accum_finalfn
  );
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION unite_garray (geometry[])
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'pgis_union_geometry_array'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.4.0
  CREATE OR REPLACE FUNCTION ST_unite_garray (geometry[])
  RETURNS geometry
  AS '$libdir/postgis-1.5','pgis_union_geometry_array'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.4.0
  CREATE OR REPLACE FUNCTION ST_Union (geometry[])
  RETURNS geometry
  AS '$libdir/postgis-1.5','pgis_union_geometry_array'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE AGGREGATE ST_Union (
  basetype = geometry,
  sfunc = pgis_geometry_accum_transfn,
  stype = pgis_abs,
  finalfunc = pgis_geometry_union_finalfn
  );
 
  -- Deprecation in 1.2.3
  CREATE AGGREGATE collect (
  basetype = geometry,
  sfunc = pgis_geometry_accum_transfn,
  stype = pgis_abs,
  finalfunc = pgis_geometry_collect_finalfn
  );
 
  -- Availability: 1.2.2
  CREATE AGGREGATE ST_Collect (
  BASETYPE = geometry,
  SFUNC = pgis_geometry_accum_transfn,
  STYPE = pgis_abs,
  FINALFUNC = pgis_geometry_collect_finalfn
  );
 
  -- Deprecation in 1.2.3
  CREATE AGGREGATE Polygonize (
  BASETYPE = geometry,
  SFUNC = pgis_geometry_accum_transfn,
  STYPE = pgis_abs,
  FINALFUNC = pgis_geometry_polygonize_finalfn
  );
 
  -- Availability: 1.2.2
  CREATE AGGREGATE ST_Polygonize (
  BASETYPE = geometry,
  SFUNC = pgis_geometry_accum_transfn,
  STYPE = pgis_abs,
  FINALFUNC = pgis_geometry_polygonize_finalfn
  );
 
  -- Deprecation in 1.2.3
  CREATE AGGREGATE makeline (
  BASETYPE = geometry,
  SFUNC = pgis_geometry_accum_transfn,
  STYPE = pgis_abs,
  FINALFUNC = pgis_geometry_makeline_finalfn
  );
 
  -- Availability: 1.2.2
  CREATE AGGREGATE ST_MakeLine (
  BASETYPE = geometry,
  SFUNC = pgis_geometry_accum_transfn,
  STYPE = pgis_abs,
  FINALFUNC = pgis_geometry_makeline_finalfn
  );
 
 
 
  --------------------------------------------------------------------------------
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION relate(geometry,geometry)
  RETURNS text
  AS '$libdir/postgis-1.5','relate_full'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_relate(geometry,geometry)
  RETURNS text
  AS '$libdir/postgis-1.5','relate_full'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION relate(geometry,geometry,text)
  RETURNS boolean
  AS '$libdir/postgis-1.5','relate_pattern'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: relate(geometry,geometry,text)
  CREATE OR REPLACE FUNCTION ST_Relate(geometry,geometry,text)
  RETURNS boolean
  AS '$libdir/postgis-1.5','relate_pattern'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION disjoint(geometry,geometry)
  RETURNS boolean
  AS '$libdir/postgis-1.5'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: disjoint(geometry,geometry)
  CREATE OR REPLACE FUNCTION ST_Disjoint(geometry,geometry)
  RETURNS boolean
  AS '$libdir/postgis-1.5','disjoint'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION touches(geometry,geometry)
  RETURNS boolean
  AS '$libdir/postgis-1.5'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: touches(geometry,geometry)
  CREATE OR REPLACE FUNCTION _ST_Touches(geometry,geometry)
  RETURNS boolean
  AS '$libdir/postgis-1.5','touches'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
  -- Availability: 1.2.2
  -- Inlines index magic
  CREATE OR REPLACE FUNCTION ST_Touches(geometry,geometry)
  RETURNS boolean
  AS 'SELECT $1 && $2 AND _ST_Touches($1,$2)'
  LANGUAGE 'SQL' IMMUTABLE;
 
  -- Availability: 1.3.4
  CREATE OR REPLACE FUNCTION _ST_DWithin(geometry,geometry,float8)
  RETURNS boolean
  AS '$libdir/postgis-1.5', 'LWGEOM_dwithin'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_DWithin(geometry, geometry, float8)
  RETURNS boolean
  AS 'SELECT $1 && ST_Expand($2,$3) AND $2 && ST_Expand($1,$3) AND _ST_DWithin($1, $2, $3)'
  LANGUAGE 'SQL' IMMUTABLE;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION intersects(geometry,geometry)
  RETURNS boolean
  AS '$libdir/postgis-1.5'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: intersects(geometry,geometry)
  CREATE OR REPLACE FUNCTION _ST_Intersects(geometry,geometry)
  RETURNS boolean
  AS '$libdir/postgis-1.5','intersects'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
  -- Availability: 1.2.2
  -- Inlines index magic
  CREATE OR REPLACE FUNCTION ST_Intersects(geometry,geometry)
  RETURNS boolean
  AS 'SELECT $1 && $2 AND _ST_Intersects($1,$2)'
  LANGUAGE 'SQL' IMMUTABLE;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION crosses(geometry,geometry)
  RETURNS boolean
  AS '$libdir/postgis-1.5'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: crosses(geometry,geometry)
  CREATE OR REPLACE FUNCTION _ST_Crosses(geometry,geometry)
  RETURNS boolean
  AS '$libdir/postgis-1.5','crosses'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
  -- Availability: 1.2.2
  -- Inlines index magic
  CREATE OR REPLACE FUNCTION ST_Crosses(geometry,geometry)
  RETURNS boolean
  AS 'SELECT $1 && $2 AND _ST_Crosses($1,$2)'
  LANGUAGE 'SQL' IMMUTABLE;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION within(geometry,geometry)
  RETURNS boolean
  AS '$libdir/postgis-1.5'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: within(geometry,geometry)
  CREATE OR REPLACE FUNCTION _ST_Within(geometry,geometry)
  RETURNS boolean
  AS '$libdir/postgis-1.5','within'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
  -- Availability: 1.2.2
  -- Inlines index magic
  CREATE OR REPLACE FUNCTION ST_Within(geometry,geometry)
  RETURNS boolean
  AS 'SELECT $1 && $2 AND _ST_Within($1,$2)'
  LANGUAGE 'SQL' IMMUTABLE;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION Contains(geometry,geometry)
  RETURNS boolean
  AS '$libdir/postgis-1.5'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: contains(geometry,geometry)
  CREATE OR REPLACE FUNCTION _ST_Contains(geometry,geometry)
  RETURNS boolean
  AS '$libdir/postgis-1.5','contains'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
  -- Availability: 1.2.2
  -- Inlines index magic
  CREATE OR REPLACE FUNCTION ST_Contains(geometry,geometry)
  RETURNS boolean
  AS 'SELECT $1 && $2 AND _ST_Contains($1,$2)'
  LANGUAGE 'SQL' IMMUTABLE;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION _ST_CoveredBy(geometry,geometry)
  RETURNS boolean
  AS '$libdir/postgis-1.5', 'coveredby'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_CoveredBy(geometry,geometry)
  RETURNS boolean
  AS 'SELECT $1 && $2 AND _ST_CoveredBy($1,$2)'
  LANGUAGE 'SQL' IMMUTABLE;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION _ST_Covers(geometry,geometry)
  RETURNS boolean
  AS '$libdir/postgis-1.5', 'covers'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
  -- Availability: 1.2.2
  -- Inlines index magic
  CREATE OR REPLACE FUNCTION ST_Covers(geometry,geometry)
  RETURNS boolean
  AS 'SELECT $1 && $2 AND _ST_Covers($1,$2)'
  LANGUAGE 'SQL' IMMUTABLE;
 
  -- Availability: 1.4.0
  CREATE OR REPLACE FUNCTION _ST_ContainsProperly(geometry,geometry)
  RETURNS boolean
  AS '$libdir/postgis-1.5','containsproperly'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
  -- Availability: 1.4.0
  -- Inlines index magic
  CREATE OR REPLACE FUNCTION ST_ContainsProperly(geometry,geometry)
  RETURNS boolean
  AS 'SELECT $1 && $2 AND _ST_ContainsProperly($1,$2)'
  LANGUAGE 'SQL' IMMUTABLE;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION overlaps(geometry,geometry)
  RETURNS boolean
  AS '$libdir/postgis-1.5'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: overlaps(geometry,geometry)
  CREATE OR REPLACE FUNCTION _ST_Overlaps(geometry,geometry)
  RETURNS boolean
  AS '$libdir/postgis-1.5','overlaps'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
  -- Availability: 1.2.2
  -- Inlines index magic
  CREATE OR REPLACE FUNCTION ST_Overlaps(geometry,geometry)
  RETURNS boolean
  AS 'SELECT $1 && $2 AND _ST_Overlaps($1,$2)'
  LANGUAGE 'SQL' IMMUTABLE;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION IsValid(geometry)
  RETURNS boolean
  AS '$libdir/postgis-1.5', 'isvalid'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
  -- PostGIS equivalent function: IsValid(geometry)
  -- TODO: change null returns to true
  CREATE OR REPLACE FUNCTION ST_IsValid(geometry)
  RETURNS boolean
  AS '$libdir/postgis-1.5', 'isvalid'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
  -- This is also available w/out GEOS
  CREATE OR REPLACE FUNCTION Centroid(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: Centroid(geometry)
  CREATE OR REPLACE FUNCTION ST_Centroid(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'centroid'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION IsRing(geometry)
  RETURNS boolean
  AS '$libdir/postgis-1.5'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: IsRing(geometry)
  CREATE OR REPLACE FUNCTION ST_IsRing(geometry)
  RETURNS boolean
  AS '$libdir/postgis-1.5', 'isring'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION PointOnSurface(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: PointOnSurface(geometry)
  CREATE OR REPLACE FUNCTION ST_PointOnSurface(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'pointonsurface'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION IsSimple(geometry)
  RETURNS boolean
  AS '$libdir/postgis-1.5', 'issimple'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: IsSimple(geometry)
  CREATE OR REPLACE FUNCTION ST_IsSimple(geometry)
  RETURNS boolean
  AS '$libdir/postgis-1.5', 'issimple'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION Equals(geometry,geometry)
  RETURNS boolean
  AS '$libdir/postgis-1.5','geomequals'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION _ST_Equals(geometry,geometry)
  RETURNS boolean
  AS '$libdir/postgis-1.5','geomequals'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
  -- Availability: 1.2.1
  CREATE OR REPLACE FUNCTION ST_Equals(geometry,geometry)
  RETURNS boolean
  AS 'SELECT $1 && $2 AND _ST_Equals($1,$2)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
 
  -----------------------------------------------------------------------
  -- GML & KML INPUT
  -- Availability: 1.5.0
  -----------------------------------------------------------------------
  CREATE OR REPLACE FUNCTION ST_GeomFromGML(text)
  RETURNS geometry
  AS '$libdir/postgis-1.5','geom_from_gml'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION ST_GMLToSQL(text)
  RETURNS geometry
  AS '$libdir/postgis-1.5','geom_from_gml'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION ST_GeomFromKML(text)
  RETURNS geometry
  AS '$libdir/postgis-1.5','geom_from_kml'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -----------------------------------------------------------------------
  -- SVG OUTPUT
  -----------------------------------------------------------------------
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION AsSVG(geometry,int4,int4)
  RETURNS TEXT
  AS '$libdir/postgis-1.5','assvg_geometry'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_AsSVG(geometry,int4,int4)
  RETURNS TEXT
  AS '$libdir/postgis-1.5','assvg_geometry'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION AsSVG(geometry,int4)
  RETURNS TEXT
  AS '$libdir/postgis-1.5','assvg_geometry'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_AsSVG(geometry,int4)
  RETURNS TEXT
  AS '$libdir/postgis-1.5','assvg_geometry'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION AsSVG(geometry)
  RETURNS TEXT
  AS '$libdir/postgis-1.5','assvg_geometry'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_AsSVG(geometry)
  RETURNS TEXT
  AS '$libdir/postgis-1.5','assvg_geometry'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -----------------------------------------------------------------------
  -- GML OUTPUT
  -----------------------------------------------------------------------
  -- _ST_AsGML(version, geom, precision, option)
  CREATE OR REPLACE FUNCTION _ST_AsGML(int4, geometry, int4, int4)
  RETURNS TEXT
  AS '$libdir/postgis-1.5','LWGEOM_asGML'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- AsGML(geom, precision) / version=2
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION AsGML(geometry, int4)
  RETURNS TEXT
  AS 'SELECT _ST_AsGML(2, $1, $2, 0)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_AsGML(geometry, int4)
  RETURNS TEXT
  AS 'SELECT _ST_AsGML(2, $1, $2, 0)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- AsGML(geom) / precision=15 version=2
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION AsGML(geometry)
  RETURNS TEXT
  AS 'SELECT _ST_AsGML(2, $1, 15, 0)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_AsGML(geometry)
  RETURNS TEXT
  AS 'SELECT _ST_AsGML(2, $1, 15, 0)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- ST_AsGML(version, geom) / precision=15 version=2
  -- Availability: 1.3.2
  CREATE OR REPLACE FUNCTION ST_AsGML(int4, geometry)
  RETURNS TEXT
  AS 'SELECT _ST_AsGML($1, $2, 15, 0)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- ST_AsGML(version, geom, precision)
  -- Availability: 1.3.2
  CREATE OR REPLACE FUNCTION ST_AsGML(int4, geometry, int4)
  RETURNS TEXT
  AS 'SELECT _ST_AsGML($1, $2, $3, 0)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- ST_AsGML (geom, precision, option) / version=2
  -- Availability: 1.4.0
  CREATE OR REPLACE FUNCTION ST_AsGML(geometry, int4, int4)
  RETURNS TEXT
  AS 'SELECT _ST_AsGML(2, $1, $2, $3)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- ST_AsGML(version, geom, precision, option)
  -- Availability: 1.4.0
  CREATE OR REPLACE FUNCTION ST_AsGML(int4, geometry, int4, int4)
  RETURNS TEXT
  AS 'SELECT _ST_AsGML($1, $2, $3, $4)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -----------------------------------------------------------------------
  -- KML OUTPUT
  -----------------------------------------------------------------------
  -- _ST_AsKML(version, geom, precision)
  CREATE OR REPLACE FUNCTION _ST_AsKML(int4, geometry, int4)
  RETURNS TEXT
  AS '$libdir/postgis-1.5','LWGEOM_asKML'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- AsKML(geom, precision) / version=2
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION AsKML(geometry, int4)
  RETURNS TEXT
  AS 'SELECT _ST_AsKML(2, transform($1,4326), $2)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_AsKML(geometry, int4)
  RETURNS TEXT
  AS 'SELECT _ST_AsKML(2, ST_Transform($1,4326), $2)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- AsKML(geom) / precision=15 version=2
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION AsKML(geometry)
  RETURNS TEXT
  AS 'SELECT _ST_AsKML(2, transform($1,4326), 15)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- AsKML(version, geom, precision)
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION AsKML(int4, geometry, int4)
  RETURNS TEXT
  AS 'SELECT _ST_AsKML($1, transform($2,4326), $3)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_AsKML(geometry)
  RETURNS TEXT
  AS 'SELECT _ST_AsKML(2, ST_Transform($1,4326), 15)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- ST_AsKML(version, geom) / precision=15 version=2
  -- Availability: 1.3.2
  CREATE OR REPLACE FUNCTION ST_AsKML(int4, geometry)
  RETURNS TEXT
  AS 'SELECT _ST_AsKML($1, ST_Transform($2,4326), 15)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- ST_AsKML(version, geom, precision)
  -- Availability: 1.3.2
  CREATE OR REPLACE FUNCTION ST_AsKML(int4, geometry, int4)
  RETURNS TEXT
  AS 'SELECT _ST_AsKML($1, ST_Transform($2,4326), $3)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -----------------------------------------------------------------------
  -- GEOJSON OUTPUT
  -- Availability: 1.3.4
  -----------------------------------------------------------------------
  -- _ST_AsGeoJson(version, geom, precision, options)
  CREATE OR REPLACE FUNCTION _ST_AsGeoJson(int4, geometry, int4, int4)
  RETURNS TEXT
  AS '$libdir/postgis-1.5','LWGEOM_asGeoJson'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- ST_AsGeoJson(geom, precision) / version=1 options=0
  CREATE OR REPLACE FUNCTION ST_AsGeoJson(geometry, int4)
  RETURNS TEXT
  AS 'SELECT _ST_AsGeoJson(1, $1, $2, 0)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- ST_AsGeoJson(geom) / precision=15 version=1 options=0
  CREATE OR REPLACE FUNCTION ST_AsGeoJson(geometry)
  RETURNS TEXT
  AS 'SELECT _ST_AsGeoJson(1, $1, 15, 0)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- ST_AsGeoJson(version, geom) / precision=15 options=0
  CREATE OR REPLACE FUNCTION ST_AsGeoJson(int4, geometry)
  RETURNS TEXT
  AS 'SELECT _ST_AsGeoJson($1, $2, 15, 0)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- ST_AsGeoJson(version, geom, precision) / options=0
  CREATE OR REPLACE FUNCTION ST_AsGeoJson(int4, geometry, int4)
  RETURNS TEXT
  AS 'SELECT _ST_AsGeoJson($1, $2, $3, 0)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- ST_AsGeoJson(geom, precision, options) / version=1
  CREATE OR REPLACE FUNCTION ST_AsGeoJson(geometry, int4, int4)
  RETURNS TEXT
  AS 'SELECT _ST_AsGeoJson(1, $1, $2, $3)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- ST_AsGeoJson(version, geom, precision,options)
  CREATE OR REPLACE FUNCTION ST_AsGeoJson(int4, geometry, int4, int4)
  RETURNS TEXT
  AS 'SELECT _ST_AsGeoJson($1, $2, $3, $4)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  ------------------------------------------------------------------------
  -- GeoHash (geohash.org)
  ------------------------------------------------------------------------
 
  -- Availability 1.4.0
  CREATE OR REPLACE FUNCTION ST_GeoHash(geometry, int4)
  RETURNS TEXT
  AS '$libdir/postgis-1.5', 'ST_GeoHash'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability 1.4.0
  CREATE OR REPLACE FUNCTION ST_GeoHash(geometry)
  RETURNS TEXT
  AS 'SELECT ST_GeoHash($1, 0)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  ------------------------------------------------------------------------
  -- OGC defined
  ------------------------------------------------------------------------
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION NumPoints(geometry)
  RETURNS int4
  AS '$libdir/postgis-1.5', 'LWGEOM_numpoints_linestring'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: NumPoints(geometry)
  CREATE OR REPLACE FUNCTION ST_NumPoints(geometry)
  RETURNS int4
  AS '$libdir/postgis-1.5', 'LWGEOM_numpoints_linestring'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION NumGeometries(geometry)
  RETURNS int4
  AS '$libdir/postgis-1.5', 'LWGEOM_numgeometries_collection'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: NumGeometries(geometry)
  CREATE OR REPLACE FUNCTION ST_NumGeometries(geometry)
  RETURNS int4
  AS '$libdir/postgis-1.5', 'LWGEOM_numgeometries_collection'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION GeometryN(geometry,integer)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_geometryn_collection'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: GeometryN(geometry)
  CREATE OR REPLACE FUNCTION ST_GeometryN(geometry,integer)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_geometryn_collection'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION Dimension(geometry)
  RETURNS int4
  AS '$libdir/postgis-1.5', 'LWGEOM_dimension'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: Dimension(geometry)
  CREATE OR REPLACE FUNCTION ST_Dimension(geometry)
  RETURNS int4
  AS '$libdir/postgis-1.5', 'LWGEOM_dimension'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION ExteriorRing(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5','LWGEOM_exteriorring_polygon'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: ExteriorRing(geometry)
  CREATE OR REPLACE FUNCTION ST_ExteriorRing(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5','LWGEOM_exteriorring_polygon'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION NumInteriorRings(geometry)
  RETURNS integer
  AS '$libdir/postgis-1.5','LWGEOM_numinteriorrings_polygon'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: NumInteriorRings(geometry)
  CREATE OR REPLACE FUNCTION ST_NumInteriorRings(geometry)
  RETURNS integer
  AS '$libdir/postgis-1.5','LWGEOM_numinteriorrings_polygon'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION NumInteriorRing(geometry)
  RETURNS integer
  AS '$libdir/postgis-1.5','LWGEOM_numinteriorrings_polygon'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_NumInteriorRing(geometry)
  RETURNS integer
  AS '$libdir/postgis-1.5','LWGEOM_numinteriorrings_polygon'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION InteriorRingN(geometry,integer)
  RETURNS geometry
  AS '$libdir/postgis-1.5','LWGEOM_interiorringn_polygon'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: InteriorRingN(geometry)
  CREATE OR REPLACE FUNCTION ST_InteriorRingN(geometry,integer)
  RETURNS geometry
  AS '$libdir/postgis-1.5','LWGEOM_interiorringn_polygon'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION GeometryType(geometry)
  RETURNS text
  AS '$libdir/postgis-1.5', 'LWGEOM_getTYPE'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Not quite equivalent to GeometryType
  CREATE OR REPLACE FUNCTION ST_GeometryType(geometry)
  RETURNS text
  AS '$libdir/postgis-1.5', 'geometry_geometrytype'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION PointN(geometry,integer)
  RETURNS geometry
  AS '$libdir/postgis-1.5','LWGEOM_pointn_linestring'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: PointN(geometry,integer)
  CREATE OR REPLACE FUNCTION ST_PointN(geometry,integer)
  RETURNS geometry
  AS '$libdir/postgis-1.5','LWGEOM_pointn_linestring'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION X(geometry)
  RETURNS float8
  AS '$libdir/postgis-1.5','LWGEOM_x_point'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: X(geometry)
  CREATE OR REPLACE FUNCTION ST_X(geometry)
  RETURNS float8
  AS '$libdir/postgis-1.5','LWGEOM_x_point'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION Y(geometry)
  RETURNS float8
  AS '$libdir/postgis-1.5','LWGEOM_y_point'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: Y(geometry)
  CREATE OR REPLACE FUNCTION ST_Y(geometry)
  RETURNS float8
  AS '$libdir/postgis-1.5','LWGEOM_y_point'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION Z(geometry)
  RETURNS float8
  AS '$libdir/postgis-1.5','LWGEOM_z_point'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_Z(geometry)
  RETURNS float8
  AS '$libdir/postgis-1.5','LWGEOM_z_point'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION M(geometry)
  RETURNS float8
  AS '$libdir/postgis-1.5','LWGEOM_m_point'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_M(geometry)
  RETURNS float8
  AS '$libdir/postgis-1.5','LWGEOM_m_point'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION StartPoint(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_startpoint_linestring'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: StartPoint(geometry))
  CREATE OR REPLACE FUNCTION ST_StartPoint(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_startpoint_linestring'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION EndPoint(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_endpoint_linestring'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: EndPoint(geometry))
  CREATE OR REPLACE FUNCTION ST_EndPoint(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_endpoint_linestring'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION IsClosed(geometry)
  RETURNS boolean
  AS '$libdir/postgis-1.5', 'LWGEOM_isclosed_linestring'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: IsClosed(geometry)
  CREATE OR REPLACE FUNCTION ST_IsClosed(geometry)
  RETURNS boolean
  AS '$libdir/postgis-1.5', 'LWGEOM_isclosed_linestring'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION IsEmpty(geometry)
  RETURNS boolean
  AS '$libdir/postgis-1.5', 'LWGEOM_isempty'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: IsEmpty(geometry)
  CREATE OR REPLACE FUNCTION ST_IsEmpty(geometry)
  RETURNS boolean
  AS '$libdir/postgis-1.5', 'LWGEOM_isempty'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION SRID(geometry)
  RETURNS int4
  AS '$libdir/postgis-1.5','LWGEOM_getSRID'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: getSRID(geometry)
  CREATE OR REPLACE FUNCTION ST_SRID(geometry)
  RETURNS int4
  AS '$libdir/postgis-1.5','LWGEOM_getSRID'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION SetSRID(geometry,int4)
  RETURNS geometry
  AS '$libdir/postgis-1.5','LWGEOM_setSRID'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_SetSRID(geometry,int4)
  RETURNS geometry
  AS '$libdir/postgis-1.5','LWGEOM_setSRID'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION AsBinary(geometry)
  RETURNS bytea
  AS '$libdir/postgis-1.5','LWGEOM_asBinary'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: AsBinary(geometry)
  CREATE OR REPLACE FUNCTION ST_AsBinary(geometry)
  RETURNS bytea
  AS '$libdir/postgis-1.5','LWGEOM_asBinary'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION AsBinary(geometry,text)
  RETURNS bytea
  AS '$libdir/postgis-1.5','LWGEOM_asBinary'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_AsBinary(geometry,text)
  RETURNS bytea
  AS '$libdir/postgis-1.5','LWGEOM_asBinary'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION AsText(geometry)
  RETURNS TEXT
  AS '$libdir/postgis-1.5','LWGEOM_asText'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: AsText(geometry)
  CREATE OR REPLACE FUNCTION ST_AsText(geometry)
  RETURNS TEXT
  AS '$libdir/postgis-1.5','LWGEOM_asText'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION GeometryFromText(text)
  RETURNS geometry
  AS '$libdir/postgis-1.5','LWGEOM_from_text'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_GeometryFromText(text)
  RETURNS geometry
  AS '$libdir/postgis-1.5','LWGEOM_from_text'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION GeometryFromText(text, int4)
  RETURNS geometry
  AS '$libdir/postgis-1.5','LWGEOM_from_text'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_GeometryFromText(text, int4)
  RETURNS geometry
  AS '$libdir/postgis-1.5','LWGEOM_from_text'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION GeomFromText(text)
  RETURNS geometry AS 'SELECT geometryfromtext($1)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_GeomFromText(text)
  RETURNS geometry
  AS '$libdir/postgis-1.5','LWGEOM_from_text'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION GeomFromText(text, int4)
  RETURNS geometry AS 'SELECT geometryfromtext($1, $2)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: ST_GeometryFromText(text, int4)
  CREATE OR REPLACE FUNCTION ST_GeomFromText(text, int4)
  RETURNS geometry
  AS '$libdir/postgis-1.5','LWGEOM_from_text'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION PointFromText(text)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(GeomFromText($1)) = ''POINT''
  THEN GeomFromText($1)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_PointFromText(text)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(ST_GeomFromText($1)) = ''POINT''
  THEN ST_GeomFromText($1)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION PointFromText(text, int4)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(GeomFromText($1, $2)) = ''POINT''
  THEN GeomFromText($1,$2)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: PointFromText(text, int4)
  -- TODO: improve this ... by not duplicating constructor time.
  CREATE OR REPLACE FUNCTION ST_PointFromText(text, int4)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(ST_GeomFromText($1, $2)) = ''POINT''
  THEN ST_GeomFromText($1, $2)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION LineFromText(text)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(GeomFromText($1)) = ''LINESTRING''
  THEN GeomFromText($1)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_LineFromText(text)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(ST_GeomFromText($1)) = ''LINESTRING''
  THEN ST_GeomFromText($1)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION LineFromText(text, int4)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(GeomFromText($1, $2)) = ''LINESTRING''
  THEN GeomFromText($1,$2)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: LineFromText(text, int4)
  CREATE OR REPLACE FUNCTION ST_LineFromText(text, int4)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(GeomFromText($1, $2)) = ''LINESTRING''
  THEN GeomFromText($1,$2)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION LineStringFromText(text)
  RETURNS geometry
  AS 'SELECT LineFromText($1)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION LineStringFromText(text, int4)
  RETURNS geometry
  AS 'SELECT LineFromText($1, $2)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION PolyFromText(text)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(GeomFromText($1)) = ''POLYGON''
  THEN GeomFromText($1)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_PolyFromText(text)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(ST_GeomFromText($1)) = ''POLYGON''
  THEN ST_GeomFromText($1)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION PolyFromText(text, int4)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(GeomFromText($1, $2)) = ''POLYGON''
  THEN GeomFromText($1,$2)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: ST_PolygonFromText(text, int4)
  CREATE OR REPLACE FUNCTION ST_PolyFromText(text, int4)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(ST_GeomFromText($1, $2)) = ''POLYGON''
  THEN ST_GeomFromText($1, $2)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION PolygonFromText(text, int4)
  RETURNS geometry
  AS 'SELECT PolyFromText($1, $2)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_PolygonFromText(text, int4)
  RETURNS geometry
  AS 'SELECT PolyFromText($1, $2)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION PolygonFromText(text)
  RETURNS geometry
  AS 'SELECT PolyFromText($1)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_PolygonFromText(text)
  RETURNS geometry
  AS 'SELECT ST_PolyFromText($1)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION MLineFromText(text, int4)
  RETURNS geometry
  AS '
  SELECT CASE
  WHEN geometrytype(GeomFromText($1, $2)) = ''MULTILINESTRING''
  THEN GeomFromText($1,$2)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: MLineFromText(text, int4)
  CREATE OR REPLACE FUNCTION ST_MLineFromText(text, int4)
  RETURNS geometry
  AS '
  SELECT CASE
  WHEN geometrytype(GeomFromText($1, $2)) = ''MULTILINESTRING''
  THEN GeomFromText($1,$2)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION MLineFromText(text)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(GeomFromText($1)) = ''MULTILINESTRING''
  THEN GeomFromText($1)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_MLineFromText(text)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(ST_GeomFromText($1)) = ''MULTILINESTRING''
  THEN ST_GeomFromText($1)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION MultiLineStringFromText(text)
  RETURNS geometry
  AS 'SELECT ST_MLineFromText($1)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_MultiLineStringFromText(text)
  RETURNS geometry
  AS 'SELECT ST_MLineFromText($1)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION MultiLineStringFromText(text, int4)
  RETURNS geometry
  AS 'SELECT MLineFromText($1, $2)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_MultiLineStringFromText(text, int4)
  RETURNS geometry
  AS 'SELECT MLineFromText($1, $2)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION MPointFromText(text, int4)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(GeomFromText($1,$2)) = ''MULTIPOINT''
  THEN GeomFromText($1,$2)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: MPointFromText(text, int4)
  CREATE OR REPLACE FUNCTION ST_MPointFromText(text, int4)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(GeomFromText($1, $2)) = ''MULTIPOINT''
  THEN GeomFromText($1, $2)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION MPointFromText(text)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(GeomFromText($1)) = ''MULTIPOINT''
  THEN GeomFromText($1)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_MPointFromText(text)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(ST_GeomFromText($1)) = ''MULTIPOINT''
  THEN ST_GeomFromText($1)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION MultiPointFromText(text, int4)
  RETURNS geometry
  AS 'SELECT MPointFromText($1, $2)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION MultiPointFromText(text)
  RETURNS geometry
  AS 'SELECT MPointFromText($1)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_MultiPointFromText(text)
  RETURNS geometry
  AS 'SELECT ST_MPointFromText($1)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION MultiPointFromText(text)
  RETURNS geometry
  AS 'SELECT MPointFromText($1)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_MultiPointFromText(text)
  RETURNS geometry
  AS 'SELECT MPointFromText($1)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION MPolyFromText(text, int4)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(GeomFromText($1, $2)) = ''MULTIPOLYGON''
  THEN GeomFromText($1,$2)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: MPolyFromText(text, int4)
  CREATE OR REPLACE FUNCTION ST_MPolyFromText(text, int4)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(ST_GeomFromText($1, $2)) = ''MULTIPOLYGON''
  THEN ST_GeomFromText($1,$2)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION MPolyFromText(text)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(GeomFromText($1)) = ''MULTIPOLYGON''
  THEN GeomFromText($1)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  --Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_MPolyFromText(text)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(ST_GeomFromText($1)) = ''MULTIPOLYGON''
  THEN ST_GeomFromText($1)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION MultiPolygonFromText(text, int4)
  RETURNS geometry
  AS 'SELECT MPolyFromText($1, $2)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_MultiPolygonFromText(text, int4)
  RETURNS geometry
  AS 'SELECT MPolyFromText($1, $2)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION MultiPolygonFromText(text)
  RETURNS geometry
  AS 'SELECT MPolyFromText($1)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_MultiPolygonFromText(text)
  RETURNS geometry
  AS 'SELECT MPolyFromText($1)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION GeomCollFromText(text, int4)
  RETURNS geometry
  AS '
  SELECT CASE
  WHEN geometrytype(GeomFromText($1, $2)) = ''GEOMETRYCOLLECTION''
  THEN GeomFromText($1,$2)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_GeomCollFromText(text, int4)
  RETURNS geometry
  AS '
  SELECT CASE
  WHEN geometrytype(ST_GeomFromText($1, $2)) = ''GEOMETRYCOLLECTION''
  THEN ST_GeomFromText($1,$2)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION GeomCollFromText(text)
  RETURNS geometry
  AS '
  SELECT CASE
  WHEN geometrytype(GeomFromText($1)) = ''GEOMETRYCOLLECTION''
  THEN GeomFromText($1)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_GeomCollFromText(text)
  RETURNS geometry
  AS '
  SELECT CASE
  WHEN geometrytype(ST_GeomFromText($1)) = ''GEOMETRYCOLLECTION''
  THEN ST_GeomFromText($1)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION GeomFromWKB(bytea)
  RETURNS geometry
  AS '$libdir/postgis-1.5','LWGEOM_from_WKB'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_GeomFromWKB(bytea)
  RETURNS geometry
  AS '$libdir/postgis-1.5','LWGEOM_from_WKB'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION GeomFromWKB(bytea, int)
  RETURNS geometry
  AS 'SELECT setSRID(GeomFromWKB($1), $2)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: GeomFromWKB(bytea, int)
  CREATE OR REPLACE FUNCTION ST_GeomFromWKB(bytea, int)
  RETURNS geometry
  AS 'SELECT ST_SetSRID(ST_GeomFromWKB($1), $2)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION PointFromWKB(bytea, int)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(GeomFromWKB($1, $2)) = ''POINT''
  THEN GeomFromWKB($1, $2)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: PointFromWKB(bytea, int)
  CREATE OR REPLACE FUNCTION ST_PointFromWKB(bytea, int)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(ST_GeomFromWKB($1, $2)) = ''POINT''
  THEN ST_GeomFromWKB($1, $2)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION PointFromWKB(bytea)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(GeomFromWKB($1)) = ''POINT''
  THEN GeomFromWKB($1)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_PointFromWKB(bytea)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(ST_GeomFromWKB($1)) = ''POINT''
  THEN ST_GeomFromWKB($1)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION LineFromWKB(bytea, int)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(GeomFromWKB($1, $2)) = ''LINESTRING''
  THEN GeomFromWKB($1, $2)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: LineFromWKB(bytea, int)
  CREATE OR REPLACE FUNCTION ST_LineFromWKB(bytea, int)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(ST_GeomFromWKB($1, $2)) = ''LINESTRING''
  THEN ST_GeomFromWKB($1, $2)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION LineFromWKB(bytea)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(GeomFromWKB($1)) = ''LINESTRING''
  THEN GeomFromWKB($1)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_LineFromWKB(bytea)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(ST_GeomFromWKB($1)) = ''LINESTRING''
  THEN ST_GeomFromWKB($1)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION LinestringFromWKB(bytea, int)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(GeomFromWKB($1, $2)) = ''LINESTRING''
  THEN GeomFromWKB($1, $2)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_LinestringFromWKB(bytea, int)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(ST_GeomFromWKB($1, $2)) = ''LINESTRING''
  THEN ST_GeomFromWKB($1, $2)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION LinestringFromWKB(bytea)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(GeomFromWKB($1)) = ''LINESTRING''
  THEN GeomFromWKB($1)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_LinestringFromWKB(bytea)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(GeomFromWKB($1)) = ''LINESTRING''
  THEN GeomFromWKB($1)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION PolyFromWKB(bytea, int)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(GeomFromWKB($1, $2)) = ''POLYGON''
  THEN GeomFromWKB($1, $2)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: PolyFromWKB(text, int)
  CREATE OR REPLACE FUNCTION ST_PolyFromWKB(bytea, int)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(ST_GeomFromWKB($1, $2)) = ''POLYGON''
  THEN ST_GeomFromWKB($1, $2)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION PolyFromWKB(bytea)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(GeomFromWKB($1)) = ''POLYGON''
  THEN GeomFromWKB($1)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_PolyFromWKB(bytea)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(ST_GeomFromWKB($1)) = ''POLYGON''
  THEN ST_GeomFromWKB($1)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION PolygonFromWKB(bytea, int)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(GeomFromWKB($1,$2)) = ''POLYGON''
  THEN GeomFromWKB($1, $2)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_PolygonFromWKB(bytea, int)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(ST_GeomFromWKB($1,$2)) = ''POLYGON''
  THEN ST_GeomFromWKB($1, $2)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION PolygonFromWKB(bytea)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(GeomFromWKB($1)) = ''POLYGON''
  THEN GeomFromWKB($1)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_PolygonFromWKB(bytea)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(GeomFromWKB($1)) = ''POLYGON''
  THEN GeomFromWKB($1)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION MPointFromWKB(bytea, int)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(GeomFromWKB($1,$2)) = ''MULTIPOINT''
  THEN GeomFromWKB($1, $2)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: MPointFromWKB(text, int)
  CREATE OR REPLACE FUNCTION ST_MPointFromWKB(bytea, int)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(GeomFromWKB($1, $2)) = ''MULTIPOINT''
  THEN GeomFromWKB($1, $2)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION MPointFromWKB(bytea)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(GeomFromWKB($1)) = ''MULTIPOINT''
  THEN GeomFromWKB($1)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_MPointFromWKB(bytea)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(ST_GeomFromWKB($1)) = ''MULTIPOINT''
  THEN ST_GeomFromWKB($1)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION MultiPointFromWKB(bytea, int)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(GeomFromWKB($1,$2)) = ''MULTIPOINT''
  THEN GeomFromWKB($1, $2)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_MultiPointFromWKB(bytea, int)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(ST_GeomFromWKB($1,$2)) = ''MULTIPOINT''
  THEN ST_GeomFromWKB($1, $2)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION MultiPointFromWKB(bytea)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(GeomFromWKB($1)) = ''MULTIPOINT''
  THEN GeomFromWKB($1)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_MultiPointFromWKB(bytea)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(ST_GeomFromWKB($1)) = ''MULTIPOINT''
  THEN ST_GeomFromWKB($1)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION MultiLineFromWKB(bytea, int)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(GeomFromWKB($1, $2)) = ''MULTILINESTRING''
  THEN GeomFromWKB($1, $2)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION MultiLineFromWKB(bytea, int)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(GeomFromWKB($1, $2)) = ''MULTILINESTRING''
  THEN GeomFromWKB($1, $2)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION MultiLineFromWKB(bytea)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(GeomFromWKB($1)) = ''MULTILINESTRING''
  THEN GeomFromWKB($1)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_MultiLineFromWKB(bytea)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(ST_GeomFromWKB($1)) = ''MULTILINESTRING''
  THEN ST_GeomFromWKB($1)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION MLineFromWKB(bytea, int)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(GeomFromWKB($1, $2)) = ''MULTILINESTRING''
  THEN GeomFromWKB($1, $2)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: MLineFromWKB(text, int)
  CREATE OR REPLACE FUNCTION ST_MLineFromWKB(bytea, int)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(ST_GeomFromWKB($1, $2)) = ''MULTILINESTRING''
  THEN ST_GeomFromWKB($1, $2)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION MLineFromWKB(bytea)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(GeomFromWKB($1)) = ''MULTILINESTRING''
  THEN GeomFromWKB($1)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_MLineFromWKB(bytea)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(ST_GeomFromWKB($1)) = ''MULTILINESTRING''
  THEN ST_GeomFromWKB($1)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION MPolyFromWKB(bytea, int)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(GeomFromWKB($1, $2)) = ''MULTIPOLYGON''
  THEN GeomFromWKB($1, $2)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: MPolyFromWKB(bytea, int)
  CREATE OR REPLACE FUNCTION ST_MPolyFromWKB(bytea, int)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(ST_GeomFromWKB($1, $2)) = ''MULTIPOLYGON''
  THEN ST_GeomFromWKB($1, $2)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION MPolyFromWKB(bytea)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(GeomFromWKB($1)) = ''MULTIPOLYGON''
  THEN GeomFromWKB($1)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_MPolyFromWKB(bytea)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(ST_GeomFromWKB($1)) = ''MULTIPOLYGON''
  THEN ST_GeomFromWKB($1)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION MultiPolyFromWKB(bytea, int)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(GeomFromWKB($1, $2)) = ''MULTIPOLYGON''
  THEN GeomFromWKB($1, $2)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_MultiPolyFromWKB(bytea, int)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(ST_GeomFromWKB($1, $2)) = ''MULTIPOLYGON''
  THEN ST_GeomFromWKB($1, $2)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION MultiPolyFromWKB(bytea)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(GeomFromWKB($1)) = ''MULTIPOLYGON''
  THEN GeomFromWKB($1)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_MultiPolyFromWKB(bytea)
  RETURNS geometry
  AS '
  SELECT CASE WHEN geometrytype(ST_GeomFromWKB($1)) = ''MULTIPOLYGON''
  THEN ST_GeomFromWKB($1)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION GeomCollFromWKB(bytea, int)
  RETURNS geometry
  AS '
  SELECT CASE
  WHEN geometrytype(GeomFromWKB($1, $2)) = ''GEOMETRYCOLLECTION''
  THEN GeomFromWKB($1, $2)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_GeomCollFromWKB(bytea, int)
  RETURNS geometry
  AS '
  SELECT CASE
  WHEN geometrytype(GeomFromWKB($1, $2)) = ''GEOMETRYCOLLECTION''
  THEN GeomFromWKB($1, $2)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION GeomCollFromWKB(bytea)
  RETURNS geometry
  AS '
  SELECT CASE
  WHEN geometrytype(GeomFromWKB($1)) = ''GEOMETRYCOLLECTION''
  THEN GeomFromWKB($1)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_GeomCollFromWKB(bytea)
  RETURNS geometry
  AS '
  SELECT CASE
  WHEN geometrytype(ST_GeomFromWKB($1)) = ''GEOMETRYCOLLECTION''
  THEN ST_GeomFromWKB($1)
  ELSE NULL END
  '
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  --New functions
 
  -- Maximum distance between linestrings.
 
  CREATE OR REPLACE FUNCTION max_distance(geometry,geometry)
  RETURNS float8
  AS '$libdir/postgis-1.5', 'LWGEOM_maxdistance2d_linestring'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION _ST_MaxDistance(geometry,geometry)
  RETURNS float8
  AS '$libdir/postgis-1.5', 'LWGEOM_maxdistance2d_linestring'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION ST_MaxDistance(geometry,geometry)
  RETURNS float8
  AS 'SELECT _ST_MaxDistance(ST_ConvexHull($1), ST_ConvexHull($2))'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION ST_ClosestPoint(geometry,geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_closestpoint'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION ST_ShortestLine(geometry,geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_shortestline2d'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION _ST_LongestLine(geometry,geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_longestline2d'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION ST_LongestLine(geometry,geometry)
  RETURNS geometry
  AS 'SELECT _ST_LongestLine(ST_ConvexHull($1), ST_ConvexHull($2))'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION _ST_DFullyWithin(geometry,geometry,float8)
  RETURNS boolean
  AS '$libdir/postgis-1.5', 'LWGEOM_dfullywithin'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION ST_DFullyWithin(geometry, geometry, float8)
  RETURNS boolean
  AS 'SELECT $1 && ST_Expand($2,$3) AND $2 && ST_Expand($1,$3) AND _ST_DFullyWithin(ST_ConvexHull($1), ST_ConvexHull($2), $3)'
  LANGUAGE 'SQL' IMMUTABLE;
 
 
  --
  -- SFSQL 1.1
  --
  -- BdPolyFromText(multiLineStringTaggedText String, SRID Integer): Polygon
  --
  -- Construct a Polygon given an arbitrary
  -- collection of closed linestrings as a
  -- MultiLineString text representation.
  --
  -- This is a PLPGSQL function rather then an SQL function
  -- To avoid double call of BuildArea (one to get GeometryType
  -- and another to actual return, in a CASE WHEN construct).
  -- Also, we profit from plpgsql to RAISE exceptions.
  --
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION BdPolyFromText(text, integer)
  RETURNS geometry
  AS $$
  DECLARE
  geomtext alias for $1;
  srid alias for $2;
  mline geometry;
  geom geometry;
  BEGIN
  mline := MultiLineStringFromText(geomtext, srid);
 
  IF mline IS NULL
  THEN
  RAISE EXCEPTION 'Input is not a MultiLinestring';
  END IF;
 
  geom := BuildArea(mline);
 
  IF GeometryType(geom) != 'POLYGON'
  THEN
  RAISE EXCEPTION 'Input returns more then a single polygon, try using BdMPolyFromText instead';
  END IF;
 
  RETURN geom;
  END;
  $$
  LANGUAGE 'plpgsql' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_BdPolyFromText(text, integer)
  RETURNS geometry
  AS $$
  DECLARE
  geomtext alias for $1;
  srid alias for $2;
  mline geometry;
  geom geometry;
  BEGIN
  mline := ST_MultiLineStringFromText(geomtext, srid);
 
  IF mline IS NULL
  THEN
  RAISE EXCEPTION 'Input is not a MultiLinestring';
  END IF;
 
  geom := ST_BuildArea(mline);
 
  IF GeometryType(geom) != 'POLYGON'
  THEN
  RAISE EXCEPTION 'Input returns more then a single polygon, try using BdMPolyFromText instead';
  END IF;
 
  RETURN geom;
  END;
  $$
  LANGUAGE 'plpgsql' IMMUTABLE STRICT;
 
  --
  -- SFSQL 1.1
  --
  -- BdMPolyFromText(multiLineStringTaggedText String, SRID Integer): MultiPolygon
  --
  -- Construct a MultiPolygon given an arbitrary
  -- collection of closed linestrings as a
  -- MultiLineString text representation.
  --
  -- This is a PLPGSQL function rather then an SQL function
  -- To raise an exception in case of invalid input.
  --
  -- Deprecation in 1.2.3
  CREATE OR REPLACE FUNCTION BdMPolyFromText(text, integer)
  RETURNS geometry
  AS $$
  DECLARE
  geomtext alias for $1;
  srid alias for $2;
  mline geometry;
  geom geometry;
  BEGIN
  mline := MultiLineStringFromText(geomtext, srid);
 
  IF mline IS NULL
  THEN
  RAISE EXCEPTION 'Input is not a MultiLinestring';
  END IF;
 
  geom := multi(BuildArea(mline));
 
  RETURN geom;
  END;
  $$
  LANGUAGE 'plpgsql' IMMUTABLE STRICT;
 
  -- Availability: 1.2.2
  CREATE OR REPLACE FUNCTION ST_BdMPolyFromText(text, integer)
  RETURNS geometry
  AS $$
  DECLARE
  geomtext alias for $1;
  srid alias for $2;
  mline geometry;
  geom geometry;
  BEGIN
  mline := ST_MultiLineStringFromText(geomtext, srid);
 
  IF mline IS NULL
  THEN
  RAISE EXCEPTION 'Input is not a MultiLinestring';
  END IF;
 
  geom := multi(ST_BuildArea(mline));
 
  RETURN geom;
  END;
  $$
  LANGUAGE 'plpgsql' IMMUTABLE STRICT;
 
 
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  --
  -- $Id: long_xact.sql.in.c 4894 2009-11-25 19:15:57Z pramsey $
  --
  -- PostGIS - Spatial Types for PostgreSQL
  -- http://postgis.refractions.net
  -- Copyright 2001-2003 Refractions Research Inc.
  --
  -- This is free software; you can redistribute and/or modify it under
  -- the terms of the GNU General Public Licence. See the COPYING file.
  --
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
 
 
 
 
  -----------------------------------------------------------------------
  -- LONG TERM LOCKING
  -----------------------------------------------------------------------
 
  -- UnlockRows(authid)
  -- removes all locks held by the given auth
  -- returns the number of locks released
  CREATE OR REPLACE FUNCTION UnlockRows(text)
  RETURNS int
  AS $$
  DECLARE
  ret int;
  BEGIN
 
  IF NOT LongTransactionsEnabled() THEN
  RAISE EXCEPTION 'Long transaction support disabled, use EnableLongTransaction() to enable.';
  END IF;
 
  EXECUTE 'DELETE FROM authorization_table where authid = ' ||
  quote_literal($1);
 
  GET DIAGNOSTICS ret = ROW_COUNT;
 
  RETURN ret;
  END;
  $$
  LANGUAGE 'plpgsql' VOLATILE STRICT;
 
  -- LockRow([schema], table, rowid, auth, [expires])
  -- Returns 1 if successfully obtained the lock, 0 otherwise
  CREATE OR REPLACE FUNCTION LockRow(text, text, text, text, timestamp)
  RETURNS int
  AS $$
  DECLARE
  myschema alias for $1;
  mytable alias for $2;
  myrid alias for $3;
  authid alias for $4;
  expires alias for $5;
  ret int;
  mytoid oid;
  myrec RECORD;
 
  BEGIN
 
  IF NOT LongTransactionsEnabled() THEN
  RAISE EXCEPTION 'Long transaction support disabled, use EnableLongTransaction() to enable.';
  END IF;
 
  EXECUTE 'DELETE FROM authorization_table WHERE expires < now()';
 
  SELECT c.oid INTO mytoid FROM pg_class c, pg_namespace n
  WHERE c.relname = mytable
  AND c.relnamespace = n.oid
  AND n.nspname = myschema;
 
  -- RAISE NOTICE 'toid: %', mytoid;
 
  FOR myrec IN SELECT * FROM authorization_table WHERE
  toid = mytoid AND rid = myrid
  LOOP
  IF myrec.authid != authid THEN
  RETURN 0;
  ELSE
  RETURN 1;
  END IF;
  END LOOP;
 
  EXECUTE 'INSERT INTO authorization_table VALUES ('||
  quote_literal(mytoid::text)||','||quote_literal(myrid)||
  ','||quote_literal(expires::text)||
  ','||quote_literal(authid) ||')';
 
  GET DIAGNOSTICS ret = ROW_COUNT;
 
  RETURN ret;
  END;
  $$
  LANGUAGE 'plpgsql' VOLATILE STRICT;
 
  -- LockRow(schema, table, rid, authid);
  CREATE OR REPLACE FUNCTION LockRow(text, text, text, text)
  RETURNS int
  AS
  $$ SELECT LockRow($1, $2, $3, $4, now()::timestamp+'1:00'); $$
  LANGUAGE 'sql' VOLATILE STRICT;
 
  -- LockRow(table, rid, authid);
  CREATE OR REPLACE FUNCTION LockRow(text, text, text)
  RETURNS int
  AS
  $$ SELECT LockRow(current_schema(), $1, $2, $3, now()::timestamp+'1:00'); $$
  LANGUAGE 'sql' VOLATILE STRICT;
 
  -- LockRow(schema, table, rid, expires);
  CREATE OR REPLACE FUNCTION LockRow(text, text, text, timestamp)
  RETURNS int
  AS
  $$ SELECT LockRow(current_schema(), $1, $2, $3, $4); $$
  LANGUAGE 'sql' VOLATILE STRICT;
 
 
  CREATE OR REPLACE FUNCTION AddAuth(text)
  RETURNS BOOLEAN
  AS $$
  DECLARE
  lockid alias for $1;
  okay boolean;
  myrec record;
  BEGIN
  -- check to see if table exists
  -- if not, CREATE TEMP TABLE mylock (transid xid, lockcode text)
  okay := 'f';
  FOR myrec IN SELECT * FROM pg_class WHERE relname = 'temp_lock_have_table' LOOP
  okay := 't';
  END LOOP;
  IF (okay <> 't') THEN
  CREATE TEMP TABLE temp_lock_have_table (transid xid, lockcode text);
  -- this will only work from pgsql7.4 up
  -- ON COMMIT DELETE ROWS;
  END IF;
 
  -- INSERT INTO mylock VALUES ( $1)
  -- EXECUTE 'INSERT INTO temp_lock_have_table VALUES ( '||
  -- quote_literal(getTransactionID()) || ',' ||
  -- quote_literal(lockid) ||')';
 
  INSERT INTO temp_lock_have_table VALUES (getTransactionID(), lockid);
 
  RETURN true::boolean;
  END;
  $$
  LANGUAGE PLPGSQL;
 
 
  -- CheckAuth( <schema>, <table>, <ridcolumn> )
  --
  -- Returns 0
  --
  CREATE OR REPLACE FUNCTION CheckAuth(text, text, text)
  RETURNS INT
  AS $$
  DECLARE
  schema text;
  BEGIN
  IF NOT LongTransactionsEnabled() THEN
  RAISE EXCEPTION 'Long transaction support disabled, use EnableLongTransaction() to enable.';
  END IF;
 
  if ( $1 != '' ) THEN
  schema = $1;
  ELSE
  SELECT current_schema() into schema;
  END IF;
 
  -- TODO: check for an already existing trigger ?
 
  EXECUTE 'CREATE TRIGGER check_auth BEFORE UPDATE OR DELETE ON '
  || quote_ident(schema) || '.' || quote_ident($2)
  ||' FOR EACH ROW EXECUTE PROCEDURE CheckAuthTrigger('
  || quote_literal($3) || ')';
 
  RETURN 0;
  END;
  $$
  LANGUAGE 'plpgsql';
 
  -- CheckAuth(<table>, <ridcolumn>)
  CREATE OR REPLACE FUNCTION CheckAuth(text, text)
  RETURNS INT
  AS
  $$ SELECT CheckAuth('', $1, $2) $$
  LANGUAGE 'SQL';
 
  CREATE OR REPLACE FUNCTION CheckAuthTrigger()
  RETURNS trigger AS
  '$libdir/postgis-1.5', 'check_authorization'
  LANGUAGE C;
 
  CREATE OR REPLACE FUNCTION GetTransactionID()
  RETURNS xid AS
  '$libdir/postgis-1.5', 'getTransactionID'
  LANGUAGE C;
 
 
  --
  -- Enable Long transactions support
  --
  -- Creates the authorization_table if not already existing
  --
  CREATE OR REPLACE FUNCTION EnableLongTransactions()
  RETURNS TEXT
  AS $$
  DECLARE
  "query" text;
  exists bool;
  rec RECORD;
 
  BEGIN
 
  exists = 'f';
  FOR rec IN SELECT * FROM pg_class WHERE relname = 'authorization_table'
  LOOP
  exists = 't';
  END LOOP;
 
  IF NOT exists
  THEN
  "query" = 'CREATE TABLE authorization_table (
  toid oid, -- table oid
  rid text, -- row id
  expires timestamp,
  authid text
  )';
  EXECUTE "query";
  END IF;
 
  exists = 'f';
  FOR rec IN SELECT * FROM pg_class WHERE relname = 'authorized_tables'
  LOOP
  exists = 't';
  END LOOP;
 
  IF NOT exists THEN
  "query" = 'CREATE VIEW authorized_tables AS ' ||
  'SELECT ' ||
  'n.nspname as schema, ' ||
  'c.relname as table, trim(' ||
  quote_literal(chr(92) || '000') ||
  ' from t.tgargs) as id_column ' ||
  'FROM pg_trigger t, pg_class c, pg_proc p ' ||
  ', pg_namespace n ' ||
  'WHERE p.proname = ' || quote_literal('checkauthtrigger') ||
  ' AND c.relnamespace = n.oid' ||
  ' AND t.tgfoid = p.oid and t.tgrelid = c.oid';
  EXECUTE "query";
  END IF;
 
  RETURN 'Long transactions support enabled';
  END;
  $$
  LANGUAGE 'plpgsql';
 
  --
  -- Check if Long transactions support is enabled
  --
  CREATE OR REPLACE FUNCTION LongTransactionsEnabled()
  RETURNS bool
  AS $$
  DECLARE
  rec RECORD;
  BEGIN
  FOR rec IN SELECT oid FROM pg_class WHERE relname = 'authorized_tables'
  LOOP
  return 't';
  END LOOP;
  return 'f';
  END;
  $$
  LANGUAGE 'plpgsql';
 
  --
  -- Disable Long transactions support
  --
  -- (1) Drop any long_xact trigger
  -- (2) Drop the authorization_table
  -- (3) KEEP the authorized_tables view
  --
  CREATE OR REPLACE FUNCTION DisableLongTransactions()
  RETURNS TEXT
  AS $$
  DECLARE
  rec RECORD;
 
  BEGIN
 
  --
  -- Drop all triggers applied by CheckAuth()
  --
  FOR rec IN
  SELECT c.relname, t.tgname, t.tgargs FROM pg_trigger t, pg_class c, pg_proc p
  WHERE p.proname = 'checkauthtrigger' and t.tgfoid = p.oid and t.tgrelid = c.oid
  LOOP
  EXECUTE 'DROP TRIGGER ' || quote_ident(rec.tgname) ||
  ' ON ' || quote_ident(rec.relname);
  END LOOP;
 
  --
  -- Drop the authorization_table table
  --
  FOR rec IN SELECT * FROM pg_class WHERE relname = 'authorization_table' LOOP
  DROP TABLE authorization_table;
  END LOOP;
 
  --
  -- Drop the authorized_tables view
  --
  FOR rec IN SELECT * FROM pg_class WHERE relname = 'authorized_tables' LOOP
  DROP VIEW authorized_tables;
  END LOOP;
 
  RETURN 'Long transactions support disabled';
  END;
  $$
  LANGUAGE 'plpgsql';
 
  ---------------------------------------------------------------
  -- END
  ---------------------------------------------------------------
 
 
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  --
  -- $Id: sqlmm.sql.in.c 4894 2009-11-25 19:15:57Z pramsey $
  --
  -- PostGIS - Spatial Types for PostgreSQL
  -- http://postgis.refractions.net
  -- Copyright 2001-2003 Refractions Research Inc.
  --
  -- This is free software; you can redistribute and/or modify it under
  -- the terms of the GNU General Public Licence. See the COPYING file.
  --
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  -- This file defines a subset of SQL/MM functions (that is, only those
  -- currently defined by ESRI's ArcSDE). Since these functions already exist
  -- in PostGIS (for the most part), these functions simply expose the current
  -- functions. Although mostly complying with SQL/MM standards, these prototypes
  -- follow ESRI's ArcSDE SQL Specifications and not SQL/MM standards where
  -- disparities exist.
  --
  -- Specification Disparity Notes:
  -- * ST_OrderingEquals(geometry, geometry) is implemented as per
  -- ESRI's ArcSDE SQL specifications, not SQL/MM specifications.
  -- (http://edndoc.esri.com/arcsde/9.1/sql_api/sqlapi3.htm#ST_OrderingEquals)
  -- * Geometry constructors default to an SRID of -1, not 0 as per SQL/MM specs.
  -- * Boolean return type methods (ie. ST_IsValid, ST_IsEmpty, ...)
  -- * SQL/MM : RETURNS 1 if TRUE, 0 if (FALSE, NULL)
  -- * ESRI in Informix : RETURNS 1 if (TRUE, NULL), 0 if FALSE
  -- * ESRI in DB2 : RETURNS 1 if TRUE, 0 if FALSE, NULL if NULL
  -- * PostGIS : RETURNS 1 if TRUE, 0 if FALSE, NULL if NULL
  --
  -- TODO: Implement ESRI's Shape constructors
  -- * SE_AsShape(geometry)
  -- * SE_ShapeToSQL
  -- * SE_GeomFromShape
  -- * SE_PointFromShape
  -- * SE_LineFromShape
  -- * SE_PolyFromShape
  -- * SE_MPointFromShape
  -- * SE_MLineFromShape
  -- * SE_MPolyFromShape
  -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
 
 
  -------------------------------------------------------------------------------
  -- SQL/MM (ArcSDE subset) - SQL Functions for constructing an ST_Geometry
  -- value given its WTK representation
  -- (http://edndoc.esri.com/arcsde/9.1/general_topics/storing_geo_in_rdbms.html)
  -------------------------------------------------------------------------------
 
  -- PostGIS equivalent function: ST_GeometryFromText(text)
  -- Note: Defaults to an SRID=-1, not 0 as per SQL/MM specs.
  CREATE OR REPLACE FUNCTION ST_WKTToSQL(text)
  RETURNS geometry
  AS '$libdir/postgis-1.5','LWGEOM_from_text'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- ST_GeomFromText(text, int4) - already defined
  -- ST_PointFromText(text, int4) - already defined
  -- ST_LineFromText(text, int4) - already defined
  -- ST_PolyFromText(text, int4) - already defined
  -- ST_MPointFromText(text, int4) - already defined
  -- ST_MLineFromText(text, int4) - already defined
  -- ST_MPolyFromText(text, int4) - already defined
 
  -------------------------------------------------------------------------------
  -- SQL/MM (ArcSDE subset) - SQL Functions for constructing an ST_Geometry
  -- value given its WKB representation
  -------------------------------------------------------------------------------
 
  -- PostGIS equivalent function: GeomFromWKB(bytea))
  -- Note: Defaults to an SRID=-1, not 0 as per SQL/MM specs.
 
  CREATE OR REPLACE FUNCTION ST_WKBToSQL(bytea)
  RETURNS geometry
  AS '$libdir/postgis-1.5','LWGEOM_from_WKB'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- ST_GeomFromWKB(bytea, int) - already defined
  -- ST_PointFromWKB(bytea, int) - already defined
  -- ST_LineFromWKB(bytea, int) - already defined
  -- ST_PolyFromWKB(bytea, int) - already defined
  -- ST_MPointFromWKB(bytea, int) - already defined
  -- ST_MLineFromWKB(bytea, int) - already defined
  -- ST_MPolyFromWKB(bytea, int) - already defined
 
  -------------------------------------------------------------------------------
  -- SQL/MM (ArcSDE subset) - SQL Functions for constructing an ST_Geometry
  -- value given an ESRI Shape representation
  -------------------------------------------------------------------------------
 
  -- TODO: SE_ShapeToSQL
  -- TODO: SE_GeomFromShape
  -- TODO: SE_PointFromShape
  -- TODO: SE_LineFromShape
  -- TODO: SE_PolyFromShape
  -- TODO: SE_MPointFromShape
  -- TODO: SE_MLineFromShape
  -- TODO: SE_MPolyFromShape
 
  -------------------------------------------------------------------------------
  -- SQL/MM (ArcSDE subset) - SQL Functions for obtaining the WKT representation
  -- of an ST_Geometry
  -------------------------------------------------------------------------------
 
  -- ST_AsText(geometry) - already defined
 
  -------------------------------------------------------------------------------
  -- SQL/MM (ArcSDE subset) - SQL Functions for obtaining the WKB representation
  -- of an ST_Geometry
  -------------------------------------------------------------------------------
 
  -- ST_AsBinary(geometry) - already defined
 
  -------------------------------------------------------------------------------
  -- SQL/MM (ArcSDE subset) - SQL Functions for obtaining the ESRI Shape
  -- representation of an ST_Geometry
  -------------------------------------------------------------------------------
 
  -- TODO: SE_AsShape(geometry)
  --CREATE OR REPLACE FUNCTION SE_AsShape(geometry)
  -- RETURNS bytea
  -- AS '$libdir/postgis-1.5','LWGEOM_AsShape'
  -- LANGUAGE 'C' IMMUTABLE STRICT;
 
  -------------------------------------------------------------------------------
  -- SQL/MM (ArcSDE subset) - SQL Functions on type ST_Geometry
  -------------------------------------------------------------------------------
 
  -- PostGIS equivalent function: ndims(geometry)
  CREATE OR REPLACE FUNCTION ST_CoordDim(geometry)
  RETURNS smallint
  AS '$libdir/postgis-1.5', 'LWGEOM_ndims'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- ST_Dimension(geometry) - already defined.
  -- ST_GeometryType(geometry) - already defined.
  -- ST_SRID(geometry) - already defined.
  -- ST_IsEmpty(geometry) - already defined.
  -- ST_IsSimple(geometry) - already defined.
  -- ST_IsValid(geometry) - already defined.
  -- ST_Boundary(geometry) - already defined.
  -- ST_Envelope(geometry) - already defined.
  -- ST_Transform(geometry) - already defined.
  -- ST_AsText(geometry) - already defined.
  -- ST_AsBinary(geometry) - already defined.
  -- SE_AsShape(geometry) - already defined.
  -- ST_X(geometry) - already defined.
  -- ST_Y(geometry) - already defined.
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION _ST_OrderingEquals(geometry, geometry)
  RETURNS boolean
  AS '$libdir/postgis-1.5', 'LWGEOM_same'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
  -- Availability: 1.3.0
  CREATE OR REPLACE FUNCTION ST_OrderingEquals(geometry, geometry)
  RETURNS boolean
  AS $$
  SELECT $1 ~= $2 AND _ST_OrderingEquals($1, $2)
  $$
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION SE_Is3D(geometry)
  RETURNS bool
  AS '$libdir/postgis-1.5', 'LWGEOM_hasz'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION SE_IsMeasured(geometry)
  RETURNS bool
  AS '$libdir/postgis-1.5', 'LWGEOM_hasm'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -------------------------------------------------------------------------------
  -- SQL/MM (ArcSDE subset) - SQL Functions on type ST_Point
  -------------------------------------------------------------------------------
 
  -- PostGIS equivalent function: makePoint(float8,float8)
  CREATE OR REPLACE FUNCTION ST_Point(float8, float8)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_makepoint'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: Z(geometry)
  CREATE OR REPLACE FUNCTION SE_Z(geometry)
  RETURNS float8
  AS '$libdir/postgis-1.5','LWGEOM_z_point'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: M(geometry)
  CREATE OR REPLACE FUNCTION SE_M(geometry)
  RETURNS float8
  AS '$libdir/postgis-1.5','LWGEOM_m_point'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -------------------------------------------------------------------------------
  -- SQL/MM (ArcSDE subset) - SQL Functions on type ST_Curve
  -------------------------------------------------------------------------------
 
  -- ST_StartPoint(geometry) - already defined.
  -- ST_EndPoint(geometry) - already defined.
  -- ST_IsClosed(geometry) - already defined.
  -- ST_IsRing(geometry) - already defined.
  -- ST_Length(geometry) - already defined.
 
  -------------------------------------------------------------------------------
  -- SQL/MM (ArcSDE subset) - SQL Functions on type ST_LineString
  -------------------------------------------------------------------------------
 
  -- ST_NumPoints(geometry) - already defined.
  -- ST_PointN(geometry) - already defined.
 
  -------------------------------------------------------------------------------
  -- SQL/MM (ArcSDE subset) - SQL Functions on type ST_Surface
  -------------------------------------------------------------------------------
 
  -- ST_Centroid(geometry) - already defined.
  -- ST_PointOnSurface(geometry) - already defined.
  -- ST_Area(geometry) - already defined.
  -- ST_Perimeter(geometry) - already defined.
 
  -------------------------------------------------------------------------------
  -- SQL/MM (ArcSDE subset) - SQL Functions on type ST_Polygon
  -------------------------------------------------------------------------------
 
  -- PostGIS equivalent function: MakePolygon(geometry)
  CREATE OR REPLACE FUNCTION ST_Polygon(geometry, int)
  RETURNS geometry
  AS $$
  SELECT setSRID(makepolygon($1), $2)
  $$
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- ST_ExteriorRing(geometry) - already defined.
  -- ST_NumInteriorRing(geometry) - already defined.
  -- ST_InteriorRingN(geometry, integer) - already defined.
 
  -------------------------------------------------------------------------------
  -- SQL/MM (ArcSDE subset) - SQL Functions on type ST_GeomCollection
  -------------------------------------------------------------------------------
 
  -- ST_NumGeometries(geometry) - already defined.
  -- ST_GeometryN(geometry, integer) - already defined.
 
  -------------------------------------------------------------------------------
  -- SQL/MM (ArcSDE subset) - SQL Functions on type ST_MultiCurve
  -------------------------------------------------------------------------------
 
  -- ST_IsClosed(geometry) - already defined.
  -- ST_Length(geometry) - already defined.
 
  -------------------------------------------------------------------------------
  -- SQL/MM (ArcSDE subset) - SQL Functions on type ST_MultiSurface
  -------------------------------------------------------------------------------
 
  -- ST_Centroid(geometry) - already defined.
  -- ST_PointOnSurface(geometry) - already defined.
  -- ST_Area(geometry) - already defined.
  -- ST_Perimeter(geometry) - already defined.
 
  -------------------------------------------------------------------------------
  -- SQL/MM (ArcSDE subset) - SQL Functions that test spatial relationships
  -------------------------------------------------------------------------------
 
  -- ST_Equals(geometry, geometry) - already defined.
  -- ST_Disjoint(geometry, geometry) - already defined.
  -- ST_Touches(geometry, geometry) - already defined.
  -- ST_Within(geometry, geometry) - already defined.
  -- ST_Overlaps(geometry, geometry) - already defined.
  -- ST_Crosses(geometry, geometry) - already defined.
  -- ST_Intersects(geometry, geometry) - already defined.
  -- ST_Contains(geometry, geometry) - already defined.
  -- ST_Relate(geometry, geometry, text) - already defined.
 
  -- PostGIS equivalent function: none
  CREATE OR REPLACE FUNCTION SE_EnvelopesIntersect(geometry,geometry)
  RETURNS boolean
  AS $$
  SELECT $1 && $2
  $$
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -------------------------------------------------------------------------------
  -- SQL/MM (ArcSDE subset) - SQL Functions for distance relationships
  -------------------------------------------------------------------------------
 
  -- ST_Distance(geometry, geometry) - already defined.
 
  -------------------------------------------------------------------------------
  -- SQL/MM (ArcSDE subset) - SQL Functions that implement spatial operators
  -------------------------------------------------------------------------------
 
  -- ST_Intersection(geometry, geometry) - already defined.
  -- ST_Difference(geometry, geometry) - already defined.
  -- ST_Union(geometry, geometry) - already defined.
  -- ST_SymDifference(geometry, geometry) - already defined.
  -- ST_Buffer(geometry, float8) - already defined.
  -- ST_ConvexHull(geometry) already defined.
 
  -- PostGIS equivalent function: locate_along_measure(geometry, float8)
  CREATE OR REPLACE FUNCTION SE_LocateAlong(geometry, float8)
  RETURNS geometry
  AS $$ SELECT locate_between_measures($1, $2, $2) $$
  LANGUAGE 'sql' IMMUTABLE STRICT;
 
  -- PostGIS equivalent function: locate_between_measures(geometry, float8, float8)
  CREATE OR REPLACE FUNCTION SE_LocateBetween(geometry, float8, float8)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_locate_between_m'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
 
 
  -------------------------------------------------------------------------------
  -- END
  -------------------------------------------------------------------------------
 
 
  ---------------------------------------------------------------------------
  -- $Id: geography.sql.in.c 5065 2009-12-30 01:25:17Z pramsey $
  --
  -- PostGIS - Spatial Types for PostgreSQL
  -- Copyright 2009 Paul Ramsey <pramsey@cleverelephant.ca>
  --
  -- This is free software; you can redistribute and/or modify it under
  -- the terms of the GNU General Public Licence. See the COPYING file.
  --
  ---------------------------------------------------------------------------
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION geography_typmod_in(cstring[])
  RETURNS integer
  AS '$libdir/postgis-1.5','geography_typmod_in'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION geography_typmod_out(integer)
  RETURNS cstring
  AS '$libdir/postgis-1.5','geography_typmod_out'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION geography_in(cstring, oid, integer)
  RETURNS geography
  AS '$libdir/postgis-1.5','geography_in'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION geography_out(geography)
  RETURNS cstring
  AS '$libdir/postgis-1.5','geography_out'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION geography_analyze(internal)
  RETURNS bool
  AS '$libdir/postgis-1.5','geography_analyze'
  LANGUAGE 'C' VOLATILE STRICT;
 
  -- Availability: 1.5.0
  CREATE TYPE geography (
  internallength = variable,
  input = geography_in,
  output = geography_out,
  typmod_in = geography_typmod_in,
  typmod_out = geography_typmod_out,
  analyze = geography_analyze,
  storage = main,
  alignment = double
  );
 
  --
  -- GIDX type is used by the GiST index bindings.
  -- In/out functions are stubs, as all access should be internal.
  ---
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION gidx_in(cstring)
  RETURNS gidx
  AS '$libdir/postgis-1.5','gidx_in'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION gidx_out(gidx)
  RETURNS cstring
  AS '$libdir/postgis-1.5','gidx_out'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0
  CREATE TYPE gidx (
  internallength = variable,
  input = gidx_in,
  output = gidx_out,
  storage = plain,
  alignment = double
  );
 
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION geography(geography, integer, boolean)
  RETURNS geography
  AS '$libdir/postgis-1.5','geography_enforce_typmod'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0
  CREATE CAST (geography AS geography) WITH FUNCTION geography(geography, integer, boolean) AS IMPLICIT;
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION ST_AsText(geography)
  RETURNS text
  AS '$libdir/postgis-1.5','geography_as_text'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0 - this is just a hack to prevent unknown from causing ambiguous name because of geography
  -- TODO Remove in 2.0
  CREATE OR REPLACE FUNCTION ST_AsText(text)
  RETURNS text AS
  $$ SELECT ST_AsText($1::geometry); $$
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION ST_GeographyFromText(text)
  RETURNS geography
  AS '$libdir/postgis-1.5','geography_from_text'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION ST_GeogFromText(text)
  RETURNS geography
  AS '$libdir/postgis-1.5','geography_from_text'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION ST_AsBinary(geography)
  RETURNS bytea
  AS '$libdir/postgis-1.5','geography_as_binary'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0 - this is just a hack to prevent unknown from causing ambiguous name because of geography
  -- TODO Remove in 2.0
  CREATE OR REPLACE FUNCTION ST_AsBinary(text)
  RETURNS bytea AS
  $$ SELECT ST_AsBinary($1::geometry); $$
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION ST_GeogFromWKB(bytea)
  RETURNS geography
  AS '$libdir/postgis-1.5','geography_from_binary'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION geography_typmod_dims(integer)
  RETURNS integer
  AS '$libdir/postgis-1.5','geography_typmod_dims'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION geography_typmod_srid(integer)
  RETURNS integer
  AS '$libdir/postgis-1.5','geography_typmod_srid'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION geography_typmod_type(integer)
  RETURNS text
  AS '$libdir/postgis-1.5','geography_typmod_type'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0
  CREATE OR REPLACE VIEW geography_columns AS
  SELECT
  current_database() AS f_table_catalog,
  n.nspname AS f_table_schema,
  c.relname AS f_table_name,
  a.attname AS f_geography_column,
  geography_typmod_dims(a.atttypmod) AS coord_dimension,
  geography_typmod_srid(a.atttypmod) AS srid,
  geography_typmod_type(a.atttypmod) AS type
  FROM
  pg_class c,
  pg_attribute a,
  pg_type t,
  pg_namespace n
  WHERE c.relkind IN('r','v')
  AND t.typname = 'geography'
  AND a.attisdropped = false
  AND a.atttypid = t.oid
  AND a.attrelid = c.oid
  AND c.relnamespace = n.oid;
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION geography(geometry)
  RETURNS geography
  AS '$libdir/postgis-1.5','geography_from_geometry'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0
  CREATE CAST (geometry AS geography) WITH FUNCTION geography(geometry) AS IMPLICIT;
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION geometry(geography)
  RETURNS geometry
  AS '$libdir/postgis-1.5','geometry_from_geography'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0
  CREATE CAST (geography AS geometry) WITH FUNCTION geometry(geography) ;
 
  -- ---------- ---------- ---------- ---------- ---------- ---------- ----------
  -- GiST Support Functions
  -- ---------- ---------- ---------- ---------- ---------- ---------- ----------
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION geography_gist_consistent(internal,geometry,int4)
  RETURNS bool
  AS '$libdir/postgis-1.5' ,'geography_gist_consistent'
  LANGUAGE 'C';
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION geography_gist_compress(internal)
  RETURNS internal
  AS '$libdir/postgis-1.5','geography_gist_compress'
  LANGUAGE 'C';
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION geography_gist_penalty(internal,internal,internal)
  RETURNS internal
  AS '$libdir/postgis-1.5' ,'geography_gist_penalty'
  LANGUAGE 'C';
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION geography_gist_picksplit(internal, internal)
  RETURNS internal
  AS '$libdir/postgis-1.5' ,'geography_gist_picksplit'
  LANGUAGE 'C';
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION geography_gist_union(bytea, internal)
  RETURNS internal
  AS '$libdir/postgis-1.5' ,'geography_gist_union'
  LANGUAGE 'C';
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION geography_gist_same(box2d, box2d, internal)
  RETURNS internal
  AS '$libdir/postgis-1.5' ,'geography_gist_same'
  LANGUAGE 'C';
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION geography_gist_decompress(internal)
  RETURNS internal
  AS '$libdir/postgis-1.5' ,'geography_gist_decompress'
  LANGUAGE 'C';
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION geography_gist_selectivity (internal, oid, internal, int4)
  RETURNS float8
  AS '$libdir/postgis-1.5', 'geography_gist_selectivity'
  LANGUAGE 'C';
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION geography_gist_join_selectivity(internal, oid, internal, smallint)
  RETURNS float8
  AS '$libdir/postgis-1.5', 'geography_gist_join_selectivity'
  LANGUAGE 'C';
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION geography_overlaps(geography, geography)
  RETURNS boolean
  AS '$libdir/postgis-1.5' ,'geography_overlaps'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0
  CREATE OPERATOR && (
  LEFTARG = geography, RIGHTARG = geography, PROCEDURE = geography_overlaps,
  COMMUTATOR = '&&',
  RESTRICT = geography_gist_selectivity,
  JOIN = geography_gist_join_selectivity
  );
 
 
  -- Availability: 1.5.0
  CREATE OPERATOR CLASS gist_geography_ops
  DEFAULT FOR TYPE geography USING GIST AS
  STORAGE gidx,
  OPERATOR 3 && ,
  -- OPERATOR 6 ~= ,
  -- OPERATOR 7 ~ ,
  -- OPERATOR 8 @ ,
  FUNCTION 1 geography_gist_consistent (internal, geometry, int4),
  FUNCTION 2 geography_gist_union (bytea, internal),
  FUNCTION 3 geography_gist_compress (internal),
  FUNCTION 4 geography_gist_decompress (internal),
  FUNCTION 5 geography_gist_penalty (internal, internal, internal),
  FUNCTION 6 geography_gist_picksplit (internal, internal),
  FUNCTION 7 geography_gist_same (box2d, box2d, internal);
 
 
  -- ---------- ---------- ---------- ---------- ---------- ---------- ----------
  -- B-Tree Functions
  -- For sorting and grouping
  -- Availability: 1.5.0
  -- ---------- ---------- ---------- ---------- ---------- ---------- ----------
 
  CREATE OR REPLACE FUNCTION geography_lt(geography, geography)
  RETURNS bool
  AS '$libdir/postgis-1.5', 'geography_lt'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION geography_le(geography, geography)
  RETURNS bool
  AS '$libdir/postgis-1.5', 'geography_le'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION geography_gt(geography, geography)
  RETURNS bool
  AS '$libdir/postgis-1.5', 'geography_gt'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION geography_ge(geography, geography)
  RETURNS bool
  AS '$libdir/postgis-1.5', 'geography_ge'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION geography_eq(geography, geography)
  RETURNS bool
  AS '$libdir/postgis-1.5', 'geography_eq'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION geography_cmp(geography, geography)
  RETURNS integer
  AS '$libdir/postgis-1.5', 'geography_cmp'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  --
  -- Sorting operators for Btree
  --
 
  CREATE OPERATOR < (
  LEFTARG = geography, RIGHTARG = geography, PROCEDURE = geography_lt,
  COMMUTATOR = '>', NEGATOR = '>=',
  RESTRICT = contsel, JOIN = contjoinsel
  );
 
  CREATE OPERATOR <= (
  LEFTARG = geography, RIGHTARG = geography, PROCEDURE = geography_le,
  COMMUTATOR = '>=', NEGATOR = '>',
  RESTRICT = contsel, JOIN = contjoinsel
  );
 
  CREATE OPERATOR = (
  LEFTARG = geography, RIGHTARG = geography, PROCEDURE = geography_eq,
  COMMUTATOR = '=', -- we might implement a faster negator here
  RESTRICT = contsel, JOIN = contjoinsel
  );
 
  CREATE OPERATOR >= (
  LEFTARG = geography, RIGHTARG = geography, PROCEDURE = geography_ge,
  COMMUTATOR = '<=', NEGATOR = '<',
  RESTRICT = contsel, JOIN = contjoinsel
  );
  CREATE OPERATOR > (
  LEFTARG = geography, RIGHTARG = geography, PROCEDURE = geography_gt,
  COMMUTATOR = '<', NEGATOR = '<=',
  RESTRICT = contsel, JOIN = contjoinsel
  );
 
  CREATE OPERATOR CLASS btree_geography_ops
  DEFAULT FOR TYPE geography USING btree AS
  OPERATOR 1 < ,
  OPERATOR 2 <= ,
  OPERATOR 3 = ,
  OPERATOR 4 >= ,
  OPERATOR 5 > ,
  FUNCTION 1 geography_cmp (geography, geography);
 
 
  -- ---------- ---------- ---------- ---------- ---------- ---------- ----------
  -- Export Functions
  -- Availability: 1.5.0
  -- ---------- ---------- ---------- ---------- ---------- ---------- ----------
 
  --
  -- SVG OUTPUT
  --
 
  -- ST_AsSVG(geography, precision, rel)
  CREATE OR REPLACE FUNCTION ST_AsSVG(geography,int4,int4)
  RETURNS text
  AS '$libdir/postgis-1.5','geography_as_svg'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- ST_AsSVG(geography, precision) / rel=0
  CREATE OR REPLACE FUNCTION ST_AsSVG(geography,int4)
  RETURNS text
  AS '$libdir/postgis-1.5','geography_as_svg'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- ST_AsSVG(geography) / precision=15, rel=0
  CREATE OR REPLACE FUNCTION ST_AsSVG(geography)
  RETURNS text
  AS '$libdir/postgis-1.5','geography_as_svg'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0 - this is just a hack to prevent unknown from causing ambiguous name because of geography
  -- TODO Remove in 2.0
  CREATE OR REPLACE FUNCTION ST_AsSVG(text)
  RETURNS text AS
  $$ SELECT ST_AsSVG($1::geometry); $$
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
 
  --
  -- GML OUTPUT
  --
 
  -- _ST_AsGML(version, geography, precision, option)
  CREATE OR REPLACE FUNCTION _ST_AsGML(int4, geography, int4, int4)
  RETURNS text
  AS '$libdir/postgis-1.5','geography_as_gml'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- ST_AsGML(geography, precision) / version=2 options=0
  CREATE OR REPLACE FUNCTION ST_AsGML(geography, int4)
  RETURNS text
  AS 'SELECT _ST_AsGML(2, $1, $2, 0)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- ST_AsGML(geography) / precision=15 version=2 options=0
  CREATE OR REPLACE FUNCTION ST_AsGML(geography)
  RETURNS text
  AS 'SELECT _ST_AsGML(2, $1, 15, 0)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0 - this is just a hack to prevent unknown from causing ambiguous name because of geography
  -- TODO Remove in 2.0
  CREATE OR REPLACE FUNCTION ST_AsGML(text)
  RETURNS text AS
  $$ SELECT ST_AsGML($1::geometry); $$
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- ST_AsGML(version, geography) / precision=15 version=2 options=0
  CREATE OR REPLACE FUNCTION ST_AsGML(int4, geography)
  RETURNS text
  AS 'SELECT _ST_AsGML($1, $2, 15, 0)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- ST_AsGML(version, geography, precision) / options = 0
  CREATE OR REPLACE FUNCTION ST_AsGML(int4, geography, int4)
  RETURNS text
  AS 'SELECT _ST_AsGML($1, $2, $3, 0)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- ST_AsGML (geography, precision, option) / version=2
  CREATE OR REPLACE FUNCTION ST_AsGML(geography, int4, int4)
  RETURNS text
  AS 'SELECT _ST_AsGML(2, $1, $2, $3)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- ST_AsGML(version, geography, precision, option)
  CREATE OR REPLACE FUNCTION ST_AsGML(int4, geography, int4, int4)
  RETURNS text
  AS 'SELECT _ST_AsGML($1, $2, $3, $4)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
 
 
  --
  -- KML OUTPUT
  --
 
  -- _ST_AsKML(version, geography, precision)
  CREATE OR REPLACE FUNCTION _ST_AsKML(int4, geography, int4)
  RETURNS text
  AS '$libdir/postgis-1.5','geography_as_kml'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- AsKML(geography,precision) / version=2
  CREATE OR REPLACE FUNCTION ST_AsKML(geography, int4)
  RETURNS text
  AS 'SELECT _ST_AsKML(2, $1, $2)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- AsKML(geography) / precision=15 version=2
  CREATE OR REPLACE FUNCTION ST_AsKML(geography)
  RETURNS text
  AS 'SELECT _ST_AsKML(2, $1, 15)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0 - this is just a hack to prevent unknown from causing ambiguous name because of geography
  -- TODO Remove in 2.0
  CREATE OR REPLACE FUNCTION ST_AsKML(text)
  RETURNS text AS
  $$ SELECT ST_AsKML($1::geometry); $$
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- ST_AsKML(version, geography) / precision=15
  CREATE OR REPLACE FUNCTION ST_AsKML(int4, geography)
  RETURNS text
  AS 'SELECT _ST_AsKML($1, $2, 15)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- ST_AsKML(version, geography, precision)
  CREATE OR REPLACE FUNCTION ST_AsKML(int4, geography, int4)
  RETURNS text
  AS 'SELECT _ST_AsKML($1, $2, $3)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
 
 
  --
  -- GeoJson Output
  --
 
  CREATE OR REPLACE FUNCTION _ST_AsGeoJson(int4, geography, int4, int4)
  RETURNS text
  AS '$libdir/postgis-1.5','geography_as_geojson'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- ST_AsGeoJson(geography, precision) / version=1 options=0
  CREATE OR REPLACE FUNCTION ST_AsGeoJson(geography, int4)
  RETURNS text
  AS 'SELECT _ST_AsGeoJson(1, $1, $2, 0)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- ST_AsGeoJson(geography) / precision=15 version=1 options=0
  CREATE OR REPLACE FUNCTION ST_AsGeoJson(geography)
  RETURNS text
  AS 'SELECT _ST_AsGeoJson(1, $1, 15, 0)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0 - this is just a hack to prevent unknown from causing ambiguous name because of geography
  -- TODO Remove in 2.0
  CREATE OR REPLACE FUNCTION ST_AsGeoJson(text)
  RETURNS text AS
  $$ SELECT ST_AsGeoJson($1::geometry); $$
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- ST_AsGeoJson(version, geography) / precision=15 options=0
  CREATE OR REPLACE FUNCTION ST_AsGeoJson(int4, geography)
  RETURNS text
  AS 'SELECT _ST_AsGeoJson($1, $2, 15, 0)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- ST_AsGeoJson(version, geography, precision) / options=0
  CREATE OR REPLACE FUNCTION ST_AsGeoJson(int4, geography, int4)
  RETURNS text
  AS 'SELECT _ST_AsGeoJson($1, $2, $3, 0)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- ST_AsGeoJson(geography, precision, options) / version=1
  CREATE OR REPLACE FUNCTION ST_AsGeoJson(geography, int4, int4)
  RETURNS text
  AS 'SELECT _ST_AsGeoJson(1, $1, $2, $3)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- ST_AsGeoJson(version, geography, precision,options)
  CREATE OR REPLACE FUNCTION ST_AsGeoJson(int4, geography, int4, int4)
  RETURNS text
  AS 'SELECT _ST_AsGeoJson($1, $2, $3, $4)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- ---------- ---------- ---------- ---------- ---------- ---------- ----------
  -- Measurement Functions
  -- Availability: 1.5.0
  -- ---------- ---------- ---------- ---------- ---------- ---------- ----------
 
  -- Stop calculation and return immediately once distance is less than tolerance
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION _ST_Distance(geography, geography, float8, boolean)
  RETURNS float8
  AS '$libdir/postgis-1.5','geography_distance'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
  -- Stop calculation and return immediately once distance is less than tolerance
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION _ST_DWithin(geography, geography, float8, boolean)
  RETURNS boolean
  AS '$libdir/postgis-1.5','geography_dwithin'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION ST_Distance(geography, geography, boolean)
  RETURNS float8
  AS 'SELECT _ST_Distance($1, $2, 0.0, $3)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Currently defaulting to spheroid calculations
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION ST_Distance(geography, geography)
  RETURNS float8
  AS 'SELECT _ST_Distance($1, $2, 0.0, true)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0 - this is just a hack to prevent unknown from causing ambiguous name because of geography
  -- TODO Remove in 2.0
  CREATE OR REPLACE FUNCTION ST_Distance(text, text)
  RETURNS float8 AS
  $$ SELECT ST_Distance($1::geometry, $2::geometry); $$
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Only expands the bounding box, the actual geometry will remain unchanged, use with care.
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION _ST_Expand(geography, float8)
  RETURNS geography
  AS '$libdir/postgis-1.5','geography_expand'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION ST_DWithin(geography, geography, float8, boolean)
  RETURNS boolean
  AS 'SELECT $1 && _ST_Expand($2,$3) AND $2 && _ST_Expand($1,$3) AND _ST_DWithin($1, $2, $3, $4)'
  LANGUAGE 'SQL' IMMUTABLE;
 
  -- Currently defaulting to spheroid calculations
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION ST_DWithin(geography, geography, float8)
  RETURNS boolean
  AS 'SELECT $1 && _ST_Expand($2,$3) AND $2 && _ST_Expand($1,$3) AND _ST_DWithin($1, $2, $3, true)'
  LANGUAGE 'SQL' IMMUTABLE;
 
  -- Availability: 1.5.0 - this is just a hack to prevent unknown from causing ambiguous name because of geography
  -- TODO Remove in 2.0
  CREATE OR REPLACE FUNCTION ST_DWithin(text, text, float8)
  RETURNS boolean AS
  $$ SELECT ST_DWithin($1::geometry, $2::geometry, $3); $$
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION ST_Area(geography, boolean)
  RETURNS float8
  AS '$libdir/postgis-1.5','geography_area'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
  -- Currently defaulting to spheroid calculations
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION ST_Area(geography)
  RETURNS float8
  AS 'SELECT ST_Area($1, true)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0 - this is just a hack to prevent unknown from causing ambiguous name because of geography
  -- TODO Remove in 2.0
  CREATE OR REPLACE FUNCTION ST_Area(text)
  RETURNS float8 AS
  $$ SELECT ST_Area($1::geometry); $$
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION ST_Length(geography, boolean)
  RETURNS float8
  AS '$libdir/postgis-1.5','geography_length'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION ST_Length(geography)
  RETURNS float8
  AS 'SELECT ST_Length($1, true)'
  LANGUAGE 'SQL' IMMUTABLE;
 
  -- Availability: 1.5.0 - this is just a hack to prevent unknown from causing ambiguous name because of geography
  -- TODO Remove in 2.0
  CREATE OR REPLACE FUNCTION ST_Length(text)
  RETURNS float8 AS
  $$ SELECT ST_Length($1::geometry); $$
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION _ST_PointOutside(geography)
  RETURNS geography
  AS '$libdir/postgis-1.5','geography_point_outside'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Only implemented for polygon-over-point
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION _ST_Covers(geography, geography)
  RETURNS boolean
  AS '$libdir/postgis-1.5','geography_covers'
  LANGUAGE 'C' IMMUTABLE STRICT
  COST 100;
 
  -- Only implemented for polygon-over-point
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION ST_Covers(geography, geography)
  RETURNS boolean
  AS 'SELECT $1 && $2 AND _ST_Covers($1, $2)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0 - this is just a hack to prevent unknown from causing ambiguous name because of geography
  -- TODO Remove in 2.0
  CREATE OR REPLACE FUNCTION ST_Covers(text, text)
  RETURNS boolean AS
  $$ SELECT ST_Covers($1::geometry, $2::geometry); $$
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Only implemented for polygon-over-point
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION ST_CoveredBy(geography, geography)
  RETURNS boolean
  AS 'SELECT $1 && $2 AND _ST_Covers($2, $1)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0 - this is just a hack to prevent unknown from causing ambiguous name because of geography
  -- TODO Remove in 2.0
  CREATE OR REPLACE FUNCTION ST_CoveredBy(text, text)
  RETURNS boolean AS
  $$ SELECT ST_CoveredBy($1::geometry, $2::geometry); $$
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION ST_Intersects(geography, geography)
  RETURNS boolean
  AS 'SELECT $1 && $2 AND _ST_Distance($1, $2, 0.0, false) < 0.00001'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0 - this is just a hack to prevent unknown from causing ambiguous name because of geography
  -- TODO Remove in 2.0
  CREATE OR REPLACE FUNCTION ST_Intersects(text, text)
  RETURNS boolean AS
  $$ SELECT ST_Intersects($1::geometry, $2::geometry); $$
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION _ST_BestSRID(geography, geography)
  RETURNS integer
  AS '$libdir/postgis-1.5','geography_bestsrid'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION _ST_BestSRID(geography)
  RETURNS integer
  AS 'SELECT _ST_BestSRID($1,$1)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION ST_Buffer(geography, float8)
  RETURNS geography
  AS 'SELECT geography(ST_Transform(ST_Buffer(ST_Transform(geometry($1), _ST_BestSRID($1)), $2), 4326))'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0 - this is just a hack to prevent unknown from causing ambiguous name because of geography
  -- TODO Remove in 2.0
  CREATE OR REPLACE FUNCTION ST_Buffer(text, float8)
  RETURNS geometry AS
  $$ SELECT ST_Buffer($1::geometry, $2); $$
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0
  CREATE OR REPLACE FUNCTION ST_Intersection(geography, geography)
  RETURNS geography
  AS 'SELECT geography(ST_Transform(ST_Intersection(ST_Transform(geometry($1), _ST_BestSRID($1, $2)), ST_Transform(geometry($2), _ST_BestSRID($1, $2))), 4326))'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- Availability: 1.5.0 - this is just a hack to prevent unknown from causing ambiguous name because of geography
  -- TODO Remove in 2.0
  CREATE OR REPLACE FUNCTION ST_Intersection(text, text)
  RETURNS geometry AS
  $$ SELECT ST_Intersection($1::geometry, $2::geometry); $$
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  -- ---------- ---------- ---------- ---------- ---------- ---------- ----------
 
  ---------------------------------------------------------------
  -- SQL-MM
  ---------------------------------------------------------------
 
  --
  -- SQL-MM
  --
  -- ST_CurveToLine(Geometry geometry, SegmentsPerQuarter integer)
  --
  -- Converts a given geometry to a linear geometry. Each curveed
  -- geometry or segment is converted into a linear approximation using
  -- the given number of segments per quarter circle.
  CREATE OR REPLACE FUNCTION ST_CurveToLine(geometry, integer)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_curve_segmentize'
  LANGUAGE 'C' IMMUTABLE STRICT;
  --
  -- SQL-MM
  --
  -- ST_CurveToLine(Geometry geometry, SegmentsPerQuarter integer)
  --
  -- Converts a given geometry to a linear geometry. Each curveed
  -- geometry or segment is converted into a linear approximation using
  -- the default value of 32 segments per quarter circle
  CREATE OR REPLACE FUNCTION ST_CurveToLine(geometry)
  RETURNS geometry AS 'SELECT ST_CurveToLine($1, 32)'
  LANGUAGE 'SQL' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION ST_HasArc(geometry)
  RETURNS boolean
  AS '$libdir/postgis-1.5', 'LWGEOM_has_arc'
  LANGUAGE 'C' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION ST_LineToCurve(geometry)
  RETURNS geometry
  AS '$libdir/postgis-1.5', 'LWGEOM_line_desegmentize'
  LANGUAGE 'C' IMMUTABLE STRICT;
  ---------------------------------------------------------------
  -- END
  ---------------------------------------------------------------
 
 
  ---------------------------------------------------------------
  -- USER CONTRIUBUTED
  ---------------------------------------------------------------
 
  -----------------------------------------------------------------------
  -- ST_MinimumBoundingCircle(inputgeom geometry, segs_per_quarter integer)
  -----------------------------------------------------------------------
  -- Returns the smallest circle polygon that can fully contain a geometry
  -- Defaults to 48 segs per quarter to approximate a circle
  -- Contributed by Bruce Rindahl
  -- Availability: 1.4.0
  -----------------------------------------------------------------------
  CREATE OR REPLACE FUNCTION ST_MinimumBoundingCircle(inputgeom geometry, segs_per_quarter integer)
  RETURNS geometry AS
  $BODY$
  DECLARE
  hull GEOMETRY;
  ring GEOMETRY;
  center GEOMETRY;
  radius DOUBLE PRECISION;
  dist DOUBLE PRECISION;
  d DOUBLE PRECISION;
  idx1 integer;
  idx2 integer;
  l1 GEOMETRY;
  l2 GEOMETRY;
  p1 GEOMETRY;
  p2 GEOMETRY;
  a1 DOUBLE PRECISION;
  a2 DOUBLE PRECISION;
 
 
  BEGIN
 
  -- First compute the ConvexHull of the geometry
  hull = ST_ConvexHull(inputgeom);
  --A point really has no MBC
  IF ST_GeometryType(hull) = 'ST_Point' THEN
  RETURN hull;
  END IF;
  -- convert the hull perimeter to a linestring so we can manipulate individual points
  --If its already a linestring force it to a closed linestring
  ring = CASE WHEN ST_GeometryType(hull) = 'ST_LineString' THEN ST_AddPoint(hull, ST_StartPoint(hull)) ELSE ST_ExteriorRing(hull) END;
 
  dist = 0;
  -- Brute Force - check every pair
  FOR i in 1 .. (ST_NumPoints(ring)-2)
  LOOP
  FOR j in i .. (ST_NumPoints(ring)-1)
  LOOP
  d = ST_Distance(ST_PointN(ring,i),ST_PointN(ring,j));
  -- Check the distance and update if larger
  IF (d > dist) THEN
  dist = d;
  idx1 = i;
  idx2 = j;
  END IF;
  END LOOP;
  END LOOP;
 
  -- We now have the diameter of the convex hull. The following line returns it if desired.
  -- RETURN MakeLine(PointN(ring,idx1),PointN(ring,idx2));
 
  -- Now for the Minimum Bounding Circle. Since we know the two points furthest from each
  -- other, the MBC must go through those two points. Start with those points as a diameter of a circle.
 
  -- The radius is half the distance between them and the center is midway between them
  radius = ST_Distance(ST_PointN(ring,idx1),ST_PointN(ring,idx2)) / 2.0;
  center = ST_Line_interpolate_point(ST_MakeLine(ST_PointN(ring,idx1),ST_PointN(ring,idx2)),0.5);
 
  -- Loop through each vertex and check if the distance from the center to the point
  -- is greater than the current radius.
  FOR k in 1 .. (ST_NumPoints(ring)-1)
  LOOP
  IF(k <> idx1 and k <> idx2) THEN
  dist = ST_Distance(center,ST_PointN(ring,k));
  IF (dist > radius) THEN
  -- We have to expand the circle. The new circle must pass trhough
  -- three points - the two original diameters and this point.
 
  -- Draw a line from the first diameter to this point
  l1 = ST_Makeline(ST_PointN(ring,idx1),ST_PointN(ring,k));
  -- Compute the midpoint
  p1 = ST_line_interpolate_point(l1,0.5);
  -- Rotate the line 90 degrees around the midpoint (perpendicular bisector)
  l1 = ST_Translate(ST_Rotate(ST_Translate(l1,-X(p1),-Y(p1)),pi()/2),X(p1),Y(p1));
  -- Compute the azimuth of the bisector
  a1 = ST_Azimuth(ST_PointN(l1,1),ST_PointN(l1,2));
  -- Extend the line in each direction the new computed distance to insure they will intersect
  l1 = ST_AddPoint(l1,ST_Makepoint(X(ST_PointN(l1,2))+sin(a1)*dist,Y(ST_PointN(l1,2))+cos(a1)*dist),-1);
  l1 = ST_AddPoint(l1,ST_Makepoint(X(ST_PointN(l1,1))-sin(a1)*dist,Y(ST_PointN(l1,1))-cos(a1)*dist),0);
 
  -- Repeat for the line from the point to the other diameter point
  l2 = ST_Makeline(ST_PointN(ring,idx2),ST_PointN(ring,k));
  p2 = ST_Line_interpolate_point(l2,0.5);
  l2 = ST_Translate(ST_Rotate(ST_Translate(l2,-X(p2),-Y(p2)),pi()/2),X(p2),Y(p2));
  a2 = ST_Azimuth(ST_PointN(l2,1),ST_PointN(l2,2));
  l2 = ST_AddPoint(l2,ST_Makepoint(X(ST_PointN(l2,2))+sin(a2)*dist,Y(ST_PointN(l2,2))+cos(a2)*dist),-1);
  l2 = ST_AddPoint(l2,ST_Makepoint(X(ST_PointN(l2,1))-sin(a2)*dist,Y(ST_PointN(l2,1))-cos(a2)*dist),0);
 
  -- The new center is the intersection of the two bisectors
  center = ST_Intersection(l1,l2);
  -- The new radius is the distance to any of the three points
  radius = ST_Distance(center,ST_PointN(ring,idx1));
  END IF;
  END IF;
  END LOOP;
  --DONE!! Return the MBC via the buffer command
  RETURN ST_Buffer(center,radius,segs_per_quarter);
 
  END;
  $BODY$
  LANGUAGE 'plpgsql' IMMUTABLE STRICT;
 
  CREATE OR REPLACE FUNCTION ST_MinimumBoundingCircle(geometry)
  RETURNS geometry AS
  'SELECT ST_MinimumBoundingCircle($1, 48)'
  LANGUAGE 'sql' IMMUTABLE STRICT;
  COMMIT;
 
 
  -- First drop old aggregates
  DROP AGGREGATE IF EXISTS geomunion(geometry);
  DROP AGGREGATE IF EXISTS st_geomunion(geometry);
  DROP AGGREGATE IF EXISTS accum_old(geometry);
  DROP AGGREGATE IF EXISTS st_accum_old(geometry);
 
  -- Then drop old functions
  DROP FUNCTION IF EXISTS box2d_overleft(box2d, box2d);
  DROP FUNCTION IF EXISTS box2d_overright(box2d, box2d);
  DROP FUNCTION IF EXISTS box2d_left(box2d, box2d);
  DROP FUNCTION IF EXISTS box2d_right(box2d, box2d);
  DROP FUNCTION IF EXISTS box2d_contain(box2d, box2d);
  DROP FUNCTION IF EXISTS box2d_contained(box2d, box2d);
  DROP FUNCTION IF EXISTS box2d_overlap(box2d, box2d);
  DROP FUNCTION IF EXISTS box2d_same(box2d, box2d);
  DROP FUNCTION IF EXISTS box2d_intersects(box2d, box2d);
  DROP FUNCTION IF EXISTS st_box2d_overleft(box2d, box2d);
  DROP FUNCTION IF EXISTS st_box2d_overright(box2d, box2d);
  DROP FUNCTION IF EXISTS st_box2d_left(box2d, box2d);
  DROP FUNCTION IF EXISTS st_box2d_right(box2d, box2d);
  DROP FUNCTION IF EXISTS st_box2d_contain(box2d, box2d);
  DROP FUNCTION IF EXISTS st_box2d_contained(box2d, box2d);
  DROP FUNCTION IF EXISTS st_box2d_overlap(box2d, box2d);
  DROP FUNCTION IF EXISTS st_box2d_same(box2d, box2d);
  DROP FUNCTION IF EXISTS st_box2d_intersects(box2d, box2d);
  DROP FUNCTION IF EXISTS st_addbbox(geometry);
  DROP FUNCTION IF EXISTS st_dropbbox(geometry);
  DROP FUNCTION IF EXISTS st_hasbbox(geometry);
  DROP FUNCTION IF EXISTS cache_bbox();
  DROP FUNCTION IF EXISTS st_cache_bbox();
  DROP FUNCTION IF EXISTS transform_geometry(geometry,text,text,int);
  DROP FUNCTION IF EXISTS collector(geometry, geometry);
  DROP FUNCTION IF EXISTS st_collector(geometry, geometry);
  DROP FUNCTION IF EXISTS geom_accum (geometry[],geometry);
  DROP FUNCTION IF EXISTS st_geom_accum (geometry[],geometry);
  DROP FUNCTION IF EXISTS collect_garray (geometry[]);
  DROP FUNCTION IF EXISTS st_collect_garray (geometry[]);
  DROP FUNCTION IF EXISTS geosnoop(geometry);
  DROP FUNCTION IF EXISTS jtsnoop(geometry);
  DROP FUNCTION IF EXISTS st_noop(geometry);
  DROP FUNCTION IF EXISTS st_max_distance(geometry, geometry);
 
 
 
  K 25
  svn:wc:ra_dav:version-url
  V 22
  /svn/!svn/ver/20/trunk
  END
  RollingCurlGroup.php
  K 25
  svn:wc:ra_dav:version-url
  V 43
  /svn/!svn/ver/20/trunk/RollingCurlGroup.php
  END
  example_groups.php
  K 25
  svn:wc:ra_dav:version-url
  V 41
  /svn/!svn/ver/20/trunk/example_groups.php
  END
  example.php
  K 25
  svn:wc:ra_dav:version-url
  V 34
  /svn/!svn/ver/20/trunk/example.php
  END
  RollingCurl.php
  K 25
  svn:wc:ra_dav:version-url
  V 38
  /svn/!svn/ver/20/trunk/RollingCurl.php
  END
  CHANGELOG.txt
  K 25
  svn:wc:ra_dav:version-url
  V 36
  /svn/!svn/ver/20/trunk/CHANGELOG.txt
  END
  README.txt
  K 25
  svn:wc:ra_dav:version-url
  V 33
  /svn/!svn/ver/20/trunk/README.txt
  END
 
  10
 
  dir
  20
  http://rolling-curl.googlecode.com/svn/trunk
  http://rolling-curl.googlecode.com/svn
 
 
 
  2010-09-12T20:39:22.711474Z
  20
  alexander.makarow
 
 
 
 
 
 
 
 
 
 
 
 
 
 
  74aa2acc-2e27-11de-b2a4-4f96ceaaac44
 
  RollingCurlGroup.php
  file
 
 
 
 
  2011-04-10T08:32:48.081650Z
  73c08d9e9e24b4adc89816624c7aca30
  2010-09-12T20:39:22.711474Z
  20
  alexander.makarow
  has-props
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
  5152
 
  example_groups.php
  file
 
 
 
 
  2011-04-10T08:32:48.082650Z
  907ed82a47d346c39acbd5578e1d0230
  2010-09-12T20:39:22.711474Z
  20
  alexander.makarow
  has-props
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
  1367
 
  example.php
  file
 
 
 
 
  2011-04-10T08:32:48.083650Z
  87aa845abfaffc09ed4eca024f2a8b8a
  2010-09-12T20:39:22.711474Z
  20
  alexander.makarow
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
  1860
 
  RollingCurl.php
  file
 
 
 
 
  2011-04-10T08:32:48.084650Z
  205391c449f3f3ee050004dadc374dc8
  2010-09-12T20:39:22.711474Z
  20
  alexander.makarow
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
  10444
 
  CHANGELOG.txt
  file
 
 
 
 
  2011-04-10T08:32:48.085650Z
  d0452f6f9530ed04580159121d0fd5f7
  2010-09-12T20:39:22.711474Z
  20
  alexander.makarow
  has-props
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
  662
 
  README.txt
  file
 
 
 
 
  2011-04-10T08:32:48.085650Z
  60dd357081431c0f2b82989cdbce8615
  2010-09-12T20:39:22.711474Z
  20
  alexander.makarow
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
  6355
 
 
  K 13
  svn:eol-style
  V 6
  native
  END
 
  K 13
  svn:eol-style
  V 6
  native
  END
 
  K 13
  svn:eol-style
  V 6
  native
  END
 
  Rolling Curl changelog
  ======================
 
  September 13, 2010
  ------------------
  - Bug #12, #14: Fixed default options overriding (LionsAd)
  - Bug #10: Added use of curl_multi_select to avoid burning CPU (LionsAd)
  - Enh #6, #9: Added $request as parameter to callback function (LionsAd)
  - Chg: Request renamed to RollingCurlRequest (LionsAd)
  - Added RollingCurlGroup class that allows processing groups of requests (LionsAd)
  - More cleanup at unsetting a class (LionsAd)
  - Timeout parameter for curl_multi_select is now configurable (LionsAd)
  - single_curl now returns true (LionsAd)
  - Readme corrections (Alexander Makarov)
  - Code cleanup (Alexander Makarov)
  Rolling Curl
  ============
 
  RollingCurl allows you to process multiple HTTP requests in parallel using CURL PHP library.
 
  Released under the Apache License 2.0.
 
  Authors
  -------
  - Was originally written by [Josh Fraser](joshfraser.com).
  - Currently maintained by [Alexander Makarov](http://rmcreative.ru/).
  - Received significant updates and patched from [LionsAd](http://github.com/LionsAd/rolling-curl).
 
  Overview
  --------
  RollingCurl is a more efficient implementation of curl_multi() curl_multi is a great way to process multiple HTTP requests in parallel in PHP.
  curl_multi is particularly handy when working with large data sets (like fetching thousands of RSS feeds at one time). Unfortunately there is
  very little documentation on the best way to implement curl_multi. As a result, most of the examples around the web are either inefficient or
  fail entirely when asked to handle more than a few hundred requests.
 
  The problem is that most implementations of curl_multi wait for each set of requests to complete before processing them. If there are too many requests
  to process at once, they usually get broken into groups that are then processed one at a time. The problem with this is that each group has to wait for
  the slowest request to download. In a group of 100 requests, all it takes is one slow one to delay the processing of 99 others. The larger the number of
  requests you are dealing with, the more noticeable this latency becomes.
 
  The solution is to process each request as soon as it completes. This eliminates the wasted CPU cycles from busy waiting. Also there is a queue of
  cURL requests to allow for maximum throughput. Each time a request is completed, a new one is added from the queue. By dynamically adding and removing
  links, we keep a constant number of links downloading at all times. This gives us a way to throttle the amount of simultaneous requests we are sending.
  The result is a faster and more efficient way of processing large quantities of cURL requests in parallel.
 
  Callbacks
  ---------
 
  Each of requests usually do have a callback to process results that is being executed when request is done
  (both successfully or not).
 
  Callback accepts three parameters and can look like the following one:
  ~~~
  [php]
  function request_callback($response, $info, $request){
  // doing something with the data received
  }
  ~~~
 
  - $response contains received page body.
  - $info is an associative array that holds various information about response such as HTTP response code, content type,
  time taken to make request etc.
  - $request contains RollingCurlRequest that was used to make request.
 
  Examples
  --------
  ### Hello world
 
  ~~~
  [php]
  // an array of URL's to fetch
  $urls = array("http://www.google.com",
  "http://www.facebook.com",
  "http://www.yahoo.com");
 
  // a function that will process the returned responses
  function request_callback($response, $info, $request) {
  // parse the page title out of the returned HTML
  if (preg_match("~<title>(.*?)</title>~i", $response, $out)) {
  $title = $out[1];
  }
  echo "<b>$title</b><br />";
  print_r($info);
  echo "<hr>";
  }
 
  // create a new RollingCurl object and pass it the name of your custom callback function
  $rc = new RollingCurl("request_callback");
  // the window size determines how many simultaneous requests to allow.
  $rc->window_size = 20;
  foreach ($urls as $url) {
  // add each request to the RollingCurl object
  $request = new RollingCurlRequest($url);
  $rc->add($request);
  }
  $rc->execute();
  ~~~
 
 
  ### Setting custom options
 
  Set custom options for EVERY request:
 
  ~~~
  [php]
  $rc = new RollingCurl("request_callback");
  $rc->options = array(CURLOPT_HEADER => true, CURLOPT_NOBODY => true);
  $rc->execute();
  ~~~
 
  Set custom options for A SINGLE request:
 
  ~~~
  [php]
  $rc = new RollingCurl("request_callback");
  $request = new RollingCurlRequest($url);
  $request->options = array(CURLOPT_HEADER => true, CURLOPT_NOBODY => true);
  $rc->add($request);
  $rc->execute();
  ~~~
 
  ### Shortcuts
 
  ~~~
  [php]
  $rc = new RollingCurl("request_callback");
  $rc->get("http://www.google.com");
  $rc->get("http://www.yahoo.com");
  $rc->execute();
  ~~~
 
  ### Class callbacks
 
  ~~~
  [php]
  class MyInfoCollector {
  private $rc;
 
  function __construct(){
  $this->rc = new RollingCurl(array($this, 'processPage'));
  }
 
  function processPage($response, $info, $request){
  //...
  }
 
  function run($urls){
  foreach ($urls as $url){
  $request = new RollingCurlRequest($url);
  $this->rc->add($request);
  }
  $this->rc->execute();
  }
  }
 
  $collector = new MyInfoCollector();
  $collector->run(array(
  'http://google.com/',
  'http://yahoo.com/'
  ));
  ~~~
 
  ### Using RollingCurlGroup
 
  ~~~
  [php]
  class TestCurlRequest extends RollingCurlGroupRequest {
  public $test_verbose = true;
 
  function process($output, $info) {
  echo "Processing " . $this->url . "\n";
  if ($this->test_verbose)
  print_r($info);
 
  parent::process($output, $info);
  }
  }
 
  class TestCurlGroup extends RollingCurlGroup {
  function process($output, $info, $request) {
  echo "Group CB: Progress " . $this->name . " (" . ($this->finished_requests + 1) . "/" . $this->num_requests . ")\n";
  parent::process($output, $info, $request);
  }
 
  function finished() {
  echo "Group CB: Finished" . $this->name . "\n";
  parent::finished();
  }
  }
 
  $group = new TestCurlGroup("High");
  $group->add(new TestCurlRequest("www.google.de"));
  $group->add(new TestCurlRequest("www.yahoo.de"));
  $group->add(new TestCurlRequest("www.newyorktimes.com"));
  $reqs[] = $group;
 
  $group = new TestCurlGroup("Normal");
  $group->add(new TestCurlRequest("twitter.com"));
  $group->add(new TestCurlRequest("www.bing.com"));
  $group->add(new TestCurlRequest("m.facebook.com"));
  $reqs[] = $group;
 
  $reqs[] = new TestCurlRequest("www.kernel.org");
 
  // No callback here, as its done in Request class
  $rc = new GroupRollingCurl();
 
  foreach ($reqs as $req)
  $rc->add($req);
 
  $rc->execute();
  ~~~
 
  The same function (add) can be used both for adding requests and groups of requests.
  The "callback" in request and groups is:
 
  process($output, $info)
 
  and
 
  process($output, $info, $request)
 
  Also you can override RollingCurlGroup::finished() that will be executed right after finishing group processing.
 
  $Id$
  <?php
  /*
  Authored by Josh Fraser (www.joshfraser.com)
  Released under Apache License 2.0
 
  Maintained by Alexander Makarov, http://rmcreative.ru/
 
  $Id$
  */
 
  /**
  * Class that represent a single curl request
  */
  class RollingCurlRequest {
  public $url = false;
  public $method = 'GET';
  public $post_data = null;
  public $headers = null;
  public $options = null;
 
  /**
  * @param string $url
  * @param string $method
  * @param $post_data
  * @param $headers
  * @param $options
  * @return void
  */
  function __construct($url, $method = "GET", $post_data = null, $headers = null, $options = null) {
  $this->url = $url;
  $this->method = $method;
  $this->post_data = $post_data;
  $this->headers = $headers;
  $this->options = $options;
  }
 
  /**
  * @return void
  */
  public function __destruct() {
  unset($this->url, $this->method, $this->post_data, $this->headers, $this->options);
  }
  }
 
  /**
  * RollingCurl custom exception
  */
  class RollingCurlException extends Exception {
  }
 
  /**
  * Class that holds a rolling queue of curl requests.
  *
  * @throws RollingCurlException
  */
  class RollingCurl {
  /**
  * @var int
  *
  * Window size is the max number of simultaneous connections allowed.
  *
  * REMEMBER TO RESPECT THE SERVERS:
  * Sending too many requests at one time can easily be perceived
  * as a DOS attack. Increase this window_size if you are making requests
  * to multiple servers or have permission from the receving server admins.
  */
  private $window_size = 5;
 
  /**
  * @var float
  *
  * Timeout is the timeout used for curl_multi_select.
  */
  private $timeout = 10;
 
  /**
  * @var string|array
  *
  * Callback function to be applied to each result.
  */
  private $callback;
 
  /**
  * @var array
  *
  * Set your base options that you want to be used with EVERY request.
  */
  protected $options = array(
  CURLOPT_SSL_VERIFYPEER => 0,
  CURLOPT_RETURNTRANSFER => 1,
  CURLOPT_CONNECTTIMEOUT => 30,
  CURLOPT_TIMEOUT => 30
  );
 
  /**
  * @var array
  */
  private $headers = array();
 
  /**
  * @var Request[]
  *
  * The request queue
  */
  private $requests = array();
 
  /**
  * @var RequestMap[]
  *
  * Maps handles to request indexes
  */
  private $requestMap = array();
 
  /**
  * @param $callback
  * Callback function to be applied to each result.
  *
  * Can be specified as 'my_callback_function'
  * or array($object, 'my_callback_method').
  *
  * Function should take three parameters: $response, $info, $request.
  * $response is response body, $info is additional curl info.
  * $request is the original request
  *
  * @return void
  */
  function __construct($callback = null) {
  $this->callback = $callback;
  }
 
  /**
  * @param string $name
  * @return mixed
  */
  public function __get($name) {
  return (isset($this->{$name})) ? $this->{$name} : null;
  }
 
  /**
  * @param string $name
  * @param mixed $value
  * @return bool
  */
  public function __set($name, $value) {
  // append the base options & headers
  if ($name == "options" || $name == "headers") {
  $this->{$name} = $value + $this->{$name};
  } else {
  $this->{$name} = $value;
  }
  return true;
  }
 
  /**
  * Add a request to the request queue
  *
  * @param Request $request
  * @return bool
  */
  public function add($request) {
  $this->requests[] = $request;
  return true;
  }
 
  /**
  * Create new Request and add it to the request queue
  *
  * @param string $url
  * @param string $method
  * @param $post_data
  * @param $headers
  * @param $options
  * @return bool
  */
  public function request($url, $method = "GET", $post_data = null, $headers = null, $options = null) {
  $this->requests[] = new RollingCurlRequest($url, $method, $post_data, $headers, $options);
  return true;
  }
 
  /**
  * Perform GET request
  *
  * @param string $url
  * @param $headers
  * @param $options
  * @return bool
  */
  public function get($url, $headers = null, $options = null) {
  return $this->request($url, "GET", null, $headers, $options);
  }
 
  /**
  * Perform POST request
  *
  * @param string $url
  * @param $post_data
  * @param $headers
  * @param $options
  * @return bool
  */
  public function post($url, $post_data = null, $headers = null, $options = null) {
  return $this->request($url, "POST", $post_data, $headers, $options);
  }
 
  /**
  * Execute processing
  *
  * @param int $window_size Max number of simultaneous connections
  * @return string|bool
  */
  public function execute($window_size = null) {
  // rolling curl window must always be greater than 1
  if (sizeof($this->requests) == 1) {
  return $this->single_curl();
  } else {
  // start the rolling curl. window_size is the max number of simultaneous connections
  return $this->rolling_curl($window_size);
  }
  }
 
  /**
  * Performs a single curl request
  *
  * @access private
  * @return string
  */
  private function single_curl() {
  $ch = curl_init();
  $request = array_shift($this->requests);
  $options = $this->get_options($request);
  curl_setopt_array($ch, $options);
  $output = curl_exec($ch);
  $info = curl_getinfo($ch);
 
  // it's not neccesary to set a callback for one-off requests
  if ($this->callback) {
  $callback = $this->callback;
  if (is_callable($this->callback)) {
  call_user_func($callback, $output, $info, $request);
  }
  }
  else
  return $output;
  return true;
  }
 
  /**
  * Performs multiple curl requests
  *
  * @access private
  * @throws RollingCurlException
  * @param int $window_size Max number of simultaneous connections
  * @return bool
  */
  private function rolling_curl($window_size = null) {
  if ($window_size)
  $this->window_size = $window_size;
 
  // make sure the rolling window isn't greater than the # of urls
  if (sizeof($this->requests) < $this->window_size)
  $this->window_size = sizeof($this->requests);
 
  if ($this->window_size < 2) {
  throw new RollingCurlException("Window size must be greater than 1");
  }
 
  $master = curl_multi_init();
 
  // start the first batch of requests
  for ($i = 0; $i < $this->window_size; $i++) {
  $ch = curl_init();
 
  $options = $this->get_options($this->requests[$i]);
 
  curl_setopt_array($ch, $options);
  curl_multi_add_handle($master, $ch);
 
  // Add to our request Maps
  $key = (string) $ch;
  $this->requestMap[$key] = $i;
  }
 
  do {
  while (($execrun = curl_multi_exec($master, $running)) == CURLM_CALL_MULTI_PERFORM) ;
  if ($execrun != CURLM_OK)
  break;
  // a request was just completed -- find out which one
  while ($done = curl_multi_info_read($master)) {
 
  // get the info and content returned on the request
  $info = curl_getinfo($done['handle']);
  $output = curl_multi_getcontent($done['handle']);
 
  // send the return values to the callback function.
  $callback = $this->callback;
  if (is_callable($callback)) {
  $key = (string) $done['handle'];
  $request = $this->requests[$this->requestMap[$key]];
  unset($this->requestMap[$key]);
  call_user_func($callback, $output, $info, $request);
  }
 
  // start a new request (it's important to do this before removing the old one)
  if ($i < sizeof($this->requests) && isset($this->requests[$i]) && $i < count($this->requests)) {
  $ch = curl_init();
  $options = $this->get_options($this->requests[$i]);
  curl_setopt_array($ch, $options);
  curl_multi_add_handle($master, $ch);
 
  // Add to our request Maps
  $key = (string) $ch;
  $this->requestMap[$key] = $i;
  $i++;
  }
 
  // remove the curl handle that just completed
  curl_multi_remove_handle($master, $done['handle']);
 
  }
 
  // Block for data in / output; error handling is done by curl_multi_exec
  if ($running)
  curl_multi_select($master, $this->timeout);
 
  } while ($running);
  curl_multi_close($master);
  return true;
  }
 
 
  /**
  * Helper function to set up a new request by setting the appropriate options
  *
  * @access private
  * @param Request $request
  * @return array
  */
  private function get_options($request) {
  // options for this entire curl object
  $options = $this->__get('options');
  if (ini_get('safe_mode') == 'Off' || !ini_get('safe_mode')) {
  $options[CURLOPT_FOLLOWLOCATION] = 1;
  $options[CURLOPT_MAXREDIRS] = 5;
  }
  $headers = $this->__get('headers');
 
  // append custom options for this specific request
  if ($request->options) {
  $options = $request->options + $options;
  }
 
  // set the request URL
  $options[CURLOPT_URL] = $request->url;
 
  // posting data w/ this request?
  if ($request->post_data) {
  $options[CURLOPT_POST] = 1;
  $options[CURLOPT_POSTFIELDS] = $request->post_data;
  }
  if ($headers) {
  $options[CURLOPT_HEADER] = 0;
  $options[CURLOPT_HTTPHEADER] = $headers;
  }
 
  return $options;
  }
 
  /**
  * @return void
  */
  public function __destruct() {
  unset($this->window_size, $this->callback, $this->options, $this->headers, $this->requests);
  }
  }
 
  <?php
  /*
 
  Authored by Fabian Franz (www.lionsad.de)
  Released under Apache License 2.0
 
  $Id$
  */
 
  class RollingCurlGroupException extends Exception {}
 
  /**
  * @throws RollingCurlGroupException
  */
  abstract class RollingCurlGroupRequest extends RollingCurlRequest {
  private $group = null;
 
  /**
  * Set group for this request
  *
  * @param group The group to be set
  */
  function setGroup($group) {
  if (!($group instanceof RollingCurlGroup))
  throw new RollingCurlGroupException("setGroup: group needs to be of instance RollingCurlGroup");
 
  $this->group = $group;
  }
 
  /**
  * Process the request
  *
  *
  */
  function process($output, $info) {
  if ($this->group)
  $this->group->process($output, $info, $this);
  }
 
  /**
  * @return void
  */
  public function __destruct() {
  unset($this->group);
  parent::__destruct();
  }
 
  }
 
  /**
  * A group of curl requests.
  *
  * @throws RollingCurlGroupException *
  */
  class RollingCurlGroup {
  /**
  * @var string group name
  */
  protected $name;
 
  /**
  * @var int total number of requests in a group
  */
  protected $num_requests = 0;
 
  /**
  * @var int total number of finished requests in a group
  */
  protected $finished_requests = 0;
 
  /**
  * @var array requests array
  */
  private $requests = array();
 
  /**
  * @param string $name group name
  * @return void
  */
  function __construct($name) {
  $this->name = $name;
  }
 
  /**
  * @return void
  */
  public function __destruct() {
  unset($this->name, $this->num_requests, $this->finished_requests, $this->requests);
  }
 
  /**
  * Adds request to a group
  *
  * @throws RollingCurlGroupException
  * @param RollingCurlGroupRequest|array $request
  * @return bool
  */
  function add($request) {
  if ($request instanceof RollingCurlGroupRequest) {
  $request->setGroup($this);
  $this->num_requests++;
  $this->requests[] = $request;
  }
  else if (is_array($request)) {
  foreach ($request as $req)
  $this->add($req);
  }
  else
  throw new RollingCurlGroupException("add: Request needs to be of instance RollingCurlGroupRequest");
 
  return true;
  }
 
  /**
  * @throws RollingCurlGroupException
  * @param RollingCurl $rc
  * @return bool
  */
  function addToRC(RollingCurl $rc){
  $ret = true;
 
  while (count($this->requests) > 0){
  $ret1 = $rc->add(array_shift($this->requests));
  if (!$ret1)
  $ret = false;
  }
 
  return $ret;
  }
 
  /**
  * Override to implement custom response processing.
  *
  * Don't forget to call parent::process().
  *
  * @param string $output received page body
  * @param array $info holds various information about response such as HTTP response code, content type, time taken to make request etc.
  * @param RollingCurlRequest $request request used
  * @return void
  */
  function process($output, $info, $request) {
  $this->finished_requests++;
 
  if ($this->finished_requests >= $this->num_requests)
  $this->finished();
  }
 
  /**
  * Override to execute code after all requests in a group are processed.
  *
  * @return void
  */
  function finished() {
  }
 
  }
 
  /**
  * Group version of rolling curl
  */
  class GroupRollingCurl extends RollingCurl {
 
  /**
  * @var mixed common callback for all groups
  */
  private $group_callback = null;
 
  /**
  * @param string $output received page body
  * @param array $info holds various information about response such as HTTP response code, content type, time taken to make request etc.
  * @param RollingCurlRequest $request request used
  * @return void
  */
  protected function process($output, $info, $request) {
  if ($request instanceof RollingCurlGroupRequest)
  $request->process($output, $info);
 
  if (is_callable($this->group_callback))
  call_user_func($this->group_callback, $output, $info, $request);
  }
 
  /**
  * @param mixed $callback common callback for all groups
  * @return void
  */
  function __construct($callback = null) {
  $this->group_callback = $callback;
 
  parent::__construct(array(&$this, "process"));
  }
 
  /**
  * Adds a group to processing queue
  *
  * @param RollingCurlGroup|Request $request
  * @return bool
  */
  public function add($request) {
  if ($request instanceof RollingCurlGroup)
  return $request->addToRC($this);
  else
  return parent::add($request);
  }
 
  /**
  * Execute processing
  *
  * @param int $window_size Max number of simultaneous connections
  * @return bool|string
  */
  public function execute($window_size = null) {
  if (count($this->requests) == 0)
  return false;
 
  return parent::execute($window_size);
  }
  }
 
  <?php
  /*
  authored by Josh Fraser (www.joshfraser.com)
  released under Apache License 2.0
 
  Maintained by Alexander Makarov, http://rmcreative.ru/
 
  $Id$
  */
 
  require("RollingCurl.php");
 
  // a little example that fetches a bunch of sites in parallel and echos the page title and response info for each request
  function request_callback($response, $info, $request) {
  // parse the page title out of the returned HTML
  if (preg_match("~<title>(.*?)</title>~i", $response, $out)) {
  $title = $out[1];
  }
  echo "<b>$title</b><br />";
  print_r($info);
  print_r($request);
  echo "<hr>";
  }
 
  // single curl request
  $rc = new RollingCurl("request_callback");
  $rc->request("http://www.msn.com");
  $rc->execute();
 
  // another single curl request
  $rc = new RollingCurl("request_callback");
  $rc->request("http://www.google.com");
  $rc->execute();
 
  echo "<hr>";
 
  // top 20 sites according to alexa (11/5/09)
  $urls = array("http://www.google.com",
  "http://www.facebook.com",
  "http://www.yahoo.com",
  "http://www.youtube.com",
  "http://www.live.com",
  "http://www.wikipedia.com",
  "http://www.blogger.com",
  "http://www.msn.com",
  "http://www.baidu.com",
  "http://www.yahoo.co.jp",
  "http://www.myspace.com",
  "http://www.qq.com",
  "http://www.google.co.in",
  "http://www.twitter.com",
  "http://www.google.de",
  "http://www.microsoft.com",
  "http://www.google.cn",
  "http://www.sina.com.cn",
  "http://www.wordpress.com",
  "http://www.google.co.uk");
 
  $rc = new RollingCurl("request_callback");
  $rc->window_size = 20;
  foreach ($urls as $url) {
  $request = new RollingCurlRequest($url);
  $rc->add($request);
  }
  $rc->execute();
 
  <?php
  require 'RollingCurl.php';
  require 'RollingCurlGroup.php';
 
  class TestCurlRequest extends RollingCurlGroupRequest {
  public $test_verbose = true;
 
  function process($output, $info) {
  echo "Processing " . $this->url . "\n";
  if ($this->test_verbose)
  print_r($info);
 
  parent::process($output, $info);
  }
  }
 
  class TestCurlGroup extends RollingCurlGroup {
  function process($output, $info, $request) {
  echo "Group CB: Progress " . $this->name . " (" . ($this->finished_requests + 1) . "/" . $this->num_requests . ")\n";
  parent::process($output, $info, $request);
  }
 
  function finished() {
  echo "Group CB: Finished" . $this->name . "\n";
  parent::finished();
  }
  }
 
  $group = new TestCurlGroup("High");
  $group->add(new TestCurlRequest("www.google.de"));
  $group->add(new TestCurlRequest("www.yahoo.de"));
  $group->add(new TestCurlRequest("www.newyorktimes.com"));
  $reqs[] = $group;
 
  $group = new TestCurlGroup("Normal");
  $group->add(new TestCurlRequest("twitter.com"));
  $group->add(new TestCurlRequest("www.bing.com"));
  $group->add(new TestCurlRequest("m.facebook.com"));
  $reqs[] = $group;
 
  $reqs[] = new TestCurlRequest("www.kernel.org");
 
  // No callback here, as its done in Request class
  $rc = new GroupRollingCurl();
 
  foreach ($reqs as $req)
  $rc->add($req);
 
  $rc->execute();
  Rolling Curl changelog
  ======================
 
  September 13, 2010
  ------------------
  - Bug #12, #14: Fixed default options overriding (LionsAd)
  - Bug #10: Added use of curl_multi_select to avoid burning CPU (LionsAd)
  - Enh #6, #9: Added $request as parameter to callback function (LionsAd)
  - Chg: Request renamed to RollingCurlRequest (LionsAd)
  - Added RollingCurlGroup class that allows processing groups of requests (LionsAd)
  - More cleanup at unsetting a class (LionsAd)
  - Timeout parameter for curl_multi_select is now configurable (LionsAd)
  - single_curl now returns true (LionsAd)
  - Readme corrections (Alexander Makarov)
  - Code cleanup (Alexander Makarov)
  Rolling Curl
  ============
 
  RollingCurl allows you to process multiple HTTP requests in parallel using CURL PHP library.
 
  Released under the Apache License 2.0.
 
  Authors
  -------
  - Was originally written by [Josh Fraser](joshfraser.com).
  - Currently maintained by [Alexander Makarov](http://rmcreative.ru/).
  - Received significant updates and patched from [LionsAd](http://github.com/LionsAd/rolling-curl).
 
  Overview
  --------
  RollingCurl is a more efficient implementation of curl_multi() curl_multi is a great way to process multiple HTTP requests in parallel in PHP.
  curl_multi is particularly handy when working with large data sets (like fetching thousands of RSS feeds at one time). Unfortunately there is
  very little documentation on the best way to implement curl_multi. As a result, most of the examples around the web are either inefficient or
  fail entirely when asked to handle more than a few hundred requests.
 
  The problem is that most implementations of curl_multi wait for each set of requests to complete before processing them. If there are too many requests
  to process at once, they usually get broken into groups that are then processed one at a time. The problem with this is that each group has to wait for
  the slowest request to download. In a group of 100 requests, all it takes is one slow one to delay the processing of 99 others. The larger the number of
  requests you are dealing with, the more noticeable this latency becomes.
 
  The solution is to process each request as soon as it completes. This eliminates the wasted CPU cycles from busy waiting. Also there is a queue of
  cURL requests to allow for maximum throughput. Each time a request is completed, a new one is added from the queue. By dynamically adding and removing
  links, we keep a constant number of links downloading at all times. This gives us a way to throttle the amount of simultaneous requests we are sending.
  The result is a faster and more efficient way of processing large quantities of cURL requests in parallel.
 
  Callbacks
  ---------
 
  Each of requests usually do have a callback to process results that is being executed when request is done
  (both successfully or not).
 
  Callback accepts three parameters and can look like the following one:
  ~~~
  [php]
  function request_callback($response, $info, $request){
  // doing something with the data received
  }
  ~~~
 
  - $response contains received page body.
  - $info is an associative array that holds various information about response such as HTTP response code, content type,
  time taken to make request etc.
  - $request contains RollingCurlRequest that was used to make request.
 
  Examples
  --------
  ### Hello world
 
  ~~~
  [php]
  // an array of URL's to fetch
  $urls = array("http://www.google.com",
  "http://www.facebook.com",
  "http://www.yahoo.com");
 
  // a function that will process the returned responses
  function request_callback($response, $info, $request) {
  // parse the page title out of the returned HTML
  if (preg_match("~<title>(.*?)</title>~i", $response, $out)) {
  $title = $out[1];
  }
  echo "<b>$title</b><br />";
  print_r($info);
  echo "<hr>";
  }
 
  // create a new RollingCurl object and pass it the name of your custom callback function
  $rc = new RollingCurl("request_callback");
  // the window size determines how many simultaneous requests to allow.
  $rc->window_size = 20;
  foreach ($urls as $url) {
  // add each request to the RollingCurl object
  $request = new RollingCurlRequest($url);
  $rc->add($request);
  }
  $rc->execute();
  ~~~
 
 
  ### Setting custom options
 
  Set custom options for EVERY request:
 
  ~~~
  [php]
  $rc = new RollingCurl("request_callback");
  $rc->options = array(CURLOPT_HEADER => true, CURLOPT_NOBODY => true);
  $rc->execute();
  ~~~
 
  Set custom options for A SINGLE request:
 
  ~~~
  [php]
  $rc = new RollingCurl("request_callback");
  $request = new RollingCurlRequest($url);
  $request->options = array(CURLOPT_HEADER => true, CURLOPT_NOBODY => true);
  $rc->add($request);
  $rc->execute();
  ~~~
 
  ### Shortcuts
 
  ~~~
  [php]
  $rc = new RollingCurl("request_callback");
  $rc->get("http://www.google.com");
  $rc->get("http://www.yahoo.com");
  $rc->execute();
  ~~~
 
  ### Class callbacks
 
  ~~~
  [php]
  class MyInfoCollector {
  private $rc;
 
  function __construct(){
  $this->rc = new RollingCurl(array($this, 'processPage'));
  }
 
  function processPage($response, $info, $request){
  //...
  }
 
  function run($urls){
  foreach ($urls as $url){
  $request = new RollingCurlRequest($url);
  $this->rc->add($request);
  }
  $this->rc->execute();
  }
  }
 
  $collector = new MyInfoCollector();
  $collector->run(array(
  'http://google.com/',
  'http://yahoo.com/'
  ));
  ~~~
 
  ### Using RollingCurlGroup
 
  ~~~
  [php]
  class TestCurlRequest extends RollingCurlGroupRequest {
  public $test_verbose = true;
 
  function process($output, $info) {
  echo "Processing " . $this->url . "\n";
  if ($this->test_verbose)
  print_r($info);
 
  parent::process($output, $info);
  }
  }
 
  class TestCurlGroup extends RollingCurlGroup {
  function process($output, $info, $request) {
  echo "Group CB: Progress " . $this->name . " (" . ($this->finished_requests + 1) . "/" . $this->num_requests . ")\n";
  parent::process($output, $info, $request);
  }
 
  function finished() {
  echo "Group CB: Finished" . $this->name . "\n";
  parent::finished();
  }
  }
 
  $group = new TestCurlGroup("High");
  $group->add(new TestCurlRequest("www.google.de"));
  $group->add(new TestCurlRequest("www.yahoo.de"));
  $group->add(new TestCurlRequest("www.newyorktimes.com"));
  $reqs[] = $group;
 
  $group = new TestCurlGroup("Normal");
  $group->add(new TestCurlRequest("twitter.com"));
  $group->add(new TestCurlRequest("www.bing.com"));
  $group->add(new TestCurlRequest("m.facebook.com"));
  $reqs[] = $group;
 
  $reqs[] = new TestCurlRequest("www.kernel.org");
 
  // No callback here, as its done in Request class
  $rc = new GroupRollingCurl();
 
  foreach ($reqs as $req)
  $rc->add($req);
 
  $rc->execute();
  ~~~
 
  The same function (add) can be used both for adding requests and groups of requests.
  The "callback" in request and groups is:
 
  process($output, $info)
 
  and
 
  process($output, $info, $request)
 
  Also you can override RollingCurlGroup::finished() that will be executed right after finishing group processing.
 
  $Id$
  <?php
  /*
  Authored by Josh Fraser (www.joshfraser.com)
  Released under Apache License 2.0
 
  Maintained by Alexander Makarov, http://rmcreative.ru/
 
  $Id$
  */
 
  /**
  * Class that represent a single curl request
  */
  class RollingCurlRequest {
  public $url = false;
  public $method = 'GET';
  public $post_data = null;
  public $headers = null;
  public $options = null;
  public $metadata = Array();
 
  /**
  * @param string $url
  * @param string $method
  * @param $post_data
  * @param $headers
  * @param $options
  * @return void
  */
  function __construct($url, $method = "GET", $post_data = null, $headers = null, $options = null) {
  $this->url = $url;
  $this->method = $method;
  $this->post_data = $post_data;
  $this->headers = $headers;
  $this->options = $options;
  }
 
  /**
  * @return void
  */
  public function __destruct() {
  unset($this->url, $this->method, $this->post_data, $this->headers, $this->options);
  }
  }
 
  /**
  * RollingCurl custom exception
  */
  class RollingCurlException extends Exception {
  }
 
  /**
  * Class that holds a rolling queue of curl requests.
  *
  * @throws RollingCurlException
  */
  class RollingCurl {
  /**
  * @var int
  *
  * Window size is the max number of simultaneous connections allowed.
  *
  * REMEMBER TO RESPECT THE SERVERS:
  * Sending too many requests at one time can easily be perceived
  * as a DOS attack. Increase this window_size if you are making requests
  * to multiple servers or have permission from the receving server admins.
  */
  private $window_size = 5;
 
  /**
  * @var float
  *
  * Timeout is the timeout used for curl_multi_select.
  */
  private $timeout = 10;
 
  /**
  * @var string|array
  *
  * Callback function to be applied to each result.
  */
  private $callback;
 
  /**
  * @var array
  *
  * Set your base options that you want to be used with EVERY request.
  */
  protected $options = array(
  CURLOPT_SSL_VERIFYPEER => 0,
  CURLOPT_RETURNTRANSFER => 1,
  CURLOPT_CONNECTTIMEOUT => 60,
  CURLOPT_TIMEOUT => 60
  );
 
  /**
  * @var array
  */
  private $headers = array();
 
  /**
  * @var Request[]
  *
  * The request queue
  */
  private $requests = array();
 
  /**
  * @var RequestMap[]
  *
  * Maps handles to request indexes
  */
  private $requestMap = array();
 
  /**
  * @param $callback
  * Callback function to be applied to each result.
  *
  * Can be specified as 'my_callback_function'
  * or array($object, 'my_callback_method').
  *
  * Function should take three parameters: $response, $info, $request.
  * $response is response body, $info is additional curl info.
  * $request is the original request
  *
  * @return void
  */
  function __construct($callback = null) {
  $this->callback = $callback;
  }
 
  /**
  * @param string $name
  * @return mixed
  */
  public function __get($name) {
  return (isset($this->{$name})) ? $this->{$name} : null;
  }
 
  /**
  * @param string $name
  * @param mixed $value
  * @return bool
  */
  public function __set($name, $value) {
  // append the base options & headers
  if ($name == "options" || $name == "headers") {
  $this->{$name} = $value + $this->{$name};
  } else {
  $this->{$name} = $value;
  }
  return true;
  }
 
  /**
  * Add a request to the request queue
  *
  * @param Request $request
  * @return bool
  */
  public function add($request) {
  $this->requests[] = $request;
  return true;
  }
 
  /**
  * Create new Request and add it to the request queue
  *
  * @param string $url
  * @param string $method
  * @param $post_data
  * @param $headers
  * @param $options
  * @return bool
  */
  public function request($url, $method = "GET", $post_data = null, $headers = null, $options = null) {
  $this->requests[] = new RollingCurlRequest($url, $method, $post_data, $headers, $options);
  return true;
  }
 
  /**
  * Perform GET request
  *
  * @param string $url
  * @param $headers
  * @param $options
  * @return bool
  */
  public function get($url, $headers = null, $options = null) {
  return $this->request($url, "GET", null, $headers, $options);
  }
 
  /**
  * Perform POST request
  *
  * @param string $url
  * @param $post_data
  * @param $headers
  * @param $options
  * @return bool
  */
  public function post($url, $post_data = null, $headers = null, $options = null) {
  return $this->request($url, "POST", $post_data, $headers, $options);
  }
 
  /**
  * Execute processing
  *
  * @param int $window_size Max number of simultaneous connections
  * @return string|bool
  */
  public function execute($window_size = null) {
  // rolling curl window must always be greater than 1
  if (sizeof($this->requests) == 1) {
  return $this->single_curl();
  } else {
  // start the rolling curl. window_size is the max number of simultaneous connections
  return $this->rolling_curl($window_size);
  }
  }
 
  /**
  * Performs a single curl request
  *
  * @access private
  * @return string
  */
  private function single_curl() {
  $ch = curl_init();
  $request = array_shift($this->requests);
  $options = $this->get_options($request);
  curl_setopt_array($ch, $options);
  $output = curl_exec($ch);
  $info = curl_getinfo($ch);
 
  // it's not neccesary to set a callback for one-off requests
  if ($this->callback) {
  $callback = $this->callback;
  if (is_callable($this->callback)) {
  call_user_func($callback, $output, $info, $request);
  }
  }
  else
  return $output;
  return true;
  }
 
  /**
  * Performs multiple curl requests
  *
  * @access private
  * @throws RollingCurlException
  * @param int $window_size Max number of simultaneous connections
  * @return bool
  */
  private function rolling_curl($window_size = null) {
  if ($window_size)
  $this->window_size = $window_size;
 
  // make sure the rolling window isn't greater than the # of urls
  if (sizeof($this->requests) < $this->window_size)
  $this->window_size = sizeof($this->requests);
 
  if ($this->window_size < 2) {
  throw new RollingCurlException("Window size must be greater than 1");
  }
 
  $master = curl_multi_init();
 
  // start the first batch of requests
  for ($i = 0; $i < $this->window_size; $i++) {
  $ch = curl_init();
 
  $options = $this->get_options($this->requests[$i]);
 
  curl_setopt_array($ch, $options);
  curl_multi_add_handle($master, $ch);
 
  // Add to our request Maps
  $key = (string) $ch;
  $this->requestMap[$key] = $i;
  }
 
  do {
  while (($execrun = curl_multi_exec($master, $running)) == CURLM_CALL_MULTI_PERFORM) ;
  if ($execrun != CURLM_OK)
  break;
  // a request was just completed -- find out which one
  while ($done = curl_multi_info_read($master)) {
 
  // get the info and content returned on the request
  $info = curl_getinfo($done['handle']);
  $output = curl_multi_getcontent($done['handle']);
 
  // send the return values to the callback function.
  $callback = $this->callback;
  if (is_callable($callback)) {
  $key = (string) $done['handle'];
  $request = $this->requests[$this->requestMap[$key]];
  unset($this->requestMap[$key]);
  call_user_func($callback, $output, $info, $request);
  }
 
  // start a new request (it's important to do this before removing the old one)
  if ($i < sizeof($this->requests) && isset($this->requests[$i]) && $i < count($this->requests)) {
  $ch = curl_init();
  $options = $this->get_options($this->requests[$i]);
  curl_setopt_array($ch, $options);
  curl_multi_add_handle($master, $ch);
 
  // Add to our request Maps
  $key = (string) $ch;
  $this->requestMap[$key] = $i;
  $i++;
  }
 
  // remove the curl handle that just completed
  curl_multi_remove_handle($master, $done['handle']);
 
  }
 
  // Block for data in / output; error handling is done by curl_multi_exec
  if ($running)
  curl_multi_select($master, $this->timeout);
 
  } while ($running);
  curl_multi_close($master);
  return true;
  }
 
 
  /**
  * Helper function to set up a new request by setting the appropriate options
  *
  * @access private
  * @param Request $request
  * @return array
  */
  private function get_options($request) {
  // options for this entire curl object
  $options = $this->__get('options');
  if (ini_get('safe_mode') == 'Off' || !ini_get('safe_mode')) {
  $options[CURLOPT_FOLLOWLOCATION] = 1;
  $options[CURLOPT_MAXREDIRS] = 5;
  }
  $headers = $this->__get('headers');
 
  // append custom options for this specific request
  if ($request->options) {
  $options = $request->options + $options;
  }
 
  // set the request URL
  $options[CURLOPT_URL] = $request->url;
 
  // posting data w/ this request?
  if ($request->post_data) {
  $options[CURLOPT_POST] = 1;
  $options[CURLOPT_POSTFIELDS] = $request->post_data;
  }
  if ($headers) {
  $options[CURLOPT_HEADER] = 0;
  $options[CURLOPT_HTTPHEADER] = $headers;
  }
 
  return $options;
  }
 
  /**
  * @return void
  */
  public function __destruct() {
  unset($this->window_size, $this->callback, $this->options, $this->headers, $this->requests);
  }
  }
 
  <?php
  /*
 
  Authored by Fabian Franz (www.lionsad.de)
  Released under Apache License 2.0
 
  $Id$
  */
 
  class RollingCurlGroupException extends Exception {}
 
  /**
  * @throws RollingCurlGroupException
  */
  abstract class RollingCurlGroupRequest extends RollingCurlRequest {
  private $group = null;
 
  /**
  * Set group for this request
  *
  * @param group The group to be set
  */
  function setGroup($group) {
  if (!($group instanceof RollingCurlGroup))
  throw new RollingCurlGroupException("setGroup: group needs to be of instance RollingCurlGroup");
 
  $this->group = $group;
  }
 
  /**
  * Process the request
  *
  *
  */
  function process($output, $info) {
  if ($this->group)
  $this->group->process($output, $info, $this);
  }
 
  /**
  * @return void
  */
  public function __destruct() {
  unset($this->group);
  parent::__destruct();
  }
 
  }
 
  /**
  * A group of curl requests.
  *
  * @throws RollingCurlGroupException *
  */
  class RollingCurlGroup {
  /**
  * @var string group name
  */
  protected $name;
 
  /**
  * @var int total number of requests in a group
  */
  protected $num_requests = 0;
 
  /**
  * @var int total number of finished requests in a group
  */
  protected $finished_requests = 0;
 
  /**
  * @var array requests array
  */
  private $requests = array();
 
  /**
  * @param string $name group name
  * @return void
  */
  function __construct($name) {
  $this->name = $name;
  }
 
  /**
  * @return void
  */
  public function __destruct() {
  unset($this->name, $this->num_requests, $this->finished_requests, $this->requests);
  }
 
  /**
  * Adds request to a group
  *
  * @throws RollingCurlGroupException
  * @param RollingCurlGroupRequest|array $request
  * @return bool
  */
  function add($request) {
  if ($request instanceof RollingCurlGroupRequest) {
  $request->setGroup($this);
  $this->num_requests++;
  $this->requests[] = $request;
  }
  else if (is_array($request)) {
  foreach ($request as $req)
  $this->add($req);
  }
  else
  throw new RollingCurlGroupException("add: Request needs to be of instance RollingCurlGroupRequest");
 
  return true;
  }
 
  /**
  * @throws RollingCurlGroupException
  * @param RollingCurl $rc
  * @return bool
  */
  function addToRC(RollingCurl $rc){
  $ret = true;
 
  while (count($this->requests) > 0){
  $ret1 = $rc->add(array_shift($this->requests));
  if (!$ret1)
  $ret = false;
  }
 
  return $ret;
  }
 
  /**
  * Override to implement custom response processing.
  *
  * Don't forget to call parent::process().
  *
  * @param string $output received page body
  * @param array $info holds various information about response such as HTTP response code, content type, time taken to make request etc.
  * @param RollingCurlRequest $request request used
  * @return void
  */
  function process($output, $info, $request) {
  $this->finished_requests++;
 
  if ($this->finished_requests >= $this->num_requests)
  $this->finished();
  }
 
  /**
  * Override to execute code after all requests in a group are processed.
  *
  * @return void
  */
  function finished() {
  }
 
  }
 
  /**
  * Group version of rolling curl
  */
  class GroupRollingCurl extends RollingCurl {
 
  /**
  * @var mixed common callback for all groups
  */
  private $group_callback = null;
 
  /**
  * @param string $output received page body
  * @param array $info holds various information about response such as HTTP response code, content type, time taken to make request etc.
  * @param RollingCurlRequest $request request used
  * @return void
  */
  protected function process($output, $info, $request) {
  if ($request instanceof RollingCurlGroupRequest)
  $request->process($output, $info);
 
  if (is_callable($this->group_callback))
  call_user_func($this->group_callback, $output, $info, $request);
  }
 
  /**
  * @param mixed $callback common callback for all groups
  * @return void
  */
  function __construct($callback = null) {
  $this->group_callback = $callback;
 
  parent::__construct(array(&$this, "process"));
  }
 
  /**
  * Adds a group to processing queue
  *
  * @param RollingCurlGroup|Request $request
  * @return bool
  */
  public function add($request) {
  if ($request instanceof RollingCurlGroup)
  return $request->addToRC($this);
  else
  return parent::add($request);
  }
 
  /**
  * Execute processing
  *
  * @param int $window_size Max number of simultaneous connections
  * @return bool|string
  */
  public function execute($window_size = null) {
  if (count($this->requests) == 0)
  return false;
 
  return parent::execute($window_size);
  }
  }
 
  <?php
  /*
  authored by Josh Fraser (www.joshfraser.com)
  released under Apache License 2.0
 
  Maintained by Alexander Makarov, http://rmcreative.ru/
 
  $Id$
  */
 
  require("RollingCurl.php");
 
  // a little example that fetches a bunch of sites in parallel and echos the page title and response info for each request
  function request_callback($response, $info, $request) {
  // parse the page title out of the returned HTML
  if (preg_match("~<title>(.*?)</title>~i", $response, $out)) {
  $title = $out[1];
  }
  echo "<b>$title</b><br />";
  print_r($info);
  print_r($request);
  echo "<hr>";
  }
 
  // single curl request
  $rc = new RollingCurl("request_callback");
  $rc->request("http://www.msn.com");
  $rc->execute();
 
  // another single curl request
  $rc = new RollingCurl("request_callback");
  $rc->request("http://www.google.com");
  $rc->execute();
 
  echo "<hr>";
 
  // top 20 sites according to alexa (11/5/09)
  $urls = array("http://www.google.com",
  "http://www.facebook.com",
  "http://www.yahoo.com",
  "http://www.youtube.com",
  "http://www.live.com",
  "http://www.wikipedia.com",
  "http://www.blogger.com",
  "http://www.msn.com",
  "http://www.baidu.com",
  "http://www.yahoo.co.jp",
  "http://www.myspace.com",
  "http://www.qq.com",
  "http://www.google.co.in",
  "http://www.twitter.com",
  "http://www.google.de",
  "http://www.microsoft.com",
  "http://www.google.cn",
  "http://www.sina.com.cn",
  "http://www.wordpress.com",
  "http://www.google.co.uk");
 
  $rc = new RollingCurl("request_callback");
  $rc->window_size = 20;
  foreach ($urls as $url) {
  $request = new RollingCurlRequest($url);
  $rc->add($request);
  }
  $rc->execute();
 
  <?php
  require 'RollingCurl.php';
  require 'RollingCurlGroup.php';
 
  class TestCurlRequest extends RollingCurlGroupRequest {
  public $test_verbose = true;
 
  function process($output, $info) {
  echo "Processing " . $this->url . "\n";
  if ($this->test_verbose)
  print_r($info);
 
  parent::process($output, $info);
  }
  }
 
  class TestCurlGroup extends RollingCurlGroup {
  function process($output, $info, $request) {
  echo "Group CB: Progress " . $this->name . " (" . ($this->finished_requests + 1) . "/" . $this->num_requests . ")\n";
  parent::process($output, $info, $request);
  }
 
  function finished() {
  echo "Group CB: Finished" . $this->name . "\n";
  parent::finished();
  }
  }
 
  $group = new TestCurlGroup("High");
  $group->add(new TestCurlRequest("www.google.de"));
  $group->add(new TestCurlRequest("www.yahoo.de"));
  $group->add(new TestCurlRequest("www.newyorktimes.com"));
  $reqs[] = $group;
 
  $group = new TestCurlGroup("Normal");
  $group->add(new TestCurlRequest("twitter.com"));
  $group->add(new TestCurlRequest("www.bing.com"));
  $group->add(new TestCurlRequest("m.facebook.com"));
  $reqs[] = $group;
 
  $reqs[] = new TestCurlRequest("www.kernel.org");
 
  // No callback here, as its done in Request class
  $rc = new GroupRollingCurl();
 
  foreach ($reqs as $req)
  $rc->add($req);
 
  $rc->execute();
file:b/postinstall (new)
  #!/bin/bash
  #dotcloud postinstall
 
  curl http://s3-ap-southeast-1.amazonaws.com/busresources/cbrfeed.zip \
  -o /home/dotcloud/current/cbrfeed.zip
 
  #db setup
  #curl https://github.com/maxious/ACTBus-ui/raw/master/transitdata.cbrfeed.sql.gz -o transitdata.cbrfeed.sql.gz
  #curl https://github.com/maxious/ACTBus-ui/raw/master/lib/postgis.sql -o postgis.sql
  #createlang -d transitdata plpgsql
  #psql -d transitdata -f postgis.sql
  #gunzip /var/www/transitdata.cbrfeed.sql.gz
  #psql -d transitdata -f transitdata.cbrfeed.sql
  #createuser transitdata -SDRP
  #password transitdata
  #psql -c \"GRANT SELECT ON TABLE agency,calendar,calendar_dates,routes,stop_times,stops,trips TO transitdata;\"
 
file:a/readme.txt -> file:b/readme.txt
Busness Time - An ACT bus timetable webapp Busness Time - An ACT bus timetable webapp
Based on the maxious-canberra-transit-feed @ http://s3-ap-southeast-1.amazonaws.com/busresources/cbrfeed.zip Based on the maxious-canberra-transit-feed @ http://s3-ap-southeast-1.amazonaws.com/busresources/cbrfeed.zip
Source code for the https://github.com/maxious/ACTBus-data transit Source code for the https://github.com/maxious/ACTBus-data transit
feed and https://github.com/maxious/ACTBus-ui this site available from github. feed and https://github.com/maxious/ACTBus-ui this site available from github.
Uses jQuery Mobile, PHP, Ruby, Python, Google Transit Feed Specification Uses jQuery Mobile, PHP, PostgreSQL, OpenTripPlanner, OpenLayers, OpenStreetMap, Cloudmade Geocoder
tools, OpenTripPlanner, OpenLayers, OpenStreetMap, Cloudmade Geocoder  
and Tile Service and Tile Service
   
Must have view.sh running on port 8765 for this webapp to work See aws/awsStartup.sh for example startup steps
   
For static maps, may have to do For static maps, may have to do
/usr/sbin/setsebool -P httpd_can_network_connect=1 /usr/sbin/setsebool -P httpd_can_network_connect=1
on fedora on fedora
   
   
<?php <?php
include ('include/common.inc.php'); include ('include/common.inc.php');
function navbar() function navbar()
{ {
echo ' echo '
<div data-role="navbar"> <div data-role="navbar">
<ul> <ul>
<li><a href="routeList.php">By Final Destination...</a></li> <li><a href="routeList.php">By Final Destination...</a></li>
<li><a href="routeList.php?bynumber=yes">By Number... </a></li> <li><a href="routeList.php?bynumber=yes">By Number... </a></li>
<li><a href="routeList.php?bysuburb=yes">By Suburb... </a></li> <li><a href="routeList.php?bysuburb=yes">By Suburb... </a></li>
<li><a href="routeList.php?nearby=yes">Nearby... </a></li> <li><a href="routeList.php?nearby=yes">Nearby... </a></li>
</ul> </ul>
</div> </div>
'; ';
} }
if ($_REQUEST['bysuburb']) { if ($_REQUEST['bysuburb']) {
include_header("Routes by Suburb", "routeList"); include_header("Routes by Suburb", "routeList");
navbar(); navbar();
echo ' <ul data-role="listview" data-filter="true" data-inset="true" >'; echo ' <ul data-role="listview" data-filter="true" data-inset="true" >';
if (!isset($_REQUEST['firstLetter'])) { if (!isset($_REQUEST['firstLetter'])) {
foreach (range('A', 'Z') as $letter) { foreach (range('A', 'Z') as $letter) {
echo "<li><a href=\"routeList.php?firstLetter=$letter&bysuburb=yes\">$letter...</a></li>\n"; echo "<li><a href=\"routeList.php?firstLetter=$letter&bysuburb=yes\">$letter...</a></li>\n";
} }
} }
else { else {
foreach ($suburbs as $suburb) { foreach ($suburbs as $suburb) {
if (startsWith($suburb, $_REQUEST['firstLetter'])) { if (startsWith($suburb, $_REQUEST['firstLetter'])) {
echo '<li><a href="routeList.php?suburb=' . urlencode($suburb) . '">' . $suburb . '</a></li>'; echo '<li><a href="routeList.php?suburb=' . urlencode($suburb) . '">' . $suburb . '</a></li>';
} }
} }
} }
echo '</ul>'; echo '</ul>';
} }
else if ($_REQUEST['nearby'] || $_REQUEST['suburb']) { else if ($_REQUEST['nearby'] || $_REQUEST['suburb']) {
  $routes = Array();
if ($_REQUEST['suburb']) { if ($_REQUEST['suburb']) {
$suburb = filter_var($_REQUEST['suburb'], FILTER_SANITIZE_STRING); $suburb = filter_var($_REQUEST['suburb'], FILTER_SANITIZE_STRING);
$url = $APIurl . "/json/stopzonesearch?q=" . $suburb; include_header($suburb ." - ".ucwords(service_period()), "routeList");
include_header("Routes by Suburb", "routeList"); navbar();
trackEvent("Route Lists", "Routes By Suburb", $suburb); timePlaceSettings();
  trackEvent("Route Lists", "Routes By Suburb", $suburb);
  $routes = getRoutesBySuburb($suburb);
   
} }
if ($_REQUEST['nearby']) { if ($_REQUEST['nearby']) {
$url = $APIurl . "/json/neareststops?lat={$_SESSION['lat']}&lon={$_SESSION['lon']}&limit=15";  
include_header("Routes Nearby", "routeList", true, true); include_header("Routes Nearby", "routeList", true, true);
timePlaceSettings(true); trackEvent("Route Lists", "Routes Nearby", $_SESSION['lat'].",".$_SESSION['lon']);
  navbar();
  timePlaceSettings(true);
if (!isset($_SESSION['lat']) || !isset($_SESSION['lat']) || $_SESSION['lat'] == "" || $_SESSION['lon'] == "") { if (!isset($_SESSION['lat']) || !isset($_SESSION['lat']) || $_SESSION['lat'] == "" || $_SESSION['lon'] == "") {
include_footer(); include_footer();
die(); die();
} }
  $routes = getRoutesNearby($_SESSION['lat'],$_SESSION['lon']);
} }
$stops = json_decode(getPage($url));  
$routes = Array(); echo ' <ul data-role="listview" data-filter="true" data-inset="true" >';
foreach ($stops as $stop) {  
$url = $APIurl . "/json/stoproutes?stop=" . $stop[0]; foreach ($routes as $route) {
$stoproutes = json_decode(getPage($url)); echo '<li><a href="trip.php?routeid=' . $route['route_id'] . '"><h3>' . $route['route_short_name'] . "</h3><p>" . $route['route_long_name'] . " (" . ucwords($route['service_id']) . ")</p>";
foreach ($stoproutes as $route) { if ($_REQUEST['nearby']) {
if (!isset($routes[$route[0]])) $routes[$route[0]] = $route; $time = getTimeInterpolatedRouteAtStop($route['route_id'], $route['stop_id']);
  echo '<span class="ui-li-count">'.($time['arrival_time']?$time['arrival_time']:"No more trips today")."<br>" .floor($route['distance']) . 'm away</span>';
} }
} echo "</a></li>\n";
navbar();  
echo ' <ul data-role="listview" data-filter="true" data-inset="true" >';  
sksort($routes, 1, true);  
foreach ($routes as $row) {  
echo '<li><a href="trip.php?routeid=' . $row[0] . '"><h3>'. $row[1] . "</h3><p>". $row[2] . " (" . ucwords($row[4]) . ")</p></a></li>\n";  
} }
} }
else if ($_REQUEST['bynumber'] || $_REQUEST['numberSeries']) { else if ($_REQUEST['bynumber'] || $_REQUEST['numberSeries']) {
include_header("Routes by Number", "routeList"); include_header("Routes by Number", "routeList");
navbar(); navbar();
echo ' <ul data-role="listview" data-inset="true">'; echo ' <ul data-role="listview" data-inset="true">';
$url = $APIurl . "/json/routes"; if ($_REQUEST['bynumber']) {
$contents = json_decode(getPage($url)); $routes = getRoutesByNumber();
$routeSeries = Array(); $routeSeries = Array();
$seriesRange = Array(); $seriesRange = Array();
foreach ($contents as $key => $row) { foreach ($routes as $key => $routeNumber) {
foreach (explode(" ", $row[1]) as $routeNumber) { foreach (explode(" ", $routeNumber['route_short_name']) as $routeNumber) {
$seriesNum = substr($routeNumber, 0, -1) . "0"; $seriesNum = substr($routeNumber, 0, -1) . "0";
if ($seriesNum == "0") $seriesNum = $routeNumber; if ($seriesNum == "0") $seriesNum = $routeNumber;
$finalDigit = substr($routeNumber, sizeof($routeNumber) - 1, 1); $finalDigit = substr($routeNumber, sizeof($routeNumber) - 1, 1);
if (isset($seriesRange[$seriesNum])) { if (isset($seriesRange[$seriesNum])) {
if ($finalDigit < $seriesRange[$seriesNum]['max']) $seriesRange[$seriesNum]['max'] = $routeNumber; if ($finalDigit < $seriesRange[$seriesNum]['max']) $seriesRange[$seriesNum]['max'] = $routeNumber;
if ($finalDigit > $seriesRange[$seriesNum]['min']) $seriesRange[$seriesNum]['min'] = $routeNumber; if ($finalDigit > $seriesRange[$seriesNum]['min']) $seriesRange[$seriesNum]['min'] = $routeNumber;
  }
  else {
  $seriesRange[$seriesNum]['max'] = $routeNumber;
  $seriesRange[$seriesNum]['min'] = $routeNumber;
  }
  $routeSeries[$seriesNum][$seriesNum . "-" . $row[1] . "-" . $row[0]] = $row;
} }
else {  
$seriesRange[$seriesNum]['max'] = $routeNumber;  
$seriesRange[$seriesNum]['min'] = $routeNumber;  
}  
$routeSeries[$seriesNum][$seriesNum . "-" . $row[1] . "-" . $row[0]] = $row;  
} }
}  
if ($_REQUEST['bynumber']) {  
ksort($routeSeries); ksort($routeSeries);
ksort($seriesRange); ksort($seriesRange);
foreach ($routeSeries as $series => $routes) { foreach ($routeSeries as $series => $routes) {
echo '<li><a href="' . curPageURL() . '/routeList.php?numberSeries=' . $series . '">'; echo '<li><a href="' . curPageURL() . '/routeList.php?numberSeries=' . $series . '">';
if ($series <= 9) echo $series; if ($series <= 9) echo $series;
else echo "{$seriesRange[$series]['min']}-{$seriesRange[$series]['max']}"; else echo "{$seriesRange[$series]['min']}-{$seriesRange[$series]['max']}";
echo "</a></li>\n"; echo "</a></li>\n";
} }
} }
else if ($_REQUEST['numberSeries']) { else if ($_REQUEST['numberSeries']) {
foreach ($routeSeries[$_REQUEST['numberSeries']] as $row) { $routes = getRoutesByNumber($_REQUEST['numberSeries']);
echo '<li> <a href="trip.php?routeid=' . $row[0] . '"><h3>' . $row[1] . "</h3><p>". $row[2] . " (" . ucwords($row[3]) . ")</p></a></li>\n"; foreach ($routes as $route) {
  echo '<li> <a href="trip.php?routeid=' . $route['route_id'] . '"><h3>' . $route['route_short_name'] . "</h3><p>" . $route['route_long_name'] . " (" . ucwords($route['service_id']) . ")</p></a></li>\n";
} }
} }
} }
else { else {
include_header("Routes by Destination", "routeList"); include_header("Routes by Destination", "routeList");
navbar(); navbar();
echo ' <ul data-role="listview" data-inset="true">'; echo ' <ul data-role="listview" data-inset="true">';
$url = $APIurl . "/json/routes";  
$contents = json_decode(getPage($url));  
// by destination!  
foreach ($contents as $row) {  
$routeDestinations[$row[2]][] = $row;  
}  
if ($_REQUEST['routeDestination']) { if ($_REQUEST['routeDestination']) {
foreach ($routeDestinations[urldecode($_REQUEST['routeDestination'])] as $row) { foreach (getRoutesByDestination(urldecode($_REQUEST['routeDestination'])) as $route) {
echo '<li><a href="trip.php?routeid=' . $row[0] . '"><h3>' . $row[1] . '</h3><p>' . $row[2] . " (" . ucwords($row[3]) . ")</p></a></li>\n"; echo '<li><a href="trip.php?routeid=' . $route["route_id"] . '"><h3>' . $route["route_short_name"] . '</h3><p>' . $route["route_long_name"] . " (" . ucwords($route['service_id']) . ")</p></a></li>\n";
} }
} }
else { else {
foreach ($routeDestinations as $destination => $routes) { foreach (getRoutesByDestination() as $destination) {
echo '<li><a href="' . curPageURL() . '/routeList.php?routeDestination=' . urlencode($destination) . '">' . $destination . "... </a></li>\n"; echo '<li><a href="' . curPageURL() . '/routeList.php?routeDestination=' . urlencode($destination['route_long_name']) . '">' . $destination['route_long_name'] . "... </a></li>\n";
} }
} }
} }
echo "</ul>\n"; echo "</ul>\n";
include_footer(); include_footer();
?> ?>
   
file:a/schedule_viewer.py (deleted)
#!/usr/bin/python2.5  
 
# Copyright (C) 2007 Google Inc.  
#  
# Licensed under the Apache License, Version 2.0 (the "License");  
# you may not use this file except in compliance with the License.  
# You may obtain a copy of the License at  
#  
# http://www.apache.org/licenses/LICENSE-2.0  
#  
# Unless required by applicable law or agreed to in writing, software  
# distributed under the License is distributed on an "AS IS" BASIS,  
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
# See the License for the specific language governing permissions and  
# limitations under the License.  
 
"""  
An example application that uses the transitfeed module.  
 
You must provide a Google Maps API key.  
"""  
 
 
import BaseHTTPServer, sys, urlparse  
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler  
from SocketServer import ThreadingMixIn  
import threading  
import bisect  
from gtfsscheduleviewer.marey_graph import MareyGraph  
import gtfsscheduleviewer  
import mimetypes  
import os.path  
import re  
import signal  
import simplejson  
import socket  
import time  
import datetime  
import transitfeed  
from transitfeed import util  
import urllib  
 
 
# By default Windows kills Python with Ctrl+Break. Instead make Ctrl+Break  
# raise a KeyboardInterrupt.  
if hasattr(signal, 'SIGBREAK'):  
signal.signal(signal.SIGBREAK, signal.default_int_handler)  
 
 
mimetypes.add_type('text/plain', '.vbs')  
 
 
class ResultEncoder(simplejson.JSONEncoder):  
def default(self, obj):  
try:  
iterable = iter(obj)  
except TypeError:  
pass  
else:  
return list(iterable)  
return simplejson.JSONEncoder.default(self, obj)  
 
class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):  
"""Handle requests in a separate thread."""  
 
def StopToTuple(stop):  
"""Return tuple as expected by javascript function addStopMarkerFromList"""  
return (stop.stop_id, stop.stop_name, float(stop.stop_lat),  
float(stop.stop_lon), stop.location_type, stop.stop_code)  
def StopZoneToTuple(stop):  
"""Return tuple as expected by javascript function addStopMarkerFromList"""  
return (stop.stop_id, stop.stop_name, float(stop.stop_lat),  
float(stop.stop_lon), stop.location_type, stop.stop_code, stop.zone_id)  
 
class ScheduleRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):  
cache = {}  
 
def do_GET(self):  
scheme, host, path, x, params, fragment = urlparse.urlparse(self.path)  
parsed_params = {}  
for k in params.split('&'):  
k = urllib.unquote(k)  
if '=' in k:  
k, v = k.split('=', 1)  
parsed_params[k] = unicode(v, 'utf8')  
else:  
parsed_params[k] = ''  
 
if path == '/':  
return self.handle_GET_home()  
 
m = re.match(r'/json/([a-z]{1,64})', path)  
if m:  
handler_name = 'handle_json_GET_%s' % m.group(1)  
handler = getattr(self, handler_name, None)  
if callable(handler):  
return self.handle_json_wrapper_GET(handler, parsed_params, handler_name)  
 
# Restrict allowable file names to prevent relative path attacks etc  
m = re.match(r'/file/([a-z0-9_-]{1,64}\.?[a-z0-9_-]{1,64})$', path)  
if m and m.group(1):  
try:  
f, mime_type = self.OpenFile(m.group(1))  
return self.handle_static_file_GET(f, mime_type)  
except IOError, e:  
print "Error: unable to open %s" % m.group(1)  
# Ignore and treat as 404  
 
m = re.match(r'/([a-z]{1,64})', path)  
if m:  
handler_name = 'handle_GET_%s' % m.group(1)  
handler = getattr(self, handler_name, None)  
if callable(handler):  
return handler(parsed_params)  
 
return self.handle_GET_default(parsed_params, path)  
 
def OpenFile(self, filename):  
"""Try to open filename in the static files directory of this server.  
Return a tuple (file object, string mime_type) or raise an exception."""  
(mime_type, encoding) = mimetypes.guess_type(filename)  
assert mime_type  
# A crude guess of when we should use binary mode. Without it non-unix  
# platforms may corrupt binary files.  
if mime_type.startswith('text/'):  
mode = 'r'  
else:  
mode = 'rb'  
return open(os.path.join(self.server.file_dir, filename), mode), mime_type  
 
def handle_GET_default(self, parsed_params, path):  
self.send_error(404)  
 
def handle_static_file_GET(self, fh, mime_type):  
content = fh.read()  
self.send_response(200)  
self.send_header('Content-Type', mime_type)  
self.send_header('Content-Length', str(len(content)))  
self.end_headers()  
self.wfile.write(content)  
 
def AllowEditMode(self):  
return False  
 
def handle_GET_home(self):  
schedule = self.server.schedule  
(min_lat, min_lon, max_lat, max_lon) = schedule.GetStopBoundingBox()  
forbid_editing = ('true', 'false')[self.AllowEditMode()]  
 
agency = ', '.join(a.agency_name for a in schedule.GetAgencyList()).encode('utf-8')  
 
key = self.server.key  
host = self.server.host  
 
# A very simple template system. For a fixed set of values replace [xxx]  
# with the value of local variable xxx  
f, _ = self.OpenFile('index.html')  
content = f.read()  
for v in ('agency', 'min_lat', 'min_lon', 'max_lat', 'max_lon', 'key',  
'host', 'forbid_editing'):  
content = content.replace('[%s]' % v, str(locals()[v]))  
 
self.send_response(200)  
self.send_header('Content-Type', 'text/html')  
self.send_header('Content-Length', str(len(content)))  
self.end_headers()  
self.wfile.write(content)  
 
def handle_json_GET_routepatterns(self, params):  
"""Given a route_id generate a list of patterns of the route. For each  
pattern include some basic information and a few sample trips."""  
schedule = self.server.schedule  
route = schedule.GetRoute(params.get('route', None))  
if not route:  
self.send_error(404)  
return  
time = int(params.get('time', 0))  
sample_size = 10 # For each pattern return the start time for this many trips  
 
pattern_id_trip_dict = route.GetPatternIdTripDict()  
patterns = []  
 
for pattern_id, trips in pattern_id_trip_dict.items():  
time_stops = trips[0].GetTimeStops()  
if not time_stops:  
continue  
has_non_zero_trip_type = False;  
for trip in trips:  
if trip['trip_type'] and trip['trip_type'] != '0':  
has_non_zero_trip_type = True  
name = u'%s to %s, %d stops' % (time_stops[0][2].stop_name, time_stops[-1][2].stop_name, len(time_stops))  
transitfeed.SortListOfTripByTime(trips)  
 
num_trips = len(trips)  
if num_trips <= sample_size:  
start_sample_index = 0  
num_after_sample = 0  
else:  
# Will return sample_size trips that start after the 'time' param.  
 
# Linear search because I couldn't find a built-in way to do a binary  
# search with a custom key.  
start_sample_index = len(trips)  
for i, trip in enumerate(trips):  
if trip.GetStartTime() >= time:  
start_sample_index = i  
break  
 
num_after_sample = num_trips - (start_sample_index + sample_size)  
if num_after_sample < 0:  
# Less than sample_size trips start after 'time' so return all the  
# last sample_size trips.  
num_after_sample = 0  
start_sample_index = num_trips - sample_size  
 
sample = []  
for t in trips[start_sample_index:start_sample_index + sample_size]:  
sample.append( (t.GetStartTime(), t.trip_id) )  
 
patterns.append((name, pattern_id, start_sample_index, sample,  
num_after_sample, (0,1)[has_non_zero_trip_type]))  
 
patterns.sort()  
return patterns  
 
def handle_json_wrapper_GET(self, handler, parsed_params, handler_name):  
"""Call handler and output the return value in JSON."""  
schedule = self.server.schedule  
# round times to nearest 100 seconds  
if "time" in parsed_params:  
parsed_params['time'] = int(round(float(parsed_params['time']),-2))  
paramkey = tuple(sorted(parsed_params.items()))  
if handler_name in self.cache and paramkey in self.cache[handler_name] :  
print ("Cache hit for ",handler_name," params ",parsed_params,  
" thread ", threading.currentThread().getName())  
else:  
print ("Cache miss for ",handler_name," params ",parsed_params,  
" thread ", threading.currentThread().getName())  
result = handler(parsed_params)  
if not handler_name in self.cache:  
self.cache[handler_name] = {}  
self.cache[handler_name][paramkey] = ResultEncoder().encode(result)  
content = self.cache[handler_name][paramkey]  
self.send_response(200)  
self.send_header('Content-Type', 'text/plain')  
self.send_header('Content-Length', str(len(content)))  
self.end_headers()  
self.wfile.write(content)  
 
def handle_json_GET_routes(self, params):  
"""Return a list of all routes."""  
schedule = self.server.schedule  
result = []  
for r in schedule.GetRouteList():  
servicep = None  
for t in schedule.GetTripList():  
if t.route_id == r.route_id:  
servicep = t.service_period  
break  
result.append( (r.route_id, r.route_short_name, r.route_long_name, servicep.service_id) )  
result.sort(key = lambda x: x[1:3])  
return result  
 
def handle_json_GET_routesearch(self, params):  
"""Return a list of routes with matching short name."""  
schedule = self.server.schedule  
routeshortname = params.get('routeshortname', None)  
result = []  
for r in schedule.GetRouteList():  
if r.route_short_name == routeshortname:  
servicep = None  
for t in schedule.GetTripList():  
if t.route_id == r.route_id:  
servicep = t.service_period  
break  
result.append( (r.route_id, r.route_short_name, r.route_long_name, servicep.service_id) )  
result.sort(key = lambda x: x[1:3])  
return result  
 
 
def handle_json_GET_routerow(self, params):  
schedule = self.server.schedule  
route = schedule.GetRoute(params.get('route', None))  
return [transitfeed.Route._FIELD_NAMES, route.GetFieldValuesTuple()]  
 
def handle_json_GET_routetrips(self, params):  
""" Get a trip for a route_id (preferablly the next one) """  
schedule = self.server.schedule  
query = params.get('route_id', None).lower()  
result = []  
for t in schedule.GetTripList():  
if t.route_id == query:  
try:  
starttime = t.GetStartTime()  
except:  
print "Error for GetStartTime of trip #" + t.trip_id + sys.exc_info()[0]  
else:  
cursor = t._schedule._connection.cursor()  
cursor.execute(  
'SELECT arrival_secs,departure_secs FROM stop_times WHERE '  
'trip_id=? ORDER BY stop_sequence DESC LIMIT 1', (t.trip_id,))  
(arrival_secs, departure_secs) = cursor.fetchone()  
if arrival_secs != None:  
endtime = arrival_secs  
elif departure_secs != None:  
endtime = departure_secs  
else:  
endtime =0  
result.append ( (starttime, t.trip_id, endtime) )  
return sorted(result, key=lambda trip: trip[2])  
 
def handle_json_GET_triprows(self, params):  
"""Return a list of rows from the feed file that are related to this  
trip."""  
schedule = self.server.schedule  
try:  
trip = schedule.GetTrip(params.get('trip', None))  
except KeyError:  
# if a non-existent trip is searched for, the return nothing  
return  
route = schedule.GetRoute(trip.route_id)  
trip_row = dict(trip.iteritems())  
route_row = dict(route.iteritems())  
return [['trips.txt', trip_row], ['routes.txt', route_row]]  
 
def handle_json_GET_tripstoptimes(self, params):  
schedule = self.server.schedule  
try:  
trip = schedule.GetTrip(params.get('trip'))  
except KeyError:  
# if a non-existent trip is searched for, the return nothing  
return  
time_stops = trip.GetTimeInterpolatedStops()  
stops = []  
times = []  
for arr,ts,is_timingpoint in time_stops:  
stops.append(StopToTuple(ts.stop))  
times.append(arr)  
return [stops, times]  
 
def handle_json_GET_tripshape(self, params):  
schedule = self.server.schedule  
try:  
trip = schedule.GetTrip(params.get('trip'))  
except KeyError:  
# if a non-existent trip is searched for, the return nothing  
return  
points = []  
if trip.shape_id:  
shape = schedule.GetShape(trip.shape_id)  
for (lat, lon, dist) in shape.points:  
points.append((lat, lon))  
else:  
time_stops = trip.GetTimeStops()  
for arr,dep,stop in time_stops:  
points.append((stop.stop_lat, stop.stop_lon))  
return points  
 
#  
# GeoPo Encode in Python  
# @author : Shintaro Inagaki  
# @param location (Dictionary) [lat (Float), lng (Float), scale(Int)]  
# @return geopo (String)  
#  
 
def handle_json_GET_neareststops(self, params):  
"""Return a list of the nearest 'limit' stops to 'lat', 'lon'"""  
schedule = self.server.schedule  
lat = float(params.get('lat'))  
lon = float(params.get('lon'))  
limit = int(params.get('limit',5))  
scale = int(params.get('scale',5)) # 5 = neighbourhood ~ 1km, 4= town 5 by 7km  
stops = []  
 
# 64characters (number + big and small letter + hyphen + underscore)  
chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_"  
 
geopo = ""  
 
# Change a degree measure to a decimal number  
lat = (lat + 90.0) / 180 * 8 ** 10 # 90.0 is forced FLOAT type when lat is INT  
lon = (lon + 180.0) / 360 * 8 ** 10 # 180.0 is same  
 
# Compute a GeoPo code from head and concatenate  
for i in range(scale):  
order = int(lat / (8 ** (9 - i)) % 8) + int(lon / (8 ** (9 - i)) % 8) * 8  
geopo = geopo + chars[order]  
 
 
for s in schedule.GetStopList():  
if s.stop_code.find(geopo) != -1:  
stops.append(s)  
 
if scale == 5:  
print stops  
return [StopToTuple(s) for s in stops]  
else:  
dist_stop_list = []  
for s in stops:  
# TODO: Use util.ApproximateDistanceBetweenStops?  
dist = (s.stop_lat - lat)**2 + (s.stop_lon - lon)**2  
if len(dist_stop_list) < limit:  
bisect.insort(dist_stop_list, (dist, s))  
elif dist < dist_stop_list[-1][0]:  
bisect.insort(dist_stop_list, (dist, s))  
dist_stop_list.pop() # Remove stop with greatest distance  
print dist_stop_list  
return [StopToTuple(s) for dist, s in dist_stop_list]  
 
def handle_json_GET_boundboxstops(self, params):  
"""Return a list of up to 'limit' stops within bounding box with 'n','e'  
and 's','w' in the NE and SW corners. Does not handle boxes crossing  
longitude line 180."""  
schedule = self.server.schedule  
n = float(params.get('n'))  
e = float(params.get('e'))  
s = float(params.get('s'))  
w = float(params.get('w'))  
limit = int(params.get('limit'))  
stops = schedule.GetStopsInBoundingBox(north=n, east=e, south=s, west=w, n=limit)  
return [StopToTuple(s) for s in stops]  
 
def handle_json_GET_stops(self, params):  
schedule = self.server.schedule  
return [StopToTuple(s) for s in schedule.GetStopList()]  
 
def handle_json_GET_timingpoints(self, params):  
schedule = self.server.schedule  
matches = []  
for s in schedule.GetStopList():  
if s.stop_code.find("Wj") == -1:  
matches.append(StopToTuple(s))  
return matches  
 
def handle_json_GET_stopsearch(self, params):  
schedule = self.server.schedule  
query = params.get('q', None).lower()  
matches = []  
for s in schedule.GetStopList():  
if s.stop_name.lower().find(query) != -1 or s.stop_code.lower().find(query) != -1:  
matches.append(StopToTuple(s))  
return matches  
 
def handle_json_GET_stopnamesearch(self, params):  
schedule = self.server.schedule  
query = params.get('q', None).lower()  
matches = []  
for s in schedule.GetStopList():  
if s.stop_name.lower().find(query) != -1:  
matches.append(StopToTuple(s))  
return matches  
 
def handle_json_GET_stopcodesearch(self, params):  
schedule = self.server.schedule  
query = params.get('q', None).lower()  
matches = []  
for s in schedule.GetStopList():  
if s.stop_code.lower().find(query) != -1:  
matches.append(StopToTuple(s))  
return matches  
 
def handle_json_GET_stopzonesearch(self, params):  
schedule = self.server.schedule  
query = params.get('q', None).lower()  
matches = []  
for s in schedule.GetStopList():  
if s.zone_id != None and s.zone_id.lower().find(query) != -1:  
matches.append(StopToTuple(s))  
return matches  
 
def handle_json_GET_stop(self, params):  
schedule = self.server.schedule  
query = params.get('stop_id', None).lower()  
for s in schedule.GetStopList():  
if s.stop_id.lower() == query:  
return StopToTuple(s)  
return []  
def handle_json_GET_stoproutes(self, params):  
"""Given a stop_id return all routes to visit the stop."""  
schedule = self.server.schedule  
stop = schedule.GetStop(params.get('stop', None))  
service_period = params.get('service_period', None)  
trips = stop.GetTrips(schedule)  
result = {}  
for trip in trips:  
route = schedule.GetRoute(trip.route_id)  
if not route.route_short_name+route.route_long_name+trip.service_id in result:  
result[route.route_short_name+route.route_long_name+trip.service_id] = (route.route_id, route.route_short_name, route.route_long_name, trip.trip_id, trip.service_id)  
return result  
 
def handle_json_GET_stopalltrips(self, params):  
"""Given a stop_id return all trips to visit the stop."""  
schedule = self.server.schedule  
stop = schedule.GetStop(params.get('stop', None))  
service_period = params.get('service_period', None)  
time_trips = stop.GetStopTimeTrips(schedule)  
result = []  
for time, (trip, index), tp in time_trips:  
headsign = None  
# Find the most recent headsign from the StopTime objects  
for stoptime in trip.GetStopTimes()[index::-1]:  
if stoptime.stop_headsign:  
headsign = stoptime.stop_headsign  
break  
# If stop_headsign isn't found, look for a trip_headsign  
if not headsign:  
headsign = trip.trip_headsign  
route = schedule.GetRoute(trip.route_id)  
trip_name = ''  
if route.route_short_name:  
trip_name += route.route_short_name  
if route.route_long_name:  
if len(trip_name):  
trip_name += " - "  
trip_name += route.route_long_name  
if service_period == None or trip.service_id == service_period:  
result.append((time, (trip.trip_id, trip_name, trip.service_id), tp))  
return result  
 
 
 
def handle_json_GET_stoptrips(self, params):  
"""Given a stop_id and time in seconds since midnight return the next  
trips to visit the stop."""  
schedule = self.server.schedule  
stop = schedule.GetStop(params.get('stop', None))  
requested_time = int(params.get('time', 0))  
limit = int(params.get('limit', 15))  
service_period = params.get('service_period', None)  
time_range = int(params.get('time_range', 24*60*60))  
 
 
filtered_time_trips = []  
for trip, index in stop._GetTripIndex(schedule):  
tripstarttime = trip.GetStartTime()  
if tripstarttime > requested_time and tripstarttime < (requested_time + time_range):  
time, stoptime, tp = trip.GetTimeInterpolatedStops()[index]  
if time > requested_time and time < (requested_time + time_range):  
bisect.insort(filtered_time_trips, (time, (trip, index), tp))  
 
result = []  
for time, (trip, index), tp in filtered_time_trips:  
if len(result) > limit:  
break  
headsign = None  
# Find the most recent headsign from the StopTime objects  
for stoptime in trip.GetStopTimes()[index::-1]:  
if stoptime.stop_headsign:  
headsign = stoptime.stop_headsign  
break  
# If stop_headsign isn't found, look for a trip_headsign  
if not headsign:  
headsign = trip.trip_headsign  
route = schedule.GetRoute(trip.route_id)  
trip_name = ''  
if route.route_short_name:  
trip_name += route.route_short_name  
if route.route_long_name:  
if len(trip_name):  
trip_name += " - "  
trip_name += route.route_long_name  
# comment out directions because we already have them in the long name  
#if headsign:  
# trip_name += " (Direction: %s)" % headsign  
if service_period == None or trip.service_id == service_period:  
result.append((time, (trip.trip_id, trip_name, trip.service_id), tp))  
return result  
 
def handle_GET_ttablegraph(self,params):  
"""Draw a Marey graph in SVG for a pattern (collection of trips in a route  
that visit the same sequence of stops)."""  
schedule = self.server.schedule  
marey = MareyGraph()  
trip = schedule.GetTrip(params.get('trip', None))  
route = schedule.GetRoute(trip.route_id)  
height = int(params.get('height', 300))  
 
if not route:  
print 'no such route'  
self.send_error(404)  
return  
 
pattern_id_trip_dict = route.GetPatternIdTripDict()  
pattern_id = trip.pattern_id  
if pattern_id not in pattern_id_trip_dict:  
print 'no pattern %s found in %s' % (pattern_id, pattern_id_trip_dict.keys())  
self.send_error(404)  
return  
triplist = pattern_id_trip_dict[pattern_id]  
 
pattern_start_time = min((t.GetStartTime() for t in triplist))  
pattern_end_time = max((t.GetEndTime() for t in triplist))  
 
marey.SetSpan(pattern_start_time,pattern_end_time)  
marey.Draw(triplist[0].GetPattern(), triplist, height)  
 
content = marey.Draw()  
 
self.send_response(200)  
self.send_header('Content-Type', 'image/svg+xml')  
self.send_header('Content-Length', str(len(content)))  
self.end_headers()  
self.wfile.write(content)  
 
 
def FindPy2ExeBase():  
"""If this is running in py2exe return the install directory else return  
None"""  
# py2exe puts gtfsscheduleviewer in library.zip. For py2exe setup.py is  
# configured to put the data next to library.zip.  
windows_ending = gtfsscheduleviewer.__file__.find('\\library.zip\\')  
if windows_ending != -1:  
return transitfeed.__file__[:windows_ending]  
else:  
return None  
 
 
def FindDefaultFileDir():  
"""Return the path of the directory containing the static files. By default  
the directory is called 'files'. The location depends on where setup.py put  
it."""  
base = FindPy2ExeBase()  
if base:  
return os.path.join(base, 'schedule_viewer_files')  
else:  
# For all other distributions 'files' is in the gtfsscheduleviewer  
# directory.  
base = os.path.dirname(gtfsscheduleviewer.__file__) # Strip __init__.py  
return os.path.join(base, 'files')  
 
 
def GetDefaultKeyFilePath():  
"""In py2exe return absolute path of file in the base directory and in all  
other distributions return relative path 'key.txt'"""  
windows_base = FindPy2ExeBase()  
if windows_base:  
return os.path.join(windows_base, 'key.txt')  
else:  
return 'key.txt'  
 
 
def main(RequestHandlerClass = ScheduleRequestHandler):  
usage = \  
'''%prog [options] [<input GTFS.zip>]  
 
Runs a webserver that lets you explore a <input GTFS.zip> in your browser.  
 
If <input GTFS.zip> is omited the filename is read from the console. Dragging  
a file into the console may enter the filename.  
'''  
parser = util.OptionParserLongError(  
usage=usage, version='%prog '+transitfeed.__version__)  
parser.add_option('--feed_filename', '--feed', dest='feed_filename',  
help='file name of feed to load')  
parser.add_option('--key', dest='key',  
help='Google Maps API key or the name '  
'of a text file that contains an API key')  
parser.add_option('--host', dest='host', help='Host name of Google Maps')  
parser.add_option('--port', dest='port', type='int',  
help='port on which to listen')  
parser.add_option('--file_dir', dest='file_dir',  
help='directory containing static files')  
parser.add_option('-n', '--noprompt', action='store_false',  
dest='manual_entry',  
help='disable interactive prompts')  
parser.set_defaults(port=8765,  
host='maps.google.com',  
file_dir=FindDefaultFileDir(),  
manual_entry=True)  
(options, args) = parser.parse_args()  
 
if not os.path.isfile(os.path.join(options.file_dir, 'index.html')):  
print "Can't find index.html with --file_dir=%s" % options.file_dir  
exit(1)  
 
if not options.feed_filename and len(args) == 1:  
options.feed_filename = args[0]  
 
if not options.feed_filename and options.manual_entry:  
options.feed_filename = raw_input('Enter Feed Location: ').strip('"')  
 
default_key_file = GetDefaultKeyFilePath()  
if not options.key and os.path.isfile(default_key_file):  
options.key = open(default_key_file).read().strip()  
 
if options.key and os.path.isfile(options.key):  
options.key = open(options.key).read().strip()  
 
schedule = transitfeed.Schedule(problem_reporter=transitfeed.ProblemReporter())  
print 'Loading data from feed "%s"...' % options.feed_filename  
print '(this may take a few minutes for larger cities)'  
t0 = datetime.datetime.now()  
schedule.Load(options.feed_filename)  
print ("Loaded in", (datetime.datetime.now() - t0).seconds , "seconds")  
server = ThreadedHTTPServer(server_address=('', options.port),  
RequestHandlerClass=RequestHandlerClass)  
server.key = options.key  
server.schedule = schedule  
server.file_dir = options.file_dir  
server.host = options.host  
server.feed_path = options.feed_filename  
 
 
print ("To view, point your browser at http://localhost:%d/" %  
(server.server_port))  
server.serve_forever()  
 
 
if __name__ == '__main__':  
main()  
 
file:a/stop.php -> file:b/stop.php
<?php <?php
include ('include/common.inc.php'); include ('include/common.inc.php');
$stopid = filter_var($_REQUEST['stopid'], FILTER_SANITIZE_NUMBER_INT); $stopid = filter_var($_REQUEST['stopid'], FILTER_SANITIZE_NUMBER_INT);
$stopcode = filter_var($_REQUEST['stopcode'], FILTER_SANITIZE_STRING); $stopcode = filter_var($_REQUEST['stopcode'], FILTER_SANITIZE_STRING);
$url = $APIurl . "/json/stop?stop_id=" . $stopid; if ($stopid) $stop = getStop($stopid);
$stop = json_decode(getPage($url)); /*if ($stopcode != "" && $stop[5] != $stopcode) {
if ($stopcode != "" && $stop[5] != $stopcode) {  
$url = $APIurl . "/json/stopcodesearch?q=" . $stopcode; $url = $APIurl . "/json/stopcodesearch?q=" . $stopcode;
$stopsearch = json_decode(getPage($url)); $stopsearch = json_decode(getPage($url));
$stopid = $stopsearch[0][0]; $stopid = $stopsearch[0][0];
$url = $APIurl . "/json/stop?stop_id=" . $stopid; $url = $APIurl . "/json/stop?stop_id=" . $stopid;
$stop = json_decode(getPage($url)); $stop = json_decode(getPage($url));
} }
if (!startsWith($stop[5], "Wj") && strpos($stop[1], "Platform") === false) { if (!startsWith($stop[5], "Wj") && strpos($stop[1], "Platform") === false) {
// expand out to all platforms // expand out to all platforms
} }*/
$stops = Array(); $stops = Array();
$stopPositions = Array(); $stopPositions = Array();
$stopNames = Array(); $stopNames = Array();
$tripStopNumbers = Array(); $tripStopNumbers = Array();
$allStopsTrips = Array(); $allStopsTrips = Array();
  $fetchedTripSequences = Array();
$stopLinks = ""; $stopLinks = "";
if (isset($_REQUEST['stopids'])) { if (isset($_REQUEST['stopids'])) {
$stopids = explode(",", filter_var($_REQUEST['stopids'], FILTER_SANITIZE_STRING)); $stopids = explode(",", filter_var($_REQUEST['stopids'], FILTER_SANITIZE_STRING));
foreach ($stopids as $sub_stopid) { foreach ($stopids as $sub_stopid) {
$url = $APIurl . "/json/stop?stop_id=" . $sub_stopid; $stops[] = getStop($sub_stopid);
$stop = json_decode(getPage($url));  
$stops[] = $stop;  
} }
$stop = $stops[0]; $stop = $stops[0];
$stopid = $stops[0][0]; $stopid = $stops[0]["stop_id"];
$stopLinks.= "Individual stop pages: "; $stopLinks.= "Individual stop pages: ";
foreach ($stops as $key => $sub_stop) { foreach ($stops as $key => $sub_stop) {
// $stopNames[$key] = $sub_stop[1] . ' Stop #' . ($key + 1); // $stopNames[$key] = $sub_stop[1] . ' Stop #' . ($key + 1);
if (strpos($stop[1], if (strpos($stop["stop_name"], "Station")) {
"Station")) { $stopNames[$key] = 'Platform ' . ($key + 1);
$stopNames[$key] = 'Platform ' . ($key + 1); $stopLinks.= '<a href="stop.php?stopid=' . $sub_stop["stop_id"] . '&stopcode=' . $sub_stop["stop_code"] . '">' . $sub_stop["stop_name"] . '</a> ';
$stopLinks.= '<a href="stop.php?stopid=' . $sub_stop[0] . '&stopcode=' . $sub_stop[5] . '">' . $sub_stop[1] . '</a> '; }
} else { else {
$stopNames[$key] = '#' . ($key + 1); $stopNames[$key] = '#' . ($key + 1);
$stopLinks.= '<a href="stop.php?stopid=' . $sub_stop[0] . '&stopcode=' . $sub_stop[5] . '">' . $sub_stop[1] . ' Stop #' . ($key + 1) . '</a> '; $stopLinks.= '<a href="stop.php?stopid=' . $sub_stop["stop_id"] . '&stopcode=' . $sub_stop["stop_code"] . '">' . $sub_stop["stop_name"] . ' Stop #' . ($key + 1) . '</a> ';
} }
$stopPositions[$key] = Array( $stopPositions[$key] = Array(
$sub_stop[2], $sub_stop["stop_lat"],
$sub_stop[3] $sub_stop["stop_lon"]
); );
$url = $APIurl . "/json/stoptrips?stop=" . $sub_stop[0] . "&time=" . midnight_seconds() . "&service_period=" . service_period(); $trips = getStopTrips($sub_stop["stop_id"]);
$trips = json_decode(getPage($url)); $tripSequence = "";
foreach ($trips as $trip) { foreach ($trips as $trip) {
if (!isset($allStopsTrips[$trip[1][0]])) $allStopsTrips[$trip[1][0]] = $trip; $tripSequence.= "{$trip['trip_id']},";
$tripStopNumbers[$trip[1][0]][] = $key; $tripStopNumbers[$trip['trip_id']][] = $key;
} }
  if (!in_array($tripSequence, $fetchedTripSequences)) {
  // only fetch new trip sequences
  $fetchedTripSequences[] = $tripSequence;
  $trips = getStopTripsWithTimes($sub_stop["stop_id"]);
  foreach ($trips as $trip) {
  if (!isset($allStopsTrips[$trip["trip_id"]])) $allStopsTrips[$trip["trip_id"]] = $trip;
  }
  }
  //else {
  // echo "skipped sequence $tripSequence";
  //}
} }
} }
include_header($stop[1], "stop"); include_header($stop['stop_name'], "stop");
timePlaceSettings(); timePlaceSettings();
echo '<div data-role="content" class="ui-content" role="main"> <a name="maincontent" id="maincontent"></a>'; echo '<div data-role="content" class="ui-content" role="main"> <a name="maincontent" id="maincontent"></a>';
echo $stopLinks; echo $stopLinks;
if (sizeof($stops) > 0) { if (sizeof($stops) > 0) {
trackEvent("View Stops","View Combined Stops", $stop[1], $stop[0]); trackEvent("View Stops", "View Combined Stops", $stop["stop_name"], $stop["stop_id"]);
   
echo '<p>' . staticmap($stopPositions) . '</p>'; echo '<p>' . staticmap($stopPositions) . '</p>';
} }
else { else {
trackEvent("View Stops","View Single Stop", $stop[1], $stop[0]); trackEvent("View Stops", "View Single Stop", $stop["stop_name"], $stop["stop_id"]);
echo '<p>' . staticmap(Array( echo '<p>' . staticmap(Array(
0 => Array( 0 => Array(
$stop[2], $stop["stop_lat"],
$stop[3] $stop["stop_lon"]
) )
)) . '</p>'; )) . '</p>';
} }
echo ' <ul data-role="listview" data-inset="true">'; echo ' <ul data-role="listview" data-inset="true">';
if (sizeof($allStopsTrips) > 0) { if (sizeof($allStopsTrips) > 0) {
sksort($allStopsTrips,0, $true); sktimesort($allStopsTrips,"arrival_time", true);
$trips = $allStopsTrips; $trips = $allStopsTrips;
} }
else { else {
$url = $APIurl . "/json/stoptrips?stop=" . $stopid . "&time=" . midnight_seconds() . "&service_period=" . service_period(); $trips = getStopTripsWithTimes($stopid);
$trips = json_decode(getPage($url));  
} }
foreach ($trips as $row) { if (sizeof($trips) == 0) {
echo '<li>'; echo "<li> <center>No trips in the near future.</center> </li>";
echo '<a href="trip.php?stopid=' . $stopid . '&tripid=' . $row[1][0] . '"><h3>' . $row[1][1]."</h3><p>";  
$viaPoints = viaPointNames($row[1][0], $stopid);  
if ($viaPoints != "") echo '<br><span class="viaPoints">Via: ' . $viaPoints . '</span>';  
if (sizeof($tripStopNumbers) > 0) {  
echo '<br><small>Boarding At: ';  
foreach ($tripStopNumbers[$row[1][0]] as $key) {  
echo $stopNames[$key] .' ';  
}  
echo '</small>';  
}  
echo '</p>';  
echo '<p class="ui-li-aside"><strong>' . midnight_seconds_to_time($row[0]) . '</strong></p>';  
echo '</a></li>';  
flush(); @ob_flush();  
} }
if (sizeof($trips) == 0) echo "<li> <center>No trips in the near future.</center> </li>"; else {
  foreach ($trips as $trip) {
  echo '<li>';
  echo '<a href="trip.php?stopid=' . $stopid . '&tripid=' . $trip['trip_id'] . '"><h3>' . $trip['route_short_name'] . " " . $trip['route_long_name'] . "</h3><p>";
  $viaPoints = viaPointNames($trip['trip_id'], $trip['stop_sequence']);
  if ($viaPoints != "") echo '<br><span class="viaPoints">Via: ' . $viaPoints . '</span>';
  if (sizeof($tripStopNumbers) > 0) {
  echo '<br><small>Boarding At: ';
  foreach ($tripStopNumbers[$trip['trip_id']] as $key) {
  echo $stopNames[$key] . ' ';
  }
  echo '</small>';
  }
  echo '</p>';
  echo '<p class="ui-li-aside"><strong>' . $trip['arrival_time'] . '</strong></p>';
  echo '</a></li>';
  flush();
  @ob_flush();
  }
  }
echo '</ul></div>'; echo '</ul></div>';
include_footer(); include_footer();
?> ?>
   
<?php <?php
include ('include/common.inc.php'); include ('include/common.inc.php');
  $stops = Array();
function filterByFirstLetter($var) function filterByFirstLetter($var)
{ {
return $var[1][0] == $_REQUEST['firstLetter']; return $var[1][0] == $_REQUEST['firstLetter'];
} }
function navbar() function navbar()
{ {
echo ' echo '
<div data-role="navbar"> <div data-role="navbar">
<ul> <ul>
<li><a href="stopList.php">Timing Points</a></li> <li><a href="stopList.php">Timing Points</a></li>
<li><a href="stopList.php?suburbs=yes">By Suburb</a></li> <li><a href="stopList.php?suburbs=yes">By Suburb</a></li>
<li><a href="stopList.php?nearby=yes">Nearby Stops</a></li> <li><a href="stopList.php?nearby=yes">Nearby Stops</a></li>
<li><a href="stopList.php?allstops=yes">All Stops</a></li> <li><a href="stopList.php?allstops=yes">All Stops</a></li>
</ul> </ul>
</div> </div>
'; ';
} }
// By suburb // By suburb
if (isset($_REQUEST['suburbs'])) { if (isset($_REQUEST['suburbs'])) {
include_header("Stops by Suburb", "stopList"); include_header("Stops by Suburb", "stopList");
navbar(); navbar();
echo ' <ul data-role="listview" data-filter="true" data-inset="true" >'; echo ' <ul data-role="listview" data-filter="true" data-inset="true" >';
if (!isset($_REQUEST['firstLetter'])) { if (!isset($_REQUEST['firstLetter'])) {
foreach (range('A', 'Z') as $letter) { foreach (range('A', 'Z') as $letter) {
echo "<li><a href=\"stopList.php?firstLetter=$letter&suburbs=yes\">$letter...</a></li>\n"; echo "<li><a href=\"stopList.php?firstLetter=$letter&suburbs=yes\">$letter...</a></li>\n";
} }
} }
else { else {
foreach ($suburbs as $suburb) { foreach ($suburbs as $suburb) {
if (startsWith($suburb, $_REQUEST['firstLetter'])) { if (startsWith($suburb, $_REQUEST['firstLetter'])) {
echo '<li><a href="stopList.php?suburb=' . urlencode($suburb) . '">' . $suburb . '</a></li>'; echo '<li><a href="stopList.php?suburb=' . urlencode($suburb) . '">' . $suburb . '</a></li>';
flush(); @ob_flush();  
} }
} }
} }
echo '</ul>'; echo '</ul>';
} }
else { else {
// Timing Points / All stops // Timing Points / All stops
if ($_REQUEST['allstops']) { if ($_REQUEST['allstops']) {
$listType = 'allstops=yes'; $listType = 'allstops=yes';
$url = $APIurl . "/json/stops"; $stops = getStops();
include_header("All Stops", "stopList"); include_header("All Stops", "stopList");
navbar(); navbar();
timePlaceSettings(); timePlaceSettings();
} }
else if ($_REQUEST['nearby']) { else if ($_REQUEST['nearby']) {
$listType = 'nearby=yes'; $listType = 'nearby=yes';
$url = $APIurl . "/json/neareststops?lat={$_SESSION['lat']}&lon={$_SESSION['lon']}&limit=15";  
include_header("Nearby Stops", "stopList", true, true); include_header("Nearby Stops", "stopList", true, true);
  trackEvent("Stop Lists","Stops Nearby", $_SESSION['lat'].",".$_SESSION['lon']);
navbar(); navbar();
timePlaceSettings(true); timePlaceSettings(true);
if (!isset($_SESSION['lat']) || !isset($_SESSION['lat']) || $_SESSION['lat'] == "" || $_SESSION['lon'] == "") { if (!isset($_SESSION['lat']) || !isset($_SESSION['lat']) || $_SESSION['lat'] == "" || $_SESSION['lon'] == "") {
include_footer(); include_footer();
die(); die();
} }
   
  $stops = getNearbyStops($_SESSION['lat'],$_SESSION['lon'],15);
} }
else if ($_REQUEST['suburb']) { else if ($_REQUEST['suburb']) {
$suburb = filter_var($_REQUEST['suburb'], FILTER_SANITIZE_STRING); $suburb = filter_var($_REQUEST['suburb'], FILTER_SANITIZE_STRING);
$listType = "suburb=$suburb"; $stops = getStopsBySuburb($suburb);
$url = $APIurl . "/json/stopzonesearch?q=" . $suburb;  
include_header("Stops in " . ucwords($suburb) , "stopList"); include_header("Stops in " . ucwords($suburb) , "stopList");
navbar(); navbar();
trackEvent("Stop Lists","Stops By Suburb", $suburb); trackEvent("Stop Lists","Stops By Suburb", $suburb);
} }
else { else {
$url = $APIurl . "/json/timingpoints"; $stops = getStops(true,$_REQUEST['firstLetter']);
include_header("Timing Points / Major Stops", "stopList"); include_header("Timing Points / Major Stops", "stopList");
navbar(); navbar();
timePlaceSettings(); timePlaceSettings();
} }
echo ' <ul data-role="listview" data-filter="true" data-inset="true" >'; echo ' <ul data-role="listview" data-filter="true" data-inset="true" >';
if (!isset($_REQUEST['firstLetter']) && !$_REQUEST['suburb'] && !$_REQUEST['nearby']) { if (!isset($_REQUEST['firstLetter']) && !$_REQUEST['suburb'] && !$_REQUEST['nearby']) {
foreach (range('A', 'Z') as $letter) { foreach (range('A', 'Z') as $letter) {
echo "<li><a href=\"stopList.php?firstLetter=$letter&$listType\">$letter...</a></li>\n"; echo "<li><a href=\"stopList.php?firstLetter=$letter&$listType\">$letter...</a></li>\n";
} }
} }
else { else {
$stops = json_decode(getPage($url)); //var_dump($stops);
foreach ($stops as $key => $row) {  
$stopName[$key] = $row[1];  
}  
// Sort the stops by name  
array_multisort($stopName, SORT_ASC, $stops);  
if (!isset($_REQUEST['suburb']) && !isset($_REQUEST['nearby'])) {  
$stops = array_filter($stops, "filterByFirstLetter");  
}  
$stopsGrouped = Array(); $stopsGrouped = Array();
foreach ($stops as $key => $row) { foreach ($stops as $key => $stop) {
if ((trim(preg_replace("/\(Platform.*/", "", $stops[$key][1])) != trim(preg_replace("/\(Platform.*/", "", $stops[$key + 1][1]))) || $key + 1 >= sizeof($stops)) { if ((trim(preg_replace("/\(Platform.*/", "", $stops[$key]["stop_name"])) != trim(preg_replace("/\(Platform.*/", "", $stops[$key + 1]["stop_name"]))) || $key + 1 >= sizeof($stops)) {
if (sizeof($stopsGrouped) > 0) { if (sizeof($stopsGrouped) > 0) {
// print and empty grouped stops // print and empty grouped stops
// subsequent duplicates // subsequent duplicates
$stopsGrouped["stop_ids"][] = $row[0]; $stopsGrouped["stop_ids"][] = $stop['stop_id'];
echo '<li>'; echo '<li>';
if (!startsWith($stopsGrouped['stop_codes'][0], "Wj")) echo '<img src="css/images/time.png" alt="Timing Point: " class="ui-li-icon">'; if (!startsWith($stopsGrouped['stop_codes'][0], "Wj")) echo '<img src="css/images/time.png" alt="Timing Point: " class="ui-li-icon">';
echo '<a href="stop.php?stopids=' . implode(",", $stopsGrouped['stop_ids']) . '">'; echo '<a href="stop.php?stopids=' . implode(",", $stopsGrouped['stop_ids']) . '">';
if (isset($_SESSION['lat']) && isset($_SESSION['lon'])) { if (isset($_SESSION['lat']) && isset($_SESSION['lon'])) {
echo '<span class="ui-li-count">' . distance($row[2], $row[3], $_SESSION['lat'], $_SESSION['lon'], true) . 'm away</span>'; echo '<span class="ui-li-count">' . distance($stop['stop_lat'],$stop['stop_lon'], $_SESSION['lat'], $_SESSION['lon'], true) . 'm away</span>';
} }
echo bracketsMeanNewLine(trim(preg_replace("/\(Platform.*/", "", $row[1])) . '(' . sizeof($stopsGrouped["stop_ids"]) . ' stops)'); echo bracketsMeanNewLine(trim(preg_replace("/\(Platform.*/", "", $stop['stop_name'])) . '(' . sizeof($stopsGrouped["stop_ids"]) . ' stops)');
echo "</a></li>\n"; echo "</a></li>\n";
flush(); @ob_flush(); flush(); @ob_flush();
$stopsGrouped = Array(); $stopsGrouped = Array();
} }
else { else {
// just a normal stop // just a normal stop
echo '<li>'; echo '<li>';
if (!startsWith($row[5], "Wj")) echo '<img src="css/images/time.png" alt="Timing Point" class="ui-li-icon">'; if (!startsWith($stop['stop_code'], "Wj")) echo '<img src="css/images/time.png" alt="Timing Point" class="ui-li-icon">';
echo '<a href="stop.php?stopid=' . $row[0] . (startsWith($row[5], "Wj") ? '&stopcode=' . $row[5] : "") . '">'; echo '<a href="stop.php?stopid=' . $stop['stop_id'] . (startsWith($stop['stop_code'], "Wj") ? '&stopcode=' . $stop['stop_code'] : "") . '">';
if (isset($_SESSION['lat']) && isset($_SESSION['lon'])) { if (isset($_SESSION['lat']) && isset($_SESSION['lon'])) {
echo '<span class="ui-li-count">' . distance($row[2], $row[3], $_SESSION['lat'], $_SESSION['lon'], true) . 'm away</span>'; echo '<span class="ui-li-count">' . distance($stop['stop_lat'],$stop['stop_lon'], $_SESSION['lat'], $_SESSION['lon'], true) . 'm away</span>';
} }
echo bracketsMeanNewLine($row[1]); echo bracketsMeanNewLine($stop['stop_name']);
echo "</a></li>\n"; echo "</a></li>\n";
flush(); @ob_flush(); flush(); @ob_flush();
} }
} }
else { else {
// this is a duplicated line item // this is a duplicated line item
if ($key - 1 <= 0 || (trim(preg_replace("/\(Platform.*/", "", $stops[$key][1])) != trim(preg_replace("/\(Platform.*/", "", $stops[$key - 1][1])))) { if ($key - 1 <= 0 || (trim(preg_replace("/\(Platform.*/", "", $stops[$key]['stop_name'])) != trim(preg_replace("/\(Platform.*/", "", $stops[$key - 1]['stop_name'])))) {
// first duplicate // first duplicate
$stopsGrouped = Array( $stopsGrouped = Array(
"name" => trim(preg_replace("/\(Platform.*/", "", $row[1])) , "name" => trim(preg_replace("/\(Platform.*/", "", $stop['stop_name'])) ,
"stop_ids" => Array( "stop_ids" => Array(
$row[0] $stop['stop_id']
) , ) ,
"stop_codes" => Array( "stop_codes" => Array(
$row[5] $stop['stop_code']
) )
); );
} }
else { else {
// subsequent duplicates // subsequent duplicates
$stopsGrouped["stop_ids"][] = $row[0]; $stopsGrouped["stop_ids"][] = $stop['stop_id'];;
} }
} }
} }
} }
echo '</ul>'; echo '</ul>';
} }
include_footer(); include_footer();
?> ?>
   
 Binary files /dev/null and b/transitdata.cbrfeed.sql.gz differ
file:a/trip.php -> file:b/trip.php
<?php <?php
include ('include/common.inc.php'); include ('include/common.inc.php');
$tripid = filter_var($_REQUEST['tripid'], FILTER_SANITIZE_NUMBER_INT); $tripid = filter_var($_REQUEST['tripid'], FILTER_SANITIZE_NUMBER_INT);
$stopid = filter_var($_REQUEST['stopid'], FILTER_SANITIZE_NUMBER_INT); $stopid = filter_var($_REQUEST['stopid'], FILTER_SANITIZE_NUMBER_INT);
$routeid = filter_var($_REQUEST['routeid'], FILTER_SANITIZE_NUMBER_INT); $routeid = filter_var($_REQUEST['routeid'], FILTER_SANITIZE_NUMBER_INT);
   
$routetrips = Array(); $routetrips = Array();
   
if ($_REQUEST['routeid'] && !$_REQUEST['tripid']) { if ($_REQUEST['routeid'] && !$_REQUEST['tripid']) {
$tripid = 0; $trip = getRouteNextTrip($routeid);
$url = $APIurl . "/json/routetrips?route_id=" . $routeid; $tripid = $trip['trip_id'];
$routetrips = json_decode(getPage($url)); } else {
foreach ($routetrips as $trip) { $trip = getTrip($tripid);
if ($trip[2] > midnight_seconds()) { $routeid = $trip["route_id"];
$tripid = $trip[1];  
break;  
}  
}  
if ($tripid == 0) $tripid = $routetrips[0][1];  
} }
$url = $APIurl . "/json/triprows?trip=" . $tripid;  
$trips = array_flatten(json_decode(getPage($url))); $routetrips = getRouteTrips($routeid);
if (sizeof($routetrips) == 0) {  
$routeid = $trips[1]->route_id; include_header("Stops on " . $trip['route_short_name'] . ' ' . $trip['route_long_name'], "trip");
$url = $APIurl . "/json/routetrips?route_id=" . $trips[1]->route_id; trackEvent("Route/Trip View","View Route", $trip['route_short_name'] . ' ' . $trip['route_long_name'], $routeid);
$routetrips = json_decode(getPage($url));  
}  
include_header("Stops on " . $trips[1]->route_short_name . ' ' . $trips[1]->route_long_name, "trip"); echo '<p><h2>Via:</h2> ' . viaPointNames($tripid) . '</small></p>';
trackEvent("Route/Trip View","View Route", $trips[1]->route_short_name . ' ' . $trips[1]->route_long_name, $trips[1]->route_id);  
$url = $APIurl . "/json/tripstoptimes?trip=" . $tripid;  
$json = json_decode(getPage($url));  
$stops = $json[0];  
$times = $json[1];  
$viaPoints = Array();  
foreach ($stops as $stop) {  
if (!startsWith($stop[5], "Wj")) {  
$viaPoints[] = $stop[1];  
}  
}  
echo '<p><h2>Via:</h2> ' . implode(", ", $viaPoints) . '</small></p>';  
echo '<p><h2>Other Trips:</h2> '; echo '<p><h2>Other Trips:</h2> ';
foreach ($routetrips as $othertrip) { foreach (getRouteTrips($routeid) as $othertrip) {
echo '<a href="trip.php?tripid=' . $othertrip[1] . "&routeid=" . $routeid . '">' . midnight_seconds_to_time($othertrip[0]) . '</a> '; echo '<a href="trip.php?tripid=' . $othertrip['trip_id'] . "&routeid=" . $routeid . '">' . str_replace(" ",":00",str_replace(":00"," ",$othertrip['arrival_time'])). '</a> ';
} }
flush(); @ob_flush(); flush(); @ob_flush();
echo '</p><p><h2>Other directions/timing periods:</h2> '; echo '</p><p><h2>Other directions/timing periods:</h2> ';
$url = $APIurl . "/json/routesearch?routeshortname=" . rawurlencode($trips[1]->route_short_name); foreach (getRoutesByNumber($trip['route_short_name']) as $row) {
$json = json_decode(getPage($url)); if ($row['route_id'] != $routeid) echo '<a href="trip.php?routeid=' . $row['route_id'] . '">' . $row['route_long_name'] . ' (' . ucwords($row['service_id']) . ')</a> ';
foreach ($json as $row) {  
if ($row[0] != $routeid) echo '<a href="trip.php?routeid=' . $row[0] . '">' . $row[2] . ' (' . ucwords($row[3]) . ')</a> ';  
} }
flush(); @ob_flush(); flush(); @ob_flush();
echo ' <ul data-role="listview" data-inset="true">'; echo ' <ul data-role="listview" data-inset="true">';
echo '<li data-role="list-divider">' . midnight_seconds_to_time($times[0]) . '-' . midnight_seconds_to_time($times[sizeof($times) - 1]) . ' ' . $trips[1]->route_long_name . '</li>';  
$stopsGrouped = Array(); $stopsGrouped = Array();
foreach ($stops as $key => $row) { $tripStopTimes = getTimeInterpolatedTrip($tripid);
if (($stops[$key][1] != $stops[$key + 1][1]) || $key + 1 >= sizeof($stops)) { echo '<li data-role="list-divider">' . $tripStopTimes[0]['arrival_time'] . ' to ' . $tripStopTimes[sizeof($tripStopTimes) - 1]['arrival_time'] . ' ' . $trips[1]->route_long_name . '</li>';
   
  foreach ($tripStopTimes as $key => $tripStopTime) {
  if (($tripStopTimes[$key]["stop_name"] != $tripStopTimes[$key + 1]["stop_name"]) || $key + 1 >= sizeof($tripStopTimes)) {
echo '<li>'; echo '<li>';
if (!startsWith($row[5], "Wj")) echo '<img src="css/images/time.png" alt="Timing Point" class="ui-li-icon">'; if (!startsWith($tripStopTime['stop_code'], "Wj")) echo '<img src="css/images/time.png" alt="Timing Point" class="ui-li-icon">';
if (sizeof($stopsGrouped) > 0) { if (sizeof($stopsGrouped) > 0) {
// print and empty grouped stops // print and empty grouped stops
// subsequent duplicates // subsequent duplicates
$stopsGrouped["stop_ids"][] = $row[0]; $stopsGrouped["stop_ids"][] = $tripStopTime['stop_id'];
$stopsGrouped["endTime"] = $times[$key]; $stopsGrouped["endTime"] = $tripStopTime['arrival_time'];
echo '<a href="stop.php?stopids=' . implode(",", $stopsGrouped['stop_ids']) . '">'; echo '<a href="stop.php?stopids=' . implode(",", $stopsGrouped['stop_ids']) . '">';
echo '<p class="ui-li-aside">' . midnight_seconds_to_time($stopsGrouped['startTime']) . ' to ' . midnight_seconds_to_time($stopsGrouped['endTime']) . '</p>'; echo '<p class="ui-li-aside">' . $stopsGrouped['startTime'] . ' to ' . $stopsGrouped['endTime'];
echo bracketsMeanNewLine($row[1]); echo '</p>';
  if (isset($_SESSION['lat']) && isset($_SESSION['lon'])) {
  echo '<span class="ui-li-count">' . distance($stop['stop_lat'],$stop['stop_lon'], $_SESSION['lat'], $_SESSION['lon'], true) . 'm away</span>';
  }
  echo bracketsMeanNewLine($tripStopTime["stop_name"]);
echo '</a></li>'; echo '</a></li>';
flush(); @ob_flush(); flush(); @ob_flush();
$stopsGrouped = Array(); $stopsGrouped = Array();
} }
else { else {
// just a normal stop // just a normal stop
echo '<a href="stop.php?stopid=' . $row[0] . (startsWith($row[5], "Wj") ? '&stopcode=' . $row[5] : "") . '">'; echo '<a href="stop.php?stopid=' . $tripStopTime['stop_id'] . (startsWith($tripStopTime['stop_code'], "Wj") ? '&stopcode=' . $tripStopTime['stop_code'] : "") . '">';
echo '<p class="ui-li-aside">' . midnight_seconds_to_time($times[$key]) . '</p>'; echo '<p class="ui-li-aside">' . $tripStopTime['arrival_time'] . '</p>';
echo bracketsMeanNewLine($row[1]); if (isset($_SESSION['lat']) && isset($_SESSION['lon'])) {
  echo '<span class="ui-li-count">' . distance($stop['stop_lat'],$stop['stop_lon'], $_SESSION['lat'], $_SESSION['lon'], true) . 'm away</span>';
  }
  echo bracketsMeanNewLine($tripStopTime['stop_name']);
echo '</a></li>'; echo '</a></li>';
flush(); @ob_flush(); flush(); @ob_flush();
} }
} }
else { else {
// this is a duplicated line item // this is a duplicated line item
if ($key - 1 <= 0 || ($stops[$key][1] != $stops[$key - 1][1])) { if ($key - 1 <= 0 || ($tripStopTimes[$key]['stop_name'] != $tripStopTimes[$key - 1]['stop_name'])) {
// first duplicate // first duplicate
$stopsGrouped = Array( $stopsGrouped = Array(
"name" => $row[1], "name" => $tripStopTime['stop_name'],
"startTime" => $times[$key], "startTime" => $tripStopTime['arrival_time'],
"stop_ids" => Array( "stop_ids" => Array(
$row[0] $tripStopTime['stop_id']
) )
); );
} }
else { else {
// subsequent duplicates // subsequent duplicates
$stopsGrouped["stop_ids"][] = $row[0]; $stopsGrouped["stop_ids"][] = $tripStopTime['stop_id'];
$stopsGrouped["endTime"] = $times[$key]; $stopsGrouped["endTime"] = $tripStopTime['arrival_time'];
} }
} }
} }
echo '</ul>'; echo '</ul>';
include_footer(); include_footer();
?> ?>
   
<?php <?php
include ('include/common.inc.php'); include ('include/common.inc.php');
include_header("Trip Planner", "tripPlanner", true, true, true); include_header("Trip Planner", "tripPlanner", true, true, true);
$from = (isset($_REQUEST['from']) ? filter_var($_REQUEST['from'], FILTER_SANITIZE_STRING) : "Brigalow"); $from = (isset($_REQUEST['from']) ? filter_var($_REQUEST['from'], FILTER_SANITIZE_STRING) : "");
$to = (isset($_REQUEST['to']) ? filter_var($_REQUEST['to'], FILTER_SANITIZE_STRING) : "Barry"); $to = (isset($_REQUEST['to']) ? filter_var($_REQUEST['to'], FILTER_SANITIZE_STRING) : "");
$date = (isset($_REQUEST['date']) ? filter_var($_REQUEST['date'], FILTER_SANITIZE_STRING) : date("m/d/Y")); $date = (isset($_REQUEST['date']) ? filter_var($_REQUEST['date'], FILTER_SANITIZE_STRING) : date("m/d/Y"));
$time = (isset($_REQUEST['time']) ? filter_var($_REQUEST['time'], FILTER_SANITIZE_STRING) : date("H:i")); $time = (isset($_REQUEST['time']) ? filter_var($_REQUEST['time'], FILTER_SANITIZE_STRING) : date("H:i"));
function formatTime($timeString) { function formatTime($timeString) {
$timeParts = explode("T",$timeString); $timeParts = explode("T",$timeString);
return str_replace("Z","",$timeParts[1]); return str_replace("Z","",$timeParts[1]);
} }
function tripPlanForm($errorMessage = "") function tripPlanForm($errorMessage = "")
{ {
   
global $date, $time, $from, $to; global $date, $time, $from, $to;
echo "<font color=red>$errorMessage</font>"; echo "<font color=red>$errorMessage</font>";
echo '<form action="tripPlanner.php" method="post"> echo '<form action="tripPlanner.php" method="post">
<div data-role="fieldcontain"> <div data-role="fieldcontain">
<label for="from">I would like to go from</label> <label for="from">I would like to go from</label>
<input type="text" name="from" id="from" value="' . $from . '" /> <input type="text" name="from" id="from" value="' . $from . '" />
<a href="#" style="display:none" name="fromHere" id="fromHere"/>Here?</a> <a href="#" style="display:none" name="fromHere" id="fromHere"/>Here?</a>
</div> </div>
<div data-role="fieldcontain"> <div data-role="fieldcontain">
<label for="to"> to </label> <label for="to"> to </label>
<input type="text" name="to" id="to" value="' . $to . '" /> <input type="text" name="to" id="to" value="' . $to . '" />
<a href="#" style="display:none" name="toHere" id="toHere"/>Here?</a> <a href="#" style="display:none" name="toHere" id="toHere"/>Here?</a>
</div> </div>
<div data-role="fieldcontain"> <div data-role="fieldcontain">
<label for="date"> on </label> <label for="date"> on </label>
<input type="text" name="date" id="date" value="' . $date . '" /> <input type="text" name="date" id="date" value="' . $date . '" />
</div> </div>
<div data-role="fieldcontain"> <div data-role="fieldcontain">
<label for="time"> at </label> <label for="time"> at </label>
<input type="time" name="time" id="time" value="' . $time . '" /> <input type="time" name="time" id="time" value="' . $time . '" />
</div> </div>
<input type="submit" value="Go!"></form>'; <input type="submit" value="Go!"></form>';
echo "<script> echo "<script>
$('#toHere').click(function(event) { $('#to').val(getCookie('geolocate')); return false;}); $('#toHere').click(function(event) { $('#to').val(getCookie('geolocate')); return false;});
$('#toHere').show(); $('#toHere').show();
$('#fromHere').click(function(event) { $('#from').val(getCookie('geolocate')); return false;}); $('#fromHere').click(function(event) { $('#from').val(getCookie('geolocate')); return false;});
$('#fromHere').show(); $('#fromHere').show();
   
</script>"; </script>";
} }
function processItinerary($itineraryNumber, $itinerary) function processItinerary($itineraryNumber, $itinerary)
{ {
echo '<div data-role="collapsible" ' . ($itineraryNumber > 0 ? 'data-collapsed="true"' : "") . '> <h3> Option #' . ($itineraryNumber + 1) . ": " . floor($itinerary->duration / 60000) . " minutes (".formatTime($itinerary->startTime)." to ".formatTime($itinerary->endTime).")</h3><p>"; echo '<div data-role="collapsible" ' . ($itineraryNumber > 0 ? 'data-collapsed="true"' : "") . '> <h3> Option #' . ($itineraryNumber + 1) . ": " . floor($itinerary->duration / 60000) . " minutes (".formatTime($itinerary->startTime)." to ".formatTime($itinerary->endTime).")</h3><p>";
echo "Walking time: " . floor($itinerary->walkTime / 60000) . " minutes (" . floor($itinerary->walkDistance) . " meters)<br>\n"; echo "Walking time: " . floor($itinerary->walkTime / 60000) . " minutes (" . floor($itinerary->walkDistance) . " meters)<br>\n";
echo "Transit time: " . floor($itinerary->transitTime / 60000) . " minutes<br>\n"; echo "Transit time: " . floor($itinerary->transitTime / 60000) . " minutes<br>\n";
echo "Waiting time: " . floor($itinerary->waitingTime / 60000) . " minutes<br>\n"; echo "Waiting time: " . floor($itinerary->waitingTime / 60000) . " minutes<br>\n";
if (is_array($itinerary->legs->leg)) { if (is_array($itinerary->legs->leg)) {
$legMarkers = array(); $legMarkers = array();
foreach ($itinerary->legs->leg as $legNumber => $leg) { foreach ($itinerary->legs->leg as $legNumber => $leg) {
$legMarkers[] = array( $legMarkers[] = array(
$leg->from->lat, $leg->from->lat,
$leg->from->lon $leg->from->lon
); );
} }
echo '' . staticmap($legMarkers, 0, "iconb", false) . "<br>\n"; echo '' . staticmap($legMarkers, 0, "iconb", false) . "<br>\n";
echo '<ul>'; echo '<ul>';
foreach ($itinerary->legs->leg as $legNumber => $leg) { foreach ($itinerary->legs->leg as $legNumber => $leg) {
echo '<li>'; echo '<li>';
processLeg($legNumber, $leg); processLeg($legNumber, $leg);
echo "</li>"; echo "</li>";
flush(); @ob_flush(); flush(); @ob_flush();
} }
echo "</ul>"; echo "</ul>";
} }
else { else {
echo '' . staticmap(array( echo '' . staticmap(array(
array( array(
$itinerary->legs->leg->from->lat, $itinerary->legs->leg->from->lat,
$itinerary->legs->leg->from->lon $itinerary->legs->leg->from->lon
) )
) , 0, "iconb", false) . "<br>\n"; ) , 0, "iconb", false) . "<br>\n";
processLeg(0, $itinerary->legs->leg); processLeg(0, $itinerary->legs->leg);
} }
echo "</p></div>"; echo "</p></div>";
} }
function processLeg($legNumber, $leg) function processLeg($legNumber, $leg)
{ {
$legArray = object2array($leg); $legArray = object2array($leg);
echo '<h3>Leg #' . ($legNumber + 1) . " ( {$legArray['@mode']} from: {$leg->from->name} to {$leg->to->name}, " . floor($leg->duration / 60000) . " minutes) </h3>\n"; echo '<h3>Leg #' . ($legNumber + 1) . " ( {$legArray['@mode']} from: {$leg->from->name} to {$leg->to->name}, " . floor($leg->duration / 60000) . " minutes) </h3>\n";
if ($legArray["@mode"] === "BUS") { if ($legArray["@mode"] === "BUS") {
echo "Take bus {$legArray['@route']} " . str_replace("To", "towards", $legArray['@headsign']) . "<br>"; echo "Take bus {$legArray['@route']} " . str_replace("To", "towards", $legArray['@headsign']) . "<br>";
} }
else { else {
$walkStepMarkers = array(); $walkStepMarkers = array();
foreach ($leg->steps->walkSteps as $stepNumber => $step) { foreach ($leg->steps->walkSteps as $stepNumber => $step) {
$walkStepMarkers[] = array( $walkStepMarkers[] = array(
$step->lat, $step->lat,
$step->lon $step->lon
); );
} }
echo "" . staticmap($walkStepMarkers, 0, "icong", false) . "<br>\n"; echo "" . staticmap($walkStepMarkers, 0, "icong", false) . "<br>\n";
foreach ($leg->steps->walkSteps as $stepNumber => $step) { foreach ($leg->steps->walkSteps as $stepNumber => $step) {
echo "Walking step " . ($stepNumber + 1) . ": "; echo "Walking step " . ($stepNumber + 1) . ": ";
if ($step->relativeDirection == "CONTINUE") { if ($step->relativeDirection == "CONTINUE") {
echo "Continue, "; echo "Continue, ";
} else if ($step->relativeDirection) echo "Turn ".ucwords(strtolower(str_replace("_"," ",$step->relativeDirection))).", "; } else if ($step->relativeDirection) echo "Turn ".ucwords(strtolower(str_replace("_"," ",$step->relativeDirection))).", ";
echo "Go ".ucwords(strtolower($step->absoluteDirection))." on "; echo "Go ".ucwords(strtolower($step->absoluteDirection))." on ";
if (strpos($step->streetName,"from") !== false && strpos($step->streetName,"way") !== false) { if (strpos($step->streetName,"from") !== false && strpos($step->streetName,"way") !== false) {
echo "footpath"; echo "footpath";
} else { } else {
echo $step->streetName; echo $step->streetName;
} }
echo " for " . floor($step->distance) . " meters<br>\n"; echo " for " . floor($step->distance) . " meters<br>\n";
} }
} }
} }
if ($_REQUEST['time']) { if ($_REQUEST['time']) {
$toPlace = (startsWith($to, "-") ? $to : geocode($to, false)); $toPlace = (startsWith($to, "-") ? $to : geocode($to, false));
$fromPlace = (startsWith($from, "-") ? $from : geocode($from, false)); $fromPlace = (startsWith($from, "-") ? $from : geocode($from, false));
if ($toPlace == "" || $fromPlace == "") { if ($toPlace == "" || $fromPlace == "") {
$errorMessage = ""; $errorMessage = "";
if ($toPlace === "") { if ($toPlace === "") {
$errorMessage.= urlencode($to) . " not found.<br>\n"; $errorMessage.= urlencode($to) . " not found.<br>\n";
trackEvent("Trip Planner","Geocoder Failed", $to); trackEvent("Trip Planner","Geocoder Failed", $to);
} }
if ($fromPlace === "") { if ($fromPlace === "") {
$errorMessage.= urlencode($from) . " not found.<br>\n"; $errorMessage.= urlencode($from) . " not found.<br>\n";
trackEvent("Trip Planner","Geocoder Failed", $from); trackEvent("Trip Planner","Geocoder Failed", $from);
} }
tripPlanForm($errorMessage); tripPlanForm($errorMessage);
} }
else { else {
$url = $otpAPIurl . "ws/plan?date=" . urlencode($_REQUEST['date']) . "&time=" . urlencode($_REQUEST['time']) . "&mode=TRANSIT%2CWALK&optimize=QUICK&maxWalkDistance=840&wheelchair=false&toPlace=$toPlace&fromPlace=$fromPlace&intermediatePlaces="; $url = $otpAPIurl . "ws/plan?date=" . urlencode($_REQUEST['date']) . "&time=" . urlencode($_REQUEST['time']) . "&mode=TRANSIT%2CWALK&optimize=QUICK&maxWalkDistance=840&wheelchair=false&toPlace=$toPlace&fromPlace=$fromPlace&intermediatePlaces=";
$ch = curl_init($url); debug($url);
  $ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_HTTPHEADER, array( curl_setopt($ch, CURLOPT_HTTPHEADER, array(
"Accept: application/json" "Accept: application/json"
)); ));
curl_setopt($ch, CURLOPT_TIMEOUT, 5); curl_setopt($ch, CURLOPT_TIMEOUT, 10);
$page = curl_exec($ch); $page = curl_exec($ch);
if (curl_errno($ch)) { if (curl_errno($ch) || curl_getinfo($ch, CURLINFO_HTTP_CODE) != 200) {
tripPlanForm("Trip planner temporarily unavailable: " . curl_errno($ch) . " " . curl_error($ch) .(isDebug() ? $url : "")); tripPlanForm("Trip planner temporarily unavailable: " . curl_errno($ch) . " " . curl_error($ch) . " ". curl_getinfo($ch, CURLINFO_HTTP_CODE) .(isDebug() ? "<br>".$url : ""));
trackEvent("Trip Planner","Trip Planner Failed", $url); trackEvent("Trip Planner","Trip Planner Failed", $url);
} }
else { else {
trackEvent("Trip Planner","Plan Trip From", $from); trackEvent("Trip Planner","Plan Trip From", $from);
trackEvent("Trip Planner","Plan Trip To", $to); trackEvent("Trip Planner","Plan Trip To", $to);
$tripplan = json_decode($page); $tripplan = json_decode($page);
debug(print_r($triplan, true)); debug(print_r($tripplan, true));
echo "<h1> From: {$tripplan->plan->from->name} To: {$tripplan->plan->to->name} </h1>"; echo "<h1> From: {$tripplan->plan->from->name} To: {$tripplan->plan->to->name} </h1>";
echo "<h1> At: ".formatTime($tripplan->plan->date)." </h1>"; echo "<h1> At: ".formatTime($tripplan->plan->date)." </h1>";
if (is_array($tripplan->plan->itineraries->itinerary)) { if (is_array($tripplan->plan->itineraries->itinerary)) {
echo '<div data-role="collapsible-set">'; echo '<div data-role="collapsible-set">';
foreach ($tripplan->plan->itineraries->itinerary as $itineraryNumber => $itinerary) { foreach ($tripplan->plan->itineraries->itinerary as $itineraryNumber => $itinerary) {
processItinerary($itineraryNumber, $itinerary); processItinerary($itineraryNumber, $itinerary);
} }
echo "</div>"; echo "</div>";
} }
else { else {
processItinerary(0, $tripplan->plan->itineraries->itinerary); processItinerary(0, $tripplan->plan->itineraries->itinerary);
} }
} }
curl_close($ch); curl_close($ch);
} }
} }
else { else {
tripPlanForm(); tripPlanForm();
} }
include_footer(); include_footer();
?> ?>
   
file:b/updatedb.php (new)
  <?php
  include('include/common-db.inc.php');
  // Unzip cbrfeed.zip, import all csv files to database
  $unzip = true;
  $zip = zip_open(dirname(__FILE__) . "/cbrfeed.zip");
  $tmpdir = "/tmp/cbrfeed/";
  mkdir($tmpdir);
  if ($unzip) {
  if (is_resource($zip)) {
  while ($zip_entry = zip_read($zip)) {
  $fp = fopen($tmpdir . zip_entry_name($zip_entry) , "w");
  if (zip_entry_open($zip, $zip_entry, "r")) {
  echo "Extracting " . zip_entry_name($zip_entry) . "\n";
  $buf = zip_entry_read($zip_entry, zip_entry_filesize($zip_entry));
  fwrite($fp, "$buf");
  zip_entry_close($zip_entry);
  fclose($fp);
  }
  }
  zip_close($zip);
  }
  }
 
  foreach (scandir($tmpdir) as $file) {
  if (!strpos($file, ".txt") === false) {
  $fieldseparator = ",";
  $lineseparator = "\n";
  $tablename = str_replace(".txt", "", $file);
  echo "Opening $file \n";
  $line = 0;
  $handle = fopen($tmpdir . $file, "r");
  while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {
  if ($line > 0) {
  $query = "insert into $tablename values(";
  $valueCount = 0;
  foreach ($data as $value) {
  $query.=($valueCount >0 ? "','" :"'").pg_escape_string($value);
  $valueCount++;
  }
  if ($tablename == "stops") {
  $query.= "', ST_GeographyFromText('SRID=4326;POINT({$data[2]} {$data[0]})'));";
  } else {
  $query.= "');";
  }
  if ($tablename =="stop_times" && $data[1] == "") {
  $query = "insert into $tablename (trip_id,stop_id,stop_sequence) values('{$data[0]}','{$data[3]}','{$data[4]}');";
  }
 
  }
  $result = pg_query($conn, $query);
  $line++;
  if ($line % 10000 == 0) echo "$line records... \n";
  }
  fclose($handle);
  echo "Found a total of $line records in $file.\n";
 
  }
  }
  ?>
 
file:a/view.sh (deleted)
#!/bin/sh  
f=`dirname $0`  
cd $f  
python schedule_viewer.py --feed=/var/www/cbrfeed.zip \  
--key=ABQIAAAA95XYXN0cki3Yj_Sb71CFvBTPaLd08ONybQDjcH_VdYtHHLgZvRTw2INzI_m17_IoOUqH3RNNmlTk1Q