Repository: geocommons/geocoder
Branch: master
Commit: 2500432a06d3
Files: 137
Total size: 12.4 MB
Directory structure:
gitextract_g0bu7lk9/
├── .gitignore
├── History.txt
├── LICENSE.txt
├── Makefile
├── Manifest.txt
├── README.rdoc
├── REST.rdoc
├── TODO.txt
├── bin/
│ └── rebuild_metaphones
├── build/
│ ├── build_indexes
│ ├── rebuild_cluster
│ ├── sql/
│ │ ├── cluster.sql
│ │ ├── convert.sql
│ │ ├── create.sql
│ │ ├── index.sql
│ │ ├── place.sql
│ │ └── setup.sql
│ ├── tiger2009_import
│ └── tiger_import
├── conf/
│ ├── geocoder-us/
│ │ ├── geocoder.ru
│ │ └── unicorn.rb
│ └── init/
│ └── geocoder-us.conf
├── debian/
│ ├── README.Debian
│ ├── changelog
│ ├── compat
│ ├── control
│ ├── copyright
│ ├── default
│ ├── docs
│ ├── geocoder-us.postinst
│ ├── geocoder-us.prerm
│ ├── rules
│ └── source/
│ └── format
├── demos/
│ ├── api/
│ │ ├── server.rb
│ │ └── views/
│ │ └── index.erb
│ ├── cli.rb
│ ├── demo/
│ │ ├── app/
│ │ │ ├── ext/
│ │ │ │ └── geocodewrap.rb
│ │ │ └── views/
│ │ │ ├── index.builder
│ │ │ └── index.erb
│ │ ├── config/
│ │ │ ├── bootstraps.rb
│ │ │ └── geoenvironment.rb
│ │ ├── config.ru
│ │ ├── geocoder_helper.rb
│ │ ├── geocom_geocode.rb
│ │ ├── main.rb
│ │ ├── rakefile.rb
│ │ └── tmp/
│ │ └── restart.txt
│ ├── parse.rb
│ └── simpledemo/
│ ├── views/
│ │ ├── index.builder
│ │ └── index.erb
│ └── ws.rb
├── doc/
│ ├── Makefile
│ ├── html4css1.css
│ ├── lookup.rst
│ ├── parsing.rst
│ └── voidspace.css
├── gemspec
├── lib/
│ └── geocoder/
│ ├── us/
│ │ ├── address.rb
│ │ ├── constants.rb
│ │ ├── database.rb
│ │ ├── metaphone.rb
│ │ ├── numbers.rb
│ │ └── rest.rb
│ └── us.rb
├── navteq/
│ ├── README
│ ├── convert.sql
│ ├── navteq_import
│ └── prepare.sql
├── setup.rb
├── src/
│ ├── Makefile
│ ├── README
│ ├── liblwgeom/
│ │ ├── Makefile
│ │ ├── box2d.c
│ │ ├── lex.yy.c
│ │ ├── liblwgeom.h
│ │ ├── lwalgorithm.c
│ │ ├── lwalgorithm.h
│ │ ├── lwcircstring.c
│ │ ├── lwcollection.c
│ │ ├── lwcompound.c
│ │ ├── lwcurvepoly.c
│ │ ├── lwgeom.c
│ │ ├── lwgeom_api.c
│ │ ├── lwgparse.c
│ │ ├── lwgunparse.c
│ │ ├── lwline.c
│ │ ├── lwmcurve.c
│ │ ├── lwmline.c
│ │ ├── lwmpoint.c
│ │ ├── lwmpoly.c
│ │ ├── lwmsurface.c
│ │ ├── lwpoint.c
│ │ ├── lwpoly.c
│ │ ├── lwsegmentize.c
│ │ ├── lwutil.c
│ │ ├── measures.c
│ │ ├── postgis_config.h
│ │ ├── ptarray.c
│ │ ├── vsprintf.c
│ │ ├── wktparse.h
│ │ ├── wktparse.lex
│ │ ├── wktparse.tab.c
│ │ ├── wktparse.tab.h
│ │ └── wktparse.y
│ ├── libsqlite3_geocoder/
│ │ ├── Makefile
│ │ ├── Makefile.nix
│ │ ├── Makefile.redhat
│ │ ├── extension.c
│ │ ├── extension.h
│ │ ├── levenshtein.c
│ │ ├── metaphon.c
│ │ ├── util.c
│ │ └── wkb_compress.c
│ ├── metaphone/
│ │ ├── Makefile
│ │ ├── README
│ │ ├── extension.c
│ │ └── metaphon.c
│ └── shp2sqlite/
│ ├── Makefile
│ ├── Makefile.macosx
│ ├── Makefile.nix
│ ├── Makefile.redhat
│ ├── dbfopen.c
│ ├── getopt.c
│ ├── getopt.h
│ ├── shapefil.h
│ ├── shp2sqlite.c
│ └── shpopen.c
└── test/
├── address.rb
├── benchmark.rb
├── constants.rb
├── data/
│ ├── address-sample.csv
│ ├── db-test.csv
│ └── locations.csv
├── database.rb
├── generate.rb
├── numbers.rb
└── run.rb
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
*.o
*.so
*.gem
pkg/
bin/shp2sqlite
src/shp2sqlite/shp2sqlite
src/liblwgeom/liblwgeom.a
doc/*.html
*.log
================================================
FILE: History.txt
================================================
=== 1.0.0 / 2009-06-02
* 1 major enhancement
* Birthday!
================================================
FILE: LICENSE.txt
================================================
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.
================================================
FILE: Makefile
================================================
all:
make -C src install
gem build gemspec
test: all
ruby -Ilib tests/run.rb
install: all
# gem install *.gem
clean:
make -C src clean
rm -f lib/geocoder/us/sqlite3.so
rm -f *.gem
================================================
FILE: Manifest.txt
================================================
History.txt
Manifest.txt
README.rdoc
Rakefile
lib/geocoder/us/database.rb
lib/geocoder/us/numbers.rb
lib/geocoder/us/address.rb
lib/geocoder/us/constants.rb
tests/database.rb
tests/numbers.rb
tests/generate.rb
tests/run.rb
tests/address.rb
tests/benchmark.rb
tests/constants.rb
tests/data/address-sample.csv
tests/data/locations.csv
tests/data/db-test.csv
================================================
FILE: README.rdoc
================================================
= Geocoder::US
Geocoder::US 2.0 is a software package designed to geocode US street
addresses. Although it is primarily intended for use with the US Census
Bureau's free TIGER/Line dataset, it uses an abstract US address data model
that can be employed with other sources of US street address range data.
Geocoder::US 2.0 implements a Ruby interface to parse US street addresses, and
perform fuzzy lookup against an SQLite 3 database. Geocoder::US is designed to
return the best matches found, with geographic coordinates interpolated from
the street range dataset. Geocoder::US will fill in missing information, and
it knows about standard and common non-standard postal abbreviations, ordinal
versus cardinal numbers, and more.
Geocoder::US 2.0 is shipped with a free US ZIP code data set, compiled from
public domain sources.
== Synopsis
>> require 'geocoder/us'
>> db = Geocoder::US::Database.new("/opt/tiger/geocoder.db")
>> p db.geocode("1600 Pennsylvania Av, Washington DC")
[{:pretyp=>"", :street=>"Pennsylvania", :sufdir=>"NW", :zip=>"20502",
:lon=>-77.037528, :number=>"1600", :fips_county=>"11001", :predir=>"",
:precision=>:range, :city=>"Washington", :lat=>38.898746, :suftyp=>"Ave",
:state=>"DC", :prequal=>"", :sufqual=>"", :score=>0.906, :prenum=>""}]
== Web service
There is a small example webservice included in the gem at lib/geocoder/rest.rb. It requires Sinatra to run as well as setting the GEOCODER_DB environment variable. To run it:
GEOCODER_DB=/path/to/geocoder/geocoder.db ruby /path/to/gem/geocoder/lib/geocoder/us/rest.rb
You can then query the GeoCommons geocoder by calling the web service like:
http://localhost:9393/geocode.html?q=303+11th St NE,Washington,DC
== Optional Options
dbtype Geocoder::US::Database.new("/opt/tiger/geocoder.db", {:dbtype => 1})
The dbtype option is used when your datasbase encodes it's geometry blogs according to a special format. The
first and default value, is in a series of little-endian 4-byte ints ("V*")
The second is for ('CVVD*') format. Use option value 2 for this second type
debug Geocoder::US::Database.new("/opt/tiger/geocoder.db", {:debug => false})
cache Geocoder::US::Database.new("/opt/tiger/geocoder.db", {:cache => 50000})
The cache_size argument is measured in kilobytes and is used to set the SQLite cache size;
larger values will trade memory for speed in long-running processes.
== Prerequisites
To build Geocoder::US, you will need gcc/g++, make, bash or equivalent, the
standard *NIX 'unzip' utility, and the SQLite 3 executable and development
files installed on your system.
To use the Ruby interface, you will need the 'Text' gem installed from
rubyforge. To run the tests, you will also need the 'fastercsv' gem.
Additionally, you will need a custom build of the 'sqlite3-ruby' gem that
supports loading extension modules in SQLite. You can get a patched version of
this gem from http://github.com/schuyler/sqlite3-ruby/. Until the sqlite3-ruby
maintainers roll in the relevant patch, you will need *this* version.
*NOTE*: If you do not have /usr/include/sqlite3ext.h installed, then your
sqlite3 binaries are probably not configured to support dynamic extension
loading. If not, you *must* compile and install SQLite from source, or rebuild
your system packages. This is not believed to be a problem on Debian/Ubuntu,
but is known to be a problem with Red Hat/CentOS.
*NOTE*: If you *do* have to install from source, make sure that the
source-installed 'sqlite3' program is in your path before proceeding (and not
the system-installed version), using `which sqlite3`. Also, be sure that you've
added your source install prefix (usually /usr/local) to /etc/ld.so.conf (or
its moral equivalent) and that you've run /sbin/ldconfig.
== Thread safety
SQLite 3 is not designed for concurrent use of a single database handle across
multiple threads. Therefore, to prevent segfaults, Geocoder::US::Database
implements a global mutex that wraps all database access. The use of this mutex
will ensure stability in multi-threaded applications, but incurs a performance
penalty. However, since the database is read-only from Ruby, there's no reason
in principle why multi-threaded apps can't each have their own database handle.
To disable the mutex for better performance, you can do the following:
* Read the following and make sure you understand them:
* http://www.sqlite.org/faq.html#q6
* http://www.sqlite.org/cvstrac/wiki?p=MultiThreading
* Make sure you have compiled SQLite 3 with thread safety enabled.
* Instantiate a separate Geocoder::US::Database object for *each* thread
in your Ruby script, and pass :threadsafe => true to new() to disable mutex
synchronization.
Per the SQLite 3 documentation, do *not* attempt to retain a
Geocoder::US::Database object across a fork! "Problems will result if you do."
== Building Geocoder::US
Unpack the source and run 'make'. This will compile the SQLite 3 extension
needed by Geocoder::US, the Shapefile import utility, and the Geocoder-US
gem.
You can run 'make install' as root to install the gem systemwide.
== Generating a Geocoder::US Database
Build the package from source as described above. Generating the database
involves three basic steps:
* Import the Shapefile data into an SQLite database.
* Build the database indexes.
* Optionally, rebuild the database to cluster indexed rows.
We will presume that you are building a Geocoder::US database from TIGER/Line,
and that you have obtained the complete set of TIGER/Line ZIP files, and put
the entire tree in /opt/tiger. Please adjust these instructions as needed.
A full TIGER/Line database import takes ten hours to run on a normal Amazon
EC2 instance, and takes up a little over 5 gigabytes after all is said and
done. You will need to have at least 12 gigabytes of free disk space *after*
downloading the TIGER/Line dataset, if you are building the full database.
=== Import TIGER/Line
From inside the Geocoder::US source tree, run the following:
$ build/tiger_import /opt/tiger/geocoder.db /opt/tiger
This will unpack each TIGER/Line ZIP file to a temporary directory, and
perform the extract/transform/load sequence to incrementally build the
database. The process takes about 10-12 hours on a normal Amazon EC2 instance,
or about 5 CPU hours flat out on a modern PC. Note that not all TIGER/Line
source files contain address range information, so you will see error messages
for some counties, but this is normal.
If you only want to import specific counties, you can pipe a list of
TIGER/Line county directories to tiger_import on stdin. For example,
the following will install just the data for the state of Delaware:
$ ls -d /opt/tiger/10_DELAWARE/1* | build/tiger_import ~/delaware.db
The tiger_import process uses a binary utility, shp2sqlite, which is derived
from shp2pgsql, which ships with PostGIS. The shp2sqlite utility converts
.shp and .dbf files into SQL suitable for import into SQLite. This SQL
is then piped into the sqlite3 command line tool, where it is loaded into
temporary tables, and then a set of static SQL statements (kept in the sql/
directory) are used to transform this data and import it into the database
itself.
== Build metaphones using Ruby metaphone
run bin/rebuild_metaphones /opt/tiger/geocoder.db
This creates the metaphones using Ruby's metaphone function and will produce better geocoding results.
=== Build the indexes
After the database import is complete, you will want to construct the database
indexes:
$ build/build_indexes /opt/tiger/geocoder.db
This process takes 25 minutes on an EC2 instance (8 CPU minutes), but it's a
*lot* faster than building the indexes incrementally during the import
process. Basically, this process simply feeds SQL statements to the sqlite3
utility to construct the indexes on the existing database.
=== Cluster the database tables (optional)
As a final optional step, you can cluster the database tables according to
their indexes, which will make the database smaller, and lookups faster. This
process will take an hour or two, and may be a micro-optimization.
$ build/rebuild_cluster /opt/tiger/geocoder.db
You will need as much free disk space to run rebuild_cluster as the database
takes up, because the process essentially reconstructs the database in a new
file, and then it renames the new database over top of the old.
== Running the unit tests
From within the source tree, you can run the following:
$ ruby tests/run.rb
This tests the libraries, except for the database routines. If you have a
database built, you can run the test harness like so:
$ ruby tests/run.rb /opt/tiger/geocoder.db
The full test suite may take 30 or so seconds to run completely.
== License
Geocoder::US 2.0 was based on earlier work by Schuyler Erle on
a Perl module of the same name. You can find it at
http://search.cpan.org/~sderle/.
Geocoder::US 2.0 was written by Schuyler Erle, of Entropy Free LLC,
with the gracious support of FortiusOne, Inc. Please send bug reports,
patches, kudos, etc. to patches at geocoder.us.
Copyright (c) 2009 FortiusOne, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
================================================
FILE: REST.rdoc
================================================
GET /1.0/geocode/address.json
The geocode/address endpoint returns the interpolated latitude and longitude of
a US street address or street intersection. When given a US city or ZIP code,
the approximate center point of that place will be returned instead.
The geocoder attempts to return the most accurate possible result, including,
where possible, correcting the given street type, city, or postal code, and
identifying and correcting misspellings in the street or city name in the given
address.
Currently, address geocoding only works in the United States.
Parameters:
q = a string containing a US street address.
Returns a GeoJSON feature collection:
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"number": "41",
"street": "Decatur St",
"city": "San Francisco",
"state": "CA",
"zip": "94103",
"fips_county": "06075",
"score": 1.0,
"precision":"range"
},
"geometry": {
"type": "Point",
"coordinates": [-122.406032, 37.772502]
}
}
],
"address":"41 Decatur St, San Francisco CA 94103"
}
Each address match in the feature collection contains some combination of the
following properties:
number
The building number of the address. When a building number is not
included in a range stored in the address database, the nearest
known building number will be returned in its place.
street
The name of the street found in the database that matches the address,
given in a normalized form.
street1 / street2
When an address is parsed as an intersection, the intersecting streets
are returned as `street1` and `street2` in place of the `number` and
`street` fields.
city
The city matching the given address. In the US, this is typically
determined from the matching ZIP code, so, for ZIP codes that cover
more than one named place, the results may be different from what you
expect, but will still be suitable for postal addressing.
state
The two letter postal abbreviation of the state containing the matching
address.
zip
In the US, the five digit ZIP code of the matching address.
plus4
In the US, the ZIP+4 extension parsed from the address, if any. This
extension is not actually used in the geocoding process, but is
returned for convenience.
fips_county
In the US, the FIPS 6-4 code of the county containing the address.
prenum / sufnum
If the building number has a non-numeric prefix, it will be returned in
`prenum`. Ditto `sufnum` for non-numeric suffixes.
precision
The qualitative precision of the geocode. The value will be one of
`intersection`, 'range`, `street`, `zip`, or `city`.
score
The percentage of text match between the given address and the geocoded
result, expressed as a float between 0 and 1. A higher score indicates
a closer match. Results with a score below 0.5 should be regarded with
care.
================================================
FILE: TODO.txt
================================================
1. Check interpolate measure: scale longitude or not?
5. Intersections...
- import ALL linestrings (even those with without ranges)
- throw away internal points on lines that don't have ranges
7. Documentation (*)
8. Make SQLite memory cache size an option to the Database constructor
9. Precision and accuracy measure
10. Street line set back
================================================
FILE: bin/rebuild_metaphones
================================================
#!/usr/bin/ruby
require 'rubygems'
require 'sqlite3'
require 'text'
if(ARGV.length < 1)
print "Missing SQLite file parameter"
printf "\nUSAGE:\n"
print """./rebuild_metaphones [SQLite File]
[SQLite File] - SQLite file generated by the geocoder.
"""
exit
end
@db = SQLite3::Database.new(ARGV[0])
@db.create_function("metaphone", 2) do |func, string, len|
test = string.to_s.gsub(/\W/o, "")
if test =~ /^(\d+)/o
mph = $1
elsif test =~ /^([wy])$/io
mph = $1
else
mph = Text::Metaphone.metaphone test
end
func.result = mph[0...len.to_i]
end
sql = "update place set city_phone = metaphone(city,5)"
@db.execute sql
@db.close
================================================
FILE: build/build_indexes
================================================
#!/bin/bash
BASE=$(dirname $0)
PATH=$PATH:$BASE/bin
SQL="$BASE/sql"
# Just run the SQL that constructs the indexes.
sqlite3 $1 < ${SQL}/index.sql
================================================
FILE: build/rebuild_cluster
================================================
#!/bin/bash
BASE=$(dirname $0)
PATH=$PATH:$BASE/bin
SQL="$BASE/sql"
OLD_DB=$1
DATABASE=${OLD_DB}.$$
[ -r $DATABASE ] && echo "$DATABASE already exists." && exit -1
[ ! -r $OLD_DB ] && echo "Can't read $OLD_DB." && exit -1
# Create a shiny new database, attach the old one,
# extract the data from it, and then index that.
# Finally, overwrite the old database with the new one.
( cat ${SQL}/create.sql && \
echo "ATTACH DATABASE '${OLD_DB}' AS old;" && \
cat ${SQL}/cluster.sql && \
echo "DETACH DATABASE old;" && \
cat ${SQL}/index.sql && \
echo "ANALYZE;" ) | sqlite3 $DATABASE \
&& mv $DATABASE $OLD_DB
================================================
FILE: build/sql/cluster.sql
================================================
.echo on
-- turn off various pragmas to make SQLite faster
PRAGMA temp_store=MEMORY;
PRAGMA journal_mode=OFF;
PRAGMA synchronous=OFF;
PRAGMA cache_size=500000;
PRAGMA count_changes=0;
BEGIN TRANSACTION;
-- order the contents of each table by their indexes to reduce
-- the number of disk pages that need to be read on each query.
INSERT INTO place SELECT * FROM old.place ORDER BY zip, priority;
INSERT INTO edge SELECT * FROM old.edge ORDER BY tlid;
INSERT INTO feature SELECT * FROM old.feature ORDER BY street_phone, zip;
INSERT INTO feature_edge SELECT * FROM old.feature_edge ORDER BY fid;
INSERT INTO range SELECT * FROM old.range ORDER BY tlid;
COMMIT;
================================================
FILE: build/sql/convert.sql
================================================
BEGIN;
-- start by indexing the temporary tables created from the input data.
CREATE INDEX featnames_tlid ON tiger_featnames (tlid);
CREATE INDEX addr_tlid ON tiger_addr (tlid);
CREATE INDEX edges_tlid ON tiger_edges (tlid);
-- generate a summary table matching each edge to one or more ZIPs
-- for those edges that are streets and have a name
CREATE TEMPORARY TABLE linezip AS
SELECT DISTINCT tlid, zip FROM (
SELECT tlid, zip FROM tiger_addr a
UNION
SELECT tlid, zipr AS zip FROM tiger_edges e
WHERE e.mtfcc LIKE 'S%' AND zipr <> "" AND zipr IS NOT NULL
UNION
SELECT tlid, zipl AS zip FROM tiger_edges e
WHERE e.mtfcc LIKE 'S%' AND zipl <> "" AND zipl IS NOT NULL
) AS whatever;
CREATE INDEX linezip_tlid ON linezip (tlid);
-- generate features from the featnames table for each desired edge
-- computing the metaphone hash of the name in the process.
-- CREATE TEMPORARY TABLE sqlite_sequence (
-- name VARCHAR(255),
-- seq INTEGER);
CREATE TEMPORARY TABLE feature_bin (
fid INTEGER PRIMARY KEY AUTOINCREMENT,
street VARCHAR(100),
street_phone VARCHAR(5),
paflag BOOLEAN,
zip CHAR(5));
INSERT OR IGNORE INTO sqlite_sequence (name, seq) VALUES ('feature_bin',0);
UPDATE sqlite_sequence
SET seq=(SELECT max(fid) FROM feature)
WHERE name="feature_bin";
INSERT INTO feature_bin
SELECT DISTINCT NULL, fullname, metaphone(name,5), paflag, zip
FROM linezip l, tiger_featnames f
WHERE l.tlid=f.tlid AND name <> "" AND name IS NOT NULL;
CREATE INDEX feature_bin_idx ON feature_bin (street, zip);
INSERT INTO feature_edge
SELECT DISTINCT fid, f.tlid
FROM linezip l, tiger_featnames f, feature_bin b
WHERE l.tlid=f.tlid AND l.zip=b.zip
AND f.fullname=b.street AND f.paflag=b.paflag;
-- SELECT min(fid),max(fid) FROM feature_bin;
INSERT INTO feature
SELECT * FROM feature_bin;
-- generate edges from the edges table for each desired edge, running
-- a simple compression on the WKB geometry (because they're all
-- linestrings).
INSERT OR IGNORE INTO edge
SELECT l.tlid, compress_wkb_line(the_geom) FROM
(SELECT DISTINCT tlid FROM linezip) AS l, tiger_edges e
WHERE l.tlid=e.tlid AND fullname <> "" AND fullname IS NOT NULL;
-- generate all ranges from the addr table, stripping off any non-digit
-- prefixes and putting them in a separate column.
INSERT INTO range
SELECT tlid, digit_suffix(fromhn), digit_suffix(tohn),
nondigit_prefix(fromhn), zip, side
FROM tiger_addr;
END;
DROP TABLE feature_bin;
DROP TABLE linezip;
DROP TABLE tiger_addr;
DROP TABLE tiger_featnames;
DROP TABLE tiger_edges;
================================================
FILE: build/sql/create.sql
================================================
-- initialize the database tables.
-- 'place' contains the gazetteer of place names.
CREATE TABLE place(
zip CHAR(5),
city VARCHAR(100),
state CHAR(2),
city_phone VARCHAR(5),
lat NUMERIC(9,6),
lon NUMERIC(9,6),
status CHAR(1),
fips_class CHAR(2),
fips_place CHAR(7),
fips_county CHAR(5),
priority char(1));
-- 'edge' stores the line geometries and their IDs.
CREATE TABLE edge (
tlid INTEGER(10) PRIMARY KEY,
geometry BLOB);
-- 'feature' stores the name(s) and ZIP(s) of each edge.
CREATE TABLE feature (
fid INTEGER PRIMARY KEY,
street VARCHAR(100),
street_phone VARCHAR(5),
paflag BOOLEAN,
zip CHAR(5));
-- 'feature_edge' links each edge to a feature.
CREATE TABLE feature_edge (
fid INTEGER,
tlid INTEGER);
-- 'range' stores the address range(s) for each edge.
CREATE TABLE range (
tlid INTEGER(10),
fromhn INTEGER(6),
tohn INTEGER(6),
prenum VARCHAR(12),
zip CHAR(5),
side CHAR(1));
================================================
FILE: build/sql/index.sql
================================================
.echo on
PRAGMA temp_store=MEMORY;
PRAGMA journal_mode=MEMORY;
PRAGMA synchronous=OFF;
PRAGMA cache_size=500000;
PRAGMA count_changes=0;
-- create indexes for all the relevant ways each table is queried.
CREATE INDEX place_city_phone_state_idx ON place (city_phone, state);
CREATE INDEX place_zip_priority_idx ON place (zip, priority);
CREATE INDEX feature_street_phone_zip_idx ON feature (street_phone, zip);
CREATE INDEX feature_edge_fid_idx ON feature_edge (fid);
CREATE INDEX range_tlid_idx ON range (tlid);
================================================
FILE: build/sql/place.sql
================================================
[File too large to display: 11.4 MB]
================================================
FILE: build/sql/setup.sql
================================================
-- create temporary tables to hold the TIGER/Line data before it's
-- transformed and loaded into the permanent tables.
--
-- this file was made by running 'shp2pgsql -p' on each of the
-- TIGER/Line shapefiles and then massaging the result by hand.
--
PRAGMA temp_store=MEMORY;
PRAGMA journal_mode=MEMORY;
PRAGMA synchronous=OFF;
PRAGMA cache_size=500000;
PRAGMA count_changes=0;
CREATE TEMPORARY TABLE "tiger_edges" (
"statefp" varchar(2),
"countyfp" varchar(3),
"tlid" int8,
"tfidl" int8,
"tfidr" int8,
"mtfcc" varchar(5),
"fullname" varchar(100),
"smid" varchar(22),
"lfromadd" varchar(12),
"ltoadd" varchar(12),
"rfromadd" varchar(12),
"rtoadd" varchar(12),
"zipl" varchar(5),
"zipr" varchar(5),
"featcat" varchar(1),
"hydroflg" varchar(1),
"railflg" varchar(1),
"roadflg" varchar(1),
"olfflg" varchar(1),
"passflg" varchar(1),
"divroad" varchar(1),
"exttyp" varchar(1),
"ttyp" varchar(1),
"deckedroad" varchar(1),
"artpath" varchar(1),
"persist" varchar(1),
"gcseflg" varchar(1),
"offsetl" varchar(1),
"offsetr" varchar(1),
"tnidf" int8,
"tnidt" int8,
"the_geom" blob
);
-- SELECT AddGeometryColumn('','edges','the_geom','-1','MULTILINESTRING',2);
CREATE TEMPORARY TABLE "tiger_featnames" (
"tlid" int8,
"fullname" varchar(100),
"name" varchar(100),
"predirabrv" varchar(15),
"pretypabrv" varchar(50),
"prequalabr" varchar(15),
"sufdirabrv" varchar(15),
"suftypabrv" varchar(50),
"sufqualabr" varchar(15),
"predir" varchar(2),
"pretyp" varchar(3),
"prequal" varchar(2),
"sufdir" varchar(2),
"suftyp" varchar(3),
"sufqual" varchar(2),
"linearid" varchar(22),
"mtfcc" varchar(5),
"paflag" varchar(1));
CREATE TEMPORARY TABLE "tiger_addr" (
"tlid" int8,
"fromhn" varchar(12),
"tohn" varchar(12),
"side" varchar(1),
"zip" varchar(5),
"plus4" varchar(4),
"fromtyp" varchar(1),
"totyp" varchar(1),
"fromarmid" int4,
"toarmid" int4,
"arid" varchar(22),
"mtfcc" varchar(5));
================================================
FILE: build/tiger2009_import
================================================
#!/bin/bash
TMP="/tmp/tiger-import.$$"
SHPS="edges"
DBFS="featnames addr"
BASE=$(dirname $0)
PATH=$PATH:$BASE
SQL="$BASE/sql"
HELPER_LIB="$BASE/../lib/geocoder/us/sqlite3.so"
DATABASE=$1
shift
mkdir -p $TMP || exit 1
# Initialize the database if it doesn't exist.
[ ! -r $DATABASE ] && cat ${SQL}/{create,place}.sql | sqlite3 $DATABASE
# Marshal the county directories to import.
#
# If no directory was given on the command-line, read a list from STDIN.
if [ x"$1" = x"" ]; then
cat
else
# Otherwise, find all of the contents of each state directory.
ls -d $1/[0-9]* | while read state; do
ls -d ${state}/[0-9]*
done
fi | while read county; do
echo "--- $county"
# Unpack the county files into the temp directory.
for file in $SHPS $DBFS; do
ZIP=$(ls ${county}/*_${file}.zip 2>/dev/null)
SHP=$(ls ${county}/*_${file}.* 2>/dev/null)
if [ x"$ZIP" != x"" ]; then
unzip -q $ZIP -d $TMP
elif [ x"$SHP" != x"" ]; then
ln -s $SHP $TMP
fi
done
# Generate an SQL stream to feed into the sqlite3 binary.
# Start by loading the helper libs and initializing the temporary tables
# that will hold the TIGER data before ETL.
(echo ".load $HELPER_LIB" && \
cat ${SQL}/setup.sql && \
for file in $SHPS; do
# Convert each Shapefile into SQL statements.
shp2sqlite -aS ${TMP}/*_${file}.shp tiger_${file}
done && \
for file in $DBFS; do
# Convert each DBF into SQL statements likewise.
shp2sqlite -an ${TMP}/*_${file}.dbf tiger_${file}
done && \
cat ${SQL}/convert.sql) | sqlite3 $DATABASE
# Finally, do the transform/load phase (convert.sql)
# and clean up the temporary files.
rm -f $TMP/*
done 2>&1 | tee import-$$.log
rm -rf $TMP
================================================
FILE: build/tiger_import
================================================
#!/bin/bash
TMP="/tmp/tiger-import.$$"
SHPS="edges"
DBFS="featnames addr"
BASE=$(dirname $0)
PATH=$PATH:$BASE
SQL="$BASE/sql"
HELPER_LIB="$BASE/../lib/geocoder/us/sqlite3.so"
SHP2SQLITE=../src/shp2sqlite/shp2sqlite
DATABASE=$1
SOURCE=$2
shift
shift
mkdir -p $TMP || exit 1
# Initialize the database if it doesn't exist.
# Added places back in after adding the "drop if exists" directive to the SQL file. theduckylittle, 2011/12/15
[ ! -r $DATABASE ] && cat ${SQL}/{create,place}.sql | sqlite3 $DATABASE
#[ ! -r $DATABASE ] && cat ${SQL}/create.sql | sqlite3 $DATABASE
# Marshal the county directories to import.
#
# If no directory was given on the command-line, read a list of county IDs from STDIN.
if [ x"$1" != x"" ]; then
cat
else
# Otherwise, find all of the IDs from the contents of the directory structure.
ls $SOURCE/tl_*_edges.zip | while read file; do
file=$(basename $file)
code=${file##tl_????_}
echo ${code%%_edges.zip}
done
fi | sort | while read code; do
echo "--- $code"
# Unpack the county files into the temp directory.
for file in $SHPS $DBFS; do
ZIP=$(ls $SOURCE/*_${code}_${file}.zip 2>/dev/null)
SHP=$(ls $SOURCE/*_${code}_${file}.* 2>/dev/null)
if [ x"$ZIP" != x"" ]; then
unzip -q $ZIP -d $TMP
elif [ x"$SHP" != x"" ]; then
ln -s $SHP $TMP
fi
done
# Generate an SQL stream to feed into the sqlite3 binary.
# Start by loading the helper libs and initializing the temporary tables
# that will hold the TIGER data before ETL.
(echo ".load $HELPER_LIB" && \
cat ${SQL}/setup.sql && \
for file in $SHPS; do
# Convert each Shapefile into SQL statements.
${SHP2SQLITE} -aS ${TMP}/*_${file}.shp tiger_${file}
done && \
for file in $DBFS; do
# Convert each DBF into SQL statements likewise.
shp2sqlite -an ${TMP}/*_${file}.dbf tiger_${file}
done && \
cat ${SQL}/convert.sql) | sqlite3 $DATABASE
# Finally, do the transform/load phase (convert.sql)
# and clean up the temporary files.
rm -f $TMP/*
done 2>&1 | tee import-$$.log
rm -rf $TMP
================================================
FILE: conf/geocoder-us/geocoder.ru
================================================
require 'sinatra'
disable :run, :reload
require 'geocoder/us/rest'
run Sinatra::Application
================================================
FILE: conf/geocoder-us/unicorn.rb
================================================
worker_processes 4
user "www-data", "www-data"
listen "/var/run/geocoder-us/unicorn.sock", :backlog => 64
pid "/var/run/geocoder-us/unicorn.pid"
stderr_path "/var/log/geocoder-us/geocoder-err.log"
stdout_path "/var/log/geocoder-us/geocoder-out.log"
# Have each process listen on a local port for debugging purposes.
after_fork do |server, worker|
addr = "127.0.0.1:#{40000 + worker.nr}"
server.listen(addr, :tries => 1, :delay => 5, :tcp_nopush => true)
end
================================================
FILE: conf/init/geocoder-us.conf
================================================
description "geocoder.us"
start on runlevel [2345]
stop on runlevel [!2345]
respawn
expect daemon
script
. /etc/default/geocoder-us
unicorn -c /etc/geocoder-us/unicorn.rb /etc/geocoder-us/geocoder.ru
end script
================================================
FILE: debian/README.Debian
================================================
geocoder-us for Debian
----------------------
The Geocoder::US package is a Ruby library that uses a database built from the
US Census Bureau's TIGER/Line data to interpolate a latitude/longitude
coordinate for a given US street address.
Binary shared objects
---------------------
The Geocoder::US module depends on being able to load a native extension module
in its SQLite driver. For this reason, a version of libsqlite-ruby >= 1.3.0 is
needed. The module is built and included in the .deb as `sqlite.so`, and it is
installed in the same directory as the Ruby modules. This may not be ideal, but
this makes it easy for the Geocoder::US library to find it there; otherwise, a
configuration option would be necessary.
REST API server
---------------
The library's API centers on a single method 'geocode' to the
Geocoder::US::Database class that takes an address string and returns a list of
dicts containing the most likely matches with coordinates.
The `geocode` method is wrapped in a very simple Sinatra application with a single
endpoint `/geocode` and a single argument `q`, which returns the result of the
geocode method in JSON format.
The Sinatra web framework does not support running as a daemon on its own, so
the Thin web server is used as a container for the application. This package
creates an `/etc/geocoder-us` directory containing two files:
`/etc/geocoder-us/geocoder.ru` is the "rackup" adapter between Thin and Sinatra
and should probably not be changed. This file doesn't have to live in /etc, but
I couldn't figure out where else to put it.
`/etc/geocoder-us/thin.yml` contains the configuration options to run the Thin
server. This file as packaged runs the REST server as the www-data user on port
8080. This file *probably* doesn't need to be changed, but if the server starts
doing weird things, different options to control Thin's behavior can be set
here.
The package creates `/var/log/geocoder-us` and `/var/run/geocoder-us`
directories for the Thin log file and PID file, respectively, and chowns them
to www-data.
An init script is also included in `/etc/init.d/geocoder-us`. It is heavily
hacked from the default Debian init.ex script to support the weirdnesses of
Thin, but it is LSB compliant and supports the `status` command.
Where to put the database
-------------------------
The location of the database file should be set in `/etc/default/geocoder-us`.
The package creates a `/var/lib/geocoder-us` directory and configures the
database location by default to be `/var/lib/geocoder-us/geocoder.db`. If you
have an EBS volume containing a file called `geocoder.db`, for example, you can
just mount the volume at `/var/lib/geocoder-us` and then start the server and
all will be well.
================================================
FILE: debian/changelog
================================================
geocoder-us (2.0.1pre-1sg66) lucid; urgency=low
[ Schuyler Erle ]
* Remove "la" from feature type affixes, because it probably is used
less frequently then "La" as part of a Spanish place name.
[ SimpleGeo Nerds ]
-- SimpleGeo Nerds Sat, 18 Jun 2011 19:06:47 +0000
geocoder-us (2.0.1pre-1sg65) lucid; urgency=low
[ Schuyler Erle ]
* Fix a bug in edge interpolation caused by zero-length segments.
[ SimpleGeo Nerds ]
-- SimpleGeo Nerds Thu, 16 Jun 2011 22:18:47 +0000
geocoder-us (2.0.1pre-1sg64) lucid; urgency=low
[ Paul Lathrop ]
* Fix upstart job.
[ SimpleGeo Nerds ]
-- SimpleGeo Nerds Wed, 08 Jun 2011 23:57:47 +0000
geocoder-us (2.0.1pre-1sg63) lucid; urgency=low
[ Schuyler Erle ]
* Remove Timeout block from geocode endpoint for being generally
unsafe. The timeout can happen elsewhere (e.g. Unicorn or Gate).
[ SimpleGeo Nerds ]
-- SimpleGeo Nerds Wed, 08 Jun 2011 22:45:46 +0000
geocoder-us (2.0.1pre-1sg62) lucid; urgency=low
[ Paul Lathrop ]
* Load the geocoder database location from the env first, then argv.
This is not optimal, because it makes sense to have the command-line
over-ride the environment, however when we run this under unicorn,
argv[0] is set to something that is *not* a database location.
* Remove cruft.
* Use unicorn to run the geocoder.
* Don't run as root.
* Reverting "Remove cruft." Resurrect these files I shouldn't have
deleted. This reverts commit
434f9d3aa40e71a20b5d2cf9b8dad1802282957a.
[ SimpleGeo Nerds ]
-- SimpleGeo Nerds Wed, 08 Jun 2011 22:10:48 +0000
geocoder-us (2.0.1pre-1sg61) lucid; urgency=low
[ Schuyler Erle ]
* Add test for intersections.
[ SimpleGeo Nerds ]
-- SimpleGeo Nerds Sun, 05 Jun 2011 01:56:46 +0000
geocoder-us (2.0.1pre-1sg60) lucid; urgency=low
[ Wade Simmons ]
* Revert "Open a new database connection for every request." This
reverts commit 5a3e3dd5ea745f15482cb16b28b2d09a6753710d.
[ Wade Simmons and Derek Smith ]
* don't swallow exceptions when creating/destroying temporary
databases
* ensure that all prepared statements are closed, and make sure the
temporary databases are cleaned up if there is an exception
* add a TODO to possibly remove the Timeout.timeout
[ SimpleGeo Nerds ]
-- SimpleGeo Nerds Sun, 05 Jun 2011 01:35:46 +0000
geocoder-us (2.0.1pre-1sg59) lucid; urgency=low
[ Schuyler Erle ]
* Fix missing require timeout?
[ SimpleGeo Nerds ]
-- SimpleGeo Nerds Fri, 03 Jun 2011 22:57:47 +0000
geocoder-us (2.0.1pre-1sg58) lucid; urgency=low
[ Schuyler Erle ]
* Open a new database connection for every request.
[ SimpleGeo Nerds ]
-- SimpleGeo Nerds Fri, 03 Jun 2011 22:49:47 +0000
geocoder-us (2.0.1pre-1sg57) lucid; urgency=low
[ Schuyler Erle ]
* Replace thin configs (undoing commit
2bd5dd8e9ae5210cd19692a4dfc557129ef07f58)
[ SimpleGeo Nerds ]
-- SimpleGeo Nerds Fri, 03 Jun 2011 22:40:48 +0000
geocoder-us (2.0.1pre-1sg56) lucid; urgency=low
[ Paul Lathrop ]
* Fix upstart script so it creates a pidfile.
[ SimpleGeo Nerds ]
-- SimpleGeo Nerds Wed, 25 May 2011 21:40:46 +0000
geocoder-us (2.0.1pre-1sg55) lucid; urgency=low
[ Schuyler Erle ]
* Bug fix the previous commit.
[ SimpleGeo Nerds ]
-- SimpleGeo Nerds Fri, 20 May 2011 00:32:48 +0000
geocoder-us (2.0.1pre-1sg54) lucid; urgency=low
[ Schuyler Erle ]
* Don't cache statements anymore.
[ SimpleGeo Nerds ]
-- SimpleGeo Nerds Fri, 20 May 2011 00:18:47 +0000
geocoder-us (2.0.1pre-1sg53) lucid; urgency=low
[ Schuyler Erle ]
* Update metaphones in place table.
* All tests now pass.
[ SimpleGeo Nerds ]
-- SimpleGeo Nerds Mon, 16 May 2011 20:59:37 +0000
geocoder-us (2.0.1pre-1sg52) lucid; urgency=low
[ Schuyler Erle ]
* Update tiger_import for TIGER/Line 2010.
* Remove obvious place duplicates.
* Update places for 2010 using TIGER/Line and Geonames.
* Final update to new place database.
[ SimpleGeo Nerds ]
-- SimpleGeo Nerds Mon, 16 May 2011 19:34:35 +0000
geocoder-us (2.0.1pre-1sg51) lucid; urgency=low
[ Schuyler Erle ]
* Try moving the temporary intersection table into a separate in-
memory database to eliminate locking contention.
[ SimpleGeo Nerds ]
-- SimpleGeo Nerds Thu, 12 May 2011 19:17:24 +0000
geocoder-us (2.0.1pre-1sg50) lucid; urgency=low
* UNRELEASED
-- SimpleGeo Nerds Tue, 03 May 2011 00:09:08 +0000
geocoder-us (2.0.1pre-1sg49) lucid; urgency=low
* UNRELEASED
-- SimpleGeo Nerds Fri, 15 Apr 2011 21:31:56 +0000
geocoder-us (2.0.1pre-1sg48) lucid; urgency=low
* UNRELEASED
-- SimpleGeo Nerds Tue, 05 Apr 2011 23:21:46 +0000
geocoder-us (2.0.1pre-1sg47) lucid; urgency=low
[ Schuyler Erle ]
* Slightly better: Explicitly close statements on the temporary
intersection table to release locks. Also checked cached statements
to see if they've been closed. This (hopefully) eliminates the need
to flush all statements after the table alteration.
[ SimpleGeo Nerds ]
-- SimpleGeo Nerds Tue, 05 Apr 2011 18:24:10 +0000
geocoder-us (2.0.1pre-1sg46) lucid; urgency=low
[ Schuyler Erle ]
* Oh, and trap SQLite3::LockedException, because maybe that'll
magically fix it.
[ SimpleGeo Nerds ]
-- SimpleGeo Nerds Tue, 05 Apr 2011 18:06:09 +0000
geocoder-us (2.0.1pre-1sg45) lucid; urgency=low
[ Schuyler Erle ]
* Throw a few more flush_statements in, because the call to DROP TABLE
seems to be wedging on "database table is locked" though who alone
knows why. Stupid SQLite. Grr.
[ SimpleGeo Nerds ]
-- SimpleGeo Nerds Tue, 05 Apr 2011 17:51:10 +0000
geocoder-us (2.0.1pre-1sg44) lucid; urgency=low
* UNRELEASED
-- SimpleGeo Nerds Tue, 29 Mar 2011 23:53:42 +0000
geocoder-us (2.0.1pre-1sg43) lucid; urgency=low
[ Schuyler Erle ]
* Fix a weird race condition when geocoding intersections.
[ SimpleGeo Nerds ]
-- SimpleGeo Nerds Thu, 17 Feb 2011 22:11:48 +0000
geocoder-us (2.0.1pre-1sg42) lucid; urgency=low
[ Schuyler Erle ]
* Break tests to demonstrate Brooklyn/Manhattan regression.
* Fix Brooklyn/Manhattan regression. Hooray, test-driven development!
* Fix the "Mountain View, CA" bug where "normalizing" a street type in
a city name would prevent the city from getting removed from the
street parts in Address.city=.
* Add more tests to cover recent bugs.
[ SimpleGeo Nerds ]
-- SimpleGeo Nerds Wed, 09 Feb 2011 22:37:07 +0000
geocoder-us (2.0.1pre-1sg41) lucid; urgency=low
[ Schuyler Erle ]
* Remove "Brooklyn, NY" as a place for ZIP 14729. Because that's
plainly WRONG.
* Pretty print output from CLI demo.
* Make all tests pass, post street side offset.
[ SimpleGeo Nerds ]
-- SimpleGeo Nerds Fri, 04 Feb 2011 00:34:10 +0000
geocoder-us (2.0.1pre-1sg39) lucid; urgency=low
[ Schuyler Erle ]
* Fix a NaN bug caused by some TIGER/Line from/to house numbers being
equal.
[ SimpleGeo Nerds ]
-- SimpleGeo Nerds Wed, 12 Jan 2011 02:30:40 +0000
geocoder-us (2.0.1pre-1sg38) lucid; urgency=low
[ Schuyler Erle ]
* Don't run directly through thin; this causes segfaults on our Debian
systems.
* Remove thin configs.
* Go back to using embedded metaphone function, which will improve
result quality.
* Move the database open back out to the init step.
[ SimpleGeo Nerds ]
-- SimpleGeo Nerds Wed, 12 Jan 2011 02:00:40 +0000
geocoder-us (2.0.1pre-1sg37) lucid; urgency=low
* UNRELEASED
-- SimpleGeo Nerds Tue, 04 Jan 2011 19:24:45 +0000
geocoder-us (2.0.1pre-1sg36) lucid; urgency=low
* UNRELEASED
-- SimpleGeo Nerds Thu, 23 Dec 2010 17:46:19 +0000
geocoder-us (2.0.1pre-1sg35) lucid; urgency=low
[ dsmith ]
* Added a health endpoint to the geocoder
[ SimpleGeo Nerds ]
-- SimpleGeo Nerds Mon, 20 Dec 2010 19:08:59 +0000
geocoder-us (2.0.1pre-1sg34) lucid; urgency=low
* UNRELEASED
-- SimpleGeo Nerds Mon, 20 Dec 2010 17:39:41 +0000
geocoder-us (2.0.1pre-1sg33) lucid; urgency=low
[ Schuyler Erle ]
* Add street-side offsets of 7.5m by default.
[ SimpleGeo Nerds ]
-- SimpleGeo Nerds Mon, 29 Nov 2010 23:12:51 +0000
geocoder-us (2.0.1pre-1sg32) lucid; urgency=low
* UNRELEASED
-- SimpleGeo Nerds Wed, 24 Nov 2010 22:20:44 +0000
geocoder-us (2.0.1pre-1sg31) lucid; urgency=low
* UNRELEASED
-- SimpleGeo Nerds Mon, 15 Nov 2010 19:35:15 +0000
geocoder-us (2.0.1pre-1sg29) lucid; urgency=low
* UNRELEASED
-- SimpleGeo Nerds Tue, 09 Nov 2010 21:28:24 +0000
geocoder-us (2.0.1pre-1sg29) lucid; urgency=low
[ Schuyler Erle ]
* Refactor rest.rb slightly for speed?
[ SimpleGeo Nerds ]
-- SimpleGeo Nerds Thu, 04 Nov 2010 21:54:45 +0000
geocoder-us (2.0.1pre-1sg28) lucid; urgency=low
[ Schuyler Erle ]
* All-singing, all-dancing REST server.
[ SimpleGeo Nerds ]
-- SimpleGeo Nerds Thu, 04 Nov 2010 19:18:25 +0000
geocoder-us (2.0.1pre-1sg27) lucid; urgency=low
[ Schuyler Erle ]
* Work around for segfaults in the Metaphone C code.
* Add Upstart config.
* Remove default and init script, start/stop in postinst/prerm.
* Fix up prerm and postinst scripts.
* Don't daemonize when running with Upstart.
* Tweak respawn rate.
* Load a new database handle on every request. Hrrr.
[ SimpleGeo Nerds ]
-- SimpleGeo Nerds Thu, 04 Nov 2010 18:50:00 +0000
geocoder-us (2.0.1pre-1sg21) lucid; urgency=low
* UNRELEASED
-- SimpleGeo Nerds Fri, 01 Oct 2010 22:27:07 +0000
geocoder-us (2.0.1pre-1sg21) lucid; urgency=low
* UNRELEASED
-- SimpleGeo Nerds Fri, 01 Oct 2010 22:21:08 +0000
geocoder-us (2.0.1pre-1sg21) lucid; urgency=low
* UNRELEASED
-- SimpleGeo Nerds Fri, 01 Oct 2010 22:12:09 +0000
geocoder-us (2.0.1pre-1sg21) lucid; urgency=low
* UNRELEASED
-- SimpleGeo Nerds Fri, 01 Oct 2010 21:52:07 +0000
geocoder-us (2.0.1pre-1sg21) lucid; urgency=low
* UNRELEASED
-- SimpleGeo Nerds Fri, 01 Oct 2010 21:18:23 +0000
geocoder-us (2.0.1pre-1sg20) lucid; urgency=low
* UNRELEASED
-- SimpleGeo Nerds Fri, 01 Oct 2010 21:07:24 +0000
geocoder-us (2.0.1pre-1sg19) lucid; urgency=low
* UNRELEASED
-- SimpleGeo Nerds Fri, 01 Oct 2010 20:57:25 +0000
geocoder-us (2.0.1pre-1sg18) lucid; urgency=low
* UNRELEASED
-- SimpleGeo Nerds Fri, 01 Oct 2010 20:45:23 +0000
geocoder-us (2.0.1pre-1sg17) lucid; urgency=low
* UNRELEASED
-- SimpleGeo Nerds Fri, 01 Oct 2010 20:35:26 +0000
geocoder-us (2.0.1pre-1sg16) lucid; urgency=low
* UNRELEASED
-- SimpleGeo Nerds Fri, 01 Oct 2010 20:24:24 +0000
geocoder-us (2.0.1pre-1sg15) lucid; urgency=low
* UNRELEASED
-- SimpleGeo Nerds Fri, 01 Oct 2010 20:12:32 +0000
geocoder-us (2.0.1pre-1sg14) lucid; urgency=low
* UNRELEASED
-- SimpleGeo Nerds Thu, 30 Sep 2010 22:18:36 +0000
geocoder-us (2.0.1pre-1sg13) lucid; urgency=low
* UNRELEASED
-- SimpleGeo Nerds Thu, 30 Sep 2010 22:07:45 +0000
geocoder-us (2.0.1pre-1sg12) lucid; urgency=low
* UNRELEASED
-- SimpleGeo Nerds Thu, 30 Sep 2010 21:56:56 +0000
geocoder-us (2.0.1pre-1sg11) lucid; urgency=low
* UNRELEASED
-- SimpleGeo Nerds Thu, 30 Sep 2010 21:30:13 +0000
geocoder-us (2.0.1pre-1sg10) lucid; urgency=low
* UNRELEASED
-- SimpleGeo Nerds Thu, 30 Sep 2010 20:58:14 +0000
geocoder-us (2.0.1pre-1sg9) lucid; urgency=low
* UNRELEASED
-- SimpleGeo Nerds Thu, 30 Sep 2010 19:16:45 +0000
geocoder-us (2.0.1pre-1sg3) lucid; urgency=low
* UNRELEASED
-- SimpleGeo Nerds Thu, 30 Sep 2010 18:49:24 +0000
geocoder-us (2.0.1pre-1sg2) lucid; urgency=low
[ Ian Eure ]
* Build the SQLite extension.
[ SimpleGeo Nerds ]
-- SimpleGeo Nerds Tue, 24 Aug 2010 20:39:11 +0000
geocoder-us (2.0.1pre-1sg1) lucid; urgency=low
[ Ian Eure ]
* Add source format.
* Build-depend on ruby1.8, build with CDBS, update standards-version.
* Strip +x bit from stuff that doesn't need it.
* Update copyright.
* Update mainteiners, fix section, add .
* Fix installation of stuff in /var.
[ SimpleGeo Nerds ]
-- SimpleGeo Nerds Tue, 24 Aug 2010 02:33:44 +0000
geocoder-us (2.0.1pre-1sg0) unstable; urgency=low
* Initial release
-- Schuyler Erle Sat, 07 Aug 2010 00:51:40 +0000
================================================
FILE: debian/compat
================================================
7
================================================
FILE: debian/control
================================================
Source: geocoder-us
Section: ruby
Priority: extra
Maintainer: SimpleGeo Nerds
Uploaders: Schuyler Erle
Build-Depends: debhelper (>= 7), libsqlite3-dev, ruby1.8, cdbs, ruby-pkg-tools
Standards-Version: 3.8.4
Homepage: http://github.com/simplegeo/geocoder/
Package: geocoder-us
Architecture: any
Depends: ${misc:Depends}, ${shlibs:Depends}, ruby1.8, libsqlite3-ruby (>= 1.3.0), libsinatra-ruby, libjson-ruby, unicorn
Description: A US address geocoder.
A US address geocoder. Requires a suitable database.
================================================
FILE: debian/copyright
================================================
This work was packaged for Debian by:
Schuyler Erle on Sat, 07 Aug 2010 00:51:40 +0000
It was downloaded from http://github.com/simplegeo/geocoder/
Upstream Author(s):
Schuyler Erle
Copyright:
(c) 2009 FortiusOne, Inc.
License:
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This package is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
The Debian packaging is:
Copyright (C) 2010 SimpleGeo, Inc.
and is licensed under the GPL version 3, see `/usr/share/common-licenses/GPL-3'.
================================================
FILE: debian/default
================================================
# Defaults for geocoder-us upstart job
# sourced by /etc/init/geocoder-us.conf
# installed at /etc/default/geocoder-us by maintainer scripts
# Set the location of the geocoder database.
export GEOCODER_DB="/var/lib/geocoder-us/geocoder.db"
================================================
FILE: debian/docs
================================================
History.txt
Manifest.txt
README.rdoc
TODO.txt
TODO.txt
================================================
FILE: debian/geocoder-us.postinst
================================================
#!/bin/sh
# postinst script for #PACKAGE#
#
# see: dh_installdeb(1)
set -e
# summary of how this script can be called:
# * `configure'
# * `abort-upgrade'
# * `abort-remove' `in-favour'
#
# * `abort-remove'
# * `abort-deconfigure' `in-favour'
# `removing'
#
# for details, see http://www.debian.org/doc/debian-policy/ or
# the debian-policy package
case "$1" in
configure)
# just make sure that /usr/bin/thin can write its PID file and logs
chown www-data /var/run/geocoder-us
chown www-data /var/log/geocoder-us
start geocoder-us || /bin/true
;;
abort-upgrade|abort-remove|abort-deconfigure)
;;
*)
echo "postinst called with unknown argument \`$1'" >&2
exit 1
;;
esac
# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#
exit 0
================================================
FILE: debian/geocoder-us.prerm
================================================
#!/bin/sh
set -e
case "$1" in
remove|deconfigure)
stop geocoder-us || true
;;
upgrade)
;;
failed-upgrade)
;;
*)
echo "prerm called with unknown argument \`$1'" >&2
exit 0
;;
esac
# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.
exit 0
================================================
FILE: debian/rules
================================================
#!/usr/bin/make -f
include /usr/share/cdbs/1/rules/debhelper.mk
include /usr/share/ruby-pkg-tools/1/class/ruby-setup-rb.mk
# Add here any variable or target overrides you need.
build/geocoder-us::
make -C $(CURDIR)/src/libsqlite3_geocoder
install -m 0644 $(CURDIR)/src/libsqlite3_geocoder/*.so \
$(CURDIR)/lib/geocoder/us/sqlite3.so
install/geocoder-us::
install -d -m 0755 $(DEB_DESTDIR)var/lib/geocoder-us \
$(DEB_DESTDIR)var/run/geocoder-us \
$(DEB_DESTDIR)var/log/geocoder-us
================================================
FILE: debian/source/format
================================================
3.0 (quilt)
================================================
FILE: demos/api/server.rb
================================================
require 'rubygems'
require 'sinatra'
require 'geocoder/us/database'
require 'json'
set :port, 8080
@@db = Geocoder::US::Database.new("/home/sderle/geocoder/california.db")
get '/geocode.json' do
if params[:q]
(@@db.geocode params[:q]).to_json
else
status 400
"parameter 'q' is missing"
end
end
get '/' do
unless params[:q].nil?
@records = @@db.geocode params[:q]
end
erb :index
end
================================================
FILE: demos/api/views/index.erb
================================================
Geocoder Demo
<% unless @records.nil? %>
| Match |
Lat |
Lon |
# |
Qual |
Dir |
Type |
Street |
Type |
Dir |
Qual |
City |
St |
ZIP |
|
<% for record in @records %>
| <%= format("%.2f", record[:score]*100) %>% |
<%= record[:lat].to_s %> |
<%= record[:lon].to_s %> |
<%= record[:prefix] if record[:prefix] %><%= record[:number] %> |
<%= record[:pretyp] %> |
<%= record[:predir] %> |
<%= record[:prequal] %> |
<%= record[:street] %> |
<%= record[:suftyp] %> |
<%= record[:sufdir] %> |
<%= record[:sufqual] %> |
<%= record[:city] %> |
<%= record[:state] %> |
<%= record[:zip] %> |
map |
<% end %>
<% end %>
================================================
FILE: demos/cli.rb
================================================
require 'geocoder/us/database'
require 'pp'
db = Geocoder::US::Database.new("/mnt/tiger2010/geocoder.db", :debug=>true)
result = db.geocode(ARGV[0])
pp(result)
print "#{result[0][:lat]} N, #{-result[0][:lon]} W\n"
================================================
FILE: demos/demo/app/ext/geocodewrap.rb
================================================
require 'rubygems'
require 'geocoder/us/database'
require 'logger'
module Sinatra
module GeocodeWrap
attr_accessor :db
def self.registered(app)
options = {:cache_size => 100000}
@@db = Geocoder::US::Database.new("/Users/katechapman/usgeocode.db", options)
stats = Logger.new("geocoderstats.log", 10, 1024000)
app.get '/' do
unless params[:address].nil?
begin
@records = @@db.geocode params[:address]
stats.debug "Geocoded: 1, Failed: 0, Geocoded At: " << DateTime.now.to_s
rescue Exception => e
stats.debug "Geocoded: 1, Failed: 1, Geocoded At: " << DateTime.now.to_s
puts e.message
end
end
case params[:format]
when /xml/
builder :index
when /atom/
builder :atom
when /json/
@records.to_json
else
erb :index
end
end
app.post '/batch' do
failed_codes = 0
total_codes = 0
puts Time.now
if params[:uploaded_csv].nil?
csv_file = request.env["rack.input"].read
csv = FasterCSV.parse(csv_file, :row_sep => "*", :col_sep => "|")
else
FileUtils.mkdir_p('uploads/')
FileUtils.mv(params[:uploaded_csv][:tempfile].path, "uploads/#{params[:uploaded_csv][:filename]}")
csv_file = open("uploads/#{params[:uploaded_csv][:filename]}")
@filename = params[:uploaded_csv][:filename].gsub(/\.csv/,"")
csv = FasterCSV.parse(csv_file)
end
headers = csv[0]
@records = csv.collect do |record|
total_codes += 1
next if record == headers
begin
result = @@db.geocode record[1]
if result.empty?
result[0] = {:lon => nil, :lat => nil, :precision => 'unmatched', :score => 0}
failed_codes += 1
end
result.first.merge(headers[0] => record[0])
rescue Exception => e
failed_codes += 1
puts e.message
next
end
end.compact
puts Time.now
stats.debug "Geocoded: " << total_codes.to_s << ", Failed: " << failed_codes.to_s << ",Geocoded At: " << DateTime.now.to_s
case params[:format]
when /xml/
builder :index
when /atom/
builder :atom
when /json/
@records.to_json
else
erb :index
end
end
end
end
register GeocodeWrap
end
================================================
FILE: demos/demo/app/views/index.builder
================================================
xml.locations do
unless @records.nil?
@records.each do |record|
xml.location do
xml.score format("%.2f", record[:score]*100)
%w{lat lon number prefix pretyp predir prequal street suftyp sufdir sufqual city state zip}.each do |field|
xml.tag! field, record[field.to_sym]
end
end
end
end
end
================================================
FILE: demos/demo/app/views/index.erb
================================================
Geocoder Demo
<% unless @records.nil? %>
| Match |
Precision |
Lat |
Lon |
# |
Qual |
Dir |
Type |
Street |
Type |
Dir |
Qual |
City |
St |
ZIP |
|
<% for record in @records %>
| <%= format("%.2f", record[:score]*100) %>% |
<%= record[:precision].to_s %> |
<%= record[:lat].to_s %> |
<%= record[:lon].to_s %> |
<%= record[:prefix] if record[:prefix] %><%= record[:number] %> |
<%= record[:pretyp] %> |
<%= record[:predir] %> |
<%= record[:prequal] %> |
<%= record[:street] %> |
<%= record[:suftyp] %> |
<%= record[:sufdir] %> |
<%= record[:sufqual] %> |
<%= record[:city] %> |
<%= record[:state] %> |
<%= record[:zip] %> |
map |
<% end %>
<% end %>
<% unless @filename.nil? %>
Atom Feed
<% end %>
================================================
FILE: demos/demo/config/bootstraps.rb
================================================
require 'rubygems'
module BootStraps
class Framework
def initialize
@methods = {}
end
def apply_settings!(app)
@methods.each_pair do |method, calls|
calls.each do |arg_set|
app.send(method, *arg_set)
end
end
end
def method_missing(method, *args)
@methods[method] ||= []
@methods[method] << args
end
end
class DataStore
def connect_action(&block)
@connect_action = block
end
#TODO raise UndefinedConnectAction
def connect
@connect_action.call if @connect_action
end
end
class Configuration
attr_accessor :db, :global, :default_env, :vendor_dir, :lib_paths, :framework, :vendored
attr_reader :gems
def initialize
@framework = Framework.new
@gems = {}
@global = {}
@default_env = 'production'
@vendor_dir = File.join(root, 'vendor')
@lib_paths = []
@vendored = false
end
def env
ENV['RACK_ENV'] ||= default_env
end
def env=(val)
ENV['RACK_ENV'] = val
end
def root
File.join(File.expand_path(File.dirname(__FILE__)), "..")
end
def gem(*args)
gem = args.first
ver = args.last
@gems[gem] = ver
#its concievable that vendored could be changed mid config
use_vendor if vendored
Kernel.send(:gem, *args)
require gem
end
private
def use_vendor
Gem.clear_paths
prepend_gem_path!(File.join(root, 'vendor'))
end
def prepend_gem_path!(path)
ENV['GEM_PATH'] = path
end
end
class Initializer
@@config = Configuration.new
class << self
def configure
unless @@config.frozen?
yield @@config
@@config.freeze
end
end
def config
@@config
end
def boot!
require File.join(@@config.root, 'config', 'geoenvironment.rb')
require_libs
end
private
def require_libs
[
subdir_expansion('lib'),
subdir_expansion(File.join('app','ext'))
].each do |p|
require_all(p)
end
end
def require_all(path)
Dir[path].each { |f| require f }
end
def subdir_expansion(subdir)
File.join(@@config.root, subdir, '**', '*.rb')
end
end
end
end
BootStraps::Initializer.boot!
Straps = BootStraps::Initializer.config
================================================
FILE: demos/demo/config/geoenvironment.rb
================================================
BootStraps::Initializer.configure do |config|
#Use the vendor directory
config.vendored = true
config.default_env = 'production'
config.gem 'sinatra'
config.gem 'fastercsv'
config.gem 'json'
config.framework.set :root, config.root
config.framework.set :environment, config.env
config.framework.set :raise_errors, true
config.framework.set :views, File.join('app','views')
config.framework.set :server, 'mongrel'
config.framework.set :static, true
config.framework.set :logging, true
config.framework.set :port, 4567
config.framework.set :lock, false
end
================================================
FILE: demos/demo/config.ru
================================================
require 'rubygems'
require 'sinatra'
Sinatra::Application.default_options.merge!(
:run => false,
:env => ENV['RACK_ENV']
)
require 'geocom_geocode'
run GeocomGeocode::GeocodeServer
================================================
FILE: demos/demo/geocoder_helper.rb
================================================
require 'rubygems'
require 'geocoder/us/database'
require 'fastercsv'
require 'json'
def initialize
end
================================================
FILE: demos/demo/geocom_geocode.rb
================================================
require 'config/bootstraps'
module GeocomGeocode
class GeocodeServer < Sinatra::Base
register Sinatra::GeocodeWrap
configure do
Straps.framework.apply_settings!(self)
end
end
end
================================================
FILE: demos/demo/main.rb
================================================
require 'geocom_geocode'
GeocomGeocode::GeocodeServer.run!
================================================
FILE: demos/demo/rakefile.rb
================================================
require 'rake'
task :boot_env do
require 'config/bootstraps';
end
namespace :db do
task :migrate => :connect do
ActiveRecord::Base.logger = Logger.new(STDOUT)
ActiveRecord::Migration.verbose = true
ActiveRecord::Migrator.migrate('db/migrate/', nil)
end
task :connect => :boot_env do
BootStraps::Initializer.config.db.connect
end
end
================================================
FILE: demos/demo/tmp/restart.txt
================================================
================================================
FILE: demos/parse.rb
================================================
require 'geocoder/us/address'
require 'pp'
pp(Geocoder::US::Address.new(ARGV[0]))
================================================
FILE: demos/simpledemo/views/index.builder
================================================
xml.locations do
unless @records.nil?
@records.each do |record|
xml.location do
xml.score format("%.2f", record[:score]*100)
%w{lat lon number prefix pretyp predir prequal street suftyp sufdir sufqual city state zip}.each do |field|
xml.tag! field, record[field.to_sym]
end
end
end
end
end
================================================
FILE: demos/simpledemo/views/index.erb
================================================
Geocoder Demo
<% unless @records.nil? %>
| Match |
Lat |
Lon |
# |
Qual |
Dir |
Type |
Street |
Type |
Dir |
Qual |
City |
St |
ZIP |
|
<% for record in @records %>
| <%= format("%.2f", record[:score]*100) %>% |
<%= record[:lat].to_s %> |
<%= record[:lon].to_s %> |
<%= record[:prefix] if record[:prefix] %><%= record[:number] %> |
<%= record[:pretyp] %> |
<%= record[:predir] %> |
<%= record[:prequal] %> |
<%= record[:street] %> |
<%= record[:suftyp] %> |
<%= record[:sufdir] %> |
<%= record[:sufqual] %> |
<%= record[:city] %> |
<%= record[:state] %> |
<%= record[:zip] %> |
map |
<% end %>
<% end %>
<% unless @filename.nil? %>
Atom Feed
<% end %>
================================================
FILE: demos/simpledemo/ws.rb
================================================
require 'rubygems'
require 'sinatra'
require 'geocoder/us/database'
require 'fastercsv'
require 'json'
set :port, 8080
@@db = Geocoder::US::Database.new("/fortiusone/geocoder/geocoder.db")
get '/' do
unless params[:address].nil?
@records = @@db.geocode params[:address]
end
case params[:format]
when /xml/
builder :index
when /atom/
builder :atom
else
erb :index
end
end
require 'open-uri'
get '/link.:format' do
if(params.include?(:url))
csv_file = params[:url]
else
csv_file = "uploads/#{params[:filename]}.csv"
end
csv = FasterCSV.parse(open(csv_file))
headers = csv[0]
@records = csv.collect do |record|
next if record == headers
begin
(@@db.geocode record[1]).first
rescue Exception => e
puts e.message
next
end
end.compact
case params[:format]
when /atom/
builder :atom
when /xml/
builder :index
else
erb :index
end
end
post '/batch' do
csv_file = request.env["rack.input"].read
csv = FasterCSV.parse(csv_file, :row_sep => "*", :col_sep => "|")
headers = csv[0]
@records = csv.collect do |record|
next if record == headers
begin
(@@db.geocode record[1]).first.merge(headers[0] => record[0])
rescue Exception => e
puts e.message
next
end
end.compact
case params[:format]
when /xml/
builder :index
when /atom/
builder :atom
when /json/
@records.to_json
else
erb :index
end
end
================================================
FILE: doc/Makefile
================================================
all: lookup.html parsing.html
%.html: %.rst voidspace.css
rst2html --stylesheet-path=voidspace.css --no-compact-lists $< > $@
clean:
rm -f *.html
================================================
FILE: doc/html4css1.css
================================================
/*
:Author: David Goodger
:Contact: goodger@users.sourceforge.net
:Date: $Date: 2005-12-18 01:56:14 +0100 (Sun, 18 Dec 2005) $
:Revision: $Revision: 4224 $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin-left: 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left {
clear: left }
img.align-right {
clear: right }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font-family: serif ;
font-size: 100% }
pre.literal-block, pre.doctest-block {
margin-left: 2em ;
margin-right: 2em ;
background-color: #eeeeee }
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
tt.docutils {
background-color: #eeeeee }
ul.auto-toc {
list-style-type: none }
================================================
FILE: doc/lookup.rst
================================================
.. _lookup:
===================================
Geocoder.us Address Lookup Strategy
===================================
:Author: Schuyler Erle
:Contact: schuyler at geocoder dot us
:Created: 2009/03/13
:Edited: 2009/03/14
Definitions
-----------
Edge
Database representation of a street segment, consisting of a linestring
geometry and an edge ID. Edges relate to many ranges and many features
through its ID.
Feature
Database representation of a named street, consisting of street name
and modifier elements, a reference ZIP code, and a primary/alternate flag.
Range
Database representation of a range of address numbers on a given
street, consisting of range start and end numbers, an optional prefix
ending with a non-numeric character, and a delivery ZIP code for that
range.
Place
Database representation of a ZIP code, consisting of a city name,
state abbreviation, a ZIP code, and a primary/alternate flag.
Address record
A set consisting of exactly one edge, one feature, and one range, related
through the edge ID.
Address query
An ordered set of {Number Prefix, Number, Directional Prefix, Type Prefix,
Qualifier Prefix, Street Name, Qualifier Suffix, Type Suffix, Directional
Suffix, City, State, ZIP}. All of the elements are optional except Number and
Street Name. Either ZIP or City must also be present. The State element
and all of the prefix and suffix elements are assumed to be normalized to
standard postal abbreviations.
Address string
A string including some or all of the elements of an address.
Address Lookup Strategy
-----------------------
1. Given a an address query, initialize an empty set of candidate places,
and an empty set of candidate address records.
#. If a ZIP was given, look up `the place from the ZIP`_, and add the
place, if any, to the candidate place set.
#. If a city was given, look up all `the places matching the metaphone hash
of the city name`_, and add them, if any, to the candidate place set.
#. Generate a unique set of ZIPs from the set of candidate places, since a ZIP
may have one or more names associated with it.
#. Generate `a list of candidate address records`_ by fetching all the street
features matching the metaphone hash of the street name and one of the ZIPs
in the query set, along with the ranges matching the edge ID of each
feature, where the given number is in the range. The edge does not
need to be fetched yet.
#. If the look up generates no results, optionally generate `more candidate
records`_ by looking up all the street features matching the metaphone hash
of the street name, along with the ranges matching the edge ID of each
feature, where the given number is in the range. This may be a very time
consuming database query, because some street names are quite common.
#. Score each of the candidate records as follows:
a. Score one point for every provided element of the address query that it
matches exactly.
#. Optionally, compute the scaled Damerau-Levenshtein distance (or
alternately the simple Levenshtein distance) between each provided
element of the address query and the corresponding element in the
candidate. Score one minus the scaled distance, which yields a fraction
of a point.
#. Score one point if the parity of starting range number matches the parity
of the queried address number.
#. Note that the maximum possible score is equal to the number of provided
elements in the address query. Divide the score by the maximum possible.
This is the confidence value of the candidate.
#. Sort the candidate address records by confidence. Retain only the records
that share the highest confidence as candidates.
#. Fetch `the edges and primary feature names`_ matching the edge IDs of
the remaining candidate address records.
#. For each remaining candidate record:
a. Replace the candidate record feature elements with those of the
primary feature name for that edge.
#. Fetch `all of the ranges for the edge ID`_ of the candidate, sorted by
starting number.
#. Compute the sum of the differences of the starting and ending house
number for each range. This is the total number width of the edge.
#. Take the difference between the candidate starting number and the lowest
starting number, add the difference between the queried number and the
candidate starting number, and divide by the total number width. This is
the interpolation distance.
#. Optionally, find the local UTM zone and project the edge into it.
#. Find the point along the line at the interpolation distance.
#. If the edge was projected, unproject the point.
#. Assign the point as the geocoded location of the query to the candidate
record.
#. Construct a set of result ZIPs from the remaining candidates, and look up
`the primary name and state for each ZIP`_ in the set. Assign the matching
primary city and state to each candidate.
#. Return the set of candidate records as the result of the query.
SQL Statements
--------------
the place from the ZIP
~~~~~~~~~~~~~~~~~~~~~~~
::
SELECT * FROM place WHERE zip = '...';
the places matching the metaphone hash of the city name
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
::
SELECT * FROM place WHERE city_phone = metaphone('...');
a list of candidate address records
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
::
SELECT feature.*, range.* FROM feature, range
WHERE name_phone = metaphone('...') AND feature.zip IN (...)
AND range.tlid = feature.tlid
AND fromhn <= ... AND tohn >= ...;
more candidate records
~~~~~~~~~~~~~~~~~~~~~~
::
SELECT feature.*, range.* FROM feature, range
WHERE name_phone = metaphone('...')
AND range.tlid = feature.tlid
AND fromhn <= ... AND tohn >= ...;
the edges and primary feature names
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
::
SELECT feature.*, edge.* FROM feature, edge
WHERE feature.tlid = ... AND paflag = 'P'
AND edge.tlid = feature.tlid;
-- or
SELECT feature.*, edge.* FROM feature, edge
WHERE feature.tlid IN (...)
AND paflag = 'P'
AND edge.tlid = feature.tlid;
all of the ranges for the edge ID
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
::
SELECT * FROM range WHERE range.tlid = ...;
-- or
SELECT * FROM range WHERE range.tlid IN (...);
the primary name and state for each ZIP
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
::
SELECT * FROM place WHERE zip IN (...) AND paflag = 'P';
= 30 =
================================================
FILE: doc/parsing.rst
================================================
.. _parsing:
====================================
Geocoder.us Address Parsing Strategy
====================================
:Author: Schuyler Erle
:Contact: schuyler at geocoder dot us
:Created: 2009/03/18
:Edited: 2009/03/18
Structured address components
-----------------------------
Unless otherwise labeled as "required", all components of a structured address
are optional.
prenum
The alphanumeric prefix portion of a house or building number. (e.g. "32-"
in "32-20 Jackson St".
number
The house or building number component. Required.
sufnum
The alphanumeric suffix portion of a house or building number. (e.g. "23B
Baker St")
fraction
The fractional portion of a house or building number. (e.g. "23 1/2 Baker
St")
predir
The prefixed street directional component. (e.g. "N", "SW")
prequal
The prefixed street qualifier component. (e.g. "Old", "Business")
pretyp
The prefixed street type component. (e.g. "US Hwy")
street
The main portion of the street name. Required.
suftyp
The suffixed street type component. (e.g. "Rd", "Ave")
sufqual
The suffixed street qualifier component.
sufdir
The suffixed street directional component.
unittyp
The unit type, if any. (e.g. "Fl", "Apt", "Ste")
unit
The unit identifer, if any.
city
The name of the city or locale.
state
The two letter postal state code.
zip
The zero padded, five digit ZIP postal code.
plus4
The zero padded, four digit ZIP+4 postal extension.
Parsing Strategy
----------------
Each component will have a regular expression, and a maximum
count. Components are ordered from first to last.
Those components drawn from finite lists - directionals, qualifiers,
types, and states - will have regular expressions composed of the union of
the corresponding list.
A *parse* will consist of a component state, a penalty count, a list of
component strings and a counter for each component.
1. Initialize an input stack, consisting of a single blank parse.
#. Split the address string on whitespace into tokens.
#. For each token:
A. For each component:
i. Test the token against the regular expression.
#. If the regexp matches, add the component name to a list of matching
components.
#. Initialize an empty output stack.
#. For each parse in the input stack:
i. Copy the current parse, increment the penalty count on the new parse,
and add it to the output stack.
#. For each matching component for the current token:
a. If the component state for this parse is later than the
matching component, continue to the next matching component.
#. If the component count for this parse state is equal to the
maximum count for the component, continue to the next matching
component.
#. Otherwise, copy the parse state, and append the token to the
component string, with a leading space, if necessary.
#. Increment the matching component counter for the current parse.
#. Set the component state of the current parse to the matching
component.
#. Push the new parse on to the output stack.
#. Replace the input stack with the output stack.
#. Post-process number prefix/suffixes and ZIP+4 extensions.
#. Score each parse by the number of components with non-empty strings,
minus the penalty count of the parse.
#. Return the sorted list of parsed string lists.
================================================
FILE: doc/voidspace.css
================================================
/*
:Authors: Ian Bicking, Michael Foord
:Contact: fuzzyman@voidspace.org.uk
:Date: 2005/08/26
:Version: 0.1.0
:Copyright: This stylesheet has been placed in the public domain.
:Modified By: Schuyler Erle, for geocoder.us, 2008-03-14
Stylesheet for Docutils.
Based on ``blue_box.css`` by Ian Bicking
and ``html4css1.css`` revision 1.46.
*/
@import url(html4css1.css);
/* changes made by SDE */
body {
font-family: Arial, sans-serif;
margin-left: 10%;
margin-right: 10%;
}
p { text-align: justify; }
dt { font-style: italic; }
/* end changes */
em, i {
/* Typically serif fonts have much nicer italics */
font-family: Times New Roman, Times, serif;
}
a.target {
color: blue;
}
a.target {
color: blue;
}
a.toc-backref {
text-decoration: none;
color: black;
}
a.toc-backref:hover {
background-color: inherit;
}
a:hover {
background-color: #cccccc;
}
div.attention, div.caution, div.danger, div.error, div.hint,
div.important, div.note, div.tip, div.warning {
background-color: #cccccc;
padding: 3px;
width: 80%;
}
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
text-align: center;
background-color: #999999;
display: block;
margin: 0;
}
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title {
color: #cc0000;
font-family: sans-serif;
text-align: center;
background-color: #999999;
display: block;
margin: 0;
}
h1, h2, h3, h4, h5, h6 {
font-family: Helvetica, Arial, sans-serif;
border: thin solid black;
/* This makes the borders rounded on Mozilla, which pleases me */
-moz-border-radius: 8px;
padding: 4px;
}
h1 {
background-color: #444499;
color: #ffffff;
border: medium solid black;
}
h1 a.toc-backref, h2 a.toc-backref {
color: #ffffff;
}
h2 {
background-color: #666666;
color: #ffffff;
border: medium solid black;
}
h3, h4, h5, h6 {
background-color: #cccccc;
color: #000000;
}
h3 a.toc-backref, h4 a.toc-backref, h5 a.toc-backref,
h6 a.toc-backref {
color: #000000;
}
h1.title {
text-align: center;
background-color: #444499;
color: #eeeeee;
border: thick solid black;
-moz-border-radius: 20px;
}
table.footnote {
padding-left: 0.5ex;
}
table.citation {
padding-left: 0.5ex
}
pre.literal-block, pre.doctest-block {
border: thin black solid;
padding: 5px;
}
.image img { border-style : solid;
border-width : 2px;
}
h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
font-size: 100%;
}
code, tt {
color: #000066;
}
================================================
FILE: gemspec
================================================
Gem::Specification.new do |s|
s.name = 'Geocoder-US'
s.version = "2.0.4"
s.author = "Schuyler Erle"
s.email = 'geocoder@entropyfree.com'
s.description = "US address geocoding based on TIGER/Line."
s.summary = "US address geocoding based on TIGER/Line."
s.homepage = "http://geocoder.us/"
s.files = ["lib/geocoder/us.rb"] + Dir["lib/geocoder/us/*"] + Dir["tests/*"]
s.require_path = "lib"
s.test_files = "test/run.rb"
s.add_dependency 'sqlite3-ruby', '~>1.3.1'
s.has_rdoc = true
s.extra_rdoc_files = ["README.rdoc"]
end
================================================
FILE: lib/geocoder/us/address.rb
================================================
require 'geocoder/us/constants'
module Geocoder::US
# Defines the matching of parsed address tokens.
Match = {
# FIXME: shouldn't have to anchor :number and :zip at start/end
:number => /^(\d+\W|[a-z]+)?(\d+)([a-z]?)\b/io,
:street => /(?:\b(?:\d+\w*|[a-z'-]+)\s*)+/io,
:city => /(?:\b[a-z'-]+\s*)+/io,
:state => Regexp.new(State.regexp.source + "\s*$", Regexp::IGNORECASE),
:zip => /(\d{5})(?:-\d{4})?\s*$/o,
:at => /\s(at|@|and|&)\s/io,
:po_box => /\b[P|p]*(OST|ost)*\.*\s*[O|o|0]*(ffice|FFICE)*\.*\s*[B|b][O|o|0][X|x]\b/
}
# The Address class takes a US street address or place name and
# constructs a list of possible structured parses of the address
# string.
class Address
attr_accessor :text
attr_accessor :prenum, :number, :sufnum
attr_accessor :street
attr_accessor :city
attr_accessor :state
attr_accessor :zip, :plus4
# Takes an address or place name string as its sole argument.
def initialize(text)
raise ArgumentError, "no text provided" unless text and !text.empty?
if text.class == Hash
@text = ""
assign_text_to_address text
else
@text = clean text
parse
end
end
# Removes any characters that aren't strictly part of an address string.
def clean(value)
value.strip \
.gsub(/[^a-z0-9 ,'&@\/-]+/io, "") \
.gsub(/\s+/o, " ")
end
def assign_text_to_address(text)
if text[:address]
@text = clean text[:address]
parse
else
@street = []
@prenum = text[:prenum]
@sufnum = text[:sufnum]
if !text[:street].nil?
@street = text[:street].scan(Match[:street])
end
@number = ""
if !@street.nil?
if text[:number].nil?
@street.map! { |single_street|
single_street.downcase!
@number = single_street.scan(Match[:number])[0].to_s
single_street.sub! @number, ""
single_street.sub! /^\s*,?\s*/o, ""
}
else
@number = text[:number].to_s
end
@street = expand_streets(@street)
street_parts
end
@city = []
if !text[:city].nil?
@city.push(text[:city])
else
@city.push("")
end
if !text[:region].nil?
# @state = []
@state = text[:region]
if @state.length > 2
# full_state = @state.strip # special case: New York
@state = State[@state]
end
elsif !text[:country].nil?
@state = text[:country]
elsif !text[:state].nil?
@state = text[:state]
end
@zip = text[:postal_code]
@plus4 = text[:plus4]
if !@zip
@zip = @plus4 = ""
end
end
end
# Expands a token into a list of possible strings based on
# the Geocoder::US::Name_Abbr constant, and expands numerals and
# number words into their possible equivalents.
def expand_numbers (string)
if /\b\d+(?:st|nd|rd|th)?\b/o.match string
match = $&
num = $&.to_i
elsif Ordinals.regexp.match string
num = Ordinals[$&]
match = $&
elsif Cardinals.regexp.match string
num = Cardinals[$&]
match = $&
end
strings = []
if num and num < 100
[num.to_s, Ordinals[num], Cardinals[num]].each {|replace|
strings << string.sub(match, replace)
}
else
strings << string
end
strings
end
def parse_zip(regex_match, text)
idx = text.rindex(regex_match)
text[idx...idx+regex_match.length] = ""
text.sub! /\s*,?\s*$/o, ""
@zip, @plus4 = @zip.map {|s|s.strip}
text
end
def parse_state(regex_match, text)
idx = text.rindex(regex_match)
text[idx...idx+regex_match.length] = ""
text.sub! /\s*,?\s*$/o, ""
@full_state = @state[0].strip # special case: New York
@state = State[@full_state]
text
end
def parse_number(regex_match, text)
# FIXME: What if this string appears twice?
idx = text.index(regex_match)
text[idx...idx+regex_match.length] = ""
text.sub! /^\s*,?\s*/o, ""
@prenum, @number, @sufnum = @number.map {|s| s and s.strip}
text
end
def parse
text = @text.clone.downcase
@zip = text.scan(Match[:zip])[-1]
if @zip
text = parse_zip($&, text)
else
@zip = @plus4 = ""
end
@state = text.scan(Match[:state])[-1]
if @state
text = parse_state($&, text)
else
@full_state = ""
@state = ""
end
@number = text.scan(Match[:number])[0]
# FIXME: 230 Fish And Game Rd, Hudson NY 12534
if @number # and not intersection?
text = parse_number($&, text)
else
@prenum = @number = @sufnum = ""
end
# FIXME: special case: Name_Abbr gets a bit aggressive
# about replacing St with Saint. exceptional case:
# Sault Ste. Marie
# FIXME: PO Box should geocode to ZIP
@street = text.scan(Match[:street])
@street = expand_streets(@street)
# SPECIAL CASE: 1600 Pennsylvania 20050
@street << @full_state if @street.empty? and @state.downcase != @full_state.downcase
@city = text.scan(Match[:city])
if !@city.empty?
@city = [@city[-1].strip]
add = @city.map {|item| item.gsub(Name_Abbr.regexp) {|m| Name_Abbr[m]}}
@city |= add
@city.map! {|s| s.downcase}
@city.uniq!
else
@city = []
end
# SPECIAL CASE: no city, but a state with the same name. e.g. "New York"
@city << @full_state if @state.downcase != @full_state.downcase
end
def expand_streets(street)
if !street.empty? && !street[0].nil?
street.map! {|s|s.strip}
add = street.map {|item| item.gsub(Name_Abbr.regexp) {|m| Name_Abbr[m]}}
street |= add
add = street.map {|item| item.gsub(Std_Abbr.regexp) {|m| Std_Abbr[m]}}
street |= add
street.map! {|item| expand_numbers(item)}
street.flatten!
street.map! {|s| s.downcase}
street.uniq!
else
street = []
end
street
end
def street_parts
strings = []
# Get all the substrings delimited by whitespace
@street.each {|string|
tokens = string.split(" ")
strings |= (0...tokens.length).map {|i|
(i...tokens.length).map {|j| tokens[i..j].join(" ")}}.flatten
}
strings = remove_noise_words(strings)
# Try a simpler case of adding the @number in case everything is an abbr.
strings += [@number] if strings.all? {|s| Std_Abbr.key? s or Name_Abbr.key? s}
strings.uniq
end
def remove_noise_words(strings)
# Don't return strings that consist solely of abbreviations.
# NOTE: Is this a micro-optimization that has edge cases that will break?
# Answer: Yes, it breaks on simple things like "Prairie St" or "Front St"
prefix = Regexp.new("^" + Prefix_Type.regexp.source + "\s*", Regexp::IGNORECASE)
suffix = Regexp.new("\s*" + Suffix_Type.regexp.source + "$", Regexp::IGNORECASE)
predxn = Regexp.new("^" + Directional.regexp.source + "\s*", Regexp::IGNORECASE)
sufdxn = Regexp.new("\s*" + Directional.regexp.source + "$", Regexp::IGNORECASE)
good_strings = strings.map {|s|
s = s.clone
s.gsub!(predxn, "")
s.gsub!(sufdxn, "")
s.gsub!(prefix, "")
s.gsub!(suffix, "")
s
}
good_strings.reject! {|s| s.empty?}
strings = good_strings if !good_strings.empty? {|s| not Std_Abbr.key?(s) and not Name_Abbr.key?(s)}
strings
end
def city_parts
strings = []
@city.map {|string|
tokens = string.split(" ")
strings |= (0...tokens.length).to_a.reverse.map {|i|
(i...tokens.length).map {|j| tokens[i..j].join(" ")}}.flatten
}
# Don't return strings that consist solely of abbreviations.
# NOTE: Is this a micro-optimization that has edge cases that will break?
# Answer: Yes, it breaks on "Prairie"
good_strings = strings.reject {|s| Std_Abbr.key? s}
strings = good_strings if !good_strings.empty?
strings.uniq
end
def city= (strings)
# NOTE: This will still fail on: 100 Broome St, 33333 (if 33333 is
# Broome, MT or what)
strings = expand_streets(strings) # fix for "Mountain View" -> "Mountain Vw"
match = Regexp.new('\s*\b(?:' + strings.join("|") + ')\b\s*$', Regexp::IGNORECASE)
# only remove city from street strings if address was parsed
unless @text == ""
@street = @street.map {|string| string.gsub(match, '')}.select {|s|!s.empty?}
end
end
def po_box?
Match[:po_box].match @text
end
def intersection?
Match[:at].match @text
end
end
end
================================================
FILE: lib/geocoder/us/constants.rb
================================================
# coding: utf-8
require 'set'
require 'geocoder/us/numbers'
module Geocoder
end
module Geocoder::US
class Map < Hash
# The Map class provides a two-way mapping between postal abbreviations
# and their fully written equivalents.
#attr_accessor :partial
attr_accessor :regexp
def self.[] (*items)
hash = super(*items)
#hash.build_partial
hash.build_match
hash.keys.each {|k| hash[k.downcase] = hash.fetch(k)}
hash.values.each {|v| hash[v.downcase] = v}
hash.freeze
end
# The build_partial method constructs a hash of case-insensitive,
# whitespace-delimited prefixes to keys and values in the two-way Map.
def build_partial
@partial = Set.new()
[keys, values].flatten.each {|item|
@partial << item.downcase
item.downcase.split.each {|token| @partial << token}
}
end
def build_match
@regexp = Regexp.new(
'\b(' + [keys,values].flatten.join("|") + ')\b',
Regexp::IGNORECASE)
end
# The partial? method returns true if the key is a prefix of some
# key in the Map.
def partial? (key)
@partial.member? key.downcase
end
def key? (key)
super(key.downcase)
end
def [] (key)
super(key.downcase)
end
end
# The Directional constant maps compass direction words in English and
# Spanish to their 1- or 2- letter abbreviations. See 2008 TIGER/Line
# technical documentation Appendix C for more details.
Directional = Map[
"North" => "N",
"South" => "S",
"East" => "E",
"West" => "W",
"Northeast" => "NE",
"Northwest" => "NW",
"Southeast" => "SE",
"Southwest" => "SW",
"Norte" => "N",
"Sur" => "S",
"Este" => "E",
"Oeste" => "O",
"Noreste" => "NE",
"Noroeste" => "NO",
"Sudeste" => "SE",
"Sudoeste" => "SO"
]
# The Prefix_Qualifier constant maps feature prefix qualifiers to their
# abbreviations. See 2008 TIGER/Line technical documentation Appendix D.
Prefix_Qualifier = Map[
"Alternate" => "Alt",
"Business" => "Bus",
"Bypass" => "Byp",
"Extended" => "Exd",
"Historic" => "Hst",
"Loop" => "Lp",
"Old" => "Old",
"Private" => "Pvt",
"Public" => "Pub",
"Spur" => "Spr",
]
# The Suffix_Qualifier constant maps feature suffix qualifiers to their
# abbreviations. See 2008 TIGER/Line technical documentation Appendix D.
Suffix_Qualifier = Map[
"Access" => "Acc",
"Alternate" => "Alt",
"Business" => "Bus",
"Bypass" => "Byp",
"Connector" => "Con",
"Extended" => "Exd",
"Extension" => "Exn",
"Loop" => "Lp",
"Private" => "Pvt",
"Public" => "Pub",
"Scenic" => "Scn",
"Spur" => "Spr",
"Ramp" => "Rmp",
"Underpass" => "Unp",
"Overpass" => "Ovp",
]
# The Prefix_Canonical constant maps canonical TIGER/Line street type
# prefixes to their abbreviations. This list is the subset of the list from
# 2008 TIGER/Line technical documentation Appendix E that was extracted from
# a TIGER/Line database import.
Prefix_Canonical = {
"Arcade" => "Arc",
"Autopista" => "Autopista",
"Avenida" => "Ave",
"Avenue" => "Ave",
"Boulevard" => "Blvd",
"Bulevar" => "Bulevar",
"Bureau of Indian Affairs Highway" => "BIA Hwy",
"Bureau of Indian Affairs Road" => "BIA Rd",
"Bureau of Indian Affairs Route" => "BIA Rte",
"Bureau of Land Management Road" => "BLM Rd",
"Bypass" => "Byp",
"Calle" => "Cll",
"Calleja" => "Calleja",
"Callejón" => "Callejón",
"Caminito" => "Cmt",
"Camino" => "Cam",
"Carretera" => "Carr",
"Cerrada" => "Cer",
"Círculo" => "Cír",
"Commons" => "Cmns",
"Corte" => "Corte",
"County Highway" => "Co Hwy",
"County Lane" => "Co Ln",
"County Road" => "Co Rd",
"County Route" => "Co Rte",
"County State Aid Highway" => "Co St Aid Hwy",
"County Trunk Highway" => "Co Trunk Hwy",
"County Trunk Road" => "Co Trunk Rd",
"Court" => "Ct",
"Delta Road" => "Delta Rd",
"District of Columbia Highway" => "DC Hwy",
"Driveway" => "Driveway",
"Entrada" => "Ent",
"Expreso" => "Expreso",
"Expressway" => "Expy",
"Farm Road" => "Farm Rd",
"Farm-to-Market Road" => "FM",
"Fire Control Road" => "Fire Cntrl Rd",
"Fire District Road" => "Fire Dist Rd",
"Fire Lane" => "Fire Ln",
"Fire Road" => "Fire Rd",
"Fire Route" => "Fire Rte",
"Fire Trail" => "Fire Trl",
"Forest Highway" => "Forest Hwy",
"Forest Road" => "Forest Rd",
"Forest Route" => "Forest Rte",
"Forest Service Road" => "FS Rd",
"Highway" => "Hwy",
"Indian Route" => "Indian Rte",
"Indian Service Route" => "Indian Svc Rte",
"Interstate Highway" => "I-",
"Lane" => "Ln",
"Logging Road" => "Logging Rd",
"Loop" => "Loop",
"National Forest Development Road" => "Nat For Dev Rd",
"Navajo Service Route" => "Navajo Svc Rte",
"Parish Road" => "Parish Rd",
"Pasaje" => "Pasaje",
"Paseo" => "Pso",
"Passage" => "Psge",
"Placita" => "Pla",
"Plaza" => "Plz",
"Point" => "Pt",
"Puente" => "Puente",
"Ranch Road" => "Ranch Rd",
"Ranch to Market Road" => "RM",
"Reservation Highway" => "Resvn Hwy",
"Road" => "Rd",
"Route" => "Rte",
"Row" => "Row",
"Rue" => "Rue",
"Ruta" => "Ruta",
"Sector" => "Sec",
"Sendero" => "Sendero",
"Service Road" => "Svc Rd",
"Skyway" => "Skwy",
"Square" => "Sq",
"State Forest Service Road" => "St FS Rd",
"State Highway" => "State Hwy",
"State Loop" => "State Loop",
"State Road" => "State Rd",
"State Route" => "State Rte",
"State Spur" => "State Spur",
"State Trunk Highway" => "St Trunk Hwy",
"Terrace" => "Ter",
"Town Highway" => "Town Hwy",
"Town Road" => "Town Rd",
"Township Highway" => "Twp Hwy",
"Township Road" => "Twp Rd",
"Trail" => "Trl",
"Tribal Road" => "Tribal Rd",
"Tunnel" => "Tunl",
"US Forest Service Highway" => "USFS Hwy",
"US Forest Service Road" => "USFS Rd",
"US Highway" => "US Hwy",
"US Route" => "US Rte",
"Vereda" => "Ver",
"Via" => "Via",
"Vista" => "Vis",
}
# The Prefix_Alternate constant maps alternate prefix street types to
# their canonical abbreviations. This list was merged in from the USPS
# list at http://www.usps.com/ncsc/lookups/abbr_suffix.txt.
Prefix_Alternate = {
"Av" => "Ave",
"Aven" => "Ave",
"Avenu" => "Ave",
"Avenue" => "Ave",
"Avn" => "Ave",
"Avnue" => "Ave",
"Boul" => "Blvd",
"Boulv" => "Blvd",
"Bypa" => "Byp",
"Bypas" => "Byp",
"Byps" => "Byp",
"Crt" => "Ct",
"Exp" => "Expy",
"Expr" => "Expy",
"Express" => "Expy",
"Expw" => "Expy",
"Highwy" => "Hwy",
"Hiway" => "Hwy",
"Hiwy" => "Hwy",
"Hway" => "Hwy",
#"La" => "Ln", # causes problems with Spanglish place names
"Lanes" => "Ln",
"Loops" => "Loop",
"Plza" => "Plz",
"Sqr" => "Sq",
"Sqre" => "Sq",
"Squ" => "Sq",
"Terr" => "Ter",
"Tr" => "Trl",
"Trails" => "Trl",
"Trls" => "Trl",
"Tunel" => "Tunl",
"Tunls" => "Tunl",
"Tunnels" => "Tunl",
"Tunnl" => "Tunl",
"Vdct" => "Via",
"Viadct" => "Via",
"Viaduct" => "Via",
"Vist" => "Vis",
"Vst" => "Vis",
"Vsta" => "Vis"
}
# The Prefix_Type constant merges the canonical prefix type abbreviations
# with their USPS accepted alternates.
Prefix_Type = Map[ Prefix_Canonical.merge(Prefix_Alternate) ]
# The Suffix_Canonical constant maps canonical TIGER/Line street type
# suffixes to their abbreviations. This list is the subset of the list from
# 2008 TIGER/Line technical documentation Appendix E that was extracted from
# a TIGER/Line database import.
Suffix_Canonical = {
"Alley" => "Aly",
"Arcade" => "Arc",
"Avenida" => "Ave",
"Avenue" => "Ave",
"Beltway" => "Beltway",
"Boulevard" => "Blvd",
"Bridge" => "Brg",
"Bypass" => "Byp",
"Causeway" => "Cswy",
"Circle" => "Cir",
"Common" => "Cmn",
"Commons" => "Cmns",
"Corners" => "Cors",
"Court" => "Ct",
"Courts" => "Cts",
"Crescent" => "Cres",
"Crest" => "Crst",
"Crossing" => "Xing",
"Cutoff" => "Cutoff",
"Drive" => "Dr",
"Driveway" => "Driveway",
"Esplanade" => "Esplanade",
"Estates" => "Ests",
"Expressway" => "Expy",
"Forest Highway" => "Forest Hwy",
"Fork" => "Frk",
"Four-Wheel Drive Trail" => "4WD Trl",
"Freeway" => "Fwy",
"Grade" => "Grade",
"Heights" => "Hts",
"Highway" => "Hwy",
"Jeep Trail" => "Jeep Trl",
"Landing" => "Lndg",
"Lane" => "Ln",
"Logging Road" => "Logging Rd",
"Loop" => "Loop",
"Motorway" => "Mtwy",
"Oval" => "Oval",
"Overpass" => "Opas",
"Parkway" => "Pkwy",
"Pass" => "Pass",
"Passage" => "Psge",
"Path" => "Path",
"Pike" => "Pike",
"Place" => "Pl",
"Plaza" => "Plz",
"Point" => "Pt",
"Pointe" => "Pointe",
"Promenade" => "Promenade",
"Railroad" => "RR",
"Railway" => "Rlwy",
"Ramp" => "Ramp",
"River" => "Riv",
"Road" => "Rd",
"Roadway" => "Roadway",
"Route" => "Rte",
"Row" => "Row",
"Rue" => "Rue",
"Service Road" => "Svc Rd",
"Skyway" => "Skwy",
"Spur" => "Spur",
"Square" => "Sq",
"Stravenue" => "Stra",
"Street" => "St",
"Strip" => "Strip",
"Terrace" => "Ter",
"Thoroughfare" => "Thoroughfare",
"Tollway" => "Tollway",
"Trace" => "Trce",
"Trafficway" => "Trfy",
"Trail" => "Trl",
"Trolley" => "Trolley",
"Truck Trail" => "Truck Trl",
"Tunnel" => "Tunl",
"Turnpike" => "Tpke",
"Viaduct" => "Viaduct",
"View" => "Vw",
"Vista" => "Vis",
"Walk" => "Walk",
"Walkway" => "Walkway",
"Way" => "Way",
}
# The Suffix_Alternate constant maps alternate suffix street types to
# their canonical abbreviations. This list was merged in from the USPS
# list at http://www.usps.com/ncsc/lookups/abbr_suffix.txt.
Suffix_Alternate = {
"Allee" => "Aly",
"Ally" => "Aly",
"Av" => "Ave",
"Aven" => "Ave",
"Avenu" => "Ave",
"Avenue" => "Ave",
"Avn" => "Ave",
"Avnue" => "Ave",
"Boul" => "Blvd",
"Boulv" => "Blvd",
"Brdge" => "Brg",
"Bypa" => "Byp",
"Bypas" => "Byp",
"Byps" => "Byp",
"Causway" => "Cswy",
"Circ" => "Cir",
"Circl" => "Cir",
"Crcl" => "Cir",
"Crcle" => "Cir",
"Crecent" => "Cres",
"Cresent" => "Cres",
"Crscnt" => "Cres",
"Crsent" => "Cres",
"Crsnt" => "Cres",
"Crssing" => "Xing",
"Crssng" => "Xing",
"Crt" => "Ct",
"Driv" => "Dr",
"Drv" => "Dr",
"Exp" => "Expy",
"Expr" => "Expy",
"Express" => "Expy",
"Expw" => "Expy",
"Freewy" => "Fwy",
"Frway" => "Fwy",
"Frwy" => "Fwy",
"Height" => "Hts",
"Hgts" => "Hts",
"Highwy" => "Hwy",
"Hiway" => "Hwy",
"Hiwy" => "Hwy",
"Ht" => "Hts",
"Hway" => "Hwy",
"La" => "Ln",
"Lanes" => "Ln",
"Lndng" => "Lndg",
"Loops" => "Loop",
"Ovl" => "Oval",
"Parkways" => "Pkwy",
"Parkwy" => "Pkwy",
"Paths" => "Path",
"Pikes" => "Pike",
"Pkway" => "Pkwy",
"Pkwys" => "Pkwy",
"Pky" => "Pkwy",
"Plza" => "Plz",
"Rivr" => "Riv",
"Rvr" => "Riv",
"Spurs" => "Spur",
"Sqr" => "Sq",
"Sqre" => "Sq",
"Squ" => "Sq",
"Str" => "St",
"Strav" => "Stra",
"Strave" => "Stra",
"Straven" => "Stra",
"Stravn" => "Stra",
"Strt" => "St",
"Strvn" => "Stra",
"Strvnue" => "Stra",
"Terr" => "Ter",
"Tpk" => "Tpke",
"Tr" => "Trl",
"Traces" => "Trce",
"Trails" => "Trl",
"Trls" => "Trl",
"Trnpk" => "Tpke",
"Trpk" => "Tpke",
"Tunel" => "Tunl",
"Tunls" => "Tunl",
"Tunnels" => "Tunl",
"Tunnl" => "Tunl",
"Turnpk" => "Tpke",
"Vist" => "Vis",
"Vst" => "Vis",
"Vsta" => "Vis",
"Walks" => "Walk",
"Wy" => "Way",
}
# The Suffix_Type constant merges the canonical suffix type abbreviations
# with their USPS accepted alternates.
Suffix_Type = Map[ Suffix_Canonical.merge(Suffix_Alternate) ]
# The Unit_Type constant lists acceptable USPS unit type abbreviations
# from http://www.usps.com/ncsc/lookups/abbr_sud.txt.
Unit_Type = Map[
"Apartment" => "Apt",
"Basement" => "Bsmt",
"Building" => "Bldg",
"Department"=> "Dept",
"Floor" => "Fl",
"Front" => "Frnt",
"Hangar" => "Hngr",
"Lobby" => "Lbby",
"Lot" => "Lot",
"Lower" => "Lowr",
"Office" => "Ofc",
"Penthouse" => "Ph",
"Pier" => "Pier",
"Rear" => "Rear",
"Room" => "Rm",
"Side" => "Side",
"Slip" => "Slip",
"Space" => "Spc",
"Stop" => "Stop",
"Suite" => "Ste",
"Trailer" => "Trlr",
"Unit" => "Unit",
"Upper" => "Uppr",
]
Std_Abbr = Map[
[Directional, Prefix_Qualifier, Suffix_Qualifier,
Prefix_Type, Suffix_Type].inject({}) {|x,y|x.merge y}
]
# The Name_Abbr constant maps common toponym abbreviations to their
# full word equivalents. This list was constructed partly by hand, and
# partly by matching USPS alternate abbreviations with feature names
# found in the TIGER/Line dataset.
Name_Abbr = Map[
"Av" => "Avenue",
"Ave" => "Avenue",
"Blvd" => "Boulevard",
"Bot" => "Bottom",
"Boul" => "Boulevard",
"Boulv" => "Boulevard",
"Br" => "Branch",
"Brg" => "Bridge",
"Canyn" => "Canyon",
"Cen" => "Center",
"Cent" => "Center",
"Cir" => "Circle",
"Circ" => "Circle",
"Ck" => "Creek",
"Cnter" => "Center",
"Cntr" => "Center",
"Cnyn" => "Canyon",
"Cor" => "Corner",
"Cors" => "Corners",
"Cp" => "Camp",
"Cr" => "Creek",
"Crcl" => "Circle",
"Crcle" => "Circle",
"Cres" => "Crescent",
"Crscnt" => "Crescent",
"Ct" => "Court",
"Ctr" => "Center",
"Cts" => "Courts",
"Cyn" => "Canyon",
"Div" => "Divide",
"Dr" => "Drive",
"Dv" => "Divide",
"Est" => "Estate",
"Ests" => "Estates",
"Ext" => "Extension",
"Extn" => "Extension",
"Extnsn" => "Extension",
"Forests" => "Forest",
"Forg" => "Forge",
"Frg" => "Forge",
"Ft" => "Fort",
"Gatewy" => "Gateway",
"Gdn" => "Garden",
"Gdns" => "Gardens",
"Gtwy" => "Gateway",
"Harb" => "Harbor",
"Hbr" => "Harbor",
"Height" => "Heights",
"Hgts" => "Heights",
"Highwy" => "Highway",
"Hiway" => "Highway",
"Hiwy" => "Highway",
"Holws" => "Hollow",
"Ht" => "Heights",
"Hway" => "Highway",
"Hwy" => "Highway",
"Is" => "Island",
"Iss" => "Islands",
"Jct" => "Junction",
"Jction" => "Junction",
"Jctn" => "Junction",
"Junctn" => "Junction",
"Juncton" => "Junction",
"Ldg" => "Lodge",
"Lgt" => "Light",
"Lndg" => "Landing",
"Lodg" => "Lodge",
"Loops" => "Loop",
"Mt" => "Mount",
"Mtin" => "Mountain",
"Mtn" => "Mountain",
"Orch" => "Orchard",
"Parkwy" => "Parkway",
"Pk" => "Park",
"Pkway" => "Parkway",
"Pkwy" => "Parkway",
"Pky" => "Parkway",
"Pl" => "Place",
"Pnes" => "Pines",
"Pr" => "Prairie",
"Prr" => "Prairie",
"Pt" => "Point",
"Pts" => "Points",
"Rdg" => "Ridge",
"Riv" => "River",
"Rnchs" => "Ranch",
"Spg" => "Spring",
"Spgs" => "Springs",
"Spng" => "Spring",
"Spngs" => "Springs",
"Sq" => "Square",
"Squ" => "Square",
# "St" => "Saint",
"Sta" => "Station",
"Statn" => "Station",
"Ste" => "Sainte",
"Stn" => "Station",
"Str" => "Street",
"Ter" => "Terrace",
"Terr" => "Terrace",
"Tpk" => "Turnpike",
"Tpke" => "Turnpike",
"Tr" => "Trail",
"Trls" => "Trail",
"Trpk" => "Turnpike",
"Tunls" => "Tunnel",
"Un" => "Union",
"Vill" => "Village",
"Villag" => "Village",
"Villg" => "Village",
"Vis" => "Vista",
"Vlg" => "Village",
"Vlgs" => "Villages",
"Wls" => "Wells",
"Wy" => "Way",
"Xing" => "Crossing",
]
# The State constant maps US state and territory names to their 2-letter
# USPS abbreviations.
State = Map[
"Alabama" => "AL",
"Alaska" => "AK",
"American Samoa" => "AS",
"Arizona" => "AZ",
"Arkansas" => "AR",
"California" => "CA",
"Colorado" => "CO",
"Connecticut" => "CT",
"Delaware" => "DE",
"District of Columbia" => "DC",
"Federated States of Micronesia" => "FM",
"Florida" => "FL",
"Georgia" => "GA",
"Guam" => "GU",
"Hawaii" => "HI",
"Idaho" => "ID",
"Illinois" => "IL",
"Indiana" => "IN",
"Iowa" => "IA",
"Kansas" => "KS",
"Kentucky" => "KY",
"Louisiana" => "LA",
"Maine" => "ME",
"Marshall Islands" => "MH",
"Maryland" => "MD",
"Massachusetts" => "MA",
"Michigan" => "MI",
"Minnesota" => "MN",
"Mississippi" => "MS",
"Missouri" => "MO",
"Montana" => "MT",
"Nebraska" => "NE",
"Nevada" => "NV",
"New Hampshire" => "NH",
"New Jersey" => "NJ",
"New Mexico" => "NM",
"New York" => "NY",
"North Carolina" => "NC",
"North Dakota" => "ND",
"Northern Mariana Islands" => "MP",
"Ohio" => "OH",
"Oklahoma" => "OK",
"Oregon" => "OR",
"Palau" => "PW",
"Pennsylvania" => "PA",
"Puerto Rico" => "PR",
"Rhode Island" => "RI",
"South Carolina" => "SC",
"South Dakota" => "SD",
"Tennessee" => "TN",
"Texas" => "TX",
"Utah" => "UT",
"Vermont" => "VT",
"Virgin Islands" => "VI",
"Virginia" => "VA",
"Washington" => "WA",
"West Virginia" => "WV",
"Wisconsin" => "WI",
"Wyoming" => "WY"
]
end
================================================
FILE: lib/geocoder/us/database.rb
================================================
# require 'rubygems'
require 'sqlite3'
# require 'text'
# require 'levenshtein'
require 'set'
require 'pp'
require 'time'
require 'thread'
require 'geocoder/us/address'
require 'geocoder/us/metaphone'
module Geocoder
end
module Geocoder::US
# Provides an interface to a Geocoder::US database.
class Database
Street_Weight = 3.0
Number_Weight = 2.0
Parity_Weight = 1.25
City_Weight = 1.0
@@mutex = Mutex.new
# Takes the path of an SQLite 3 database prepared for Geocoder::US
# as the sole mandatory argument. The helper argument points to the
# Geocoder::US SQLite plugin; the module looks for this in the same
# directory as database.rb by default. The cache_size argument is
# measured in kilobytes and is used to set the SQLite cache size; larger
# values will trade memory for speed in long-running processes.
# dbtype option is used when your datasbase encodes it's geometry blogs according to two formats
# the first. default, is in a series of little-endian 4-byte ints. The second is
# | 1 byte Type | 4 byte SRID | 4 byte element count| 8 byte double coordinates. Use option value 2 for this
# second type
def initialize (filename, options = {})
defaults = {:debug => false, :cache_size => 50000,
:helper => "sqlite3.so", :threadsafe => false,
:create => false, :dbtype => 1}
options = defaults.merge options
raise ArgumentError, "can't find database #{filename}" \
unless options[:create] or File.exists? filename
@db = SQLite3::Database.new( filename )
@st = {}
@dbtype = options[:dbtype]
@debug = options[:debug]
@threadsafe = options[:threadsafe]
tune options[:helper], options[:cache_size]
end
def synchronize
if not @threadsafe
@@mutex.synchronize { yield }
else
yield
end
end
#private
# Load the SQLite extension and tune the database settings.
# q.v. http://web.utk.edu/~jplyon/sqlite/SQLite_optimization_FAQ.html
def tune (helper, cache_size)
if File.expand_path(helper) != helper
helper = File.join(File.dirname(__FILE__), helper)
end
synchronize do
# @db.create_function("levenshtein", 2) do |func, word1, word2|
# test1, test2 = [word1, word2].map {|w|
# w.to_s.gsub(/\W/o, "").downcase
# }
# dist = Levenshtein.distance(test1, test2)
# result = dist.to_f / [test1.length, test2.length].max
# func.set_result result
# end
# @db.create_function("rb_metaphone", 2) do |func, string, len|
# test = string.to_s.gsub(/\W/o, "")
# if test =~ /^(\d+)/o
# mph = $1
# elsif test =~ /^([wy])$/io
# mph = $1
# else
# mph = Text::Metaphone.metaphone test
# end
# func.result = mph[0...len.to_i]
# end
# @db.create_function("nondigit_prefix", 1) do |func, string|
# string.to_s =~ /^(.*\D)?(\d+)$/o
# func.result = ($1 || "")
# end
# @db.create_function("digit_suffix", 1) do |func, string|
# string.to_s =~ /^(.*\D)?(\d+)$/o
# func.result = ($2 || "")
# end
@db.enable_load_extension(1)
@db.load_extension(helper)
@db.enable_load_extension(0)
@db.cache_size = cache_size
@db.temp_store = "memory"
@db.synchronous = "off"
end
end
# Return a cached SQLite statement object, preparing it first if
# it's not already in the cache.
def prepare (sql)
$stderr.print "SQL : #{sql}\n" if @debug
st = nil
synchronize do
# don't even bother cache SQL anymore, it seems to be messing things up
#@st[sql] = @db.prepare sql if not @st[sql] or @st[sql].closed?
st = @db.prepare sql
end
# return @st[sql]
return st
end
def flush_statements
@st = {}
end
# Generate enough SQL placeholders for a list of objects.
def placeholders_for (list)
(["?"] * list.length).join(",")
end
# Generate enough SQL placeholders for a list of objects.
def metaphone_placeholders_for (list)
(["metaphone(?,5)"] * list.length).join(",")
end
# Execute an SQL statement, bind a list of parameters, and
# return the result as a list of hashes.
def execute (sql, *params)
st = prepare(sql)
begin
execute_statement st, *params
ensure
st.close
end
end
# Execute an SQLite statement object, bind the parameters,
# map the column names to symbols, and return the rows
# as a list of hashes.
def execute_statement (st, *params)
if @debug
start = Time.now
$stderr.print "EXEC: #{params.inspect}\n" if !params.empty?
end
rows = []
synchronize do
result = st.execute(*params)
columns = result.columns.map {|c| c.to_sym}
result.each {|row|
rows << Hash[*(columns.zip(row).flatten)]}
end
if @debug
runtime = format("%.3f", Time.now - start)
$stderr.print "ROWS: #{rows.length} (#{runtime}s)\n"
end
rows.reverse!
end
def places_by_zip (city, zip)
execute("SELECT *, levenshtein(?, city) AS city_score
FROM place WHERE zip = ? order by priority desc;", city, zip)
end
# Query the place table for by city, optional state, and zip.
# The metaphone index on the place table is used to match
# city names.
def places_by_city (city, tokens, state)
if city.nil?
city = ""
end
if state.nil? or state.empty?
and_state = ""
args = [city] + tokens.clone
else
and_state = "AND state = ?"
args = [city] + tokens.clone + [state]
end
metaphones = metaphone_placeholders_for tokens
execute("SELECT *, levenshtein(?, city) AS city_score
FROM place WHERE city_phone IN (#{metaphones}) #{and_state} order by priority desc;", *args)
end
# Generate an SQL query and set of parameters against the feature and range
# tables for a street name and optional building number. The SQL is
# used by candidate_records and more_candidate_records to filter results
# by ZIP code.
def features_by_street (street, tokens)
metaphones = (["metaphone(?,5)"] * tokens.length).join(",")
sql = "
SELECT feature.*, levenshtein(?, street) AS street_score
FROM feature
WHERE street_phone IN (#{metaphones})"
params = [street] + tokens
return [sql, params]
end
# Query the feature and range tables for a set of ranges, given a
# building number, street name, and list of candidate ZIP codes.
# The metaphone and ZIP code indexes on the feature table are
# used to match results.
def features_by_street_and_zip (street, tokens, zips)
sql, params = features_by_street(street, tokens)
in_list = placeholders_for zips
sql += " AND feature.zip IN (#{in_list})"
params += zips
execute sql, *params
end
# Query the feature and range tables for a set of ranges, given a
# building number, street name, and list of candidate ZIP codes.
# The ZIP codes are reduced to a set of 3-digit prefixes, broadening
# the search area.
def more_features_by_street_and_zip (street, tokens, zips)
sql, params = features_by_street(street, tokens)
if !zips.empty? and !zips[0].nil?
#puts "zip results 2"
zip3s = zips.map {|z| z[0..2]+'%'}.to_set.to_a
like_list = zip3s.map {|z| "feature.zip LIKE ?"}.join(" OR ")
sql += " AND (#{like_list})"
params += zip3s
end
execute sql, *params
end
def ranges_by_feature (fids, number, prenum)
in_list = placeholders_for fids
limit = 4 * fids.length
sql = "
SELECT feature_edge.fid AS fid, range.*
FROM feature_edge, range
WHERE fid IN (#{in_list})
AND feature_edge.tlid = range.tlid"
params = fids.clone
unless prenum.nil?
sql += " AND prenum = ?"
params += [prenum]
end
sql += "
ORDER BY min(abs(fromhn - ?), abs(tohn - ?))
LIMIT #{limit};"
params += [number, number]
execute sql, *params
end
# Query the edge table for a list of edges matching a list of edge IDs.
def edges (edge_ids)
in_list = placeholders_for edge_ids
sql = "SELECT edge.* FROM edge WHERE edge.tlid IN (#{in_list})"
execute sql, *edge_ids
end
# Query the range table for all ranges associated with the given
# list of edge IDs.
def range_ends (edge_ids)
in_list = placeholders_for edge_ids
sql = "SELECT tlid, side,
min(fromhn) > min(tohn) AS flipped,
min(fromhn) AS from0, max(tohn) AS to0,
min(tohn) AS from1, max(fromhn) AS to1
FROM range WHERE tlid IN (#{in_list})
GROUP BY tlid, side;"
execute(sql, *edge_ids).map {|r|
if r[:flipped].to_i == 1
r[:flipped] = true
r[:fromhn], r[:tohn] = r[:from1], r[:to1]
else
r[:flipped] = false
r[:fromhn], r[:tohn] = r[:from0], r[:to0]
end
[:from0, :to0, :from1, :to1].each {|k| r.delete k}
r
}
end
def intersections_by_fid (fids)
temp_db = "temp_" + rand(1<<32).to_s
temp_table = "intersection_" + rand(1<<32).to_s
execute "ATTACH DATABASE ':memory:' as #{temp_db};"
begin
# flush_statements # the CREATE/DROP TABLE invalidates prepared statements
in_list = placeholders_for fids
sql = "
CREATE TABLE #{temp_db}.#{temp_table} AS
SELECT fid, substr(geometry,1,8) AS point
FROM feature_edge, edge
WHERE feature_edge.tlid = edge.tlid
AND fid IN (#{in_list})
UNION
SELECT fid, substr(geometry,length(geometry)-7,8) AS point
FROM feature_edge, edge
WHERE feature_edge.tlid = edge.tlid
AND fid IN (#{in_list});
CREATE INDEX #{temp_db}.#{temp_table}_pt_idx ON #{temp_table} (point);"
execute sql, *(fids + fids)
# the a.fid < b.fid inequality guarantees consistent ordering of street
# names in the output
sql = "
SELECT a.fid AS fid1, b.fid AS fid2, a.point
FROM #{temp_table} a, #{temp_table} b,
feature f1, feature f2
WHERE a.point = b.point AND a.fid < b.fid
AND f1.fid = a.fid AND f2.fid = b.fid
AND f1.zip = f2.zip
AND f1.paflag = 'P' AND f2.paflag = 'P';"
return execute sql
ensure
# flush_statements # the CREATE/DROP TABLE invalidates prepared statements
execute "DETACH DATABASE #{temp_db};"
end
end
# Query the place table for notional "primary" place names for each of a
# list of ZIP codes. Since the place table shipped with this code is
# bespoke, and constructed from a variety of public domain sources,
# the primary name for a ZIP is not always the "right" one.
def primary_places (zips)
in_list = placeholders_for zips
sql = "SELECT * FROM place WHERE zip IN (#{in_list}) order by priority desc;"
execute sql, *zips
end
# Given a list of rows, find the unique values for a given key.
def unique_values (rows, key)
rows.map {|r| r[key]}.to_set.to_a
end
# Convert a list of rows into a hash keyed by the given keys.
def rows_to_h (rows, *keys)
hash = {}
rows.each {|row| (hash[row.values_at(*keys)] ||= []) << row; }
hash
end
# Merge the values in the list of rows given in src into the
# list of rows in dest, matching rows on the given list of keys.
# May generate more than one row in dest for each input dest row.
def merge_rows! (dest, src, *keys)
src = rows_to_h src, *keys
dest.map! {|row|
vals = row.values_at(*keys)
if src.key? vals
src[vals].map {|row2| row.merge row2}
else
[row]
end
}
dest.flatten!
end
def find_candidates (address)
places = []
candidates = []
city = address.city.sort {|a,b|a.length <=> b.length}[0]
if(!address.zip.empty? && !address.zip.nil?)
places = places_by_zip city, address.zip
end
places = places_by_city city, address.city_parts, address.state if places.empty?
return [] if places.empty?
# setting city will remove city from street, so save off before
address.city = unique_values places, :city
return places if address.street.empty?
zips = unique_values places, :zip
street = address.street.sort {|a,b|a.length <=> b.length}[0]
candidates = features_by_street_and_zip street, address.street_parts, zips
if candidates.empty?
candidates = more_features_by_street_and_zip street, address.street_parts, zips
end
merge_rows! candidates, places, :zip
candidates
end
# Given a query hash and a list of candidates, assign :number
# and :precision values to each candidate. If the query building
# number is inside the candidate range, set the number on the result
# and set the precision to :range; otherwise, find the closest
# corner and set precision to :street.
def assign_number! (hn, candidates)
hn = 0 unless hn
for candidate in candidates
fromhn, tohn = candidate[:fromhn].to_i, candidate[:tohn].to_i
if (hn >= fromhn and hn <= tohn) or (hn <= fromhn and hn >= tohn)
candidate[:number] = hn.to_s
candidate[:precision] = :range
else
candidate[:number] = ((hn - fromhn).abs < (hn - tohn).abs ?
candidate[:fromhn] : candidate[:tohn]).to_s
candidate[:precision] = :street
end
end
end
def add_ranges! (address, candidates)
number = address.number.to_i
fids = unique_values candidates, :fid
ranges = ranges_by_feature fids, number, address.prenum
ranges = ranges_by_feature fids, number, nil unless !ranges.empty?
merge_rows! candidates, ranges, :fid
assign_number! number, candidates
end
def merge_edges! (candidates)
edge_ids = unique_values candidates, :tlid
records = edges edge_ids
merge_rows! candidates, records, :tlid
candidates.reject! {|record| record[:tlid].nil?}
edge_ids
end
def extend_ranges! (candidates)
edge_ids = merge_edges! candidates
full_ranges = range_ends edge_ids
merge_rows! candidates, full_ranges, :tlid, :side
end
# Score a list of candidates. For each candidate:
# * For each item in the query:
# ** if the query item is blank but the candidate is not, score 0.15;
# otherwise, if both are blank, score 1.0.
# ** If both items are set, compute the scaled Levenshtein-Damerau distance
# between them, and add that value (between 0.0 and 1.0) to the score.
# * Add 0.5 to the score for each numbered end of the range that matches
# the parity of the query number.
# * Add 1.0 if the query number is in the candidate range, otherwise
# add a fractional value for the notional distance between the
# closest candidate corner and the query.
# * Finally, divide the score by the total number of comparisons.
# The result should be between 0.0 and 1.0, with 1.0 indicating a
# perfect match.
def score_candidates! (address, candidates)
for candidate in candidates
candidate[:components] = {}
compare = [:prenum, :state, :zip]
denominator = compare.length + Street_Weight + City_Weight
street_score = (1.0 - candidate[:street_score].to_f) * Street_Weight
candidate[:components][:street] = street_score
city_score = (1.0 - candidate[:city_score].to_f) * City_Weight
candidate[:components][:city] = city_score
score = street_score + city_score
compare.each {|key|
src = address.send(key); src = src ? src.downcase : ""
dest = candidate[key]; dest = dest ? dest.downcase : ""
item_score = (src == dest) ? 1 : 0
candidate[:components][key] = item_score
score += item_score
}
if address.number and !address.number.empty?
parity = subscore = 0.0
fromhn, tohn, assigned, hn = [
candidate[:fromhn],
candidate[:tohn],
candidate[:number],
address.number].map {|s|s.to_i}
if candidate[:precision] == :range
subscore += Number_Weight
elsif assigned > 0
# only credit number subscore if assigned
subscore += Number_Weight/(assigned - hn).abs.to_f
end
candidate[:components][:number] = subscore
if hn > 0 and assigned > 0
# only credit parity if a number was given *and* assigned
parity += Parity_Weight/2.0 if fromhn % 2 == hn % 2
parity += Parity_Weight/2.0 if tohn % 2 == hn % 2
end
candidate[:components][:parity] = parity
score += subscore + parity
denominator += Number_Weight + Parity_Weight
end
candidate[:components][:total] = score.to_f
candidate[:components][:denominator] = denominator
candidate[:score] = score.to_f / denominator
end
end
# Find the candidates in a list of candidates that are tied for the
# top score and prune the remainder from the list.
def best_candidates! (candidates)
candidates.sort! {|a,b| b[:score] <=> a[:score]}
#candidates.reverse_each {|c| print "#{c[:number]} #{c[:state]} #{c[:city]} #{c[:raw_score]} #{c[:number_score]} #{c[:street_score]} #{c[:city_score]}\n" }
candidates.delete_if {|record| record[:score] < candidates[0][:score]}
end
# Compute the fractional interpolation distance for a query number along an
# edge, given all of the ranges for the same side of that edge.
def interpolation_distance (candidate)
fromhn, tohn, number = candidate.values_at(:fromhn, :tohn, :number).map{|x| x.to_i}
$stderr.print "NUM : #{fromhn} < #{number} < #{tohn} (flipped? #{candidate[:flipped]})\n" if @debug
# don't need this anymore since range_ends was improved...
fromhn, tohn = tohn, fromhn if fromhn > tohn
if fromhn > number
0.0
elsif tohn < number
1.0
elsif tohn == fromhn
# this is not supposed to happen, per Census Bureau rules, but apparently it does anyway.
0.0
else
(number - fromhn) / (tohn - fromhn).to_f
end
end
# Unpack an array of little-endian 4-byte ints, and convert them into
# signed floats by dividing by 10^6, inverting the process used by the
# compress_wkb_line() function in the SQLite helper extension.
def unpack_geometry (geom)
points = []
if !geom.nil?
if @dbtype == 2
# For special case?
#| 1 byte Type | 4 byte SRID | 4 byte element count| 8 byte double coordinates *
info = geom.unpack('CVVD*')
coords = info.slice(3, info.length)
else
#old format
coords = geom.unpack "V*" # little-endian 4-byte long ints
## now map them into signed floats
coords.map! {|i| ( i > (1 << 31) ? i - (1 << 32) : i ) / 1_000_000.0}
end
points << [coords.shift, coords.shift] until coords.empty?
end
points
end
# Calculate the longitude scaling for the average of two latitudes.
def scale_lon (lat1,lat2)
# an approximation in place of lookup.rst (10e) and (10g)
# = scale longitude distances by the cosine of the latitude
# (or, actually, the mean of two latitudes)
# -- is this even necessary?
Math.cos((lat1+lat2) / 2 * Math::PI / 180)
end
# Simple Euclidean distances between two 2-D coordinate pairs, scaled
# along the longitudinal axis by scale_lon.
def distance (a, b)
dx = (b[0] - a[0]) * scale_lon(a[1], b[1])
dy = (b[1] - a[1])
Math.sqrt(dx ** 2 + dy ** 2)
end
def street_side_offset (b, p1, p2)
# Find a point (x2, y2) that is at a distance b from a line A=(x0, y0)-(x1, y1)
# along a tangent B passing through (x1,y1)-(x2,y2).
# Let a = the length of line A
a = Math.sqrt((p2[0]-p1[0])**2.0 + (p2[1]-p1[1])**2.0)
# theta is the angle between the line A and the x axis
theta = Math.atan2(p2[1]-p1[1], p2[0]-p1[0])
# Now lines A and B form a right triangle where the third vertex is (x2, y2).
# Let c = the length of the hypotenuse line C=(x0,y0)-(x2,y2)
c = Math.sqrt(a**2.0 + b**2.0)
# Now alpha is the angle between lines A and C.
alpha = Math.atan2(b, a)
# Therefore the difference between theta and alpha is the angle between C and
# the x axis. Since we know the length of C, the lengths of the two lines
# parallel to the axes that form a right triangle C, and hence the
# coordinates (x2, y2), fall out.
return [p1[0] + c * Math.cos(theta - alpha), p1[1] + c * Math.sin(theta - alpha)]
end
# Find an interpolated point along a list of linestring vertices
# proportional to the given fractional distance along the line.
# Offset is in degrees and defaults to ~8 meters.
def interpolate (points, fraction, side, offset=0.000075)
$stderr.print "POINTS: #{points.inspect}" if @debug
return points[0] if fraction == 0.0
return points[-1] if fraction == 1.0
total = 0.0
(1...points.length).each {|n| total += distance(points[n-1], points[n])}
target = total * fraction
for n in 1...points.length
next if points[n-1] == points[n] # because otherwise step==0 and dx/dy==NaN
step = distance(points[n-1], points[n])
if step < target
target -= step
else
scale = scale_lon(points[n][1], points[n-1][1])
dx = (points[n][0] - points[n-1][0]) * (target/step) * scale
dy = (points[n][1] - points[n-1][1]) * (target/step)
found = [points[n-1][0]+dx, points[n-1][1]+dy]
return street_side_offset(offset*side, points[n-1], found)
end
end
# in a pathological case, points[n-1] == points[n] for n==-1
# so *sigh* just forget interpolating and return points[-1]
return points[-1]
end
# Find and replace the city, state, and county information
# in a list of candidates with the primary place information
# for the ZIP codes in the candidate list.
def canonicalize_places! (candidates)
zips_used = unique_values(candidates, :zip)
pri_places = rows_to_h primary_places(zips_used), :zip
candidates.map! {|record|
current_places = pri_places[[record[:zip]]]
# FIXME: this should never happen!
return [] unless current_places
top_priority = current_places.map{|p| p[:priority]}.min
current_places.select {|p| p[:priority] == top_priority}.map {|p|
record.merge({
:city => p[:city],
:state => p[:state],
:fips_county => p[:fips_county]
})
}
}
candidates.flatten!
end
# Clean up a candidate record by formatting the score, replacing nil
# values with empty strings, and deleting artifacts from database
# queries.
def clean_record! (record)
record[:score] = format("%.3f", record[:score]).to_f \
unless record[:score].nil?
record.keys.each {|k| record[k] = "" if record[k].nil? } # clean up nils
record.delete :components unless @debug
record.delete_if {|k,v| k.is_a? Fixnum or
[:geometry, :side, :tlid, :fid, :fid1, :fid2, :street_phone,
:city_phone, :fromhn, :tohn, :paflag, :flipped, :street_score,
:city_score, :priority, :fips_class, :fips_place, :status].include? k}
end
def best_places (address, places, canonicalize=false)
return [] unless !places.empty?
score_candidates! address, places
best_candidates! places
canonicalize_places! places if canonicalize
# uniqify places
by_name = rows_to_h(places, :city, :state)
if !by_name.nil?
begin
by_name.values.each {|v|
v.sort! {|a,b|
a[:zip] <=> b[:zip]
}}
rescue
end
places = by_name.map {|k,v| v[0]}
places.each {|record| clean_record! record}
places.each {|record|
record[:precision] = (record[:zip] == address.zip ? :zip : :city)
}
end
places
end
# Given an Address object, return a list of possible geocodes by place
# name. If canonicalize is true, attempt to return the "primary" postal
# place name for the given city, state, or ZIP.
def geocode_place (address, canonicalize=false)
places = []
places = places_by_zip address.text, address.zip if !address.zip.empty? or !address.zip.nil?
places = places_by_city address.text, address.city_parts, address.state if places.empty?
best_places address, places, canonicalize
end
def geocode_intersection (address, canonical_place=false)
candidates = find_candidates address
return [] if candidates.empty?
return best_places(address, candidates, canonical_place) if candidates[0][:street].nil?
features = rows_to_h candidates, :fid
intersects = intersections_by_fid features.keys.flatten
intersects.map! {|record|
feat1, feat2 = record.values_at(:fid1, :fid2).map {|k| features[[k]][0]}
record.merge! feat1
record[:street1] = record.delete(:street)
record[:street2] = feat2[:street]
record[:lon], record[:lat] = unpack_geometry(record.delete(:point))[0]
record[:precision] = :intersection
record[:street_score] = (feat1[:street_score].to_f + feat2[:street_score].to_f)/2
record
}
#pp(intersects)
score_candidates! address, intersects
best_candidates! intersects
by_point = rows_to_h(intersects, :lon, :lat)
candidates = by_point.values.map {|records| records[0]}
canonicalize_places! candidates if canonical_place
candidates.each {|record| clean_record! record}
candidates
end
# Given an Address object, return a list of possible geocodes by address
# range interpolation. If canonicalize is true, attempt to return the
# "primary" street and place names, if they are different from the ones
# given.
def geocode_address (address, canonical_place=false)
candidates = find_candidates address
return [] if candidates.empty?
return best_places(address, candidates, canonical_place) if candidates[0][:street].nil?
score_candidates! address, candidates
best_candidates! candidates
#candidates.sort {|a,b| b[:score] <=> a[:score]}.each {|candidate|
add_ranges! address, candidates
score_candidates! address, candidates
#pp candidates.sort {|a,b| b[:score] <=> a[:score]}
best_candidates! candidates
# sometimes multiple fids match the same tlid
by_tlid = rows_to_h candidates, :tlid
candidates = by_tlid.values.map {|records| records[0]}
# if no number is assigned in the query, only return one
# result for each street/zip combo
if !address.number.empty?
extend_ranges! candidates
else
by_street = rows_to_h candidates, :street, :zip
candidates = by_street.values.map {|records| records[0]}
merge_edges! candidates
end
candidates.map {|record|
dist = interpolation_distance record
points = unpack_geometry record[:geometry]
side = (record[:side] == "R" ? 1 : -1)
if record[:flipped]
side *= -1
points.reverse!
end
$stderr.print "DIST: #{dist} FLIPPED: #{record[:flipped]} SIDE: #{side}\n" if @debug
found = interpolate points, dist, side
record[:lon], record[:lat] = found.map {|x| format("%.6f", x).to_f}
}
canonicalize_places! candidates if canonical_place
candidates.each {|record| clean_record! record}
candidates
end
public
# Geocode a given address or place name string. The max_penalty and cutoff
# arguments are passed to the Address parse functions. If canonicalize is
# true, attempt to return the "primary" street and place names, if they are
# different from the ones given.
#
# Returns possible candidate matches as a list of hashes.
#
# * The :lat and :lon values of each hash store the range-interpolated
# address coordinates as latitude and longitude in the WGS84 spheroid.
# * The :precision value may be one of :city, :zip, :street, or :range, in
# order of increasing precision.
# * The :score value will be a float between 0.0 and 1.0 representing
# the approximate "goodness" of the candidate match.
# * The other values in the hash will represent various structured
# components of the address and place name.
def geocode (info_to_geocode, canonical_place=false)
address = Address.new info_to_geocode
$stderr.print "ADDR: #{address.inspect}\n" if @debug
return [] if address.city.empty? and address.zip.empty?
results = []
start_time = Time.now if @debug
if address.po_box? and !address.zip.empty?
results = geocode_place address, canonical_place
end
if address.intersection? and !address.street.empty? and address.number.empty?
results = geocode_intersection address, canonical_place
end
if results.empty? and !address.street.empty?
results = geocode_address address, canonical_place
end
if results.empty?
results = geocode_place address, canonical_place
end
if @debug
runtime = format("%.3f", Time.now - start_time)
$stderr.print "DONE: #{runtime}s\n"
end
results
end
end
end
================================================
FILE: lib/geocoder/us/metaphone.rb
================================================
module Text # :nodoc:
module Metaphone
module Rules # :nodoc:all
# Metaphone rules. These are simply applied in order.
#
STANDARD = [
# Regexp, replacement
[ /([bcdfhjklmnpqrstvwxyz])\1+/,
'\1' ], # Remove doubled consonants except g.
# [PHP] remove c from regexp.
[ /^ae/, 'E' ],
[ /^[gkp]n/, 'N' ],
[ /^wr/, 'R' ],
[ /^x/, 'S' ],
[ /^wh/, 'W' ],
[ /mb$/, 'M' ], # [PHP] remove $ from regexp.
[ /(?!^)sch/, 'SK' ],
[ /th/, '0' ],
[ /t?ch|sh/, 'X' ],
[ /c(?=ia)/, 'X' ],
[ /[st](?=i[ao])/, 'X' ],
[ /s?c(?=[iey])/, 'S' ],
[ /[cq]/, 'K' ],
[ /dg(?=[iey])/, 'J' ],
[ /d/, 'T' ],
[ /g(?=h[^aeiou])/, '' ],
[ /gn(ed)?/, 'N' ],
[ /([^g]|^)g(?=[iey])/,
'\1J' ],
[ /g+/, 'K' ],
[ /ph/, 'F' ],
[ /([aeiou])h(?=\b|[^aeiou])/,
'\1' ],
[ /[wy](?![aeiou])/, '' ],
[ /z/, 'S' ],
[ /v/, 'F' ],
[ /(?!^)[aeiou]+/, '' ],
]
# The rules for the 'buggy' alternate implementation used by PHP etc.
#
BUGGY = STANDARD.dup
BUGGY[0] = [ /([bdfhjklmnpqrstvwxyz])\1+/, '\1' ]
BUGGY[6] = [ /mb/, 'M' ]
end
# Returns the Metaphone representation of a string. If the string contains
# multiple words, each word in turn is converted into its Metaphone
# representation. Note that only the letters A-Z are supported, so any
# language-specific processing should be done beforehand.
#
# If the :buggy option is set, alternate 'buggy' rules are used.
#
def metaphone(str, options={})
return str.strip.split(/\s+/).map { |w| metaphone_word(w, options) }.join(' ')
end
private
def metaphone_word(w, options={})
# Normalise case and remove non-ASCII
s = w.downcase.gsub(/[^a-z]/, '')
# Apply the Metaphone rules
rules = options[:buggy] ? Rules::BUGGY : Rules::STANDARD
rules.each { |rx, rep| s.gsub!(rx, rep) }
return s.upcase
end
extend self
end
end
================================================
FILE: lib/geocoder/us/numbers.rb
================================================
module Geocoder
end
module Geocoder::US
# The NumberMap class provides a means for mapping ordinal
# and cardinal number words to digits and back.
class NumberMap < Hash
attr_accessor :regexp
def self.[] (array)
nmap = self.new({})
array.each {|item| nmap << item }
nmap.build_match
nmap
end
def initialize (array)
@count = 0
end
def build_match
@regexp = Regexp.new(
'\b(' + keys.flatten.join("|") + ')\b',
Regexp::IGNORECASE)
end
def clean (key)
key.is_a?(String) ? key.downcase.gsub(/\W/o, "") : key
end
def <<(item)
store clean(item), @count
store @count, item
@count += 1
end
def [] (key)
super(clean(key))
end
end
# The Cardinals constant maps digits to cardinal number words and back.
Cardinals = NumberMap[%w[
zero one two three four five six seven eight nine ten
eleven twelve thirteen fourteen fifteen sixteen seventeen
eighteen nineteen
]]
Cardinal_Tens = %w[ twenty thirty forty fifty sixty seventy eighty ninety ]
Cardinal_Tens.each {|tens|
Cardinals << tens
(1..9).each {|n| Cardinals << tens + "-" + Cardinals[n]}
}
# The Ordinals constant maps digits to ordinal number words and back.
Ordinals = NumberMap[%w[
zeroth first second third fourth fifth sixth seventh eighth ninth
tenth eleventh twelfth thirteenth fourteenth fifteenth sixteenth
seventeenth eighteenth nineteenth
]]
Cardinal_Tens.each {|tens|
Ordinals << tens.gsub("y","ieth")
(1..9).each {|n| Ordinals << tens + "-" + Ordinals[n]}
}
end
================================================
FILE: lib/geocoder/us/rest.rb
================================================
require 'rubygems'
require 'sinatra'
require 'geocoder/us/database'
require 'json'
@@db = Geocoder::US::Database.new(ENV["GEOCODER_DB"] || ARGV[0])
set :port, 8081
get '/geocode.?:format?' do
if params[:q]
results = @@db.geocode params[:q].gsub(/\s+(and|at)\s+/i,' ')
@features = []
results.each do |result|
coords = [result.delete(:lon), result.delete(:lat)]
result.keys.each do |key|
if result[key].is_a? String
result[key] = result[key].unpack("C*").pack("U*") # utf8
end
end
@features << {
:type => "Feature",
:properties => result,
:geometry => {
:type => "Point",
:coordinates => coords
}
}
end
case params[:format]
when /json/
begin
{
:type => "FeatureCollection",
:address => params[:q],
:features => @features
}.to_json
rescue JSON::GeneratorError
{
:type => "FeatureCollection",
:error => "JSON::GeneratorError",
:features => []
}.to_json
end
else
haml :index
end
else
status 400
"parameter 'q' is missing"
end
end
get '/health' do
"All is well."
end
def radius_for_precision(precision)
case precision
when /range/
50
else
200
end
end
__END__
@@ layout
%html
%head
%link(rel="stylesheet" href="http://leaflet.cloudmade.com/dist/leaflet.css")
%script(src="http://leaflet.cloudmade.com/dist/leaflet.js")
%body
= yield
@@ index
%div#map(style="height:400px")
%div
%h2 Features
%table{:border => "1", :cellspacing => "0", :cellpadding => "4"}
%tr
- @features.first[:properties].each do |key,property|
%th= key
- @features.each do |feature|
%tr
- feature[:properties].each do |key,property|
%td= property
%script
var features = []
- @features.each do |feature|
= "features.push(#{feature.to_json})"
:javascript
var map = new L.Map('map');
var cloudmadeUrl = 'http://acetate.geoiq.com/tiles/acetate/{z}/{x}/{y}.png',
cloudmadeAttrib = 'Map data © 2011 OpenStreetMap contributors, Style © 2011 GeoIQ',
cloudmade = new L.TileLayer(cloudmadeUrl, {maxZoom: 18, attribution: cloudmadeAttrib});
var center = new L.LatLng(#{@features.first[:geometry][:coordinates][1]}, #{@features.first[:geometry][:coordinates][0]});
map.setView(center, 13).addLayer(cloudmade);
circleOptions = {
color: 'red',
fillColor: '#f03',
fillOpacity: 0.5
};
var circleLocation, circle;
for(var i=0; i> require 'geocoder/us'
# >> db = Geocoder::US::Database.new("/opt/tiger/geocoder.db")
# >> p db.geocode("1600 Pennsylvania Av, Washington DC")
#
# [{:pretyp=>"", :street=>"Pennsylvania", :sufdir=>"NW", :zip=>"20502",
# :lon=>-77.037528, :number=>"1600", :fips_county=>"11001", :predir=>"",
# :precision=>:range, :city=>"Washington", :lat=>38.898746, :suftyp=>"Ave",
# :state=>"DC", :prequal=>"", :sufqual=>"", :score=>0.906, :prenum=>""}]
#
# See Geocoder::US::Database and README.txt for more details.
module Geocoder::US
VERSION = "2.0.0"
end
================================================
FILE: navteq/README
================================================
The navteq_import script in this directory is designed to be used with Navteq's
local_streets layer. It works basically like tiger_import, except that you
provide either a list of .zip files containing the local_streets.* files on the
command line, or via standard input.
================================================
FILE: navteq/convert.sql
================================================
BEGIN;
CREATE INDEX navteq_link_id on local_streets (link_id);
CREATE TEMPORARY TABLE linezip AS
SELECT DISTINCT tlid, zip FROM (
SELECT link_id AS tlid, r_postcode AS zip FROM local_streets
WHERE addr_type IS NOT NULL AND st_name IS NOT NULL
AND r_postcode IS NOT NULL
UNION
SELECT link_id AS tlid, l_postcode AS zip FROM local_streets
WHERE addr_type IS NOT NULL AND st_name IS NOT NULL
AND l_postcode IS NOT NULL
) AS whatever;
INSERT INTO feature
SELECT l.tlid, st_nm_base, metaphone(st_nm_base,5), st_nm_pref, st_typ_bef,
NULL, st_nm_suff, st_typ_aft, NULL, 'P', zip
FROM linezip l, local_streets f
WHERE l.tlid=f.link_id AND st_name IS NOT NULL;
INSERT OR IGNORE INTO edge
SELECT l.tlid, compress_wkb_line(the_geom) FROM
(SELECT DISTINCT tlid FROM linezip) AS l, local_streets f
WHERE l.tlid=f.link_id AND st_name IS NOT NULL;
INSERT INTO range
SELECT link_id, digit_suffix(l_refaddr), digit_suffix(l_nrefaddr),
nondigit_prefix(l_refaddr), l_postcode, 'L'
FROM linezip l, local_streets f
WHERE l.tlid=f.link_id AND l_refaddr IS NOT NULL
UNION
SELECT link_id, digit_suffix(r_refaddr), digit_suffix(r_nrefaddr),
nondigit_prefix(r_refaddr), r_postcode, 'R'
FROM linezip l, local_streets f
WHERE l.tlid=f.link_id AND r_refaddr IS NOT NULL;
END;
================================================
FILE: navteq/navteq_import
================================================
#!/bin/bash
TMP="/tmp/navteq-import.$$"
SHPS="local_streets"
DBFS=""
BASE=$(dirname $0)
PATH=$PATH:$BASE/../bin
SQL="$BASE/../sql"
HELPER_LIB="$BASE/../lib/geocoder/us/sqlite3.so"
DATABASE=$1
shift
mkdir -p $TMP || exit 1
[ ! -r $DATABASE ] && cat ${SQL}/create.sql ${SQL}/place.sql | sqlite3 $DATABASE
if [ x"$1" = x"" ]; then
cat
else
ls $@
fi | while read county; do
echo "--- $county"
if [ -r ${county%.zip}.zip ]; then
unzip -q $(ls ${county}.zip) -d $TMP
else
cp ${county%.*}.* $TMP
fi
(echo ".load $HELPER_LIB" && \
cat ${BASE}/prepare.sql && \
for file in $SHPS; do
shp2sqlite -aS $(ls ${TMP}/${file}.shp) ${file}
done && \
for file in $DBFS; do
shp2sqlite -an $(ls ${TMP}/${file}.dbf) ${file}
done && \
cat ${BASE}/convert.sql) | sqlite3 $DATABASE
rm -f $TMP/*
done 2>&1 | tee import-$$.log
rm -rf $TMP
================================================
FILE: navteq/prepare.sql
================================================
PRAGMA temp_store=MEMORY;
PRAGMA journal_mode=MEMORY;
PRAGMA synchronous=OFF;
PRAGMA cache_size=250000;
PRAGMA count_changes=0;
BEGIN;
CREATE TABLE "local_streets" (gid integer PRIMARY KEY,
"the_geom" blob,
"link_id" integer,
"st_name" varchar(80),
"feat_id" integer,
"st_langcd" varchar(3),
"num_stnmes" integer,
"st_nm_pref" varchar(2),
"st_typ_bef" varchar(30),
"st_nm_base" varchar(35),
"st_nm_suff" varchar(2),
"st_typ_aft" varchar(30),
"st_typ_att" varchar(1),
"addr_type" varchar(1),
"l_refaddr" varchar(10),
"l_nrefaddr" varchar(10),
"l_addrsch" varchar(1),
"l_addrform" varchar(1),
"r_refaddr" varchar(10),
"r_nrefaddr" varchar(10),
"r_addrsch" varchar(1),
"r_addrform" varchar(1),
"ref_in_id" integer,
"nref_in_id" integer,
"n_shapepnt" integer,
"func_class" varchar(1),
"speed_cat" varchar(1),
"fr_spd_lim" integer,
"to_spd_lim" integer,
"to_lanes" integer,
"from_lanes" integer,
"enh_geom" varchar(1),
"lane_cat" varchar(1),
"divider" varchar(1),
"dir_travel" varchar(1),
"l_area_id" integer,
"r_area_id" integer,
"l_postcode" varchar(11),
"r_postcode" varchar(11),
"l_numzones" integer,
"r_numzones" integer,
"num_ad_rng" integer,
"ar_auto" varchar(1),
"ar_bus" varchar(1),
"ar_taxis" varchar(1),
"ar_carpool" varchar(1),
"ar_pedest" varchar(1),
"ar_trucks" varchar(1),
"ar_traff" varchar(1),
"ar_deliv" varchar(1),
"ar_emerveh" varchar(1),
"paved" varchar(1),
"private" varchar(1),
"frontage" varchar(1),
"bridge" varchar(1),
"tunnel" varchar(1),
"ramp" varchar(1),
"tollway" varchar(1),
"poiaccess" varchar(1),
"contracc" varchar(1),
"roundabout" varchar(1),
"interinter" varchar(1),
"undeftraff" varchar(1),
"ferry_type" varchar(1),
"multidigit" varchar(1),
"maxattr" varchar(1),
"spectrfig" varchar(1),
"indescrib" varchar(1),
"manoeuvre" varchar(1),
"dividerleg" varchar(1),
"inprocdata" varchar(1),
"full_geom" varchar(1),
"urban" varchar(1),
"route_type" varchar(1),
"dironsign" varchar(1),
"explicatbl" varchar(1),
"nameonrdsn" varchar(1),
"postalname" varchar(1),
"stalename" varchar(1),
"vanityname" varchar(1),
"junctionnm" varchar(1),
"exitname" varchar(1),
"scenic_rt" varchar(1),
"scenic_nm" varchar(1));
--SELECT AddGeometryColumn('','local_streets','the_geom','-1','MULTILINESTRING',2);
END;
================================================
FILE: setup.rb
================================================
#
# setup.rb
#
# Copyright (c) 2000-2005 Minero Aoki
#
# This program is free software.
# You can distribute/modify this program under the terms of
# the GNU LGPL, Lesser General Public License version 2.1.
#
unless Enumerable.method_defined?(:map) # Ruby 1.4.6
module Enumerable
alias map collect
end
end
unless File.respond_to?(:read) # Ruby 1.6
def File.read(fname)
open(fname) {|f|
return f.read
}
end
end
unless Errno.const_defined?(:ENOTEMPTY) # Windows?
module Errno
class ENOTEMPTY
# We do not raise this exception, implementation is not needed.
end
end
end
def File.binread(fname)
open(fname, 'rb') {|f|
return f.read
}
end
# for corrupted Windows' stat(2)
def File.dir?(path)
File.directory?((path[-1,1] == '/') ? path : path + '/')
end
class ConfigTable
include Enumerable
def initialize(rbconfig)
@rbconfig = rbconfig
@items = []
@table = {}
# options
@install_prefix = nil
@config_opt = nil
@verbose = true
@no_harm = false
end
attr_accessor :install_prefix
attr_accessor :config_opt
attr_writer :verbose
def verbose?
@verbose
end
attr_writer :no_harm
def no_harm?
@no_harm
end
def [](key)
lookup(key).resolve(self)
end
def []=(key, val)
lookup(key).set val
end
def names
@items.map {|i| i.name }
end
def each(&block)
@items.each(&block)
end
def key?(name)
@table.key?(name)
end
def lookup(name)
@table[name] or setup_rb_error "no such config item: #{name}"
end
def add(item)
@items.push item
@table[item.name] = item
end
def remove(name)
item = lookup(name)
@items.delete_if {|i| i.name == name }
@table.delete_if {|name, i| i.name == name }
item
end
def load_script(path, inst = nil)
if File.file?(path)
MetaConfigEnvironment.new(self, inst).instance_eval File.read(path), path
end
end
def savefile
'.config'
end
def load_savefile
begin
File.foreach(savefile()) do |line|
k, v = *line.split(/=/, 2)
self[k] = v.strip
end
rescue Errno::ENOENT
setup_rb_error $!.message + "\n#{File.basename($0)} config first"
end
end
def save
@items.each {|i| i.value }
File.open(savefile(), 'w') {|f|
@items.each do |i|
f.printf "%s=%s\n", i.name, i.value if i.value? and i.value
end
}
end
def load_standard_entries
standard_entries(@rbconfig).each do |ent|
add ent
end
end
def standard_entries(rbconfig)
c = rbconfig
rubypath = File.join(c['bindir'], c['ruby_install_name'] + c['EXEEXT'])
major = c['MAJOR'].to_i
minor = c['MINOR'].to_i
teeny = c['TEENY'].to_i
version = "#{major}.#{minor}"
# ruby ver. >= 1.4.4?
newpath_p = ((major >= 2) or
((major == 1) and
((minor >= 5) or
((minor == 4) and (teeny >= 4)))))
if c['rubylibdir']
# V > 1.6.3
libruby = "#{c['prefix']}/lib/ruby"
librubyver = c['rubylibdir']
librubyverarch = c['archdir']
siteruby = c['sitedir']
siterubyver = c['sitelibdir']
siterubyverarch = c['sitearchdir']
elsif newpath_p
# 1.4.4 <= V <= 1.6.3
libruby = "#{c['prefix']}/lib/ruby"
librubyver = "#{c['prefix']}/lib/ruby/#{version}"
librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}"
siteruby = c['sitedir']
siterubyver = "$siteruby/#{version}"
siterubyverarch = "$siterubyver/#{c['arch']}"
else
# V < 1.4.4
libruby = "#{c['prefix']}/lib/ruby"
librubyver = "#{c['prefix']}/lib/ruby/#{version}"
librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}"
siteruby = "#{c['prefix']}/lib/ruby/#{version}/site_ruby"
siterubyver = siteruby
siterubyverarch = "$siterubyver/#{c['arch']}"
end
parameterize = lambda {|path|
path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix')
}
if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg }
makeprog = arg.sub(/'/, '').split(/=/, 2)[1]
else
makeprog = 'make'
end
[
ExecItem.new('installdirs', 'std/site/home',
'std: install under libruby; site: install under site_ruby; home: install under $HOME')\
{|val, table|
case val
when 'std'
table['rbdir'] = '$librubyver'
table['sodir'] = '$librubyverarch'
when 'site'
table['rbdir'] = '$siterubyver'
table['sodir'] = '$siterubyverarch'
when 'home'
setup_rb_error '$HOME was not set' unless ENV['HOME']
table['prefix'] = ENV['HOME']
table['rbdir'] = '$libdir/ruby'
table['sodir'] = '$libdir/ruby'
end
},
PathItem.new('prefix', 'path', c['prefix'],
'path prefix of target environment'),
PathItem.new('bindir', 'path', parameterize.call(c['bindir']),
'the directory for commands'),
PathItem.new('libdir', 'path', parameterize.call(c['libdir']),
'the directory for libraries'),
PathItem.new('datadir', 'path', parameterize.call(c['datadir']),
'the directory for shared data'),
PathItem.new('mandir', 'path', parameterize.call(c['mandir']),
'the directory for man pages'),
PathItem.new('sysconfdir', 'path', parameterize.call(c['sysconfdir']),
'the directory for system configuration files'),
PathItem.new('localstatedir', 'path', parameterize.call(c['localstatedir']),
'the directory for local state data'),
PathItem.new('libruby', 'path', libruby,
'the directory for ruby libraries'),
PathItem.new('librubyver', 'path', librubyver,
'the directory for standard ruby libraries'),
PathItem.new('librubyverarch', 'path', librubyverarch,
'the directory for standard ruby extensions'),
PathItem.new('siteruby', 'path', siteruby,
'the directory for version-independent aux ruby libraries'),
PathItem.new('siterubyver', 'path', siterubyver,
'the directory for aux ruby libraries'),
PathItem.new('siterubyverarch', 'path', siterubyverarch,
'the directory for aux ruby binaries'),
PathItem.new('rbdir', 'path', '$siterubyver',
'the directory for ruby scripts'),
PathItem.new('sodir', 'path', '$siterubyverarch',
'the directory for ruby extentions'),
PathItem.new('rubypath', 'path', rubypath,
'the path to set to #! line'),
ProgramItem.new('rubyprog', 'name', rubypath,
'the ruby program using for installation'),
ProgramItem.new('makeprog', 'name', makeprog,
'the make program to compile ruby extentions'),
SelectItem.new('shebang', 'all/ruby/never', 'ruby',
'shebang line (#!) editing mode'),
BoolItem.new('without-ext', 'yes/no', 'no',
'does not compile/install ruby extentions')
]
end
private :standard_entries
def load_multipackage_entries
multipackage_entries().each do |ent|
add ent
end
end
def multipackage_entries
[
PackageSelectionItem.new('with', 'name,name...', '', 'ALL',
'package names that you want to install'),
PackageSelectionItem.new('without', 'name,name...', '', 'NONE',
'package names that you do not want to install')
]
end
private :multipackage_entries
ALIASES = {
'std-ruby' => 'librubyver',
'stdruby' => 'librubyver',
'rubylibdir' => 'librubyver',
'archdir' => 'librubyverarch',
'site-ruby-common' => 'siteruby', # For backward compatibility
'site-ruby' => 'siterubyver', # For backward compatibility
'bin-dir' => 'bindir',
'bin-dir' => 'bindir',
'rb-dir' => 'rbdir',
'so-dir' => 'sodir',
'data-dir' => 'datadir',
'ruby-path' => 'rubypath',
'ruby-prog' => 'rubyprog',
'ruby' => 'rubyprog',
'make-prog' => 'makeprog',
'make' => 'makeprog'
}
def fixup
ALIASES.each do |ali, name|
@table[ali] = @table[name]
end
@items.freeze
@table.freeze
@options_re = /\A--(#{@table.keys.join('|')})(?:=(.*))?\z/
end
def parse_opt(opt)
m = @options_re.match(opt) or setup_rb_error "config: unknown option #{opt}"
m.to_a[1,2]
end
def dllext
@rbconfig['DLEXT']
end
def value_config?(name)
lookup(name).value?
end
class Item
def initialize(name, template, default, desc)
@name = name.freeze
@template = template
@value = default
@default = default
@description = desc
end
attr_reader :name
attr_reader :description
attr_accessor :default
alias help_default default
def help_opt
"--#{@name}=#{@template}"
end
def value?
true
end
def value
@value
end
def resolve(table)
@value.gsub(%r<\$([^/]+)>) { table[$1] }
end
def set(val)
@value = check(val)
end
private
def check(val)
setup_rb_error "config: --#{name} requires argument" unless val
val
end
end
class BoolItem < Item
def config_type
'bool'
end
def help_opt
"--#{@name}"
end
private
def check(val)
return 'yes' unless val
case val
when /\Ay(es)?\z/i, /\At(rue)?\z/i then 'yes'
when /\An(o)?\z/i, /\Af(alse)\z/i then 'no'
else
setup_rb_error "config: --#{@name} accepts only yes/no for argument"
end
end
end
class PathItem < Item
def config_type
'path'
end
private
def check(path)
setup_rb_error "config: --#{@name} requires argument" unless path
path[0,1] == '$' ? path : File.expand_path(path)
end
end
class ProgramItem < Item
def config_type
'program'
end
end
class SelectItem < Item
def initialize(name, selection, default, desc)
super
@ok = selection.split('/')
end
def config_type
'select'
end
private
def check(val)
unless @ok.include?(val.strip)
setup_rb_error "config: use --#{@name}=#{@template} (#{val})"
end
val.strip
end
end
class ExecItem < Item
def initialize(name, selection, desc, &block)
super name, selection, nil, desc
@ok = selection.split('/')
@action = block
end
def config_type
'exec'
end
def value?
false
end
def resolve(table)
setup_rb_error "$#{name()} wrongly used as option value"
end
undef set
def evaluate(val, table)
v = val.strip.downcase
unless @ok.include?(v)
setup_rb_error "invalid option --#{@name}=#{val} (use #{@template})"
end
@action.call v, table
end
end
class PackageSelectionItem < Item
def initialize(name, template, default, help_default, desc)
super name, template, default, desc
@help_default = help_default
end
attr_reader :help_default
def config_type
'package'
end
private
def check(val)
unless File.dir?("packages/#{val}")
setup_rb_error "config: no such package: #{val}"
end
val
end
end
class MetaConfigEnvironment
def initialize(config, installer)
@config = config
@installer = installer
end
def config_names
@config.names
end
def config?(name)
@config.key?(name)
end
def bool_config?(name)
@config.lookup(name).config_type == 'bool'
end
def path_config?(name)
@config.lookup(name).config_type == 'path'
end
def value_config?(name)
@config.lookup(name).config_type != 'exec'
end
def add_config(item)
@config.add item
end
def add_bool_config(name, default, desc)
@config.add BoolItem.new(name, 'yes/no', default ? 'yes' : 'no', desc)
end
def add_path_config(name, default, desc)
@config.add PathItem.new(name, 'path', default, desc)
end
def set_config_default(name, default)
@config.lookup(name).default = default
end
def remove_config(name)
@config.remove(name)
end
# For only multipackage
def packages
raise '[setup.rb fatal] multi-package metaconfig API packages() called for single-package; contact application package vendor' unless @installer
@installer.packages
end
# For only multipackage
def declare_packages(list)
raise '[setup.rb fatal] multi-package metaconfig API declare_packages() called for single-package; contact application package vendor' unless @installer
@installer.packages = list
end
end
end # class ConfigTable
# This module requires: #verbose?, #no_harm?
module FileOperations
def mkdir_p(dirname, prefix = nil)
dirname = prefix + File.expand_path(dirname) if prefix
$stderr.puts "mkdir -p #{dirname}" if verbose?
return if no_harm?
# Does not check '/', it's too abnormal.
dirs = File.expand_path(dirname).split(%r<(?=/)>)
if /\A[a-z]:\z/i =~ dirs[0]
disk = dirs.shift
dirs[0] = disk + dirs[0]
end
dirs.each_index do |idx|
path = dirs[0..idx].join('')
Dir.mkdir path unless File.dir?(path)
end
end
def rm_f(path)
$stderr.puts "rm -f #{path}" if verbose?
return if no_harm?
force_remove_file path
end
def rm_rf(path)
$stderr.puts "rm -rf #{path}" if verbose?
return if no_harm?
remove_tree path
end
def remove_tree(path)
if File.symlink?(path)
remove_file path
elsif File.dir?(path)
remove_tree0 path
else
force_remove_file path
end
end
def remove_tree0(path)
Dir.foreach(path) do |ent|
next if ent == '.'
next if ent == '..'
entpath = "#{path}/#{ent}"
if File.symlink?(entpath)
remove_file entpath
elsif File.dir?(entpath)
remove_tree0 entpath
else
force_remove_file entpath
end
end
begin
Dir.rmdir path
rescue Errno::ENOTEMPTY
# directory may not be empty
end
end
def move_file(src, dest)
force_remove_file dest
begin
File.rename src, dest
rescue
File.open(dest, 'wb') {|f|
f.write File.binread(src)
}
File.chmod File.stat(src).mode, dest
File.unlink src
end
end
def force_remove_file(path)
begin
remove_file path
rescue
end
end
def remove_file(path)
File.chmod 0777, path
File.unlink path
end
def install(from, dest, mode, prefix = nil)
$stderr.puts "install #{from} #{dest}" if verbose?
return if no_harm?
realdest = prefix ? prefix + File.expand_path(dest) : dest
realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest)
str = File.binread(from)
if diff?(str, realdest)
verbose_off {
rm_f realdest if File.exist?(realdest)
}
File.open(realdest, 'wb') {|f|
f.write str
}
File.chmod mode, realdest
File.open("#{objdir_root()}/InstalledFiles", 'a') {|f|
if prefix
f.puts realdest.sub(prefix, '')
else
f.puts realdest
end
}
end
end
def diff?(new_content, path)
return true unless File.exist?(path)
new_content != File.binread(path)
end
def command(*args)
$stderr.puts args.join(' ') if verbose?
system(*args) or raise RuntimeError,
"system(#{args.map{|a| a.inspect }.join(' ')}) failed"
end
def ruby(*args)
command config('rubyprog'), *args
end
def make(task = nil)
command(*[config('makeprog'), task].compact)
end
def extdir?(dir)
File.exist?("#{dir}/MANIFEST") or File.exist?("#{dir}/extconf.rb")
end
def files_of(dir)
Dir.open(dir) {|d|
return d.select {|ent| File.file?("#{dir}/#{ent}") }
}
end
DIR_REJECT = %w( . .. CVS SCCS RCS CVS.adm .svn )
def directories_of(dir)
Dir.open(dir) {|d|
return d.select {|ent| File.dir?("#{dir}/#{ent}") } - DIR_REJECT
}
end
end
# This module requires: #srcdir_root, #objdir_root, #relpath
module HookScriptAPI
def get_config(key)
@config[key]
end
alias config get_config
# obsolete: use metaconfig to change configuration
def set_config(key, val)
@config[key] = val
end
#
# srcdir/objdir (works only in the package directory)
#
def curr_srcdir
"#{srcdir_root()}/#{relpath()}"
end
def curr_objdir
"#{objdir_root()}/#{relpath()}"
end
def srcfile(path)
"#{curr_srcdir()}/#{path}"
end
def srcexist?(path)
File.exist?(srcfile(path))
end
def srcdirectory?(path)
File.dir?(srcfile(path))
end
def srcfile?(path)
File.file?(srcfile(path))
end
def srcentries(path = '.')
Dir.open("#{curr_srcdir()}/#{path}") {|d|
return d.to_a - %w(. ..)
}
end
def srcfiles(path = '.')
srcentries(path).select {|fname|
File.file?(File.join(curr_srcdir(), path, fname))
}
end
def srcdirectories(path = '.')
srcentries(path).select {|fname|
File.dir?(File.join(curr_srcdir(), path, fname))
}
end
end
class ToplevelInstaller
Version = '3.4.1'
Copyright = 'Copyright (c) 2000-2005 Minero Aoki'
TASKS = [
[ 'all', 'do config, setup, then install' ],
[ 'config', 'saves your configurations' ],
[ 'show', 'shows current configuration' ],
[ 'setup', 'compiles ruby extentions and others' ],
[ 'install', 'installs files' ],
[ 'test', 'run all tests in test/' ],
[ 'clean', "does `make clean' for each extention" ],
[ 'distclean',"does `make distclean' for each extention" ]
]
def ToplevelInstaller.invoke
config = ConfigTable.new(load_rbconfig())
config.load_standard_entries
config.load_multipackage_entries if multipackage?
config.fixup
klass = (multipackage?() ? ToplevelInstallerMulti : ToplevelInstaller)
klass.new(File.dirname($0), config).invoke
end
def ToplevelInstaller.multipackage?
File.dir?(File.dirname($0) + '/packages')
end
def ToplevelInstaller.load_rbconfig
if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg }
ARGV.delete(arg)
load File.expand_path(arg.split(/=/, 2)[1])
$".push 'rbconfig.rb'
else
require 'rbconfig'
end
::Config::CONFIG
end
def initialize(ardir_root, config)
@ardir = File.expand_path(ardir_root)
@config = config
# cache
@valid_task_re = nil
end
def config(key)
@config[key]
end
def inspect
"#<#{self.class} #{__id__()}>"
end
def invoke
run_metaconfigs
case task = parsearg_global()
when nil, 'all'
parsearg_config
init_installers
exec_config
exec_setup
exec_install
else
case task
when 'config', 'test'
;
when 'clean', 'distclean'
@config.load_savefile if File.exist?(@config.savefile)
else
@config.load_savefile
end
__send__ "parsearg_#{task}"
init_installers
__send__ "exec_#{task}"
end
end
def run_metaconfigs
@config.load_script "#{@ardir}/metaconfig"
end
def init_installers
@installer = Installer.new(@config, @ardir, File.expand_path('.'))
end
#
# Hook Script API bases
#
def srcdir_root
@ardir
end
def objdir_root
'.'
end
def relpath
'.'
end
#
# Option Parsing
#
def parsearg_global
while arg = ARGV.shift
case arg
when /\A\w+\z/
setup_rb_error "invalid task: #{arg}" unless valid_task?(arg)
return arg
when '-q', '--quiet'
@config.verbose = false
when '--verbose'
@config.verbose = true
when '--help'
print_usage $stdout
exit 0
when '--version'
puts "#{File.basename($0)} version #{Version}"
exit 0
when '--copyright'
puts Copyright
exit 0
else
setup_rb_error "unknown global option '#{arg}'"
end
end
nil
end
def valid_task?(t)
valid_task_re() =~ t
end
def valid_task_re
@valid_task_re ||= /\A(?:#{TASKS.map {|task,desc| task }.join('|')})\z/
end
def parsearg_no_options
unless ARGV.empty?
task = caller(0).first.slice(%r<`parsearg_(\w+)'>, 1)
setup_rb_error "#{task}: unknown options: #{ARGV.join(' ')}"
end
end
alias parsearg_show parsearg_no_options
alias parsearg_setup parsearg_no_options
alias parsearg_test parsearg_no_options
alias parsearg_clean parsearg_no_options
alias parsearg_distclean parsearg_no_options
def parsearg_config
evalopt = []
set = []
@config.config_opt = []
while i = ARGV.shift
if /\A--?\z/ =~ i
@config.config_opt = ARGV.dup
break
end
name, value = *@config.parse_opt(i)
if @config.value_config?(name)
@config[name] = value
else
evalopt.push [name, value]
end
set.push name
end
evalopt.each do |name, value|
@config.lookup(name).evaluate value, @config
end
# Check if configuration is valid
set.each do |n|
@config[n] if @config.value_config?(n)
end
end
def parsearg_install
@config.no_harm = false
@config.install_prefix = ''
while a = ARGV.shift
case a
when '--no-harm'
@config.no_harm = true
when /\A--prefix=/
path = a.split(/=/, 2)[1]
path = File.expand_path(path) unless path[0,1] == '/'
@config.install_prefix = path
else
setup_rb_error "install: unknown option #{a}"
end
end
end
def print_usage(out)
out.puts 'Typical Installation Procedure:'
out.puts " $ ruby #{File.basename $0} config"
out.puts " $ ruby #{File.basename $0} setup"
out.puts " # ruby #{File.basename $0} install (may require root privilege)"
out.puts
out.puts 'Detailed Usage:'
out.puts " ruby #{File.basename $0} "
out.puts " ruby #{File.basename $0} [] []"
fmt = " %-24s %s\n"
out.puts
out.puts 'Global options:'
out.printf fmt, '-q,--quiet', 'suppress message outputs'
out.printf fmt, ' --verbose', 'output messages verbosely'
out.printf fmt, ' --help', 'print this message'
out.printf fmt, ' --version', 'print version and quit'
out.printf fmt, ' --copyright', 'print copyright and quit'
out.puts
out.puts 'Tasks:'
TASKS.each do |name, desc|
out.printf fmt, name, desc
end
fmt = " %-24s %s [%s]\n"
out.puts
out.puts 'Options for CONFIG or ALL:'
@config.each do |item|
out.printf fmt, item.help_opt, item.description, item.help_default
end
out.printf fmt, '--rbconfig=path', 'rbconfig.rb to load',"running ruby's"
out.puts
out.puts 'Options for INSTALL:'
out.printf fmt, '--no-harm', 'only display what to do if given', 'off'
out.printf fmt, '--prefix=path', 'install path prefix', ''
out.puts
end
#
# Task Handlers
#
def exec_config
@installer.exec_config
@config.save # must be final
end
def exec_setup
@installer.exec_setup
end
def exec_install
@installer.exec_install
end
def exec_test
@installer.exec_test
end
def exec_show
@config.each do |i|
printf "%-20s %s\n", i.name, i.value if i.value?
end
end
def exec_clean
@installer.exec_clean
end
def exec_distclean
@installer.exec_distclean
end
end # class ToplevelInstaller
class ToplevelInstallerMulti < ToplevelInstaller
include FileOperations
def initialize(ardir_root, config)
super
@packages = directories_of("#{@ardir}/packages")
raise 'no package exists' if @packages.empty?
@root_installer = Installer.new(@config, @ardir, File.expand_path('.'))
end
def run_metaconfigs
@config.load_script "#{@ardir}/metaconfig", self
@packages.each do |name|
@config.load_script "#{@ardir}/packages/#{name}/metaconfig"
end
end
attr_reader :packages
def packages=(list)
raise 'package list is empty' if list.empty?
list.each do |name|
raise "directory packages/#{name} does not exist"\
unless File.dir?("#{@ardir}/packages/#{name}")
end
@packages = list
end
def init_installers
@installers = {}
@packages.each do |pack|
@installers[pack] = Installer.new(@config,
"#{@ardir}/packages/#{pack}",
"packages/#{pack}")
end
with = extract_selection(config('with'))
without = extract_selection(config('without'))
@selected = @installers.keys.select {|name|
(with.empty? or with.include?(name)) \
and not without.include?(name)
}
end
def extract_selection(list)
a = list.split(/,/)
a.each do |name|
setup_rb_error "no such package: #{name}" unless @installers.key?(name)
end
a
end
def print_usage(f)
super
f.puts 'Inluded packages:'
f.puts ' ' + @packages.sort.join(' ')
f.puts
end
#
# Task Handlers
#
def exec_config
run_hook 'pre-config'
each_selected_installers {|inst| inst.exec_config }
run_hook 'post-config'
@config.save # must be final
end
def exec_setup
run_hook 'pre-setup'
each_selected_installers {|inst| inst.exec_setup }
run_hook 'post-setup'
end
def exec_install
run_hook 'pre-install'
each_selected_installers {|inst| inst.exec_install }
run_hook 'post-install'
end
def exec_test
run_hook 'pre-test'
each_selected_installers {|inst| inst.exec_test }
run_hook 'post-test'
end
def exec_clean
rm_f @config.savefile
run_hook 'pre-clean'
each_selected_installers {|inst| inst.exec_clean }
run_hook 'post-clean'
end
def exec_distclean
rm_f @config.savefile
run_hook 'pre-distclean'
each_selected_installers {|inst| inst.exec_distclean }
run_hook 'post-distclean'
end
#
# lib
#
def each_selected_installers
Dir.mkdir 'packages' unless File.dir?('packages')
@selected.each do |pack|
$stderr.puts "Processing the package `#{pack}' ..." if verbose?
Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}")
Dir.chdir "packages/#{pack}"
yield @installers[pack]
Dir.chdir '../..'
end
end
def run_hook(id)
@root_installer.run_hook id
end
# module FileOperations requires this
def verbose?
@config.verbose?
end
# module FileOperations requires this
def no_harm?
@config.no_harm?
end
end # class ToplevelInstallerMulti
class Installer
FILETYPES = %w( bin lib ext data conf man )
include FileOperations
include HookScriptAPI
def initialize(config, srcroot, objroot)
@config = config
@srcdir = File.expand_path(srcroot)
@objdir = File.expand_path(objroot)
@currdir = '.'
end
def inspect
"#<#{self.class} #{File.basename(@srcdir)}>"
end
def noop(rel)
end
#
# Hook Script API base methods
#
def srcdir_root
@srcdir
end
def objdir_root
@objdir
end
def relpath
@currdir
end
#
# Config Access
#
# module FileOperations requires this
def verbose?
@config.verbose?
end
# module FileOperations requires this
def no_harm?
@config.no_harm?
end
def verbose_off
begin
save, @config.verbose = @config.verbose?, false
yield
ensure
@config.verbose = save
end
end
#
# TASK config
#
def exec_config
exec_task_traverse 'config'
end
alias config_dir_bin noop
alias config_dir_lib noop
def config_dir_ext(rel)
extconf if extdir?(curr_srcdir())
end
alias config_dir_data noop
alias config_dir_conf noop
alias config_dir_man noop
def extconf
ruby "#{curr_srcdir()}/extconf.rb", *@config.config_opt
end
#
# TASK setup
#
def exec_setup
exec_task_traverse 'setup'
end
def setup_dir_bin(rel)
files_of(curr_srcdir()).each do |fname|
update_shebang_line "#{curr_srcdir()}/#{fname}"
end
end
alias setup_dir_lib noop
def setup_dir_ext(rel)
make if extdir?(curr_srcdir())
end
alias setup_dir_data noop
alias setup_dir_conf noop
alias setup_dir_man noop
def update_shebang_line(path)
return if no_harm?
return if config('shebang') == 'never'
old = Shebang.load(path)
if old
$stderr.puts "warning: #{path}: Shebang line includes too many args. It is not portable and your program may not work." if old.args.size > 1
new = new_shebang(old)
return if new.to_s == old.to_s
else
return unless config('shebang') == 'all'
new = Shebang.new(config('rubypath'))
end
$stderr.puts "updating shebang: #{File.basename(path)}" if verbose?
open_atomic_writer(path) {|output|
File.open(path, 'rb') {|f|
f.gets if old # discard
output.puts new.to_s
output.print f.read
}
}
end
def new_shebang(old)
if /\Aruby/ =~ File.basename(old.cmd)
Shebang.new(config('rubypath'), old.args)
elsif File.basename(old.cmd) == 'env' and old.args.first == 'ruby'
Shebang.new(config('rubypath'), old.args[1..-1])
else
return old unless config('shebang') == 'all'
Shebang.new(config('rubypath'))
end
end
def open_atomic_writer(path, &block)
tmpfile = File.basename(path) + '.tmp'
begin
File.open(tmpfile, 'wb', &block)
File.rename tmpfile, File.basename(path)
ensure
File.unlink tmpfile if File.exist?(tmpfile)
end
end
class Shebang
def Shebang.load(path)
line = nil
File.open(path) {|f|
line = f.gets
}
return nil unless /\A#!/ =~ line
parse(line)
end
def Shebang.parse(line)
cmd, *args = *line.strip.sub(/\A\#!/, '').split(' ')
new(cmd, args)
end
def initialize(cmd, args = [])
@cmd = cmd
@args = args
end
attr_reader :cmd
attr_reader :args
def to_s
"#! #{@cmd}" + (@args.empty? ? '' : " #{@args.join(' ')}")
end
end
#
# TASK install
#
def exec_install
rm_f 'InstalledFiles'
exec_task_traverse 'install'
end
def install_dir_bin(rel)
install_files targetfiles(), "#{config('bindir')}/#{rel}", 0755
end
def install_dir_lib(rel)
install_files libfiles(), "#{config('rbdir')}/#{rel}", 0644
end
def install_dir_ext(rel)
return unless extdir?(curr_srcdir())
install_files rubyextentions('.'),
"#{config('sodir')}/#{File.dirname(rel)}",
0555
end
def install_dir_data(rel)
install_files targetfiles(), "#{config('datadir')}/#{rel}", 0644
end
def install_dir_conf(rel)
# FIXME: should not remove current config files
# (rename previous file to .old/.org)
install_files targetfiles(), "#{config('sysconfdir')}/#{rel}", 0644
end
def install_dir_man(rel)
install_files targetfiles(), "#{config('mandir')}/#{rel}", 0644
end
def install_files(list, dest, mode)
mkdir_p dest, @config.install_prefix
list.each do |fname|
install fname, dest, mode, @config.install_prefix
end
end
def libfiles
glob_reject(%w(*.y *.output), targetfiles())
end
def rubyextentions(dir)
ents = glob_select("*.#{@config.dllext}", targetfiles())
if ents.empty?
setup_rb_error "no ruby extention exists: 'ruby #{$0} setup' first"
end
ents
end
def targetfiles
mapdir(existfiles() - hookfiles())
end
def mapdir(ents)
ents.map {|ent|
if File.exist?(ent)
then ent # objdir
else "#{curr_srcdir()}/#{ent}" # srcdir
end
}
end
# picked up many entries from cvs-1.11.1/src/ignore.c
JUNK_FILES = %w(
core RCSLOG tags TAGS .make.state
.nse_depinfo #* .#* cvslog.* ,* .del-* *.olb
*~ *.old *.bak *.BAK *.orig *.rej _$* *$
*.org *.in .*
)
def existfiles
glob_reject(JUNK_FILES, (files_of(curr_srcdir()) | files_of('.')))
end
def hookfiles
%w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt|
%w( config setup install clean ).map {|t| sprintf(fmt, t) }
}.flatten
end
def glob_select(pat, ents)
re = globs2re([pat])
ents.select {|ent| re =~ ent }
end
def glob_reject(pats, ents)
re = globs2re(pats)
ents.reject {|ent| re =~ ent }
end
GLOB2REGEX = {
'.' => '\.',
'$' => '\$',
'#' => '\#',
'*' => '.*'
}
def globs2re(pats)
/\A(?:#{
pats.map {|pat| pat.gsub(/[\.\$\#\*]/) {|ch| GLOB2REGEX[ch] } }.join('|')
})\z/
end
#
# TASK test
#
TESTDIR = 'test'
def exec_test
unless File.directory?('test')
$stderr.puts 'no test in this package' if verbose?
return
end
$stderr.puts 'Running tests...' if verbose?
begin
require 'test/unit'
rescue LoadError
setup_rb_error 'test/unit cannot loaded. You need Ruby 1.8 or later to invoke this task.'
end
runner = Test::Unit::AutoRunner.new(true)
runner.to_run << TESTDIR
runner.run
end
#
# TASK clean
#
def exec_clean
exec_task_traverse 'clean'
rm_f @config.savefile
rm_f 'InstalledFiles'
end
alias clean_dir_bin noop
alias clean_dir_lib noop
alias clean_dir_data noop
alias clean_dir_conf noop
alias clean_dir_man noop
def clean_dir_ext(rel)
return unless extdir?(curr_srcdir())
make 'clean' if File.file?('Makefile')
end
#
# TASK distclean
#
def exec_distclean
exec_task_traverse 'distclean'
rm_f @config.savefile
rm_f 'InstalledFiles'
end
alias distclean_dir_bin noop
alias distclean_dir_lib noop
def distclean_dir_ext(rel)
return unless extdir?(curr_srcdir())
make 'distclean' if File.file?('Makefile')
end
alias distclean_dir_data noop
alias distclean_dir_conf noop
alias distclean_dir_man noop
#
# Traversing
#
def exec_task_traverse(task)
run_hook "pre-#{task}"
FILETYPES.each do |type|
if type == 'ext' and config('without-ext') == 'yes'
$stderr.puts 'skipping ext/* by user option' if verbose?
next
end
traverse task, type, "#{task}_dir_#{type}"
end
run_hook "post-#{task}"
end
def traverse(task, rel, mid)
dive_into(rel) {
run_hook "pre-#{task}"
__send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '')
directories_of(curr_srcdir()).each do |d|
traverse task, "#{rel}/#{d}", mid
end
run_hook "post-#{task}"
}
end
def dive_into(rel)
return unless File.dir?("#{@srcdir}/#{rel}")
dir = File.basename(rel)
Dir.mkdir dir unless File.dir?(dir)
prevdir = Dir.pwd
Dir.chdir dir
$stderr.puts '---> ' + rel if verbose?
@currdir = rel
yield
Dir.chdir prevdir
$stderr.puts '<--- ' + rel if verbose?
@currdir = File.dirname(rel)
end
def run_hook(id)
path = [ "#{curr_srcdir()}/#{id}",
"#{curr_srcdir()}/#{id}.rb" ].detect {|cand| File.file?(cand) }
return unless path
begin
instance_eval File.read(path), path, 1
rescue
raise if $DEBUG
setup_rb_error "hook #{path} failed:\n" + $!.message
end
end
end # class Installer
class SetupError < StandardError; end
def setup_rb_error(msg)
raise SetupError, msg
end
if $0 == __FILE__
begin
ToplevelInstaller.invoke
rescue SetupError
raise if $DEBUG
$stderr.puts $!.message
$stderr.puts "Try 'ruby #{$0} --help' for detailed usage."
exit 1
end
end
================================================
FILE: src/Makefile
================================================
all:
$(MAKE) -C libsqlite3_geocoder
$(MAKE) -C liblwgeom
$(MAKE) -C shp2sqlite
clean:
$(MAKE) -C libsqlite3_geocoder clean
$(MAKE) -C liblwgeom clean
$(MAKE) -C shp2sqlite clean
install: all
cp libsqlite3_geocoder/*.so ../lib/geocoder/us/sqlite3.so
$(MAKE) -C shp2sqlite install
================================================
FILE: src/README
================================================
What's in this directory
------------------------
shp2sqlite/
A fork of shp2pgsql that generates SQLite 3 compatible
output. Used for import.
liblwgeom/
Required by shp2sqlite for converting Shapefiles to WKB.
libsqlite3_geocoder/
Not actually the geocoder itself, but a library of
extensions to SQLite 3 to facilitate geocoding.
metaphone/
Unused in this project. The metaphone functions have
been rolled into libsqlite3_geocoder.
================================================
FILE: src/liblwgeom/Makefile
================================================
# **********************************************************************
# * $Id: Makefile.in
# *
# * PostGIS - Spatial Types for PostgreSQL
# * http://postgis.refractions.net
# * Copyright 2008 Mark Cave-Ayland
# *
# * This is free software; you can redistribute and/or modify it under
# * the terms of the GNU General Public Licence. See the COPYING file.
# *
# **********************************************************************
CC=gcc
CFLAGS=-g -O2 -fPIC -DPIC -Wall -Wmissing-prototypes
YACC=yacc
LEX=flex
# Standalone LWGEOM objects
SA_OBJS = \
measures.o \
box2d.o \
ptarray.o \
lwgeom_api.o \
lwgeom.o \
lwpoint.o \
lwline.o \
lwpoly.o \
lwmpoint.o \
lwmline.o \
lwmpoly.o \
lwcollection.o \
lwcircstring.o \
lwcompound.o \
lwcurvepoly.o \
lwmcurve.o \
lwmsurface.o \
lwutil.o \
lwalgorithm.o \
lwgunparse.o \
lwgparse.o \
lwsegmentize.o \
wktparse.tab.o \
lex.yy.o \
vsprintf.o
SA_HEADERS = \
liblwgeom.h \
lwalgorithm.h
all: liblwgeom.a
liblwgeom.a: $(SA_OBJS) $(SA_HEADERS)
ar rs liblwgeom.a $(SA_OBJS)
clean:
rm -f $(SA_OBJS)
rm -f liblwgeom.a
check: liblwgeom.a
make -C cunit check
# Command to build each of the .o files
$(SA_OBJS): %.o: %.c
$(CC) $(CFLAGS) -c -o $@ $<
# Commands to generate the lexer and parser from input files
wktparse.tab.c: wktparse.y
$(YACC) -vd -p lwg_parse_yy wktparse.y
mv -f y.tab.c wktparse.tab.c
mv -f y.tab.h wktparse.tab.h
lex.yy.c: wktparse.lex wktparse.tab.c
$(LEX) -Plwg_parse_yy -i -f -o'lex.yy.c' wktparse.lex
================================================
FILE: src/liblwgeom/box2d.c
================================================
#include
#include
#include
#include
#include "liblwgeom.h"
#ifndef EPSILON
#define EPSILON 1.0E-06
#endif
#ifndef FPeq
#define FPeq(A,B) (fabs((A) - (B)) <= EPSILON)
#endif
/* Expand given box of 'd' units in all directions */
void
expand_box2d(BOX2DFLOAT4 *box, double d)
{
box->xmin -= d;
box->ymin -= d;
box->xmax += d;
box->ymax += d;
}
/*
* This has been changed in PostGIS 1.1.2 to
* check exact equality of values (rather then using
* the FPeq macro taking into account coordinate drifts).
*/
char
box2d_same(BOX2DFLOAT4 *box1, BOX2DFLOAT4 *box2)
{
return( (box1->xmax==box2->xmax) &&
(box1->xmin==box2->xmin) &&
(box1->ymax==box2->ymax) &&
(box1->ymin==box2->ymin));
#if 0
return(FPeq(box1->xmax, box2->xmax) &&
FPeq(box1->xmin, box2->xmin) &&
FPeq(box1->ymax, box2->ymax) &&
FPeq(box1->ymin, box2->ymin));
#endif
}
BOX2DFLOAT4 *
box2d_clone(const BOX2DFLOAT4 *in)
{
BOX2DFLOAT4 *ret = lwalloc(sizeof(BOX2DFLOAT4));
memcpy(ret, in, sizeof(BOX2DFLOAT4));
return ret;
}
================================================
FILE: src/liblwgeom/lex.yy.c
================================================
#line 2 "lex.yy.c"
#line 4 "lex.yy.c"
#define YY_INT_ALIGNED short int
/* A lexical scanner generated by flex */
#define yy_create_buffer lwg_parse_yy_create_buffer
#define yy_delete_buffer lwg_parse_yy_delete_buffer
#define yy_flex_debug lwg_parse_yy_flex_debug
#define yy_init_buffer lwg_parse_yy_init_buffer
#define yy_flush_buffer lwg_parse_yy_flush_buffer
#define yy_load_buffer_state lwg_parse_yy_load_buffer_state
#define yy_switch_to_buffer lwg_parse_yy_switch_to_buffer
#define yyin lwg_parse_yyin
#define yyleng lwg_parse_yyleng
#define yylex lwg_parse_yylex
#define yylineno lwg_parse_yylineno
#define yyout lwg_parse_yyout
#define yyrestart lwg_parse_yyrestart
#define yytext lwg_parse_yytext
#define yywrap lwg_parse_yywrap
#define yyalloc lwg_parse_yyalloc
#define yyrealloc lwg_parse_yyrealloc
#define yyfree lwg_parse_yyfree
#define FLEX_SCANNER
#define YY_FLEX_MAJOR_VERSION 2
#define YY_FLEX_MINOR_VERSION 5
#define YY_FLEX_SUBMINOR_VERSION 35
#if YY_FLEX_SUBMINOR_VERSION > 0
#define FLEX_BETA
#endif
/* First, we deal with platform-specific or compiler-specific issues. */
/* begin standard C headers. */
#include
#include
#include
#include
/* end standard C headers. */
/* flex integer type definitions */
#ifndef FLEXINT_H
#define FLEXINT_H
/* C99 systems have . Non-C99 systems may or may not. */
#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
* if you want the limit (max/min) macros for int types.
*/
#ifndef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS 1
#endif
#include
typedef int8_t flex_int8_t;
typedef uint8_t flex_uint8_t;
typedef int16_t flex_int16_t;
typedef uint16_t flex_uint16_t;
typedef int32_t flex_int32_t;
typedef uint32_t flex_uint32_t;
#else
typedef signed char flex_int8_t;
typedef short int flex_int16_t;
typedef int flex_int32_t;
typedef unsigned char flex_uint8_t;
typedef unsigned short int flex_uint16_t;
typedef unsigned int flex_uint32_t;
/* Limits of integral types. */
#ifndef INT8_MIN
#define INT8_MIN (-128)
#endif
#ifndef INT16_MIN
#define INT16_MIN (-32767-1)
#endif
#ifndef INT32_MIN
#define INT32_MIN (-2147483647-1)
#endif
#ifndef INT8_MAX
#define INT8_MAX (127)
#endif
#ifndef INT16_MAX
#define INT16_MAX (32767)
#endif
#ifndef INT32_MAX
#define INT32_MAX (2147483647)
#endif
#ifndef UINT8_MAX
#define UINT8_MAX (255U)
#endif
#ifndef UINT16_MAX
#define UINT16_MAX (65535U)
#endif
#ifndef UINT32_MAX
#define UINT32_MAX (4294967295U)
#endif
#endif /* ! C99 */
#endif /* ! FLEXINT_H */
#ifdef __cplusplus
/* The "const" storage-class-modifier is valid. */
#define YY_USE_CONST
#else /* ! __cplusplus */
/* C99 requires __STDC__ to be defined as 1. */
#if defined (__STDC__)
#define YY_USE_CONST
#endif /* defined (__STDC__) */
#endif /* ! __cplusplus */
#ifdef YY_USE_CONST
#define yyconst const
#else
#define yyconst
#endif
/* Returned upon end-of-file. */
#define YY_NULL 0
/* Promotes a possibly negative, possibly signed char to an unsigned
* integer for use as an array index. If the signed char is negative,
* we want to instead treat it as an 8-bit unsigned char, hence the
* double cast.
*/
#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
/* Enter a start condition. This macro really ought to take a parameter,
* but we do it the disgusting crufty way forced on us by the ()-less
* definition of BEGIN.
*/
#define BEGIN (yy_start) = 1 + 2 *
/* Translate the current start state into a value that can be later handed
* to BEGIN to return to the state. The YYSTATE alias is for lex
* compatibility.
*/
#define YY_START (((yy_start) - 1) / 2)
#define YYSTATE YY_START
/* Action number for EOF rule of a given start state. */
#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
/* Special action meaning "start processing a new file". */
#define YY_NEW_FILE lwg_parse_yyrestart(lwg_parse_yyin )
#define YY_END_OF_BUFFER_CHAR 0
/* Size of default input buffer. */
#ifndef YY_BUF_SIZE
#define YY_BUF_SIZE 16384
#endif
/* The state buf must be large enough to hold one state per character in the main buffer.
*/
#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
#ifndef YY_TYPEDEF_YY_BUFFER_STATE
#define YY_TYPEDEF_YY_BUFFER_STATE
typedef struct yy_buffer_state *YY_BUFFER_STATE;
#endif
extern int lwg_parse_yyleng;
extern FILE *lwg_parse_yyin, *lwg_parse_yyout;
#define EOB_ACT_CONTINUE_SCAN 0
#define EOB_ACT_END_OF_FILE 1
#define EOB_ACT_LAST_MATCH 2
#define YY_LESS_LINENO(n)
/* Return all but the first "n" matched characters back to the input stream. */
#define yyless(n) \
do \
{ \
/* Undo effects of setting up lwg_parse_yytext. */ \
int yyless_macro_arg = (n); \
YY_LESS_LINENO(yyless_macro_arg);\
*yy_cp = (yy_hold_char); \
YY_RESTORE_YY_MORE_OFFSET \
(yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
YY_DO_BEFORE_ACTION; /* set up lwg_parse_yytext again */ \
} \
while ( 0 )
#define unput(c) yyunput( c, (yytext_ptr) )
#ifndef YY_TYPEDEF_YY_SIZE_T
#define YY_TYPEDEF_YY_SIZE_T
typedef size_t yy_size_t;
#endif
#ifndef YY_STRUCT_YY_BUFFER_STATE
#define YY_STRUCT_YY_BUFFER_STATE
struct yy_buffer_state
{
FILE *yy_input_file;
char *yy_ch_buf; /* input buffer */
char *yy_buf_pos; /* current position in input buffer */
/* Size of input buffer in bytes, not including room for EOB
* characters.
*/
yy_size_t yy_buf_size;
/* Number of characters read into yy_ch_buf, not including EOB
* characters.
*/
int yy_n_chars;
/* Whether we "own" the buffer - i.e., we know we created it,
* and can realloc() it to grow it, and should free() it to
* delete it.
*/
int yy_is_our_buffer;
/* Whether this is an "interactive" input source; if so, and
* if we're using stdio for input, then we want to use getc()
* instead of fread(), to make sure we stop fetching input after
* each newline.
*/
int yy_is_interactive;
/* Whether we're considered to be at the beginning of a line.
* If so, '^' rules will be active on the next match, otherwise
* not.
*/
int yy_at_bol;
int yy_bs_lineno; /**< The line count. */
int yy_bs_column; /**< The column count. */
/* Whether to try to fill the input buffer when we reach the
* end of it.
*/
int yy_fill_buffer;
int yy_buffer_status;
#define YY_BUFFER_NEW 0
#define YY_BUFFER_NORMAL 1
/* When an EOF's been seen but there's still some text to process
* then we mark the buffer as YY_EOF_PENDING, to indicate that we
* shouldn't try reading from the input source any more. We might
* still have a bunch of tokens to match, though, because of
* possible backing-up.
*
* When we actually see the EOF, we change the status to "new"
* (via lwg_parse_yyrestart()), so that the user can continue scanning by
* just pointing lwg_parse_yyin at a new input file.
*/
#define YY_BUFFER_EOF_PENDING 2
};
#endif /* !YY_STRUCT_YY_BUFFER_STATE */
/* Stack of input buffers. */
static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */
static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */
static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */
/* We provide macros for accessing buffer states in case in the
* future we want to put the buffer states in a more general
* "scanner state".
*
* Returns the top of the stack, or NULL.
*/
#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \
? (yy_buffer_stack)[(yy_buffer_stack_top)] \
: NULL)
/* Same as previous macro, but useful when we know that the buffer stack is not
* NULL or when we need an lvalue. For internal use only.
*/
#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)]
/* yy_hold_char holds the character lost when lwg_parse_yytext is formed. */
static char yy_hold_char;
static int yy_n_chars; /* number of characters read into yy_ch_buf */
int lwg_parse_yyleng;
/* Points to current character in buffer. */
static char *yy_c_buf_p = (char *) 0;
static int yy_init = 0; /* whether we need to initialize */
static int yy_start = 0; /* start state number */
/* Flag which is used to allow lwg_parse_yywrap()'s to do buffer switches
* instead of setting up a fresh lwg_parse_yyin. A bit of a hack ...
*/
static int yy_did_buffer_switch_on_eof;
void lwg_parse_yyrestart (FILE *input_file );
void lwg_parse_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer );
YY_BUFFER_STATE lwg_parse_yy_create_buffer (FILE *file,int size );
void lwg_parse_yy_delete_buffer (YY_BUFFER_STATE b );
void lwg_parse_yy_flush_buffer (YY_BUFFER_STATE b );
void lwg_parse_yypush_buffer_state (YY_BUFFER_STATE new_buffer );
void lwg_parse_yypop_buffer_state (void );
static void lwg_parse_yyensure_buffer_stack (void );
static void lwg_parse_yy_load_buffer_state (void );
static void lwg_parse_yy_init_buffer (YY_BUFFER_STATE b,FILE *file );
#define YY_FLUSH_BUFFER lwg_parse_yy_flush_buffer(YY_CURRENT_BUFFER )
YY_BUFFER_STATE lwg_parse_yy_scan_buffer (char *base,yy_size_t size );
YY_BUFFER_STATE lwg_parse_yy_scan_string (yyconst char *yy_str );
YY_BUFFER_STATE lwg_parse_yy_scan_bytes (yyconst char *bytes,int len );
void *lwg_parse_yyalloc (yy_size_t );
void *lwg_parse_yyrealloc (void *,yy_size_t );
void lwg_parse_yyfree (void * );
#define yy_new_buffer lwg_parse_yy_create_buffer
#define yy_set_interactive(is_interactive) \
{ \
if ( ! YY_CURRENT_BUFFER ){ \
lwg_parse_yyensure_buffer_stack (); \
YY_CURRENT_BUFFER_LVALUE = \
lwg_parse_yy_create_buffer(lwg_parse_yyin,YY_BUF_SIZE ); \
} \
YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
}
#define yy_set_bol(at_bol) \
{ \
if ( ! YY_CURRENT_BUFFER ){\
lwg_parse_yyensure_buffer_stack (); \
YY_CURRENT_BUFFER_LVALUE = \
lwg_parse_yy_create_buffer(lwg_parse_yyin,YY_BUF_SIZE ); \
} \
YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
}
#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
/* Begin user sect3 */
typedef char YY_CHAR;
FILE *lwg_parse_yyin = (FILE *) 0, *lwg_parse_yyout = (FILE *) 0;
typedef int yy_state_type;
extern int lwg_parse_yylineno;
int lwg_parse_yylineno = 1;
extern char *lwg_parse_yytext;
#define yytext_ptr lwg_parse_yytext
static yyconst flex_int16_t yy_nxt[][128] =
{
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0
},
{
5, 6, 6, 6, 6, 6, 6, 6, 6, 7,
8, 6, 6, 7, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 7, 6, 6, 6, 6, 6, 6, 6,
9, 10, 6, 6, 11, 6, 6, 6, 12, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 13,
6, 14, 6, 6, 6, 6, 6, 15, 6, 16,
6, 17, 6, 6, 6, 6, 18, 19, 6, 6,
20, 6, 6, 21, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 15,
6, 16, 6, 17, 6, 6, 6, 6, 18, 19,
6, 6, 20, 6, 6, 21, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6
},
{
5, 6, 6, 6, 6, 6, 6, 6, 6, 7,
8, 6, 6, 7, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 7, 6, 6, 6, 6, 6, 6, 6,
9, 10, 6, 6, 11, 6, 6, 6, 12, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 13,
6, 14, 6, 6, 6, 6, 6, 15, 6, 16,
6, 17, 6, 6, 6, 6, 18, 19, 6, 6,
20, 6, 6, 21, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 15,
6, 16, 6, 17, 6, 6, 6, 6, 18, 19,
6, 6, 20, 6, 6, 21, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6
},
{
5, 6, 6, 6, 6, 6, 6, 6, 6, 7,
8, 6, 6, 7, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 7, 6, 6, 6, 6, 6, 6, 6,
9, 10, 6, 22, 11, 22, 23, 6, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 6, 13,
6, 14, 6, 6, 6, 6, 6, 15, 6, 16,
6, 17, 6, 6, 6, 6, 18, 19, 6, 6,
20, 6, 6, 21, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 15,
6, 16, 6, 17, 6, 6, 6, 6, 18, 19,
6, 6, 20, 6, 6, 21, 6, 6, 6, 6,
6, 6, 6, 6, 22, 6, 6, 6
},
{
5, 6, 6, 6, 6, 6, 6, 6, 6, 7,
8, 6, 6, 7, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 7, 6, 6, 6, 6, 6, 6, 6,
9, 10, 6, 22, 11, 22, 23, 6, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 6, 13,
6, 14, 6, 6, 6, 6, 6, 15, 6, 16,
6, 17, 6, 6, 6, 6, 18, 19, 6, 6,
20, 6, 6, 21, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 15,
6, 16, 6, 17, 6, 6, 6, 6, 18, 19,
6, 6, 20, 6, 6, 21, 6, 6, 6, 6,
6, 6, 6, 6, 22, 6, 6, 6
},
{
-5, -5, -5, -5, -5, -5, -5, -5, -5, -5,
-5, -5, -5, -5, -5, -5, -5, -5, -5, -5,
-5, -5, -5, -5, -5, -5, -5, -5, -5, -5,
-5, -5, -5, -5, -5, -5, -5, -5, -5, -5,
-5, -5, -5, -5, -5, -5, -5, -5, -5, -5,
-5, -5, -5, -5, -5, -5, -5, -5, -5, -5,
-5, -5, -5, -5, -5, -5, -5, -5, -5, -5,
-5, -5, -5, -5, -5, -5, -5, -5, -5, -5,
-5, -5, -5, -5, -5, -5, -5, -5, -5, -5,
-5, -5, -5, -5, -5, -5, -5, -5, -5, -5,
-5, -5, -5, -5, -5, -5, -5, -5, -5, -5,
-5, -5, -5, -5, -5, -5, -5, -5, -5, -5,
-5, -5, -5, -5, -5, -5, -5, -5
},
{
5, -6, -6, -6, -6, -6, -6, -6, -6, -6,
-6, -6, -6, -6, -6, -6, -6, -6, -6, -6,
-6, -6, -6, -6, -6, -6, -6, -6, -6, -6,
-6, -6, -6, -6, -6, -6, -6, -6, -6, -6,
-6, -6, -6, -6, -6, -6, -6, -6, -6, -6,
-6, -6, -6, -6, -6, -6, -6, -6, -6, -6,
-6, -6, -6, -6, -6, -6, -6, -6, -6, -6,
-6, -6, -6, -6, -6, -6, -6, -6, -6, -6,
-6, -6, -6, -6, -6, -6, -6, -6, -6, -6,
-6, -6, -6, -6, -6, -6, -6, -6, -6, -6,
-6, -6, -6, -6, -6, -6, -6, -6, -6, -6,
-6, -6, -6, -6, -6, -6, -6, -6, -6, -6,
-6, -6, -6, -6, -6, -6, -6, -6
},
{
5, -7, -7, -7, -7, -7, -7, -7, -7, 25,
25, -7, -7, 25, -7, -7, -7, -7, -7, -7,
-7, -7, -7, -7, -7, -7, -7, -7, -7, -7,
-7, -7, 25, -7, -7, -7, -7, -7, -7, -7,
-7, -7, -7, -7, -7, -7, -7, -7, -7, -7,
-7, -7, -7, -7, -7, -7, -7, -7, -7, -7,
-7, -7, -7, -7, -7, -7, -7, -7, -7, -7,
-7, -7, -7, -7, -7, -7, -7, -7, -7, -7,
-7, -7, -7, -7, -7, -7, -7, -7, -7, -7,
-7, -7, -7, -7, -7, -7, -7, -7, -7, -7,
-7, -7, -7, -7, -7, -7, -7, -7, -7, -7,
-7, -7, -7, -7, -7, -7, -7, -7, -7, -7,
-7, -7, -7, -7, -7, -7, -7, -7
},
{
5, -8, -8, -8, -8, -8, -8, -8, -8, 25,
25, -8, -8, 25, -8, -8, -8, -8, -8, -8,
-8, -8, -8, -8, -8, -8, -8, -8, -8, -8,
-8, -8, 25, -8, -8, -8, -8, -8, -8, -8,
-8, -8, -8, -8, -8, -8, -8, -8, -8, -8,
-8, -8, -8, -8, -8, -8, -8, -8, -8, -8,
-8, -8, -8, -8, -8, -8, -8, -8, -8, -8,
-8, -8, -8, -8, -8, -8, -8, -8, -8, -8,
-8, -8, -8, -8, -8, -8, -8, -8, -8, -8,
-8, -8, -8, -8, -8, -8, -8, -8, -8, -8,
-8, -8, -8, -8, -8, -8, -8, -8, -8, -8,
-8, -8, -8, -8, -8, -8, -8, -8, -8, -8,
-8, -8, -8, -8, -8, -8, -8, -8
},
{
5, -9, -9, -9, -9, -9, -9, -9, -9, -9,
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9,
-9, -9, -9, -9, -9, -9, -9, -9
},
{
5, -10, -10, -10, -10, -10, -10, -10, -10, -10,
-10, -10, -10, -10, -10, -10, -10, -10, -10, -10,
-10, -10, -10, -10, -10, -10, -10, -10, -10, -10,
-10, -10, -10, -10, -10, -10, -10, -10, -10, -10,
-10, -10, -10, -10, -10, -10, -10, -10, -10, -10,
-10, -10, -10, -10, -10, -10, -10, -10, -10, -10,
-10, -10, -10, -10, -10, -10, -10, -10, -10, -10,
-10, -10, -10, -10, -10, -10, -10, -10, -10, -10,
-10, -10, -10, -10, -10, -10, -10, -10, -10, -10,
-10, -10, -10, -10, -10, -10, -10, -10, -10, -10,
-10, -10, -10, -10, -10, -10, -10, -10, -10, -10,
-10, -10, -10, -10, -10, -10, -10, -10, -10, -10,
-10, -10, -10, -10, -10, -10, -10, -10
},
{
5, -11, -11, -11, -11, -11, -11, -11, -11, -11,
-11, -11, -11, -11, -11, -11, -11, -11, -11, -11,
-11, -11, -11, -11, -11, -11, -11, -11, -11, -11,
-11, -11, -11, -11, -11, -11, -11, -11, -11, -11,
-11, -11, -11, -11, -11, -11, -11, -11, -11, -11,
-11, -11, -11, -11, -11, -11, -11, -11, -11, -11,
-11, -11, -11, -11, -11, -11, -11, -11, -11, -11,
-11, -11, -11, -11, -11, -11, -11, -11, -11, -11,
-11, -11, -11, -11, -11, -11, -11, -11, -11, -11,
-11, -11, -11, -11, -11, -11, -11, -11, -11, -11,
-11, -11, -11, -11, -11, -11, -11, -11, -11, -11,
-11, -11, -11, -11, -11, -11, -11, -11, -11, -11,
-11, -11, -11, -11, -11, -11, -11, -11
},
{
5, -12, -12, -12, -12, -12, -12, -12, -12, -12,
-12, -12, -12, -12, -12, -12, -12, -12, -12, -12,
-12, -12, -12, -12, -12, -12, -12, -12, -12, -12,
-12, -12, -12, -12, -12, -12, -12, -12, -12, -12,
-12, -12, -12, -12, -12, -12, -12, -12, 26, 27,
-12, -12, -12, -12, -12, -12, -12, -12, -12, -12,
-12, -12, -12, -12, -12, -12, -12, -12, -12, -12,
-12, -12, -12, -12, -12, -12, -12, -12, -12, -12,
-12, -12, -12, -12, -12, -12, -12, -12, -12, -12,
-12, -12, -12, -12, -12, -12, -12, -12, -12, -12,
-12, -12, -12, -12, -12, -12, -12, -12, -12, -12,
-12, -12, -12, -12, -12, -12, -12, -12, -12, -12,
-12, -12, -12, -12, -12, -12, -12, -12
},
{
5, -13, -13, -13, -13, -13, -13, -13, -13, -13,
-13, -13, -13, -13, -13, -13, -13, -13, -13, -13,
-13, -13, -13, -13, -13, -13, -13, -13, -13, -13,
-13, -13, -13, -13, -13, -13, -13, -13, -13, -13,
-13, -13, -13, -13, -13, -13, -13, -13, -13, -13,
-13, -13, -13, -13, -13, -13, -13, -13, -13, -13,
-13, -13, -13, -13, -13, -13, -13, -13, -13, -13,
-13, -13, -13, -13, -13, -13, -13, -13, -13, -13,
-13, -13, -13, -13, -13, -13, -13, -13, -13, -13,
-13, -13, -13, -13, -13, -13, -13, -13, -13, -13,
-13, -13, -13, -13, -13, -13, -13, -13, -13, -13,
-13, -13, -13, -13, -13, -13, -13, -13, -13, -13,
-13, -13, -13, -13, -13, -13, -13, -13
},
{
5, -14, -14, -14, -14, -14, -14, -14, -14, -14,
-14, -14, -14, -14, -14, -14, -14, -14, -14, -14,
-14, -14, -14, -14, -14, -14, -14, -14, -14, -14,
-14, -14, -14, -14, -14, -14, -14, -14, -14, -14,
-14, -14, -14, -14, -14, -14, -14, -14, -14, -14,
-14, -14, -14, -14, -14, -14, -14, -14, -14, -14,
-14, -14, -14, -14, -14, -14, -14, -14, -14, -14,
-14, -14, -14, -14, -14, -14, -14, -14, -14, -14,
-14, -14, -14, -14, -14, -14, -14, -14, -14, -14,
-14, -14, -14, -14, -14, -14, -14, -14, -14, -14,
-14, -14, -14, -14, -14, -14, -14, -14, -14, -14,
-14, -14, -14, -14, -14, -14, -14, -14, -14, -14,
-14, -14, -14, -14, -14, -14, -14, -14
},
{
5, -15, -15, -15, -15, -15, -15, -15, -15, -15,
-15, -15, -15, -15, -15, -15, -15, -15, -15, -15,
-15, -15, -15, -15, -15, -15, -15, -15, -15, -15,
-15, -15, -15, -15, -15, -15, -15, -15, -15, -15,
-15, -15, -15, -15, -15, -15, -15, -15, -15, -15,
-15, -15, -15, -15, -15, -15, -15, -15, -15, -15,
-15, -15, -15, -15, -15, -15, -15, -15, -15, -15,
-15, -15, -15, 28, -15, -15, -15, -15, -15, 29,
-15, -15, -15, -15, -15, 30, -15, -15, -15, -15,
-15, -15, -15, -15, -15, -15, -15, -15, -15, -15,
-15, -15, -15, -15, -15, 28, -15, -15, -15, -15,
-15, 29, -15, -15, -15, -15, -15, 30, -15, -15,
-15, -15, -15, -15, -15, -15, -15, -15
},
{
5, -16, -16, -16, -16, -16, -16, -16, -16, -16,
-16, -16, -16, -16, -16, -16, -16, -16, -16, -16,
-16, -16, -16, -16, -16, -16, -16, -16, -16, -16,
-16, -16, -16, -16, -16, -16, -16, -16, -16, -16,
-16, -16, -16, -16, -16, -16, -16, -16, -16, -16,
-16, -16, -16, -16, -16, -16, -16, -16, -16, -16,
-16, -16, -16, -16, -16, -16, -16, -16, -16, -16,
-16, -16, -16, -16, -16, -16, -16, 31, -16, -16,
-16, -16, -16, -16, -16, -16, -16, -16, -16, -16,
-16, -16, -16, -16, -16, -16, -16, -16, -16, -16,
-16, -16, -16, -16, -16, -16, -16, -16, -16, 31,
-16, -16, -16, -16, -16, -16, -16, -16, -16, -16,
-16, -16, -16, -16, -16, -16, -16, -16
},
{
5, -17, -17, -17, -17, -17, -17, -17, -17, -17,
-17, -17, -17, -17, -17, -17, -17, -17, -17, -17,
-17, -17, -17, -17, -17, -17, -17, -17, -17, -17,
-17, -17, -17, -17, -17, -17, -17, -17, -17, -17,
-17, -17, -17, -17, -17, -17, -17, -17, -17, -17,
-17, -17, -17, -17, -17, -17, -17, -17, -17, -17,
-17, -17, -17, -17, -17, -17, -17, -17, -17, 32,
-17, -17, -17, -17, -17, -17, -17, -17, -17, -17,
-17, -17, -17, -17, -17, -17, -17, -17, -17, -17,
-17, -17, -17, -17, -17, -17, -17, -17, -17, -17,
-17, 32, -17, -17, -17, -17, -17, -17, -17, -17,
-17, -17, -17, -17, -17, -17, -17, -17, -17, -17,
-17, -17, -17, -17, -17, -17, -17, -17
},
{
5, -18, -18, -18, -18, -18, -18, -18, -18, -18,
-18, -18, -18, -18, -18, -18, -18, -18, -18, -18,
-18, -18, -18, -18, -18, -18, -18, -18, -18, -18,
-18, -18, -18, -18, -18, -18, -18, -18, -18, -18,
-18, -18, -18, -18, -18, -18, -18, -18, -18, -18,
-18, -18, -18, -18, -18, -18, -18, -18, -18, -18,
-18, -18, -18, -18, -18, -18, -18, -18, -18, -18,
-18, -18, -18, 33, -18, -18, -18, -18, -18, -18,
-18, -18, -18, -18, -18, -18, -18, -18, -18, -18,
-18, -18, -18, -18, -18, -18, -18, -18, -18, -18,
-18, -18, -18, -18, -18, 33, -18, -18, -18, -18,
-18, -18, -18, -18, -18, -18, -18, -18, -18, -18,
-18, -18, -18, -18, -18, -18, -18, -18
},
{
5, -19, -19, -19, -19, -19, -19, -19, -19, -19,
-19, -19, -19, -19, -19, -19, -19, -19, -19, -19,
-19, -19, -19, -19, -19, -19, -19, -19, -19, -19,
-19, -19, -19, -19, -19, -19, -19, -19, -19, -19,
-19, -19, -19, -19, -19, -19, -19, -19, -19, -19,
-19, -19, -19, -19, -19, -19, -19, -19, -19, -19,
-19, -19, -19, -19, -19, -19, -19, -19, -19, -19,
-19, -19, -19, -19, -19, -19, -19, -19, -19, -19,
-19, -19, -19, -19, -19, 34, -19, -19, -19, -19,
-19, -19, -19, -19, -19, -19, -19, -19, -19, -19,
-19, -19, -19, -19, -19, -19, -19, -19, -19, -19,
-19, -19, -19, -19, -19, -19, -19, 34, -19, -19,
-19, -19, -19, -19, -19, -19, -19, -19
},
{
5, -20, -20, -20, -20, -20, -20, -20, -20, -20,
-20, -20, -20, -20, -20, -20, -20, -20, -20, -20,
-20, -20, -20, -20, -20, -20, -20, -20, -20, -20,
-20, -20, -20, -20, -20, -20, -20, -20, -20, -20,
-20, -20, -20, -20, -20, -20, -20, -20, -20, -20,
-20, -20, -20, -20, -20, -20, -20, -20, -20, -20,
-20, -20, -20, -20, -20, -20, -20, -20, -20, -20,
-20, -20, -20, -20, -20, -20, -20, -20, -20, 35,
-20, -20, -20, -20, -20, -20, -20, -20, -20, -20,
-20, -20, -20, -20, -20, -20, -20, -20, -20, -20,
-20, -20, -20, -20, -20, -20, -20, -20, -20, -20,
-20, 35, -20, -20, -20, -20, -20, -20, -20, -20,
-20, -20, -20, -20, -20, -20, -20, -20
},
{
5, -21, -21, -21, -21, -21, -21, -21, -21, -21,
-21, -21, -21, -21, -21, -21, -21, -21, -21, -21,
-21, -21, -21, -21, -21, -21, -21, -21, -21, -21,
-21, -21, -21, -21, -21, -21, -21, -21, -21, -21,
-21, -21, -21, -21, -21, -21, -21, -21, -21, -21,
-21, -21, -21, -21, -21, -21, -21, -21, -21, -21,
-21, -21, -21, -21, -21, -21, -21, -21, -21, -21,
-21, -21, -21, -21, -21, -21, -21, -21, -21, -21,
-21, -21, 36, -21, -21, -21, -21, -21, -21, -21,
-21, -21, -21, -21, -21, -21, -21, -21, -21, -21,
-21, -21, -21, -21, -21, -21, -21, -21, -21, -21,
-21, -21, -21, -21, 36, -21, -21, -21, -21, -21,
-21, -21, -21, -21, -21, -21, -21, -21
},
{
5, -22, -22, -22, -22, -22, -22, -22, -22, -22,
-22, -22, -22, -22, -22, -22, -22, -22, -22, -22,
-22, -22, -22, -22, -22, -22, -22, -22, -22, -22,
-22, -22, -22, -22, -22, -22, -22, -22, -22, -22,
-22, -22, -22, -22, -22, -22, 37, -22, 38, 38,
38, 38, 38, 38, 38, 38, 38, 38, -22, -22,
-22, -22, -22, -22, -22, -22, -22, -22, -22, -22,
-22, -22, -22, -22, -22, -22, -22, -22, -22, -22,
-22, -22, -22, -22, -22, -22, -22, -22, -22, -22,
-22, -22, -22, -22, -22, -22, -22, -22, -22, -22,
-22, -22, -22, -22, -22, -22, -22, -22, -22, -22,
-22, -22, -22, -22, -22, -22, -22, -22, -22, -22,
-22, -22, -22, -22, -22, -22, -22, -22
},
{
5, -23, -23, -23, -23, -23, -23, -23, -23, -23,
-23, -23, -23, -23, -23, -23, -23, -23, -23, -23,
-23, -23, -23, -23, -23, -23, -23, -23, -23, -23,
-23, -23, -23, -23, -23, -23, -23, -23, -23, -23,
-23, -23, -23, -23, -23, -23, -23, -23, 39, 39,
39, 39, 39, 39, 39, 39, 39, 39, -23, -23,
-23, -23, -23, -23, -23, -23, -23, -23, -23, -23,
-23, -23, -23, -23, -23, -23, -23, -23, -23, -23,
-23, -23, -23, -23, -23, -23, -23, -23, -23, -23,
-23, -23, -23, -23, -23, -23, -23, -23, -23, -23,
-23, -23, -23, -23, -23, -23, -23, -23, -23, -23,
-23, -23, -23, -23, -23, -23, -23, -23, -23, -23,
-23, -23, -23, -23, -23, -23, -23, -23
},
{
5, -24, -24, -24, -24, -24, -24, -24, -24, -24,
-24, -24, -24, -24, -24, -24, -24, -24, -24, -24,
-24, -24, -24, -24, -24, -24, -24, -24, -24, -24,
-24, -24, -24, -24, -24, -24, -24, -24, -24, -24,
-24, -24, -24, -24, -24, -24, 40, -24, 38, 38,
38, 38, 38, 38, 38, 38, 38, 38, -24, -24,
-24, -24, -24, -24, -24, -24, -24, -24, -24, 41,
-24, -24, -24, -24, -24, -24, -24, -24, -24, -24,
-24, -24, -24, -24, -24, -24, -24, -24, -24, -24,
-24, -24, -24, -24, -24, -24, -24, -24, -24, -24,
-24, 41, -24, -24, -24, -24, -24, -24, -24, -24,
-24, -24, -24, -24, -24, -24, -24, -24, -24, -24,
-24, -24, -24, -24, -24, -24, -24, -24
},
{
5, -25, -25, -25, -25, -25, -25, -25, -25, 25,
25, -25, -25, 25, -25, -25, -25, -25, -25, -25,
-25, -25, -25, -25, -25, -25, -25, -25, -25, -25,
-25, -25, 25, -25, -25, -25, -25, -25, -25, -25,
-25, -25, -25, -25, -25, -25, -25, -25, -25, -25,
-25, -25, -25, -25, -25, -25, -25, -25, -25, -25,
-25, -25, -25, -25, -25, -25, -25, -25, -25, -25,
-25, -25, -25, -25, -25, -25, -25, -25, -25, -25,
-25, -25, -25, -25, -25, -25, -25, -25, -25, -25,
-25, -25, -25, -25, -25, -25, -25, -25, -25, -25,
-25, -25, -25, -25, -25, -25, -25, -25, -25, -25,
-25, -25, -25, -25, -25, -25, -25, -25, -25, -25,
-25, -25, -25, -25, -25, -25, -25, -25
},
{
5, -26, -26, -26, -26, -26, -26, -26, -26, -26,
-26, -26, -26, -26, -26, -26, -26, -26, -26, -26,
-26, -26, -26, -26, -26, -26, -26, -26, -26, -26,
-26, -26, -26, -26, -26, -26, -26, -26, -26, -26,
-26, -26, -26, -26, -26, -26, -26, -26, 42, 42,
42, 42, 42, 42, 42, 42, 42, 42, -26, -26,
-26, -26, -26, -26, -26, 42, 42, 42, 42, 42,
42, -26, -26, -26, -26, -26, -26, -26, -26, -26,
-26, -26, -26, -26, -26, -26, -26, -26, -26, -26,
-26, -26, -26, -26, -26, -26, -26, 42, 42, 42,
42, 42, 42, -26, -26, -26, -26, -26, -26, -26,
-26, -26, -26, -26, -26, -26, -26, -26, -26, -26,
-26, -26, -26, -26, -26, -26, -26, -26
},
{
5, -27, -27, -27, -27, -27, -27, -27, -27, -27,
-27, -27, -27, -27, -27, -27, -27, -27, -27, -27,
-27, -27, -27, -27, -27, -27, -27, -27, -27, -27,
-27, -27, -27, -27, -27, -27, -27, -27, -27, -27,
-27, -27, -27, -27, -27, -27, -27, -27, 43, 43,
43, 43, 43, 43, 43, 43, 43, 43, -27, -27,
-27, -27, -27, -27, -27, 43, 43, 43, 43, 43,
43, -27, -27, -27, -27, -27, -27, -27, -27, -27,
-27, -27, -27, -27, -27, -27, -27, -27, -27, -27,
-27, -27, -27, -27, -27, -27, -27, 43, 43, 43,
43, 43, 43, -27, -27, -27, -27, -27, -27, -27,
-27, -27, -27, -27, -27, -27, -27, -27, -27, -27,
-27, -27, -27, -27, -27, -27, -27, -27
},
{
5, -28, -28, -28, -28, -28, -28, -28, -28, -28,
-28, -28, -28, -28, -28, -28, -28, -28, -28, -28,
-28, -28, -28, -28, -28, -28, -28, -28, -28, -28,
-28, -28, -28, -28, -28, -28, -28, -28, -28, -28,
-28, -28, -28, -28, -28, -28, -28, -28, -28, -28,
-28, -28, -28, -28, -28, -28, -28, -28, -28, -28,
-28, -28, -28, -28, -28, -28, -28, -28, -28, -28,
-28, -28, -28, -28, -28, -28, -28, -28, -28, -28,
-28, -28, 44, -28, -28, -28, -28, -28, -28, -28,
-28, -28, -28, -28, -28, -28, -28, -28, -28, -28,
-28, -28, -28, -28, -28, -28, -28, -28, -28, -28,
-28, -28, -28, -28, 44, -28, -28, -28, -28, -28,
-28, -28, -28, -28, -28, -28, -28, -28
},
{
5, -29, -29, -29, -29, -29, -29, -29, -29, -29,
-29, -29, -29, -29, -29, -29, -29, -29, -29, -29,
-29, -29, -29, -29, -29, -29, -29, -29, -29, -29,
-29, -29, -29, -29, -29, -29, -29, -29, -29, -29,
-29, -29, -29, -29, -29, -29, -29, -29, -29, -29,
-29, -29, -29, -29, -29, -29, -29, -29, -29, -29,
-29, -29, -29, -29, -29, -29, -29, -29, -29, -29,
-29, -29, -29, -29, -29, -29, -29, 45, -29, -29,
-29, -29, -29, -29, -29, -29, -29, -29, -29, -29,
-29, -29, -29, -29, -29, -29, -29, -29, -29, -29,
-29, -29, -29, -29, -29, -29, -29, -29, -29, 45,
-29, -29, -29, -29, -29, -29, -29, -29, -29, -29,
-29, -29, -29, -29, -29, -29, -29, -29
},
{
5, -30, -30, -30, -30, -30, -30, -30, -30, -30,
-30, -30, -30, -30, -30, -30, -30, -30, -30, -30,
-30, -30, -30, -30, -30, -30, -30, -30, -30, -30,
-30, -30, -30, -30, -30, -30, -30, -30, -30, -30,
-30, -30, -30, -30, -30, -30, -30, -30, -30, -30,
-30, -30, -30, -30, -30, -30, -30, -30, -30, -30,
-30, -30, -30, -30, -30, -30, -30, -30, -30, -30,
-30, -30, -30, -30, -30, -30, -30, -30, -30, -30,
-30, -30, 46, -30, -30, -30, -30, -30, -30, -30,
-30, -30, -30, -30, -30, -30, -30, -30, -30, -30,
-30, -30, -30, -30, -30, -30, -30, -30, -30, -30,
-30, -30, -30, -30, 46, -30, -30, -30, -30, -30,
-30, -30, -30, -30, -30, -30, -30, -30
},
{
5, -31, -31, -31, -31, -31, -31, -31, -31, -31,
-31, -31, -31, -31, -31, -31, -31, -31, -31, -31,
-31, -31, -31, -31, -31, -31, -31, -31, -31, -31,
-31, -31, -31, -31, -31, -31, -31, -31, -31, -31,
-31, -31, -31, -31, -31, -31, -31, -31, -31, -31,
-31, -31, -31, -31, -31, -31, -31, -31, -31, -31,
-31, -31, -31, -31, -31, -31, -31, -31, -31, -31,
-31, -31, -31, -31, -31, -31, -31, -31, -31, -31,
47, -31, -31, -31, -31, -31, -31, -31, -31, -31,
-31, -31, -31, -31, -31, -31, -31, -31, -31, -31,
-31, -31, -31, -31, -31, -31, -31, -31, -31, -31,
-31, -31, 47, -31, -31, -31, -31, -31, -31, -31,
-31, -31, -31, -31, -31, -31, -31, -31
},
{
5, -32, -32, -32, -32, -32, -32, -32, -32, -32,
-32, -32, -32, -32, -32, -32, -32, -32, -32, -32,
-32, -32, -32, -32, -32, -32, -32, -32, -32, -32,
-32, -32, -32, -32, -32, -32, -32, -32, -32, -32,
-32, -32, -32, -32, -32, -32, -32, -32, -32, -32,
-32, -32, -32, -32, -32, -32, -32, -32, -32, -32,
-32, -32, -32, -32, -32, -32, -32, -32, -32, -32,
-32, -32, -32, -32, -32, -32, -32, -32, -32, 48,
-32, -32, -32, -32, -32, -32, -32, -32, -32, -32,
-32, -32, -32, -32, -32, -32, -32, -32, -32, -32,
-32, -32, -32, -32, -32, -32, -32, -32, -32, -32,
-32, 48, -32, -32, -32, -32, -32, -32, -32, -32,
-32, -32, -32, -32, -32, -32, -32, -32
},
{
5, -33, -33, -33, -33, -33, -33, -33, -33, -33,
-33, -33, -33, -33, -33, -33, -33, -33, -33, -33,
-33, -33, -33, -33, -33, -33, -33, -33, -33, -33,
-33, -33, -33, -33, -33, -33, -33, -33, -33, -33,
-33, -33, -33, -33, -33, -33, -33, -33, -33, -33,
-33, -33, -33, -33, -33, -33, -33, -33, -33, -33,
-33, -33, -33, -33, -33, -33, -33, -33, -33, -33,
-33, -33, -33, -33, -33, -33, -33, -33, 49, -33,
-33, -33, -33, -33, -33, -33, -33, -33, -33, -33,
-33, -33, -33, -33, -33, -33, -33, -33, -33, -33,
-33, -33, -33, -33, -33, -33, -33, -33, -33, -33,
49, -33, -33, -33, -33, -33, -33, -33, -33, -33,
-33, -33, -33, -33, -33, -33, -33, -33
},
{
5, -34, -34, -34, -34, -34, -34, -34, -34, -34,
-34, -34, -34, -34, -34, -34, -34, -34, -34, -34,
-34, -34, -34, -34, -34, -34, -34, -34, -34, -34,
-34, -34, -34, -34, -34, -34, -34, -34, -34, -34,
-34, -34, -34, -34, -34, -34, -34, -34, -34, -34,
-34, -34, -34, -34, -34, -34, -34, -34, -34, -34,
-34, -34, -34, -34, -34, -34, -34, -34, -34, -34,
-34, -34, -34, -34, -34, -34, 50, -34, -34, -34,
-34, -34, -34, -34, -34, -34, -34, -34, -34, -34,
-34, -34, -34, -34, -34, -34, -34, -34, -34, -34,
-34, -34, -34, -34, -34, -34, -34, -34, 50, -34,
-34, -34, -34, -34, -34, -34, -34, -34, -34, -34,
-34, -34, -34, -34, -34, -34, -34, -34
},
{
5, -35, -35, -35, -35, -35, -35, -35, -35, -35,
-35, -35, -35, -35, -35, -35, -35, -35, -35, -35,
-35, -35, -35, -35, -35, -35, -35, -35, -35, -35,
-35, -35, -35, -35, -35, -35, -35, -35, -35, -35,
-35, -35, -35, -35, -35, -35, -35, -35, -35, -35,
-35, -35, -35, -35, -35, -35, -35, -35, -35, -35,
-35, -35, -35, -35, -35, -35, -35, -35, -35, -35,
-35, -35, -35, 51, -35, -35, 52, -35, -35, -35,
-35, -35, -35, -35, -35, -35, -35, -35, -35, -35,
-35, -35, -35, -35, -35, -35, -35, -35, -35, -35,
-35, -35, -35, -35, -35, 51, -35, -35, 52, -35,
-35, -35, -35, -35, -35, -35, -35, -35, -35, -35,
-35, -35, -35, -35, -35, -35, -35, -35
},
{
5, -36, -36, -36, -36, -36, -36, -36, -36, -36,
-36, -36, -36, -36, -36, -36, -36, -36, -36, -36,
-36, -36, -36, -36, -36, -36, -36, -36, -36, -36,
-36, -36, -36, -36, -36, -36, -36, -36, -36, -36,
-36, -36, -36, -36, -36, -36, -36, -36, -36, -36,
-36, -36, -36, -36, -36, -36, -36, -36, -36, -36,
-36, -36, -36, -36, -36, -36, -36, -36, -36, -36,
-36, -36, -36, 53, -36, -36, -36, -36, -36, -36,
-36, -36, -36, -36, -36, -36, -36, -36, -36, -36,
-36, -36, -36, -36, -36, -36, -36, -36, -36, -36,
-36, -36, -36, -36, -36, 53, -36, -36, -36, -36,
-36, -36, -36, -36, -36, -36, -36, -36, -36, -36,
-36, -36, -36, -36, -36, -36, -36, -36
},
{
5, -37, -37, -37, -37, -37, -37, -37, -37, -37,
-37, -37, -37, -37, -37, -37, -37, -37, -37, -37,
-37, -37, -37, -37, -37, -37, -37, -37, -37, -37,
-37, -37, -37, -37, -37, -37, -37, -37, -37, -37,
-37, -37, -37, -37, -37, -37, -37, -37, 39, 39,
39, 39, 39, 39, 39, 39, 39, 39, -37, -37,
-37, -37, -37, -37, -37, -37, -37, -37, -37, -37,
-37, -37, -37, -37, -37, -37, -37, -37, -37, -37,
-37, -37, -37, -37, -37, -37, -37, -37, -37, -37,
-37, -37, -37, -37, -37, -37, -37, -37, -37, -37,
-37, -37, -37, -37, -37, -37, -37, -37, -37, -37,
-37, -37, -37, -37, -37, -37, -37, -37, -37, -37,
-37, -37, -37, -37, -37, -37, -37, -37
},
{
5, -38, -38, -38, -38, -38, -38, -38, -38, -38,
-38, -38, -38, -38, -38, -38, -38, -38, -38, -38,
-38, -38, -38, -38, -38, -38, -38, -38, -38, -38,
-38, -38, -38, -38, -38, -38, -38, -38, -38, -38,
-38, -38, -38, -38, -38, -38, 40, -38, 38, 38,
38, 38, 38, 38, 38, 38, 38, 38, -38, -38,
-38, -38, -38, -38, -38, -38, -38, -38, -38, 41,
-38, -38, -38, -38, -38, -38, -38, -38, -38, -38,
-38, -38, -38, -38, -38, -38, -38, -38, -38, -38,
-38, -38, -38, -38, -38, -38, -38, -38, -38, -38,
-38, 41, -38, -38, -38, -38, -38, -38, -38, -38,
-38, -38, -38, -38, -38, -38, -38, -38, -38, -38,
-38, -38, -38, -38, -38, -38, -38, -38
},
{
5, -39, -39, -39, -39, -39, -39, -39, -39, -39,
-39, -39, -39, -39, -39, -39, -39, -39, -39, -39,
-39, -39, -39, -39, -39, -39, -39, -39, -39, -39,
-39, -39, -39, -39, -39, -39, -39, -39, -39, -39,
-39, -39, -39, -39, -39, -39, -39, -39, 39, 39,
39, 39, 39, 39, 39, 39, 39, 39, -39, -39,
-39, -39, -39, -39, -39, -39, -39, -39, -39, 54,
-39, -39, -39, -39, -39, -39, -39, -39, -39, -39,
-39, -39, -39, -39, -39, -39, -39, -39, -39, -39,
-39, -39, -39, -39, -39, -39, -39, -39, -39, -39,
-39, 54, -39, -39, -39, -39, -39, -39, -39, -39,
-39, -39, -39, -39, -39, -39, -39, -39, -39, -39,
-39, -39, -39, -39, -39, -39, -39, -39
},
{
5, -40, -40, -40, -40, -40, -40, -40, -40, -40,
-40, -40, -40, -40, -40, -40, -40, -40, -40, -40,
-40, -40, -40, -40, -40, -40, -40, -40, -40, -40,
-40, -40, -40, -40, -40, -40, -40, -40, -40, -40,
-40, -40, -40, -40, -40, -40, -40, -40, 55, 55,
55, 55, 55, 55, 55, 55, 55, 55, -40, -40,
-40, -40, -40, -40, -40, -40, -40, -40, -40, -40,
-40, -40, -40, -40, -40, -40, -40, -40, -40, -40,
-40, -40, -40, -40, -40, -40, -40, -40, -40, -40,
-40, -40, -40, -40, -40, -40, -40, -40, -40, -40,
-40, -40, -40, -40, -40, -40, -40, -40, -40, -40,
-40, -40, -40, -40, -40, -40, -40, -40, -40, -40,
-40, -40, -40, -40, -40, -40, -40, -40
},
{
5, -41, -41, -41, -41, -41, -41, -41, -41, -41,
-41, -41, -41, -41, -41, -41, -41, -41, -41, -41,
-41, -41, -41, -41, -41, -41, -41, -41, -41, -41,
-41, -41, -41, -41, -41, -41, -41, -41, -41, -41,
-41, -41, -41, 56, -41, 56, -41, -41, 57, 57,
57, 57, 57, 57, 57, 57, 57, 57, -41, -41,
-41, -41, -41, -41, -41, -41, -41, -41, -41, -41,
-41, -41, -41, -41, -41, -41, -41, -41, -41, -41,
-41, -41, -41, -41, -41, -41, -41, -41, -41, -41,
-41, -41, -41, -41, -41, -41, -41, -41, -41, -41,
-41, -41, -41, -41, -41, -41, -41, -41, -41, -41,
-41, -41, -41, -41, -41, -41, -41, -41, -41, -41,
-41, -41, -41, -41, -41, -41, -41, -41
},
{
5, -42, -42, -42, -42, -42, -42, -42, -42, -42,
-42, -42, -42, -42, -42, -42, -42, -42, -42, -42,
-42, -42, -42, -42, -42, -42, -42, -42, -42, -42,
-42, -42, -42, -42, -42, -42, -42, -42, -42, -42,
-42, -42, -42, -42, -42, -42, -42, -42, 42, 42,
42, 42, 42, 42, 42, 42, 42, 42, -42, -42,
-42, -42, -42, -42, -42, 42, 42, 42, 42, 42,
42, -42, -42, -42, -42, -42, -42, -42, -42, -42,
-42, -42, -42, -42, -42, -42, -42, -42, -42, -42,
-42, -42, -42, -42, -42, -42, -42, 42, 42, 42,
42, 42, 42, -42, -42, -42, -42, -42, -42, -42,
-42, -42, -42, -42, -42, -42, -42, -42, -42, -42,
-42, -42, -42, -42, -42, -42, -42, -42
},
{
5, -43, -43, -43, -43, -43, -43, -43, -43, -43,
-43, -43, -43, -43, -43, -43, -43, -43, -43, -43,
-43, -43, -43, -43, -43, -43, -43, -43, -43, -43,
-43, -43, -43, -43, -43, -43, -43, -43, -43, -43,
-43, -43, -43, -43, -43, -43, -43, -43, 43, 43,
43, 43, 43, 43, 43, 43, 43, 43, -43, -43,
-43, -43, -43, -43, -43, 43, 43, 43, 43, 43,
43, -43, -43, -43, -43, -43, -43, -43, -43, -43,
-43, -43, -43, -43, -43, -43, -43, -43, -43, -43,
-43, -43, -43, -43, -43, -43, -43, 43, 43, 43,
43, 43, 43, -43, -43, -43, -43, -43, -43, -43,
-43, -43, -43, -43, -43, -43, -43, -43, -43, -43,
-43, -43, -43, -43, -43, -43, -43, -43
},
{
5, -44, -44, -44, -44, -44, -44, -44, -44, -44,
-44, -44, -44, -44, -44, -44, -44, -44, -44, -44,
-44, -44, -44, -44, -44, -44, -44, -44, -44, -44,
-44, -44, -44, -44, -44, -44, -44, -44, -44, -44,
-44, -44, -44, -44, -44, -44, -44, -44, -44, -44,
-44, -44, -44, -44, -44, -44, -44, -44, -44, -44,
-44, -44, -44, -44, -44, -44, -44, 58, -44, -44,
-44, -44, -44, -44, -44, -44, -44, -44, -44, -44,
-44, -44, -44, -44, -44, -44, -44, -44, -44, -44,
-44, -44, -44, -44, -44, -44, -44, -44, -44, 58,
-44, -44, -44, -44, -44, -44, -44, -44, -44, -44,
-44, -44, -44, -44, -44, -44, -44, -44, -44, -44,
-44, -44, -44, -44, -44, -44, -44, -44
},
{
5, -45, -45, -45, -45, -45, -45, -45, -45, -45,
-45, -45, -45, -45, -45, -45, -45, -45, -45, -45,
-45, -45, -45, -45, -45, -45, -45, -45, -45, -45,
-45, -45, -45, -45, -45, -45, -45, -45, -45, -45,
-45, -45, -45, -45, -45, -45, -45, -45, -45, -45,
-45, -45, -45, -45, -45, -45, -45, -45, -45, -45,
-45, -45, -45, -45, -45, -45, -45, -45, -45, -45,
-45, -45, -45, -45, -45, -45, -45, -45, -45, -45,
59, -45, -45, -45, -45, -45, -45, -45, -45, -45,
-45, -45, -45, -45, -45, -45, -45, -45, -45, -45,
-45, -45, -45, -45, -45, -45, -45, -45, -45, -45,
-45, -45, 59, -45, -45, -45, -45, -45, -45, -45,
-45, -45, -45, -45, -45, -45, -45, -45
},
{
5, -46, -46, -46, -46, -46, -46, -46, -46, -46,
-46, -46, -46, -46, -46, -46, -46, -46, -46, -46,
-46, -46, -46, -46, -46, -46, -46, -46, -46, -46,
-46, -46, -46, -46, -46, -46, -46, -46, -46, -46,
-46, -46, -46, -46, -46, -46, -46, -46, -46, -46,
-46, -46, -46, -46, -46, -46, -46, -46, -46, -46,
-46, -46, -46, -46, -46, -46, -46, -46, -46, -46,
-46, -46, -46, -46, -46, -46, -46, -46, -46, -46,
-46, -46, -46, -46, -46, -46, 60, -46, -46, -46,
-46, -46, -46, -46, -46, -46, -46, -46, -46, -46,
-46, -46, -46, -46, -46, -46, -46, -46, -46, -46,
-46, -46, -46, -46, -46, -46, -46, -46, 60, -46,
-46, -46, -46, -46, -46, -46, -46, -46
},
{
5, -47, -47, -47, -47, -47, -47, -47, -47, -47,
-47, -47, -47, -47, -47, -47, -47, -47, -47, -47,
-47, -47, -47, -47, -47, -47, -47, -47, -47, -47,
-47, -47, -47, -47, -47, -47, -47, -47, -47, -47,
-47, -47, -47, -47, -47, -47, -47, -47, -47, -47,
-47, -47, -47, -47, -47, -47, -47, -47, -47, -47,
-47, -47, -47, -47, -47, -47, -47, -47, -47, -47,
-47, -47, -47, -47, -47, -47, -47, -47, -47, -47,
-47, -47, -47, -47, 61, -47, -47, -47, -47, -47,
-47, -47, -47, -47, -47, -47, -47, -47, -47, -47,
-47, -47, -47, -47, -47, -47, -47, -47, -47, -47,
-47, -47, -47, -47, -47, -47, 61, -47, -47, -47,
-47, -47, -47, -47, -47, -47, -47, -47
},
{
5, -48, -48, -48, -48, -48, -48, -48, -48, -48,
-48, -48, -48, -48, -48, -48, -48, -48, -48, -48,
-48, -48, -48, -48, -48, -48, -48, -48, -48, -48,
-48, -48, -48, -48, -48, -48, -48, -48, -48, -48,
-48, -48, -48, -48, -48, -48, -48, -48, -48, -48,
-48, -48, -48, -48, -48, -48, -48, -48, -48, -48,
-48, -48, -48, -48, -48, -48, -48, -48, -48, -48,
-48, -48, -48, -48, -48, -48, -48, 62, -48, -48,
-48, -48, -48, -48, -48, -48, -48, -48, -48, -48,
-48, -48, -48, -48, -48, -48, -48, -48, -48, -48,
-48, -48, -48, -48, -48, -48, -48, -48, -48, 62,
-48, -48, -48, -48, -48, -48, -48, -48, -48, -48,
-48, -48, -48, -48, -48, -48, -48, -48
},
{
5, -49, -49, -49, -49, -49, -49, -49, -49, -49,
-49, -49, -49, -49, -49, -49, -49, -49, -49, -49,
-49, -49, -49, -49, -49, -49, -49, -49, -49, -49,
-49, -49, -49, -49, -49, -49, -49, -49, -49, -49,
-49, -49, -49, -49, -49, -49, -49, -49, -49, -49,
-49, -49, -49, -49, -49, -49, -49, -49, -49, -49,
-49, -49, -49, -49, -49, -49, -49, -49, -49, 63,
-49, -49, -49, -49, -49, -49, -49, -49, -49, -49,
-49, -49, -49, -49, -49, -49, -49, -49, -49, -49,
-49, -49, -49, -49, -49, -49, -49, -49, -49, -49,
-49, 63, -49, -49, -49, -49, -49, -49, -49, -49,
-49, -49, -49, -49, -49, -49, -49, -49, -49, -49,
-49, -49, -49, -49, -49, -49, -49, -49
},
{
5, -50, -50, -50, -50, -50, -50, -50, -50, -50,
-50, -50, -50, -50, -50, -50, -50, -50, -50, -50,
-50, -50, -50, -50, -50, -50, -50, -50, -50, -50,
-50, -50, -50, -50, -50, -50, -50, -50, -50, -50,
-50, -50, -50, -50, -50, -50, -50, -50, -50, -50,
-50, -50, -50, -50, -50, -50, -50, -50, -50, -50,
-50, -50, -50, -50, -50, -50, -50, -50, -50, -50,
-50, -50, -50, -50, -50, -50, -50, -50, -50, -50,
-50, -50, -50, -50, 64, -50, -50, -50, -50, -50,
-50, -50, -50, -50, -50, -50, -50, -50, -50, -50,
-50, -50, -50, -50, -50, -50, -50, -50, -50, -50,
-50, -50, -50, -50, -50, -50, 64, -50, -50, -50,
-50, -50, -50, -50, -50, -50, -50, -50
},
{
5, -51, -51, -51, -51, -51, -51, -51, -51, -51,
-51, -51, -51, -51, -51, -51, -51, -51, -51, -51,
-51, -51, -51, -51, -51, -51, -51, -51, -51, -51,
-51, -51, -51, -51, -51, -51, -51, -51, -51, -51,
-51, -51, -51, -51, -51, -51, -51, -51, -51, -51,
-51, -51, -51, -51, -51, -51, -51, -51, -51, -51,
-51, -51, -51, -51, -51, -51, -51, -51, -51, -51,
-51, -51, -51, -51, -51, -51, -51, -51, 65, -51,
-51, -51, -51, -51, -51, -51, -51, -51, -51, -51,
-51, -51, -51, -51, -51, -51, -51, -51, -51, -51,
-51, -51, -51, -51, -51, -51, -51, -51, -51, -51,
65, -51, -51, -51, -51, -51, -51, -51, -51, -51,
-51, -51, -51, -51, -51, -51, -51, -51
},
{
5, -52, -52, -52, -52, -52, -52, -52, -52, -52,
-52, -52, -52, -52, -52, -52, -52, -52, -52, -52,
-52, -52, -52, -52, -52, -52, -52, -52, -52, -52,
-52, -52, -52, -52, -52, -52, -52, -52, -52, -52,
-52, -52, -52, -52, -52, -52, -52, -52, -52, -52,
-52, -52, -52, -52, -52, -52, -52, -52, -52, -52,
-52, -52, -52, -52, -52, -52, -52, -52, -52, -52,
-52, -52, -52, -52, -52, -52, -52, -52, -52, -52,
-52, -52, -52, -52, -52, -52, -52, -52, -52, 66,
-52, -52, -52, -52, -52, -52, -52, -52, -52, -52,
-52, -52, -52, -52, -52, -52, -52, -52, -52, -52,
-52, -52, -52, -52, -52, -52, -52, -52, -52, -52,
-52, 66, -52, -52, -52, -52, -52, -52
},
{
5, -53, -53, -53, -53, -53, -53, -53, -53, -53,
-53, -53, -53, -53, -53, -53, -53, -53, -53, -53,
-53, -53, -53, -53, -53, -53, -53, -53, -53, -53,
-53, -53, -53, -53, -53, -53, -53, -53, -53, -53,
-53, -53, -53, -53, -53, -53, -53, -53, -53, -53,
-53, -53, -53, -53, -53, -53, -53, -53, -53, -53,
-53, -53, -53, -53, -53, -53, -53, -53, 67, -53,
-53, -53, -53, -53, -53, -53, -53, -53, -53, -53,
-53, -53, -53, -53, -53, -53, -53, -53, -53, -53,
-53, -53, -53, -53, -53, -53, -53, -53, -53, -53,
67, -53, -53, -53, -53, -53, -53, -53, -53, -53,
-53, -53, -53, -53, -53, -53, -53, -53, -53, -53,
-53, -53, -53, -53, -53, -53, -53, -53
},
{
5, -54, -54, -54, -54, -54, -54, -54, -54, -54,
-54, -54, -54, -54, -54, -54, -54, -54, -54, -54,
-54, -54, -54, -54, -54, -54, -54, -54, -54, -54,
-54, -54, -54, -54, -54, -54, -54, -54, -54, -54,
-54, -54, -54, 68, -54, 68, -54, -54, 69, 69,
69, 69, 69, 69, 69, 69, 69, 69, -54, -54,
-54, -54, -54, -54, -54, -54, -54, -54, -54, -54,
-54, -54, -54, -54, -54, -54, -54, -54, -54, -54,
-54, -54, -54, -54, -54, -54, -54, -54, -54, -54,
-54, -54, -54, -54, -54, -54, -54, -54, -54, -54,
-54, -54, -54, -54, -54, -54, -54, -54, -54, -54,
-54, -54, -54, -54, -54, -54, -54, -54, -54, -54,
-54, -54, -54, -54, -54, -54, -54, -54
},
{
5, -55, -55, -55, -55, -55, -55, -55, -55, -55,
-55, -55, -55, -55, -55, -55, -55, -55, -55, -55,
-55, -55, -55, -55, -55, -55, -55, -55, -55, -55,
-55, -55, -55, -55, -55, -55, -55, -55, -55, -55,
-55, -55, -55, -55, -55, -55, -55, -55, 55, 55,
55, 55, 55, 55, 55, 55, 55, 55, -55, -55,
-55, -55, -55, -55, -55, -55, -55, -55, -55, 41,
-55, -55, -55, -55, -55, -55, -55, -55, -55, -55,
-55, -55, -55, -55, -55, -55, -55, -55, -55, -55,
-55, -55, -55, -55, -55, -55, -55, -55, -55, -55,
-55, 41, -55, -55, -55, -55, -55, -55, -55, -55,
-55, -55, -55, -55, -55, -55, -55, -55, -55, -55,
-55, -55, -55, -55, -55, -55, -55, -55
},
{
5, -56, -56, -56, -56, -56, -56, -56, -56, -56,
-56, -56, -56, -56, -56, -56, -56, -56, -56, -56,
-56, -56, -56, -56, -56, -56, -56, -56, -56, -56,
-56, -56, -56, -56, -56, -56, -56, -56, -56, -56,
-56, -56, -56, -56, -56, -56, -56, -56, 57, 57,
57, 57, 57, 57, 57, 57, 57, 57, -56, -56,
-56, -56, -56, -56, -56, -56, -56, -56, -56, -56,
-56, -56, -56, -56, -56, -56, -56, -56, -56, -56,
-56, -56, -56, -56, -56, -56, -56, -56, -56, -56,
-56, -56, -56, -56, -56, -56, -56, -56, -56, -56,
-56, -56, -56, -56, -56, -56, -56, -56, -56, -56,
-56, -56, -56, -56, -56, -56, -56, -56, -56, -56,
-56, -56, -56, -56, -56, -56, -56, -56
},
{
5, -57, -57, -57, -57, -57, -57, -57, -57, -57,
-57, -57, -57, -57, -57, -57, -57, -57, -57, -57,
-57, -57, -57, -57, -57, -57, -57, -57, -57, -57,
-57, -57, -57, -57, -57, -57, -57, -57, -57, -57,
-57, -57, -57, -57, -57, -57, -57, -57, 57, 57,
57, 57, 57, 57, 57, 57, 57, 57, -57, -57,
-57, -57, -57, -57, -57, -57, -57, -57, -57, -57,
-57, -57, -57, -57, -57, -57, -57, -57, -57, -57,
-57, -57, -57, -57, -57, -57, -57, -57, -57, -57,
-57, -57, -57, -57, -57, -57, -57, -57, -57, -57,
-57, -57, -57, -57, -57, -57, -57, -57, -57, -57,
-57, -57, -57, -57, -57, -57, -57, -57, -57, -57,
-57, -57, -57, -57, -57, -57, -57, -57
},
{
5, -58, -58, -58, -58, -58, -58, -58, -58, -58,
-58, -58, -58, -58, -58, -58, -58, -58, -58, -58,
-58, -58, -58, -58, -58, -58, -58, -58, -58, -58,
-58, -58, -58, -58, -58, -58, -58, -58, -58, -58,
-58, -58, -58, -58, -58, -58, -58, -58, -58, -58,
-58, -58, -58, -58, -58, -58, -58, -58, -58, -58,
-58, -58, -58, -58, -58, -58, -58, -58, -58, -58,
-58, -58, -58, -58, -58, -58, -58, -58, -58, -58,
-58, -58, -58, -58, -58, 70, -58, -58, -58, -58,
-58, -58, -58, -58, -58, -58, -58, -58, -58, -58,
-58, -58, -58, -58, -58, -58, -58, -58, -58, -58,
-58, -58, -58, -58, -58, -58, -58, 70, -58, -58,
-58, -58, -58, -58, -58, -58, -58, -58
},
{
5, -59, -59, -59, -59, -59, -59, -59, -59, -59,
-59, -59, -59, -59, -59, -59, -59, -59, -59, -59,
-59, -59, -59, -59, -59, -59, -59, -59, -59, -59,
-59, -59, -59, -59, -59, -59, -59, -59, -59, -59,
-59, -59, -59, -59, -59, -59, -59, -59, -59, -59,
-59, -59, -59, -59, -59, -59, -59, -59, -59, -59,
-59, -59, -59, -59, -59, -59, -59, -59, -59, -59,
-59, -59, -59, -59, -59, -59, -59, -59, -59, 71,
-59, -59, -59, -59, -59, -59, -59, -59, -59, -59,
-59, -59, -59, -59, -59, -59, -59, -59, -59, -59,
-59, -59, -59, -59, -59, -59, -59, -59, -59, -59,
-59, 71, -59, -59, -59, -59, -59, -59, -59, -59,
-59, -59, -59, -59, -59, -59, -59, -59
},
{
5, -60, -60, -60, -60, -60, -60, -60, -60, -60,
-60, -60, -60, -60, -60, -60, -60, -60, -60, -60,
-60, -60, -60, -60, -60, -60, -60, -60, -60, -60,
-60, -60, -60, -60, -60, -60, -60, -60, -60, -60,
-60, -60, -60, -60, -60, -60, -60, -60, -60, -60,
-60, -60, -60, -60, -60, -60, -60, -60, -60, -60,
-60, -60, -60, -60, -60, -60, -60, -60, -60, 72,
-60, -60, -60, -60, -60, -60, -60, -60, -60, -60,
-60, -60, -60, -60, -60, -60, -60, -60, -60, -60,
-60, -60, -60, -60, -60, -60, -60, -60, -60, -60,
-60, 72, -60, -60, -60, -60, -60, -60, -60, -60,
-60, -60, -60, -60, -60, -60, -60, -60, -60, -60,
-60, -60, -60, -60, -60, -60, -60, -60
},
{
5, -61, -61, -61, -61, -61, -61, -61, -61, -61,
-61, -61, -61, -61, -61, -61, -61, -61, -61, -61,
-61, -61, -61, -61, -61, -61, -61, -61, -61, -61,
-61, -61, -61, -61, -61, -61, -61, -61, -61, -61,
-61, -61, -61, -61, -61, -61, -61, -61, -61, -61,
-61, -61, -61, -61, -61, -61, -61, -61, -61, -61,
-61, -61, -61, -61, -61, -61, -61, -61, -61, -61,
-61, -61, -61, -61, -61, -61, -61, -61, -61, -61,
-61, -61, -61, -61, -61, -61, -61, -61, -61, 73,
-61, -61, -61, -61, -61, -61, -61, -61, -61, -61,
-61, -61, -61, -61, -61, -61, -61, -61, -61, -61,
-61, -61, -61, -61, -61, -61, -61, -61, -61, -61,
-61, 73, -61, -61, -61, -61, -61, -61
},
{
5, -62, -62, -62, -62, -62, -62, -62, -62, -62,
-62, -62, -62, -62, -62, -62, -62, -62, -62, -62,
-62, -62, -62, -62, -62, -62, -62, -62, -62, -62,
-62, -62, -62, -62, -62, -62, -62, -62, -62, -62,
-62, -62, -62, -62, -62, -62, -62, -62, -62, -62,
-62, -62, -62, -62, -62, -62, -62, -62, -62, -62,
-62, -62, -62, -62, -62, -62, -62, -62, -62, 74,
-62, -62, -62, -62, -62, -62, -62, -62, -62, -62,
-62, -62, -62, -62, -62, -62, -62, -62, -62, -62,
-62, -62, -62, -62, -62, -62, -62, -62, -62, -62,
-62, 74, -62, -62, -62, -62, -62, -62, -62, -62,
-62, -62, -62, -62, -62, -62, -62, -62, -62, -62,
-62, -62, -62, -62, -62, -62, -62, -62
},
{
5, -63, -63, -63, -63, -63, -63, -63, -63, -63,
-63, -63, -63, -63, -63, -63, -63, -63, -63, -63,
-63, -63, -63, -63, -63, -63, -63, -63, -63, -63,
-63, -63, -63, -63, -63, -63, -63, -63, -63, -63,
-63, -63, -63, -63, -63, -63, -63, -63, -63, -63,
-63, -63, -63, -63, -63, -63, -63, -63, -63, -63,
-63, -63, -63, -63, -63, -63, -63, -63, -63, -63,
-63, -63, -63, -63, -63, -63, -63, -63, -63, -63,
-63, -63, -63, 75, -63, -63, -63, -63, -63, -63,
-63, -63, -63, -63, -63, -63, -63, -63, -63, -63,
-63, -63, -63, -63, -63, -63, -63, -63, -63, -63,
-63, -63, -63, -63, -63, 75, -63, -63, -63, -63,
-63, -63, -63, -63, -63, -63, -63, -63
},
{
5, -64, -64, -64, -64, -64, -64, -64, -64, -64,
-64, -64, -64, -64, -64, -64, -64, -64, -64, -64,
-64, -64, -64, -64, -64, -64, -64, -64, -64, -64,
-64, -64, -64, -64, -64, -64, -64, -64, -64, -64,
-64, -64, -64, -64, -64, -64, -64, -64, -64, -64,
-64, -64, -64, -64, -64, -64, -64, -64, -64, -64,
-64, -64, -64, -64, -64, -64, -64, -64, -64, -64,
-64, -64, -64, 76, -64, -64, -64, -64, -64, -64,
-64, -64, -64, -64, -64, -64, -64, -64, -64, -64,
-64, -64, -64, -64, -64, -64, -64, -64, -64, -64,
-64, -64, -64, -64, -64, 76, -64, -64, -64, -64,
-64, -64, -64, -64, -64, -64, -64, -64, -64, -64,
-64, -64, -64, -64, -64, -64, -64, -64
},
{
5, -65, -65, -65, -65, -65, -65, -65, -65, -65,
-65, -65, -65, -65, -65, -65, -65, -65, -65, -65,
-65, -65, -65, -65, -65, -65, -65, -65, -65, -65,
-65, -65, -65, -65, -65, -65, -65, -65, -65, -65,
-65, -65, -65, -65, -65, -65, -65, -65, -65, -65,
-65, -65, -65, -65, -65, -65, -65, -65, -65, -65,
-65, -65, -65, -65, -65, -65, -65, -65, -65, -65,
-65, -65, -65, -65, -65, -65, -65, -65, -65, -65,
-65, -65, -65, -65, 77, -65, -65, -65, -65, -65,
-65, -65, -65, -65, -65, -65, -65, -65, -65, -65,
-65, -65, -65, -65, -65, -65, -65, -65, -65, -65,
-65, -65, -65, -65, -65, -65, 77, -65, -65, -65,
-65, -65, -65, -65, -65, -65, -65, -65
},
{
5, -66, -66, -66, -66, -66, -66, -66, -66, -66,
-66, -66, -66, -66, -66, -66, -66, -66, -66, -66,
-66, -66, -66, -66, -66, -66, -66, -66, -66, -66,
-66, -66, -66, -66, -66, -66, -66, -66, -66, -66,
-66, -66, -66, -66, -66, -66, -66, -66, -66, -66,
-66, -66, -66, -66, -66, -66, -66, -66, -66, -66,
-66, -66, -66, -66, -66, -66, -66, -66, -66, -66,
-66, 78, -66, -66, -66, -66, -66, -66, -66, -66,
-66, -66, -66, -66, -66, -66, -66, -66, -66, -66,
-66, -66, -66, -66, -66, -66, -66, -66, -66, -66,
-66, -66, -66, 78, -66, -66, -66, -66, -66, -66,
-66, -66, -66, -66, -66, -66, -66, -66, -66, -66,
-66, -66, -66, -66, -66, -66, -66, -66
},
{
5, -67, -67, -67, -67, -67, -67, -67, -67, -67,
-67, -67, -67, -67, -67, -67, -67, -67, -67, -67,
-67, -67, -67, -67, -67, -67, -67, -67, -67, -67,
-67, -67, -67, -67, -67, -67, -67, -67, -67, -67,
-67, -67, -67, -67, -67, -67, -67, -67, -67, -67,
-67, -67, -67, -67, -67, -67, -67, -67, -67, -67,
-67, -67, -67, -67, -67, -67, -67, -67, -67, -67,
-67, -67, -67, -67, -67, -67, -67, -67, -67, -67,
-67, -67, -67, -67, -67, -67, -67, -67, -67, -67,
-67, -67, -67, -67, -67, -67, -67, -67, -67, -67,
-67, -67, -67, -67, -67, -67, -67, -67, -67, -67,
-67, -67, -67, -67, -67, -67, -67, -67, -67, -67,
-67, -67, -67, -67, -67, -67, -67, -67
},
{
5, -68, -68, -68, -68, -68, -68, -68, -68, -68,
-68, -68, -68, -68, -68, -68, -68, -68, -68, -68,
-68, -68, -68, -68, -68, -68, -68, -68, -68, -68,
-68, -68, -68, -68, -68, -68, -68, -68, -68, -68,
-68, -68, -68, -68, -68, -68, -68, -68, 69, 69,
69, 69, 69, 69, 69, 69, 69, 69, -68, -68,
-68, -68, -68, -68, -68, -68, -68, -68, -68, -68,
-68, -68, -68, -68, -68, -68, -68, -68, -68, -68,
-68, -68, -68, -68, -68, -68, -68, -68, -68, -68,
-68, -68, -68, -68, -68, -68, -68, -68, -68, -68,
-68, -68, -68, -68, -68, -68, -68, -68, -68, -68,
-68, -68, -68, -68, -68, -68, -68, -68, -68, -68,
-68, -68, -68, -68, -68, -68, -68, -68
},
{
5, -69, -69, -69, -69, -69, -69, -69, -69, -69,
-69, -69, -69, -69, -69, -69, -69, -69, -69, -69,
-69, -69, -69, -69, -69, -69, -69, -69, -69, -69,
-69, -69, -69, -69, -69, -69, -69, -69, -69, -69,
-69, -69, -69, -69, -69, -69, -69, -69, 69, 69,
69, 69, 69, 69, 69, 69, 69, 69, -69, -69,
-69, -69, -69, -69, -69, -69, -69, -69, -69, -69,
-69, -69, -69, -69, -69, -69, -69, -69, -69, -69,
-69, -69, -69, -69, -69, -69, -69, -69, -69, -69,
-69, -69, -69, -69, -69, -69, -69, -69, -69, -69,
-69, -69, -69, -69, -69, -69, -69, -69, -69, -69,
-69, -69, -69, -69, -69, -69, -69, -69, -69, -69,
-69, -69, -69, -69, -69, -69, -69, -69
},
{
5, -70, -70, -70, -70, -70, -70, -70, -70, -70,
-70, -70, -70, -70, -70, -70, -70, -70, -70, -70,
-70, -70, -70, -70, -70, -70, -70, -70, -70, -70,
-70, -70, -70, -70, -70, -70, -70, -70, -70, -70,
-70, -70, -70, -70, -70, -70, -70, -70, -70, -70,
-70, -70, -70, -70, -70, -70, -70, -70, -70, -70,
-70, -70, -70, -70, -70, -70, -70, -70, -70, -70,
-70, -70, -70, -70, -70, -70, 79, -70, -70, -70,
-70, -70, -70, -70, -70, -70, -70, -70, -70, -70,
-70, -70, -70, -70, -70, -70, -70, -70, -70, -70,
-70, -70, -70, -70, -70, -70, -70, -70, 79, -70,
-70, -70, -70, -70, -70, -70, -70, -70, -70, -70,
-70, -70, -70, -70, -70, -70, -70, -70
},
{
5, -71, -71, -71, -71, -71, -71, -71, -71, -71,
-71, -71, -71, -71, -71, -71, -71, -71, -71, -71,
-71, -71, -71, -71, -71, -71, -71, -71, -71, -71,
-71, -71, -71, -71, -71, -71, -71, -71, -71, -71,
-71, -71, -71, -71, -71, -71, -71, -71, -71, -71,
-71, -71, -71, -71, -71, -71, -71, -71, -71, -71,
-71, -71, -71, -71, -71, -71, -71, -71, -71, -71,
-71, -71, -71, -71, -71, -71, -71, -71, -71, -71,
-71, -71, -71, -71, -71, 80, -71, -71, -71, -71,
-71, -71, -71, -71, -71, -71, -71, -71, -71, -71,
-71, -71, -71, -71, -71, -71, -71, -71, -71, -71,
-71, -71, -71, -71, -71, -71, -71, 80, -71, -71,
-71, -71, -71, -71, -71, -71, -71, -71
},
{
5, -72, -72, -72, -72, -72, -72, -72, -72, -72,
-72, -72, -72, -72, -72, -72, -72, -72, -72, -72,
-72, -72, -72, -72, -72, -72, -72, -72, -72, -72,
-72, -72, -72, -72, -72, -72, -72, -72, -72, -72,
-72, -72, -72, -72, -72, -72, -72, -72, -72, -72,
-72, -72, -72, -72, -72, -72, -72, -72, -72, -72,
-72, -72, -72, -72, -72, -72, -72, -72, -72, -72,
-72, -72, -72, -72, -72, -72, -72, -72, -72, -72,
81, -72, -72, -72, -72, -72, -72, -72, -72, -72,
-72, -72, -72, -72, -72, -72, -72, -72, -72, -72,
-72, -72, -72, -72, -72, -72, -72, -72, -72, -72,
-72, -72, 81, -72, -72, -72, -72, -72, -72, -72,
-72, -72, -72, -72, -72, -72, -72, -72
},
{
5, -73, -73, -73, -73, -73, -73, -73, -73, -73,
-73, -73, -73, -73, -73, -73, -73, -73, -73, -73,
-73, -73, -73, -73, -73, -73, -73, -73, -73, -73,
-73, -73, -73, -73, -73, -73, -73, -73, -73, -73,
-73, -73, -73, -73, -73, -73, -73, -73, -73, -73,
-73, -73, -73, -73, -73, -73, -73, -73, -73, -73,
-73, -73, -73, -73, -73, -73, -73, -73, -73, -73,
-73, -73, -73, -73, -73, -73, -73, -73, -73, -73,
-73, -73, -73, -73, -73, -73, -73, -73, -73, -73,
-73, -73, -73, -73, -73, -73, -73, -73, -73, -73,
-73, -73, -73, -73, -73, -73, -73, -73, -73, -73,
-73, -73, -73, -73, -73, -73, -73, -73, -73, -73,
-73, -73, -73, -73, -73, -73, -73, -73
},
{
5, -74, -74, -74, -74, -74, -74, -74, -74, -74,
-74, -74, -74, -74, -74, -74, -74, -74, -74, -74,
-74, -74, -74, -74, -74, -74, -74, -74, -74, -74,
-74, -74, -74, -74, -74, -74, -74, -74, -74, -74,
-74, -74, -74, -74, -74, -74, -74, -74, -74, -74,
-74, -74, -74, -74, -74, -74, -74, -74, -74, -74,
-74, -74, -74, -74, -74, -74, -74, -74, -74, -74,
-74, -74, -74, -74, -74, -74, -74, -74, -74, -74,
-74, -74, -74, -74, 82, -74, -74, -74, -74, -74,
-74, -74, -74, -74, -74, -74, -74, -74, -74, -74,
-74, -74, -74, -74, -74, -74, -74, -74, -74, -74,
-74, -74, -74, -74, -74, -74, 82, -74, -74, -74,
-74, -74, -74, -74, -74, -74, -74, -74
},
{
5, -75, -75, -75, -75, -75, -75, -75, -75, -75,
-75, -75, -75, -75, -75, -75, -75, -75, -75, -75,
-75, -75, -75, -75, -75, -75, -75, -75, -75, -75,
-75, -75, -75, -75, -75, -75, -75, -75, -75, -75,
-75, -75, -75, -75, -75, -75, -75, -75, -75, -75,
-75, -75, -75, -75, -75, -75, -75, -75, -75, -75,
-75, -75, -75, -75, -75, -75, -75, -75, -75, -75,
-75, -75, -75, -75, -75, -75, -75, -75, -75, -75,
-75, -75, -75, -75, 83, -75, -75, -75, -75, -75,
-75, -75, -75, -75, -75, -75, -75, -75, -75, -75,
-75, -75, -75, -75, -75, -75, -75, -75, -75, -75,
-75, -75, -75, -75, -75, -75, 83, -75, -75, -75,
-75, -75, -75, -75, -75, -75, -75, -75
},
{
5, -76, -76, -76, -76, -76, -76, -76, -76, -76,
-76, -76, -76, -76, -76, -76, -76, -76, -76, -76,
-76, -76, -76, -76, -76, -76, -76, -76, -76, -76,
-76, -76, -76, -76, -76, -76, -76, -76, -76, -76,
-76, -76, -76, -76, -76, -76, -76, -76, -76, -76,
-76, -76, -76, -76, -76, -76, -76, -76, -76, -76,
-76, -76, -76, -76, -76, -76, -76, 84, -76, -76,
-76, -76, -76, -76, -76, -76, 85, -76, -76, -76,
86, -76, -76, 87, -76, -76, -76, -76, -76, -76,
-76, -76, -76, -76, -76, -76, -76, -76, -76, 84,
-76, -76, -76, -76, -76, -76, -76, -76, 85, -76,
-76, -76, 86, -76, -76, 87, -76, -76, -76, -76,
-76, -76, -76, -76, -76, -76, -76, -76
},
{
5, -77, -77, -77, -77, -77, -77, -77, -77, -77,
-77, -77, -77, -77, -77, -77, -77, -77, -77, -77,
-77, -77, -77, -77, -77, -77, -77, -77, -77, -77,
-77, -77, -77, -77, -77, -77, -77, -77, -77, -77,
-77, -77, -77, -77, -77, -77, -77, -77, -77, -77,
-77, -77, -77, -77, -77, -77, -77, -77, -77, -77,
-77, -77, -77, -77, -77, -77, -77, -77, -77, -77,
-77, -77, -77, -77, -77, -77, -77, 88, -77, -77,
-77, -77, -77, -77, -77, -77, -77, -77, -77, -77,
-77, -77, -77, -77, -77, -77, -77, -77, -77, -77,
-77, -77, -77, -77, -77, -77, -77, -77, -77, 88,
-77, -77, -77, -77, -77, -77, -77, -77, -77, -77,
-77, -77, -77, -77, -77, -77, -77, -77
},
{
5, -78, -78, -78, -78, -78, -78, -78, -78, -78,
-78, -78, -78, -78, -78, -78, -78, -78, -78, -78,
-78, -78, -78, -78, -78, -78, -78, -78, -78, -78,
-78, -78, -78, -78, -78, -78, -78, -78, -78, -78,
-78, -78, -78, -78, -78, -78, -78, -78, -78, -78,
-78, -78, -78, -78, -78, -78, -78, -78, -78, -78,
-78, -78, -78, -78, -78, -78, -78, -78, -78, -78,
-78, -78, -78, -78, -78, -78, -78, -78, -78, 89,
-78, -78, -78, -78, -78, -78, -78, -78, -78, -78,
-78, -78, -78, -78, -78, -78, -78, -78, -78, -78,
-78, -78, -78, -78, -78, -78, -78, -78, -78, -78,
-78, 89, -78, -78, -78, -78, -78, -78, -78, -78,
-78, -78, -78, -78, -78, -78, -78, -78
},
{
5, -79, -79, -79, -79, -79, -79, -79, -79, -79,
-79, -79, -79, -79, -79, -79, -79, -79, -79, -79,
-79, -79, -79, -79, -79, -79, -79, -79, -79, -79,
-79, -79, -79, -79, -79, -79, -79, -79, -79, -79,
-79, -79, -79, -79, -79, -79, -79, -79, -79, -79,
-79, -79, -79, -79, -79, -79, -79, -79, -79, -79,
-79, -79, -79, -79, -79, 90, -79, -79, -79, -79,
-79, -79, -79, -79, -79, -79, -79, -79, -79, -79,
-79, -79, -79, -79, -79, -79, -79, -79, -79, -79,
-79, -79, -79, -79, -79, -79, -79, 90, -79, -79,
-79, -79, -79, -79, -79, -79, -79, -79, -79, -79,
-79, -79, -79, -79, -79, -79, -79, -79, -79, -79,
-79, -79, -79, -79, -79, -79, -79, -79
},
{
5, -80, -80, -80, -80, -80, -80, -80, -80, -80,
-80, -80, -80, -80, -80, -80, -80, -80, -80, -80,
-80, -80, -80, -80, -80, -80, -80, -80, -80, -80,
-80, -80, -80, -80, -80, -80, -80, -80, -80, -80,
-80, -80, -80, -80, -80, -80, -80, -80, -80, -80,
-80, -80, -80, -80, -80, -80, -80, -80, -80, -80,
-80, -80, -80, -80, -80, -80, -80, -80, -80, -80,
-80, -80, -80, -80, -80, -80, -80, -80, 91, -80,
-80, -80, -80, -80, -80, -80, -80, -80, -80, -80,
-80, -80, -80, -80, -80, -80, -80, -80, -80, -80,
-80, -80, -80, -80, -80, -80, -80, -80, -80, -80,
91, -80, -80, -80, -80, -80, -80, -80, -80, -80,
-80, -80, -80, -80, -80, -80, -80, -80
},
{
5, -81, -81, -81, -81, -81, -81, -81, -81, -81,
-81, -81, -81, -81, -81, -81, -81, -81, -81, -81,
-81, -81, -81, -81, -81, -81, -81, -81, -81, -81,
-81, -81, -81, -81, -81, -81, -81, -81, -81, -81,
-81, -81, -81, -81, -81, -81, -81, -81, -81, -81,
-81, -81, -81, -81, -81, -81, -81, -81, -81, -81,
-81, -81, -81, -81, -81, -81, -81, -81, -81, -81,
-81, -81, -81, -81, -81, -81, -81, -81, -81, 92,
-81, -81, -81, -81, -81, -81, -81, -81, -81, -81,
-81, -81, -81, -81, -81, -81, -81, -81, -81, -81,
-81, -81, -81, -81, -81, -81, -81, -81, -81, -81,
-81, 92, -81, -81, -81, -81, -81, -81, -81, -81,
-81, -81, -81, -81, -81, -81, -81, -81
},
{
5, -82, -82, -82, -82, -82, -82, -82, -82, -82,
-82, -82, -82, -82, -82, -82, -82, -82, -82, -82,
-82, -82, -82, -82, -82, -82, -82, -82, -82, -82,
-82, -82, -82, -82, -82, -82, -82, -82, -82, -82,
-82, -82, -82, -82, -82, -82, -82, -82, -82, -82,
-82, -82, -82, -82, -82, -82, -82, -82, -82, -82,
-82, -82, -82, -82, -82, -82, -82, -82, -82, -82,
-82, -82, -82, -82, -82, -82, -82, -82, -82, -82,
-82, -82, 93, -82, -82, -82, -82, -82, -82, -82,
-82, -82, -82, -82, -82, -82, -82, -82, -82, -82,
-82, -82, -82, -82, -82, -82, -82, -82, -82, -82,
-82, -82, -82, -82, 93, -82, -82, -82, -82, -82,
-82, -82, -82, -82, -82, -82, -82, -82
},
{
5, -83, -83, -83, -83, -83, -83, -83, -83, -83,
-83, -83, -83, -83, -83, -83, -83, -83, -83, -83,
-83, -83, -83, -83, -83, -83, -83, -83, -83, -83,
-83, -83, -83, -83, -83, -83, -83, -83, -83, -83,
-83, -83, -83, -83, -83, -83, -83, -83, -83, -83,
-83, -83, -83, -83, -83, -83, -83, -83, -83, -83,
-83, -83, -83, -83, -83, -83, -83, -83, -83, -83,
-83, -83, -83, -83, -83, -83, -83, -83, -83, -83,
-83, -83, 94, -83, -83, -83, -83, -83, -83, -83,
-83, -83, -83, -83, -83, -83, -83, -83, -83, -83,
-83, -83, -83, -83, -83, -83, -83, -83, -83, -83,
-83, -83, -83, -83, 94, -83, -83, -83, -83, -83,
-83, -83, -83, -83, -83, -83, -83, -83
},
{
5, -84, -84, -84, -84, -84, -84, -84, -84, -84,
-84, -84, -84, -84, -84, -84, -84, -84, -84, -84,
-84, -84, -84, -84, -84, -84, -84, -84, -84, -84,
-84, -84, -84, -84, -84, -84, -84, -84, -84, -84,
-84, -84, -84, -84, -84, -84, -84, -84, -84, -84,
-84, -84, -84, -84, -84, -84, -84, -84, -84, -84,
-84, -84, -84, -84, -84, -84, -84, -84, -84, -84,
-84, -84, -84, -84, -84, -84, -84, -84, -84, -84,
-84, -84, -84, -84, -84, 95, -84, -84, -84, -84,
-84, -84, -84, -84, -84, -84, -84, -84, -84, -84,
-84, -84, -84, -84, -84, -84, -84, -84, -84, -84,
-84, -84, -84, -84, -84, -84, -84, 95, -84, -84,
-84, -84, -84, -84, -84, -84, -84, -84
},
{
5, -85, -85, -85, -85, -85, -85, -85, -85, -85,
-85, -85, -85, -85, -85, -85, -85, -85, -85, -85,
-85, -85, -85, -85, -85, -85, -85, -85, -85, -85,
-85, -85, -85, -85, -85, -85, -85, -85, -85, -85,
-85, -85, -85, -85, -85, -85, -85, -85, -85, -85,
-85, -85, -85, -85, -85, -85, -85, -85, -85, -85,
-85, -85, -85, -85, -85, -85, -85, -85, -85, -85,
-85, -85, -85, 96, -85, -85, -85, -85, -85, -85,
-85, -85, -85, -85, -85, -85, -85, -85, -85, -85,
-85, -85, -85, -85, -85, -85, -85, -85, -85, -85,
-85, -85, -85, -85, -85, 96, -85, -85, -85, -85,
-85, -85, -85, -85, -85, -85, -85, -85, -85, -85,
-85, -85, -85, -85, -85, -85, -85, -85
},
{
5, -86, -86, -86, -86, -86, -86, -86, -86, -86,
-86, -86, -86, -86, -86, -86, -86, -86, -86, -86,
-86, -86, -86, -86, -86, -86, -86, -86, -86, -86,
-86, -86, -86, -86, -86, -86, -86, -86, -86, -86,
-86, -86, -86, -86, -86, -86, -86, -86, -86, -86,
-86, -86, -86, -86, -86, -86, -86, -86, -86, -86,
-86, -86, -86, -86, -86, -86, -86, -86, -86, -86,
-86, -86, -86, -86, -86, -86, -86, -86, -86, 97,
-86, -86, -86, -86, -86, -86, -86, -86, -86, -86,
-86, -86, -86, -86, -86, -86, -86, -86, -86, -86,
-86, -86, -86, -86, -86, -86, -86, -86, -86, -86,
-86, 97, -86, -86, -86, -86, -86, -86, -86, -86,
-86, -86, -86, -86, -86, -86, -86, -86
},
{
5, -87, -87, -87, -87, -87, -87, -87, -87, -87,
-87, -87, -87, -87, -87, -87, -87, -87, -87, -87,
-87, -87, -87, -87, -87, -87, -87, -87, -87, -87,
-87, -87, -87, -87, -87, -87, -87, -87, -87, -87,
-87, -87, -87, -87, -87, -87, -87, -87, -87, -87,
-87, -87, -87, -87, -87, -87, -87, -87, -87, -87,
-87, -87, -87, -87, -87, -87, -87, -87, -87, -87,
-87, -87, -87, -87, -87, -87, -87, -87, -87, -87,
-87, -87, -87, -87, -87, 98, -87, -87, -87, -87,
-87, -87, -87, -87, -87, -87, -87, -87, -87, -87,
-87, -87, -87, -87, -87, -87, -87, -87, -87, -87,
-87, -87, -87, -87, -87, -87, -87, 98, -87, -87,
-87, -87, -87, -87, -87, -87, -87, -87
},
{
5, -88, -88, -88, -88, -88, -88, -88, -88, -88,
-88, -88, -88, -88, -88, -88, -88, -88, -88, -88,
-88, -88, -88, -88, -88, -88, -88, -88, -88, -88,
-88, -88, -88, -88, -88, -88, -88, -88, -88, -88,
-88, -88, -88, -88, -88, -88, -88, -88, -88, -88,
-88, -88, -88, -88, -88, -88, -88, -88, -88, -88,
-88, -88, -88, -88, -88, -88, -88, -88, -88, -88,
-88, -88, -88, -88, -88, -88, -88, -88, -88, -88,
-88, -88, -88, -88, -88, -88, -88, -88, -88, -88,
-88, -88, -88, -88, -88, -88, -88, -88, -88, -88,
-88, -88, -88, -88, -88, -88, -88, -88, -88, -88,
-88, -88, -88, -88, -88, -88, -88, -88, -88, -88,
-88, -88, -88, -88, -88, -88, -88, -88
},
{
5, -89, -89, -89, -89, -89, -89, -89, -89, -89,
-89, -89, -89, -89, -89, -89, -89, -89, -89, -89,
-89, -89, -89, -89, -89, -89, -89, -89, -89, -89,
-89, -89, -89, -89, -89, -89, -89, -89, -89, -89,
-89, -89, -89, -89, -89, -89, -89, -89, -89, -89,
-89, -89, -89, -89, -89, -89, -89, -89, -89, -89,
-89, -89, -89, -89, -89, -89, -89, -89, -89, -89,
-89, -89, -89, -89, -89, -89, -89, -89, 99, -89,
-89, -89, -89, -89, -89, -89, -89, -89, -89, -89,
-89, -89, -89, -89, -89, -89, -89, -89, -89, -89,
-89, -89, -89, -89, -89, -89, -89, -89, -89, -89,
99, -89, -89, -89, -89, -89, -89, -89, -89, -89,
-89, -89, -89, -89, -89, -89, -89, -89
},
{
5, -90, -90, -90, -90, -90, -90, -90, -90, -90,
-90, -90, -90, -90, -90, -90, -90, -90, -90, -90,
-90, -90, -90, -90, -90, -90, -90, -90, -90, -90,
-90, -90, -90, -90, -90, -90, -90, -90, -90, -90,
-90, -90, -90, -90, -90, -90, -90, -90, -90, -90,
-90, -90, -90, -90, -90, -90, -90, -90, -90, -90,
-90, -90, -90, -90, -90, -90, -90, -90, -90, -90,
-90, -90, -90, -90, -90, -90, -90, -90, -90, -90,
-90, -90, 100, -90, -90, -90, -90, -90, -90, -90,
-90, -90, -90, -90, -90, -90, -90, -90, -90, -90,
-90, -90, -90, -90, -90, -90, -90, -90, -90, -90,
-90, -90, -90, -90, 100, -90, -90, -90, -90, -90,
-90, -90, -90, -90, -90, -90, -90, -90
},
{
5, -91, -91, -91, -91, -91, -91, -91, -91, -91,
-91, -91, -91, -91, -91, -91, -91, -91, -91, -91,
-91, -91, -91, -91, -91, -91, -91, -91, -91, -91,
-91, -91, -91, -91, -91, -91, -91, -91, -91, -91,
-91, -91, -91, -91, -91, -91, -91, -91, -91, -91,
-91, -91, -91, -91, -91, -91, -91, -91, -91, -91,
-91, -91, -91, -91, -91, -91, -91, -91, 101, -91,
-91, -91, -91, -91, -91, -91, -91, -91, -91, -91,
-91, -91, -91, -91, -91, -91, -91, -91, -91, -91,
-91, -91, -91, -91, -91, -91, -91, -91, -91, -91,
101, -91, -91, -91, -91, -91, -91, -91, -91, -91,
-91, -91, -91, -91, -91, -91, -91, -91, -91, -91,
-91, -91, -91, -91, -91, -91, -91, -91
},
{
5, -92, -92, -92, -92, -92, -92, -92, -92, -92,
-92, -92, -92, -92, -92, -92, -92, -92, -92, -92,
-92, -92, -92, -92, -92, -92, -92, -92, -92, -92,
-92, -92, -92, -92, -92, -92, -92, -92, -92, -92,
-92, -92, -92, -92, -92, -92, -92, -92, -92, -92,
-92, -92, -92, -92, -92, -92, -92, -92, -92, -92,
-92, -92, -92, -92, -92, -92, -92, -92, -92, -92,
-92, -92, -92, -92, -92, -92, 102, -92, -92, -92,
-92, -92, -92, -92, -92, -92, -92, -92, -92, -92,
-92, -92, -92, -92, -92, -92, -92, -92, -92, -92,
-92, -92, -92, -92, -92, -92, -92, -92, 102, -92,
-92, -92, -92, -92, -92, -92, -92, -92, -92, -92,
-92, -92, -92, -92, -92, -92, -92, -92
},
{
5, -93, -93, -93, -93, -93, -93, -93, -93, -93,
-93, -93, -93, -93, -93, -93, -93, -93, -93, -93,
-93, -93, -93, -93, -93, -93, -93, -93, -93, -93,
-93, -93, -93, -93, -93, -93, -93, -93, -93, -93,
-93, -93, -93, -93, -93, -93, -93, -93, -93, -93,
-93, -93, -93, -93, -93, -93, -93, -93, -93, -93,
-93, -93, -93, -93, -93, -93, -93, -93, -93, -93,
-93, -93, -93, -93, -93, -93, -93, -93, -93, -93,
-93, -93, -93, -93, -93, -93, -93, -93, -93, 103,
-93, -93, -93, -93, -93, -93, -93, -93, -93, -93,
-93, -93, -93, -93, -93, -93, -93, -93, -93, -93,
-93, -93, -93, -93, -93, -93, -93, -93, -93, -93,
-93, 103, -93, -93, -93, -93, -93, -93
},
{
5, -94, -94, -94, -94, -94, -94, -94, -94, -94,
-94, -94, -94, -94, -94, -94, -94, -94, -94, -94,
-94, -94, -94, -94, -94, -94, -94, -94, -94, -94,
-94, -94, -94, -94, -94, -94, -94, -94, -94, -94,
-94, -94, -94, -94, -94, -94, -94, -94, -94, -94,
-94, -94, -94, -94, -94, -94, -94, -94, -94, -94,
-94, -94, -94, -94, -94, -94, -94, -94, -94, -94,
-94, -94, -94, 104, -94, -94, -94, -94, -94, -94,
-94, -94, -94, -94, -94, -94, -94, -94, -94, -94,
-94, -94, -94, -94, -94, -94, -94, -94, -94, -94,
-94, -94, -94, -94, -94, 104, -94, -94, -94, -94,
-94, -94, -94, -94, -94, -94, -94, -94, -94, -94,
-94, -94, -94, -94, -94, -94, -94, -94
},
{
5, -95, -95, -95, -95, -95, -95, -95, -95, -95,
-95, -95, -95, -95, -95, -95, -95, -95, -95, -95,
-95, -95, -95, -95, -95, -95, -95, -95, -95, -95,
-95, -95, -95, -95, -95, -95, -95, -95, -95, -95,
-95, -95, -95, -95, -95, -95, -95, -95, -95, -95,
-95, -95, -95, -95, -95, -95, -95, -95, -95, -95,
-95, -95, -95, -95, -95, -95, -95, -95, -95, -95,
-95, -95, -95, -95, -95, -95, -95, -95, -95, -95,
-95, -95, 105, -95, -95, -95, -95, -95, -95, -95,
-95, -95, -95, -95, -95, -95, -95, -95, -95, -95,
-95, -95, -95, -95, -95, -95, -95, -95, -95, -95,
-95, -95, -95, -95, 105, -95, -95, -95, -95, -95,
-95, -95, -95, -95, -95, -95, -95, -95
},
{
5, -96, -96, -96, -96, -96, -96, -96, -96, -96,
-96, -96, -96, -96, -96, -96, -96, -96, -96, -96,
-96, -96, -96, -96, -96, -96, -96, -96, -96, -96,
-96, -96, -96, -96, -96, -96, -96, -96, -96, -96,
-96, -96, -96, -96, -96, -96, -96, -96, -96, -96,
-96, -96, -96, -96, -96, -96, -96, -96, -96, -96,
-96, -96, -96, -96, -96, -96, -96, -96, -96, -96,
-96, -96, -96, -96, -96, -96, -96, -96, 106, -96,
-96, -96, -96, -96, -96, -96, -96, -96, -96, -96,
-96, -96, -96, -96, -96, -96, -96, -96, -96, -96,
-96, -96, -96, -96, -96, -96, -96, -96, -96, -96,
106, -96, -96, -96, -96, -96, -96, -96, -96, -96,
-96, -96, -96, -96, -96, -96, -96, -96
},
{
5, -97, -97, -97, -97, -97, -97, -97, -97, -97,
-97, -97, -97, -97, -97, -97, -97, -97, -97, -97,
-97, -97, -97, -97, -97, -97, -97, -97, -97, -97,
-97, -97, -97, -97, -97, -97, -97, -97, -97, -97,
-97, -97, -97, -97, -97, -97, -97, -97, -97, -97,
-97, -97, -97, -97, -97, -97, -97, -97, -97, -97,
-97, -97, -97, -97, -97, -97, -97, -97, -97, -97,
-97, -97, -97, 107, -97, -97, 108, -97, -97, -97,
-97, -97, -97, -97, -97, -97, -97, -97, -97, -97,
-97, -97, -97, -97, -97, -97, -97, -97, -97, -97,
-97, -97, -97, -97, -97, 107, -97, -97, 108, -97,
-97, -97, -97, -97, -97, -97, -97, -97, -97, -97,
-97, -97, -97, -97, -97, -97, -97, -97
},
{
5, -98, -98, -98, -98, -98, -98, -98, -98, -98,
-98, -98, -98, -98, -98, -98, -98, -98, -98, -98,
-98, -98, -98, -98, -98, -98, -98, -98, -98, -98,
-98, -98, -98, -98, -98, -98, -98, -98, -98, -98,
-98, -98, -98, -98, -98, -98, -98, -98, -98, -98,
-98, -98, -98, -98, -98, -98, -98, -98, -98, -98,
-98, -98, -98, -98, -98, -98, -98, -98, -98, -98,
-98, -98, -98, -98, -98, -98, -98, -98, -98, -98,
-98, -98, 109, -98, -98, -98, -98, -98, -98, -98,
-98, -98, -98, -98, -98, -98, -98, -98, -98, -98,
-98, -98, -98, -98, -98, -98, -98, -98, -98, -98,
-98, -98, -98, -98, 109, -98, -98, -98, -98, -98,
-98, -98, -98, -98, -98, -98, -98, -98
},
{
5, -99, -99, -99, -99, -99, -99, -99, -99, -99,
-99, -99, -99, -99, -99, -99, -99, -99, -99, -99,
-99, -99, -99, -99, -99, -99, -99, -99, -99, -99,
-99, -99, -99, -99, -99, -99, -99, -99, -99, -99,
-99, -99, -99, -99, -99, -99, -99, -99, -99, -99,
-99, -99, -99, -99, -99, -99, -99, -99, -99, -99,
-99, -99, -99, -99, -99, -99, -99, -99, -99, -99,
-99, -99, -99, -99, -99, -99, -99, 110, -99, -99,
-99, -99, -99, -99, -99, -99, -99, -99, -99, -99,
-99, -99, -99, -99, -99, -99, -99, -99, -99, -99,
-99, -99, -99, -99, -99, -99, -99, -99, -99, 110,
-99, -99, -99, -99, -99, -99, -99, -99, -99, -99,
-99, -99, -99, -99, -99, -99, -99, -99
},
{
5, -100, -100, -100, -100, -100, -100, -100, -100, -100,
-100, -100, -100, -100, -100, -100, -100, -100, -100, -100,
-100, -100, -100, -100, -100, -100, -100, -100, -100, -100,
-100, -100, -100, -100, -100, -100, -100, -100, -100, -100,
-100, -100, -100, -100, -100, -100, -100, -100, -100, -100,
-100, -100, -100, -100, -100, -100, -100, -100, -100, -100,
-100, -100, -100, -100, -100, -100, -100, -100, -100, -100,
-100, -100, -100, -100, -100, -100, -100, -100, -100, -100,
-100, -100, -100, 111, -100, -100, -100, -100, -100, -100,
-100, -100, -100, -100, -100, -100, -100, -100, -100, -100,
-100, -100, -100, -100, -100, -100, -100, -100, -100, -100,
-100, -100, -100, -100, -100, 111, -100, -100, -100, -100,
-100, -100, -100, -100, -100, -100, -100, -100
},
{
5, -101, -101, -101, -101, -101, -101, -101, -101, -101,
-101, -101, -101, -101, -101, -101, -101, -101, -101, -101,
-101, -101, -101, -101, -101, -101, -101, -101, -101, -101,
-101, -101, -101, -101, -101, -101, -101, -101, -101, -101,
-101, -101, -101, -101, -101, -101, -101, -101, -101, -101,
-101, -101, -101, -101, -101, -101, -101, -101, -101, -101,
-101, -101, -101, -101, -101, -101, -101, 112, -101, -101,
-101, -101, -101, -101, -101, -101, -101, -101, -101, -101,
-101, -101, -101, -101, -101, -101, -101, -101, -101, -101,
-101, -101, -101, -101, -101, -101, -101, -101, -101, 112,
-101, -101, -101, -101, -101, -101, -101, -101, -101, -101,
-101, -101, -101, -101, -101, -101, -101, -101, -101, -101,
-101, -101, -101, -101, -101, -101, -101, -101
},
{
5, -102, -102, -102, -102, -102, -102, -102, -102, -102,
-102, -102, -102, -102, -102, -102, -102, -102, -102, -102,
-102, -102, -102, -102, -102, -102, -102, -102, -102, -102,
-102, -102, -102, -102, -102, -102, -102, -102, -102, -102,
-102, -102, -102, -102, -102, -102, -102, -102, -102, -102,
-102, -102, -102, -102, -102, -102, -102, -102, -102, -102,
-102, -102, -102, -102, -102, -102, -102, -102, -102, -102,
-102, -102, -102, -102, -102, -102, -102, -102, -102, -102,
-102, -102, -102, -102, -102, -102, -102, -102, -102, 113,
-102, -102, -102, -102, -102, -102, -102, -102, -102, -102,
-102, -102, -102, -102, -102, -102, -102, -102, -102, -102,
-102, -102, -102, -102, -102, -102, -102, -102, -102, -102,
-102, 113, -102, -102, -102, -102, -102, -102
},
{
5, -103, -103, -103, -103, -103, -103, -103, -103, -103,
-103, -103, -103, -103, -103, -103, -103, -103, -103, -103,
-103, -103, -103, -103, -103, -103, -103, -103, -103, -103,
-103, -103, -103, -103, -103, -103, -103, -103, -103, -103,
-103, -103, -103, -103, -103, -103, -103, -103, -103, -103,
-103, -103, -103, -103, -103, -103, -103, -103, -103, -103,
-103, -103, -103, -103, -103, -103, -103, 114, -103, -103,
-103, -103, -103, -103, -103, -103, -103, -103, -103, -103,
-103, -103, -103, -103, -103, -103, -103, -103, -103, -103,
-103, -103, -103, -103, -103, -103, -103, -103, -103, 114,
-103, -103, -103, -103, -103, -103, -103, -103, -103, -103,
-103, -103, -103, -103, -103, -103, -103, -103, -103, -103,
-103, -103, -103, -103, -103, -103, -103, -103
},
{
5, -104, -104, -104, -104, -104, -104, -104, -104, -104,
-104, -104, -104, -104, -104, -104, -104, -104, -104, -104,
-104, -104, -104, -104, -104, -104, -104, -104, -104, -104,
-104, -104, -104, -104, -104, -104, -104, -104, -104, -104,
-104, -104, -104, -104, -104, -104, -104, -104, -104, -104,
-104, -104, -104, -104, -104, -104, -104, -104, -104, -104,
-104, -104, -104, -104, -104, -104, -104, -104, -104, -104,
-104, -104, -104, -104, -104, -104, -104, -104, 115, -104,
-104, -104, -104, -104, -104, -104, -104, -104, -104, -104,
-104, -104, -104, -104, -104, -104, -104, -104, -104, -104,
-104, -104, -104, -104, -104, -104, -104, -104, -104, -104,
115, -104, -104, -104, -104, -104, -104, -104, -104, -104,
-104, -104, -104, -104, -104, -104, -104, -104
},
{
5, -105, -105, -105, -105, -105, -105, -105, -105, -105,
-105, -105, -105, -105, -105, -105, -105, -105, -105, -105,
-105, -105, -105, -105, -105, -105, -105, -105, -105, -105,
-105, -105, -105, -105, -105, -105, -105, -105, -105, -105,
-105, -105, -105, -105, -105, -105, -105, -105, -105, -105,
-105, -105, -105, -105, -105, -105, -105, -105, -105, -105,
-105, -105, -105, -105, -105, -105, -105, -105, -105, -105,
-105, -105, -105, -105, -105, -105, -105, -105, -105, -105,
-105, -105, -105, -105, -105, -105, 116, -105, -105, -105,
-105, -105, -105, -105, -105, -105, -105, -105, -105, -105,
-105, -105, -105, -105, -105, -105, -105, -105, -105, -105,
-105, -105, -105, -105, -105, -105, -105, -105, 116, -105,
-105, -105, -105, -105, -105, -105, -105, -105
},
{
5, -106, -106, -106, -106, -106, -106, -106, -106, -106,
-106, -106, -106, -106, -106, -106, -106, -106, -106, -106,
-106, -106, -106, -106, -106, -106, -106, -106, -106, -106,
-106, -106, -106, -106, -106, -106, -106, -106, -106, -106,
-106, -106, -106, -106, -106, -106, -106, -106, -106, -106,
-106, -106, -106, -106, -106, -106, -106, -106, -106, -106,
-106, -106, -106, -106, -106, -106, -106, -106, -106, 117,
-106, -106, -106, -106, -106, -106, -106, -106, -106, -106,
-106, -106, -106, -106, -106, -106, -106, -106, -106, -106,
-106, -106, -106, -106, -106, -106, -106, -106, -106, -106,
-106, 117, -106, -106, -106, -106, -106, -106, -106, -106,
-106, -106, -106, -106, -106, -106, -106, -106, -106, -106,
-106, -106, -106, -106, -106, -106, -106, -106
},
{
5, -107, -107, -107, -107, -107, -107, -107, -107, -107,
-107, -107, -107, -107, -107, -107, -107, -107, -107, -107,
-107, -107, -107, -107, -107, -107, -107, -107, -107, -107,
-107, -107, -107, -107, -107, -107, -107, -107, -107, -107,
-107, -107, -107, -107, -107, -107, -107, -107, -107, -107,
-107, -107, -107, -107, -107, -107, -107, -107, -107, -107,
-107, -107, -107, -107, -107, -107, -107, -107, -107, -107,
-107, -107, -107, -107, -107, -107, -107, -107, 118, -107,
-107, -107, -107, -107, -107, -107, -107, -107, -107, -107,
-107, -107, -107, -107, -107, -107, -107, -107, -107, -107,
-107, -107, -107, -107, -107, -107, -107, -107, -107, -107,
118, -107, -107, -107, -107, -107, -107, -107, -107, -107,
-107, -107, -107, -107, -107, -107, -107, -107
},
{
5, -108, -108, -108, -108, -108, -108, -108, -108, -108,
-108, -108, -108, -108, -108, -108, -108, -108, -108, -108,
-108, -108, -108, -108, -108, -108, -108, -108, -108, -108,
-108, -108, -108, -108, -108, -108, -108, -108, -108, -108,
-108, -108, -108, -108, -108, -108, -108, -108, -108, -108,
-108, -108, -108, -108, -108, -108, -108, -108, -108, -108,
-108, -108, -108, -108, -108, -108, -108, -108, -108, -108,
-108, -108, -108, -108, -108, -108, -108, -108, -108, -108,
-108, -108, -108, -108, -108, -108, -108, -108, -108, 119,
-108, -108, -108, -108, -108, -108, -108, -108, -108, -108,
-108, -108, -108, -108, -108, -108, -108, -108, -108, -108,
-108, -108, -108, -108, -108, -108, -108, -108, -108, -108,
-108, 119, -108, -108, -108, -108, -108, -108
},
{
5, -109, -109, -109, -109, -109, -109, -109, -109, -109,
-109, -109, -109, -109, -109, -109, -109, -109, -109, -109,
-109, -109, -109, -109, -109, -109, -109, -109, -109, -109,
-109, -109, -109, -109, -109, -109, -109, -109, -109, -109,
-109, -109, -109, -109, -109, -109, -109, -109, -109, -109,
-109, -109, -109, -109, -109, -109, -109, -109, -109, -109,
-109, -109, -109, -109, -109, -109, -109, -109, -109, -109,
120, -109, -109, -109, -109, -109, -109, -109, -109, -109,
-109, -109, -109, -109, -109, -109, -109, -109, -109, -109,
-109, -109, -109, -109, -109, -109, -109, -109, -109, -109,
-109, -109, 120, -109, -109, -109, -109, -109, -109, -109,
-109, -109, -109, -109, -109, -109, -109, -109, -109, -109,
-109, -109, -109, -109, -109, -109, -109, -109
},
{
5, -110, -110, -110, -110, -110, -110, -110, -110, -110,
-110, -110, -110, -110, -110, -110, -110, -110, -110, -110,
-110, -110, -110, -110, -110, -110, -110, -110, -110, -110,
-110, -110, -110, -110, -110, -110, -110, -110, -110, -110,
-110, -110, -110, -110, -110, -110, -110, -110, -110, -110,
-110, -110, -110, -110, -110, -110, -110, -110, -110, -110,
-110, -110, -110, -110, -110, -110, -110, -110, -110, -110,
-110, -110, -110, -110, -110, -110, -110, -110, -110, -110,
-110, -110, -110, -110, -110, -110, -110, -110, -110, -110,
-110, -110, -110, -110, -110, -110, -110, -110, -110, -110,
-110, -110, -110, -110, -110, -110, -110, -110, -110, -110,
-110, -110, -110, -110, -110, -110, -110, -110, -110, -110,
-110, -110, -110, -110, -110, -110, -110, -110
},
{
5, -111, -111, -111, -111, -111, -111, -111, -111, -111,
-111, -111, -111, -111, -111, -111, -111, -111, -111, -111,
-111, -111, -111, -111, -111, -111, -111, -111, -111, -111,
-111, -111, -111, -111, -111, -111, -111, -111, -111, -111,
-111, -111, -111, -111, -111, -111, -111, -111, -111, -111,
-111, -111, -111, -111, -111, -111, -111, -111, -111, -111,
-111, -111, -111, -111, -111, -111, -111, -111, -111, -111,
-111, -111, -111, -111, -111, -111, -111, -111, -111, -111,
-111, -111, -111, -111, 121, -111, -111, -111, -111, -111,
-111, -111, -111, -111, -111, -111, -111, -111, -111, -111,
-111, -111, -111, -111, -111, -111, -111, -111, -111, -111,
-111, -111, -111, -111, -111, -111, 121, -111, -111, -111,
-111, -111, -111, -111, -111, -111, -111, -111
},
{
5, -112, -112, -112, -112, -112, -112, -112, -112, -112,
-112, -112, -112, -112, -112, -112, -112, -112, -112, -112,
-112, -112, -112, -112, -112, -112, -112, -112, -112, -112,
-112, -112, -112, -112, -112, -112, -112, -112, -112, -112,
-112, -112, -112, -112, -112, -112, -112, -112, -112, -112,
-112, -112, -112, -112, -112, -112, -112, -112, -112, -112,
-112, -112, -112, -112, -112, -112, -112, -112, -112, -112,
-112, -112, -112, -112, -112, -112, -112, -112, -112, -112,
-112, -112, -112, -112, -112, 122, -112, -112, -112, -112,
-112, -112, -112, -112, -112, -112, -112, -112, -112, -112,
-112, -112, -112, -112, -112, -112, -112, -112, -112, -112,
-112, -112, -112, -112, -112, -112, -112, 122, -112, -112,
-112, -112, -112, -112, -112, -112, -112, -112
},
{
5, -113, -113, -113, -113, -113, -113, -113, -113, -113,
-113, -113, -113, -113, -113, -113, -113, -113, -113, -113,
-113, -113, -113, -113, -113, -113, -113, -113, -113, -113,
-113, -113, -113, -113, -113, -113, -113, -113, -113, -113,
-113, -113, -113, -113, -113, -113, -113, -113, -113, -113,
-113, -113, -113, -113, -113, -113, -113, -113, -113, -113,
-113, -113, -113, -113, -113, -113, -113, -113, -113, -113,
-113, 123, -113, -113, -113, -113, -113, -113, -113, -113,
-113, -113, -113, -113, -113, -113, -113, -113, -113, -113,
-113, -113, -113, -113, -113, -113, -113, -113, -113, -113,
-113, -113, -113, 123, -113, -113, -113, -113, -113, -113,
-113, -113, -113, -113, -113, -113, -113, -113, -113, -113,
-113, -113, -113, -113, -113, -113, -113, -113
},
{
5, -114, -114, -114, -114, -114, -114, -114, -114, -114,
-114, -114, -114, -114, -114, -114, -114, -114, -114, -114,
-114, -114, -114, -114, -114, -114, -114, -114, -114, -114,
-114, -114, -114, -114, -114, -114, -114, -114, -114, -114,
-114, -114, -114, -114, -114, -114, -114, -114, -114, -114,
-114, -114, -114, -114, -114, -114, -114, -114, -114, -114,
-114, -114, -114, -114, -114, -114, -114, -114, -114, -114,
-114, -114, -114, -114, -114, -114, -114, -114, -114, 124,
-114, -114, -114, -114, -114, -114, -114, -114, -114, -114,
-114, -114, -114, -114, -114, -114, -114, -114, -114, -114,
-114, -114, -114, -114, -114, -114, -114, -114, -114, -114,
-114, 124, -114, -114, -114, -114, -114, -114, -114, -114,
-114, -114, -114, -114, -114, -114, -114, -114
},
{
5, -115, -115, -115, -115, -115, -115, -115, -115, -115,
-115, -115, -115, -115, -115, -115, -115, -115, -115, -115,
-115, -115, -115, -115, -115, -115, -115, -115, -115, -115,
-115, -115, -115, -115, -115, -115, -115, -115, -115, -115,
-115, -115, -115, -115, -115, -115, -115, -115, -115, -115,
-115, -115, -115, -115, -115, -115, -115, -115, -115, -115,
-115, -115, -115, -115, -115, -115, -115, -115, -115, -115,
-115, 125, -115, -115, -115, -115, -115, -115, -115, -115,
-115, -115, -115, -115, -115, -115, -115, -115, -115, -115,
-115, -115, -115, -115, -115, -115, -115, -115, -115, -115,
-115, -115, -115, 125, -115, -115, -115, -115, -115, -115,
-115, -115, -115, -115, -115, -115, -115, -115, -115, -115,
-115, -115, -115, -115, -115, -115, -115, -115
},
{
5, -116, -116, -116, -116, -116, -116, -116, -116, -116,
-116, -116, -116, -116, -116, -116, -116, -116, -116, -116,
-116, -116, -116, -116, -116, -116, -116, -116, -116, -116,
-116, -116, -116, -116, -116, -116, -116, -116, -116, -116,
-116, -116, -116, -116, -116, -116, -116, -116, -116, -116,
-116, -116, -116, -116, -116, -116, -116, -116, -116, -116,
-116, -116, -116, -116, -116, -116, -116, -116, -116, 126,
-116, -116, -116, -116, -116, -116, -116, -116, -116, -116,
-116, -116, -116, -116, -116, -116, -116, -116, -116, -116,
-116, -116, -116, -116, -116, -116, -116, -116, -116, -116,
-116, 126, -116, -116, -116, -116, -116, -116, -116, -116,
-116, -116, -116, -116, -116, -116, -116, -116, -116, -116,
-116, -116, -116, -116, -116, -116, -116, -116
},
{
5, -117, -117, -117, -117, -117, -117, -117, -117, -117,
-117, -117, -117, -117, -117, -117, -117, -117, -117, -117,
-117, -117, -117, -117, -117, -117, -117, -117, -117, -117,
-117, -117, -117, -117, -117, -117, -117, -117, -117, -117,
-117, -117, -117, -117, -117, -117, -117, -117, -117, -117,
-117, -117, -117, -117, -117, -117, -117, -117, -117, -117,
-117, -117, -117, -117, -117, -117, -117, -117, -117, -117,
-117, -117, -117, -117, -117, -117, -117, -117, -117, -117,
-117, -117, -117, 127, -117, -117, -117, -117, -117, -117,
-117, -117, -117, -117, -117, -117, -117, -117, -117, -117,
-117, -117, -117, -117, -117, -117, -117, -117, -117, -117,
-117, -117, -117, -117, -117, 127, -117, -117, -117, -117,
-117, -117, -117, -117, -117, -117, -117, -117
},
{
5, -118, -118, -118, -118, -118, -118, -118, -118, -118,
-118, -118, -118, -118, -118, -118, -118, -118, -118, -118,
-118, -118, -118, -118, -118, -118, -118, -118, -118, -118,
-118, -118, -118, -118, -118, -118, -118, -118, -118, -118,
-118, -118, -118, -118, -118, -118, -118, -118, -118, -118,
-118, -118, -118, -118, -118, -118, -118, -118, -118, -118,
-118, -118, -118, -118, -118, -118, -118, -118, -118, -118,
-118, -118, -118, -118, -118, -118, -118, -118, -118, -118,
-118, -118, -118, -118, 128, -118, -118, -118, -118, -118,
-118, -118, -118, -118, -118, -118, -118, -118, -118, -118,
-118, -118, -118, -118, -118, -118, -118, -118, -118, -118,
-118, -118, -118, -118, -118, -118, 128, -118, -118, -118,
-118, -118, -118, -118, -118, -118, -118, -118
},
{
5, -119, -119, -119, -119, -119, -119, -119, -119, -119,
-119, -119, -119, -119, -119, -119, -119, -119, -119, -119,
-119, -119, -119, -119, -119, -119, -119, -119, -119, -119,
-119, -119, -119, -119, -119, -119, -119, -119, -119, -119,
-119, -119, -119, -119, -119, -119, -119, -119, -119, -119,
-119, -119, -119, -119, -119, -119, -119, -119, -119, -119,
-119, -119, -119, -119, -119, -119, -119, -119, -119, -119,
-119, 129, -119, -119, -119, -119, -119, -119, -119, -119,
-119, -119, -119, -119, -119, -119, -119, -119, -119, -119,
-119, -119, -119, -119, -119, -119, -119, -119, -119, -119,
-119, -119, -119, 129, -119, -119, -119, -119, -119, -119,
-119, -119, -119, -119, -119, -119, -119, -119, -119, -119,
-119, -119, -119, -119, -119, -119, -119, -119
},
{
5, -120, -120, -120, -120, -120, -120, -120, -120, -120,
-120, -120, -120, -120, -120, -120, -120, -120, -120, -120,
-120, -120, -120, -120, -120, -120, -120, -120, -120, -120,
-120, -120, -120, -120, -120, -120, -120, -120, -120, -120,
-120, -120, -120, -120, -120, -120, -120, -120, -120, -120,
-120, -120, -120, -120, -120, -120, -120, -120, -120, -120,
-120, -120, -120, -120, -120, 130, -120, -120, -120, -120,
-120, -120, -120, -120, -120, -120, -120, -120, -120, -120,
-120, -120, -120, -120, -120, -120, -120, -120, -120, -120,
-120, -120, -120, -120, -120, -120, -120, 130, -120, -120,
-120, -120, -120, -120, -120, -120, -120, -120, -120, -120,
-120, -120, -120, -120, -120, -120, -120, -120, -120, -120,
-120, -120, -120, -120, -120, -120, -120, -120
},
{
5, -121, -121, -121, -121, -121, -121, -121, -121, -121,
-121, -121, -121, -121, -121, -121, -121, -121, -121, -121,
-121, -121, -121, -121, -121, -121, -121, -121, -121, -121,
-121, -121, -121, -121, -121, -121, -121, -121, -121, -121,
-121, -121, -121, -121, -121, -121, -121, -121, -121, -121,
-121, -121, -121, -121, -121, -121, -121, -121, -121, -121,
-121, -121, -121, -121, -121, -121, -121, -121, -121, -121,
-121, -121, -121, -121, -121, -121, -121, -121, -121, -121,
-121, -121, 131, -121, -121, -121, -121, -121, -121, -121,
-121, -121, -121, -121, -121, -121, -121, -121, -121, -121,
-121, -121, -121, -121, -121, -121, -121, -121, -121, -121,
-121, -121, -121, -121, 131, -121, -121, -121, -121, -121,
-121, -121, -121, -121, -121, -121, -121, -121
},
{
5, -122, -122, -122, -122, -122, -122, -122, -122, -122,
-122, -122, -122, -122, -122, -122, -122, -122, -122, -122,
-122, -122, -122, -122, -122, -122, -122, -122, -122, -122,
-122, -122, -122, -122, -122, -122, -122, -122, -122, -122,
-122, -122, -122, -122, -122, -122, -122, -122, -122, -122,
-122, -122, -122, -122, -122, -122, -122, -122, -122, -122,
-122, -122, -122, -122, -122, -122, -122, -122, -122, -122,
-122, -122, -122, -122, -122, -122, -122, -122, -122, -122,
-122, -122, 132, -122, -122, -122, -122, -122, -122, -122,
-122, -122, -122, -122, -122, -122, -122, -122, -122, -122,
-122, -122, -122, -122, -122, -122, -122, -122, -122, -122,
-122, -122, -122, -122, 132, -122, -122, -122, -122, -122,
-122, -122, -122, -122, -122, -122, -122, -122
},
{
5, -123, -123, -123, -123, -123, -123, -123, -123, -123,
-123, -123, -123, -123, -123, -123, -123, -123, -123, -123,
-123, -123, -123, -123, -123, -123, -123, -123, -123, -123,
-123, -123, -123, -123, -123, -123, -123, -123, -123, -123,
-123, -123, -123, -123, -123, -123, -123, -123, -123, -123,
-123, -123, -123, -123, -123, -123, -123, -123, -123, -123,
-123, -123, -123, -123, -123, -123, -123, -123, -123, -123,
-123, -123, -123, -123, -123, -123, -123, -123, -123, 133,
-123, -123, -123, -123, -123, -123, -123, -123, -123, -123,
-123, -123, -123, -123, -123, -123, -123, -123, -123, -123,
-123, -123, -123, -123, -123, -123, -123, -123, -123, -123,
-123, 133, -123, -123, -123, -123, -123, -123, -123, -123,
-123, -123, -123, -123, -123, -123, -123, -123
},
{
5, -124, -124, -124, -124, -124, -124, -124, -124, -124,
-124, -124, -124, -124, -124, -124, -124, -124, -124, -124,
-124, -124, -124, -124, -124, -124, -124, -124, -124, -124,
-124, -124, -124, -124, -124, -124, -124, -124, -124, -124,
-124, -124, -124, -124, -124, -124, -124, -124, -124, -124,
-124, -124, -124, -124, -124, -124, -124, -124, -124, -124,
-124, -124, -124, -124, -124, -124, -124, -124, -124, -124,
-124, -124, -124, -124, -124, -124, 134, -124, -124, -124,
-124, -124, -124, -124, -124, -124, -124, -124, -124, -124,
-124, -124, -124, -124, -124, -124, -124, -124, -124, -124,
-124, -124, -124, -124, -124, -124, -124, -124, 134, -124,
-124, -124, -124, -124, -124, -124, -124, -124, -124, -124,
-124, -124, -124, -124, -124, -124, -124, -124
},
{
5, -125, -125, -125, -125, -125, -125, -125, -125, -125,
-125, -125, -125, -125, -125, -125, -125, -125, -125, -125,
-125, -125, -125, -125, -125, -125, -125, -125, -125, -125,
-125, -125, -125, -125, -125, -125, -125, -125, -125, -125,
-125, -125, -125, -125, -125, -125, -125, -125, -125, -125,
-125, -125, -125, -125, -125, -125, -125, -125, -125, -125,
-125, -125, -125, -125, -125, -125, -125, -125, -125, -125,
-125, -125, -125, -125, -125, -125, -125, 135, -125, -125,
-125, -125, -125, -125, -125, -125, -125, -125, -125, -125,
-125, -125, -125, -125, -125, -125, -125, -125, -125, -125,
-125, -125, -125, -125, -125, -125, -125, -125, -125, 135,
-125, -125, -125, -125, -125, -125, -125, -125, -125, -125,
-125, -125, -125, -125, -125, -125, -125, -125
},
{
5, -126, -126, -126, -126, -126, -126, -126, -126, -126,
-126, -126, -126, -126, -126, -126, -126, -126, -126, -126,
-126, -126, -126, -126, -126, -126, -126, -126, -126, -126,
-126, -126, -126, -126, -126, -126, -126, -126, -126, -126,
-126, -126, -126, -126, -126, -126, -126, -126, -126, -126,
-126, -126, -126, -126, -126, -126, -126, -126, -126, -126,
-126, -126, -126, -126, -126, -126, -126, -126, -126, -126,
-126, -126, -126, -126, -126, -126, -126, 136, -126, -126,
-126, -126, -126, -126, -126, -126, -126, -126, -126, -126,
-126, -126, -126, -126, -126, -126, -126, -126, -126, -126,
-126, -126, -126, -126, -126, -126, -126, -126, -126, 136,
-126, -126, -126, -126, -126, -126, -126, -126, -126, -126,
-126, -126, -126, -126, -126, -126, -126, -126
},
{
5, -127, -127, -127, -127, -127, -127, -127, -127, -127,
-127, -127, -127, -127, -127, -127, -127, -127, -127, -127,
-127, -127, -127, -127, -127, -127, -127, -127, -127, -127,
-127, -127, -127, -127, -127, -127, -127, -127, -127, -127,
-127, -127, -127, -127, -127, -127, -127, -127, -127, -127,
-127, -127, -127, -127, -127, -127, -127, -127, -127, -127,
-127, -127, -127, -127, -127, -127, -127, -127, -127, -127,
-127, -127, -127, -127, -127, -127, -127, -127, -127, -127,
-127, -127, -127, -127, 137, -127, -127, -127, -127, -127,
-127, -127, -127, -127, -127, -127, -127, -127, -127, -127,
-127, -127, -127, -127, -127, -127, -127, -127, -127, -127,
-127, -127, -127, -127, -127, -127, 137, -127, -127, -127,
-127, -127, -127, -127, -127, -127, -127, -127
},
{
5, -128, -128, -128, -128, -128, -128, -128, -128, -128,
-128, -128, -128, -128, -128, -128, -128, -128, -128, -128,
-128, -128, -128, -128, -128, -128, -128, -128, -128, -128,
-128, -128, -128, -128, -128, -128, -128, -128, -128, -128,
-128, -128, -128, -128, -128, -128, -128, -128, -128, -128,
-128, -128, -128, -128, -128, -128, -128, -128, -128, -128,
-128, -128, -128, -128, -128, -128, -128, -128, -128, -128,
-128, -128, -128, -128, -128, -128, -128, 138, -128, -128,
-128, -128, -128, -128, -128, -128, -128, -128, -128, -128,
-128, -128, -128, -128, -128, -128, -128, -128, -128, -128,
-128, -128, -128, -128, -128, -128, -128, -128, -128, 138,
-128, -128, -128, -128, -128, -128, -128, -128, -128, -128,
-128, -128, -128, -128, -128, -128, -128, -128
},
{
5, -129, -129, -129, -129, -129, -129, -129, -129, -129,
-129, -129, -129, -129, -129, -129, -129, -129, -129, -129,
-129, -129, -129, -129, -129, -129, -129, -129, -129, -129,
-129, -129, -129, -129, -129, -129, -129, -129, -129, -129,
-129, -129, -129, -129, -129, -129, -129, -129, -129, -129,
-129, -129, -129, -129, -129, -129, -129, -129, -129, -129,
-129, -129, -129, -129, -129, -129, -129, -129, -129, -129,
-129, -129, -129, -129, -129, -129, -129, -129, -129, 139,
-129, -129, -129, -129, -129, -129, -129, -129, -129, -129,
-129, -129, -129, -129, -129, -129, -129, -129, -129, -129,
-129, -129, -129, -129, -129, -129, -129, -129, -129, -129,
-129, 139, -129, -129, -129, -129, -129, -129, -129, -129,
-129, -129, -129, -129, -129, -129, -129, -129
},
{
5, -130, -130, -130, -130, -130, -130, -130, -130, -130,
-130, -130, -130, -130, -130, -130, -130, -130, -130, -130,
-130, -130, -130, -130, -130, -130, -130, -130, -130, -130,
-130, -130, -130, -130, -130, -130, -130, -130, -130, -130,
-130, -130, -130, -130, -130, -130, -130, -130, -130, -130,
-130, -130, -130, -130, -130, -130, -130, -130, -130, -130,
-130, -130, -130, -130, -130, -130, -130, 140, -130, -130,
-130, -130, -130, -130, -130, -130, -130, -130, -130, -130,
-130, -130, -130, -130, -130, -130, -130, -130, -130, -130,
-130, -130, -130, -130, -130, -130, -130, -130, -130, 140,
-130, -130, -130, -130, -130, -130, -130, -130, -130, -130,
-130, -130, -130, -130, -130, -130, -130, -130, -130, -130,
-130, -130, -130, -130, -130, -130, -130, -130
},
{
5, -131, -131, -131, -131, -131, -131, -131, -131, -131,
-131, -131, -131, -131, -131, -131, -131, -131, -131, -131,
-131, -131, -131, -131, -131, -131, -131, -131, -131, -131,
-131, -131, -131, -131, -131, -131, -131, -131, -131, -131,
-131, -131, -131, -131, -131, -131, -131, -131, -131, -131,
-131, -131, -131, -131, -131, -131, -131, -131, -131, -131,
-131, -131, -131, -131, -131, -131, -131, -131, -131, -131,
-131, -131, -131, 141, -131, -131, -131, -131, -131, -131,
-131, -131, -131, -131, -131, -131, -131, -131, -131, -131,
-131, -131, -131, -131, -131, -131, -131, -131, -131, -131,
-131, -131, -131, -131, -131, 141, -131, -131, -131, -131,
-131, -131, -131, -131, -131, -131, -131, -131, -131, -131,
-131, -131, -131, -131, -131, -131, -131, -131
},
{
5, -132, -132, -132, -132, -132, -132, -132, -132, -132,
-132, -132, -132, -132, -132, -132, -132, -132, -132, -132,
-132, -132, -132, -132, -132, -132, -132, -132, -132, -132,
-132, -132, -132, -132, -132, -132, -132, -132, -132, -132,
-132, -132, -132, -132, -132, -132, -132, -132, -132, -132,
-132, -132, -132, -132, -132, -132, -132, -132, -132, -132,
-132, -132, -132, -132, -132, -132, -132, -132, -132, -132,
-132, -132, -132, -132, -132, -132, -132, -132, -132, -132,
-132, -132, -132, -132, -132, -132, 142, -132, -132, -132,
-132, -132, -132, -132, -132, -132, -132, -132, -132, -132,
-132, -132, -132, -132, -132, -132, -132, -132, -132, -132,
-132, -132, -132, -132, -132, -132, -132, -132, 142, -132,
-132, -132, -132, -132, -132, -132, -132, -132
},
{
5, -133, -133, -133, -133, -133, -133, -133, -133, -133,
-133, -133, -133, -133, -133, -133, -133, -133, -133, -133,
-133, -133, -133, -133, -133, -133, -133, -133, -133, -133,
-133, -133, -133, -133, -133, -133, -133, -133, -133, -133,
-133, -133, -133, -133, -133, -133, -133, -133, -133, -133,
-133, -133, -133, -133, -133, -133, -133, -133, -133, -133,
-133, -133, -133, -133, -133, -133, -133, -133, -133, -133,
-133, -133, -133, -133, -133, -133, -133, -133, 143, -133,
-133, -133, -133, -133, -133, -133, -133, -133, -133, -133,
-133, -133, -133, -133, -133, -133, -133, -133, -133, -133,
-133, -133, -133, -133, -133, -133, -133, -133, -133, -133,
143, -133, -133, -133, -133, -133, -133, -133, -133, -133,
-133, -133, -133, -133, -133, -133, -133, -133
},
{
5, -134, -134, -134, -134, -134, -134, -134, -134, -134,
-134, -134, -134, -134, -134, -134, -134, -134, -134, -134,
-134, -134, -134, -134, -134, -134, -134, -134, -134, -134,
-134, -134, -134, -134, -134, -134, -134, -134, -134, -134,
-134, -134, -134, -134, -134, -134, -134, -134, -134, -134,
-134, -134, -134, -134, -134, -134, -134, -134, -134, -134,
-134, -134, -134, -134, -134, -134, -134, -134, -134, -134,
-134, -134, -134, -134, -134, -134, 144, -134, -134, -134,
-134, -134, -134, -134, -134, -134, -134, -134, -134, -134,
-134, -134, -134, -134, -134, -134, -134, -134, -134, -134,
-134, -134, -134, -134, -134, -134, -134, -134, 144, -134,
-134, -134, -134, -134, -134, -134, -134, -134, -134, -134,
-134, -134, -134, -134, -134, -134, -134, -134
},
{
5, -135, -135, -135, -135, -135, -135, -135, -135, -135,
-135, -135, -135, -135, -135, -135, -135, -135, -135, -135,
-135, -135, -135, -135, -135, -135, -135, -135, -135, -135,
-135, -135, -135, -135, -135, -135, -135, -135, -135, -135,
-135, -135, -135, -135, -135, -135, -135, -135, -135, -135,
-135, -135, -135, -135, -135, -135, -135, -135, -135, -135,
-135, -135, -135, -135, -135, -135, -135, -135, -135, -135,
-135, -135, -135, -135, -135, -135, -135, -135, -135, -135,
-135, -135, -135, -135, -135, -135, -135, -135, -135, -135,
-135, -135, -135, -135, -135, -135, -135, -135, -135, -135,
-135, -135, -135, -135, -135, -135, -135, -135, -135, -135,
-135, -135, -135, -135, -135, -135, -135, -135, -135, -135,
-135, -135, -135, -135, -135, -135, -135, -135
},
{
5, -136, -136, -136, -136, -136, -136, -136, -136, -136,
-136, -136, -136, -136, -136, -136, -136, -136, -136, -136,
-136, -136, -136, -136, -136, -136, -136, -136, -136, -136,
-136, -136, -136, -136, -136, -136, -136, -136, -136, -136,
-136, -136, -136, -136, -136, -136, -136, -136, -136, -136,
-136, -136, -136, -136, -136, -136, -136, -136, -136, -136,
-136, -136, -136, -136, -136, -136, -136, -136, -136, -136,
-136, -136, -136, -136, -136, -136, -136, -136, -136, -136,
-136, -136, -136, -136, -136, -136, -136, -136, -136, -136,
-136, -136, -136, -136, -136, -136, -136, -136, -136, -136,
-136, -136, -136, -136, -136, -136, -136, -136, -136, -136,
-136, -136, -136, -136, -136, -136, -136, -136, -136, -136,
-136, -136, -136, -136, -136, -136, -136, -136
},
{
5, -137, -137, -137, -137, -137, -137, -137, -137, -137,
-137, -137, -137, -137, -137, -137, -137, -137, -137, -137,
-137, -137, -137, -137, -137, -137, -137, -137, -137, -137,
-137, -137, -137, -137, -137, -137, -137, -137, -137, -137,
-137, -137, -137, -137, -137, -137, -137, -137, -137, -137,
-137, -137, -137, -137, -137, -137, -137, -137, -137, -137,
-137, -137, -137, -137, -137, -137, -137, -137, -137, -137,
-137, -137, -137, -137, -137, -137, -137, -137, -137, -137,
-137, -137, 145, -137, -137, -137, -137, -137, -137, -137,
-137, -137, -137, -137, -137, -137, -137, -137, -137, -137,
-137, -137, -137, -137, -137, -137, -137, -137, -137, -137,
-137, -137, -137, -137, 145, -137, -137, -137, -137, -137,
-137, -137, -137, -137, -137, -137, -137, -137
},
{
5, -138, -138, -138, -138, -138, -138, -138, -138, -138,
-138, -138, -138, -138, -138, -138, -138, -138, -138, -138,
-138, -138, -138, -138, -138, -138, -138, -138, -138, -138,
-138, -138, -138, -138, -138, -138, -138, -138, -138, -138,
-138, -138, -138, -138, -138, -138, -138, -138, -138, -138,
-138, -138, -138, -138, -138, -138, -138, -138, -138, -138,
-138, -138, -138, -138, -138, -138, -138, -138, -138, -138,
-138, -138, -138, -138, -138, -138, -138, -138, -138, -138,
-138, -138, -138, -138, -138, -138, -138, -138, -138, -138,
-138, -138, -138, -138, -138, -138, -138, -138, -138, -138,
-138, -138, -138, -138, -138, -138, -138, -138, -138, -138,
-138, -138, -138, -138, -138, -138, -138, -138, -138, -138,
-138, -138, -138, -138, -138, -138, -138, -138
},
{
5, -139, -139, -139, -139, -139, -139, -139, -139, -139,
-139, -139, -139, -139, -139, -139, -139, -139, -139, -139,
-139, -139, -139, -139, -139, -139, -139, -139, -139, -139,
-139, -139, -139, -139, -139, -139, -139, -139, -139, -139,
-139, -139, -139, -139, -139, -139, -139, -139, -139, -139,
-139, -139, -139, -139, -139, -139, -139, -139, -139, -139,
-139, -139, -139, -139, -139, -139, -139, -139, -139, -139,
-139, -139, -139, -139, -139, -139, -139, -139, 146, -139,
-139, -139, -139, -139, -139, -139, -139, -139, -139, -139,
-139, -139, -139, -139, -139, -139, -139, -139, -139, -139,
-139, -139, -139, -139, -139, -139, -139, -139, -139, -139,
146, -139, -139, -139, -139, -139, -139, -139, -139, -139,
-139, -139, -139, -139, -139, -139, -139, -139
},
{
5, -140, -140, -140, -140, -140, -140, -140, -140, -140,
-140, -140, -140, -140, -140, -140, -140, -140, -140, -140,
-140, -140, -140, -140, -140, -140, -140, -140, -140, -140,
-140, -140, -140, -140, -140, -140, -140, -140, -140, -140,
-140, -140, -140, -140, -140, -140, -140, -140, -140, -140,
-140, -140, -140, -140, -140, -140, -140, -140, -140, -140,
-140, -140, -140, -140, -140, -140, -140, -140, -140, 147,
-140, -140, -140, -140, -140, -140, -140, -140, -140, -140,
-140, -140, -140, -140, -140, -140, -140, -140, -140, -140,
-140, -140, -140, -140, -140, -140, -140, -140, -140, -140,
-140, 147, -140, -140, -140, -140, -140, -140, -140, -140,
-140, -140, -140, -140, -140, -140, -140, -140, -140, -140,
-140, -140, -140, -140, -140, -140, -140, -140
},
{
5, -141, -141, -141, -141, -141, -141, -141, -141, -141,
-141, -141, -141, -141, -141, -141, -141, -141, -141, -141,
-141, -141, -141, -141, -141, -141, -141, -141, -141, -141,
-141, -141, -141, -141, -141, -141, -141, -141, -141, -141,
-141, -141, -141, -141, -141, -141, -141, -141, -141, -141,
-141, -141, -141, -141, -141, -141, -141, -141, -141, -141,
-141, -141, -141, -141, -141, -141, -141, -141, -141, -141,
-141, -141, -141, -141, -141, -141, -141, -141, 148, -141,
-141, -141, -141, -141, -141, -141, -141, -141, -141, -141,
-141, -141, -141, -141, -141, -141, -141, -141, -141, -141,
-141, -141, -141, -141, -141, -141, -141, -141, -141, -141,
148, -141, -141, -141, -141, -141, -141, -141, -141, -141,
-141, -141, -141, -141, -141, -141, -141, -141
},
{
5, -142, -142, -142, -142, -142, -142, -142, -142, -142,
-142, -142, -142, -142, -142, -142, -142, -142, -142, -142,
-142, -142, -142, -142, -142, -142, -142, -142, -142, -142,
-142, -142, -142, -142, -142, -142, -142, -142, -142, -142,
-142, -142, -142, -142, -142, -142, -142, -142, -142, -142,
-142, -142, -142, -142, -142, -142, -142, -142, -142, -142,
-142, -142, -142, -142, -142, -142, -142, -142, -142, 149,
-142, -142, -142, -142, -142, -142, -142, -142, -142, -142,
-142, -142, -142, -142, -142, -142, -142, -142, -142, -142,
-142, -142, -142, -142, -142, -142, -142, -142, -142, -142,
-142, 149, -142, -142, -142, -142, -142, -142, -142, -142,
-142, -142, -142, -142, -142, -142, -142, -142, -142, -142,
-142, -142, -142, -142, -142, -142, -142, -142
},
{
5, -143, -143, -143, -143, -143, -143, -143, -143, -143,
-143, -143, -143, -143, -143, -143, -143, -143, -143, -143,
-143, -143, -143, -143, -143, -143, -143, -143, -143, -143,
-143, -143, -143, -143, -143, -143, -143, -143, -143, -143,
-143, -143, -143, -143, -143, -143, -143, -143, -143, -143,
-143, -143, -143, -143, -143, -143, -143, -143, -143, -143,
-143, -143, -143, -143, -143, -143, -143, -143, -143, -143,
-143, -143, -143, -143, -143, -143, -143, 150, -143, -143,
-143, -143, -143, -143, -143, -143, -143, -143, -143, -143,
-143, -143, -143, -143, -143, -143, -143, -143, -143, -143,
-143, -143, -143, -143, -143, -143, -143, -143, -143, 150,
-143, -143, -143, -143, -143, -143, -143, -143, -143, -143,
-143, -143, -143, -143, -143, -143, -143, -143
},
{
5, -144, -144, -144, -144, -144, -144, -144, -144, -144,
-144, -144, -144, -144, -144, -144, -144, -144, -144, -144,
-144, -144, -144, -144, -144, -144, -144, -144, -144, -144,
-144, -144, -144, -144, -144, -144, -144, -144, -144, -144,
-144, -144, -144, -144, -144, -144, -144, -144, -144, -144,
-144, -144, -144, -144, -144, -144, -144, -144, -144, -144,
-144, -144, -144, -144, -144, -144, -144, -144, -144, 151,
-144, -144, -144, -144, -144, -144, -144, -144, -144, -144,
-144, -144, -144, -144, -144, -144, -144, -144, -144, -144,
-144, -144, -144, -144, -144, -144, -144, -144, -144, -144,
-144, 151, -144, -144, -144, -144, -144, -144, -144, -144,
-144, -144, -144, -144, -144, -144, -144, -144, -144, -144,
-144, -144, -144, -144, -144, -144, -144, -144
},
{
5, -145, -145, -145, -145, -145, -145, -145, -145, -145,
-145, -145, -145, -145, -145, -145, -145, -145, -145, -145,
-145, -145, -145, -145, -145, -145, -145, -145, -145, -145,
-145, -145, -145, -145, -145, -145, -145, -145, -145, -145,
-145, -145, -145, -145, -145, -145, -145, -145, -145, -145,
-145, -145, -145, -145, -145, -145, -145, -145, -145, -145,
-145, -145, -145, -145, -145, -145, -145, -145, -145, -145,
-145, -145, -145, 152, -145, -145, -145, -145, -145, -145,
-145, -145, -145, -145, -145, -145, -145, -145, -145, -145,
-145, -145, -145, -145, -145, -145, -145, -145, -145, -145,
-145, -145, -145, -145, -145, 152, -145, -145, -145, -145,
-145, -145, -145, -145, -145, -145, -145, -145, -145, -145,
-145, -145, -145, -145, -145, -145, -145, -145
},
{
5, -146, -146, -146, -146, -146, -146, -146, -146, -146,
-146, -146, -146, -146, -146, -146, -146, -146, -146, -146,
-146, -146, -146, -146, -146, -146, -146, -146, -146, -146,
-146, -146, -146, -146, -146, -146, -146, -146, -146, -146,
-146, -146, -146, -146, -146, -146, -146, -146, -146, -146,
-146, -146, -146, -146, -146, -146, -146, -146, -146, -146,
-146, -146, -146, -146, -146, -146, -146, -146, -146, -146,
-146, -146, -146, -146, -146, -146, -146, 153, -146, -146,
-146, -146, -146, -146, -146, -146, -146, -146, -146, -146,
-146, -146, -146, -146, -146, -146, -146, -146, -146, -146,
-146, -146, -146, -146, -146, -146, -146, -146, -146, 153,
-146, -146, -146, -146, -146, -146, -146, -146, -146, -146,
-146, -146, -146, -146, -146, -146, -146, -146
},
{
5, -147, -147, -147, -147, -147, -147, -147, -147, -147,
-147, -147, -147, -147, -147, -147, -147, -147, -147, -147,
-147, -147, -147, -147, -147, -147, -147, -147, -147, -147,
-147, -147, -147, -147, -147, -147, -147, -147, -147, -147,
-147, -147, -147, -147, -147, -147, -147, -147, -147, -147,
-147, -147, -147, -147, -147, -147, -147, -147, -147, -147,
-147, -147, -147, -147, -147, -147, -147, -147, -147, -147,
-147, -147, -147, -147, -147, -147, -147, 154, -147, -147,
-147, -147, -147, -147, -147, -147, -147, -147, -147, -147,
-147, -147, -147, -147, -147, -147, -147, -147, -147, -147,
-147, -147, -147, -147, -147, -147, -147, -147, -147, 154,
-147, -147, -147, -147, -147, -147, -147, -147, -147, -147,
-147, -147, -147, -147, -147, -147, -147, -147
},
{
5, -148, -148, -148, -148, -148, -148, -148, -148, -148,
-148, -148, -148, -148, -148, -148, -148, -148, -148, -148,
-148, -148, -148, -148, -148, -148, -148, -148, -148, -148,
-148, -148, -148, -148, -148, -148, -148, -148, -148, -148,
-148, -148, -148, -148, -148, -148, -148, -148, -148, -148,
-148, -148, -148, -148, -148, -148, -148, -148, -148, -148,
-148, -148, -148, -148, -148, -148, -148, -148, -148, -148,
-148, 155, -148, -148, -148, -148, -148, -148, -148, -148,
-148, -148, -148, -148, -148, -148, -148, -148, -148, -148,
-148, -148, -148, -148, -148, -148, -148, -148, -148, -148,
-148, -148, -148, 155, -148, -148, -148, -148, -148, -148,
-148, -148, -148, -148, -148, -148, -148, -148, -148, -148,
-148, -148, -148, -148, -148, -148, -148, -148
},
{
5, -149, -149, -149, -149, -149, -149, -149, -149, -149,
-149, -149, -149, -149, -149, -149, -149, -149, -149, -149,
-149, -149, -149, -149, -149, -149, -149, -149, -149, -149,
-149, -149, -149, -149, -149, -149, -149, -149, -149, -149,
-149, -149, -149, -149, -149, -149, -149, -149, -149, -149,
-149, -149, -149, -149, -149, -149, -149, -149, -149, -149,
-149, -149, -149, -149, -149, -149, -149, -149, -149, -149,
-149, -149, -149, -149, -149, -149, -149, 156, -149, -149,
-149, -149, -149, -149, -149, -149, -149, -149, -149, -149,
-149, -149, -149, -149, -149, -149, -149, -149, -149, -149,
-149, -149, -149, -149, -149, -149, -149, -149, -149, 156,
-149, -149, -149, -149, -149, -149, -149, -149, -149, -149,
-149, -149, -149, -149, -149, -149, -149, -149
},
{
5, -150, -150, -150, -150, -150, -150, -150, -150, -150,
-150, -150, -150, -150, -150, -150, -150, -150, -150, -150,
-150, -150, -150, -150, -150, -150, -150, -150, -150, -150,
-150, -150, -150, -150, -150, -150, -150, -150, -150, -150,
-150, -150, -150, -150, -150, -150, -150, -150, -150, -150,
-150, -150, -150, -150, -150, -150, -150, -150, -150, -150,
-150, -150, -150, -150, -150, -150, -150, -150, -150, -150,
-150, -150, -150, -150, -150, -150, -150, -150, -150, -150,
-150, -150, -150, -150, -150, -150, -150, -150, -150, -150,
-150, -150, -150, -150, -150, -150, -150, -150, -150, -150,
-150, -150, -150, -150, -150, -150, -150, -150, -150, -150,
-150, -150, -150, -150, -150, -150, -150, -150, -150, -150,
-150, -150, -150, -150, -150, -150, -150, -150
},
{
5, -151, -151, -151, -151, -151, -151, -151, -151, -151,
-151, -151, -151, -151, -151, -151, -151, -151, -151, -151,
-151, -151, -151, -151, -151, -151, -151, -151, -151, -151,
-151, -151, -151, -151, -151, -151, -151, -151, -151, -151,
-151, -151, -151, -151, -151, -151, -151, -151, -151, -151,
-151, -151, -151, -151, -151, -151, -151, -151, -151, -151,
-151, -151, -151, -151, -151, -151, -151, 157, -151, -151,
-151, -151, -151, -151, -151, -151, -151, -151, -151, -151,
-151, -151, -151, -151, -151, -151, -151, -151, -151, -151,
-151, -151, -151, -151, -151, -151, -151, -151, -151, 157,
-151, -151, -151, -151, -151, -151, -151, -151, -151, -151,
-151, -151, -151, -151, -151, -151, -151, -151, -151, -151,
-151, -151, -151, -151, -151, -151, -151, -151
},
{
5, -152, -152, -152, -152, -152, -152, -152, -152, -152,
-152, -152, -152, -152, -152, -152, -152, -152, -152, -152,
-152, -152, -152, -152, -152, -152, -152, -152, -152, -152,
-152, -152, -152, -152, -152, -152, -152, -152, -152, -152,
-152, -152, -152, -152, -152, -152, -152, -152, -152, -152,
-152, -152, -152, -152, -152, -152, -152, -152, -152, -152,
-152, -152, -152, -152, -152, -152, -152, -152, -152, -152,
-152, -152, -152, -152, -152, -152, -152, -152, 158, -152,
-152, -152, -152, -152, -152, -152, -152, -152, -152, -152,
-152, -152, -152, -152, -152, -152, -152, -152, -152, -152,
-152, -152, -152, -152, -152, -152, -152, -152, -152, -152,
158, -152, -152, -152, -152, -152, -152, -152, -152, -152,
-152, -152, -152, -152, -152, -152, -152, -152
},
{
5, -153, -153, -153, -153, -153, -153, -153, -153, -153,
-153, -153, -153, -153, -153, -153, -153, -153, -153, -153,
-153, -153, -153, -153, -153, -153, -153, -153, -153, -153,
-153, -153, -153, -153, -153, -153, -153, -153, -153, -153,
-153, -153, -153, -153, -153, -153, -153, -153, -153, -153,
-153, -153, -153, -153, -153, -153, -153, -153, -153, -153,
-153, -153, -153, -153, -153, -153, -153, -153, -153, -153,
-153, -153, -153, -153, -153, -153, -153, -153, -153, -153,
-153, -153, -153, -153, -153, -153, -153, -153, -153, -153,
-153, -153, -153, -153, -153, -153, -153, -153, -153, -153,
-153, -153, -153, -153, -153, -153, -153, -153, -153, -153,
-153, -153, -153, -153, -153, -153, -153, -153, -153, -153,
-153, -153, -153, -153, -153, -153, -153, -153
},
{
5, -154, -154, -154, -154, -154, -154, -154, -154, -154,
-154, -154, -154, -154, -154, -154, -154, -154, -154, -154,
-154, -154, -154, -154, -154, -154, -154, -154, -154, -154,
-154, -154, -154, -154, -154, -154, -154, -154, -154, -154,
-154, -154, -154, -154, -154, -154, -154, -154, -154, -154,
-154, -154, -154, -154, -154, -154, -154, -154, -154, -154,
-154, -154, -154, -154, -154, -154, -154, -154, -154, -154,
-154, -154, -154, -154, -154, -154, -154, -154, -154, -154,
-154, -154, -154, -154, -154, -154, -154, -154, -154, -154,
-154, -154, -154, -154, -154, -154, -154, -154, -154, -154,
-154, -154, -154, -154, -154, -154, -154, -154, -154, -154,
-154, -154, -154, -154, -154, -154, -154, -154, -154, -154,
-154, -154, -154, -154, -154, -154, -154, -154
},
{
5, -155, -155, -155, -155, -155, -155, -155, -155, -155,
-155, -155, -155, -155, -155, -155, -155, -155, -155, -155,
-155, -155, -155, -155, -155, -155, -155, -155, -155, -155,
-155, -155, -155, -155, -155, -155, -155, -155, -155, -155,
-155, -155, -155, -155, -155, -155, -155, -155, -155, -155,
-155, -155, -155, -155, -155, -155, -155, -155, -155, -155,
-155, -155, -155, -155, -155, -155, -155, -155, -155, -155,
-155, -155, -155, -155, -155, -155, -155, 159, -155, -155,
-155, -155, -155, -155, -155, -155, -155, -155, -155, -155,
-155, -155, -155, -155, -155, -155, -155, -155, -155, -155,
-155, -155, -155, -155, -155, -155, -155, -155, -155, 159,
-155, -155, -155, -155, -155, -155, -155, -155, -155, -155,
-155, -155, -155, -155, -155, -155, -155, -155
},
{
5, -156, -156, -156, -156, -156, -156, -156, -156, -156,
-156, -156, -156, -156, -156, -156, -156, -156, -156, -156,
-156, -156, -156, -156, -156, -156, -156, -156, -156, -156,
-156, -156, -156, -156, -156, -156, -156, -156, -156, -156,
-156, -156, -156, -156, -156, -156, -156, -156, -156, -156,
-156, -156, -156, -156, -156, -156, -156, -156, -156, -156,
-156, -156, -156, -156, -156, -156, -156, -156, -156, -156,
-156, -156, -156, -156, -156, -156, -156, -156, -156, -156,
-156, -156, -156, -156, -156, -156, -156, -156, -156, -156,
-156, -156, -156, -156, -156, -156, -156, -156, -156, -156,
-156, -156, -156, -156, -156, -156, -156, -156, -156, -156,
-156, -156, -156, -156, -156, -156, -156, -156, -156, -156,
-156, -156, -156, -156, -156, -156, -156, -156
},
{
5, -157, -157, -157, -157, -157, -157, -157, -157, -157,
-157, -157, -157, -157, -157, -157, -157, -157, -157, -157,
-157, -157, -157, -157, -157, -157, -157, -157, -157, -157,
-157, -157, -157, -157, -157, -157, -157, -157, -157, -157,
-157, -157, -157, -157, -157, -157, -157, -157, -157, -157,
-157, -157, -157, -157, -157, -157, -157, -157, -157, -157,
-157, -157, -157, -157, -157, -157, -157, -157, -157, -157,
-157, -157, -157, -157, -157, -157, -157, -157, -157, -157,
-157, -157, -157, -157, 160, -157, -157, -157, -157, -157,
-157, -157, -157, -157, -157, -157, -157, -157, -157, -157,
-157, -157, -157, -157, -157, -157, -157, -157, -157, -157,
-157, -157, -157, -157, -157, -157, 160, -157, -157, -157,
-157, -157, -157, -157, -157, -157, -157, -157
},
{
5, -158, -158, -158, -158, -158, -158, -158, -158, -158,
-158, -158, -158, -158, -158, -158, -158, -158, -158, -158,
-158, -158, -158, -158, -158, -158, -158, -158, -158, -158,
-158, -158, -158, -158, -158, -158, -158, -158, -158, -158,
-158, -158, -158, -158, -158, -158, -158, -158, -158, -158,
-158, -158, -158, -158, -158, -158, -158, -158, -158, -158,
-158, -158, -158, -158, -158, -158, -158, -158, -158, -158,
-158, 161, -158, -158, -158, -158, -158, -158, -158, -158,
-158, -158, -158, -158, -158, -158, -158, -158, -158, -158,
-158, -158, -158, -158, -158, -158, -158, -158, -158, -158,
-158, -158, -158, 161, -158, -158, -158, -158, -158, -158,
-158, -158, -158, -158, -158, -158, -158, -158, -158, -158,
-158, -158, -158, -158, -158, -158, -158, -158
},
{
5, -159, -159, -159, -159, -159, -159, -159, -159, -159,
-159, -159, -159, -159, -159, -159, -159, -159, -159, -159,
-159, -159, -159, -159, -159, -159, -159, -159, -159, -159,
-159, -159, -159, -159, -159, -159, -159, -159, -159, -159,
-159, -159, -159, -159, -159, -159, -159, -159, -159, -159,
-159, -159, -159, -159, -159, -159, -159, -159, -159, -159,
-159, -159, -159, -159, -159, -159, -159, -159, -159, -159,
-159, -159, -159, -159, -159, -159, -159, -159, -159, -159,
-159, -159, -159, -159, -159, -159, -159, -159, -159, -159,
-159, -159, -159, -159, -159, -159, -159, -159, -159, -159,
-159, -159, -159, -159, -159, -159, -159, -159, -159, -159,
-159, -159, -159, -159, -159, -159, -159, -159, -159, -159,
-159, -159, -159, -159, -159, -159, -159, -159
},
{
5, -160, -160, -160, -160, -160, -160, -160, -160, -160,
-160, -160, -160, -160, -160, -160, -160, -160, -160, -160,
-160, -160, -160, -160, -160, -160, -160, -160, -160, -160,
-160, -160, -160, -160, -160, -160, -160, -160, -160, -160,
-160, -160, -160, -160, -160, -160, -160, -160, -160, -160,
-160, -160, -160, -160, -160, -160, -160, -160, -160, -160,
-160, -160, -160, -160, -160, -160, -160, -160, -160, -160,
-160, -160, -160, 162, -160, -160, -160, -160, -160, -160,
-160, -160, -160, -160, -160, -160, -160, -160, -160, -160,
-160, -160, -160, -160, -160, -160, -160, -160, -160, -160,
-160, -160, -160, -160, -160, 162, -160, -160, -160, -160,
-160, -160, -160, -160, -160, -160, -160, -160, -160, -160,
-160, -160, -160, -160, -160, -160, -160, -160
},
{
5, -161, -161, -161, -161, -161, -161, -161, -161, -161,
-161, -161, -161, -161, -161, -161, -161, -161, -161, -161,
-161, -161, -161, -161, -161, -161, -161, -161, -161, -161,
-161, -161, -161, -161, -161, -161, -161, -161, -161, -161,
-161, -161, -161, -161, -161, -161, -161, -161, -161, -161,
-161, -161, -161, -161, -161, -161, -161, -161, -161, -161,
-161, -161, -161, -161, -161, -161, -161, -161, -161, -161,
-161, -161, -161, -161, -161, -161, -161, 163, -161, -161,
-161, -161, -161, -161, -161, -161, -161, -161, -161, -161,
-161, -161, -161, -161, -161, -161, -161, -161, -161, -161,
-161, -161, -161, -161, -161, -161, -161, -161, -161, 163,
-161, -161, -161, -161, -161, -161, -161, -161, -161, -161,
-161, -161, -161, -161, -161, -161, -161, -161
},
{
5, -162, -162, -162, -162, -162, -162, -162, -162, -162,
-162, -162, -162, -162, -162, -162, -162, -162, -162, -162,
-162, -162, -162, -162, -162, -162, -162, -162, -162, -162,
-162, -162, -162, -162, -162, -162, -162, -162, -162, -162,
-162, -162, -162, -162, -162, -162, -162, -162, -162, -162,
-162, -162, -162, -162, -162, -162, -162, -162, -162, -162,
-162, -162, -162, -162, -162, -162, -162, -162, -162, -162,
-162, -162, -162, -162, -162, -162, -162, -162, -162, 164,
-162, -162, -162, -162, -162, -162, -162, -162, -162, -162,
-162, -162, -162, -162, -162, -162, -162, -162, -162, -162,
-162, -162, -162, -162, -162, -162, -162, -162, -162, -162,
-162, 164, -162, -162, -162, -162, -162, -162, -162, -162,
-162, -162, -162, -162, -162, -162, -162, -162
},
{
5, -163, -163, -163, -163, -163, -163, -163, -163, -163,
-163, -163, -163, -163, -163, -163, -163, -163, -163, -163,
-163, -163, -163, -163, -163, -163, -163, -163, -163, -163,
-163, -163, -163, -163, -163, -163, -163, -163, -163, -163,
-163, -163, -163, -163, -163, -163, -163, -163, -163, -163,
-163, -163, -163, -163, -163, -163, -163, -163, -163, -163,
-163, -163, -163, -163, -163, -163, -163, -163, -163, -163,
-163, -163, -163, -163, -163, -163, -163, -163, -163, -163,
-163, -163, -163, -163, -163, -163, -163, -163, -163, -163,
-163, -163, -163, -163, -163, -163, -163, -163, -163, -163,
-163, -163, -163, -163, -163, -163, -163, -163, -163, -163,
-163, -163, -163, -163, -163, -163, -163, -163, -163, -163,
-163, -163, -163, -163, -163, -163, -163, -163
},
{
5, -164, -164, -164, -164, -164, -164, -164, -164, -164,
-164, -164, -164, -164, -164, -164, -164, -164, -164, -164,
-164, -164, -164, -164, -164, -164, -164, -164, -164, -164,
-164, -164, -164, -164, -164, -164, -164, -164, -164, -164,
-164, -164, -164, -164, -164, -164, -164, -164, -164, -164,
-164, -164, -164, -164, -164, -164, -164, -164, -164, -164,
-164, -164, -164, -164, -164, -164, -164, -164, -164, -164,
-164, -164, -164, -164, -164, -164, -164, -164, 165, -164,
-164, -164, -164, -164, -164, -164, -164, -164, -164, -164,
-164, -164, -164, -164, -164, -164, -164, -164, -164, -164,
-164, -164, -164, -164, -164, -164, -164, -164, -164, -164,
165, -164, -164, -164, -164, -164, -164, -164, -164, -164,
-164, -164, -164, -164, -164, -164, -164, -164
},
{
5, -165, -165, -165, -165, -165, -165, -165, -165, -165,
-165, -165, -165, -165, -165, -165, -165, -165, -165, -165,
-165, -165, -165, -165, -165, -165, -165, -165, -165, -165,
-165, -165, -165, -165, -165, -165, -165, -165, -165, -165,
-165, -165, -165, -165, -165, -165, -165, -165, -165, -165,
-165, -165, -165, -165, -165, -165, -165, -165, -165, -165,
-165, -165, -165, -165, -165, -165, -165, -165, -165, -165,
-165, -165, -165, -165, -165, -165, -165, 166, -165, -165,
-165, -165, -165, -165, -165, -165, -165, -165, -165, -165,
-165, -165, -165, -165, -165, -165, -165, -165, -165, -165,
-165, -165, -165, -165, -165, -165, -165, -165, -165, 166,
-165, -165, -165, -165, -165, -165, -165, -165, -165, -165,
-165, -165, -165, -165, -165, -165, -165, -165
},
{
5, -166, -166, -166, -166, -166, -166, -166, -166, -166,
-166, -166, -166, -166, -166, -166, -166, -166, -166, -166,
-166, -166, -166, -166, -166, -166, -166, -166, -166, -166,
-166, -166, -166, -166, -166, -166, -166, -166, -166, -166,
-166, -166, -166, -166, -166, -166, -166, -166, -166, -166,
-166, -166, -166, -166, -166, -166, -166, -166, -166, -166,
-166, -166, -166, -166, -166, -166, -166, -166, -166, -166,
-166, -166, -166, -166, -166, -166, -166, -166, -166, -166,
-166, -166, -166, -166, -166, -166, -166, -166, -166, -166,
-166, -166, -166, -166, -166, -166, -166, -166, -166, -166,
-166, -166, -166, -166, -166, -166, -166, -166, -166, -166,
-166, -166, -166, -166, -166, -166, -166, -166, -166, -166,
-166, -166, -166, -166, -166, -166, -166, -166
},
} ;
static yy_state_type yy_get_previous_state (void );
static yy_state_type yy_try_NUL_trans (yy_state_type current_state );
static int yy_get_next_buffer (void );
static void yy_fatal_error (yyconst char msg[] );
/* Done after the current pattern has been matched and before the
* corresponding action - sets up lwg_parse_yytext.
*/
#define YY_DO_BEFORE_ACTION \
(yytext_ptr) = yy_bp; \
lwg_parse_yyleng = (size_t) (yy_cp - yy_bp); \
(yy_hold_char) = *yy_cp; \
*yy_cp = '\0'; \
(yy_c_buf_p) = yy_cp;
#define YY_NUM_RULES 38
#define YY_END_OF_BUFFER 39
/* This struct is not used in this scanner,
but its presence is necessary. */
struct yy_trans_info
{
flex_int32_t yy_verify;
flex_int32_t yy_nxt;
};
static yyconst flex_int16_t yy_accept[167] =
{ 0,
0, 0, 0, 0, 39, 37, 36, 36, 31, 32,
33, 37, 35, 34, 37, 37, 37, 37, 37, 37,
37, 37, 37, 1, 36, 3, 4, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1, 2, 0,
0, 3, 4, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 29, 0, 2, 0,
0, 0, 30, 0, 0, 0, 5, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 6, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 11, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 12,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 7, 21, 0, 17, 0, 0,
0, 0, 0, 0, 8, 22, 0, 18, 0, 0,
0, 0, 15, 0, 0, 23, 25, 0, 13, 16,
0, 0, 24, 26, 9, 14, 0, 0, 10, 0,
19, 0, 20, 0, 27, 28
} ;
static yy_state_type yy_last_accepting_state;
static char *yy_last_accepting_cpos;
static yyconst yy_state_type yy_NUL_trans[167] =
{ 0,
6, 6, 6, 6, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0
} ;
extern int lwg_parse_yy_flex_debug;
int lwg_parse_yy_flex_debug = 0;
/* The intent behind this definition is that it'll catch
* any uses of REJECT which flex missed.
*/
#define REJECT reject_used_but_not_detected
#define yymore() yymore_used_but_not_detected
#define YY_MORE_ADJ 0
#define YY_RESTORE_YY_MORE_OFFSET
char *lwg_parse_yytext;
#line 1 "wktparse.lex"
/*
* Written by Ralph Mason ralph.masontelogis.com
*
* Copyright Telogis 2004
* www.telogis.com
*
*/
#line 11 "wktparse.lex"
#include "wktparse.tab.h"
#include
#include /* need stdlib for atof() definition */
void init_parser(const char *src);
void close_parser(void);
int lwg_parse_yywrap(void);
int lwg_parse_yylex(void);
static YY_BUFFER_STATE buf_state;
void init_parser(const char *src) { BEGIN(0);buf_state = lwg_parse_yy_scan_string(src); }
void close_parser() { lwg_parse_yy_delete_buffer(buf_state); }
int lwg_parse_yywrap(void){ return 1; }
/* Macro to keep track of the current parse position */
#define UPDATE_YYLLOC() (lwg_parse_yylloc.last_column += lwg_parse_yyleng)
#line 3370 "lex.yy.c"
#define INITIAL 0
#define vals_ok 1
#ifndef YY_NO_UNISTD_H
/* Special case for "unistd.h", since it is non-ANSI. We include it way
* down here because we want the user's section 1 to have been scanned first.
* The user has a chance to override it with an option.
*/
#include
#endif
#ifndef YY_EXTRA_TYPE
#define YY_EXTRA_TYPE void *
#endif
static int yy_init_globals (void );
/* Accessor methods to globals.
These are made visible to non-reentrant scanners for convenience. */
int lwg_parse_yylex_destroy (void );
int lwg_parse_yyget_debug (void );
void lwg_parse_yyset_debug (int debug_flag );
YY_EXTRA_TYPE lwg_parse_yyget_extra (void );
void lwg_parse_yyset_extra (YY_EXTRA_TYPE user_defined );
FILE *lwg_parse_yyget_in (void );
void lwg_parse_yyset_in (FILE * in_str );
FILE *lwg_parse_yyget_out (void );
void lwg_parse_yyset_out (FILE * out_str );
int lwg_parse_yyget_leng (void );
char *lwg_parse_yyget_text (void );
int lwg_parse_yyget_lineno (void );
void lwg_parse_yyset_lineno (int line_number );
/* Macros after this point can all be overridden by user definitions in
* section 1.
*/
#ifndef YY_SKIP_YYWRAP
#ifdef __cplusplus
extern "C" int lwg_parse_yywrap (void );
#else
extern int lwg_parse_yywrap (void );
#endif
#endif
static void yyunput (int c,char *buf_ptr );
#ifndef yytext_ptr
static void yy_flex_strncpy (char *,yyconst char *,int );
#endif
#ifdef YY_NEED_STRLEN
static int yy_flex_strlen (yyconst char * );
#endif
#ifndef YY_NO_INPUT
#ifdef __cplusplus
static int yyinput (void );
#else
static int input (void );
#endif
#endif
/* Amount of stuff to slurp up with each read. */
#ifndef YY_READ_BUF_SIZE
#define YY_READ_BUF_SIZE 8192
#endif
/* Copy whatever the last rule matched to the standard output. */
#ifndef ECHO
/* This used to be an fputs(), but since the string might contain NUL's,
* we now use fwrite().
*/
#define ECHO fwrite( lwg_parse_yytext, lwg_parse_yyleng, 1, lwg_parse_yyout )
#endif
/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL,
* is returned in "result".
*/
#ifndef YY_INPUT
#define YY_INPUT(buf,result,max_size) \
errno=0; \
while ( (result = read( fileno(lwg_parse_yyin), (char *) buf, max_size )) < 0 ) \
{ \
if( errno != EINTR) \
{ \
YY_FATAL_ERROR( "input in flex scanner failed" ); \
break; \
} \
errno=0; \
clearerr(lwg_parse_yyin); \
}\
\
#endif
/* No semi-colon after return; correct usage is to write "yyterminate();" -
* we don't want an extra ';' after the "return" because that will cause
* some compilers to complain about unreachable statements.
*/
#ifndef yyterminate
#define yyterminate() return YY_NULL
#endif
/* Number of entries by which start-condition stack grows. */
#ifndef YY_START_STACK_INCR
#define YY_START_STACK_INCR 25
#endif
/* Report a fatal error. */
#ifndef YY_FATAL_ERROR
#define YY_FATAL_ERROR(msg) yy_fatal_error( msg )
#endif
/* end tables serialization structures and prototypes */
/* Default declaration of generated scanner - a define so the user can
* easily add parameters.
*/
#ifndef YY_DECL
#define YY_DECL_IS_OURS 1
extern int lwg_parse_yylex (void);
#define YY_DECL int lwg_parse_yylex (void)
#endif /* !YY_DECL */
/* Code executed at the beginning of each rule, after lwg_parse_yytext and lwg_parse_yyleng
* have been set up.
*/
#ifndef YY_USER_ACTION
#define YY_USER_ACTION
#endif
/* Code executed at the end of each rule. */
#ifndef YY_BREAK
#define YY_BREAK break;
#endif
#define YY_RULE_SETUP \
YY_USER_ACTION
/** The main scanner function which does all the work.
*/
YY_DECL
{
register yy_state_type yy_current_state;
register char *yy_cp, *yy_bp;
register int yy_act;
#line 30 "wktparse.lex"
#line 3540 "lex.yy.c"
if ( !(yy_init) )
{
(yy_init) = 1;
#ifdef YY_USER_INIT
YY_USER_INIT;
#endif
if ( ! (yy_start) )
(yy_start) = 1; /* first start state */
if ( ! lwg_parse_yyin )
lwg_parse_yyin = stdin;
if ( ! lwg_parse_yyout )
lwg_parse_yyout = stdout;
if ( ! YY_CURRENT_BUFFER ) {
lwg_parse_yyensure_buffer_stack ();
YY_CURRENT_BUFFER_LVALUE =
lwg_parse_yy_create_buffer(lwg_parse_yyin,YY_BUF_SIZE );
}
lwg_parse_yy_load_buffer_state( );
}
while ( 1 ) /* loops until end-of-file is reached */
{
yy_cp = (yy_c_buf_p);
/* Support of lwg_parse_yytext. */
*yy_cp = (yy_hold_char);
/* yy_bp points to the position in yy_ch_buf of the start of
* the current run.
*/
yy_bp = yy_cp;
yy_current_state = (yy_start);
yy_match:
while ( (yy_current_state = yy_nxt[yy_current_state][ YY_SC_TO_UI(*yy_cp) ]) > 0 )
{
if ( yy_accept[yy_current_state] )
{
(yy_last_accepting_state) = yy_current_state;
(yy_last_accepting_cpos) = yy_cp;
}
++yy_cp;
}
yy_current_state = -yy_current_state;
yy_find_action:
yy_act = yy_accept[yy_current_state];
YY_DO_BEFORE_ACTION;
do_action: /* This label is used only to access EOF actions. */
switch ( yy_act )
{ /* beginning of action switch */
case 0: /* must back up */
/* undo the effects of YY_DO_BEFORE_ACTION */
*yy_cp = (yy_hold_char);
yy_cp = (yy_last_accepting_cpos) + 1;
yy_current_state = (yy_last_accepting_state);
goto yy_find_action;
case 1:
YY_RULE_SETUP
#line 32 "wktparse.lex"
{ lwg_parse_yylval.value=atof(lwg_parse_yytext); UPDATE_YYLLOC(); return VALUE; }
YY_BREAK
case 2:
YY_RULE_SETUP
#line 33 "wktparse.lex"
{ lwg_parse_yylval.value=atof(lwg_parse_yytext); UPDATE_YYLLOC(); return VALUE; }
YY_BREAK
case 3:
YY_RULE_SETUP
#line 35 "wktparse.lex"
{ lwg_parse_yylval.wkb=lwg_parse_yytext; return WKB;}
YY_BREAK
case 4:
YY_RULE_SETUP
#line 36 "wktparse.lex"
{ lwg_parse_yylval.wkb=lwg_parse_yytext; return WKB;}
YY_BREAK
case 5:
YY_RULE_SETUP
#line 38 "wktparse.lex"
{ UPDATE_YYLLOC(); return POINT; }
YY_BREAK
case 6:
YY_RULE_SETUP
#line 39 "wktparse.lex"
{ UPDATE_YYLLOC(); return POINTM; }
YY_BREAK
case 7:
YY_RULE_SETUP
#line 40 "wktparse.lex"
{ UPDATE_YYLLOC(); return LINESTRING; }
YY_BREAK
case 8:
YY_RULE_SETUP
#line 41 "wktparse.lex"
{ UPDATE_YYLLOC(); return LINESTRINGM; }
YY_BREAK
case 9:
YY_RULE_SETUP
#line 42 "wktparse.lex"
{ UPDATE_YYLLOC(); return CIRCULARSTRING; }
YY_BREAK
case 10:
YY_RULE_SETUP
#line 43 "wktparse.lex"
{ UPDATE_YYLLOC(); return CIRCULARSTRINGM; }
YY_BREAK
case 11:
YY_RULE_SETUP
#line 44 "wktparse.lex"
{ UPDATE_YYLLOC(); return POLYGON; }
YY_BREAK
case 12:
YY_RULE_SETUP
#line 45 "wktparse.lex"
{ UPDATE_YYLLOC(); return POLYGONM; }
YY_BREAK
case 13:
YY_RULE_SETUP
#line 46 "wktparse.lex"
{ UPDATE_YYLLOC(); return COMPOUNDCURVE; }
YY_BREAK
case 14:
YY_RULE_SETUP
#line 47 "wktparse.lex"
{ UPDATE_YYLLOC(); return COMPOUNDCURVEM; }
YY_BREAK
case 15:
YY_RULE_SETUP
#line 48 "wktparse.lex"
{ UPDATE_YYLLOC(); return CURVEPOLYGON; }
YY_BREAK
case 16:
YY_RULE_SETUP
#line 49 "wktparse.lex"
{ UPDATE_YYLLOC(); return CURVEPOLYGONM; }
YY_BREAK
case 17:
YY_RULE_SETUP
#line 50 "wktparse.lex"
{ UPDATE_YYLLOC(); return MULTIPOINT; }
YY_BREAK
case 18:
YY_RULE_SETUP
#line 51 "wktparse.lex"
{ UPDATE_YYLLOC(); return MULTIPOINTM; }
YY_BREAK
case 19:
YY_RULE_SETUP
#line 52 "wktparse.lex"
{ UPDATE_YYLLOC(); return MULTILINESTRING; }
YY_BREAK
case 20:
YY_RULE_SETUP
#line 53 "wktparse.lex"
{ UPDATE_YYLLOC(); return MULTILINESTRINGM; }
YY_BREAK
case 21:
YY_RULE_SETUP
#line 54 "wktparse.lex"
{ UPDATE_YYLLOC(); return MULTICURVE; }
YY_BREAK
case 22:
YY_RULE_SETUP
#line 55 "wktparse.lex"
{ UPDATE_YYLLOC(); return MULTICURVEM; }
YY_BREAK
case 23:
YY_RULE_SETUP
#line 56 "wktparse.lex"
{ UPDATE_YYLLOC(); return MULTIPOLYGON; }
YY_BREAK
case 24:
YY_RULE_SETUP
#line 57 "wktparse.lex"
{ UPDATE_YYLLOC(); return MULTIPOLYGONM; }
YY_BREAK
case 25:
YY_RULE_SETUP
#line 58 "wktparse.lex"
{ UPDATE_YYLLOC(); return MULTISURFACE; }
YY_BREAK
case 26:
YY_RULE_SETUP
#line 59 "wktparse.lex"
{ UPDATE_YYLLOC(); return MULTISURFACEM; }
YY_BREAK
case 27:
YY_RULE_SETUP
#line 60 "wktparse.lex"
{ UPDATE_YYLLOC(); return GEOMETRYCOLLECTION; }
YY_BREAK
case 28:
YY_RULE_SETUP
#line 61 "wktparse.lex"
{ UPDATE_YYLLOC(); return GEOMETRYCOLLECTIONM; }
YY_BREAK
case 29:
YY_RULE_SETUP
#line 62 "wktparse.lex"
{ BEGIN(vals_ok); UPDATE_YYLLOC(); return SRID; }
YY_BREAK
case 30:
YY_RULE_SETUP
#line 63 "wktparse.lex"
{ UPDATE_YYLLOC(); return EMPTY; }
YY_BREAK
case 31:
YY_RULE_SETUP
#line 65 "wktparse.lex"
{ BEGIN(vals_ok); UPDATE_YYLLOC(); return LPAREN; }
YY_BREAK
case 32:
YY_RULE_SETUP
#line 66 "wktparse.lex"
{ UPDATE_YYLLOC(); return RPAREN; }
YY_BREAK
case 33:
YY_RULE_SETUP
#line 67 "wktparse.lex"
{ UPDATE_YYLLOC(); return COMMA ; }
YY_BREAK
case 34:
YY_RULE_SETUP
#line 68 "wktparse.lex"
{ UPDATE_YYLLOC(); return EQUALS ; }
YY_BREAK
case 35:
YY_RULE_SETUP
#line 69 "wktparse.lex"
{ BEGIN(0); UPDATE_YYLLOC(); return SEMICOLON; }
YY_BREAK
case 36:
/* rule 36 can match eol */
YY_RULE_SETUP
#line 70 "wktparse.lex"
/*eat whitespace*/ { UPDATE_YYLLOC(); }
YY_BREAK
case 37:
YY_RULE_SETUP
#line 71 "wktparse.lex"
{ return lwg_parse_yytext[0]; }
YY_BREAK
case 38:
YY_RULE_SETUP
#line 73 "wktparse.lex"
ECHO;
YY_BREAK
#line 3802 "lex.yy.c"
case YY_STATE_EOF(INITIAL):
case YY_STATE_EOF(vals_ok):
yyterminate();
case YY_END_OF_BUFFER:
{
/* Amount of text matched not including the EOB char. */
int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1;
/* Undo the effects of YY_DO_BEFORE_ACTION. */
*yy_cp = (yy_hold_char);
YY_RESTORE_YY_MORE_OFFSET
if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
{
/* We're scanning a new file or input source. It's
* possible that this happened because the user
* just pointed lwg_parse_yyin at a new source and called
* lwg_parse_yylex(). If so, then we have to assure
* consistency between YY_CURRENT_BUFFER and our
* globals. Here is the right place to do so, because
* this is the first action (other than possibly a
* back-up) that will match for the new input source.
*/
(yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
YY_CURRENT_BUFFER_LVALUE->yy_input_file = lwg_parse_yyin;
YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
}
/* Note that here we test for yy_c_buf_p "<=" to the position
* of the first EOB in the buffer, since yy_c_buf_p will
* already have been incremented past the NUL character
* (since all states make transitions on EOB to the
* end-of-buffer state). Contrast this with the test
* in input().
*/
if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
{ /* This was really a NUL. */
yy_state_type yy_next_state;
(yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text;
yy_current_state = yy_get_previous_state( );
/* Okay, we're now positioned to make the NUL
* transition. We couldn't have
* yy_get_previous_state() go ahead and do it
* for us because it doesn't know how to deal
* with the possibility of jamming (and we don't
* want to build jamming into it because then it
* will run more slowly).
*/
yy_next_state = yy_try_NUL_trans( yy_current_state );
yy_bp = (yytext_ptr) + YY_MORE_ADJ;
if ( yy_next_state )
{
/* Consume the NUL. */
yy_cp = ++(yy_c_buf_p);
yy_current_state = yy_next_state;
goto yy_match;
}
else
{
yy_cp = (yy_c_buf_p);
goto yy_find_action;
}
}
else switch ( yy_get_next_buffer( ) )
{
case EOB_ACT_END_OF_FILE:
{
(yy_did_buffer_switch_on_eof) = 0;
if ( lwg_parse_yywrap( ) )
{
/* Note: because we've taken care in
* yy_get_next_buffer() to have set up
* lwg_parse_yytext, we can now set up
* yy_c_buf_p so that if some total
* hoser (like flex itself) wants to
* call the scanner after we return the
* YY_NULL, it'll still work - another
* YY_NULL will get returned.
*/
(yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ;
yy_act = YY_STATE_EOF(YY_START);
goto do_action;
}
else
{
if ( ! (yy_did_buffer_switch_on_eof) )
YY_NEW_FILE;
}
break;
}
case EOB_ACT_CONTINUE_SCAN:
(yy_c_buf_p) =
(yytext_ptr) + yy_amount_of_matched_text;
yy_current_state = yy_get_previous_state( );
yy_cp = (yy_c_buf_p);
yy_bp = (yytext_ptr) + YY_MORE_ADJ;
goto yy_match;
case EOB_ACT_LAST_MATCH:
(yy_c_buf_p) =
&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)];
yy_current_state = yy_get_previous_state( );
yy_cp = (yy_c_buf_p);
yy_bp = (yytext_ptr) + YY_MORE_ADJ;
goto yy_find_action;
}
break;
}
default:
YY_FATAL_ERROR(
"fatal flex scanner internal error--no action found" );
} /* end of action switch */
} /* end of scanning one token */
} /* end of lwg_parse_yylex */
/* yy_get_next_buffer - try to read in a new buffer
*
* Returns a code representing an action:
* EOB_ACT_LAST_MATCH -
* EOB_ACT_CONTINUE_SCAN - continue scanning from current position
* EOB_ACT_END_OF_FILE - end of file
*/
static int yy_get_next_buffer (void)
{
register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
register char *source = (yytext_ptr);
register int number_to_move, i;
int ret_val;
if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] )
YY_FATAL_ERROR(
"fatal flex scanner internal error--end of buffer missed" );
if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
{ /* Don't try to fill the buffer, so this is an EOF. */
if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 )
{
/* We matched a single character, the EOB, so
* treat this as a final EOF.
*/
return EOB_ACT_END_OF_FILE;
}
else
{
/* We matched some text prior to the EOB, first
* process it.
*/
return EOB_ACT_LAST_MATCH;
}
}
/* Try to read more data. */
/* First move last chars to start of buffer. */
number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1;
for ( i = 0; i < number_to_move; ++i )
*(dest++) = *(source++);
if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
/* don't do the read, it's not guaranteed to return an EOF,
* just force an EOF
*/
YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0;
else
{
int num_to_read =
YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
while ( num_to_read <= 0 )
{ /* Not enough room in the buffer - grow it. */
/* just a shorter name for the current buffer */
YY_BUFFER_STATE b = YY_CURRENT_BUFFER;
int yy_c_buf_p_offset =
(int) ((yy_c_buf_p) - b->yy_ch_buf);
if ( b->yy_is_our_buffer )
{
int new_size = b->yy_buf_size * 2;
if ( new_size <= 0 )
b->yy_buf_size += b->yy_buf_size / 8;
else
b->yy_buf_size *= 2;
b->yy_ch_buf = (char *)
/* Include room in for 2 EOB chars. */
lwg_parse_yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 );
}
else
/* Can't grow it, we don't own it. */
b->yy_ch_buf = 0;
if ( ! b->yy_ch_buf )
YY_FATAL_ERROR(
"fatal error - scanner input buffer overflow" );
(yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset];
num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
number_to_move - 1;
}
if ( num_to_read > YY_READ_BUF_SIZE )
num_to_read = YY_READ_BUF_SIZE;
/* Read in more data. */
YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
(yy_n_chars), (size_t) num_to_read );
YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
}
if ( (yy_n_chars) == 0 )
{
if ( number_to_move == YY_MORE_ADJ )
{
ret_val = EOB_ACT_END_OF_FILE;
lwg_parse_yyrestart(lwg_parse_yyin );
}
else
{
ret_val = EOB_ACT_LAST_MATCH;
YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
YY_BUFFER_EOF_PENDING;
}
}
else
ret_val = EOB_ACT_CONTINUE_SCAN;
if ((yy_size_t) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
/* Extend the array by 50%, plus the number we really need. */
yy_size_t new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1);
YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) lwg_parse_yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size );
if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
}
(yy_n_chars) += number_to_move;
YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR;
YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR;
(yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
return ret_val;
}
/* yy_get_previous_state - get the state just before the EOB char was reached */
static yy_state_type yy_get_previous_state (void)
{
register yy_state_type yy_current_state;
register char *yy_cp;
yy_current_state = (yy_start);
for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp )
{
if ( *yy_cp )
{
yy_current_state = yy_nxt[yy_current_state][YY_SC_TO_UI(*yy_cp)];
}
else
yy_current_state = yy_NUL_trans[yy_current_state];
if ( yy_accept[yy_current_state] )
{
(yy_last_accepting_state) = yy_current_state;
(yy_last_accepting_cpos) = yy_cp;
}
}
return yy_current_state;
}
/* yy_try_NUL_trans - try to make a transition on the NUL character
*
* synopsis
* next_state = yy_try_NUL_trans( current_state );
*/
static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state )
{
register int yy_is_jam;
register char *yy_cp = (yy_c_buf_p);
yy_current_state = yy_NUL_trans[yy_current_state];
yy_is_jam = (yy_current_state == 0);
if ( ! yy_is_jam )
{
if ( yy_accept[yy_current_state] )
{
(yy_last_accepting_state) = yy_current_state;
(yy_last_accepting_cpos) = yy_cp;
}
}
return yy_is_jam ? 0 : yy_current_state;
}
static void yyunput (int c, register char * yy_bp )
{
register char *yy_cp;
yy_cp = (yy_c_buf_p);
/* undo effects of setting up lwg_parse_yytext */
*yy_cp = (yy_hold_char);
if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
{ /* need to shift things up to make room */
/* +2 for EOB chars. */
register int number_to_move = (yy_n_chars) + 2;
register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[
YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2];
register char *source =
&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move];
while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
*--dest = *--source;
yy_cp += (int) (dest - source);
yy_bp += (int) (dest - source);
YY_CURRENT_BUFFER_LVALUE->yy_n_chars =
(yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_buf_size;
if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
YY_FATAL_ERROR( "flex scanner push-back overflow" );
}
*--yy_cp = (char) c;
(yytext_ptr) = yy_bp;
(yy_hold_char) = *yy_cp;
(yy_c_buf_p) = yy_cp;
}
#ifndef YY_NO_INPUT
#ifdef __cplusplus
static int yyinput (void)
#else
static int input (void)
#endif
{
int c;
*(yy_c_buf_p) = (yy_hold_char);
if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR )
{
/* yy_c_buf_p now points to the character we want to return.
* If this occurs *before* the EOB characters, then it's a
* valid NUL; if not, then we've hit the end of the buffer.
*/
if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
/* This was really a NUL. */
*(yy_c_buf_p) = '\0';
else
{ /* need more input */
int offset = (yy_c_buf_p) - (yytext_ptr);
++(yy_c_buf_p);
switch ( yy_get_next_buffer( ) )
{
case EOB_ACT_LAST_MATCH:
/* This happens because yy_g_n_b()
* sees that we've accumulated a
* token and flags that we need to
* try matching the token before
* proceeding. But for input(),
* there's no matching to consider.
* So convert the EOB_ACT_LAST_MATCH
* to EOB_ACT_END_OF_FILE.
*/
/* Reset buffer status. */
lwg_parse_yyrestart(lwg_parse_yyin );
/*FALLTHROUGH*/
case EOB_ACT_END_OF_FILE:
{
if ( lwg_parse_yywrap( ) )
return EOF;
if ( ! (yy_did_buffer_switch_on_eof) )
YY_NEW_FILE;
#ifdef __cplusplus
return yyinput();
#else
return input();
#endif
}
case EOB_ACT_CONTINUE_SCAN:
(yy_c_buf_p) = (yytext_ptr) + offset;
break;
}
}
}
c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */
*(yy_c_buf_p) = '\0'; /* preserve lwg_parse_yytext */
(yy_hold_char) = *++(yy_c_buf_p);
return c;
}
#endif /* ifndef YY_NO_INPUT */
/** Immediately switch to a different input stream.
* @param input_file A readable stream.
*
* @note This function does not reset the start condition to @c INITIAL .
*/
void lwg_parse_yyrestart (FILE * input_file )
{
if ( ! YY_CURRENT_BUFFER ){
lwg_parse_yyensure_buffer_stack ();
YY_CURRENT_BUFFER_LVALUE =
lwg_parse_yy_create_buffer(lwg_parse_yyin,YY_BUF_SIZE );
}
lwg_parse_yy_init_buffer(YY_CURRENT_BUFFER,input_file );
lwg_parse_yy_load_buffer_state( );
}
/** Switch to a different input buffer.
* @param new_buffer The new input buffer.
*
*/
void lwg_parse_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer )
{
/* TODO. We should be able to replace this entire function body
* with
* lwg_parse_yypop_buffer_state();
* lwg_parse_yypush_buffer_state(new_buffer);
*/
lwg_parse_yyensure_buffer_stack ();
if ( YY_CURRENT_BUFFER == new_buffer )
return;
if ( YY_CURRENT_BUFFER )
{
/* Flush out information for old buffer. */
*(yy_c_buf_p) = (yy_hold_char);
YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
}
YY_CURRENT_BUFFER_LVALUE = new_buffer;
lwg_parse_yy_load_buffer_state( );
/* We don't actually know whether we did this switch during
* EOF (lwg_parse_yywrap()) processing, but the only time this flag
* is looked at is after lwg_parse_yywrap() is called, so it's safe
* to go ahead and always set it.
*/
(yy_did_buffer_switch_on_eof) = 1;
}
static void lwg_parse_yy_load_buffer_state (void)
{
(yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
(yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
lwg_parse_yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
(yy_hold_char) = *(yy_c_buf_p);
}
/** Allocate and initialize an input buffer state.
* @param file A readable stream.
* @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
*
* @return the allocated buffer state.
*/
YY_BUFFER_STATE lwg_parse_yy_create_buffer (FILE * file, int size )
{
YY_BUFFER_STATE b;
b = (YY_BUFFER_STATE) lwg_parse_yyalloc(sizeof( struct yy_buffer_state ) );
if ( ! b )
YY_FATAL_ERROR( "out of dynamic memory in lwg_parse_yy_create_buffer()" );
b->yy_buf_size = size;
/* yy_ch_buf has to be 2 characters longer than the size given because
* we need to put in 2 end-of-buffer characters.
*/
b->yy_ch_buf = (char *) lwg_parse_yyalloc(b->yy_buf_size + 2 );
if ( ! b->yy_ch_buf )
YY_FATAL_ERROR( "out of dynamic memory in lwg_parse_yy_create_buffer()" );
b->yy_is_our_buffer = 1;
lwg_parse_yy_init_buffer(b,file );
return b;
}
/** Destroy the buffer.
* @param b a buffer created with lwg_parse_yy_create_buffer()
*
*/
void lwg_parse_yy_delete_buffer (YY_BUFFER_STATE b )
{
if ( ! b )
return;
if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
if ( b->yy_is_our_buffer )
lwg_parse_yyfree((void *) b->yy_ch_buf );
lwg_parse_yyfree((void *) b );
}
#ifndef __cplusplus
extern int isatty (int );
#endif /* __cplusplus */
/* Initializes or reinitializes a buffer.
* This function is sometimes called more than once on the same buffer,
* such as during a lwg_parse_yyrestart() or at EOF.
*/
static void lwg_parse_yy_init_buffer (YY_BUFFER_STATE b, FILE * file )
{
int oerrno = errno;
lwg_parse_yy_flush_buffer(b );
b->yy_input_file = file;
b->yy_fill_buffer = 1;
/* If b is the current buffer, then lwg_parse_yy_init_buffer was _probably_
* called from lwg_parse_yyrestart() or through yy_get_next_buffer.
* In that case, we don't want to reset the lineno or column.
*/
if (b != YY_CURRENT_BUFFER){
b->yy_bs_lineno = 1;
b->yy_bs_column = 0;
}
b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
errno = oerrno;
}
/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
* @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
*
*/
void lwg_parse_yy_flush_buffer (YY_BUFFER_STATE b )
{
if ( ! b )
return;
b->yy_n_chars = 0;
/* We always need two end-of-buffer characters. The first causes
* a transition to the end-of-buffer state. The second causes
* a jam in that state.
*/
b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
b->yy_buf_pos = &b->yy_ch_buf[0];
b->yy_at_bol = 1;
b->yy_buffer_status = YY_BUFFER_NEW;
if ( b == YY_CURRENT_BUFFER )
lwg_parse_yy_load_buffer_state( );
}
/** Pushes the new state onto the stack. The new state becomes
* the current state. This function will allocate the stack
* if necessary.
* @param new_buffer The new state.
*
*/
void lwg_parse_yypush_buffer_state (YY_BUFFER_STATE new_buffer )
{
if (new_buffer == NULL)
return;
lwg_parse_yyensure_buffer_stack();
/* This block is copied from lwg_parse_yy_switch_to_buffer. */
if ( YY_CURRENT_BUFFER )
{
/* Flush out information for old buffer. */
*(yy_c_buf_p) = (yy_hold_char);
YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
}
/* Only push if top exists. Otherwise, replace top. */
if (YY_CURRENT_BUFFER)
(yy_buffer_stack_top)++;
YY_CURRENT_BUFFER_LVALUE = new_buffer;
/* copied from lwg_parse_yy_switch_to_buffer. */
lwg_parse_yy_load_buffer_state( );
(yy_did_buffer_switch_on_eof) = 1;
}
/** Removes and deletes the top of the stack, if present.
* The next element becomes the new top.
*
*/
void lwg_parse_yypop_buffer_state (void)
{
if (!YY_CURRENT_BUFFER)
return;
lwg_parse_yy_delete_buffer(YY_CURRENT_BUFFER );
YY_CURRENT_BUFFER_LVALUE = NULL;
if ((yy_buffer_stack_top) > 0)
--(yy_buffer_stack_top);
if (YY_CURRENT_BUFFER) {
lwg_parse_yy_load_buffer_state( );
(yy_did_buffer_switch_on_eof) = 1;
}
}
/* Allocates the stack if it does not exist.
* Guarantees space for at least one push.
*/
static void lwg_parse_yyensure_buffer_stack (void)
{
int num_to_alloc;
if (!(yy_buffer_stack)) {
/* First allocation is just for 2 elements, since we don't know if this
* scanner will even need a stack. We use 2 instead of 1 to avoid an
* immediate realloc on the next call.
*/
num_to_alloc = 1;
(yy_buffer_stack) = (struct yy_buffer_state**)lwg_parse_yyalloc
(num_to_alloc * sizeof(struct yy_buffer_state*)
);
if ( ! (yy_buffer_stack) )
YY_FATAL_ERROR( "out of dynamic memory in lwg_parse_yyensure_buffer_stack()" );
memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*));
(yy_buffer_stack_max) = num_to_alloc;
(yy_buffer_stack_top) = 0;
return;
}
if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){
/* Increase the buffer to prepare for a possible push. */
int grow_size = 8 /* arbitrary grow size */;
num_to_alloc = (yy_buffer_stack_max) + grow_size;
(yy_buffer_stack) = (struct yy_buffer_state**)lwg_parse_yyrealloc
((yy_buffer_stack),
num_to_alloc * sizeof(struct yy_buffer_state*)
);
if ( ! (yy_buffer_stack) )
YY_FATAL_ERROR( "out of dynamic memory in lwg_parse_yyensure_buffer_stack()" );
/* zero only the new slots.*/
memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*));
(yy_buffer_stack_max) = num_to_alloc;
}
}
/** Setup the input buffer state to scan directly from a user-specified character buffer.
* @param base the character buffer
* @param size the size in bytes of the character buffer
*
* @return the newly allocated buffer state object.
*/
YY_BUFFER_STATE lwg_parse_yy_scan_buffer (char * base, yy_size_t size )
{
YY_BUFFER_STATE b;
if ( size < 2 ||
base[size-2] != YY_END_OF_BUFFER_CHAR ||
base[size-1] != YY_END_OF_BUFFER_CHAR )
/* They forgot to leave room for the EOB's. */
return 0;
b = (YY_BUFFER_STATE) lwg_parse_yyalloc(sizeof( struct yy_buffer_state ) );
if ( ! b )
YY_FATAL_ERROR( "out of dynamic memory in lwg_parse_yy_scan_buffer()" );
b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */
b->yy_buf_pos = b->yy_ch_buf = base;
b->yy_is_our_buffer = 0;
b->yy_input_file = 0;
b->yy_n_chars = b->yy_buf_size;
b->yy_is_interactive = 0;
b->yy_at_bol = 1;
b->yy_fill_buffer = 0;
b->yy_buffer_status = YY_BUFFER_NEW;
lwg_parse_yy_switch_to_buffer(b );
return b;
}
/** Setup the input buffer state to scan a string. The next call to lwg_parse_yylex() will
* scan from a @e copy of @a str.
* @param yystr a NUL-terminated string to scan
*
* @return the newly allocated buffer state object.
* @note If you want to scan bytes that may contain NUL values, then use
* lwg_parse_yy_scan_bytes() instead.
*/
YY_BUFFER_STATE lwg_parse_yy_scan_string (yyconst char * yystr )
{
return lwg_parse_yy_scan_bytes(yystr,strlen(yystr) );
}
/** Setup the input buffer state to scan the given bytes. The next call to lwg_parse_yylex() will
* scan from a @e copy of @a bytes.
* @param bytes the byte buffer to scan
* @param len the number of bytes in the buffer pointed to by @a bytes.
*
* @return the newly allocated buffer state object.
*/
YY_BUFFER_STATE lwg_parse_yy_scan_bytes (yyconst char * yybytes, int _yybytes_len )
{
YY_BUFFER_STATE b;
char *buf;
yy_size_t n;
int i;
/* Get memory for full buffer, including space for trailing EOB's. */
n = _yybytes_len + 2;
buf = (char *) lwg_parse_yyalloc(n );
if ( ! buf )
YY_FATAL_ERROR( "out of dynamic memory in lwg_parse_yy_scan_bytes()" );
for ( i = 0; i < _yybytes_len; ++i )
buf[i] = yybytes[i];
buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
b = lwg_parse_yy_scan_buffer(buf,n );
if ( ! b )
YY_FATAL_ERROR( "bad buffer in lwg_parse_yy_scan_bytes()" );
/* It's okay to grow etc. this buffer, and we should throw it
* away when we're done.
*/
b->yy_is_our_buffer = 1;
return b;
}
#ifndef YY_EXIT_FAILURE
#define YY_EXIT_FAILURE 2
#endif
static void yy_fatal_error (yyconst char* msg )
{
(void) fprintf( stderr, "%s\n", msg );
exit( YY_EXIT_FAILURE );
}
/* Redefine yyless() so it works in section 3 code. */
#undef yyless
#define yyless(n) \
do \
{ \
/* Undo effects of setting up lwg_parse_yytext. */ \
int yyless_macro_arg = (n); \
YY_LESS_LINENO(yyless_macro_arg);\
lwg_parse_yytext[lwg_parse_yyleng] = (yy_hold_char); \
(yy_c_buf_p) = lwg_parse_yytext + yyless_macro_arg; \
(yy_hold_char) = *(yy_c_buf_p); \
*(yy_c_buf_p) = '\0'; \
lwg_parse_yyleng = yyless_macro_arg; \
} \
while ( 0 )
/* Accessor methods (get/set functions) to struct members. */
/** Get the current line number.
*
*/
int lwg_parse_yyget_lineno (void)
{
return lwg_parse_yylineno;
}
/** Get the input stream.
*
*/
FILE *lwg_parse_yyget_in (void)
{
return lwg_parse_yyin;
}
/** Get the output stream.
*
*/
FILE *lwg_parse_yyget_out (void)
{
return lwg_parse_yyout;
}
/** Get the length of the current token.
*
*/
int lwg_parse_yyget_leng (void)
{
return lwg_parse_yyleng;
}
/** Get the current token.
*
*/
char *lwg_parse_yyget_text (void)
{
return lwg_parse_yytext;
}
/** Set the current line number.
* @param line_number
*
*/
void lwg_parse_yyset_lineno (int line_number )
{
lwg_parse_yylineno = line_number;
}
/** Set the input stream. This does not discard the current
* input buffer.
* @param in_str A readable stream.
*
* @see lwg_parse_yy_switch_to_buffer
*/
void lwg_parse_yyset_in (FILE * in_str )
{
lwg_parse_yyin = in_str ;
}
void lwg_parse_yyset_out (FILE * out_str )
{
lwg_parse_yyout = out_str ;
}
int lwg_parse_yyget_debug (void)
{
return lwg_parse_yy_flex_debug;
}
void lwg_parse_yyset_debug (int bdebug )
{
lwg_parse_yy_flex_debug = bdebug ;
}
static int yy_init_globals (void)
{
/* Initialization is the same as for the non-reentrant scanner.
* This function is called from lwg_parse_yylex_destroy(), so don't allocate here.
*/
(yy_buffer_stack) = 0;
(yy_buffer_stack_top) = 0;
(yy_buffer_stack_max) = 0;
(yy_c_buf_p) = (char *) 0;
(yy_init) = 0;
(yy_start) = 0;
/* Defined in main.c */
#ifdef YY_STDINIT
lwg_parse_yyin = stdin;
lwg_parse_yyout = stdout;
#else
lwg_parse_yyin = (FILE *) 0;
lwg_parse_yyout = (FILE *) 0;
#endif
/* For future reference: Set errno on error, since we are called by
* lwg_parse_yylex_init()
*/
return 0;
}
/* lwg_parse_yylex_destroy is for both reentrant and non-reentrant scanners. */
int lwg_parse_yylex_destroy (void)
{
/* Pop the buffer stack, destroying each element. */
while(YY_CURRENT_BUFFER){
lwg_parse_yy_delete_buffer(YY_CURRENT_BUFFER );
YY_CURRENT_BUFFER_LVALUE = NULL;
lwg_parse_yypop_buffer_state();
}
/* Destroy the stack itself. */
lwg_parse_yyfree((yy_buffer_stack) );
(yy_buffer_stack) = NULL;
/* Reset the globals. This is important in a non-reentrant scanner so the next time
* lwg_parse_yylex() is called, initialization will occur. */
yy_init_globals( );
return 0;
}
/*
* Internal utility routines.
*/
#ifndef yytext_ptr
static void yy_flex_strncpy (char* s1, yyconst char * s2, int n )
{
register int i;
for ( i = 0; i < n; ++i )
s1[i] = s2[i];
}
#endif
#ifdef YY_NEED_STRLEN
static int yy_flex_strlen (yyconst char * s )
{
register int n;
for ( n = 0; s[n]; ++n )
;
return n;
}
#endif
void *lwg_parse_yyalloc (yy_size_t size )
{
return (void *) malloc( size );
}
void *lwg_parse_yyrealloc (void * ptr, yy_size_t size )
{
/* The cast to (char *) in the following accommodates both
* implementations that use char* generic pointers, and those
* that use void* generic pointers. It works with the latter
* because both ANSI C and C++ allow castless assignment from
* any pointer type to void*, and deal with argument conversions
* as though doing an assignment.
*/
return (void *) realloc( (char *) ptr, size );
}
void lwg_parse_yyfree (void * ptr )
{
free( (char *) ptr ); /* see lwg_parse_yyrealloc() for (char *) cast */
}
#define YYTABLES_NAME "yytables"
#line 73 "wktparse.lex"
================================================
FILE: src/liblwgeom/liblwgeom.h
================================================
/**********************************************************************
* $Id: liblwgeom.h 3812 2009-03-09 14:36:15Z pramsey $
*
* PostGIS - Spatial Types for PostgreSQL
* http://postgis.refractions.net
* Copyright 2001-2006 Refractions Research Inc.
* Copyright 2007-2008 Mark Cave-Ayland
* Copyright 2008 Paul Ramsey
*
* This is free software; you can redistribute and/or modify it under
* the terms of the GNU General Public Licence. See the COPYING file.
*
**********************************************************************/
#ifndef _LIBLWGEOM_H
#define _LIBLWGEOM_H 1
#include "postgis_config.h"
#include
#include
/**
* @file liblwgeom.h
*
* This library is the generic geometry handling section of PostGIS. The geometry
* objects, constructors, destructors, and a set of spatial processing functions,
* are implemented here.
*
* The library is designed for use in non-PostGIS applications if necessary. The
* units tests at cunit/cu_tester.c and the loader/dumper programs at
* ../loader/shp2pgsql.c are examples of non-PostGIS applications using liblwgeom.
*
* Programs using this library should set up the default memory managers and error
* handlers by implementing an lwgeom_init_allocators() function, which can be as
* a wrapper around the lwgeom_install_default_allocators() function if you want
* no special handling for memory management and error reporting.
*/
#define INTEGRITY_CHECKS 1
/*
* Floating point comparitors.
*/
#define PGIS_EPSILON 1e-12
#define FP_MAX(A, B) ((A > B) ? A : B)
#define FP_MIN(A, B) ((A < B) ? A : B)
#define FP_LT(A, B) ((A + PGIS_EPSILON) < B)
#define FP_LTEQ(A, B) ((A - PGIS_EPSILON) <= B)
#define FP_GT(A, B) ((A - PGIS_EPSILON) > B)
#define FP_GTEQ(A, B) ((A + PGIS_EPSILON) >= B)
#define FP_CONTAINS_TOP(A, X, B) (FP_LT(A, X) && FP_LTEQ(X, B))
#define FP_CONTAINS_BOTTOM(A, X, B) (FP_LTEQ(A, X) && FP_LT(X, B))
#define FP_CONTAINS_INCL(A, X, B) (FP_LTEQ(A, X) && FP_LTEQ(X, B))
#define FP_CONTAINS_EXCL(A, X, B) (FP_LT(A, X) && FP_LT(X, B))
#define FP_CONTAINS(A, X, B) FP_CONTAINS_EXCL(A, X, B)
#define LW_TRUE 1
#define LW_FALSE 0
/*
* this will change to NaN when I figure out how to
* get NaN in a platform-independent way
*/
#define NO_VALUE 0.0
#define NO_Z_VALUE NO_VALUE
#define NO_M_VALUE NO_VALUE
#ifndef C_H
typedef unsigned int uint32;
typedef int int32;
#endif
/**
* Global functions for memory/logging handlers.
*/
typedef void* (*lwallocator)(size_t size);
typedef void* (*lwreallocator)(void *mem, size_t size);
typedef void (*lwfreeor)(void* mem);
typedef void (*lwreporter)(const char* fmt, va_list ap);
extern lwreallocator lwrealloc_var;
extern lwallocator lwalloc_var;
extern lwfreeor lwfree_var;
extern lwreporter lwerror_var;
extern lwreporter lwnotice_var;
/**
* Supply the memory management and error handling functions you want your
* application to use.
* @ingroup system
*/
extern void lwgeom_init_allocators(void);
/**
* Apply the default memory management (malloc() and free()) and error handlers.
* Called inside lwgeom_init_allocators() generally.
* @ingroup system
*/
extern void lwgeom_install_default_allocators(void);
/**
* Write a notice out to the notice handler. Uses standard printf() substitutions.
* Use for messages you always want output. For debugging, use LWDEBUG() or LWDEBUGF().
* @ingroup logging
*/
void lwnotice(const char *fmt, ...);
/**
* Write a notice out to the error handler. Uses standard printf() substitutions.
* Use for errors you always want output. For debugging, use LWDEBUG() or LWDEBUGF().
* @ingroup logging
*/
void lwerror(const char *fmt, ...);
/**
* The default memory/logging handlers installed by lwgeom_install_default_allocators()
*/
void *default_allocator(size_t size);
void *default_reallocator(void *mem, size_t size);
void default_freeor(void *ptr);
void default_errorreporter(const char *fmt, va_list ap);
void default_noticereporter(const char *fmt, va_list ap);
extern int lw_vasprintf (char **result, const char *format, va_list args);
/* Debug macros */
#if POSTGIS_DEBUG_LEVEL > 0
/* Display a notice at the given debug level */
#define LWDEBUG(level, msg) \
do { \
if (POSTGIS_DEBUG_LEVEL >= level) \
lwnotice("[%s:%s:%d] " msg, __FILE__, __func__, __LINE__); \
} while (0);
/* Display a formatted notice at the given debug level (like printf, with variadic arguments) */
#define LWDEBUGF(level, msg, ...) \
do { \
if (POSTGIS_DEBUG_LEVEL >= level) \
lwnotice("[%s:%s:%d] " msg, __FILE__, __func__, __LINE__, __VA_ARGS__); \
} while (0);
#else
/* Empty prototype that can be optimised away by the compiler for non-debug builds */
#define LWDEBUG(level, msg) \
((void) 0)
/* Empty prototype that can be optimised away by the compiler for non-debug builds */
#define LWDEBUGF(level, msg, ...) \
((void) 0)
#endif
/******************************************************************/
typedef unsigned char uchar;
typedef struct
{
float xmin;
float ymin;
float xmax;
float ymax;
} BOX2DFLOAT4;
typedef struct
{
double xmin, ymin, zmin;
double xmax, ymax, zmax;
} BOX3D;
typedef struct chiptag
{
int size; /* unused (for use by postgresql) */
int endian_hint; /* the number 1 in the endian of this datastruct */
BOX3D bvol;
int SRID;
char future[4];
float factor; /* Usually 1.0.
* Integer values are multiplied by this number
* to get the actual height value
* (for sub-meter accuracy height data).
*/
int datatype; /* 1 = float32,
* 5 = 24bit integer,
* 6 = 16bit integer (short)
* 7 = 16bit ???
* 8 = 8bit ???
* 101 = float32 (NDR),
* 105 = 24bit integer (NDR),
* 106 = 16bit int (NDR)
* 107 = 16bit ??? (NDR)
* 108 = 8bit ??? (NDR) (this doesn't make sense)
*/
int height;
int width;
int compression; /* 0 = no compression, 1 = differencer
* 0x80 = new value
* 0x7F = nodata
*/
/*
* this is provided for convenience, it should be set to
* sizeof(chip) bytes into the struct because the serialized form is:
*
* NULL when serialized
*/
void *data; /* data[0] = bottm left,
* data[width] = 1st pixel, 2nd row (uncompressed)
*/
} CHIP;
/*
* standard definition of an ellipsoid (what wkt calls a spheroid)
* f = (a-b)/a
* e_sq = (a*a - b*b)/(a*a)
* b = a - fa
*/
typedef struct
{
double a; /* semimajor axis */
double b; /* semiminor axis */
double f; /* flattening */
double e; /* eccentricity (first) */
double e_sq; /* eccentricity (first), squared */
char name[20]; /* name of ellipse */
} SPHEROID;
/*
* ALL LWGEOM structures will use POINT3D as an abstract point.
* This means a 2d geometry will be stored as (x,y) in its serialized
* form, but all functions will work on (x,y,0). This keeps all the
* analysis functions simple.
* NOTE: for GEOS integration, we'll probably set z=NaN
* so look out - z might be NaN for 2d geometries!
*/
typedef struct { double x,y,z; } POINT3DZ;
typedef struct { double x,y,z; } POINT3D; /* alias for POINT3DZ */
typedef struct { double x,y,m; } POINT3DM;
/*
* type for 2d points. When you convert this to 3d, the
* z component will be either 0 or NaN.
*/
typedef struct
{
double x;
double y;
} POINT2D;
typedef struct
{
double x;
double y;
double z;
double m;
} POINT4D;
/******************************************************************/
/*
* Point array abstracts a lot of the complexity of points and point lists.
* It handles miss-alignment in the serialized form, 2d/3d translation
* (2d points converted to 3d will have z=0 or NaN)
* DONT MIX 2D and 3D POINTS! *EVERYTHING* is either one or the other
*/
typedef struct
{
/* array of POINT 2D, 3D or 4D. probably missaligned. */
uchar *serialized_pointlist;
/* use TYPE_* macros to handle */
uchar dims;
uint32 npoints;
} POINTARRAY;
/*
* Use the following to build pointarrays
* when number of points in output is not
* known in advance
*/
typedef struct {
POINTARRAY *pa;
size_t ptsize;
size_t capacity; /* given in points */
} DYNPTARRAY;
/* Create a new dynamic pointarray */
extern DYNPTARRAY *dynptarray_create(size_t initial_capacity, int dims);
/*
* Add a POINT4D to the dynamic pointarray.
*
* The dynamic pointarray may be of any dimension, only
* accepted dimensions will be copied.
*
* If allow_duplicates is set to 0 (false) a check
* is performed to see if last point in array is equal to the
* provided one. NOTE that the check is 4d based, with missing
* ordinates in the pointarray set to NO_Z_VALUE and NO_M_VALUE
* respectively.
*/
extern int dynptarray_addPoint4d(DYNPTARRAY *dpa, POINT4D *p4d,
int allow_duplicates);
/******************************************************************
*
* LWGEOM (any type)
*
******************************************************************/
typedef struct
{
uchar type;
BOX2DFLOAT4 *bbox;
uint32 SRID; /* -1 == unneeded */
void *data;
} LWGEOM;
/* POINTYPE */
typedef struct
{
uchar type; /* POINTTYPE */
BOX2DFLOAT4 *bbox;
uint32 SRID;
POINTARRAY *point; /* hide 2d/3d (this will be an array of 1 point) */
} LWPOINT; /* "light-weight point" */
/* LINETYPE */
typedef struct
{
uchar type; /* LINETYPE */
BOX2DFLOAT4 *bbox;
uint32 SRID;
POINTARRAY *points; /* array of POINT3D */
} LWLINE; /* "light-weight line" */
/* POLYGONTYPE */
typedef struct
{
uchar type; /* POLYGONTYPE */
BOX2DFLOAT4 *bbox;
uint32 SRID;
int nrings;
POINTARRAY **rings; /* list of rings (list of points) */
} LWPOLY; /* "light-weight polygon" */
/* MULTIPOINTTYPE */
typedef struct
{
uchar type;
BOX2DFLOAT4 *bbox;
uint32 SRID;
int ngeoms;
LWPOINT **geoms;
} LWMPOINT;
/* MULTILINETYPE */
typedef struct
{
uchar type;
BOX2DFLOAT4 *bbox;
uint32 SRID;
int ngeoms;
LWLINE **geoms;
} LWMLINE;
/* MULTIPOLYGONTYPE */
typedef struct
{
uchar type;
BOX2DFLOAT4 *bbox;
uint32 SRID;
int ngeoms;
LWPOLY **geoms;
} LWMPOLY;
/* COLLECTIONTYPE */
typedef struct
{
uchar type;
BOX2DFLOAT4 *bbox;
uint32 SRID;
int ngeoms;
LWGEOM **geoms;
} LWCOLLECTION;
/* CIRCSTRINGTYPE */
typedef struct
{
uchar type; /* CIRCSTRINGTYPE */
BOX2DFLOAT4 *bbox;
uint32 SRID;
POINTARRAY *points; /* array of POINT(3D/3DM) */
} LWCIRCSTRING; /* "light-weight circularstring" */
/* COMPOUNDTYPE */
typedef struct
{
uchar type; /* COMPOUNDTYPE */
BOX2DFLOAT4 *bbox;
uint32 SRID;
int ngeoms;
LWGEOM **geoms;
} LWCOMPOUND; /* "light-weight compound line" */
/* CURVEPOLYTYPE */
typedef struct
{
uchar type; /* CURVEPOLYTYPE */
BOX2DFLOAT4 *bbox;
uint32 SRID;
int nrings;
LWGEOM **rings; /* list of rings (list of points) */
} LWCURVEPOLY; /* "light-weight polygon" */
/* MULTICURVE */
typedef struct
{
uchar type;
BOX2DFLOAT4 *bbox;
uint32 SRID;
int ngeoms;
LWGEOM **geoms;
} LWMCURVE;
/* MULTISURFACETYPE */
typedef struct
{
uchar type;
BOX2DFLOAT4 *bbox;
uint32 SRID;
int ngeoms;
LWGEOM **geoms;
} LWMSURFACE;
/* Casts LWGEOM->LW* (return NULL if cast is illegal) */
extern LWMPOLY *lwgeom_as_lwmpoly(LWGEOM *lwgeom);
extern LWMLINE *lwgeom_as_lwmline(LWGEOM *lwgeom);
extern LWMPOINT *lwgeom_as_lwmpoint(LWGEOM *lwgeom);
extern LWCOLLECTION *lwgeom_as_lwcollection(LWGEOM *lwgeom);
extern LWPOLY *lwgeom_as_lwpoly(LWGEOM *lwgeom);
extern LWLINE *lwgeom_as_lwline(LWGEOM *lwgeom);
extern LWPOINT *lwgeom_as_lwpoint(LWGEOM *lwgeom);
extern LWCIRCSTRING *lwgeom_as_lwcircstring(LWGEOM *lwgeom);
/* Casts LW*->LWGEOM (always cast) */
extern LWGEOM *lwmpoly_as_lwgeom(LWMPOLY *obj);
extern LWGEOM *lwmline_as_lwgeom(LWMLINE *obj);
extern LWGEOM *lwmpoint_as_lwgeom(LWMPOINT *obj);
extern LWGEOM *lwcollection_as_lwgeom(LWCOLLECTION *obj);
extern LWGEOM *lwpoly_as_lwgeom(LWPOLY *obj);
extern LWGEOM *lwline_as_lwgeom(LWLINE *obj);
extern LWGEOM *lwpoint_as_lwgeom(LWPOINT *obj);
/*
* Call this function everytime LWGEOM coordinates
* change so to invalidate bounding box
*/
extern void lwgeom_changed(LWGEOM *lwgeom);
/*
* Call this function to drop BBOX and SRID
* from LWGEOM. If LWGEOM type is *not* flagged
* with the HASBBOX flag and has a bbox, it
* will be released.
*/
extern void lwgeom_drop_bbox(LWGEOM *lwgeom);
/* Compute a bbox if not already computed */
extern void lwgeom_add_bbox(LWGEOM *lwgeom);
extern void lwgeom_dropSRID(LWGEOM *lwgeom);
/* Determine whether a LWGEOM can contain sub-geometries or not */
extern int lwgeom_contains_subgeoms(int type);
/******************************************************************/
/*
* copies a point from the point array into the parameter point
* will set point's z=0 (or NaN) if pa is 2d
* will set point's m=0 (or NaN) if pa is 3d or 2d
* NOTE: point is a real POINT3D *not* a pointer
*/
extern POINT4D getPoint4d(const POINTARRAY *pa, int n);
/*
* copies a point from the point array into the parameter point
* will set point's z=0 (or NaN) if pa is 2d
* will set point's m=0 (or NaN) if pa is 3d or 2d
* NOTE: this will modify the point4d pointed to by 'point'.
*/
extern int getPoint4d_p(const POINTARRAY *pa, int n, POINT4D *point);
/*
* copies a point from the point array into the parameter point
* will set point's z=0 (or NaN) if pa is 2d
* NOTE: point is a real POINT3D *not* a pointer
*/
extern POINT3DZ getPoint3dz(const POINTARRAY *pa, int n);
extern POINT3DM getPoint3dm(const POINTARRAY *pa, int n);
/*
* copies a point from the point array into the parameter point
* will set point's z=0 (or NaN) if pa is 2d
* NOTE: this will modify the point3d pointed to by 'point'.
*/
extern int getPoint3dz_p(const POINTARRAY *pa, int n, POINT3DZ *point);
extern int getPoint3dm_p(const POINTARRAY *pa, int n, POINT3DM *point);
/*
* copies a point from the point array into the parameter point
* z value (if present is not returned)
* NOTE: point is a real POINT3D *not* a pointer
*/
extern POINT2D getPoint2d(const POINTARRAY *pa, int n);
/*
* copies a point from the point array into the parameter point
* z value (if present is not returned)
* NOTE: this will modify the point2d pointed to by 'point'.
*/
extern int getPoint2d_p(const POINTARRAY *pa, int n, POINT2D *point);
/*
* set point N to the given value
* NOTE that the pointarray can be of any
* dimension, the appropriate ordinate values
* will be extracted from it
*
*/
extern void setPoint4d(POINTARRAY *pa, int n, POINT4D *p4d);
/*
* get a pointer to nth point of a POINTARRAY
* You'll need to cast it to appropriate dimensioned point.
* Note that if you cast to a higher dimensional point you'll
* possibly corrupt the POINTARRAY.
*
* WARNING: Don't cast this to a POINT !
* it would not be reliable due to memory alignment constraints
*/
extern uchar *getPoint_internal(const POINTARRAY *pa, int n);
/* --- here is a macro equivalent, for speed... */
/* #define getPoint(x,n) &( (x)->serialized_pointlist[((x)->ndims*8)*(n)] ) */
/*
* constructs a POINTARRAY.
* NOTE: points is *not* copied, so be careful about modification
* (can be aligned/missaligned)
* NOTE: hasz and hasm are descriptive - it describes what type of data
* 'points' points to. No data conversion is done.
*/
extern POINTARRAY *pointArray_construct(uchar *points, char hasz, char hasm,
uint32 npoints);
/*
* Calculate the (BOX3D) bounding box of a set of points.
* Returns an alloced BOX3D or NULL (for empty geom) in the first form.
* Write result in user-provided BOX3D in second form (return 0 if untouched).
* If pa is 2d, then box3d's zmin/zmax will be set to NO_Z_VALUE
*/
extern BOX3D *ptarray_compute_box3d(const POINTARRAY *pa);
extern int ptarray_compute_box3d_p(const POINTARRAY *pa, BOX3D *out);
/*
* size of point represeneted in the POINTARRAY
* 16 for 2d, 24 for 3d, 32 for 4d
*/
extern int pointArray_ptsize(const POINTARRAY *pa);
#define POINTTYPE 1
#define LINETYPE 2
#define POLYGONTYPE 3
#define MULTIPOINTTYPE 4
#define MULTILINETYPE 5
#define MULTIPOLYGONTYPE 6
#define COLLECTIONTYPE 7
#define CIRCSTRINGTYPE 8
#define COMPOUNDTYPE 9
#define CURVEPOLYTYPE 13
#define MULTICURVETYPE 14
#define MULTISURFACETYPE 15
#define WKBZOFFSET 0x80000000
#define WKBMOFFSET 0x40000000
#define WKBSRIDFLAG 0x20000000
#define WKBBBOXFLAG 0x10000000
/* These macros work on PG_LWGEOM.type, LWGEOM.type and all its subclasses */
#define TYPE_SETTYPE(c,t) ((c)=(((c)&0xF0)|(t)))
#define TYPE_SETZM(t,z,m) ((t)=(((t)&0xCF)|((z)<<5)|((m)<<4)))
#define TYPE_SETHASBBOX(t,b) ((t)=(((t)&0x7F)|((b)<<7)))
#define TYPE_SETHASSRID(t,s) ((t)=(((t)&0xBF)|((s)<<6)))
#define TYPE_HASZ(t) ( ((t)&0x20)>>5 )
#define TYPE_HASM(t) ( ((t)&0x10)>>4 )
#define TYPE_HASBBOX(t) ( ((t)&0x80)>>7 )
#define TYPE_HASSRID(t) ( (((t)&0x40))>>6 )
#define TYPE_NDIMS(t) ((((t)&0x20)>>5)+(((t)&0x10)>>4)+2)
#define TYPE_GETTYPE(t) ((t)&0x0F)
/* 0x02==Z 0x01==M */
#define TYPE_GETZM(t) (((t)&0x30)>>4)
extern char lwgeom_hasBBOX(uchar type); /* true iff B bit set */
extern int lwgeom_ndims(uchar type); /* returns 2,3 or 4 */
extern int lwgeom_hasZ(uchar type); /* has Z ? */
extern int lwgeom_hasM(uchar type); /* has M ? */
extern int lwgeom_getType(uchar type); /* returns the tttt value */
extern uchar lwgeom_makeType(char hasZ, char hasM, char hasSRID, int type);
extern uchar lwgeom_makeType_full(char hasZ, char hasM, char hasSRID, int type, char hasBBOX);
extern char lwgeom_hasSRID(uchar type); /* true iff S bit is set */
extern char lwgeom_hasBBOX(uchar type); /* true iff B bit set */
/*
* This is the binary representation of lwgeom compatible
* with postgresql varlena struct
*/
typedef struct {
uint32 size; /* varlena header (do not touch directly!) */
uchar type; /* encodes ndims, type, bbox presence,
srid presence */
uchar data[1];
} PG_LWGEOM;
/*
* Construct a full PG_LWGEOM type (including size header)
* from a serialized form.
* The constructed PG_LWGEOM object will be allocated using palloc
* and the serialized form will be copied.
* If you specify a SRID other then -1 it will be set.
* If you request bbox (wantbbox=1) it will be extracted or computed
* from the serialized form.
*/
extern PG_LWGEOM *PG_LWGEOM_construct(uchar *serialized, int SRID,
int wantbbox);
/*
* Compute bbox of serialized geom
*/
extern int compute_serialized_box2d_p(uchar *serialized_form, BOX2DFLOAT4 *box);
extern BOX3D *compute_serialized_box3d(uchar *serialized_form);
extern int compute_serialized_box3d_p(uchar *serialized_form, BOX3D *box);
/*
* Evaluate with an heuristic if the provided PG_LWGEOM is worth
* caching a bbox
*/
char is_worth_caching_pglwgeom_bbox(const PG_LWGEOM *);
char is_worth_caching_serialized_bbox(const uchar *);
char is_worth_caching_lwgeom_bbox(const LWGEOM *);
/*
* Use this macro to extract the char * required
* by most functions from an PG_LWGEOM struct.
* (which is an PG_LWGEOM w/out int32 size casted to char *)
*/
#define SERIALIZED_FORM(x) ((uchar *)VARDATA((x)))
/*
* This function computes the size in bytes
* of the serialized geometries.
*/
extern size_t lwgeom_size(const uchar *serialized_form);
extern size_t lwgeom_size_subgeom(const uchar *serialized_form, int geom_number);
extern size_t lwgeom_size_line(const uchar *serialized_line);
extern size_t lwgeom_size_circstring(const uchar *serialized_curve);
extern size_t lwgeom_size_point(const uchar *serialized_point);
extern size_t lwgeom_size_poly(const uchar *serialized_line);
/*--------------------------------------------------------
* all the base types (point/line/polygon) will have a
* basic constructor, basic de-serializer, basic serializer,
* bounding box finder and (TODO) serialized form size finder.
*--------------------------------------------------------*/
/*
* given the LWPOINT serialized form (or a pointer into a muli* one)
* construct a proper LWPOINT.
* serialized_form should point to the 8bit type format (with type = 1)
* Returns NULL if serialized form is not a point.
* See serialized form doc
*/
extern LWPOINT *lwpoint_deserialize(uchar *serialized_form);
/*
* Find size this point would get when serialized (no BBOX)
*/
extern size_t lwpoint_serialize_size(LWPOINT *point);
/*
* convert this point into its serialize form
* result's first char will be the 8bit type.
* See serialized form doc
*/
extern uchar *lwpoint_serialize(LWPOINT *point);
/* same as above, writes to buf */
extern void lwpoint_serialize_buf(LWPOINT *point, uchar *buf, size_t *size);
/*
* find bounding box (standard one)
* zmin=zmax=0 if 2d (might change to NaN)
*/
extern BOX3D *lwpoint_compute_box3d(LWPOINT *point);
/*
* convenience functions to hide the POINTARRAY
*/
extern int lwpoint_getPoint2d_p(const LWPOINT *point, POINT2D *out);
extern int lwpoint_getPoint3dz_p(const LWPOINT *point, POINT3DZ *out);
extern int lwpoint_getPoint3dm_p(const LWPOINT *point, POINT3DM *out);
extern int lwpoint_getPoint4d_p(const LWPOINT *point, POINT4D *out);
/******************************************************************
* LWLINE functions
******************************************************************/
/*
* given the LWGEOM serialized form (or a pointer into a muli* one)
* construct a proper LWLINE.
* serialized_form should point to the 8bit type format (with type = 2)
* See SERIALIZED_FORM doc
*/
extern LWLINE *lwline_deserialize(uchar *serialized_form);
/* find the size this line would get when serialized */
extern size_t lwline_serialize_size(LWLINE *line);
/*
* convert this line into its serialize form
* result's first char will be the 8bit type. See serialized form doc
* copies data.
*/
extern uchar *lwline_serialize(LWLINE *line);
/* same as above, writes to buf */
extern void lwline_serialize_buf(LWLINE *line, uchar *buf, size_t *size);
/*
* find bounding box (standard one) zmin=zmax=0 if 2d (might change to NaN)
*/
extern BOX3D *lwline_compute_box3d(LWLINE *line);
/******************************************************************
* LWPOLY functions
******************************************************************/
/*
* given the LWPOLY serialized form (or a pointer into a muli* one)
* construct a proper LWPOLY.
* serialized_form should point to the 8bit type format (with type = 3)
* See SERIALIZED_FORM doc
*/
extern LWPOLY *lwpoly_deserialize(uchar *serialized_form);
/* find the size this polygon would get when serialized */
extern size_t lwpoly_serialize_size(LWPOLY *poly);
/*
* create the serialized form of the polygon
* result's first char will be the 8bit type. See serialized form doc
* points copied
*/
extern uchar *lwpoly_serialize(LWPOLY *poly);
/* same as above, writes to buf */
extern void lwpoly_serialize_buf(LWPOLY *poly, uchar *buf, size_t *size);
/*
* find bounding box (standard one) zmin=zmax=0 if 2d (might change to NaN)
*/
extern BOX3D *lwpoly_compute_box3d(LWPOLY *poly);
/******************************************************************
* LWCIRCSTRING functions
******************************************************************/
/*
* given the LWGEOM serialized form (or a pointer into a muli* one)
* construct a proper LWCIRCSTRING.
* serialized_form should point to the 8bit type format (with type = 2)
* See SERIALIZED_FORM doc
*/
extern LWCIRCSTRING *lwcircstring_deserialize(uchar *serialized_form);
/* find the size this curve would get when serialized */
extern size_t lwcircstring_serialize_size(LWCIRCSTRING *curve);
/*
* convert this circularstring into its serialize form
* result's first char will be the 8bit type. See serialized form doc
* copies data.
*/
extern uchar *lwcircstring_serialize(LWCIRCSTRING *curve);
/* same as above, writes to buf */
extern void lwcircstring_serialize_buf(LWCIRCSTRING *curve, uchar *buf, size_t *size);
/*
* find bounding box (standard one) zmin=zmax=0 if 2d (might change to NaN)
*/
extern BOX3D *lwcircstring_compute_box3d(LWCIRCSTRING *curve);
/******************************************************************
* LWGEOM functions
******************************************************************/
extern size_t lwgeom_serialize_size(LWGEOM *geom);
extern size_t lwcollection_serialize_size(LWCOLLECTION *coll);
extern void lwgeom_serialize_buf(LWGEOM *geom, uchar *buf, size_t *size);
extern uchar *lwgeom_serialize(LWGEOM *geom);
extern void lwcollection_serialize_buf(LWCOLLECTION *mcoll, uchar *buf, size_t *size);
extern int lwcollection_ngeoms(const LWCOLLECTION *col);
/*
* Deserialize an lwgeom serialized form.
* The deserialized (recursive) structure will store
* pointers to the serialized form (POINTARRAYs).
*/
LWGEOM *lwgeom_deserialize(uchar *serializedform);
BOX3D *lwgeom_compute_box3d(const LWGEOM *geom);
/******************************************************************
* LWMULTIx and LWCOLLECTION functions
******************************************************************/
LWMPOINT *lwmpoint_deserialize(uchar *serializedform);
LWMLINE *lwmline_deserialize(uchar *serializedform);
LWMPOLY *lwmpoly_deserialize(uchar *serializedform);
LWCOLLECTION *lwcollection_deserialize(uchar *serializedform);
LWCOMPOUND *lwcompound_deserialize(uchar *serialized_form);
LWCURVEPOLY *lwcurvepoly_deserialize(uchar *serialized_form);
LWMCURVE *lwmcurve_deserialize(uchar *serialized_form);
LWMSURFACE *lwmsurface_deserialize(uchar *serialized_form);
LWGEOM *lwcollection_getsubgeom(LWCOLLECTION *col, int gnum);
BOX3D *lwcollection_compute_box3d(LWCOLLECTION *col);
/******************************************************************
* SERIALIZED FORM functions
******************************************************************/
/******************************************************************
* Multi-geometries
*
* These are all handled equivelently so its easy to write iterator code.
* NOTE NOTE: you can hand in a non-multigeometry to most of these functions
* and get usual behavior (ie. get geometry 0 on a POINT
* will return the point).
* This makes coding even easier since you dont have to necessarily
* differenciate between the multi* and non-multi geometries.
*
* NOTE: these usually work directly off the serialized form, so
* they're a little more difficult to handle (and slower)
* NOTE NOTE: the get functions maybe slow, so we may want to have an
* "analysed" lwgeom that would just have pointer to the start
* of each sub-geometry.
*
******************************************************************/
/* use this version for speed. READ-ONLY! */
typedef struct
{
int SRID;
const uchar *serialized_form; /* orginal structure */
uchar type; /* 8-bit type for the LWGEOM */
int ngeometries; /* number of sub-geometries */
uchar **sub_geoms; /* list of pointers (into serialized_form)
of the sub-geoms */
} LWGEOM_INSPECTED;
extern int lwgeom_size_inspected(const LWGEOM_INSPECTED *inspected, int geom_number);
/*
* note - for a simple type (ie. point), this will have
* sub_geom[0] = serialized_form.
* for multi-geomtries sub_geom[0] will be a few bytes into the
* serialized form.
* This function just computes the length of each sub-object and
* pre-caches this info.
* For a geometry collection of multi* geometries, you can inspect
* the sub-components as well.
*/
extern LWGEOM_INSPECTED *lwgeom_inspect(const uchar *serialized_form);
/*
* 1st geometry has geom_number = 0
* if the actual sub-geometry isnt a POINT, null is returned (see _gettype()).
* if there arent enough geometries, return null.
* this is fine to call on a point (with geom_num=0), multipoint
* or geometrycollection
*/
extern LWPOINT *lwgeom_getpoint(uchar *serialized_form, int geom_number);
extern LWPOINT *lwgeom_getpoint_inspected(LWGEOM_INSPECTED *inspected, int geom_number);
/*
* 1st geometry has geom_number = 0
* if the actual geometry isnt a LINE, null is returned (see _gettype()).
* if there arent enough geometries, return null.
* this is fine to call on a line, multiline or geometrycollection
*/
extern LWLINE *lwgeom_getline(uchar *serialized_form, int geom_number);
extern LWLINE *lwgeom_getline_inspected(LWGEOM_INSPECTED *inspected, int geom_number);
/*
* 1st geometry has geom_number = 0
* if the actual geometry isnt a POLYGON, null is returned (see _gettype()).
* if there arent enough geometries, return null.
* this is fine to call on a polygon, multipolygon or geometrycollection
*/
extern LWPOLY *lwgeom_getpoly(uchar *serialized_form, int geom_number);
extern LWPOLY *lwgeom_getpoly_inspected(LWGEOM_INSPECTED *inspected, int geom_number);
/*
* 1st geometry has geom_number = 0
* if the actual geometry isnt a POLYGON, null is returned (see _gettype()).
* if there arent enough geometries, return null.
* this is fine to call on a polygon, multipolygon or geometrycollection
*/
extern LWCIRCSTRING *lwgeom_getcircstring_inspected(LWGEOM_INSPECTED *inspected, int geom_number);
extern LWGEOM *lwgeom_getgeom_inspected(LWGEOM_INSPECTED *inspected, int geom_number);
/*
* this gets the serialized form of a sub-geometry
* 1st geometry has geom_number = 0
* if this isnt a multi* geometry, and geom_number ==0 then it returns
* itself
* returns null on problems.
* in the future this is how you would access a muli* portion of a
* geometry collection.
* GEOMETRYCOLLECTION(MULTIPOINT(0 0, 1 1), LINESTRING(0 0, 1 1))
* ie. lwgeom_getpoint( lwgeom_getsubgeometry( serialized, 0), 1)
* --> POINT(1 1)
* you can inspect the sub-geometry as well if you wish.
*/
extern uchar *lwgeom_getsubgeometry(const uchar *serialized_form, int geom_number);
extern uchar *lwgeom_getsubgeometry_inspected(LWGEOM_INSPECTED *inspected, int geom_number);
/*
* 1st geometry has geom_number = 0
* use geom_number = -1 to find the actual type of the serialized form.
* ie lwgeom_gettype( <'MULTIPOINT(0 0, 1 1)'>, -1)
* --> multipoint
* ie lwgeom_gettype( <'MULTIPOINT(0 0, 1 1)'>, 0)
* --> point
* gets the 8bit type of the geometry at location geom_number
*/
extern uchar lwgeom_getsubtype(uchar *serialized_form, int geom_number);
extern uchar lwgeom_getsubtype_inspected(LWGEOM_INSPECTED *inspected, int geom_number);
/*
* how many sub-geometries are there?
* for point,line,polygon will return 1.
*/
extern int lwgeom_getnumgeometries(uchar *serialized_form);
extern int lwgeom_getnumgeometries_inspected(LWGEOM_INSPECTED *inspected);
/*
* set finalType to COLLECTIONTYPE or 0 (0 means choose a best type)
* (ie. give it 2 points and ask it to be a multipoint)
* use SRID=-1 for unknown SRID (will have 8bit type's S = 0)
* all subgeometries must have the same SRID
* if you want to construct an inspected, call this then inspect the result...
*/
extern uchar *lwgeom_serialized_construct(int SRID, int finalType, char hasz, char hasm, int nsubgeometries, uchar **serialized_subs);
/* construct the empty geometry (GEOMETRYCOLLECTION(EMPTY)) */
extern uchar *lwgeom_constructempty(int SRID, char hasz, char hasm);
extern void lwgeom_constructempty_buf(int SRID, char hasz, char hasm, uchar *buf, size_t *size);
size_t lwgeom_empty_length(int SRID);
/*
* get the SRID from the LWGEOM
* none present => -1
*/
extern int lwgeom_getsrid(uchar *serialized);
/*------------------------------------------------------
* other stuff
*
* handle the double-to-float conversion. The results of this
* will usually be a slightly bigger box because of the difference
* between float8 and float4 representations.
*/
extern BOX2DFLOAT4 *box3d_to_box2df(BOX3D *box);
extern int box3d_to_box2df_p(BOX3D *box, BOX2DFLOAT4 *res);
extern BOX3D box2df_to_box3d(BOX2DFLOAT4 *box);
extern void box2df_to_box3d_p(BOX2DFLOAT4 *box, BOX3D *box3d);
extern BOX3D *box3d_union(BOX3D *b1, BOX3D *b2);
extern int box3d_union_p(BOX3D *b1, BOX3D *b2, BOX3D *ubox);
/*
* Returns a pointer to the BBOX internal to the serialized form.
* READ-ONLY!
* Or NULL if serialized form does not have a BBOX
* OBSOLETED to avoid memory alignment problems.
*/
/*extern BOX2DFLOAT4 *getbox2d_internal(uchar *serialized_form);*/
/*
* this function writes to 'box' and returns 0 if serialized_form
* does not have a bounding box (empty geom)
*/
extern int getbox2d_p(uchar *serialized_form, BOX2DFLOAT4 *box);
/* Expand given box of 'd' units in all directions */
void expand_box2d(BOX2DFLOAT4 *box, double d);
void expand_box3d(BOX3D *box, double d);
/* Check if to boxes are equal (considering FLOAT approximations) */
char box2d_same(BOX2DFLOAT4 *box1, BOX2DFLOAT4 *box2);
/****************************************************************
* MEMORY MANAGEMENT
****************************************************************/
/*
* The lwfree_* family of functions frees *all* memory associated
* with the pointer, including the serialized__pointlist in the
* point arrays. Do not use these on LWGEOMs de-serialized from
* PG_LWGEOMs or they will try to free an underlying structure
* managed by PgSQL. Only use these on LWGEOMs you have
* constructed yourself.
*/
extern void ptarray_free(POINTARRAY *pa);
extern void lwpoint_free(LWPOINT *pt);
extern void lwline_free(LWLINE *line);
extern void lwpoly_free(LWPOLY *poly);
extern void lwmpoint_free(LWMPOINT *mpt);
extern void lwmline_free(LWMLINE *mline);
extern void lwmpoly_free(LWMPOLY *mpoly);
extern void lwcollection_free(LWCOLLECTION *col);
extern void lwcircstring_free(LWCIRCSTRING *curve);
extern void lwgeom_free(LWGEOM *geom);
extern void lwinspected_release(LWGEOM_INSPECTED *inspected); /* TODO: make this deep free... */
/*
* The *_release family of functions frees the LWGEOM structures
* surrounding the POINTARRAYs but leaves the POINTARRAYs
* intact. Use these on LWGEOMs that have been de-serialized
* from PG_LWGEOMs. Do not use these on LWGEOMs you have
* constructed yourself, or you will leak lots of memory.
*/
extern void lwpoint_release(LWPOINT *lwpoint);
extern void lwline_release(LWLINE *lwline);
extern void lwpoly_release(LWPOLY *lwpoly);
extern void lwmpoint_release(LWMPOINT *lwpoint);
extern void lwmline_release(LWMLINE *lwline);
extern void lwmpoly_release(LWMPOLY *lwpoly);
extern void lwcollection_release(LWCOLLECTION *lwcollection);
extern void lwgeom_release(LWGEOM *lwgeom);
/****************************************************************
* utility
****************************************************************/
extern uint32 lw_get_uint32(const uchar *loc);
extern int32 lw_get_int32(const uchar *loc);
extern void printBOX3D(BOX3D *b);
extern void printPA(POINTARRAY *pa);
extern void printLWPOINT(LWPOINT *point);
extern void printLWLINE(LWLINE *line);
extern void printLWPOLY(LWPOLY *poly);
extern void printBYTES(uchar *a, int n);
extern void printMULTI(uchar *serialized);
extern void printType(uchar str);
extern float LWGEOM_Minf(float a, float b);
extern float LWGEOM_Maxf(float a, float b);
extern double LWGEOM_Mind(double a, double b);
extern double LWGEOM_Maxd(double a, double b);
extern float nextDown_f(double d);
extern float nextUp_f(double d);
extern double nextDown_d(float d);
extern double nextUp_d(float d);
extern float nextafterf_custom(float x, float y);
#define LW_MAX(a,b) ((a) > (b) ? (a) : (b))
#define LW_MIN(a,b) ((a) <= (b) ? (a) : (b))
#define LW_ABS(a) ((a) < (0) ? (-a) : (a))
/* general utilities */
extern double lwgeom_polygon_area(LWPOLY *poly);
extern double lwgeom_polygon_perimeter(LWPOLY *poly);
extern double lwgeom_polygon_perimeter2d(LWPOLY *poly);
extern double lwgeom_pointarray_length2d(POINTARRAY *pts);
extern double lwgeom_pointarray_length(POINTARRAY *pts);
extern void lwgeom_force2d_recursive(uchar *serialized, uchar *optr, size_t *retsize);
extern void lwgeom_force3dz_recursive(uchar *serialized, uchar *optr, size_t *retsize);
extern void lwgeom_force3dm_recursive(uchar *serialized, uchar *optr, size_t *retsize);
extern void lwgeom_force4d_recursive(uchar *serialized, uchar *optr, size_t *retsize);
extern double distance2d_pt_pt(POINT2D *p1, POINT2D *p2);
extern double distance2d_pt_seg(POINT2D *p, POINT2D *A, POINT2D *B);
extern double distance2d_seg_seg(POINT2D *A, POINT2D *B, POINT2D *C, POINT2D *D);
extern double distance2d_pt_ptarray(POINT2D *p, POINTARRAY *pa);
extern double distance2d_ptarray_ptarray(POINTARRAY *l1, POINTARRAY *l2);
extern int pt_in_ring_2d(POINT2D *p, POINTARRAY *ring);
extern int pt_in_poly_2d(POINT2D *p, LWPOLY *poly);
extern double distance2d_ptarray_poly(POINTARRAY *pa, LWPOLY *poly);
extern double distance2d_point_point(LWPOINT *point1, LWPOINT *point2);
extern double distance2d_point_line(LWPOINT *point, LWLINE *line);
extern double distance2d_line_line(LWLINE *line1, LWLINE *line2);
extern double distance2d_point_poly(LWPOINT *point, LWPOLY *poly);
extern double distance2d_poly_poly(LWPOLY *poly1, LWPOLY *poly2);
extern double distance2d_line_poly(LWLINE *line, LWPOLY *poly);
extern int azimuth_pt_pt(POINT2D *p1, POINT2D *p2, double *ret);
extern double lwgeom_mindistance2d_recursive(uchar *lw1, uchar *lw2);
extern double lwgeom_mindistance2d_recursive_tolerance(uchar *lw1, uchar *lw2, double tolerance);
extern int lwgeom_pt_inside_circle(POINT2D *p, double cx, double cy, double rad);
extern int32 lwgeom_npoints(uchar *serialized);
extern char ptarray_isccw(const POINTARRAY *pa);
extern void lwgeom_reverse(LWGEOM *lwgeom);
extern void lwline_reverse(LWLINE *line);
extern void lwpoly_reverse(LWPOLY *poly);
extern void lwpoly_forceRHR(LWPOLY *poly);
extern void lwgeom_forceRHR(LWGEOM *lwgeom);
extern char *lwgeom_summary(LWGEOM *lwgeom, int offset);
extern const char *lwgeom_typename(int type);
extern int ptarray_compute_box2d_p(const POINTARRAY *pa, BOX2DFLOAT4 *result);
extern BOX2DFLOAT4 *ptarray_compute_box2d(const POINTARRAY *pa);
extern int lwpoint_compute_box2d_p(LWPOINT *point, BOX2DFLOAT4 *box);
extern int lwline_compute_box2d_p(LWLINE *line, BOX2DFLOAT4 *box);
extern int lwpoly_compute_box2d_p(LWPOLY *poly, BOX2DFLOAT4 *box);
extern int lwcollection_compute_box2d_p(LWCOLLECTION *col, BOX2DFLOAT4 *box);
extern int lwcircstring_compute_box2d_p(LWCIRCSTRING *curve, BOX2DFLOAT4 *box);
extern BOX2DFLOAT4 *lwgeom_compute_box2d(LWGEOM *lwgeom);
extern void interpolate_point4d(POINT4D *A, POINT4D *B, POINT4D *I, double F);
/* return alloced memory */
extern BOX2DFLOAT4 *box2d_union(BOX2DFLOAT4 *b1, BOX2DFLOAT4 *b2);
/* args may overlap ! */
extern int box2d_union_p(BOX2DFLOAT4 *b1, BOX2DFLOAT4 *b2, BOX2DFLOAT4 *ubox);
extern int lwgeom_compute_box2d_p(LWGEOM *lwgeom, BOX2DFLOAT4 *box);
void lwgeom_longitude_shift(LWGEOM *lwgeom);
/* Is lwgeom1 geometrically equal to lwgeom2 ? */
char lwgeom_same(const LWGEOM *lwgeom1, const LWGEOM *lwgeom2);
char ptarray_same(const POINTARRAY *pa1, const POINTARRAY *pa2);
char lwpoint_same(const LWPOINT *p1, const LWPOINT *p2);
char lwline_same(const LWLINE *p1, const LWLINE *p2);
char lwpoly_same(const LWPOLY *p1, const LWPOLY *p2);
char lwcollection_same(const LWCOLLECTION *p1, const LWCOLLECTION *p2);
/*
* Add 'what' to 'to' at position 'where'.
* where=0 == prepend
* where=-1 == append
* Mix of dimensions is not allowed (TODO: allow it?).
* Returns a newly allocated LWGEOM (with NO BBOX)
*/
extern LWGEOM *lwgeom_add(const LWGEOM *to, uint32 where, const LWGEOM *what);
LWGEOM *lwpoint_add(const LWPOINT *to, uint32 where, const LWGEOM *what);
LWGEOM *lwline_add(const LWLINE *to, uint32 where, const LWGEOM *what);
LWGEOM *lwpoly_add(const LWPOLY *to, uint32 where, const LWGEOM *what);
LWGEOM *lwmpoly_add(const LWMPOLY *to, uint32 where, const LWGEOM *what);
LWGEOM *lwmline_add(const LWMLINE *to, uint32 where, const LWGEOM *what);
LWGEOM *lwmpoint_add(const LWMPOINT *to, uint32 where, const LWGEOM *what);
LWGEOM *lwcollection_add(const LWCOLLECTION *to, uint32 where, const LWGEOM *what);
LWGEOM *lwcompound_add(const LWCOMPOUND *to, uint32 where, const LWGEOM *what);
LWGEOM *lwcurvepoly_add(const LWCURVEPOLY *to, uint32 where, const LWGEOM *what);
LWGEOM *lwmcurve_add(const LWMCURVE *to, uint32 where, const LWGEOM *what);
LWGEOM *lwmsurface_add(const LWMSURFACE *to, uint32 where, const LWGEOM *what);
LWGEOM *lwcircstring_add(const LWCIRCSTRING *to, uint32 where, const LWGEOM *what);
/*
* Clone an LWGEOM
* pointarray are not copied.
* BBOXes are copied
*/
extern LWGEOM *lwgeom_clone(const LWGEOM *lwgeom);
extern LWPOINT *lwpoint_clone(const LWPOINT *lwgeom);
extern LWLINE *lwline_clone(const LWLINE *lwgeom);
extern LWPOLY *lwpoly_clone(const LWPOLY *lwgeom);
extern LWCOLLECTION *lwcollection_clone(const LWCOLLECTION *lwgeom);
extern LWCIRCSTRING *lwcircstring_clone(const LWCIRCSTRING *curve);
extern BOX2DFLOAT4 *box2d_clone(const BOX2DFLOAT4 *lwgeom);
extern POINTARRAY *ptarray_clone(const POINTARRAY *ptarray);
/*
* Geometry constructors
* Take ownership of arguments
*/
extern LWPOINT *lwpoint_construct(int SRID, BOX2DFLOAT4 *bbox,
POINTARRAY *point);
extern LWLINE *lwline_construct(int SRID, BOX2DFLOAT4 *bbox,
POINTARRAY *points);
/*
* Construct a new LWPOLY. arrays (points/points per ring) will NOT be copied
* use SRID=-1 for unknown SRID (will have 8bit type's S = 0)
*/
extern LWPOLY *lwpoly_construct(int SRID, BOX2DFLOAT4 *bbox,
unsigned int nrings, POINTARRAY **points);
extern LWCOLLECTION *lwcollection_construct(unsigned int type, int SRID,
BOX2DFLOAT4 *bbox, unsigned int ngeoms, LWGEOM **geoms);
extern LWCOLLECTION *lwcollection_construct_empty(int SRID,
char hasZ, char hasM);
/*
* Construct a new LWCIRCSTRING. arrays (points/points per ring) will NOT be copied
* use SRID=-1 for unknown SRID (will have 8bit type's S = 0)
*/
extern LWCIRCSTRING *lwcircstring_construct(int SRID, BOX2DFLOAT4 *bbox, POINTARRAY *points);
/* Other constructors */
extern LWPOINT *make_lwpoint2d(int SRID, double x, double y);
extern LWPOINT *make_lwpoint3dz(int SRID, double x, double y, double z);
extern LWPOINT *make_lwpoint3dm(int SRID, double x, double y, double m);
extern LWPOINT *make_lwpoint4d(int SRID, double x, double y, double z, double m);
extern LWLINE *lwline_from_lwpointarray(int SRID, unsigned int npoints, LWPOINT **points);
extern LWLINE *lwline_from_lwmpoint(int SRID, LWMPOINT *mpoint);
extern LWLINE *lwline_addpoint(LWLINE *line, LWPOINT *point, unsigned int where);
extern LWLINE *lwline_removepoint(LWLINE *line, unsigned int which);
extern void lwline_setPoint4d(LWLINE *line, unsigned int which, POINT4D *newpoint);
extern LWPOLY *lwpoly_from_lwlines(const LWLINE *shell, unsigned int nholes, const LWLINE **holes);
/* Return a char string with ASCII versionf of type flags */
extern const char *lwgeom_typeflags(uchar type);
/* Construct an empty pointarray */
extern POINTARRAY *ptarray_construct(char hasz, char hasm, unsigned int npoints);
/*
* extern POINTARRAY *ptarray_construct2d(uint32 npoints, const POINT2D *pts);
* extern POINTARRAY *ptarray_construct3dz(uint32 npoints, const POINT3DZ *pts);
* extern POINTARRAY *ptarray_construct3dm(uint32 npoints, const POINT3DM *pts);
* extern POINTARRAY *ptarray_construct4d(uint32 npoints, const POINT4D *pts);
*/
extern POINTARRAY *ptarray_addPoint(POINTARRAY *pa, uchar *p, size_t pdims,
unsigned int where);
extern POINTARRAY *ptarray_removePoint(POINTARRAY *pa, unsigned int where);
extern int ptarray_isclosed2d(const POINTARRAY *pa);
extern void ptarray_longitude_shift(POINTARRAY *pa);
extern int32 lwgeom_nrings_recursive(uchar *serialized);
extern void ptarray_reverse(POINTARRAY *pa);
extern POINTARRAY *ptarray_substring(POINTARRAY *, double, double);
extern double ptarray_locate_point(POINTARRAY *, POINT2D *);
extern void closest_point_on_segment(POINT2D *p, POINT2D *A, POINT2D *B, POINT2D *ret);
/*
* Ensure every segment is at most 'dist' long.
* Returned LWGEOM might is unchanged if a POINT.
*/
extern LWGEOM *lwgeom_segmentize2d(LWGEOM *line, double dist);
extern POINTARRAY *ptarray_segmentize2d(POINTARRAY *ipa, double dist);
extern LWLINE *lwline_segmentize2d(LWLINE *line, double dist);
extern LWPOLY *lwpoly_segmentize2d(LWPOLY *line, double dist);
extern LWCOLLECTION *lwcollection_segmentize2d(LWCOLLECTION *coll, double dist);
extern uchar parse_hex(char *str);
extern void deparse_hex(uchar str, char *result);
/* Parser check flags */
#define PARSER_CHECK_MINPOINTS 1
#define PARSER_CHECK_ODD 2
#define PARSER_CHECK_CLOSURE 4
#define PARSER_CHECK_NONE 0
#define PARSER_CHECK_ALL (PARSER_CHECK_MINPOINTS | PARSER_CHECK_ODD | PARSER_CHECK_CLOSURE)
/*
* Parser result structure: returns the result of attempting to convert (E)WKT/(E)WKB to LWGEOM
*/
typedef struct struct_lwgeom_parser_result
{
const char *wkinput; /* Copy of pointer to input WKT/WKB */
uchar *serialized_lwgeom; /* Pointer to serialized LWGEOM */
int size; /* Size of serialized LWGEOM in bytes */
const char *message; /* Error/warning message */
int errlocation; /* Location of error */
} LWGEOM_PARSER_RESULT;
/*
* Parser error messages (these must match the message array in lwgparse.c)
*/
#define PARSER_ERROR_MOREPOINTS 1
#define PARSER_ERROR_ODDPOINTS 2
#define PARSER_ERROR_UNCLOSED 3
#define PARSER_ERROR_MIXDIMS 4
#define PARSER_ERROR_INVALIDGEOM 5
#define PARSER_ERROR_INVALIDWKBTYPE 6
/*
* Unparser result structure: returns the result of attempting to convert LWGEOM to (E)WKT/(E)WKB
*/
typedef struct struct_lwgeom_unparser_result
{
uchar *serialized_lwgeom; /* Copy of pointer to input serialized LWGEOM */
char *wkoutput; /* Pointer to WKT or WKB output */
int size; /* Size of serialized LWGEOM in bytes */
const char *message; /* Error/warning message */
int errlocation; /* Location of error */
} LWGEOM_UNPARSER_RESULT;
/*
* Unparser error messages (these must match the message array in lwgunparse.c)
*/
#define UNPARSER_ERROR_MOREPOINTS 1
#define UNPARSER_ERROR_ODDPOINTS 2
#define UNPARSER_ERROR_UNCLOSED 3
/* Parser access routines */
extern char *lwgeom_to_ewkt(LWGEOM *lwgeom, int flags);
extern char *lwgeom_to_hexwkb(LWGEOM *lwgeom, int flags, unsigned int byteorder);
extern LWGEOM *lwgeom_from_ewkb(uchar *ewkb, int flags, size_t ewkblen);
extern LWGEOM *lwgeom_from_ewkt(char *ewkt, int flags);
extern uchar *lwgeom_to_ewkb(LWGEOM *lwgeom, int flags, char byteorder, size_t *ewkblen);
extern int serialized_lwgeom_to_ewkt(LWGEOM_UNPARSER_RESULT *lwg_unparser_result, uchar *serialized, int flags);
extern int serialized_lwgeom_from_ewkt(LWGEOM_PARSER_RESULT *lwg_parser_result, char *wkt_input, int flags);
extern int serialized_lwgeom_to_hexwkb(LWGEOM_UNPARSER_RESULT *lwg_unparser_result, uchar *serialized, int flags, unsigned int byteorder);
extern int serialized_lwgeom_from_hexwkb(LWGEOM_PARSER_RESULT *lwg_parser_result, char *hexwkb_input, int flags);
extern int serialized_lwgeom_to_ewkb(LWGEOM_UNPARSER_RESULT *lwg_unparser_result, uchar *serialized, int flags, unsigned int byteorder);
extern void *lwalloc(size_t size);
extern void *lwrealloc(void *mem, size_t size);
extern void lwfree(void *mem);
/* Utilities */
extern void trim_trailing_zeros(char *num);
extern char *lwmessage_truncate(char *str, int startpos, int endpos, int maxlength, int truncdirection);
/* Machine endianness */
#define XDR 0
#define NDR 1
extern char getMachineEndian(void);
void errorIfSRIDMismatch(int srid1, int srid2);
/*******************************************************************************
* SQLMM internal functions - TODO: Move into separate header files
******************************************************************************/
uint32 has_arc(LWGEOM *geom);
double lwcircle_center(POINT4D *p1, POINT4D *p2, POINT4D *p3, POINT4D **result);
LWGEOM *lwgeom_segmentize(LWGEOM *geom, uint32 perQuad);
LWGEOM *lwgeom_desegmentize(LWGEOM *geom);
extern double lwgeom_curvepolygon_area(LWCURVEPOLY *curvepoly);
double lwcircle_center(POINT4D *p1, POINT4D *p2, POINT4D *p3, POINT4D **result);
#endif /* !defined _LIBLWGEOM_H */
================================================
FILE: src/liblwgeom/lwalgorithm.c
================================================
/**********************************************************************
* $Id: lwalgorithm.c 3812 2009-03-09 14:36:15Z pramsey $
*
* PostGIS - Spatial Types for PostgreSQL
* http://postgis.refractions.net
* Copyright 2008 Paul Ramsey
*
* This is free software; you can redistribute and/or modify it under
* the terms of the GNU General Public Licence. See the COPYING file.
*
**********************************************************************/
#include "lwalgorithm.h"
/*
** lw_segment_side()
**
** Return < 0.0 if point Q is left of segment P
** Return > 0.0 if point Q is right of segment P
** Return = 0.0 if point Q in on segment P
*/
double lw_segment_side(POINT2D *p1, POINT2D *p2, POINT2D *q)
{
return ( (q->x - p1->x) * (p2->y - p1->y) - (p2->x - p1->x) * (q->y - p1->y) );
}
int lw_segment_envelope_intersects(POINT2D p1, POINT2D p2, POINT2D q1, POINT2D q2)
{
double minq=LW_MIN(q1.x,q2.x);
double maxq=LW_MAX(q1.x,q2.x);
double minp=LW_MIN(p1.x,p2.x);
double maxp=LW_MAX(p1.x,p2.x);
if (minp>maxq || maxpmaxq || maxp we are done. */
if (!lw_segment_envelope_intersects(*p1, *p2, *q1, *p2))
{
return SEG_NO_INTERSECTION;
}
/* Are the start and end points of q on the same side of p? */
pq1=lw_segment_side(p1,p2,q1);
pq2=lw_segment_side(p1,p2,q2);
if ((pq1>0 && pq2>0) || (pq1<0 && pq2<0))
{
return SEG_NO_INTERSECTION;
}
/* Are the start and end points of p on the same side of q? */
qp1=lw_segment_side(q1,q2,p1);
qp2=lw_segment_side(q1,q2,p2);
if ((qp1>0 && qp2>0) || (qp1<0 && qp2<0))
{
return SEG_NO_INTERSECTION;
}
/* Nobody is on one side or another? Must be colinear. */
if (pq1 == 0.0 && pq2 == 0.0 && qp1 == 0.0 && qp2 == 0.0)
{
return SEG_COLINEAR;
}
/*
** When one end-point touches, the sidedness is determined by the
** location of the other end-point.
*/
if ( pq2 == 0.0 )
{
if ( pq1 < 0.0 )
return SEG_TOUCH_LEFT;
else
return SEG_TOUCH_RIGHT;
}
if ( pq1 == 0.0 )
{
if ( pq2 < 0.0 )
return SEG_TOUCH_LEFT;
else
return SEG_TOUCH_RIGHT;
}
/* The segments cross, what direction is the crossing? */
if ( pq1 < pq2 )
return SEG_CROSS_RIGHT;
else
return SEG_CROSS_LEFT;
/* This should never happen! */
return SEG_ERROR;
}
/*
** lwline_crossing_direction()
**
** Returns one of
** LINE_NO_CROSS = 0
** LINE_CROSS_LEFT = -1
** LINE_CROSS_RIGHT = 1
** LINE_MULTICROSS_END_LEFT = -2
** LINE_MULTICROSS_END_RIGHT = 2
** LINE_MULTICROSS_END_SAME_FIRST_LEFT = -3
** LINE_MULTICROSS_END_SAME_FIRST_RIGHT = 3
**
*/
int lwline_crossing_direction(LWLINE *l1, LWLINE *l2)
{
int i = 0, j = 0, rv = 0;
POINT2D *p1;
POINT2D *p2;
POINT2D *q1;
POINT2D *q2;
POINTARRAY *pa1;
POINTARRAY *pa2;
int cross_left = 0;
int cross_right = 0;
int first_cross = 0;
int final_cross = 0;
int this_cross = 0;
int vertex_touch = -1;
int vertex_touch_type = 0;
pa1 = (POINTARRAY*)l1->points;
pa2 = (POINTARRAY*)l2->points;
p1 = lwalloc(sizeof(POINT2D));
p2 = lwalloc(sizeof(POINT2D));
q1 = lwalloc(sizeof(POINT2D));
q2 = lwalloc(sizeof(POINT2D));
/* One-point lines can't intersect (and shouldn't exist). */
if ( pa1->npoints < 2 || pa2->npoints < 2 )
return LINE_NO_CROSS;
LWDEBUGF(4, "lineCrossingDirection: l1 = %s", lwgeom_to_ewkt((LWGEOM*)l1,0));
LWDEBUGF(4, "lineCrossingDirection: l2 = %s", lwgeom_to_ewkt((LWGEOM*)l2,0));
for ( i = 1; i < pa2->npoints; i++ )
{
rv = getPoint2d_p(pa2, i-1, q1);
rv = getPoint2d_p(pa2, i, q2);
for ( j = 1; j < pa1->npoints; j++ )
{
rv = getPoint2d_p(pa1, j-1, p1);
rv = getPoint2d_p(pa1, j, p2);
LWDEBUGF(4, "lineCrossingDirection: i=%d, j=%d", i, j);
this_cross = lw_segment_intersects(p1, p2, q1, q2);
if ( ! first_cross && this_cross )
first_cross = this_cross;
if ( this_cross )
final_cross = this_cross;
if ( this_cross == SEG_CROSS_LEFT )
{
cross_left++;
break;
}
if ( this_cross == SEG_CROSS_RIGHT )
{
cross_right++;
break;
}
/*
** Crossing at a co-linearity can be turned into crossing at
** a vertex by pulling the original touch point forward along
** the co-linearity.
*/
if ( this_cross == SEG_COLINEAR && vertex_touch == (i-1) )
{
vertex_touch = i;
break;
}
/*
** Crossing-at-vertex will cause four segment touch interactions, two at
** j-1 and two at j. We avoid incrementing our cross count by ignoring the
** second pair.
*/
if ( this_cross == SEG_TOUCH_LEFT )
{
if ( vertex_touch == (i-1) && vertex_touch_type == SEG_TOUCH_RIGHT )
{
cross_left++;
vertex_touch = -1;
vertex_touch_type = 0;
}
else
{
vertex_touch = i;
vertex_touch_type = this_cross;
}
break;
}
if ( this_cross == SEG_TOUCH_RIGHT )
{
if ( vertex_touch == (i-1) && vertex_touch_type == SEG_TOUCH_LEFT )
{
cross_right++;
vertex_touch = -1;
vertex_touch_type = 0;
}
else
{
vertex_touch = i;
vertex_touch_type = this_cross;
}
break;
}
/*
** TODO Handle co-linear cases.
*/
LWDEBUGF(4, "lineCrossingDirection: this_cross=%d, vertex_touch=%d, vertex_touch_type=%d", this_cross, vertex_touch, vertex_touch_type);
}
}
LWDEBUGF(4, "first_cross=%d, final_cross=%d, cross_left=%d, cross_right=%d", first_cross, final_cross, cross_left, cross_right);
lwfree(p1);
lwfree(p2);
lwfree(q1);
lwfree(q2);
if ( !cross_left && !cross_right )
return LINE_NO_CROSS;
if ( !cross_left && cross_right == 1 )
return LINE_CROSS_RIGHT;
if ( !cross_right && cross_left == 1 )
return LINE_CROSS_LEFT;
if ( cross_left - cross_right == 1 )
return LINE_MULTICROSS_END_LEFT;
if ( cross_left - cross_right == -1 )
return LINE_MULTICROSS_END_RIGHT;
if ( cross_left - cross_right == 0 && first_cross == SEG_CROSS_LEFT )
return LINE_MULTICROSS_END_SAME_FIRST_LEFT;
if ( cross_left - cross_right == 0 && first_cross == SEG_CROSS_RIGHT )
return LINE_MULTICROSS_END_SAME_FIRST_RIGHT;
if ( cross_left - cross_right == 0 && first_cross == SEG_TOUCH_LEFT )
return LINE_MULTICROSS_END_SAME_FIRST_RIGHT;
if ( cross_left - cross_right == 0 && first_cross == SEG_TOUCH_RIGHT )
return LINE_MULTICROSS_END_SAME_FIRST_LEFT;
return LINE_NO_CROSS;
}
/*
** lwpoint_get_ordinate(point, ordinate) => double
*/
double lwpoint_get_ordinate(const POINT4D *p, int ordinate)
{
if ( ! p )
{
lwerror("Null input geometry.");
return 0.0;
}
if ( ordinate > 3 || ordinate < 0 )
{
lwerror("Cannot extract ordinate %d.", ordinate);
return 0.0;
}
if ( ordinate == 3 )
return p->m;
if ( ordinate == 2 )
return p->z;
if ( ordinate == 1 )
return p->y;
return p->x;
}
void lwpoint_set_ordinate(POINT4D *p, int ordinate, double value)
{
if ( ! p )
{
lwerror("Null input geometry.");
return;
}
if ( ordinate > 3 || ordinate < 0 )
{
lwerror("Cannot extract ordinate %d.", ordinate);
return;
}
LWDEBUGF(4, " setting ordinate %d to %g", ordinate, value);
switch ( ordinate )
{
case 3:
p->m = value;
return;
case 2:
p->z = value;
return;
case 1:
p->y = value;
return;
case 0:
p->x = value;
return;
}
}
int lwpoint_interpolate(const POINT4D *p1, const POINT4D *p2, POINT4D *p, int ndims, int ordinate, double interpolation_value)
{
double p1_value = lwpoint_get_ordinate(p1, ordinate);
double p2_value = lwpoint_get_ordinate(p2, ordinate);
double proportion;
int i = 0;
if ( ordinate < 0 || ordinate >= ndims )
{
lwerror("Ordinate (%d) is not within ndims (%d).", ordinate, ndims);
return 0;
}
if ( FP_MIN(p1_value, p2_value) > interpolation_value ||
FP_MAX(p1_value, p2_value) < interpolation_value )
{
lwerror("Cannot interpolate to a value (%g) not between the input points (%g, %g).", interpolation_value, p1_value, p2_value);
return 0;
}
proportion = fabs((interpolation_value - p1_value) / (p2_value - p1_value));
for ( i = 0; i < ndims; i++ )
{
double newordinate = 0.0;
p1_value = lwpoint_get_ordinate(p1, i);
p2_value = lwpoint_get_ordinate(p2, i);
newordinate = p1_value + proportion * (p2_value - p1_value);
lwpoint_set_ordinate(p, i, newordinate);
LWDEBUGF(4, " clip ordinate(%d) p1_value(%g) p2_value(%g) proportion(%g) newordinate(%g) ", i, p1_value, p2_value, proportion, newordinate );
}
return 1;
}
LWCOLLECTION *lwmline_clip_to_ordinate_range(LWMLINE *mline, int ordinate, double from, double to)
{
LWCOLLECTION *lwgeom_out = NULL;
if ( ! mline )
{
lwerror("Null input geometry.");
return NULL;
}
if ( mline->ngeoms == 1)
{
lwgeom_out = lwline_clip_to_ordinate_range(mline->geoms[0], ordinate, from, to);
}
else
{
LWCOLLECTION *col;
char hasz = TYPE_HASZ(mline->type);
char hasm = TYPE_HASM(mline->type);
char hassrid = TYPE_HASSRID(mline->type);
int i, j;
char homogeneous = 1;
size_t geoms_size = 0;
lwgeom_out = lwcollection_construct_empty(mline->SRID, hasz, hasm);
lwgeom_out->type = lwgeom_makeType(hasz, hasm, hassrid, MULTILINETYPE);
for ( i = 0; i < mline->ngeoms; i ++ )
{
col = lwline_clip_to_ordinate_range(mline->geoms[i], ordinate, from, to);
if ( col )
{ /* Something was left after the clip. */
if ( lwgeom_out->ngeoms + col->ngeoms > geoms_size )
{
geoms_size += 16;
if ( lwgeom_out->geoms )
{
lwgeom_out->geoms = lwrealloc(lwgeom_out->geoms, geoms_size * sizeof(LWGEOM*));
}
else
{
lwgeom_out->geoms = lwalloc(geoms_size * sizeof(LWGEOM*));
}
}
for ( j = 0; j < col->ngeoms; j++ )
{
lwgeom_out->geoms[lwgeom_out->ngeoms] = col->geoms[j];
lwgeom_out->ngeoms++;
}
if ( TYPE_GETTYPE(col->type) != TYPE_GETTYPE(mline->type) )
{
homogeneous = 0;
}
/* Shallow free the struct, leaving the geoms behind. */
if ( col->bbox ) lwfree(col->bbox);
lwfree(col);
}
}
lwgeom_drop_bbox((LWGEOM*)lwgeom_out);
lwgeom_add_bbox((LWGEOM*)lwgeom_out);
if ( ! homogeneous )
{
lwgeom_out->type = lwgeom_makeType(hasz, hasm, hassrid, COLLECTIONTYPE);
}
}
if ( ! lwgeom_out || lwgeom_out->ngeoms == 0 ) /* Nothing left after clip. */
{
return NULL;
}
return lwgeom_out;
}
/*
** lwline_clip_to_ordinate_range(line, ordinate, from, to) => lwmline
**
** Take in a LINESTRING and return a MULTILINESTRING of those portions of the
** LINESTRING between the from/to range for the specified ordinate (XYZM)
*/
LWCOLLECTION *lwline_clip_to_ordinate_range(LWLINE *line, int ordinate, double from, double to)
{
POINTARRAY *pa_in = NULL;
LWCOLLECTION *lwgeom_out = NULL;
POINTARRAY *pa_out = NULL;
DYNPTARRAY *dp = NULL;
int i, rv;
int added_last_point = 0;
POINT4D *p = NULL, *q = NULL, *r = NULL;
double ordinate_value_p = 0.0, ordinate_value_q = 0.0;
char hasz = TYPE_HASZ(line->type);
char hasm = TYPE_HASM(line->type);
char dims = TYPE_NDIMS(line->type);
char hassrid = TYPE_HASSRID(line->type);
LWDEBUGF(4, "hassrid = %d", hassrid);
/* Null input, nothing we can do. */
if ( ! line )
{
lwerror("Null input geometry.");
return NULL;
}
/* Ensure 'from' is less than 'to'. */
if ( to < from )
{
double t = from;
from = to;
to = t;
}
LWDEBUGF(4, "from = %g, to = %g, ordinate = %d", from, to, ordinate);
LWDEBUGF(4, "%s", lwgeom_to_ewkt((LWGEOM*)line, PARSER_CHECK_NONE));
/* Asking for an ordinate we don't have. Error. */
if ( ordinate >= dims )
{
lwerror("Cannot clip on ordinate %d in a %d-d geometry.", ordinate, dims);
return NULL;
}
/* Prepare our working point objects. */
p = lwalloc(sizeof(POINT4D));
q = lwalloc(sizeof(POINT4D));
r = lwalloc(sizeof(POINT4D));
/* Construct a collection to hold our outputs. */
lwgeom_out = lwalloc(sizeof(LWCOLLECTION));
lwgeom_out->type = lwgeom_makeType(hasz, hasm, hassrid, MULTILINETYPE);
if (hassrid)
lwgeom_out->SRID = line->SRID;
else
lwgeom_out->SRID = -1;
lwgeom_out->bbox = NULL;
lwgeom_out->ngeoms = 0;
lwgeom_out->geoms = NULL;
pa_in = (POINTARRAY*)line->points;
for ( i = 0; i < pa_in->npoints; i++ )
{
LWDEBUGF(4, "Point #%d", i);
if ( i > 0 )
{
q->x = p->x;
q->y = p->y;
q->z = p->z;
q->m = p->m;
ordinate_value_q = ordinate_value_p;
}
rv = getPoint4d_p(pa_in, i, p);
ordinate_value_p = lwpoint_get_ordinate(p, ordinate);
LWDEBUGF(4, " ordinate_value_p %g (current)", ordinate_value_p);
LWDEBUGF(4, " ordinate_value_q %g (previous)", ordinate_value_q);
/* Is this point inside the ordinate range? Yes. */
if ( ordinate_value_p >= from && ordinate_value_p <= to )
{
LWDEBUGF(4, " inside ordinate range (%g, %g)", from, to);
if ( ! added_last_point )
{
LWDEBUG(4," new ptarray required");
/* We didn't add the previous point, so this is a new segment.
* Make a new point array. */
if ( dp ) lwfree(dp);
dp = dynptarray_create(64, line->type);
/* We're transiting into the range so add an interpolated
* point at the range boundary.
* If we're on a boundary and crossing from the far side,
* we also need an interpolated point. */
if ( i > 0 && ( /* Don't try to interpolate if this is the first point */
( ordinate_value_p > from && ordinate_value_p < to ) || /* Inside */
( ordinate_value_p == from && ordinate_value_q > to ) || /* Hopping from above */
( ordinate_value_p == to && ordinate_value_q < from ) ) ) /* Hopping from below */
{
double interpolation_value;
(ordinate_value_q > to) ? (interpolation_value = to) : (interpolation_value = from);
rv = lwpoint_interpolate(q, p, r, dims, ordinate, interpolation_value);
rv = dynptarray_addPoint4d(dp, r, 0);
LWDEBUGF(4, " interpolating between (%g, %g) with interpolation point (%g)", ordinate_value_q, ordinate_value_p, interpolation_value);
}
}
/* Add the current vertex to the point array. */
rv = dynptarray_addPoint4d(dp, p, 0);
if ( ordinate_value_p == from || ordinate_value_p == to )
{
added_last_point = 2; /* Added on boundary. */
}
else
{
added_last_point = 1; /* Added inside range. */
}
}
/* Is this point inside the ordinate range? No. */
else
{
LWDEBUGF(4, " added_last_point (%d)", added_last_point);
if ( added_last_point == 1 )
{
/* We're transiting out of the range, so add an interpolated point
* to the point array at the range boundary. */
double interpolation_value;
(ordinate_value_p > to) ? (interpolation_value = to) : (interpolation_value = from);
rv = lwpoint_interpolate(q, p, r, dims, ordinate, interpolation_value);
rv = dynptarray_addPoint4d(dp, r, 0);
LWDEBUGF(4, " interpolating between (%g, %g) with interpolation point (%g)", ordinate_value_q, ordinate_value_p, interpolation_value);
}
else if ( added_last_point == 2 )
{
/* We're out and the last point was on the boundary.
* If the last point was the near boundary, nothing to do.
* If it was the far boundary, we need an interpolated point. */
if ( from != to && (
(ordinate_value_q == from && ordinate_value_p > from) ||
(ordinate_value_q == to && ordinate_value_p < to) ) )
{
double interpolation_value;
(ordinate_value_p > to) ? (interpolation_value = to) : (interpolation_value = from);
rv = lwpoint_interpolate(q, p, r, dims, ordinate, interpolation_value);
rv = dynptarray_addPoint4d(dp, r, 0);
LWDEBUGF(4, " interpolating between (%g, %g) with interpolation point (%g)", ordinate_value_q, ordinate_value_p, interpolation_value);
}
}
else if ( i && ordinate_value_q < from && ordinate_value_p > to )
{
/* We just hopped over the whole range, from bottom to top,
* so we need to add *two* interpolated points! */
pa_out = ptarray_construct(hasz, hasm, 2);
/* Interpolate lower point. */
rv = lwpoint_interpolate(p, q, r, dims, ordinate, from);
setPoint4d(pa_out, 0, r);
/* Interpolate upper point. */
rv = lwpoint_interpolate(p, q, r, dims, ordinate, to);
setPoint4d(pa_out, 1, r);
}
else if ( i && ordinate_value_q > to && ordinate_value_p < from )
{
/* We just hopped over the whole range, from top to bottom,
* so we need to add *two* interpolated points! */
pa_out = ptarray_construct(hasz, hasm, 2);
/* Interpolate upper point. */
rv = lwpoint_interpolate(p, q, r, dims, ordinate, to);
setPoint4d(pa_out, 0, r);
/* Interpolate lower point. */
rv = lwpoint_interpolate(p, q, r, dims, ordinate, from);
setPoint4d(pa_out, 1, r);
}
/* We have an extant point-array, save it out to a multi-line. */
if ( dp || pa_out )
{
LWGEOM *oline;
LWDEBUG(4, "saving pointarray to multi-line (1)");
if ( dp )
{
/* Only one point, so we have to make an lwpoint to hold this
* and set the overall output type to a generic collection. */
if ( dp->pa->npoints == 1 )
{
oline = (LWGEOM*)lwpoint_construct(line->SRID, NULL, dp->pa);
oline->type = lwgeom_makeType(hasz, hasm, hassrid, POINTTYPE);
lwgeom_out->type = lwgeom_makeType(hasz, hasm, hassrid, COLLECTIONTYPE);
}
else
{
oline = (LWGEOM*)lwline_construct(line->SRID, NULL, dp->pa);
oline->type = lwgeom_makeType(hasz, hasm, hassrid, LINETYPE);
}
}
else
{
oline = (LWGEOM*)lwline_construct(line->SRID, NULL, pa_out);
}
lwgeom_out->ngeoms++;
if ( lwgeom_out->geoms ) /* We can't just realloc, since repalloc chokes on a starting null ptr. */
{
lwgeom_out->geoms = lwrealloc(lwgeom_out->geoms, sizeof(LWGEOM*) * lwgeom_out->ngeoms);
}
else
{
lwgeom_out->geoms = lwalloc(sizeof(LWGEOM*) * lwgeom_out->ngeoms);
}
lwgeom_out->geoms[lwgeom_out->ngeoms - 1] = oline;
lwgeom_drop_bbox((LWGEOM*)lwgeom_out);
lwgeom_add_bbox((LWGEOM*)lwgeom_out);
if ( dp ) lwfree(dp);
dp = NULL;
if ( pa_out ) pa_out = NULL;
}
added_last_point = 0;
}
}
/* Still some points left to be saved out. */
if ( dp && dp->pa->npoints > 0 )
{
LWGEOM *oline;
LWDEBUG(4, "saving pointarray to multi-line (2)");
LWDEBUGF(4, "dp->pa->npoints == %d", dp->pa->npoints);
LWDEBUGF(4, "lwgeom_out->ngeoms == %d", lwgeom_out->ngeoms);
if ( dp->pa->npoints == 1 )
{
oline = (LWGEOM*)lwpoint_construct(line->SRID, NULL, dp->pa);
oline->type = lwgeom_makeType(hasz, hasm, hassrid, POINTTYPE);
lwgeom_out->type = lwgeom_makeType(hasz, hasm, hassrid, COLLECTIONTYPE);
}
else
{
oline = (LWGEOM*)lwline_construct(line->SRID, NULL, dp->pa);
oline->type = lwgeom_makeType(hasz, hasm, hassrid, LINETYPE);
}
lwgeom_out->ngeoms++;
if ( lwgeom_out->geoms ) /* We can't just realloc, since repalloc chokes on a starting null ptr. */
{
lwgeom_out->geoms = lwrealloc(lwgeom_out->geoms, sizeof(LWGEOM*) * lwgeom_out->ngeoms);
}
else
{
lwgeom_out->geoms = lwalloc(sizeof(LWGEOM*) * lwgeom_out->ngeoms);
}
lwgeom_out->geoms[lwgeom_out->ngeoms - 1] = oline;
lwgeom_drop_bbox((LWGEOM*)lwgeom_out);
lwgeom_add_bbox((LWGEOM*)lwgeom_out);
if ( dp ) lwfree(dp);
dp = NULL;
}
lwfree(p);
lwfree(q);
lwfree(r);
if ( lwgeom_out->ngeoms > 0 )
return lwgeom_out;
return NULL;
}
static char *base32 = "0123456789bcdefghjkmnpqrstuvwxyz";
/*
** Calculate the geohash, iterating downwards and gaining precision.
** From geohash-native.c, (c) 2008 David Troy
** Released under the MIT License.
*/
char *geohash_point(double longitude, double latitude, int precision)
{
int is_even=1, i=0;
double lat[2], lon[2], mid;
char bits[] = {16,8,4,2,1};
int bit=0, ch=0;
char *geohash = NULL;
geohash = lwalloc(precision + 1);
lat[0] = -90.0; lat[1] = 90.0;
lon[0] = -180.0; lon[1] = 180.0;
while (i < precision)
{
if (is_even)
{
mid = (lon[0] + lon[1]) / 2;
if (longitude > mid)
{
ch |= bits[bit];
lon[0] = mid;
}
else
{
lon[1] = mid;
}
}
else
{
mid = (lat[0] + lat[1]) / 2;
if (latitude > mid)
{
ch |= bits[bit];
lat[0] = mid;
}
else
{
lat[1] = mid;
}
}
is_even = !is_even;
if (bit < 4)
{
bit++;
}
else
{
geohash[i++] = base32[ch];
bit = 0;
ch = 0;
}
}
geohash[i] = 0;
return geohash;
}
int lwgeom_geohash_precision(BOX3D bbox, BOX3D *bounds)
{
double minx, miny, maxx, maxy;
double latmax, latmin, lonmax, lonmin;
double lonwidth, latwidth;
double latmaxadjust, lonmaxadjust, latminadjust, lonminadjust;
int precision = 0;
/* Get the bounding box, return error if things don't work out. */
minx = bbox.xmin;
miny = bbox.ymin;
maxx = bbox.xmax;
maxy = bbox.ymax;
if( minx == maxx && miny == maxy )
{
/* It's a point. Doubles have 51 bits of precision.
** 2 * 51 / 5 == 20 */
return 20;
}
lonmin = -180.0;
latmin = -90.0;
lonmax = 180.0;
latmax = 90.0;
/* Shrink a world bounding box until one of the edges interferes with the
** bounds of our rectangle. */
while( 1 )
{
lonwidth = lonmax - lonmin;
latwidth = latmax - latmin;
latmaxadjust = lonmaxadjust = latminadjust = lonminadjust = 0.0;
if( minx > lonmin + lonwidth / 2.0 )
{
lonminadjust = lonwidth / 2.0;
}
else if ( maxx < lonmax - lonwidth / 2.0 )
{
lonmaxadjust = -1 * lonwidth / 2.0;
}
if( miny > latmin + latwidth / 2.0 )
{
latminadjust = latwidth / 2.0;
}
else if (maxy < latmax - latwidth / 2.0 )
{
latmaxadjust = -1 * latwidth / 2.0;
}
/* Only adjust if adjustments are legal (we haven't crossed any edges). */
if ( (lonminadjust || lonmaxadjust) && (latminadjust || latmaxadjust ) )
{
latmin += latminadjust;
lonmin += lonminadjust;
latmax += latmaxadjust;
lonmax += lonmaxadjust;
/* Each adjustment cycle corresponds to 2 bits of storage in the
** geohash. */
precision += 2;
}
else
{
break;
}
}
/* Save the edges of our bounds, in case someone cares later. */
bounds->xmin = lonmin;
bounds->xmax = lonmax;
bounds->ymin = latmin;
bounds->ymax = latmax;
/* Each geohash character (base32) can contain 5 bits of information.
** We are returning the precision in characters, so here we divide. */
return precision / 5;
}
/*
** Return a geohash string for the geometry.
** Where the precision is non-positive, calculate a precision based on the
** bounds of the feature. Big features have loose precision.
** Small features have tight precision.
*/
char *lwgeom_geohash(const LWGEOM *lwgeom, int precision)
{
BOX3D *bbox = NULL;
BOX3D precision_bounds;
double lat, lon;
bbox = lwgeom_compute_box3d(lwgeom);
if( ! bbox ) return NULL;
/* Return error if we are being fed something outside our working bounds */
if ( bbox->xmin < -180 || bbox->ymin < -90 || bbox->xmax > 180 || bbox->ymax > 90 )
{
lwerror("Geohash requires inputs in decimal degrees.");
lwfree(bbox);
return NULL;
}
/* What is the center of our geometry bounds? We'll use that to
** approximate location. */
lon = bbox->xmin + (bbox->xmax - bbox->xmin) / 2;
lat = bbox->ymin + (bbox->ymax - bbox->ymin) / 2;
if ( precision <= 0 )
{
precision = lwgeom_geohash_precision(*bbox, &precision_bounds);
}
lwfree(bbox);
/*
** Return the geohash of the center, with a precision determined by the
** extent of the bounds.
** Possible change: return the point at the center of the precision bounds?
*/
return geohash_point(lon, lat, precision);
}
================================================
FILE: src/liblwgeom/lwalgorithm.h
================================================
/**********************************************************************
* $Id: lwalgorithm.h 3688 2009-02-11 21:48:13Z pramsey $
*
* PostGIS - Spatial Types for PostgreSQL
* http://postgis.refractions.net
* Copyright 2008 Paul Ramsey
*
* This is free software; you can redistribute and/or modify it under
* the terms of the GNU General Public Licence. See the COPYING file.
*
**********************************************************************/
#include
#include "liblwgeom.h"
enum CG_SEGMENT_INTERSECTION_TYPE {
SEG_ERROR = -1,
SEG_NO_INTERSECTION = 0,
SEG_COLINEAR = 1,
SEG_CROSS_LEFT = 2,
SEG_CROSS_RIGHT = 3,
SEG_TOUCH_LEFT = 4,
SEG_TOUCH_RIGHT = 5
};
double lw_segment_side(POINT2D *p1, POINT2D *p2, POINT2D *q);
int lw_segment_intersects(POINT2D *p1, POINT2D *p2, POINT2D *q1, POINT2D *q2);
int lw_segment_envelope_intersects(POINT2D p1, POINT2D p2, POINT2D q1, POINT2D q2);
enum CG_LINE_CROSS_TYPE {
LINE_NO_CROSS = 0,
LINE_CROSS_LEFT = -1,
LINE_CROSS_RIGHT = 1,
LINE_MULTICROSS_END_LEFT = -2,
LINE_MULTICROSS_END_RIGHT = 2,
LINE_MULTICROSS_END_SAME_FIRST_LEFT = -3,
LINE_MULTICROSS_END_SAME_FIRST_RIGHT = 3
};
int lwline_crossing_direction(LWLINE *l1, LWLINE *l2);
double lwpoint_get_ordinate(const POINT4D *p, int ordinate);
void lwpoint_set_ordinate(POINT4D *p, int ordinate, double value);
int lwpoint_interpolate(const POINT4D *p1, const POINT4D *p2, POINT4D *p, int ndims, int ordinate, double interpolation_value);
LWCOLLECTION *lwline_clip_to_ordinate_range(LWLINE *line, int ordinate, double from, double to);
LWCOLLECTION *lwmline_clip_to_ordinate_range(LWMLINE *mline, int ordinate, double from, double to);
int lwgeom_geohash_precision(BOX3D bbox, BOX3D *bounds);
char *lwgeom_geohash(const LWGEOM *lwgeom, int precision);
char *geohash_point(double longitude, double latitude, int precision);
================================================
FILE: src/liblwgeom/lwcircstring.c
================================================
/**********************************************************************
* $Id: lwcircstring.c 3639 2009-02-04 00:28:37Z pramsey $
*
* PostGIS - Spatial Types for PostgreSQL
* http://postgis.refractions.net
* Copyright 2001-2006 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.
*
**********************************************************************/
/* basic LWCIRCSTRING functions */
#include
#include
#include
#include
#include "liblwgeom.h"
BOX3D *lwcircle_compute_box3d(POINT4D *p1, POINT4D *p2, POINT4D *p3);
void printLWCIRCSTRING(LWCIRCSTRING *curve);
void lwcircstring_reverse(LWCIRCSTRING *curve);
LWCIRCSTRING *lwcircstring_segmentize2d(LWCIRCSTRING *curve, double dist);
char lwcircstring_same(const LWCIRCSTRING *me, const LWCIRCSTRING *you);
LWCIRCSTRING *lwcircstring_from_lwpointarray(int SRID, unsigned int npoints, LWPOINT **points);
LWCIRCSTRING *lwcircstring_from_lwmpoint(int SRID, LWMPOINT *mpoint);
LWCIRCSTRING *lwcircstring_addpoint(LWCIRCSTRING *curve, LWPOINT *point, unsigned int where);
LWCIRCSTRING *lwcircstring_removepoint(LWCIRCSTRING *curve, unsigned int index);
void lwcircstring_setPoint4d(LWCIRCSTRING *curve, unsigned int index, POINT4D *newpoint);
#ifndef MAXFLOAT
#define MAXFLOAT 3.402823466e+38F
#endif
/*
* Construct a new LWCIRCSTRING. points will *NOT* be copied
* use SRID=-1 for unknown SRID (will have 8bit type's S = 0)
*/
LWCIRCSTRING *
lwcircstring_construct(int SRID, BOX2DFLOAT4 *bbox, POINTARRAY *points)
{
LWCIRCSTRING *result;
/*
* The first arc requires three points. Each additional
* arc requires two more points. Thus the minimum point count
* is three, and the count must be odd.
*/
if(points->npoints % 2 != 1 || points->npoints < 3)
{
lwerror("lwcircstring_construct: invalid point count %d", points->npoints);
return NULL;
}
result = (LWCIRCSTRING*) lwalloc(sizeof(LWCIRCSTRING));
result->type = lwgeom_makeType_full(
TYPE_HASZ(points->dims),
TYPE_HASM(points->dims),
(SRID!=-1), CIRCSTRINGTYPE, 0);
result->SRID = SRID;
result->points = points;
result->bbox = bbox;
return result;
}
/*
* given the LWGEOM serialized form (or a point into a multi* one)
* construct a proper LWCIRCSTRING.
* serialized_form should point to the 8bit type format (with type = 8)
* See serialized form doc
*/
LWCIRCSTRING *
lwcircstring_deserialize(uchar *serialized_form)
{
uchar type;
LWCIRCSTRING *result;
uchar *loc=NULL;
uint32 npoints;
POINTARRAY *pa;
type = (uchar)serialized_form[0];
if(lwgeom_getType(type) != CIRCSTRINGTYPE)
{
lwerror("lwcircstring_deserialize: attempt to deserialize a circularstring which is really a %s", lwgeom_typename(type));
return NULL;
}
result = (LWCIRCSTRING*) lwalloc(sizeof(LWCIRCSTRING));
result->type = type;
loc = serialized_form + 1;
if(lwgeom_hasBBOX(type))
{
LWDEBUG(3, "lwcircstring_deserialize: input has bbox");
result->bbox = lwalloc(sizeof(BOX2DFLOAT4));
memcpy(result->bbox, loc, sizeof(BOX2DFLOAT4));
loc += sizeof(BOX2DFLOAT4);
}
else
{
LWDEBUG(3, "lwcircstring_deserialize: input lacks bbox");
result->bbox = NULL;
}
if(lwgeom_hasSRID(type))
{
LWDEBUG(3, "lwcircstring_deserialize: input has srid");
result->SRID = lw_get_int32(loc);
loc += 4; /* type + SRID */
}
else
{
LWDEBUG(3, "lwcircstring_deserialize: input lacks srid");
result->SRID = -1;
}
/* we've read the type (1 byte) and SRID (4 bytes, if present) */
npoints = lw_get_uint32(loc);
LWDEBUGF(3, "circstring npoints = %d", npoints);
loc += 4;
pa = pointArray_construct(loc, TYPE_HASZ(type), TYPE_HASM(type), npoints);
result->points = pa;
return result;
}
/*
* convert this circularstring into its serialized form
* result's first char will be the 8bit type. See serialized form doc
*/
uchar *
lwcircstring_serialize(LWCIRCSTRING *curve)
{
size_t size, retsize;
uchar * result;
if(curve == NULL) {
lwerror("lwcircstring_serialize:: given null curve");
return NULL;
}
size = lwcircstring_serialize_size(curve);
result = lwalloc(size);
lwcircstring_serialize_buf(curve, result, &retsize);
if(retsize != size)
lwerror("lwcircstring_serialize_size returned %d, ..serialize_buf returned %d", size, retsize);
return result;
}
/*
* convert this circularstring into its serialized form writing it into
* the given buffer, and returning number of bytes written into
* the given int pointer.
* result's first char will be the 8bit type. See serialized form doc
*/
void lwcircstring_serialize_buf(LWCIRCSTRING *curve, uchar *buf, size_t *retsize)
{
char hasSRID;
uchar *loc;
int ptsize;
size_t size;
LWDEBUGF(2, "lwcircstring_serialize_buf(%p, %p, %p) called",
curve, buf, retsize);
if(curve == NULL)
{
lwerror("lwcircstring_serialize:: given null curve");
return;
}
if(TYPE_GETZM(curve->type) != TYPE_GETZM(curve->points->dims))
{
lwerror("Dimensions mismatch in lwcircstring");
return;
}
ptsize = pointArray_ptsize(curve->points);
hasSRID = (curve->SRID != -1);
buf[0] = (uchar)lwgeom_makeType_full(
TYPE_HASZ(curve->type), TYPE_HASM(curve->type),
hasSRID, CIRCSTRINGTYPE, curve->bbox ? 1 : 0);
loc = buf+1;
LWDEBUGF(3, "lwcircstring_serialize_buf added type (%d)", curve->type);
if(curve->bbox)
{
memcpy(loc, curve->bbox, sizeof(BOX2DFLOAT4));
loc += sizeof(BOX2DFLOAT4);
LWDEBUG(3, "lwcircstring_serialize_buf added BBOX");
}
if(hasSRID)
{
memcpy(loc, &curve->SRID, sizeof(int32));
loc += sizeof(int32);
LWDEBUG(3, "lwcircstring_serialize_buf added SRID");
}
memcpy(loc, &curve->points->npoints, sizeof(uint32));
loc += sizeof(uint32);
LWDEBUGF(3, "lwcircstring_serialize_buf added npoints (%d)",
curve->points->npoints);
/* copy in points */
size = curve->points->npoints * ptsize;
memcpy(loc, getPoint_internal(curve->points, 0), size);
loc += size;
LWDEBUGF(3, "lwcircstring_serialize_buf copied serialized_pointlist (%d bytes)",
ptsize * curve->points->npoints);
if(retsize) *retsize = loc-buf;
LWDEBUGF(3, "lwcircstring_serialize_buf returning (loc: %p, size: %d)",
loc, loc-buf);
}
/* find length of this deserialized circularstring */
size_t
lwcircstring_serialize_size(LWCIRCSTRING *curve)
{
size_t size = 1; /* type */
LWDEBUG(2, "lwcircstring_serialize_size called");
if(curve->SRID != -1) size += 4; /* SRID */
if(curve->bbox) size += sizeof(BOX2DFLOAT4);
size += 4; /* npoints */
size += pointArray_ptsize(curve->points) * curve->points->npoints;
LWDEBUGF(3, "lwcircstring_serialize_size returning %d", size);
return size;
}
BOX3D *
lwcircle_compute_box3d(POINT4D *p1, POINT4D *p2, POINT4D *p3)
{
double x1, x2, y1, y2, z1, z2;
double angle, radius, sweep;
/* angles from center */
double a1, a2, a3;
/* angles from center once a1 is rotated to zero */
double r2, r3;
double xe = 0.0, ye = 0.0;
POINT4D *center;
int i;
BOX3D *box;
LWDEBUG(2, "lwcircle_compute_box3d called.");
center = lwalloc(sizeof(POINT4D));
radius = lwcircle_center(p1, p2, p3, ¢er);
if(radius < 0.0) return NULL;
/*
top = center->y + radius;
left = center->x - radius;
LWDEBUGF(3, "lwcircle_compute_box3d: center (%.16f, %.16f)", center->x, center->y);
*/
x1 = MAXFLOAT;
x2 = -1 * MAXFLOAT;
y1 = MAXFLOAT;
y2 = -1 * MAXFLOAT;
a1 = atan2(p1->y - center->y, p1->x - center->x);
a2 = atan2(p2->y - center->y, p2->x - center->x);
a3 = atan2(p3->y - center->y, p3->x - center->x);
/* Rotate a2 and a3 such that a1 = 0 */
r2 = a2 - a1;
r3 = a3 - a1;
/*
* There are six cases here I'm interested in
* Clockwise:
* 1. a1-a2 < 180 == r2 < 0 && (r3 > 0 || r3 < r2)
* 2. a1-a2 > 180 == r2 > 0 && (r3 > 0 && r3 < r2)
* 3. a1-a2 > 180 == r2 > 0 && (r3 > r2 || r3 < 0)
* Counter-clockwise:
* 4. a1-a2 < 180 == r2 > 0 && (r3 < 0 || r3 > r2)
* 5. a1-a2 > 180 == r2 < 0 && (r3 < 0 && r3 > r2)
* 6. a1-a2 > 180 == r2 < 0 && (r3 < r2 || r3 > 0)
* 3 and 6 are invalid cases where a3 is the midpoint.
* BBOX is fundamental, so these cannot error out and will instead
* calculate the sweep using a3 as the middle point.
*/
/* clockwise 1 */
if(FP_LT(r2, 0) && (FP_GT(r3, 0) || FP_LT(r3, r2)))
{
sweep = (FP_GT(r3, 0)) ? (r3 - 2 * M_PI) : r3;
}
/* clockwise 2 */
else if(FP_GT(r2, 0) && FP_GT(r3, 0) && FP_LT(r3, r2))
{
sweep = (FP_GT(r3, 0)) ? (r3 - 2 * M_PI) : r3;
}
/* counter-clockwise 4 */
else if(FP_GT(r2, 0) && (FP_LT(r3, 0) || FP_GT(r3, r2)))
{
sweep = (FP_LT(r3, 0)) ? (r3 + 2 * M_PI) : r3;
}
/* counter-clockwise 5 */
else if(FP_LT(r2, 0) && FP_LT(r3, 0) && FP_GT(r3, r2))
{
sweep = (FP_LT(r3, 0)) ? (r3 + 2 * M_PI) : r3;
}
/* clockwise invalid 3 */
else if(FP_GT(r2, 0) && (FP_GT(r3, r2) || FP_LT(r3, 0)))
{
sweep = (FP_GT(r2, 0)) ? (r2 - 2 * M_PI) : r2;
}
/* clockwise invalid 6 */
else
{
sweep = (FP_LT(r2, 0)) ? (r2 + 2 * M_PI) : r2;
}
LWDEBUGF(3, "a1 %.16f, a2 %.16f, a3 %.16f, sweep %.16f", a1, a2, a3, sweep);
angle = 0.0;
for(i=0; i < 6; i++)
{
switch(i) {
/* right extent */
case 0:
angle = 0.0;
xe = center->x + radius;
ye = center->y;
break;
/* top extent */
case 1:
angle = M_PI_2;
xe = center->x;
ye = center->y + radius;
break;
/* left extent */
case 2:
angle = M_PI;
xe = center->x - radius;
ye = center->y;
break;
/* bottom extent */
case 3:
angle = -1 * M_PI_2;
xe = center->x;
ye = center->y - radius;
break;
/* first point */
case 4:
angle = a1;
xe = p1->x;
ye = p1->y;
break;
/* last point */
case 5:
angle = a3;
xe = p3->x;
ye = p3->y;
break;
}
/* determine if the extents are outside the arc */
if(i < 4)
{
if(FP_GT(sweep, 0.0))
{
if(FP_LT(a3, a1))
{
if(FP_GT(angle, (a3 + 2 * M_PI)) || FP_LT(angle, a1)) continue;
}
else
{
if(FP_GT(angle, a3) || FP_LT(angle, a1)) continue;
}
}
else
{
if(FP_GT(a3, a1))
{
if(FP_LT(angle, (a3 - 2 * M_PI)) || FP_GT(angle, a1)) continue;
}
else
{
if(FP_LT(angle, a3) || FP_GT(angle, a1)) continue;
}
}
}
LWDEBUGF(3, "lwcircle_compute_box3d: potential extreame %d (%.16f, %.16f)", i, xe, ye);
x1 = (FP_LT(x1, xe)) ? x1 : xe;
y1 = (FP_LT(y1, ye)) ? y1 : ye;
x2 = (FP_GT(x2, xe)) ? x2 : xe;
y2 = (FP_GT(y2, ye)) ? y2 : ye;
}
LWDEBUGF(3, "lwcircle_compute_box3d: extreames found (%.16f %.16f, %.16f %.16f)", x1, y1, x2, y2);
/*
x1 = center->x + x1 * radius;
x2 = center->x + x2 * radius;
y1 = center->y + y1 * radius;
y2 = center->y + y2 * radius;
*/
z1 = (FP_LT(p1->z, p2->z)) ? p1->z : p2->z;
z1 = (FP_LT(z1, p3->z)) ? z1 : p3->z;
z2 = (FP_GT(p1->z, p2->z)) ? p1->z : p2->z;
z2 = (FP_GT(z2, p3->z)) ? z2 : p3->z;
box = lwalloc(sizeof(BOX3D));
box->xmin = x1; box->xmax = x2;
box->ymin = y1; box->ymax = y2;
box->zmin = z1; box->zmax = z2;
lwfree(center);
return box;
}
/*
* Find bounding box (standard one)
* zmin=zmax=NO_Z_VALUE if 2d
* TODO: This ignores curvature, which should be taken into account.
*/
BOX3D *
lwcircstring_compute_box3d(LWCIRCSTRING *curve)
{
BOX3D *box, *tmp;
int i;
POINT4D *p1 = lwalloc(sizeof(POINT4D));
POINT4D *p2 = lwalloc(sizeof(POINT4D));
POINT4D *p3 = lwalloc(sizeof(POINT4D));
LWDEBUG(2, "lwcircstring_compute_box3d called.");
/* initialize box values */
box = lwalloc(sizeof(BOX3D));
box->xmin = MAXFLOAT; box->xmax = -1 * MAXFLOAT;
box->ymin = MAXFLOAT; box->ymax = -1 * MAXFLOAT;
box->zmin = MAXFLOAT; box->zmax = -1 * MAXFLOAT;
for(i = 2; i < curve->points->npoints; i+=2)
{
getPoint4d_p(curve->points, i-2, p1);
getPoint4d_p(curve->points, i-1, p2);
getPoint4d_p(curve->points, i, p3);
tmp = lwcircle_compute_box3d(p1, p2, p3);
if(tmp == NULL) continue;
box->xmin = (box->xmin < tmp->xmin) ? box->xmin : tmp->xmin;
box->xmax = (box->xmax > tmp->xmax) ? box->xmax : tmp->xmax;
box->ymin = (box->ymin < tmp->ymin) ? box->ymin : tmp->ymin;
box->ymax = (box->ymax > tmp->ymax) ? box->ymax : tmp->ymax;
box->zmin = (box->zmin < tmp->zmin) ? box->zmin : tmp->zmin;
box->zmax = (box->zmax > tmp->zmax) ? box->zmax : tmp->zmax;
LWDEBUGF(4, "circularstring %d x=(%.16f,%.16f) y=(%.16f,%.16f) z=(%.16f,%.16f)", i/2, box->xmin, box->xmax, box->ymin, box->ymax, box->zmin, box->zmax);
}
return box;
}
int
lwcircstring_compute_box2d_p(LWCIRCSTRING *curve, BOX2DFLOAT4 *result)
{
BOX3D *box = lwcircstring_compute_box3d(curve);
LWDEBUG(2, "lwcircstring_compute_box2d_p called.");
if(box == NULL) return 0;
box3d_to_box2df_p(box, result);
return 1;
}
void lwcircstring_free(LWCIRCSTRING *curve)
{
lwfree(curve->points);
lwfree(curve);
}
/* find length of this serialized curve */
size_t
lwgeom_size_circstring(const uchar *serialized_curve)
{
int type = (uchar)serialized_curve[0];
uint32 result = 1; /* type */
const uchar *loc;
uint32 npoints;
LWDEBUG(2, "lwgeom_size_circstring called");
if(lwgeom_getType(type) != CIRCSTRINGTYPE)
lwerror("lwgeom_size_circstring::attempt to find the length of a non-circularstring");
loc = serialized_curve + 1;
if(lwgeom_hasBBOX(type))
{
loc += sizeof(BOX2DFLOAT4);
result += sizeof(BOX2DFLOAT4);
}
if(lwgeom_hasSRID(type))
{
loc += 4; /* type + SRID */
result += 4;
}
/* we've read the type (1 byte) and SRID (4 bytes, if present) */
npoints = lw_get_uint32(loc);
result += sizeof(uint32); /* npoints */
result += TYPE_NDIMS(type) * sizeof(double) * npoints;
LWDEBUGF(3, "lwgeom_size_circstring returning %d", result);
return result;
}
void printLWCIRCSTRING(LWCIRCSTRING *curve)
{
lwnotice("LWCIRCSTRING {");
lwnotice(" ndims = %i", (int)TYPE_NDIMS(curve->type));
lwnotice(" SRID = %i", (int)curve->SRID);
printPA(curve->points);
lwnotice("}");
}
/* Clone LWCIRCSTRING object. POINTARRAY is not copied. */
LWCIRCSTRING *
lwcircstring_clone(const LWCIRCSTRING *g)
{
LWCIRCSTRING *ret = lwalloc(sizeof(LWCIRCSTRING));
memcpy(ret, g, sizeof(LWCIRCSTRING));
if(g->bbox) ret->bbox = box2d_clone(g->bbox);
return ret;
}
/*
* Add 'what' to this curve at position 'where'.
* where=0 == prepend
* where=-1 == append
* Returns a MULTICURVE or a GEOMETRYCOLLECTION
*/
LWGEOM *
lwcircstring_add(const LWCIRCSTRING *to, uint32 where, const LWGEOM *what)
{
LWCOLLECTION *col;
LWGEOM **geoms;
int newtype;
if(where != -1 && where != 0)
{
lwerror("lwcurve_add only supports 0 or -1 as second argument %d", where);
return NULL;
}
/* dimensions compatibility are checked by caller */
/* Construct geoms array */
geoms = lwalloc(sizeof(LWGEOM *)*2);
if(where == -1) /* append */
{
geoms[0] = lwgeom_clone((LWGEOM *)to);
geoms[1] = lwgeom_clone(what);
}
else /* prepend */
{
geoms[0] = lwgeom_clone(what);
geoms[1] = lwgeom_clone((LWGEOM *)to);
}
/* reset SRID and wantbbox flag from component types */
geoms[0]->SRID = geoms[1]->SRID = -1;
TYPE_SETHASSRID(geoms[0]->type, 0);
TYPE_SETHASSRID(geoms[1]->type, 0);
TYPE_SETHASBBOX(geoms[0]->type, 0);
TYPE_SETHASBBOX(geoms[1]->type, 0);
/* Find appropriate geom type */
if(TYPE_GETTYPE(what->type) == CIRCSTRINGTYPE || TYPE_GETTYPE(what->type) == LINETYPE) newtype = MULTICURVETYPE;
else newtype = COLLECTIONTYPE;
col = lwcollection_construct(newtype,
to->SRID, NULL,
2, geoms);
return (LWGEOM *)col;
}
void lwcircstring_reverse(LWCIRCSTRING *curve)
{
ptarray_reverse(curve->points);
}
/*
* TODO: Invalid segmentization. This should accomodate the curvature.
*/
LWCIRCSTRING *
lwcircstring_segmentize2d(LWCIRCSTRING *curve, double dist)
{
return lwcircstring_construct(curve->SRID, NULL,
ptarray_segmentize2d(curve->points, dist));
}
/* check coordinate equality */
char
lwcircstring_same(const LWCIRCSTRING *me, const LWCIRCSTRING *you)
{
return ptarray_same(me->points, you->points);
}
/*
* Construct a LWCIRCSTRING from an array of LWPOINTs
* LWCIRCSTRING dimensions are large enough to host all input dimensions.
*/
LWCIRCSTRING *
lwcircstring_from_lwpointarray(int SRID, unsigned int npoints, LWPOINT **points)
{
int zmflag=0;
unsigned int i;
POINTARRAY *pa;
uchar *newpoints, *ptr;
size_t ptsize, size;
/*
* Find output dimensions, check integrity
*/
for(i = 0; i < npoints; i++)
{
if(TYPE_GETTYPE(points[i]->type) != POINTTYPE)
{
lwerror("lwcurve_from_lwpointarray: invalid input type: %s",
lwgeom_typename(TYPE_GETTYPE(points[i]->type)));
return NULL;
}
if(TYPE_HASZ(points[i]->type)) zmflag |= 2;
if(TYPE_HASM(points[i]->type)) zmflag |=1;
if(zmflag == 3) break;
}
if(zmflag == 0) ptsize = 2 * sizeof(double);
else if(zmflag == 3) ptsize = 4 * sizeof(double);
else ptsize = 3 * sizeof(double);
/*
* Allocate output points array
*/
size = ptsize * npoints;
newpoints = lwalloc(size);
memset(newpoints, 0, size);
ptr = newpoints;
for(i = 0; i < npoints; i++)
{
size = pointArray_ptsize(points[i]->point);
memcpy(ptr, getPoint_internal(points[i]->point, 0), size);
ptr += ptsize;
}
pa = pointArray_construct(newpoints, zmflag&2, zmflag&1, npoints);
return lwcircstring_construct(SRID, NULL, pa);
}
/*
* Construct a LWCIRCSTRING from a LWMPOINT
*/
LWCIRCSTRING *
lwcircstring_from_lwmpoint(int SRID, LWMPOINT *mpoint)
{
unsigned int i;
POINTARRAY *pa;
char zmflag = TYPE_GETZM(mpoint->type);
size_t ptsize, size;
uchar *newpoints, *ptr;
if(zmflag == 0) ptsize = 2 * sizeof(double);
else if(zmflag == 3) ptsize = 4 * sizeof(double);
else ptsize = 3 * sizeof(double);
/* Allocate space for output points */
size = ptsize * mpoint->ngeoms;
newpoints = lwalloc(size);
memset(newpoints, 0, size);
ptr = newpoints;
for(i = 0; i < mpoint->ngeoms; i++)
{
memcpy(ptr,
getPoint_internal(mpoint->geoms[i]->point, 0),
ptsize);
ptr += ptsize;
}
pa = pointArray_construct(newpoints, zmflag&2, zmflag&1,
mpoint->ngeoms);
LWDEBUGF(3, "lwcurve_from_lwmpoint: constructed pointarray for %d points, %d zmflag", mpoint->ngeoms, zmflag);
return lwcircstring_construct(SRID, NULL, pa);
}
LWCIRCSTRING *
lwcircstring_addpoint(LWCIRCSTRING *curve, LWPOINT *point, unsigned int where)
{
POINTARRAY *newpa;
LWCIRCSTRING *ret;
newpa = ptarray_addPoint(curve->points,
getPoint_internal(point->point, 0),
TYPE_NDIMS(point->type), where);
ret = lwcircstring_construct(curve->SRID, NULL, newpa);
return ret;
}
LWCIRCSTRING *
lwcircstring_removepoint(LWCIRCSTRING *curve, unsigned int index)
{
POINTARRAY *newpa;
LWCIRCSTRING *ret;
newpa = ptarray_removePoint(curve->points, index);
ret = lwcircstring_construct(curve->SRID, NULL, newpa);
return ret;
}
/*
* Note: input will be changed, make sure you have permissions for this.
* */
void
lwcircstring_setPoint4d(LWCIRCSTRING *curve, unsigned int index, POINT4D *newpoint)
{
setPoint4d(curve->points, index, newpoint);
}
================================================
FILE: src/liblwgeom/lwcollection.c
================================================
/**********************************************************************
* $Id: lwcollection.c 3812 2009-03-09 14:36:15Z pramsey $
*
* PostGIS - Spatial Types for PostgreSQL
* http://postgis.refractions.net
* Copyright 2001-2006 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.
*
**********************************************************************/
#include
#include
#include
#include "liblwgeom.h"
#define CHECK_LWGEOM_ZM 1
void
lwcollection_release(LWCOLLECTION *lwcollection)
{
lwgeom_release(lwcollection_as_lwgeom(lwcollection));
}
LWCOLLECTION *
lwcollection_construct(unsigned int type, int SRID, BOX2DFLOAT4 *bbox,
unsigned int ngeoms, LWGEOM **geoms)
{
LWCOLLECTION *ret;
int hasz, hasm;
#ifdef CHECK_LWGEOM_ZM
char zm;
unsigned int i;
#endif
LWDEBUGF(2, "lwcollection_construct called with %d, %d, %p, %d, %p.", type, SRID, bbox, ngeoms, geoms);
hasz = 0;
hasm = 0;
if ( ngeoms > 0 )
{
hasz = TYPE_HASZ(geoms[0]->type);
hasm = TYPE_HASM(geoms[0]->type);
#ifdef CHECK_LWGEOM_ZM
zm = TYPE_GETZM(geoms[0]->type);
LWDEBUGF(3, "lwcollection_construct type[0]=%d", geoms[0]->type);
for (i=1; itype);
if ( zm != TYPE_GETZM(geoms[i]->type) )
lwerror("lwcollection_construct: mixed dimension geometries: %d/%d", zm, TYPE_GETZM(geoms[i]->type));
}
#endif
}
ret = lwalloc(sizeof(LWCOLLECTION));
ret->type = lwgeom_makeType_full(hasz, hasm, (SRID!=-1),
type, 0);
ret->SRID = SRID;
ret->ngeoms = ngeoms;
ret->geoms = geoms;
ret->bbox = bbox;
return ret;
}
LWCOLLECTION *
lwcollection_construct_empty(int SRID, char hasz, char hasm)
{
LWCOLLECTION *ret;
ret = lwalloc(sizeof(LWCOLLECTION));
ret->type = lwgeom_makeType_full(hasz, hasm, (SRID!=-1),
COLLECTIONTYPE, 0);
ret->SRID = SRID;
ret->ngeoms = 0;
ret->geoms = NULL;
ret->bbox = NULL;
return ret;
}
LWCOLLECTION *
lwcollection_deserialize(uchar *srl)
{
LWCOLLECTION *result;
LWGEOM_INSPECTED *insp;
char typefl = srl[0];
int type = lwgeom_getType(typefl);
int i;
if ( type != COLLECTIONTYPE )
{
lwerror("lwcollection_deserialize called on NON geometrycollection: %d", type);
return NULL;
}
insp = lwgeom_inspect(srl);
result = lwalloc(sizeof(LWCOLLECTION));
result->type = typefl;
result->SRID = insp->SRID;
result->ngeoms = insp->ngeometries;
if (lwgeom_hasBBOX(srl[0]))
{
result->bbox = lwalloc(sizeof(BOX2DFLOAT4));
memcpy(result->bbox, srl+1, sizeof(BOX2DFLOAT4));
}
else result->bbox = NULL;
if ( insp->ngeometries )
{
result->geoms = lwalloc(sizeof(LWGEOM *)*insp->ngeometries);
for (i=0; ingeometries; i++)
{
result->geoms[i] =
lwgeom_deserialize(insp->sub_geoms[i]);
}
}
return result;
}
LWGEOM *
lwcollection_getsubgeom(LWCOLLECTION *col, int gnum)
{
return (LWGEOM *)col->geoms[gnum];
}
/* find serialized size of this collection */
size_t
lwcollection_serialize_size(LWCOLLECTION *col)
{
size_t size = 5; /* type + nsubgeoms */
int i;
if ( col->SRID != -1 ) size += 4; /* SRID */
if ( col->bbox ) size += sizeof(BOX2DFLOAT4);
LWDEBUGF(2, "lwcollection_serialize_size[%p]: start size: %d", col, size);
for (i=0; ingeoms; i++)
{
size += lwgeom_serialize_size(col->geoms[i]);
LWDEBUGF(3, "lwcollection_serialize_size[%p]: with geom%d: %d", col, i, size);
}
LWDEBUGF(3, "lwcollection_serialize_size[%p]: returning %d", col, size);
return size;
}
/*
* convert this collectoin into its serialize form writing it into
* the given buffer, and returning number of bytes written into
* the given int pointer.
*/
void
lwcollection_serialize_buf(LWCOLLECTION *coll, uchar *buf, size_t *retsize)
{
size_t size=1; /* type */
size_t subsize=0;
char hasSRID;
uchar *loc;
int i;
LWDEBUGF(2, "lwcollection_serialize_buf called (%s with %d elems)",
lwgeom_typename(TYPE_GETTYPE(coll->type)), coll->ngeoms);
hasSRID = (coll->SRID != -1);
buf[0] = lwgeom_makeType_full(
TYPE_HASZ(coll->type), TYPE_HASM(coll->type),
hasSRID, TYPE_GETTYPE(coll->type), coll->bbox ? 1 : 0);
loc = buf+1;
/* Add BBOX if requested */
if ( coll->bbox )
{
memcpy(loc, coll->bbox, sizeof(BOX2DFLOAT4));
size += sizeof(BOX2DFLOAT4);
loc += sizeof(BOX2DFLOAT4);
}
/* Add SRID if requested */
if (hasSRID)
{
memcpy(loc, &coll->SRID, 4);
size += 4;
loc += 4;
}
/* Write number of subgeoms */
memcpy(loc, &coll->ngeoms, 4);
size += 4;
loc += 4;
/* Serialize subgeoms */
for (i=0; ingeoms; i++)
{
lwgeom_serialize_buf(coll->geoms[i], loc, &subsize);
size += subsize;
loc += subsize;
}
if (retsize) *retsize = size;
LWDEBUG(3, "lwcollection_serialize_buf returning");
}
int
lwcollection_compute_box2d_p(LWCOLLECTION *col, BOX2DFLOAT4 *box)
{
BOX2DFLOAT4 boxbuf;
uint32 i;
if ( ! col->ngeoms ) return 0;
if ( ! lwgeom_compute_box2d_p(col->geoms[0], box) ) return 0;
for (i=1; ingeoms; i++)
{
if ( ! lwgeom_compute_box2d_p(col->geoms[i], &boxbuf) )
return 0;
if ( ! box2d_union_p(box, &boxbuf, box) ) return 0;
}
return 1;
}
/*
* Clone LWCOLLECTION object. POINTARRAY are not copied.
* Bbox is cloned if present in input.
*/
LWCOLLECTION *
lwcollection_clone(const LWCOLLECTION *g)
{
uint32 i;
LWCOLLECTION *ret = lwalloc(sizeof(LWCOLLECTION));
memcpy(ret, g, sizeof(LWCOLLECTION));
if ( g->ngeoms > 0 )
{
ret->geoms = lwalloc(sizeof(LWGEOM *)*g->ngeoms);
for (i=0; ingeoms; i++)
{
ret->geoms[i] = lwgeom_clone(g->geoms[i]);
}
if ( g->bbox ) ret->bbox = box2d_clone(g->bbox);
}
else
{
ret->bbox = NULL; /* empty collection */
ret->geoms = NULL;
}
return ret;
}
/*
* Add 'what' to this collection at position 'where'.
* where=0 == prepend
* where=-1 == append
* Returns a GEOMETRYCOLLECTION
*/
LWGEOM *
lwcollection_add(const LWCOLLECTION *to, uint32 where, const LWGEOM *what)
{
LWCOLLECTION *col;
LWGEOM **geoms;
uint32 i;
if ( where == -1 ) where = to->ngeoms;
else if ( where < -1 || where > to->ngeoms )
{
lwerror("lwcollection_add: add position out of range %d..%d",
-1, to->ngeoms);
return NULL;
}
/* dimensions compatibility are checked by caller */
/* Construct geoms array */
geoms = lwalloc(sizeof(LWGEOM *)*(to->ngeoms+1));
for (i=0; igeoms[i]);
lwgeom_dropSRID(geoms[i]);
lwgeom_drop_bbox(geoms[i]);
}
geoms[where] = lwgeom_clone(what);
lwgeom_dropSRID(geoms[where]);
lwgeom_drop_bbox(geoms[where]);
for (i=where; ingeoms; i++)
{
geoms[i+1] = lwgeom_clone(to->geoms[i]);
lwgeom_dropSRID(geoms[i+1]);
lwgeom_drop_bbox(geoms[i+1]);
}
col = lwcollection_construct(COLLECTIONTYPE,
to->SRID, NULL,
to->ngeoms+1, geoms);
return (LWGEOM *)col;
}
LWCOLLECTION *
lwcollection_segmentize2d(LWCOLLECTION *col, double dist)
{
unsigned int i;
LWGEOM **newgeoms;
if ( ! col->ngeoms ) return col;
newgeoms = lwalloc(sizeof(LWGEOM *)*col->ngeoms);
for (i=0; ingeoms; i++)
newgeoms[i] = lwgeom_segmentize2d(col->geoms[i], dist);
return lwcollection_construct(col->type, col->SRID, NULL,
col->ngeoms, newgeoms);
}
/* check for same geometry composition */
char
lwcollection_same(const LWCOLLECTION *c1, const LWCOLLECTION *c2)
{
unsigned int i, j;
unsigned int *hit;
LWDEBUG(2, "lwcollection_same called");
if ( TYPE_GETTYPE(c1->type) != TYPE_GETTYPE(c2->type) ) return 0;
if ( c1->ngeoms != c2->ngeoms ) return 0;
hit = lwalloc(sizeof(unsigned int)*c1->ngeoms);
memset(hit, 0, sizeof(unsigned int)*c1->ngeoms);
for (i=0; ingeoms; i++)
{
char found=0;
for (j=0; jngeoms; j++)
{
if ( hit[j] ) continue;
if ( lwgeom_same(c1->geoms[i], c2->geoms[j]) )
{
hit[j] = 1;
found=1;
break;
}
}
if ( ! found ) return 0;
}
return 1;
}
int lwcollection_ngeoms(const LWCOLLECTION *col)
{
int i;
int ngeoms = 0;
if( ! col ) {
lwerror("Null input geometry.");
return 0;
}
for( i = 0; i < col->ngeoms; i++ )
{
if( col->geoms[i]) {
switch(TYPE_GETTYPE(col->geoms[i]->type)) {
case POINTTYPE:
case LINETYPE:
case CIRCSTRINGTYPE:
case POLYGONTYPE:
ngeoms += 1;
break;
case MULTIPOINTTYPE:
case MULTILINETYPE:
case MULTICURVETYPE:
case MULTIPOLYGONTYPE:
ngeoms += col->ngeoms;
break;
case COLLECTIONTYPE:
ngeoms += lwcollection_ngeoms((LWCOLLECTION*)col->geoms[i]);
break;
}
}
}
return ngeoms;
}
/*
** Given a generic collection, return the "simplest" form.
** eg: GEOMETRYCOLLECTION(MULTILINESTRING()) => MULTELINESTRING()
** GEOMETRYCOLLECTION(MULTILINESTRING(), MULTILINESTRING(), POINT()) => GEOMETRYCOLLECTION(MULTILINESTRING(), MULTIPOINT())
**
** In general, if the subcomponents are homogeneous, return a properly typed collection.
** Otherwise, return a generic collection, with the subtypes in minimal typed collections.
LWCOLLECTION *lwcollection_homogenize(const LWCOLLECTION *c1)
{
TODO: pramsey
}
*/
/*
** Given a generic collection, extract and return just the desired types.
LWGEOM *lwcollection_extract(const LWCOLLECTION *col, char type)
{
LWGEOM **extracted_geoms;
extracted_geoms = lwalloc(sizeof(void*)*col->ngeoms);
extracted_curgeom = 0;
char reqtype = TYPE_GETTYPE(type);
for ( i = 0; i < col->ngeoms; i++ )
{
if( col->geoms[i] )
char geomtype = TYPE_GETTYPE(col->geoms[i]->type);
if ( geomtype == reqtype ) {
extracted_geoms[extracted_curgeom] = col->geoms[i];
extracted_curgeom++;
continue;
}
else {
if ( geomtype == COLLECTIONTYPE ) {
LWGEOM *colgeom;
colgeom = lwcollection_extract(col->geoms[i], type);
extracted_geoms[extracted_curgeom] = colgeom->geoms;
extracted_curgeom++;
if( colgeom->bbox ) lwfree(colgeom->bbox);
lwfree(colgeom);
continue;
}
}
TODO: pramsey
}
*/
void lwcollection_free(LWCOLLECTION *col)
{
int i;
if( col->bbox )
{
lwfree(col->bbox);
}
for ( i = 0; i < col->ngeoms; i++ )
{
if( col->geoms[i] ) {
switch( TYPE_GETTYPE(col->geoms[i]->type) )
{
case POINTTYPE:
lwpoint_free((LWPOINT*)col->geoms[i]);
break;
case LINETYPE:
lwline_free((LWLINE*)col->geoms[i]);
break;
case POLYGONTYPE:
lwpoly_free((LWPOLY*)col->geoms[i]);
break;
case MULTIPOINTTYPE:
lwmpoint_free((LWMPOINT*)col->geoms[i]);
break;
case MULTILINETYPE:
lwmline_free((LWMLINE*)col->geoms[i]);
break;
case MULTIPOLYGONTYPE:
lwmpoly_free((LWMPOLY*)col->geoms[i]);
break;
case COLLECTIONTYPE:
lwcollection_free((LWCOLLECTION*)col->geoms[i]);
break;
}
}
}
if( col->geoms )
{
lwfree(col->geoms);
}
lwfree(col);
};
BOX3D *lwcollection_compute_box3d(LWCOLLECTION *col)
{
int i;
BOX3D *boxfinal = NULL;
BOX3D *boxtmp1 = NULL;
BOX3D *boxtmp2 = NULL;
for ( i = 0; i < col->ngeoms; i++ )
{
if( col->geoms[i] ) {
switch( TYPE_GETTYPE(col->geoms[i]->type) )
{
case POINTTYPE:
boxtmp1 = lwpoint_compute_box3d((LWPOINT*)(col->geoms[i]));
break;
case LINETYPE:
boxtmp1 = lwline_compute_box3d((LWLINE*)(col->geoms[i]));
break;
case POLYGONTYPE:
boxtmp1 = lwpoly_compute_box3d((LWPOLY*)(col->geoms[i]));
break;
case CIRCSTRINGTYPE:
boxtmp1 = lwcircstring_compute_box3d((LWCIRCSTRING *)(col->geoms[i]));
break;
case COMPOUNDTYPE:
case CURVEPOLYTYPE:
case MULTIPOINTTYPE:
case MULTILINETYPE:
case MULTIPOLYGONTYPE:
case MULTICURVETYPE:
case MULTISURFACETYPE:
case COLLECTIONTYPE:
boxtmp1 = lwcollection_compute_box3d((LWCOLLECTION*)(col->geoms[i]));
boxfinal = box3d_union(boxtmp1, boxtmp2);
break;
}
boxtmp2 = boxfinal;
boxfinal = box3d_union(boxtmp1, boxtmp2);
if( boxtmp1 && boxtmp1 != boxfinal )
{
lwfree(boxtmp1);
boxtmp1 = NULL;
}
if( boxtmp2 && boxtmp2 != boxfinal )
{
lwfree(boxtmp2);
boxtmp2 = NULL;
}
}
}
return boxfinal;
}
================================================
FILE: src/liblwgeom/lwcompound.c
================================================
/**********************************************************************
* $Id: lwcompound.c 3639 2009-02-04 00:28:37Z pramsey $
*
* PostGIS - Spatial Types for PostgreSQL
* http://postgis.refractions.net
* Copyright 2001-2006 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.
*
**********************************************************************/
#include
#include
#include
#include "liblwgeom.h"
LWCOMPOUND *
lwcompound_deserialize(uchar *serialized)
{
LWCOMPOUND *result;
LWGEOM_INSPECTED *insp;
int type = lwgeom_getType(serialized[0]);
int i;
if(type != COMPOUNDTYPE)
{
lwerror("lwcompound_deserialize called on non compound: %d", type);
return NULL;
}
insp = lwgeom_inspect(serialized);
result = lwalloc(sizeof(LWCOMPOUND));
result->type = insp->type;
result->SRID = insp->SRID;
result->ngeoms = insp->ngeometries;
result->geoms = lwalloc(sizeof(LWGEOM *)*insp->ngeometries);
if(lwgeom_hasBBOX(serialized[0]))
{
result->bbox = lwalloc(sizeof(BOX2DFLOAT4));
memcpy(result->bbox, serialized + 1, sizeof(BOX2DFLOAT4));
}
else result->bbox = NULL;
for(i = 0; i < insp->ngeometries; i++)
{
if(lwgeom_getType(insp->sub_geoms[i][0]) == LINETYPE)
result->geoms[i] = (LWGEOM *)lwline_deserialize(insp->sub_geoms[i]);
else
result->geoms[i] = (LWGEOM *)lwcircstring_deserialize(insp->sub_geoms[i]);
if(TYPE_NDIMS(result->geoms[i]->type) != TYPE_NDIMS(result->type))
{
lwerror("Mixed dimensions (compound: %d, line/circularstring %d:%d)",
TYPE_NDIMS(result->type), i,
TYPE_NDIMS(result->geoms[i]->type)
);
lwfree(result);
return NULL;
}
}
return result;
}
/*
* Add 'what' to this string at position 'where'
* where=0 == prepend
* where=-1 == append
* Returns a COMPOUND or a GEOMETRYCOLLECTION
*/
LWGEOM *
lwcompound_add(const LWCOMPOUND *to, uint32 where, const LWGEOM *what)
{
LWCOLLECTION *col;
LWGEOM **geoms;
int newtype;
LWDEBUG(2, "lwcompound_add called.");
if(where != -1 && where != 0)
{
lwerror("lwcompound_add only supports 0 or -1 as a second argument, not %d", where);
return NULL;
}
/* dimensions compatibility are checked by caller */
/* Construct geoms array */
geoms = lwalloc(sizeof(LWGEOM *)*2);
if(where == -1) /* append */
{
geoms[0] = lwgeom_clone((LWGEOM *)to);
geoms[1] = lwgeom_clone(what);
}
else /* prepend */
{
geoms[0] = lwgeom_clone(what);
geoms[1] = lwgeom_clone((LWGEOM *)to);
}
/* reset SRID and wantbbox flag from component types */
geoms[0]->SRID = geoms[1]->SRID = -1;
TYPE_SETHASSRID(geoms[0]->type, 0);
TYPE_SETHASSRID(geoms[1]->type, 0);
TYPE_SETHASBBOX(geoms[0]->type, 0);
TYPE_SETHASBBOX(geoms[1]->type, 0);
/* Find appropriate geom type */
if(TYPE_GETTYPE(what->type) == LINETYPE || TYPE_GETTYPE(what->type) == CIRCSTRINGTYPE) newtype = COMPOUNDTYPE;
else newtype = COLLECTIONTYPE;
col = lwcollection_construct(newtype,
to->SRID, NULL, 2, geoms);
return (LWGEOM *)col;
}
================================================
FILE: src/liblwgeom/lwcurvepoly.c
================================================
/**********************************************************************
* $Id: lwcurvepoly.c 3639 2009-02-04 00:28:37Z pramsey $
*
* PostGIS - Spatial Types for PostgreSQL
* http://postgis.refractions.net
* Copyright 2001-2006 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.
*
**********************************************************************/
/* basic LWCURVEPOLY manipulation */
#include
#include
#include
#include "liblwgeom.h"
LWCURVEPOLY *
lwcurvepoly_deserialize(uchar *srl)
{
LWCURVEPOLY *result;
LWGEOM_INSPECTED *insp;
int type = lwgeom_getType(srl[0]);
int i;
LWDEBUG(3, "lwcurvepoly_deserialize called.");
if(type != CURVEPOLYTYPE)
{
lwerror("lwcurvepoly_deserialize called on NON curvepoly: %d",
type);
return NULL;
}
insp = lwgeom_inspect(srl);
result = lwalloc(sizeof(LWCURVEPOLY));
result->type = insp->type;
result->SRID = insp->SRID;
result->nrings = insp->ngeometries;
result->rings = lwalloc(sizeof(LWGEOM *)*insp->ngeometries);
if(lwgeom_hasBBOX(srl[0]))
{
result->bbox = lwalloc(sizeof(BOX2DFLOAT4));
memcpy(result->bbox, srl + 1, sizeof(BOX2DFLOAT4));
}
else result->bbox = NULL;
for(i = 0; i < insp->ngeometries; i++)
{
result->rings[i] = lwgeom_deserialize(insp->sub_geoms[i]);
if(lwgeom_getType(result->rings[i]->type) != CIRCSTRINGTYPE
&& lwgeom_getType(result->rings[i]->type) != LINETYPE)
{
lwerror("Only Circularstrings and Linestrings are currently supported as rings, not %s (%d)", lwgeom_typename(result->rings[i]->type), result->rings[i]->type);
lwfree(result);
lwfree(insp);
return NULL;
}
if(TYPE_NDIMS(result->rings[i]->type) != TYPE_NDIMS(result->type))
{
lwerror("Mixed dimensions (curvepoly %d, ring %d)",
TYPE_NDIMS(result->type), i,
TYPE_NDIMS(result->rings[i]->type));
lwfree(result);
lwfree(insp);
return NULL;
}
}
return result;
}
LWGEOM *
lwcurvepoly_add(const LWCURVEPOLY *to, uint32 where, const LWGEOM *what)
{
/* TODO */
lwerror("lwcurvepoly_add not yet implemented.");
return NULL;
}
================================================
FILE: src/liblwgeom/lwgeom.c
================================================
/**********************************************************************
* $Id: lwgeom.c 3812 2009-03-09 14:36:15Z pramsey $
*
* PostGIS - Spatial Types for PostgreSQL
* http://postgis.refractions.net
* Copyright 2001-2006 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.
*
**********************************************************************/
#include
#include
#include
#include "liblwgeom.h"
#include "wktparse.h"
LWGEOM *
lwgeom_deserialize(uchar *srl)
{
int type = lwgeom_getType(srl[0]);
LWDEBUGF(2, "lwgeom_deserialize got %d - %s", type, lwgeom_typename(type));
switch (type)
{
case POINTTYPE:
return (LWGEOM *)lwpoint_deserialize(srl);
case LINETYPE:
return (LWGEOM *)lwline_deserialize(srl);
case CIRCSTRINGTYPE:
return (LWGEOM *)lwcircstring_deserialize(srl);
case POLYGONTYPE:
return (LWGEOM *)lwpoly_deserialize(srl);
case MULTIPOINTTYPE:
return (LWGEOM *)lwmpoint_deserialize(srl);
case MULTILINETYPE:
return (LWGEOM *)lwmline_deserialize(srl);
case MULTIPOLYGONTYPE:
return (LWGEOM *)lwmpoly_deserialize(srl);
case COLLECTIONTYPE:
return (LWGEOM *)lwcollection_deserialize(srl);
case COMPOUNDTYPE:
return (LWGEOM *)lwcompound_deserialize(srl);
case CURVEPOLYTYPE:
return (LWGEOM *)lwcurvepoly_deserialize(srl);
case MULTICURVETYPE:
return (LWGEOM *)lwmcurve_deserialize(srl);
case MULTISURFACETYPE:
return (LWGEOM *)lwmsurface_deserialize(srl);
default:
lwerror("Unknown geometry type: %d", type);
return NULL;
}
}
size_t
lwgeom_serialize_size(LWGEOM *lwgeom)
{
int type = TYPE_GETTYPE(lwgeom->type);
LWDEBUGF(2, "lwgeom_serialize_size(%s) called", lwgeom_typename(type));
switch (type)
{
case POINTTYPE:
return lwpoint_serialize_size((LWPOINT *)lwgeom);
case LINETYPE:
return lwline_serialize_size((LWLINE *)lwgeom);
case POLYGONTYPE:
return lwpoly_serialize_size((LWPOLY *)lwgeom);
case CIRCSTRINGTYPE:
return lwcircstring_serialize_size((LWCIRCSTRING *)lwgeom);
case CURVEPOLYTYPE:
case COMPOUNDTYPE:
case MULTIPOINTTYPE:
case MULTILINETYPE:
case MULTICURVETYPE:
case MULTIPOLYGONTYPE:
case MULTISURFACETYPE:
case COLLECTIONTYPE:
return lwcollection_serialize_size((LWCOLLECTION *)lwgeom);
default:
lwerror("Unknown geometry type: %d", type);
return 0;
}
}
void
lwgeom_serialize_buf(LWGEOM *lwgeom, uchar *buf, size_t *retsize)
{
int type = TYPE_GETTYPE(lwgeom->type);
LWDEBUGF(2, "lwgeom_serialize_buf called with a %s",
lwgeom_typename(type));
switch (type)
{
case POINTTYPE:
lwpoint_serialize_buf((LWPOINT *)lwgeom, buf, retsize);
break;
case LINETYPE:
lwline_serialize_buf((LWLINE *)lwgeom, buf, retsize);
break;
case POLYGONTYPE:
lwpoly_serialize_buf((LWPOLY *)lwgeom, buf, retsize);
break;
case CIRCSTRINGTYPE:
lwcircstring_serialize_buf((LWCIRCSTRING *)lwgeom, buf, retsize);
break;
case CURVEPOLYTYPE:
case COMPOUNDTYPE:
case MULTIPOINTTYPE:
case MULTILINETYPE:
case MULTICURVETYPE:
case MULTIPOLYGONTYPE:
case MULTISURFACETYPE:
case COLLECTIONTYPE:
lwcollection_serialize_buf((LWCOLLECTION *)lwgeom, buf,
retsize);
break;
default:
lwerror("Unknown geometry type: %d", type);
return;
}
return;
}
uchar *
lwgeom_serialize(LWGEOM *lwgeom)
{
size_t size = lwgeom_serialize_size(lwgeom);
size_t retsize;
uchar *serialized = lwalloc(size);
lwgeom_serialize_buf(lwgeom, serialized, &retsize);
#if POSTGIS_DEBUG_LEVEL > 0
if ( retsize != size )
{
lwerror("lwgeom_serialize: computed size %d, returned size %d",
size, retsize);
}
#endif
return serialized;
}
/* Force Right-hand-rule on LWGEOM polygons */
void
lwgeom_forceRHR(LWGEOM *lwgeom)
{
LWCOLLECTION *coll;
int i;
switch (TYPE_GETTYPE(lwgeom->type))
{
case POLYGONTYPE:
lwpoly_forceRHR((LWPOLY *)lwgeom);
return;
case MULTIPOLYGONTYPE:
case COLLECTIONTYPE:
coll = (LWCOLLECTION *)lwgeom;
for (i=0; ingeoms; i++)
lwgeom_forceRHR(coll->geoms[i]);
return;
}
}
/* Reverse vertex order of LWGEOM */
void
lwgeom_reverse(LWGEOM *lwgeom)
{
int i;
LWCOLLECTION *col;
switch (TYPE_GETTYPE(lwgeom->type))
{
case LINETYPE:
lwline_reverse((LWLINE *)lwgeom);
return;
case POLYGONTYPE:
lwpoly_reverse((LWPOLY *)lwgeom);
return;
case MULTILINETYPE:
case MULTIPOLYGONTYPE:
case COLLECTIONTYPE:
col = (LWCOLLECTION *)lwgeom;
for (i=0; ingeoms; i++)
lwgeom_reverse(col->geoms[i]);
return;
}
}
BOX3D *lwgeom_compute_box3d(const LWGEOM *lwgeom)
{
if( ! lwgeom ) return NULL;
switch(TYPE_GETTYPE(lwgeom->type))
{
case POINTTYPE:
return lwpoint_compute_box3d((LWPOINT *)lwgeom);
case LINETYPE:
return lwline_compute_box3d((LWLINE *)lwgeom);
case CIRCSTRINGTYPE:
return lwcircstring_compute_box3d((LWCIRCSTRING *)lwgeom);
case POLYGONTYPE:
return lwpoly_compute_box3d((LWPOLY *)lwgeom);
case COMPOUNDTYPE:
case CURVEPOLYTYPE:
case MULTIPOINTTYPE:
case MULTILINETYPE:
case MULTICURVETYPE:
case MULTIPOLYGONTYPE:
case MULTISURFACETYPE:
case COLLECTIONTYPE:
return lwcollection_compute_box3d((LWCOLLECTION *)lwgeom);
}
/* Never get here, please. */
return NULL;
}
int
lwgeom_compute_box2d_p(LWGEOM *lwgeom, BOX2DFLOAT4 *buf)
{
LWDEBUGF(2, "lwgeom_compute_box2d_p called of %p of type %d.", lwgeom, TYPE_GETTYPE(lwgeom->type));
switch(TYPE_GETTYPE(lwgeom->type))
{
case POINTTYPE:
return lwpoint_compute_box2d_p((LWPOINT *)lwgeom, buf);
case LINETYPE:
return lwline_compute_box2d_p((LWLINE *)lwgeom, buf);
case CIRCSTRINGTYPE:
return lwcircstring_compute_box2d_p((LWCIRCSTRING *)lwgeom, buf);
case POLYGONTYPE:
return lwpoly_compute_box2d_p((LWPOLY *)lwgeom, buf);
case COMPOUNDTYPE:
case CURVEPOLYTYPE:
case MULTIPOINTTYPE:
case MULTILINETYPE:
case MULTICURVETYPE:
case MULTIPOLYGONTYPE:
case MULTISURFACETYPE:
case COLLECTIONTYPE:
return lwcollection_compute_box2d_p((LWCOLLECTION *)lwgeom, buf);
}
return 0;
}
/*
* dont forget to lwfree() result
*/
BOX2DFLOAT4 *
lwgeom_compute_box2d(LWGEOM *lwgeom)
{
BOX2DFLOAT4 *result = lwalloc(sizeof(BOX2DFLOAT4));
if ( lwgeom_compute_box2d_p(lwgeom, result) ) return result;
else {
lwfree(result);
return NULL;
}
}
LWPOINT *
lwgeom_as_lwpoint(LWGEOM *lwgeom)
{
if ( TYPE_GETTYPE(lwgeom->type) == POINTTYPE )
return (LWPOINT *)lwgeom;
else return NULL;
}
LWLINE *
lwgeom_as_lwline(LWGEOM *lwgeom)
{
if ( TYPE_GETTYPE(lwgeom->type) == LINETYPE )
return (LWLINE *)lwgeom;
else return NULL;
}
LWCIRCSTRING *
lwgeom_as_lwcircstring(LWGEOM *lwgeom)
{
if( TYPE_GETTYPE(lwgeom->type) == CIRCSTRINGTYPE )
return (LWCIRCSTRING *)lwgeom;
else return NULL;
}
LWPOLY *
lwgeom_as_lwpoly(LWGEOM *lwgeom)
{
if ( TYPE_GETTYPE(lwgeom->type) == POLYGONTYPE )
return (LWPOLY *)lwgeom;
else return NULL;
}
LWCOLLECTION *
lwgeom_as_lwcollection(LWGEOM *lwgeom)
{
if ( TYPE_GETTYPE(lwgeom->type) >= MULTIPOINTTYPE
&& TYPE_GETTYPE(lwgeom->type) <= COLLECTIONTYPE)
return (LWCOLLECTION *)lwgeom;
else return NULL;
}
LWMPOINT *
lwgeom_as_lwmpoint(LWGEOM *lwgeom)
{
if ( TYPE_GETTYPE(lwgeom->type) == MULTIPOINTTYPE )
return (LWMPOINT *)lwgeom;
else return NULL;
}
LWMLINE *
lwgeom_as_lwmline(LWGEOM *lwgeom)
{
if ( TYPE_GETTYPE(lwgeom->type) == MULTILINETYPE )
return (LWMLINE *)lwgeom;
else return NULL;
}
LWMPOLY *
lwgeom_as_lwmpoly(LWGEOM *lwgeom)
{
if ( TYPE_GETTYPE(lwgeom->type) == MULTIPOLYGONTYPE )
return (LWMPOLY *)lwgeom;
else return NULL;
}
LWGEOM *lwmpoly_as_lwgeom(LWMPOLY *obj) { return (LWGEOM *)obj; }
LWGEOM *lwmline_as_lwgeom(LWMLINE *obj) { return (LWGEOM *)obj; }
LWGEOM *lwmpoint_as_lwgeom(LWMPOINT *obj) { return (LWGEOM *)obj; }
LWGEOM *lwcollection_as_lwgeom(LWCOLLECTION *obj) { return (LWGEOM *)obj; }
LWGEOM *lwpoly_as_lwgeom(LWPOLY *obj) { return (LWGEOM *)obj; }
LWGEOM *lwline_as_lwgeom(LWLINE *obj) { return (LWGEOM *)obj; }
LWGEOM *lwpoint_as_lwgeom(LWPOINT *obj) { return (LWGEOM *)obj; }
void
lwgeom_release(LWGEOM *lwgeom)
{
uint32 i;
LWCOLLECTION *col;
#ifdef INTEGRITY_CHECKS
if ( ! lwgeom )
lwerror("lwgeom_release: someone called on 0x0");
#endif
/* Drop bounding box (always a copy) */
if ( lwgeom->bbox ) {
LWDEBUG(3, "lwgeom_release: releasing bbox.");
lwfree(lwgeom->bbox);
}
/* Collection */
if ( (col=lwgeom_as_lwcollection(lwgeom)) )
{
LWDEBUG(3, "lwgeom_release: Releasing collection.");
for (i=0; ingeoms; i++)
{
lwgeom_release(col->geoms[i]);
}
lwfree(lwgeom);
}
/* Single element */
else lwfree(lwgeom);
}
/* Clone an LWGEOM object. POINTARRAY are not copied. */
LWGEOM *
lwgeom_clone(const LWGEOM *lwgeom)
{
LWDEBUGF(2, "lwgeom_clone called with %p, %d", lwgeom, TYPE_GETTYPE(lwgeom->type));
switch(TYPE_GETTYPE(lwgeom->type))
{
case POINTTYPE:
return (LWGEOM *)lwpoint_clone((LWPOINT *)lwgeom);
case LINETYPE:
return (LWGEOM *)lwline_clone((LWLINE *)lwgeom);
case CIRCSTRINGTYPE:
return (LWGEOM *)lwcircstring_clone((LWCIRCSTRING *)lwgeom);
case POLYGONTYPE:
return (LWGEOM *)lwpoly_clone((LWPOLY *)lwgeom);
case COMPOUNDTYPE:
case CURVEPOLYTYPE:
case MULTIPOINTTYPE:
case MULTILINETYPE:
case MULTICURVETYPE:
case MULTIPOLYGONTYPE:
case MULTISURFACETYPE:
case COLLECTIONTYPE:
return (LWGEOM *)lwcollection_clone((LWCOLLECTION *)lwgeom);
default:
return NULL;
}
}
/*
* Add 'what' to 'to' at position 'where'
*
* where=0 == prepend
* where=-1 == append
* Appended-to LWGEOM gets a new type based on new condition.
* Mix of dimensions is not allowed (TODO: allow it?).
*/
LWGEOM *
lwgeom_add(const LWGEOM *to, uint32 where, const LWGEOM *what)
{
if ( TYPE_NDIMS(what->type) != TYPE_NDIMS(to->type) )
{
lwerror("lwgeom_add: mixed dimensions not supported");
return NULL;
}
LWDEBUGF(2, "lwgeom_add(%s, %d, %s) called",
lwgeom_typename(TYPE_GETTYPE(to->type)),
where,
lwgeom_typename(TYPE_GETTYPE(what->type)));
switch(TYPE_GETTYPE(to->type))
{
case POINTTYPE:
return (LWGEOM *)lwpoint_add((const LWPOINT *)to, where, what);
case LINETYPE:
return (LWGEOM *)lwline_add((const LWLINE *)to, where, what);
case CIRCSTRINGTYPE:
return (LWGEOM *)lwcircstring_add((const LWCIRCSTRING *)to, where, what);
case POLYGONTYPE:
return (LWGEOM *)lwpoly_add((const LWPOLY *)to, where, what);
case COMPOUNDTYPE:
return (LWGEOM *)lwcompound_add((const LWCOMPOUND *)to, where, what);
case CURVEPOLYTYPE:
return (LWGEOM *)lwcurvepoly_add((const LWCURVEPOLY *)to, where, what);
case MULTIPOINTTYPE:
return (LWGEOM *)lwmpoint_add((const LWMPOINT *)to,
where, what);
case MULTILINETYPE:
return (LWGEOM *)lwmline_add((const LWMLINE *)to,
where, what);
case MULTICURVETYPE:
return (LWGEOM *)lwmcurve_add((const LWMCURVE *)to,
where, what);
case MULTIPOLYGONTYPE:
return (LWGEOM *)lwmpoly_add((const LWMPOLY *)to,
where, what);
case MULTISURFACETYPE:
return (LWGEOM *)lwmsurface_add((const LWMSURFACE *)to,
where, what);
case COLLECTIONTYPE:
return (LWGEOM *)lwcollection_add(
(const LWCOLLECTION *)to, where, what);
default:
lwerror("lwgeom_add: unknown geometry type: %d",
TYPE_GETTYPE(to->type));
return NULL;
}
}
/*
* Return an alloced string
*/
char *
lwgeom_to_ewkt(LWGEOM *lwgeom, int flags)
{
LWGEOM_UNPARSER_RESULT lwg_unparser_result;
uchar *serialized = lwgeom_serialize(lwgeom);
int result;
if ( ! serialized ) {
lwerror("Error serializing geom %p", lwgeom);
}
result = unparse_WKT(&lwg_unparser_result, serialized, lwalloc, lwfree, flags);
lwfree(serialized);
return lwg_unparser_result.wkoutput;
}
/*
* Return an alloced string
*/
char *
lwgeom_to_hexwkb(LWGEOM *lwgeom, int flags, unsigned int byteorder)
{
LWGEOM_UNPARSER_RESULT lwg_unparser_result;
uchar *serialized = lwgeom_serialize(lwgeom);
int result;
result = unparse_WKB(&lwg_unparser_result, serialized, lwalloc, lwfree, flags, byteorder,1);
lwfree(serialized);
return lwg_unparser_result.wkoutput;
}
/*
* Return an alloced string
*/
uchar *
lwgeom_to_ewkb(LWGEOM *lwgeom, int flags, char byteorder, size_t *outsize)
{
LWGEOM_UNPARSER_RESULT lwg_unparser_result;
uchar *serialized = lwgeom_serialize(lwgeom);
int result;
/*
* We cast return to "unsigned" char as we are
* requesting a "binary" output, not HEX
* (last argument set to 0)
*/
result = unparse_WKB(&lwg_unparser_result, serialized, lwalloc, lwfree,
flags, byteorder, 0);
lwfree(serialized);
return (uchar *)lwg_unparser_result.wkoutput;
}
/*
* Make an LWGEOM object from a EWKB binary representation.
* Currently highly unoptimized as it:
* - convert EWKB to HEXEWKB
* - construct PG_LWGEOM
* - deserialize it
*/
LWGEOM *
lwgeom_from_ewkb(uchar *ewkb, int flags, size_t size)
{
size_t hexewkblen = size*2;
char *hexewkb;
long int i;
int result;
LWGEOM *ret;
LWGEOM_PARSER_RESULT lwg_parser_result;
/* "HEXify" the EWKB */
hexewkb = lwalloc(hexewkblen+1);
for (i=0; itype)),
lwgeom_typename(TYPE_GETTYPE(lwgeom2->type)));
if ( TYPE_GETTYPE(lwgeom1->type) != TYPE_GETTYPE(lwgeom2->type) )
{
LWDEBUG(3, " type differ");
return 0;
}
if ( TYPE_GETZM(lwgeom1->type) != TYPE_GETZM(lwgeom2->type) )
{
LWDEBUG(3, " ZM flags differ");
return 0;
}
/* Check boxes if both already computed */
if ( lwgeom1->bbox && lwgeom2->bbox )
{
/*lwnotice("bbox1:%p, bbox2:%p", lwgeom1->bbox, lwgeom2->bbox);*/
if ( ! box2d_same(lwgeom1->bbox, lwgeom2->bbox) )
{
LWDEBUG(3, " bounding boxes differ");
return 0;
}
}
/* geoms have same type, invoke type-specific function */
switch(TYPE_GETTYPE(lwgeom1->type))
{
case POINTTYPE:
return lwpoint_same((LWPOINT *)lwgeom1,
(LWPOINT *)lwgeom2);
case LINETYPE:
return lwline_same((LWLINE *)lwgeom1,
(LWLINE *)lwgeom2);
case POLYGONTYPE:
return lwpoly_same((LWPOLY *)lwgeom1,
(LWPOLY *)lwgeom2);
case MULTIPOINTTYPE:
case MULTILINETYPE:
case MULTIPOLYGONTYPE:
case COLLECTIONTYPE:
return lwcollection_same((LWCOLLECTION *)lwgeom1,
(LWCOLLECTION *)lwgeom2);
default:
lwerror("lwgeom_same: unsupported geometry type: %s",
lwgeom_typename(TYPE_GETTYPE(lwgeom1->type)));
return 0;
}
}
void
lwgeom_changed(LWGEOM *lwgeom)
{
if ( lwgeom->bbox ) lwfree(lwgeom->bbox);
lwgeom->bbox = NULL;
TYPE_SETHASBBOX(lwgeom->type, 0);
}
void
lwgeom_drop_bbox(LWGEOM *lwgeom)
{
if ( lwgeom->bbox ) lwfree(lwgeom->bbox);
lwgeom->bbox = NULL;
TYPE_SETHASBBOX(lwgeom->type, 0);
}
/*
* Ensure there's a box in the LWGEOM.
* If the box is already there just return,
* else compute it.
*/
void
lwgeom_add_bbox(LWGEOM *lwgeom)
{
if ( lwgeom->bbox ) return;
lwgeom->bbox = lwgeom_compute_box2d(lwgeom);
TYPE_SETHASBBOX(lwgeom->type, 1);
}
void
lwgeom_dropSRID(LWGEOM *lwgeom)
{
TYPE_SETHASSRID(lwgeom->type, 0);
lwgeom->SRID = -1;
}
LWGEOM *
lwgeom_segmentize2d(LWGEOM *lwgeom, double dist)
{
switch(TYPE_GETTYPE(lwgeom->type))
{
case LINETYPE:
return (LWGEOM *)lwline_segmentize2d((LWLINE *)lwgeom,
dist);
case POLYGONTYPE:
return (LWGEOM *)lwpoly_segmentize2d((LWPOLY *)lwgeom,
dist);
case MULTILINETYPE:
case MULTIPOLYGONTYPE:
case COLLECTIONTYPE:
return (LWGEOM *)lwcollection_segmentize2d(
(LWCOLLECTION *)lwgeom, dist);
default:
return lwgeom_clone(lwgeom);
}
}
void
lwgeom_longitude_shift(LWGEOM *lwgeom)
{
int i;
switch(TYPE_GETTYPE(lwgeom->type))
{
LWPOINT *point;
LWLINE *line;
LWPOLY *poly;
LWCOLLECTION *coll;
case POINTTYPE:
point = (LWPOINT *)lwgeom;
ptarray_longitude_shift(point->point);
return;
case LINETYPE:
line = (LWLINE *)lwgeom;
ptarray_longitude_shift(line->points);
return;
case POLYGONTYPE:
poly = (LWPOLY *)lwgeom;
for (i=0; inrings; i++)
ptarray_longitude_shift(poly->rings[i]);
return;
case MULTIPOINTTYPE:
case MULTILINETYPE:
case MULTIPOLYGONTYPE:
case COLLECTIONTYPE:
coll = (LWCOLLECTION *)lwgeom;
for (i=0; ingeoms; i++)
lwgeom_longitude_shift(coll->geoms[i]);
return;
default:
lwerror("%s:%d: unsupported geom type: %s",
__FILE__, __LINE__,
lwgeom_typename(TYPE_GETTYPE(lwgeom->type)));
}
}
int
lwgeom_contains_subgeoms(int type)
{
/* Return TRUE if the geometry may contain sub-geometries, i.e. it is a MULTI* or COMPOUNDCURVE */
switch(type)
{
case MULTIPOINTTYPE:
case MULTILINETYPE:
case MULTIPOLYGONTYPE:
case COLLECTIONTYPE:
case COMPOUNDTYPE:
case MULTICURVETYPE:
case MULTISURFACETYPE:
return -1;
break;
default:
return 0;
}
}
void lwgeom_free(LWGEOM *lwgeom) {
switch(TYPE_GETTYPE(lwgeom->type))
{
case POINTTYPE:
lwpoint_free((LWPOINT *)lwgeom);
break;
case LINETYPE:
lwline_free((LWLINE *)lwgeom);
break;
case POLYGONTYPE:
lwpoly_free((LWPOLY *)lwgeom);
break;
case MULTIPOINTTYPE:
lwmpoint_free((LWMPOINT *)lwgeom);
break;
case MULTILINETYPE:
lwmline_free((LWMLINE *)lwgeom);
break;
case MULTIPOLYGONTYPE:
lwmpoly_free((LWMPOLY *)lwgeom);
break;
case COLLECTIONTYPE:
lwcollection_free((LWCOLLECTION *)lwgeom);
break;
}
return;
};
================================================
FILE: src/liblwgeom/lwgeom_api.c
================================================
#include
#include
#include
#include
#include
#include "liblwgeom.h"
#include "wktparse.h"
/*
* Lower this to reduce integrity checks
*/
#define PARANOIA_LEVEL 1
/**********************************************************************
* BOX routines
*
* returns the float thats very close to the input, but <=
* handles the funny differences in float4 and float8 reps.
**********************************************************************/
/*
* These are taken from glibc
* some machines do *not* have these functions defined, so we give
* an implementation of them here.
*/
typedef int int32_tt;
typedef unsigned int u_int32_tt;
typedef union
{
float value;
u_int32_tt word;
} ieee_float_shape_type;
#define GET_FLOAT_WORD(i,d) \
do { \
ieee_float_shape_type gf_u; \
gf_u.value = (d); \
(i) = gf_u.word; \
} while (0)
#define SET_FLOAT_WORD(d,i) \
do { \
ieee_float_shape_type sf_u; \
sf_u.word = (i); \
(d) = sf_u.value; \
} while (0)
/*
* Returns the next smaller or next larger float
* from x (in direction of y).
*/
float
nextafterf_custom(float x, float y)
{
int32_tt hx,hy,ix,iy;
GET_FLOAT_WORD(hx,x);
GET_FLOAT_WORD(hy,y);
ix = hx&0x7fffffff; /* |x| */
iy = hy&0x7fffffff; /* |y| */
if((ix>0x7f800000) || /* x is nan */
(iy>0x7f800000)) /* y is nan */
return x+y;
if(x==y) return y; /* x=y, return y */
if(ix==0) { /* x == 0 */
SET_FLOAT_WORD(x,(hy&0x80000000)|1);/* return +-minsubnormal */
y = x*x;
if(y==x) return y; else return x; /* raise underflow flag */
}
if(hx>=0) { /* x > 0 */
if(hx>hy) { /* x > y, x -= ulp */
hx -= 1;
} else { /* x < y, x += ulp */
hx += 1;
}
} else { /* x < 0 */
if(hy>=0||hx>hy){ /* x < y, x -= ulp */
hx -= 1;
} else { /* x > y, x += ulp */
hx += 1;
}
}
hy = hx&0x7f800000;
if(hy>=0x7f800000) return x+x; /* overflow */
if(hy<0x00800000) { /* underflow */
y = x*x;
if(y!=x) { /* raise underflow flag */
SET_FLOAT_WORD(y,hx);
return y;
}
}
SET_FLOAT_WORD(x,hx);
return x;
}
float nextDown_f(double d)
{
float result = d;
if ( ((double) result) <=d)
return result;
return nextafterf_custom(result, result - 1000000);
}
/*
* Returns the float thats very close to the input, but >=.
* handles the funny differences in float4 and float8 reps.
*/
float
nextUp_f(double d)
{
float result = d;
if ( ((double) result) >=d)
return result;
return nextafterf_custom(result, result + 1000000);
}
/*
* Returns the double thats very close to the input, but <.
* handles the funny differences in float4 and float8 reps.
*/
double
nextDown_d(float d)
{
double result = d;
if ( result < d)
return result;
return nextafterf_custom(result, result - 1000000);
}
/*
* Returns the double thats very close to the input, but >
* handles the funny differences in float4 and float8 reps.
*/
double
nextUp_d(float d)
{
double result = d;
if ( result > d)
return result;
return nextafterf_custom(result, result + 1000000);
}
/*
* Convert BOX3D to BOX2D
* returned box2d is allocated with 'lwalloc'
*/
BOX2DFLOAT4 *
box3d_to_box2df(BOX3D *box)
{
BOX2DFLOAT4 *result = (BOX2DFLOAT4*) lwalloc(sizeof(BOX2DFLOAT4));
#if PARANOIA_LEVEL > 0
if (box == NULL)
{
lwerror("box3d_to_box2df got NUL box");
return NULL;
}
#endif
result->xmin = nextDown_f(box->xmin);
result->ymin = nextDown_f(box->ymin);
result->xmax = nextUp_f(box->xmax);
result->ymax = nextUp_f(box->ymax);
return result;
}
/*
* Convert BOX3D to BOX2D using pre-allocated BOX2D
* returned box2d is allocated with 'lwalloc'
* return 0 on error (NULL input box)
*/
int
box3d_to_box2df_p(BOX3D *box, BOX2DFLOAT4 *result)
{
#if PARANOIA_LEVEL > 0
if (box == NULL)
{
lwerror("box3d_to_box2df got NUL box");
return 0;
}
#endif
result->xmin = nextDown_f(box->xmin);
result->ymin = nextDown_f(box->ymin);
result->xmax = nextUp_f(box->xmax);
result->ymax = nextUp_f(box->ymax);
return 1;
}
/*
* Convert BOX2D to BOX3D
* zmin and zmax are set to NO_Z_VALUE
*/
BOX3D
box2df_to_box3d(BOX2DFLOAT4 *box)
{
BOX3D result;
#if PARANOIA_LEVEL > 0
if (box == NULL)
lwerror("box2df_to_box3d got NULL box");
#endif
result.xmin = box->xmin;
result.ymin = box->ymin;
result.xmax = box->xmax;
result.ymax = box->ymax;
result.zmin = result.zmax = NO_Z_VALUE;
return result;
}
/*
* Convert BOX2D to BOX3D, using pre-allocated BOX3D as output
* Z values are set to NO_Z_VALUE.
*/
void
box2df_to_box3d_p(BOX2DFLOAT4 *box, BOX3D *out)
{
if (box == NULL) return;
out->xmin = box->xmin;
out->ymin = box->ymin;
out->xmax = box->xmax;
out->ymax = box->ymax;
out->zmin = out->zmax = NO_Z_VALUE;
}
/*
* Returns a BOX3D that encloses b1 and b2
* box3d_union(NULL,A) --> A
* box3d_union(A,NULL) --> A
* box3d_union(A,B) --> A union B
*/
BOX3D *
box3d_union(BOX3D *b1, BOX3D *b2)
{
BOX3D *result;
result = lwalloc(sizeof(BOX3D));
if ( (b1 == NULL) && (b2 == NULL) )
{
return NULL;
}
if (b1 == NULL)
{
/*return b2 */
memcpy(result, b2, sizeof(BOX3D));
return result;
}
if (b2 == NULL)
{
/*return b1 */
memcpy(result, b1, sizeof(BOX3D));
return result;
}
if (b1->xmin < b2->xmin)
result->xmin = b1->xmin;
else
result->xmin = b2->xmin;
if (b1->ymin < b2->ymin)
result->ymin = b1->ymin;
else
result->ymin = b2->ymin;
if (b1->xmax > b2->xmax)
result->xmax = b1->xmax;
else
result->xmax = b2->xmax;
if (b1->ymax > b2->ymax)
result->ymax = b1->ymax;
else
result->ymax = b2->ymax;
if (b1->zmax > b2->zmax)
result->zmax = b1->zmax;
else
result->zmax = b2->zmax;
if (b1->zmin > b2->zmin)
result->zmin = b1->zmin;
else
result->zmin = b2->zmin;
return result;
}
/* Make given ubox a union of b1 and b2 */
int
box3d_union_p(BOX3D *b1, BOX3D *b2, BOX3D *ubox)
{
LWDEBUG(2, "box3d_union_p called: (xmin, xmax), (ymin, ymax), (zmin, zmax)");
LWDEBUGF(4, "b1: (%.16f, %.16f),(%.16f, %.16f),(%.16f, %.16f)", b1->xmin, b1->xmax, b1->ymin, b1->ymax, b1->zmin, b1->zmax);
LWDEBUGF(4, "b2: (%.16f, %.16f),(%.16f, %.16f),(%.16f, %.16f)", b2->xmin, b2->xmax, b2->ymin, b2->ymax, b2->zmin, b2->zmax);
if ( (b1 == NULL) && (b2 == NULL) )
{
return 0;
}
if (b1 == NULL)
{
memcpy(ubox, b2, sizeof(BOX3D));
return 1;
}
if (b2 == NULL)
{
memcpy(ubox, b1, sizeof(BOX3D));
return 1;
}
if (b1->xmin < b2->xmin)
ubox->xmin = b1->xmin;
else
ubox->xmin = b2->xmin;
if (b1->ymin < b2->ymin)
ubox->ymin = b1->ymin;
else
ubox->ymin = b2->ymin;
if (b1->xmax > b2->xmax)
ubox->xmax = b1->xmax;
else
ubox->xmax = b2->xmax;
if (b1->ymax > b2->ymax)
ubox->ymax = b1->ymax;
else
ubox->ymax = b2->ymax;
if (b1->zmax > b2->zmax)
ubox->zmax = b1->zmax;
else
ubox->zmax = b2->zmax;
if (b1->zmin < b2->zmin)
ubox->zmin = b1->zmin;
else
ubox->zmin = b2->zmin;
return 1;
}
#if 0 /* UNUSED */
/*
* Returns a pointer to internal storage, or NULL
* if the serialized form does not have a BBOX.
*/
BOX2DFLOAT4 *
getbox2d_internal(uchar *srl)
{
if (TYPE_HASBBOX(srl[0])) return (BOX2DFLOAT4 *)(srl+1);
else return NULL;
}
#endif /* UNUSED */
/*
* Same as getbox2d, but modifies box instead of returning result on the stack
*/
int
getbox2d_p(uchar *srl, BOX2DFLOAT4 *box)
{
uchar type = srl[0];
uchar *loc;
BOX3D box3d;
LWDEBUG(2, "getbox2d_p call");
loc = srl+1;
if (lwgeom_hasBBOX(type))
{
/*woot - this is easy */
LWDEBUG(4, "getbox2d_p: has box");
memcpy(box, loc, sizeof(BOX2DFLOAT4));
return 1;
}
LWDEBUG(4, "getbox2d_p: has no box - computing");
/* We have to actually compute it! */
if ( ! compute_serialized_box3d_p(srl, &box3d) ) return 0;
LWDEBUGF(4, "getbox2d_p: compute_serialized_box3d returned %p", box3d);
if ( ! box3d_to_box2df_p(&box3d, box) ) return 0;
LWDEBUG(4, "getbox2d_p: box3d converted to box2d");
return 1;
}
/************************************************************************
* POINTARRAY support functions
*
* TODO: should be moved to ptarray.c probably
*
************************************************************************/
/*
* Copies a point from the point array into the parameter point
* will set point's z=NO_Z_VALUE if pa is 2d
* will set point's m=NO_M_VALUE if pa is 3d or 2d
*
* NOTE: point is a real POINT3D *not* a pointer
*/
POINT4D
getPoint4d(const POINTARRAY *pa, int n)
{
POINT4D result;
getPoint4d_p(pa, n, &result);
return result;
}
/*
* Copies a point from the point array into the parameter point
* will set point's z=NO_Z_VALUE if pa is 2d
* will set point's m=NO_M_VALUE if pa is 3d or 2d
*
* NOTE: this will modify the point4d pointed to by 'point'.
*/
int
getPoint4d_p(const POINTARRAY *pa, int n, POINT4D *op)
{
uchar *ptr;
int zmflag;
#if PARANOIA_LEVEL > 0
if ( ! pa ) lwerror("getPoint4d_p: NULL pointarray");
if ( (n<0) || (n>=pa->npoints))
{
lwerror("getPoint4d_p: point offset out of range");
}
#endif
LWDEBUG(4, "getPoint4d_p called.");
/* Get a pointer to nth point offset and zmflag */
ptr=getPoint_internal(pa, n);
zmflag=TYPE_GETZM(pa->dims);
LWDEBUGF(4, "ptr %p, zmflag %d", ptr, zmflag);
switch (zmflag)
{
case 0: /* 2d */
memcpy(op, ptr, sizeof(POINT2D));
op->m=NO_M_VALUE;
op->z=NO_Z_VALUE;
break;
case 3: /* ZM */
memcpy(op, ptr, sizeof(POINT4D));
break;
case 2: /* Z */
memcpy(op, ptr, sizeof(POINT3DZ));
op->m=NO_M_VALUE;
break;
case 1: /* M */
memcpy(op, ptr, sizeof(POINT3DM));
op->m=op->z; /* we use Z as temporary storage */
op->z=NO_Z_VALUE;
break;
default:
lwerror("Unknown ZM flag ??");
}
return 1;
}
/*
* Copy a point from the point array into the parameter point
* will set point's z=NO_Z_VALUE if pa is 2d
* NOTE: point is a real POINT3DZ *not* a pointer
*/
POINT3DZ
getPoint3dz(const POINTARRAY *pa, int n)
{
POINT3DZ result;
getPoint3dz_p(pa, n, &result);
return result;
}
/*
* Copy a point from the point array into the parameter point
* will set point's z=NO_Z_VALUE if pa is 2d
*
* NOTE: point is a real POINT3DZ *not* a pointer
*/
POINT3DM
getPoint3dm(const POINTARRAY *pa, int n)
{
POINT3DM result;
getPoint3dm_p(pa, n, &result);
return result;
}
/*
* Copy a point from the point array into the parameter point
* will set point's z=NO_Z_VALUE if pa is 2d
*
* NOTE: this will modify the point3dz pointed to by 'point'.
*/
int
getPoint3dz_p(const POINTARRAY *pa, int n, POINT3DZ *op)
{
uchar *ptr;
#if PARANOIA_LEVEL > 0
if ( ! pa ) return 0;
if ( (n<0) || (n>=pa->npoints))
{
LWDEBUGF(4, "%d out of numpoint range (%d)", n, pa->npoints);
return 0; /*error */
}
#endif
LWDEBUGF(2, "getPoint3dz_p called on array of %d-dimensions / %u pts",
TYPE_NDIMS(pa->dims), pa->npoints);
/* Get a pointer to nth point offset */
ptr=getPoint_internal(pa, n);
/*
* if input POINTARRAY has the Z, it is always
* at third position so make a single copy
*/
if ( TYPE_HASZ(pa->dims) )
{
memcpy(op, ptr, sizeof(POINT3DZ));
}
/*
* Otherwise copy the 2d part and initialize
* Z to NO_Z_VALUE
*/
else
{
memcpy(op, ptr, sizeof(POINT2D));
op->z=NO_Z_VALUE;
}
return 1;
}
/*
* Copy a point from the point array into the parameter point
* will set point's m=NO_Z_VALUE if pa has no M
*
* NOTE: this will modify the point3dm pointed to by 'point'.
*/
int
getPoint3dm_p(const POINTARRAY *pa, int n, POINT3DM *op)
{
uchar *ptr;
int zmflag;
#if PARANOIA_LEVEL > 0
if ( ! pa ) return 0;
if ( (n<0) || (n>=pa->npoints))
{
lwerror("%d out of numpoint range (%d)", n, pa->npoints);
return 0; /*error */
}
#endif
LWDEBUGF(2, "getPoint3dm_p(%d) called on array of %d-dimensions / %u pts",
n, TYPE_NDIMS(pa->dims), pa->npoints);
/* Get a pointer to nth point offset and zmflag */
ptr=getPoint_internal(pa, n);
zmflag=TYPE_GETZM(pa->dims);
/*
* if input POINTARRAY has the M and NO Z,
* we can issue a single memcpy
*/
if ( zmflag == 1 )
{
memcpy(op, ptr, sizeof(POINT3DM));
return 1;
}
/*
* Otherwise copy the 2d part and
* initialize M to NO_M_VALUE
*/
memcpy(op, ptr, sizeof(POINT2D));
/*
* Then, if input has Z skip it and
* copy next double, otherwise initialize
* M to NO_M_VALUE
*/
if ( zmflag == 3 )
{
ptr+=sizeof(POINT3DZ);
memcpy(&(op->m), ptr, sizeof(double));
}
else
{
op->m=NO_M_VALUE;
}
return 1;
}
/*
* Copy a point from the point array into the parameter point
* z value (if present) is not returned.
*
* NOTE: point is a real POINT2D *not* a pointer
*/
POINT2D
getPoint2d(const POINTARRAY *pa, int n)
{
POINT2D result;
getPoint2d_p(pa, n, &result);
return result;
}
/*
* Copy a point from the point array into the parameter point
* z value (if present) is not returned.
*
* NOTE: this will modify the point2d pointed to by 'point'.
*/
int
getPoint2d_p(const POINTARRAY *pa, int n, POINT2D *point)
{
#if PARANOIA_LEVEL > 0
if ( ! pa ) return 0;
if ( (n<0) || (n>=pa->npoints))
{
lwerror("getPoint2d_p: point offset out of range");
return 0; /*error */
}
#endif
/* this does x,y */
memcpy(point, getPoint_internal(pa, n), sizeof(POINT2D));
return 1;
}
/*
* set point N to the given value
* NOTE that the pointarray can be of any
* dimension, the appropriate ordinate values
* will be extracted from it
*
*/
void
setPoint4d(POINTARRAY *pa, int n, POINT4D *p4d)
{
uchar *ptr=getPoint_internal(pa, n);
switch ( TYPE_GETZM(pa->dims) )
{
case 3:
memcpy(ptr, p4d, sizeof(POINT4D));
break;
case 2:
memcpy(ptr, p4d, sizeof(POINT3DZ));
break;
case 1:
memcpy(ptr, p4d, sizeof(POINT2D));
ptr+=sizeof(POINT2D);
memcpy(ptr, &(p4d->m), sizeof(double));
break;
case 0:
memcpy(ptr, p4d, sizeof(POINT2D));
break;
}
}
/*
* Get a pointer to nth point of a POINTARRAY.
* You cannot safely cast this to a real POINT, due to memory alignment
* constraints. Use getPoint*_p for that.
*/
uchar *
getPoint_internal(const POINTARRAY *pa, int n)
{
int size;
#if PARANOIA_LEVEL > 0
if ( pa == NULL ) {
lwerror("getPoint got NULL pointarray");
return NULL;
}
if ( (n<0) || (n>=pa->npoints))
{
return NULL; /*error */
}
#endif
size = pointArray_ptsize(pa);
return &(pa->serialized_pointlist[size*n]);
}
/*
* Constructs a POINTARRAY.
*
* NOTE: points is *not* copied, so be careful about modification
* (can be aligned/missaligned).
*
* NOTE: ndims is descriptive - it describes what type of data 'points'
* points to. No data conversion is done.
*/
POINTARRAY *
pointArray_construct(uchar *points, char hasz, char hasm, uint32 npoints)
{
POINTARRAY *pa;
LWDEBUG(2, "pointArray_construct called.");
pa = (POINTARRAY*)lwalloc(sizeof(POINTARRAY));
pa->dims = 0;
TYPE_SETZM(pa->dims, hasz?1:0, hasm?1:0);
pa->npoints = npoints;
pa->serialized_pointlist = points;
LWDEBUGF(4, "pointArray_construct returning %p", pa);
return pa;
}
/*
* Size of point represeneted in the POINTARRAY
* 16 for 2d, 24 for 3d, 32 for 4d
*/
int
pointArray_ptsize(const POINTARRAY *pa)
{
LWDEBUGF(2, "pointArray_ptsize: TYPE_NDIMS(pa->dims)=%x",TYPE_NDIMS(pa->dims));
return sizeof(double)*TYPE_NDIMS(pa->dims);
}
/***************************************************************************
* Basic type handling
***************************************************************************/
/* Returns true if this type says it has an SRID (S bit set) */
char
lwgeom_hasSRID(uchar type)
{
return TYPE_HASSRID(type);
}
/* Returns either 2,3, or 4 -- 2=2D, 3=3D, 4=4D */
int
lwgeom_ndims(uchar type)
{
return TYPE_NDIMS(type);
}
/* has M ? */
int lwgeom_hasM(uchar type)
{
return ( (type & 0x10) >>4);
}
/* has Z ? */
int lwgeom_hasZ(uchar type)
{
return ( (type & 0x20) >>5);
}
/* get base type (ie. POLYGONTYPE) */
int
lwgeom_getType(uchar type)
{
LWDEBUGF(2, "lwgeom_getType %d", type);
return (type & 0x0F);
}
/* Construct a type (hasBOX=false) */
uchar
lwgeom_makeType(char hasz, char hasm, char hasSRID, int type)
{
return lwgeom_makeType_full(hasz, hasm, hasSRID, type, 0);
}
/*
* Construct a type
* TODO: needs to be expanded to accept explicit MZ type
*/
uchar
lwgeom_makeType_full(char hasz, char hasm, char hasSRID, int type, char hasBBOX)
{
uchar result = (char)type;
TYPE_SETZM(result, hasz, hasm);
TYPE_SETHASSRID(result, hasSRID);
TYPE_SETHASBBOX(result, hasBBOX);
return result;
}
/* Returns true if there's a bbox in this LWGEOM (B bit set) */
char
lwgeom_hasBBOX(uchar type)
{
return TYPE_HASBBOX(type);
}
/*****************************************************************************
* Basic sub-geometry types
*****************************************************************************/
/* handle missaligned unsigned int32 data */
uint32
lw_get_uint32(const uchar *loc)
{
uint32 result;
memcpy(&result, loc, sizeof(uint32));
return result;
}
/* handle missaligned signed int32 data */
int32
lw_get_int32(const uchar *loc)
{
int32 result;
memcpy(&result,loc, sizeof(int32));
return result;
}
/*************************************************************************
*
* Multi-geometry support
*
* Note - for a simple type (ie. point), this will have
* sub_geom[0] = serialized_form.
*
* For multi-geomtries sub_geom[0] will be a few bytes
* into the serialized form.
*
* This function just computes the length of each sub-object and
* pre-caches this info.
*
* For a geometry collection of multi* geometries, you can inspect
* the sub-components
* as well.
*/
LWGEOM_INSPECTED *
lwgeom_inspect(const uchar *serialized_form)
{
LWGEOM_INSPECTED *result = lwalloc(sizeof(LWGEOM_INSPECTED));
uchar typefl = (uchar)serialized_form[0];
uchar type;
uchar **sub_geoms;
const uchar *loc;
int t;
LWDEBUGF(2, "lwgeom_inspect: serialized@%p", serialized_form);
if (serialized_form == NULL)
return NULL;
result->serialized_form = serialized_form;
result->type = (uchar) serialized_form[0];
result->SRID = -1; /* assume */
type = lwgeom_getType(typefl);
loc = serialized_form+1;
if ( lwgeom_hasBBOX(typefl) )
{
loc += sizeof(BOX2DFLOAT4);
}
if ( lwgeom_hasSRID(typefl) )
{
result->SRID = lw_get_int32(loc);
loc += 4;
}
if ( (type==POINTTYPE) || (type==LINETYPE) || (type==POLYGONTYPE) || (type == CIRCSTRINGTYPE))
{
/* simple geometry (point/line/polygon/circstring)-- not multi! */
result->ngeometries = 1;
sub_geoms = (uchar**) lwalloc(sizeof(char*));
sub_geoms[0] = (uchar *)serialized_form;
result->sub_geoms = (uchar **)sub_geoms;
return result;
}
/* its a GeometryCollection or multi* geometry */
result->ngeometries = lw_get_uint32(loc);
loc +=4;
LWDEBUGF(3, "lwgeom_inspect: geometry is a collection of %d elements",
result->ngeometries);
if ( ! result->ngeometries ) return result;
sub_geoms = lwalloc(sizeof(uchar*) * result->ngeometries );
result->sub_geoms = sub_geoms;
sub_geoms[0] = (uchar *)loc;
LWDEBUGF(3, "subgeom[0] @ %p (+%d)", sub_geoms[0], sub_geoms[0]-serialized_form);
for (t=1;tngeometries; t++)
{
/* -1 = entire object */
int sub_length = lwgeom_size_subgeom(sub_geoms[t-1], -1);
sub_geoms[t] = sub_geoms[t-1] + sub_length;
LWDEBUGF(3, "subgeom[%d] @ %p (+%d)",
t, sub_geoms[t], sub_geoms[0]-serialized_form);
}
return result;
}
/*
* 1st geometry has geom_number = 0
* if the actual sub-geometry isnt a POINT, null is returned (see _gettype()).
* if there arent enough geometries, return null.
* this is fine to call on a point (with geom_num=0),
* multipoint or geometrycollection
*/
LWPOINT *
lwgeom_getpoint(uchar *serialized_form, int geom_number)
{
int type = lwgeom_getType((uchar)serialized_form[0]);
uchar *sub_geom;
if ((type == POINTTYPE) && (geom_number == 0))
{
/* Be nice and do as they want instead of what they say */
return lwpoint_deserialize(serialized_form);
}
if ((type != MULTIPOINTTYPE) && (type != COLLECTIONTYPE) )
return NULL;
sub_geom = lwgeom_getsubgeometry(serialized_form, geom_number);
if (sub_geom == NULL)
return NULL;
type = lwgeom_getType(sub_geom[0]);
if (type != POINTTYPE)
return NULL;
return lwpoint_deserialize(sub_geom);
}
/*
* 1st geometry has geom_number = 0
* if the actual sub-geometry isnt a POINT, null is returned (see _gettype()).
* if there arent enough geometries, return null.
* this is fine to call on a point (with geom_num=0), multipoint
* or geometrycollection
*/
LWPOINT *lwgeom_getpoint_inspected(LWGEOM_INSPECTED *inspected, int geom_number)
{
uchar *sub_geom;
uchar type;
sub_geom = lwgeom_getsubgeometry_inspected(inspected, geom_number);
if (sub_geom == NULL) return NULL;
type = lwgeom_getType(sub_geom[0]);
if (type != POINTTYPE) return NULL;
return lwpoint_deserialize(sub_geom);
}
/*
* 1st geometry has geom_number = 0
* if the actual geometry isnt a LINE, null is returned (see _gettype()).
* if there arent enough geometries, return null.
* this is fine to call on a line, multiline or geometrycollection
*/
LWLINE *
lwgeom_getline(uchar *serialized_form, int geom_number)
{
uchar type = lwgeom_getType( (uchar) serialized_form[0]);
uchar *sub_geom;
if ((type == LINETYPE) && (geom_number == 0))
{
/* be nice and do as they want instead of what they say */
return lwline_deserialize(serialized_form);
}
if ((type != MULTILINETYPE) && (type != COLLECTIONTYPE) )
return NULL;
sub_geom = lwgeom_getsubgeometry(serialized_form, geom_number);
if (sub_geom == NULL) return NULL;
type = lwgeom_getType((uchar) sub_geom[0]);
if (type != LINETYPE) return NULL;
return lwline_deserialize(sub_geom);
}
/*
* 1st geometry has geom_number = 0
* if the actual geometry isnt a LINE, null is returned (see _gettype()).
* if there arent enough geometries, return null.
* this is fine to call on a line, multiline or geometrycollection
*/
LWLINE *
lwgeom_getline_inspected(LWGEOM_INSPECTED *inspected, int geom_number)
{
uchar *sub_geom;
uchar type;
sub_geom = lwgeom_getsubgeometry_inspected(inspected, geom_number);
if (sub_geom == NULL) return NULL;
type = lwgeom_getType((uchar) sub_geom[0]);
if (type != LINETYPE) return NULL;
return lwline_deserialize(sub_geom);
}
/*
* 1st geometry has geom_number = 0
* if the actual geometry isnt a POLYGON, null is returned (see _gettype()).
* if there arent enough geometries, return null.
* this is fine to call on a polygon, multipolygon or geometrycollection
*/
LWPOLY *
lwgeom_getpoly(uchar *serialized_form, int geom_number)
{
uchar type = lwgeom_getType((uchar)serialized_form[0]);
uchar *sub_geom;
if ((type == POLYGONTYPE) && (geom_number == 0))
{
/* Be nice and do as they want instead of what they say */
return lwpoly_deserialize(serialized_form);
}
if ((type != MULTIPOLYGONTYPE) && (type != COLLECTIONTYPE) )
return NULL;
sub_geom = lwgeom_getsubgeometry(serialized_form, geom_number);
if (sub_geom == NULL) return NULL;
type = lwgeom_getType(sub_geom[0]);
if (type != POLYGONTYPE) return NULL;
return lwpoly_deserialize(sub_geom);
}
/*
* 1st geometry has geom_number = 0
* if the actual geometry isnt a POLYGON, null is returned (see _gettype()).
* if there arent enough geometries, return null.
* this is fine to call on a polygon, multipolygon or geometrycollection
*/
LWPOLY *
lwgeom_getpoly_inspected(LWGEOM_INSPECTED *inspected, int geom_number)
{
uchar *sub_geom;
uchar type;
sub_geom = lwgeom_getsubgeometry_inspected(inspected, geom_number);
if (sub_geom == NULL) return NULL;
type = lwgeom_getType(sub_geom[0]);
if (type != POLYGONTYPE) return NULL;
return lwpoly_deserialize(sub_geom);
}
/*
* 1st geometry has geom_number = 0
* if the actual geometry isnt a CIRCULARSTRING, null is returned (see _gettype()).
* if there arent enough geometries, return null.
* this is fine to call on a circularstring
*/
LWCIRCSTRING *
lwgeom_getcircstring_inspected(LWGEOM_INSPECTED *inspected, int geom_number)
{
uchar *sub_geom;
uchar type;
sub_geom = lwgeom_getsubgeometry_inspected(inspected, geom_number);
if (sub_geom == NULL) return NULL;
type = lwgeom_getType(sub_geom[0]);
if (type != CIRCSTRINGTYPE) return NULL;
return lwcircstring_deserialize(sub_geom);
}
/*
* 1st geometry has geom_number = 0
* if there arent enough geometries, return null.
*/
LWGEOM *lwgeom_getgeom_inspected(LWGEOM_INSPECTED *inspected, int geom_number)
{
uchar *sub_geom;
uchar type;
sub_geom = lwgeom_getsubgeometry_inspected(inspected, geom_number);
if (sub_geom == NULL) return NULL;
type = lwgeom_getType(sub_geom[0]);
return lwgeom_deserialize(sub_geom);
}
/*
* This gets the serialized form of a sub-geometry
*
* 1st geometry has geom_number = 0
* if this isnt a multi* geometry, and geom_number ==0 then it returns
* itself.
*
* Returns null on problems.
*
* In the future this is how you would access a muli* portion of a
* geometry collection.
* GEOMETRYCOLLECTION(MULTIPOINT(0 0, 1 1), LINESTRING(0 0, 1 1))
* ie. lwgeom_getpoint( lwgeom_getsubgeometry( serialized, 0), 1)
* --> POINT(1 1)
*
* You can inspect the sub-geometry as well if you wish.
*
*/
uchar *
lwgeom_getsubgeometry(const uchar *serialized_form, int geom_number)
{
uchar *result;
/*major cheat!! */
LWGEOM_INSPECTED *inspected = lwgeom_inspect(serialized_form);
result = lwgeom_getsubgeometry_inspected(inspected, geom_number);
lwinspected_release(inspected);
return result;
}
uchar *
lwgeom_getsubgeometry_inspected(LWGEOM_INSPECTED *inspected, int geom_number)
{
if ((geom_number <0) || (geom_number >= inspected->ngeometries) )
{
lwerror("lwgeom_getsubgeometry_inspected: geom_number out of range");
return NULL;
}
return inspected->sub_geoms[geom_number];
}
/*
* 1st geometry has geom_number = 0
* use geom_number = -1 to find the actual type of the serialized form.
* ie lwgeom_gettype( <'MULTIPOINT(0 0, 1 1)'>, -1)
* --> multipoint
* ie lwgeom_gettype( <'MULTIPOINT(0 0, 1 1)'>, 0)
* --> point
* gets the 8bit type of the geometry at location geom_number
*/
uchar
lwgeom_getsubtype(uchar *serialized_form, int geom_number)
{
char result;
/*major cheat!! */
LWGEOM_INSPECTED *inspected = lwgeom_inspect(serialized_form);
result = lwgeom_getsubtype_inspected(inspected, geom_number);
lwinspected_release(inspected);
return result;
}
uchar
lwgeom_getsubtype_inspected(LWGEOM_INSPECTED *inspected, int geom_number)
{
if ((geom_number <0) || (geom_number >= inspected->ngeometries) )
return 99;
return inspected->sub_geoms[geom_number][0]; /* 1st byte is type */
}
/*
* How many sub-geometries are there?
* for point,line,polygon will return 1.
*/
int
lwgeom_getnumgeometries(uchar *serialized_form)
{
uchar type = lwgeom_getType((uchar)serialized_form[0]);
uchar *loc;
if ( (type==POINTTYPE) || (type==LINETYPE) || (type==POLYGONTYPE) ||
(type==CIRCSTRINGTYPE) || (type==COMPOUNDTYPE) || (type==CURVEPOLYTYPE) )
{
return 1;
}
loc = serialized_form+1;
if (lwgeom_hasBBOX((uchar) serialized_form[0]))
{
loc += sizeof(BOX2DFLOAT4);
}
if (lwgeom_hasSRID((uchar) serialized_form[0]) )
{
loc += 4;
}
/* its a GeometryCollection or multi* geometry */
return lw_get_uint32(loc);
}
/*
* How many sub-geometries are there?
* for point,line,polygon will return 1.
*/
int
lwgeom_getnumgeometries_inspected(LWGEOM_INSPECTED *inspected)
{
return inspected->ngeometries;
}
/*
* Set finalType to COLLECTIONTYPE or 0 (0 means choose a best type)
* (ie. give it 2 points and ask it to be a multipoint)
* use SRID=-1 for unknown SRID (will have 8bit type's S = 0)
* all subgeometries must have the same SRID
* if you want to construct an inspected, call this then inspect the result...
*/
uchar *
lwgeom_serialized_construct(int SRID, int finalType, char hasz, char hasm,
int nsubgeometries, uchar **serialized_subs)
{
uint32 *lengths;
int t;
int total_length = 0;
char type = (char)-1;
char this_type = -1;
uchar *result;
uchar *loc;
if (nsubgeometries == 0)
return lwgeom_constructempty(SRID, hasz, hasm);
lengths = lwalloc(sizeof(int32) * nsubgeometries);
for (t=0;t, -1)
* --> size of the multipoint
* ie lwgeom_gettype( <'MULTIPOINT(0 0, 1 1)'>, 0)
* --> size of the point
* take a geometry, and find its length
*/
size_t
lwgeom_size(const uchar *serialized_form)
{
uchar type = lwgeom_getType((uchar) serialized_form[0]);
int t;
const uchar *loc;
uint32 ngeoms;
int sub_size;
int result = 1; /* type */
LWDEBUG(2, "lwgeom_size called");
if (type == POINTTYPE)
{
LWDEBUG(3, "lwgeom_size: is a point");
return lwgeom_size_point(serialized_form);
}
else if (type == LINETYPE)
{
LWDEBUG(3, "lwgeom_size: is a line");
return lwgeom_size_line(serialized_form);
}
else if(type == CIRCSTRINGTYPE)
{
LWDEBUG(3, "lwgeom_size: is a circularstring");
return lwgeom_size_circstring(serialized_form);
}
else if (type == POLYGONTYPE)
{
LWDEBUG(3, "lwgeom_size: is a polygon");
return lwgeom_size_poly(serialized_form);
}
else if (type == COMPOUNDTYPE)
{
LWDEBUG(3, "lwgeom_size: is a compound curve");
}
if ( type == 0 )
{
lwerror("lwgeom_size called with unknown-typed serialized geometry");
return 0;
}
/*
* Handle all the multi* and geometrycollections the same
*
* NOTE: for a geometry collection of GC of GC of GC we will
* be recursing...
*/
LWDEBUGF(3, "lwgeom_size called on a geoemtry with type %d", type);
loc = serialized_form+1;
if (lwgeom_hasBBOX((uchar) serialized_form[0]))
{
LWDEBUG(3, "lwgeom_size: has bbox");
loc += sizeof(BOX2DFLOAT4);
result +=sizeof(BOX2DFLOAT4);
}
if (lwgeom_hasSRID( (uchar) serialized_form[0]) )
{
LWDEBUG(3, "lwgeom_size: has srid");
result +=4;
loc +=4;
}
ngeoms = lw_get_uint32(loc);
loc +=4;
result += 4; /* numgeoms */
LWDEBUGF(3, "lwgeom_size called on a geoemtry with %d elems (result so far: %d)", ngeoms, result);
for (t=0;tsub_geoms[geom_number]);
}
int
compute_serialized_box3d_p(uchar *srl, BOX3D *out)
{
BOX3D *box = compute_serialized_box3d(srl);
if ( ! box ) return 0;
out->xmin = box->xmin;
out->ymin = box->ymin;
out->zmin = box->zmin;
out->xmax = box->xmax;
out->ymax = box->ymax;
out->zmax = box->zmax;
lwfree(box);
return 1;
}
/*
* Compute bounding box of a serialized LWGEOM, even if it is
* already cached. The computed BOX2DFLOAT4 is stored at
* the given location, the function returns 0 is the geometry
* does not have a bounding box (EMPTY GEOM)
*/
int
compute_serialized_box2d_p(uchar *srl, BOX2DFLOAT4 *out)
{
BOX3D *result = compute_serialized_box3d(srl);
if ( ! result ) return 0;
out->xmin = result->xmin;
out->xmax = result->xmax;
out->ymin = result->ymin;
out->ymax = result->ymax;
lwfree(result);
return 1;
}
/*
* Don't forget to lwfree() result !
*/
BOX3D *
compute_serialized_box3d(uchar *srl)
{
int type = lwgeom_getType(srl[0]);
int t;
uchar *loc = srl;
uint32 nelems;
BOX3D *result;
BOX3D b1;
int sub_size;
char nboxes=0;
LWDEBUGF(2, "compute_serialized_box3d called on type %d", type);
loc += 1; /* Move past the 'type' byte. */
if (lwgeom_hasBBOX(srl[0]))
{
loc += sizeof(BOX2DFLOAT4); /* Move past the bbox */
}
if (lwgeom_hasSRID(srl[0]) )
{
loc +=4; /* Move past the SRID */
}
if (type == POINTTYPE)
{
LWPOINT *pt = lwpoint_deserialize(srl);
LWDEBUG(3, "compute_serialized_box3d: lwpoint deserialized");
result = lwpoint_compute_box3d(pt);
LWDEBUG(3, "compute_serialized_box3d: bbox found");
lwpoint_free(pt);
return result;
}
/*
** For items that have elements (everything except points),
** nelems == 0 => EMPTY geometry
*/
nelems = lw_get_uint32(loc);
if ( nelems == 0 ) return NULL;
if (type == LINETYPE)
{
LWLINE *line = lwline_deserialize(srl);
result = lwline_compute_box3d(line);
lwline_free(line);
return result;
}
else if (type == CIRCSTRINGTYPE)
{
LWCIRCSTRING *curve = lwcircstring_deserialize(srl);
result = lwcircstring_compute_box3d(curve);
lwcircstring_free(curve);
return result;
}
else if (type == POLYGONTYPE)
{
LWPOLY *poly = lwpoly_deserialize(srl);
result = lwpoly_compute_box3d(poly);
lwpoly_free(poly);
return result;
}
if ( ! ( type == MULTIPOINTTYPE || type == MULTILINETYPE ||
type == MULTIPOLYGONTYPE || type == COLLECTIONTYPE ||
type == COMPOUNDTYPE || type == CURVEPOLYTYPE ||
type == MULTICURVETYPE || type == MULTISURFACETYPE) )
{
lwnotice("compute_serialized_box3d called on unknown type %d", type);
return NULL;
}
loc += 4;
/* each sub-type */
result = NULL;
for (t=0; t= 3
printBOX3D(&b1);
#endif
if (result)
{
nboxes += box3d_union_p(result, &b1, result);
}
else
{
result = lwalloc(sizeof(BOX3D));
memcpy(result, &b1, sizeof(BOX3D));
}
}
sub_size = lwgeom_size(loc);
loc += sub_size;
}
return result;
}
/****************************************************************
* memory management
*
* these only delete the memory associated
* directly with the structure - NOT the stuff pointing into
* the original de-serialized info
*
****************************************************************/
void
lwinspected_release(LWGEOM_INSPECTED *inspected)
{
if ( inspected->ngeometries )
lwfree(inspected->sub_geoms);
lwfree(inspected);
}
/************************************************
* debugging routines
************************************************/
void printBOX3D(BOX3D *box)
{
lwnotice("BOX3D: %g %g, %g %g", box->xmin, box->ymin,
box->xmax, box->ymax);
}
void printPA(POINTARRAY *pa)
{
int t;
POINT4D pt;
char *mflag;
if ( TYPE_HASM(pa->dims) ) mflag = "M";
else mflag = "";
lwnotice(" POINTARRAY%s{", mflag);
lwnotice(" ndims=%i, ptsize=%i",
TYPE_NDIMS(pa->dims), pointArray_ptsize(pa));
lwnotice(" npoints = %i", pa->npoints);
for (t =0; tnpoints;t++)
{
getPoint4d_p(pa, t, &pt);
if (TYPE_NDIMS(pa->dims) == 2)
{
lwnotice(" %i : %lf,%lf",t,pt.x,pt.y);
}
if (TYPE_NDIMS(pa->dims) == 3)
{
lwnotice(" %i : %lf,%lf,%lf",t,pt.x,pt.y,pt.z);
}
if (TYPE_NDIMS(pa->dims) == 4)
{
lwnotice(" %i : %lf,%lf,%lf,%lf",t,pt.x,pt.y,pt.z,pt.m);
}
}
lwnotice(" }");
}
void printBYTES(uchar *a, int n)
{
int t;
char buff[3];
buff[2] = 0; /* null terminate */
lwnotice(" BYTE ARRAY (n=%i) IN HEX: {", n);
for (t=0;tngeometries);
for (t=0;tngeometries;t++)
{
lwnotice(" sub-geometry %i:", t);
line = NULL; point = NULL; poly = NULL;
line = lwgeom_getline_inspected(inspected,t);
if (line !=NULL)
{
printLWLINE(line);
}
poly = lwgeom_getpoly_inspected(inspected,t);
if (poly !=NULL)
{
printLWPOLY(poly);
}
point = lwgeom_getpoint_inspected(inspected,t);
if (point !=NULL)
{
printPA(point->point);
}
}
lwnotice("end multi*");
lwinspected_release(inspected);
}
void
printType(uchar type)
{
lwnotice("type 0x%x ==> hasBBOX=%i, hasSRID=%i, ndims=%i, type=%i",(unsigned int) type, lwgeom_hasBBOX(type), lwgeom_hasSRID(type),lwgeom_ndims(type), lwgeom_getType(type));
}
/*
* Get the SRID from the LWGEOM.
* None present => -1
*/
int
lwgeom_getsrid(uchar *serialized)
{
uchar type = serialized[0];
uchar *loc = serialized+1;
if ( ! lwgeom_hasSRID(type)) return -1;
if (lwgeom_hasBBOX(type))
{
loc += sizeof(BOX2DFLOAT4);
}
return lw_get_int32(loc);
}
char
ptarray_isccw(const POINTARRAY *pa)
{
int i;
double area = 0;
POINT2D p1, p2;
for (i=0; inpoints-1; i++)
{
getPoint2d_p(pa, i, &p1);
getPoint2d_p(pa, i+1, &p2);
area += (p1.y * p2.x) - (p1.x * p2.y);
}
if ( area > 0 ) return 0;
else return 1;
}
/*
* Returns a BOX2DFLOAT4 that encloses b1 and b2
*
* box2d_union(NULL,A) --> A
* box2d_union(A,NULL) --> A
* box2d_union(A,B) --> A union B
*/
BOX2DFLOAT4 *
box2d_union(BOX2DFLOAT4 *b1, BOX2DFLOAT4 *b2)
{
BOX2DFLOAT4 *result;
if ( (b1 == NULL) && (b2 == NULL) )
{
return NULL;
}
result = lwalloc(sizeof(BOX2DFLOAT4));
if (b1 == NULL)
{
memcpy(result, b2, sizeof(BOX2DFLOAT4));
return result;
}
if (b2 == NULL)
{
memcpy(result, b1, sizeof(BOX2DFLOAT4));
return result;
}
if (b1->xmin < b2->xmin) result->xmin = b1->xmin;
else result->xmin = b2->xmin;
if (b1->ymin < b2->ymin) result->ymin = b1->ymin;
else result->ymin = b2->ymin;
if (b1->xmax > b2->xmax) result->xmax = b1->xmax;
else result->xmax = b2->xmax;
if (b1->ymax > b2->ymax) result->ymax = b1->ymax;
else result->ymax = b2->ymax;
return result;
}
/*
* ubox may be one of the two args...
* return 1 if done something to ubox, 0 otherwise.
*/
int
box2d_union_p(BOX2DFLOAT4 *b1, BOX2DFLOAT4 *b2, BOX2DFLOAT4 *ubox)
{
if ( (b1 == NULL) && (b2 == NULL) )
{
return 0;
}
if (b1 == NULL)
{
memcpy(ubox, b2, sizeof(BOX2DFLOAT4));
return 1;
}
if (b2 == NULL)
{
memcpy(ubox, b1, sizeof(BOX2DFLOAT4));
return 1;
}
if (b1->xmin < b2->xmin) ubox->xmin = b1->xmin;
else ubox->xmin = b2->xmin;
if (b1->ymin < b2->ymin) ubox->ymin = b1->ymin;
else ubox->ymin = b2->ymin;
if (b1->xmax > b2->xmax) ubox->xmax = b1->xmax;
else ubox->xmax = b2->xmax;
if (b1->ymax > b2->ymax) ubox->ymax = b1->ymax;
else ubox->ymax = b2->ymax;
return 1;
}
const char *
lwgeom_typeflags(uchar type)
{
static char flags[5];
int flagno=0;
if ( TYPE_HASZ(type) ) flags[flagno++] = 'Z';
if ( TYPE_HASM(type) ) flags[flagno++] = 'M';
if ( TYPE_HASBBOX(type) ) flags[flagno++] = 'B';
if ( TYPE_HASSRID(type) ) flags[flagno++] = 'S';
flags[flagno] = '\0';
LWDEBUGF(4, "Flags: %s - returning %p", flags, flags);
return flags;
}
/*
* Given a string with at least 2 chars in it, convert them to
* a byte value. No error checking done!
*/
uchar
parse_hex(char *str)
{
/* do this a little brute force to make it faster */
uchar result_high = 0;
uchar result_low = 0;
switch (str[0])
{
case '0' :
result_high = 0;
break;
case '1' :
result_high = 1;
break;
case '2' :
result_high = 2;
break;
case '3' :
result_high = 3;
break;
case '4' :
result_high = 4;
break;
case '5' :
result_high = 5;
break;
case '6' :
result_high = 6;
break;
case '7' :
result_high = 7;
break;
case '8' :
result_high = 8;
break;
case '9' :
result_high = 9;
break;
case 'A' :
case 'a' :
result_high = 10;
break;
case 'B' :
case 'b' :
result_high = 11;
break;
case 'C' :
case 'c' :
result_high = 12;
break;
case 'D' :
case 'd' :
result_high = 13;
break;
case 'E' :
case 'e' :
result_high = 14;
break;
case 'F' :
case 'f' :
result_high = 15;
break;
}
switch (str[1])
{
case '0' :
result_low = 0;
break;
case '1' :
result_low = 1;
break;
case '2' :
result_low = 2;
break;
case '3' :
result_low = 3;
break;
case '4' :
result_low = 4;
break;
case '5' :
result_low = 5;
break;
case '6' :
result_low = 6;
break;
case '7' :
result_low = 7;
break;
case '8' :
result_low = 8;
break;
case '9' :
result_low = 9;
break;
case 'A' :
case 'a' :
result_low = 10;
break;
case 'B' :
case 'b' :
result_low = 11;
break;
case 'C' :
case 'c' :
result_low = 12;
break;
case 'D' :
case 'd' :
result_low = 13;
break;
case 'E' :
case 'e' :
result_low = 14;
break;
case 'F' :
case 'f' :
result_low = 15;
break;
}
return (uchar) ((result_high<<4) + result_low);
}
/*
* Given one byte, populate result with two byte representing
* the hex number.
*
* Ie. deparse_hex( 255, mystr)
* -> mystr[0] = 'F' and mystr[1] = 'F'
*
* No error checking done
*/
void
deparse_hex(uchar str, char *result)
{
int input_high;
int input_low;
static char outchr[]={"0123456789ABCDEF" };
input_high = (str>>4);
input_low = (str & 0x0F);
result[0] = outchr[input_high];
result[1] = outchr[input_low];
}
/*
* Find interpolation point I
* between point A and point B
* so that the len(AI) == len(AB)*F
* and I falls on AB segment.
*
* Example:
*
* F=0.5 : A----I----B
* F=1 : A---------B==I
* F=0 : A==I---------B
* F=.2 : A-I-------B
*/
void
interpolate_point4d(POINT4D *A, POINT4D *B, POINT4D *I, double F)
{
#if PARANOIA_LEVEL > 0
double absF=fabs(F);
if ( absF < 0 || absF > 1 )
{
lwerror("interpolate_point4d: invalid F (%g)", F);
}
#endif
I->x=A->x+((B->x-A->x)*F);
I->y=A->y+((B->y-A->y)*F);
I->z=A->z+((B->z-A->z)*F);
I->m=A->m+((B->m-A->m)*F);
}
================================================
FILE: src/liblwgeom/lwgparse.c
================================================
/*
* Written by Ralph Mason ralph.masontelogis.com
*
* Copyright Telogis 2004
* www.telogis.com
*
*/
#include
#include
/* Solaris9 does not provide stdint.h */
/* #include */
#include
#include "liblwgeom.h"
#include "wktparse.h"
#include "wktparse.tab.h"
/*
* To get byte order
#include
#include
#include
#include
*/
void set_zm(char z, char m);
void close_parser(void);
typedef uint32_t int4;
typedef struct tag_tuple tuple;
struct tag_outputstate{
uchar* pos;
};
typedef struct tag_outputstate output_state;
typedef void (*output_func)(tuple* this, output_state* out);
typedef void (*read_col_func)(const char **f);
/* Globals */
int srid=-1;
static int parser_ferror_occured;
static allocator local_malloc;
static report_error error_func;
struct tag_tuple{
output_func of;
union union_tag {
double points[4];
int4 pointsi[4];
struct struct_tag {
tuple* stack_next;
int type;
int num;
int size_here;
} nn;
} uu;
struct tag_tuple *next;
};
struct {
int type;
int flags;
int srid;
int ndims;
int hasZ;
int hasM;
/* create integer version */
int lwgi;
/* input is integer (wkb only)*/
int from_lwgi;
int4 alloc_size;
/*
linked list of all tuples
*/
tuple* first;
tuple* last;
/*
stack of open geometries
*/
tuple* stack;
} the_geom;
tuple* free_list=0;
/*
* Parser current instance check flags - a bitmap of flags that determine which checks are enabled during the current parse
* (see liblwgeom.h for the related PARSER_CHECK constants)
*/
int current_parser_check_flags;
/*
* Parser current instance result structure - the result structure being used for the current parse
*/
LWGEOM_PARSER_RESULT *current_lwg_parser_result;
/* Parser state flags - these are set automatically by the parser */
int minpoints;
int checkclosed;
/*
* This indicates if the number of points in the geometry is required to
* be odd (one) or even (zero, currently not enforced) or whatever (-one)
*/
int isodd;
double *first_point=NULL;
double *last_point=NULL;
/*
* Parser error messages
*
* IMPORTANT: Make sure the order of these messages matches the PARSER_ERROR constants in liblwgeom.h!
* The 0th element should always be empty since it is unused (error constants start from -1)
*/
const char *parser_error_messages[] = {
"",
"geometry requires more points",
"geometry must have an odd number of points",
"geometry contains non-closed rings",
"can not mix dimensionality in a geometry",
"parse error - invalid geometry",
"invalid WKB type"
};
/* Macro to return the error message and the current position within WKT */
#define LWGEOM_WKT_PARSER_ERROR(errcode) \
do { \
if (!parser_ferror_occured) { \
parser_ferror_occured = -1 * errcode; \
current_lwg_parser_result->message = parser_error_messages[errcode]; \
current_lwg_parser_result->errlocation = lwg_parse_yylloc.last_column; \
} \
} while (0);
/* Macro to return the error message and the current position within WKB
NOTE: the position is handled automatically by strhex_readbyte */
#define LWGEOM_WKB_PARSER_ERROR(errcode) \
do { \
if (!parser_ferror_occured) { \
parser_ferror_occured = -1 * errcode; \
current_lwg_parser_result->message = parser_error_messages[errcode]; \
} \
} while (0);
/* External functions */
extern void init_parser(const char *);
/* Prototypes */
tuple* alloc_tuple(output_func of,size_t size);
void free_tuple(tuple* to_free);
void inc_num(void);
void alloc_stack_tuple(int type,output_func of,size_t size);
void check_dims(int num);
void WRITE_DOUBLES(output_state* out,double* points, int cnt);
#ifdef SHRINK_INTS
void WRITE_INT4(output_state * out,int4 val);
#endif
void empty_stack(tuple* this,output_state* out);
void alloc_lwgeom(int srid);
void write_point_2(tuple* this,output_state* out);
void write_point_3(tuple* this,output_state* out);
void write_point_4(tuple* this,output_state* out);
void write_point_2i(tuple* this,output_state* out);
void write_point_3i(tuple* this,output_state* out);
void write_point_4i(tuple* this,output_state* out);
void alloc_point_2d(double x,double y);
void alloc_point_3d(double x,double y,double z);
void alloc_point_4d(double x,double y,double z,double m);
void write_type(tuple* this,output_state* out);
void write_count(tuple* this,output_state* out);
void write_type_count(tuple* this,output_state* out);
void alloc_point(void);
void alloc_linestring(void);
void alloc_linestring_closed(void);
void alloc_circularstring(void);
void alloc_circularstring_closed(void);
void alloc_polygon(void);
void alloc_multipoint(void);
void alloc_multilinestring(void);
void alloc_multicurve(void);
void alloc_multipolygon(void);
void alloc_multisurface(void);
void alloc_geomertycollection(void);
void alloc_counter(void);
void alloc_empty(void);
void make_serialized_lwgeom(LWGEOM_PARSER_RESULT *lwg_parser_result);
uchar strhex_readbyte(const char *in);
uchar read_wkb_byte(const char **in);
void read_wkb_bytes(const char **in, uchar* out, int cnt);
int4 read_wkb_int(const char **in);
double read_wkb_double(const char **in, int convert_from_int);
void read_wkb_point(const char **b);
void read_wkb_polygon(const char **b);
void read_wkb_linestring(const char **b);
void read_wkb_circstring(const char **b);
void read_wkb_ordinate_array(const char **b);
void read_collection(const char **b, read_col_func f);
void parse_wkb(const char **b);
void alloc_wkb(const char *parser);
int parse_it(LWGEOM_PARSER_RESULT *lwg_parser_result, const char* geometry, int flags, allocator allocfunc, report_error errfunc);
int parse_lwg(LWGEOM_PARSER_RESULT *lwg_parser_result, const char* geometry, int flags, allocator allocfunc, report_error errfunc);
int parse_lwgi(LWGEOM_PARSER_RESULT *lwg_parser_result, const char* geometry, int flags, allocator allocfunc, report_error errfunc);
void
set_srid(double d_srid)
{
if ( d_srid >= 0 )
d_srid+=0.1;
else
d_srid-=0.1;
srid=(int)(d_srid+0.1);
}
tuple *
alloc_tuple(output_func of,size_t size)
{
tuple* ret = free_list;
if ( ! ret ){
int toalloc = (ALLOC_CHUNKS /sizeof(tuple));
ret = malloc( toalloc *sizeof(tuple) );
free_list = ret;
while(--toalloc){
ret->next = ret+1;
ret++;
}
ret->next = NULL;
return alloc_tuple(of,size);
}
free_list = ret->next;
ret->of = of;
ret->next = NULL;
if ( the_geom.last ) {
the_geom.last->next = ret;
the_geom.last = ret;
}
else {
the_geom.first = the_geom.last = ret;
}
the_geom.alloc_size += size;
return ret;
}
void
free_tuple(tuple* to_free)
{
tuple* list_end = to_free;
if( !to_free)
return;
while(list_end->next){
list_end=list_end->next;
}
list_end->next = free_list;
free_list = to_free;
}
void
inc_num(void)
{
the_geom.stack->uu.nn.num++;
}
/*
Allocate a 'counting' tuple
*/
void
alloc_stack_tuple(int type,output_func of,size_t size)
{
tuple* p;
inc_num();
LWDEBUGF(2, "alloc_stack_tuple %d, %d", type, size);
p = alloc_tuple(of,size);
p->uu.nn.stack_next = the_geom.stack;
p->uu.nn.type = type;
p->uu.nn.size_here = the_geom.alloc_size;
p->uu.nn.num = 0;
the_geom.stack = p;
}
void
pop(void)
{
the_geom.stack = the_geom.stack->uu.nn.stack_next;
}
void
popc(void)
{
/* If the minimum point check has been enabled, perform it */
if (current_parser_check_flags & PARSER_CHECK_MINPOINTS) {
if ( the_geom.stack->uu.nn.num < minpoints){
LWGEOM_WKT_PARSER_ERROR(PARSER_ERROR_MOREPOINTS);
}
}
/* If the odd number point check has been enabled, perform it */
if (current_parser_check_flags & PARSER_CHECK_ODD) {
if(isodd != -1 && the_geom.stack->uu.nn.num % 2 != isodd) {
LWGEOM_WKT_PARSER_ERROR(PARSER_ERROR_ODDPOINTS);
}
}
/* If the polygon closure check has been enabled, perform it */
if (current_parser_check_flags & PARSER_CHECK_CLOSURE) {
if ( checkclosed && first_point && last_point) {
if ( memcmp(first_point, last_point,
sizeof(double)*the_geom.ndims) )
{
LWGEOM_WKT_PARSER_ERROR(PARSER_ERROR_UNCLOSED);
}
}
}
the_geom.stack = the_geom.stack->uu.nn.stack_next;
}
void
check_dims(int num)
{
LWDEBUGF(2, "check_dims the_geom.ndims = %d, num = %d", the_geom.ndims, num);
if( the_geom.ndims != num){
if (the_geom.ndims) {
LWGEOM_WKT_PARSER_ERROR(PARSER_ERROR_MIXDIMS);
} else {
LWDEBUGF(3, "check_dims: setting dim %d", num);
the_geom.ndims = num;
if ( num > 2 ) the_geom.hasZ = 1;
if ( num > 3 ) the_geom.hasM = 1;
}
}
}
#define WRITE_INT4_REAL(x,y) { memcpy(x->pos,&y,4); x->pos+=4;}
#define WRITE_INT4_REAL_MULTIPLE(x,y,z) { memcpy(x->pos,&y,z*4); x->pos+=(z*4);}
/*
we can shrink ints to one byte if they are less than 127
by setting the extra bit. Because if the different byte
ordering possibilities we need to set the lsb on little
endian to show a packed one and the msb on a big endian
machine
*/
#ifdef SHRINK_INTS
void
WRITE_INT4(output_state * out,int4 val)
{
if ( val <= 0x7f ){
if ( getMachineEndian() == NDR ){
val = (val<<1) | 1;
}
else{
val |=0x80;
}
*out->pos++ = (uchar)val;
the_geom.alloc_size-=3;
}
else{
if ( getMachineEndian() == NDR ){
val <<=1;
}
WRITE_INT4_REAL(out,val);
}
}
#else
#define WRITE_INT4 WRITE_INT4_REAL
#endif
void
WRITE_DOUBLES(output_state* out,double* points, int cnt)
{
if ( the_geom.lwgi){
int4 vals[4];
int i;
for(i=0; ipos,vals,sizeof(int4)*cnt);
out->pos+=sizeof(int4)*cnt;
}
else{
memcpy(out->pos,points,sizeof(double)*cnt);
out->pos+=sizeof(double)*cnt;
}
}
void
empty_stack(tuple *this,output_state* out)
{
/* Do nothing but provide an empty base for the geometry stack */
}
void
alloc_lwgeom(int srid)
{
LWDEBUGF(2, "alloc_lwgeom %d", srid);
the_geom.srid=srid;
the_geom.alloc_size=0;
the_geom.stack=NULL;
the_geom.ndims=0;
the_geom.hasZ=0;
the_geom.hasM=0;
/* Free if used already */
if ( the_geom.first ){
free_tuple(the_geom.first);
the_geom.first=the_geom.last=NULL;
}
if ( srid != -1 ){
the_geom.alloc_size+=4;
}
/* Setup up an empty tuple as the stack base */
the_geom.stack = alloc_tuple(empty_stack, 0);
}
void
write_point_2(tuple* this,output_state* out)
{
WRITE_DOUBLES(out,this->uu.points,2);
}
void
write_point_3(tuple* this,output_state* out)
{
WRITE_DOUBLES(out,this->uu.points,3);
}
void
write_point_4(tuple* this,output_state* out)
{
WRITE_DOUBLES(out,this->uu.points,4);
}
void
write_point_2i(tuple* this,output_state* out)
{
WRITE_INT4_REAL_MULTIPLE(out,this->uu.points,2);
}
void
write_point_3i(tuple* this,output_state* out)
{
WRITE_INT4_REAL_MULTIPLE(out,this->uu.points,3);
}
void
write_point_4i(tuple* this,output_state* out)
{
WRITE_INT4_REAL_MULTIPLE(out,this->uu.points,4);
}
void
alloc_point_2d(double x,double y)
{
tuple* p = alloc_tuple(write_point_2,the_geom.lwgi?8:16);
p->uu.points[0] = x;
p->uu.points[1] = y;
LWDEBUGF(2, "alloc_point_2d %f,%f", x, y);
/* keep track of point */
if ( checkclosed ) {
if ( ! the_geom.stack->uu.nn.num )
first_point = p->uu.points;
last_point = p->uu.points;
}
inc_num();
check_dims(2);
}
void
alloc_point_3d(double x,double y,double z)
{
tuple* p = alloc_tuple(write_point_3,the_geom.lwgi?12:24);
p->uu.points[0] = x;
p->uu.points[1] = y;
p->uu.points[2] = z;
LWDEBUGF(2, "alloc_point_3d %f, %f, %f", x, y, z);
/* keep track of point */
if ( checkclosed ) {
if ( ! the_geom.stack->uu.nn.num )
first_point = p->uu.points;
last_point = p->uu.points;
}
inc_num();
check_dims(3);
}
void
alloc_point_4d(double x,double y,double z,double m)
{
tuple* p = alloc_tuple(write_point_4,the_geom.lwgi?16:32);
p->uu.points[0] = x;
p->uu.points[1] = y;
p->uu.points[2] = z;
p->uu.points[3] = m;
LWDEBUGF(2, "alloc_point_4d %f, %f, %f, %f", x, y, z, m);
/* keep track of point */
if ( checkclosed ) {
if ( ! the_geom.stack->uu.nn.num )
first_point = p->uu.points;
last_point = p->uu.points;
}
inc_num();
check_dims(4);
}
void
write_type(tuple* this,output_state* out)
{
uchar type=0;
/* Empty handler - switch back */
if ( this->uu.nn.type == 0xff )
this->uu.nn.type = COLLECTIONTYPE;
type |= this->uu.nn.type;
if (the_geom.ndims) /* Support empty */
{
TYPE_SETZM(type, the_geom.hasZ, the_geom.hasM);
}
if ( the_geom.srid != -1 ){
type |= 0x40;
}
*(out->pos)=type;
out->pos++;
if ( the_geom.srid != -1 ){
/* Only the first geometry will have a srid attached */
WRITE_INT4(out,the_geom.srid);
the_geom.srid = -1;
}
}
void
write_count(tuple* this,output_state* out)
{
int num = this->uu.nn.num;
WRITE_INT4(out,num);
}
void
write_type_count(tuple* this,output_state* out)
{
write_type(this,out);
write_count(this,out);
}
void
alloc_point(void)
{
LWDEBUG(2, "alloc_point");
if( the_geom.lwgi)
alloc_stack_tuple(POINTTYPEI,write_type,1);
else
alloc_stack_tuple(POINTTYPE,write_type,1);
minpoints=1;
checkclosed=0;
isodd=-1;
}
void
alloc_linestring(void)
{
LWDEBUG(2, "alloc_linestring");
if( the_geom.lwgi)
alloc_stack_tuple(LINETYPEI,write_type,1);
else
alloc_stack_tuple(LINETYPE,write_type,1);
minpoints=2;
checkclosed=0;
isodd=-1;
}
void alloc_linestring_closed(void)
{
LWDEBUG(2, "alloc_linestring_closed called.");
alloc_linestring();
checkclosed=1;
}
void
alloc_circularstring(void)
{
LWDEBUG(2, "alloc_circularstring");
alloc_stack_tuple(CIRCSTRINGTYPE,write_type,1);
minpoints=3;
checkclosed=0;
isodd=1;
}
void alloc_circularstring_closed(void)
{
LWDEBUG(2, "alloc_circularstring_closed");
alloc_circularstring();
checkclosed=1;
}
void
alloc_polygon(void)
{
LWDEBUG(2, "alloc_polygon");
if( the_geom.lwgi)
alloc_stack_tuple(POLYGONTYPEI, write_type,1);
else
alloc_stack_tuple(POLYGONTYPE, write_type,1);
minpoints=4;
checkclosed=1;
isodd=-1;
}
void
alloc_curvepolygon(void)
{
LWDEBUG(2, "alloc_curvepolygon called.");
alloc_stack_tuple(CURVEPOLYTYPE, write_type, 1);
minpoints=4;
checkclosed=1;
isodd=-1;
}
void
alloc_compoundcurve(void)
{
LWDEBUG(2, "alloc_compoundcurve called.");
alloc_stack_tuple(COMPOUNDTYPE, write_type, 1);
}
void
alloc_multipoint(void)
{
LWDEBUG(2, "alloc_multipoint");
alloc_stack_tuple(MULTIPOINTTYPE,write_type,1);
}
void
alloc_multilinestring(void)
{
LWDEBUG(2, "alloc_multilinestring");
alloc_stack_tuple(MULTILINETYPE,write_type,1);
}
void
alloc_multicurve(void)
{
LWDEBUG(2, "alloc_multicurve");
alloc_stack_tuple(MULTICURVETYPE,write_type,1);
}
void
alloc_multipolygon(void)
{
LWDEBUG(2, "alloc_multipolygon");
alloc_stack_tuple(MULTIPOLYGONTYPE,write_type,1);
}
void
alloc_multisurface(void)
{
LWDEBUG(2, "alloc_multisurface called");
alloc_stack_tuple(MULTISURFACETYPE,write_type,1);
}
void
alloc_geomertycollection(void)
{
LWDEBUG(2, "alloc_geometrycollection");
alloc_stack_tuple(COLLECTIONTYPE,write_type,1);
}
void
alloc_counter(void)
{
LWDEBUG(2, "alloc_counter");
alloc_stack_tuple(0,write_count,4);
}
void
alloc_empty(void)
{
tuple* st = the_geom.stack;
LWDEBUG(2, "alloc_empty");
/* Find the last geometry */
while(st->uu.nn.type == 0){
st =st->uu.nn.stack_next;
}
/* Reclaim memory */
free_tuple(st->next);
/* Put an empty geometry collection on the top of the stack */
st->next=NULL;
the_geom.stack=st;
the_geom.alloc_size=st->uu.nn.size_here;
/* Mark as a empty stop */
if (st->uu.nn.type != 0xFF){
st->uu.nn.type=0xFF;
st->of = write_type_count;
the_geom.alloc_size+=4;
st->uu.nn.size_here=the_geom.alloc_size;
}
st->uu.nn.num=0;
}
void
make_serialized_lwgeom(LWGEOM_PARSER_RESULT *lwg_parser_result)
{
uchar* out_c;
output_state out;
tuple* cur;
LWDEBUG(2, "make_serialized_lwgeom");
/* Allocate the LWGEOM itself */
out_c = (uchar*)local_malloc(the_geom.alloc_size);
out.pos = out_c;
cur = the_geom.first ;
while(cur){
cur->of(cur,&out);
cur=cur->next;
}
/* Setup the LWGEOM_PARSER_RESULT structure */
lwg_parser_result->serialized_lwgeom = out_c;
lwg_parser_result->size = the_geom.alloc_size;
return;
}
void
lwg_parse_yynotice(char* s)
{
lwnotice(s);
}
int
lwg_parse_yyerror(char* s)
{
LWGEOM_WKT_PARSER_ERROR(PARSER_ERROR_INVALIDGEOM);
return 1;
}
/*
Table below generated using this ruby.
a=(0..0xff).to_a.collect{|x|0xff};('0'..'9').each{|x|a[x[0]]=x[0]-'0'[0]}
('a'..'f').each{|x|v=x[0]-'a'[0]+10;a[x[0]]=a[x.upcase[0]]=v}
puts '{'+a.join(",")+'}'
*/
static const uchar to_hex[] = {
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
0,1,2,3,4,5,6,7,8,9,255,255,255,255,255,255,255,10,11,12,13,14,
15,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,10,11,12,13,14,15,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255};
uchar
strhex_readbyte(const char* in)
{
if ( *in == 0 ){
if ( ! parser_ferror_occured){
LWGEOM_WKB_PARSER_ERROR(PARSER_ERROR_INVALIDGEOM);
}
return 0;
}
if (!parser_ferror_occured) {
lwg_parse_yylloc.last_column++;
return to_hex[(int)*in]<<4 | to_hex[(int)*(in+1)];
} else {
return 0;
}
}
uchar
read_wkb_byte(const char** in)
{
uchar ret = strhex_readbyte(*in);
(*in)+=2;
return ret;
}
int swap_order;
void
read_wkb_bytes(const char** in, uchar* out, int cnt)
{
if ( ! swap_order ){
while(cnt--) *out++ = read_wkb_byte(in);
}
else{
out += (cnt-1);
while(cnt--) *out-- = read_wkb_byte(in);
}
}
int4
read_wkb_int(const char** in)
{
int4 ret=0;
read_wkb_bytes(in,(uchar*)&ret,4);
return ret;
}
double
read_wkb_double(const char** in, int convert_from_int)
{
double ret=0;
if ( ! convert_from_int){
read_wkb_bytes(in,(uchar*)&ret,8);
return ret;
}else{
ret = read_wkb_int(in);
ret /= 0xb60b60;
return ret-180.0;
}
}
void
read_wkb_point(const char **b)
{
int i;
tuple* p = NULL;
if(the_geom.lwgi && the_geom.from_lwgi ){
/*
* Special case - reading from lwgi to lwgi
* we don't want to go via doubles in the middle.
*/
switch(the_geom.ndims){
case 2: p=alloc_tuple(write_point_2i,8); break;
case 3: p=alloc_tuple(write_point_3i,12); break;
case 4: p=alloc_tuple(write_point_4i,16); break;
}
for(i=0;iuu.pointsi[i]=read_wkb_int(b);
}
}
else{
int mul = the_geom.lwgi ? 1 : 2;
switch(the_geom.ndims){
case 2: p=alloc_tuple(write_point_2,8*mul); break;
case 3: p=alloc_tuple(write_point_3,12*mul); break;
case 4: p=alloc_tuple(write_point_4,16*mul); break;
}
for(i=0;iuu.points[i]=read_wkb_double(b,the_geom.from_lwgi);
}
}
/* keep track of point */
if ( checkclosed ) {
if ( ! the_geom.stack->uu.nn.num )
first_point = p->uu.points;
last_point = p->uu.points;
}
inc_num();
check_dims(the_geom.ndims);
}
void
read_wkb_polygon(const char **b)
{
/* Stack the number of ORDINATE_ARRAYs (rings) */
int4 cnt=read_wkb_int(b);
alloc_counter();
/* Read through each ORDINATE_ARRAY in turn */
while(cnt--){
if ( parser_ferror_occured ) return;
/* Things to check for POLYGON ORDINATE_ARRAYs */
minpoints=4;
checkclosed=1;
isodd=-1;
read_wkb_ordinate_array(b);
}
pop();
}
void
read_wkb_linestring(const char **b)
{
/* Things to check for LINESTRING ORDINATE_ARRAYs */
minpoints=2;
checkclosed=0;
isodd=-1;
read_wkb_ordinate_array(b);
}
void
read_wkb_circstring(const char **b)
{
/* Things to check for CIRCULARSTRING ORDINATE_ARRAYs */
minpoints=3;
checkclosed=0;
isodd=-1;
read_wkb_ordinate_array(b);
}
void
read_wkb_ordinate_array(const char **b)
{
/* Read through a WKB ordinate array */
int4 cnt=read_wkb_int(b);
alloc_counter();
while(cnt--){
if ( parser_ferror_occured ) return;
read_wkb_point(b);
}
/* Perform a check of the ordinate array */
popc();
}
void
read_collection(const char **b, read_col_func f)
{
/* Read through a COLLECTION or an EWKB */
int4 cnt=read_wkb_int(b);
alloc_counter();
while(cnt--){
if ( parser_ferror_occured ) return;
f(b);
}
pop();
}
void
parse_wkb(const char **b)
{
int4 type;
uchar xdr = read_wkb_byte(b);
int4 localsrid;
LWDEBUG(2, "parse_wkb");
swap_order=0;
if ( xdr != getMachineEndian() )
{
swap_order=1;
}
type = read_wkb_int(b);
/* quick exit on error */
if ( parser_ferror_occured ) return;
the_geom.ndims=2;
if (type & WKBZOFFSET)
{
the_geom.hasZ = 1;
the_geom.ndims++;
}
else the_geom.hasZ = 0;
if (type & WKBMOFFSET)
{
the_geom.hasM = 1;
the_geom.ndims++;
}
else the_geom.hasM = 0;
if (type & WKBSRIDFLAG )
{
/* local (in-EWKB) srid spec overrides SRID=#; */
localsrid = read_wkb_int(b);
if ( localsrid != -1 )
{
if ( the_geom.srid == -1 ) the_geom.alloc_size += 4;
the_geom.srid = localsrid;
}
}
type &=0x0f;
if ( the_geom.lwgi ){
if ( type<= POLYGONTYPE )
alloc_stack_tuple(type +9,write_type,1);
else
alloc_stack_tuple(type,write_type,1);
}
else{
/* If we are writing lwg and are reading wbki */
int4 towrite=type;
if (towrite >= POINTTYPEI && towrite <= POLYGONTYPEI){
towrite-=9;
}
alloc_stack_tuple(towrite,write_type,1);
}
switch(type ){
case POINTTYPE:
read_wkb_point(b);
break;
case LINETYPE:
read_wkb_linestring(b);
break;
case CIRCSTRINGTYPE:
read_wkb_circstring(b);
break;
case POLYGONTYPE:
read_wkb_polygon(b);
break;
case COMPOUNDTYPE:
read_collection(b,parse_wkb);
break;
case CURVEPOLYTYPE:
read_collection(b,parse_wkb);
break;
case MULTIPOINTTYPE:
case MULTILINETYPE:
case MULTICURVETYPE:
case MULTIPOLYGONTYPE:
case MULTISURFACETYPE:
case COLLECTIONTYPE:
read_collection(b,parse_wkb);
break;
case POINTTYPEI:
the_geom.from_lwgi=1;
read_wkb_point(b);
break;
case LINETYPEI:
the_geom.from_lwgi=1;
read_wkb_linestring(b);
break;
case POLYGONTYPEI:
the_geom.from_lwgi=1;
read_wkb_polygon(b);
break;
default:
LWGEOM_WKB_PARSER_ERROR(PARSER_ERROR_INVALIDWKBTYPE);
}
the_geom.from_lwgi=0;
pop();
}
void
alloc_wkb(const char *parser)
{
LWDEBUG(2, "alloc_wkb");
parse_wkb(&parser);
}
/*
Parse a string and return a LW_GEOM
*/
int
parse_it(LWGEOM_PARSER_RESULT *lwg_parser_result, const char *geometry, int flags, allocator allocfunc, report_error errfunc)
{
LWDEBUGF(2, "parse_it: %s with parser flags %d", geometry, flags);
local_malloc = allocfunc;
error_func=errfunc;
parser_ferror_occured = 0;
/* Setup the inital parser flags and empty the return struct */
current_lwg_parser_result = lwg_parser_result;
current_parser_check_flags = flags;
lwg_parser_result->serialized_lwgeom = NULL;
lwg_parser_result->size = 0;
lwg_parser_result->wkinput = geometry;
init_parser(geometry);
lwg_parse_yyparse();
close_parser();
/* Return the parsed geometry */
make_serialized_lwgeom(lwg_parser_result);
return parser_ferror_occured;
}
int
parse_lwg(LWGEOM_PARSER_RESULT *lwg_parser_result, const char* geometry, int flags, allocator allocfunc, report_error errfunc)
{
the_geom.lwgi=0;
return parse_it(lwg_parser_result, geometry, flags, allocfunc, errfunc);
}
int
parse_lwgi(LWGEOM_PARSER_RESULT *lwg_parser_result, const char* geometry, int flags, allocator allocfunc, report_error errfunc)
{
the_geom.lwgi=1;
return parse_it(lwg_parser_result, geometry, flags, allocfunc, errfunc);
}
void
set_zm(char z, char m)
{
LWDEBUGF(2, "set_zm %d, %d", z, m);
the_geom.hasZ = z;
the_geom.hasM = m;
the_geom.ndims = 2+z+m;
}
================================================
FILE: src/liblwgeom/lwgunparse.c
================================================
/*
* Written by Ralph Mason ralph.masontelogis.com
*
* Copyright Telogis 2004
* www.telogis.com
*
* $Id: lwgunparse.c 3639 2009-02-04 00:28:37Z pramsey $
*/
#include
#include
#include
/* TO get byte order */
#include
#include
/* Solaris9 does not provide stdint.h */
/* #include */
#include
#include "liblwgeom.h"
#include "wktparse.h"
/*-- Typedefs ---------------------------------------------- */
typedef uint32_t int4;
typedef uchar* (*outfunc)(uchar*,int);
typedef uchar* (*outwkbfunc)(uchar*);
/*-- Prototypes -------------------------------------------- */
void ensure(int chars);
void to_end(void);
void write_str(const char* str);
void write_double(double val);
void write_int(int i);
int4 read_int(uchar** geom);
double read_double(uchar** geom);
uchar* output_point(uchar* geom,int supress);
uchar* output_single(uchar* geom,int supress);
uchar* output_collection(uchar* geom,outfunc func,int supress);
uchar* output_line_collection(uchar* geom,outfunc func,int supress);
uchar* output_polygon_collection(uchar* geom,int suppress);
uchar* output_polygon_ring_collection(uchar* geom,outfunc func,int supress);
uchar* output_circstring_collection(uchar* geom,outfunc func,int supress);
uchar* output_multipoint(uchar* geom,int suppress);
uchar* output_compound(uchar* geom, int suppress);
uchar* output_multisurface(uchar* geom, int suppress);
void write_wkb_hex_bytes(uchar* ptr, unsigned int cnt, size_t size);
void write_wkb_bin_bytes(uchar* ptr, unsigned int cnt, size_t size);
void write_wkb_bin_flip_bytes(uchar* ptr, unsigned int cnt, size_t size);
void write_wkb_hex_flip_bytes(uchar* ptr, unsigned int cnt, size_t size);
void write_wkb_int(int i);
uchar* output_wkb_collection(uchar* geom,outwkbfunc func);
uchar* output_wkb_polygon_collection(uchar* geom);
uchar* output_wkb_polygon_ring_collection(uchar* geom,outwkbfunc func);
uchar* output_wkb_line_collection(uchar* geom,outwkbfunc func);
uchar* output_wkb_circstring_collection(uchar* geom,outwkbfunc func);
uchar* output_wkb_point(uchar* geom);
uchar* output_wkb(uchar* geom);
/*-- Globals ----------------------------------------------- */
static int unparser_ferror_occured;
static int dims;
static allocator local_malloc;
static freeor local_free;
static char* out_start;
static char* out_pos;
static int len;
static int lwgi;
static uchar endianbyte;
void (*write_wkb_bytes)(uchar* ptr,unsigned int cnt,size_t size);
/*
* Unparser current instance check flags - a bitmap of flags that determine which checks are enabled during the current unparse
* (see liblwgeom.h for the related PARSER_CHECK constants)
*/
int current_unparser_check_flags;
/*
* Unparser current instance result structure - the result structure being used for the current unparse
*/
LWGEOM_UNPARSER_RESULT *current_lwg_unparser_result;
/*
* Unparser error messages
*
* IMPORTANT: Make sure the order of these messages matches the UNPARSER_ERROR constants in liblwgeom.h!
* The 0th element should always be empty since it is unused (error constants start from -1)
*/
const char *unparser_error_messages[] = {
"",
"geometry requires more points",
"geometry must have an odd number of points",
"geometry contains non-closed rings"
};
/* Macro to return the error message and the current position within WKT */
#define LWGEOM_WKT_UNPARSER_ERROR(errcode) \
do { \
if (!unparser_ferror_occured) { \
unparser_ferror_occured = -1 * errcode; \
current_lwg_unparser_result->message = unparser_error_messages[errcode]; \
current_lwg_unparser_result->errlocation = (out_pos - out_start); \
} \
} while (0);
/* Macro to return the error message and the current position within WKB */
#define LWGEOM_WKB_UNPARSER_ERROR(errcode) \
do { \
if (!unparser_ferror_occured) { \
unparser_ferror_occured = -1 * errcode; \
current_lwg_unparser_result->message = unparser_error_messages[errcode]; \
current_lwg_unparser_result->errlocation = (out_pos - out_start); \
} \
} while (0);
/*---------------------------------------------------------- */
/*
* Ensure there is enough space for chars bytes.
* Reallocate memory is this is not the case.
*/
void
ensure(int chars){
int pos = out_pos - out_start;
if ( (pos + chars) >= len ){
char* newp =(char*)local_malloc(len*2);
memcpy(newp,out_start,len);
local_free(out_start);
out_start = newp;
out_pos = newp + pos;
len *=2;
}
}
void
to_end(void)
{
while(*out_pos){
out_pos++;
}
}
void
write_str(const char* str)
{
ensure(32);
strcpy(out_pos,str);
to_end();
}
void
write_double(double val){
ensure(32);
if (lwgi)
sprintf(out_pos,"%.8g",val);
else
sprintf(out_pos,"%.15g",val);
to_end();
}
void
write_int(int i){
ensure(32);
sprintf(out_pos,"%i",i);
to_end();
}
int4
read_int(uchar** geom)
{
int4 ret;
#ifdef SHRINK_INTS
if ( getMachineEndian() == NDR ){
if( (**geom)& 0x01){
ret = **geom >>1;
(*geom)++;
return ret;
}
}
else{
if( (**geom)& 0x80){
ret = **geom & ~0x80;
(*geom)++;
return ret;
}
}
#endif
memcpy(&ret,*geom,4);
#ifdef SHRINK_INTS
if ( getMachineEndian() == NDR ){
ret >>= 1;
}
#endif
(*geom)+=4;
return ret;
}
double round(double);
double read_double(uchar** geom){
if (lwgi){
double ret = *((int4*)*geom);
ret /= 0xb60b60;
(*geom)+=4;
return ret-180.0;
}
else{
double ret;
memcpy(&ret, *geom, 8);
(*geom)+=8;
return ret;
}
}
uchar *
output_point(uchar* geom,int supress)
{
int i;
for( i = 0 ; i < dims ; i++ ){
write_double(read_double(&geom));
if (i +1 < dims )
write_str(" ");
}
return geom;
}
uchar *
output_single(uchar* geom,int supress)
{
write_str("(");
geom=output_point(geom,supress);
write_str(")");
return geom;
}
/* Output a standard collection */
uchar *
output_collection(uchar* geom,outfunc func,int supress)
{
int cnt = read_int(&geom);
if ( cnt == 0 ){
write_str(" EMPTY");
}
else{
write_str("(");
while(cnt--){
geom=func(geom,supress);
if ( cnt ){
write_str(",");
}
}
write_str(")");
}
return geom;
}
/* Output a set of LINESTRING points */
uchar *
output_line_collection(uchar* geom,outfunc func,int supress)
{
int cnt = read_int(&geom);
int orig_cnt = cnt;
if ( cnt == 0 ){
write_str(" EMPTY");
}
else{
write_str("(");
while(cnt--){
geom=func(geom,supress);
if ( cnt ){
write_str(",");
}
}
write_str(")");
}
/* Ensure that LINESTRING has a minimum of 2 points */
if ((current_unparser_check_flags & PARSER_CHECK_MINPOINTS) && orig_cnt < 2)
LWGEOM_WKT_UNPARSER_ERROR(UNPARSER_ERROR_MOREPOINTS);
return geom;
}
/* Output an individual ring from a POLYGON */
uchar *
output_polygon_ring_collection(uchar* geom,outfunc func,int supress)
{
uchar *temp;
int dimcount;
double first_point[dims];
double last_point[dims];
int cnt = read_int(&geom);
int orig_cnt = cnt;
if ( cnt == 0 ){
write_str(" EMPTY");
}
else{
write_str("(");
/* Store the first point of the ring (use a temp var since read_double alters
the pointer after use) */
temp = geom;
dimcount = 0;
while (dimcount < dims)
{
first_point[dimcount] = read_double(&temp);
dimcount++;
}
while(cnt--){
geom=func(geom,supress);
if ( cnt ){
write_str(",");
}
}
write_str(")");
/* Store the last point of the ring (note: we will have moved past it, so we
need to count backwards) */
temp = geom - sizeof(double) * dims;
dimcount = 0;
while (dimcount < dims)
{
last_point[dimcount] = read_double(&temp);
dimcount++;
}
/* Check if they are the same... */
if (memcmp(&first_point, &last_point, sizeof(double) * dims) &&
(current_unparser_check_flags & PARSER_CHECK_CLOSURE))
LWGEOM_WKT_UNPARSER_ERROR(UNPARSER_ERROR_UNCLOSED);
/* Ensure that POLYGON has a minimum of 4 points */
if ((current_unparser_check_flags & PARSER_CHECK_MINPOINTS) && orig_cnt < 4)
LWGEOM_WKT_UNPARSER_ERROR(UNPARSER_ERROR_MOREPOINTS);
}
return geom;
}
/* Ouput the points from a CIRCULARSTRING */
uchar *
output_circstring_collection(uchar* geom,outfunc func,int supress)
{
int cnt = read_int(&geom);
int orig_cnt = cnt;
if ( cnt == 0 ){
write_str(" EMPTY");
}
else{
write_str("(");
while(cnt--){
geom=func(geom,supress);
if ( cnt ){
write_str(",");
}
}
write_str(")");
}
/* Ensure that a CIRCULARSTRING has a minimum of 3 points */
if ((current_unparser_check_flags & PARSER_CHECK_MINPOINTS) && orig_cnt < 3) {
LWGEOM_WKT_UNPARSER_ERROR(UNPARSER_ERROR_MOREPOINTS);
}
/* Ensure that a CIRCULARSTRING has an odd number of points */
if ((current_unparser_check_flags & PARSER_CHECK_ODD) && orig_cnt % 2 != 1) {
LWGEOM_WKT_UNPARSER_ERROR(UNPARSER_ERROR_ODDPOINTS);
}
return geom;
}
/* Output a POLYGON consisting of a number of rings */
uchar *
output_polygon_collection(uchar* geom,int suppress)
{
return output_polygon_ring_collection(geom,output_point,suppress);
}
uchar *output_wkt(uchar* geom, int supress);
/* special case for multipoint to supress extra brackets */
uchar *output_multipoint(uchar* geom,int suppress){
unsigned char type = *geom & 0x0f;
if ( type == POINTTYPE )
return output_point(++geom,suppress);
else if ( type == POINTTYPEI ){
lwgi++;
geom=output_point(++geom,0);
lwgi--;
return geom;
}
return output_wkt(geom,suppress);
}
/* Special case for compound curves: suppress the LINESTRING prefix from a curve if it appears as
a component of a COMPOUNDCURVE, but not CIRCULARSTRING */
uchar *output_compound(uchar* geom, int suppress) {
unsigned char type;
LWDEBUG(2, "output_compound called.");
type=*geom;
switch(TYPE_GETTYPE(type))
{
case LINETYPE:
geom = output_wkt(geom,2);
break;
case CIRCSTRINGTYPE:
geom = output_wkt(geom,1);
break;
}
return geom;
}
/* Special case for multisurfaces: suppress the POLYGON prefix from a surface if it appears as
a component of a MULTISURFACE, but not CURVEPOLYGON */
uchar *output_multisurface(uchar* geom, int suppress) {
unsigned char type;
LWDEBUG(2, "output_multisurface called.");
type=*geom;
switch(TYPE_GETTYPE(type))
{
case POLYGONTYPE:
geom = output_wkt(geom,2);
break;
case CURVEPOLYTYPE:
geom = output_wkt(geom,1);
break;
}
return geom;
}
/*
* Suppress=0 -- write TYPE, M, coords
* Suppress=1 -- write TYPE, coords
* Suppress=2 -- write only coords
*/
uchar *
output_wkt(uchar* geom, int supress)
{
unsigned char type=*geom++;
char writeM=0;
dims = TYPE_NDIMS(type); /* ((type & 0x30) >> 4)+2; */
LWDEBUG(2, "output_wkt called.");
if ( ! supress && !TYPE_HASZ(type) && TYPE_HASM(type) ) writeM=1;
/* Skip the bounding box if there is one */
if ( TYPE_HASBBOX(type) )
{
geom+=16;
}
if ( TYPE_HASSRID(type) ) {
write_str("SRID=");write_int(read_int(&geom));write_str(";");
}
switch(TYPE_GETTYPE(type)) {
case POINTTYPE:
if ( supress < 2 )
{
if (writeM) write_str("POINTM");
else write_str("POINT");
}
geom=output_single(geom,0);
break;
case LINETYPE:
if ( supress < 2 )
{
if (writeM) write_str("LINESTRINGM");
else write_str("LINESTRING");
}
geom = output_line_collection(geom,output_point,0);
break;
case CIRCSTRINGTYPE:
if ( supress < 2 )
{
if(writeM) write_str("CIRCULARSTRINGM");
else write_str("CIRCULARSTRING");
}
geom = output_circstring_collection(geom,output_point,0);
break;
case POLYGONTYPE:
if ( supress < 2 )
{
if (writeM) write_str("POLYGONM");
else write_str("POLYGON");
}
geom = output_collection(geom,output_polygon_collection,0);
break;
case COMPOUNDTYPE:
if ( supress < 2 )
{
if (writeM) write_str("COMPOUNDCURVEM");
else write_str("COMPOUNDCURVE");
}
geom = output_collection(geom, output_compound,1);
break;
case CURVEPOLYTYPE:
if (supress < 2)
{
if(writeM) write_str("CURVEPOLYGONM");
else write_str("CURVEPOLYGON");
}
geom = output_collection(geom, output_compound,0);
break;
case MULTIPOINTTYPE:
if ( supress < 2 )
{
if (writeM) write_str("MULTIPOINTM");
else write_str("MULTIPOINT");
}
geom = output_collection(geom,output_multipoint,2);
break;
case MULTILINETYPE:
if ( supress < 2 )
{
if (writeM) write_str("MULTILINESTRINGM");
else write_str("MULTILINESTRING");
}
geom = output_collection(geom,output_wkt,2);
break;
case MULTICURVETYPE:
if ( supress < 2 )
{
if (writeM) write_str("MULTICURVEM");
else write_str("MULTICURVE");
}
geom = output_collection(geom,output_compound,2);
break;
case MULTIPOLYGONTYPE:
if ( supress < 2 )
{
if (writeM) write_str("MULTIPOLYGONM");
else write_str("MULTIPOLYGON");
}
geom = output_collection(geom,output_wkt,2);
break;
case MULTISURFACETYPE:
if ( supress < 2)
{
if (writeM) write_str("MULTISURFACEM");
else write_str("MULTISURFACE");
}
geom = output_collection(geom,output_multisurface,2);
break;
case COLLECTIONTYPE:
if ( supress < 2 )
{
if (writeM) write_str("GEOMETRYCOLLECTIONM");
else write_str("GEOMETRYCOLLECTION");
}
geom = output_collection(geom,output_wkt,1);
break;
case POINTTYPEI:
if ( supress < 2 )
{
if (writeM) write_str("POINTM");
else write_str("POINT");
}
lwgi++;
geom=output_single(geom,0);
lwgi--;
break;
case LINETYPEI:
if ( supress < 2 )
{
if (writeM) write_str("LINESTRINGM");
else write_str("LINESTRING");
}
lwgi++;
geom = output_collection(geom,output_point,0);
lwgi--;
break;
case POLYGONTYPEI:
if ( supress < 2 )
{
if (writeM) write_str("POLYGONM");
else write_str("POLYGON");
}
lwgi++;
geom = output_collection(geom,output_polygon_collection,0);
lwgi--;
break;
}
return geom;
}
int
unparse_WKT(LWGEOM_UNPARSER_RESULT *lwg_unparser_result, uchar* serialized, allocator alloc, freeor free, int flags)
{
LWDEBUGF(2, "unparse_WKT called with parser flags %d.", flags);
if (serialized==NULL)
return 0;
/* Setup the inital parser flags and empty the return struct */
current_lwg_unparser_result = lwg_unparser_result;
current_unparser_check_flags = flags;
lwg_unparser_result->wkoutput = NULL;
lwg_unparser_result->size = 0;
lwg_unparser_result->serialized_lwgeom = serialized;
unparser_ferror_occured = 0;
local_malloc=alloc;
local_free=free;
len = 128;
out_start = out_pos = alloc(len);
lwgi=0;
output_wkt(serialized, 0);
/* Store the result in the struct */
lwg_unparser_result->wkoutput = out_start;
lwg_unparser_result->size = strlen(out_start);
return unparser_ferror_occured;
}
static char outchr[]={"0123456789ABCDEF" };
/* Write HEX bytes flipping */
void
write_wkb_hex_flip_bytes(uchar* ptr, unsigned int cnt, size_t size)
{
unsigned int bc; /* byte count */
ensure(cnt*2*size);
while(cnt--){
for(bc=size; bc; bc--)
{
*out_pos++ = outchr[ptr[bc-1]>>4];
*out_pos++ = outchr[ptr[bc-1]&0x0F];
}
ptr+=size;
}
}
/* Write HEX bytes w/out flipping */
void
write_wkb_hex_bytes(uchar* ptr, unsigned int cnt, size_t size)
{
unsigned int bc; /* byte count */
ensure(cnt*2*size);
while(cnt--){
for(bc=0; bc>4];
*out_pos++ = outchr[ptr[bc]&0x0F];
}
ptr+=size;
}
}
/* Write BIN bytes flipping */
void
write_wkb_bin_flip_bytes(uchar* ptr, unsigned int cnt, size_t size)
{
unsigned int bc; /* byte count */
ensure(cnt*size);
while(cnt--)
{
for(bc=size; bc; bc--)
*out_pos++ = ptr[bc-1];
ptr+=size;
}
}
/* Write BIN bytes w/out flipping */
void
write_wkb_bin_bytes(uchar* ptr, unsigned int cnt, size_t size)
{
unsigned int bc; /* byte count */
ensure(cnt*size);
/* Could just use a memcpy here ... */
while(cnt--)
{
for(bc=0; bcwkoutput = NULL;
lwg_unparser_result->size = 0;
lwg_unparser_result->serialized_lwgeom = serialized;
unparser_ferror_occured = 0;
local_malloc=alloc;
local_free=free;
len = 128;
out_start = out_pos = alloc(len);
lwgi=0;
if ( endian == (char)-1 )
{
endianbyte = getMachineEndian();
if ( hex ) write_wkb_bytes = write_wkb_hex_bytes;
else write_wkb_bytes = write_wkb_bin_bytes;
}
else
{
endianbyte = endian;
if ( endianbyte != getMachineEndian() )
{
if ( hex ) write_wkb_bytes = write_wkb_hex_flip_bytes;
else write_wkb_bytes = write_wkb_bin_flip_bytes;
}
else
{
if ( hex ) write_wkb_bytes = write_wkb_hex_bytes;
else write_wkb_bytes = write_wkb_bin_bytes;
}
}
output_wkb(serialized);
if ( hex ) {
ensure(1);
*out_pos=0;
}
/* Store the result in the struct */
lwg_unparser_result->wkoutput = out_start;
lwg_unparser_result->size = (out_pos-out_start);
return unparser_ferror_occured;
}
/******************************************************************
* $Log$
* Revision 1.23 2006/02/06 11:12:22 strk
* uint32_t typedef moved back from wktparse.h to lwgparse.c and wktunparse.c
*
* Revision 1.22 2006/02/03 20:53:37 strk
* Swapped stdint.h (unavailable on Solaris9) with inttypes.h
*
* Revision 1.21 2006/02/03 09:52:14 strk
* Changed int4 typedefs to use POSIX uint32_t
*
* Revision 1.20 2006/01/09 15:12:02 strk
* ISO C90 comments
*
* Revision 1.19 2005/03/10 18:19:16 strk
* Made void args explicit to make newer compilers happy
*
* Revision 1.18 2005/02/21 16:16:14 strk
* Changed byte to uchar to avoid clashes with win32 headers.
*
* Revision 1.17 2005/02/07 13:21:10 strk
* Replaced DEBUG* macros with PGIS_DEBUG*, to avoid clashes with postgresql DEBUG
*
* Revision 1.16 2005/01/18 09:32:03 strk
* Changed unparse_WKB interface to take an output size pointer and an HEXFORM
* specifier. Reworked code in wktunparse to use function pointers.
*
* Revision 1.15 2004/12/21 15:19:01 strk
* Canonical binary reverted back to EWKB, now supporting SRID inclusion.
*
* Revision 1.14 2004/12/17 11:08:53 strk
* Moved getMachineEndian from parser to liblwgeom.{h,c}.
* Added XDR and NDR defines.
* Fixed all usage of them.
*
* Revision 1.13 2004/10/25 12:27:33 strk
* Removed useless network type includes,
* Added param.h include for BYTE_ORDER defines under win32.
*
* Revision 1.12 2004/10/21 19:48:34 strk
* Stricter syntax fixes. Reported by Sbastien NICAISE
*
* Revision 1.11 2004/10/15 07:35:41 strk
* Fixed a bug introduced by me (byteorder skipped for inner geoms in WKB)
*
* Revision 1.10 2004/10/11 14:03:33 strk
* Added endiannes specification to unparse_WKB, AsBinary, lwgeom_to_wkb.
*
******************************************************************/
================================================
FILE: src/liblwgeom/lwline.c
================================================
/**********************************************************************
* $Id: lwline.c 3639 2009-02-04 00:28:37Z pramsey $
*
* PostGIS - Spatial Types for PostgreSQL
* http://postgis.refractions.net
* Copyright 2001-2006 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.
*
**********************************************************************/
/* basic LWLINE functions */
#include
#include
#include
#include "liblwgeom.h"
/*
* Construct a new LWLINE. points will *NOT* be copied
* use SRID=-1 for unknown SRID (will have 8bit type's S = 0)
*/
LWLINE *
lwline_construct(int SRID, BOX2DFLOAT4 *bbox, POINTARRAY *points)
{
LWLINE *result;
result = (LWLINE*) lwalloc(sizeof(LWLINE));
LWDEBUG(2, "lwline_construct called.");
result->type = lwgeom_makeType_full(
TYPE_HASZ(points->dims),
TYPE_HASM(points->dims),
(SRID!=-1), LINETYPE,
0);
LWDEBUGF(3, "lwline_construct type=%d", result->type);
result->SRID = SRID;
result->points = points;
result->bbox = bbox;
return result;
}
/*
* given the LWGEOM serialized form (or a pointer into a muli* one)
* construct a proper LWLINE.
* serialized_form should point to the 8bit type format (with type = 2)
* See serialized form doc
*/
LWLINE *
lwline_deserialize(uchar *serialized_form)
{
uchar type;
LWLINE *result;
uchar *loc =NULL;
uint32 npoints;
POINTARRAY *pa;
type = (uchar) serialized_form[0];
if ( lwgeom_getType(type) != LINETYPE)
{
lwerror("lwline_deserialize: attempt to deserialize a line which is really a %s", lwgeom_typename(type));
return NULL;
}
result = (LWLINE*) lwalloc(sizeof(LWLINE)) ;
result->type = type;
loc = serialized_form+1;
if (lwgeom_hasBBOX(type))
{
LWDEBUG(3, "lwline_deserialize: input has bbox");
result->bbox = lwalloc(sizeof(BOX2DFLOAT4));
memcpy(result->bbox, loc, sizeof(BOX2DFLOAT4));
loc += sizeof(BOX2DFLOAT4);
}
else
{
result->bbox = NULL;
/*lwnotice("line has NO bbox"); */
}
if ( lwgeom_hasSRID(type))
{
/*lwnotice("line has srid"); */
result->SRID = lw_get_int32(loc);
loc +=4; /* type + SRID */
}
else
{
/*lwnotice("line has NO srid"); */
result->SRID = -1;
}
/* we've read the type (1 byte) and SRID (4 bytes, if present) */
npoints = lw_get_uint32(loc);
/*lwnotice("line npoints = %d", npoints); */
loc +=4;
pa = pointArray_construct(loc, TYPE_HASZ(type), TYPE_HASM(type), npoints);
result->points = pa;
return result;
}
/*
* convert this line into its serialize form
* result's first char will be the 8bit type. See serialized form doc
*/
uchar *
lwline_serialize(LWLINE *line)
{
size_t size, retsize;
uchar * result;
if (line == NULL) lwerror("lwline_serialize:: given null line");
size = lwline_serialize_size(line);
result = lwalloc(size);
lwline_serialize_buf(line, result, &retsize);
if ( retsize != size )
{
lwerror("lwline_serialize_size returned %d, ..serialize_buf returned %d", size, retsize);
}
return result;
}
/*
* convert this line into its serialize form writing it into
* the given buffer, and returning number of bytes written into
* the given int pointer.
* result's first char will be the 8bit type. See serialized form doc
*/
void
lwline_serialize_buf(LWLINE *line, uchar *buf, size_t *retsize)
{
char hasSRID;
uchar *loc;
int ptsize;
size_t size;
LWDEBUGF(2, "lwline_serialize_buf(%p, %p, %p) called",
line, buf, retsize);
if (line == NULL)
lwerror("lwline_serialize:: given null line");
if ( TYPE_GETZM(line->type) != TYPE_GETZM(line->points->dims) )
lwerror("Dimensions mismatch in lwline");
ptsize = pointArray_ptsize(line->points);
hasSRID = (line->SRID != -1);
buf[0] = (uchar) lwgeom_makeType_full(
TYPE_HASZ(line->type), TYPE_HASM(line->type),
hasSRID, LINETYPE, line->bbox ? 1 : 0);
loc = buf+1;
LWDEBUGF(3, "lwline_serialize_buf added type (%d)", line->type);
if (line->bbox)
{
memcpy(loc, line->bbox, sizeof(BOX2DFLOAT4));
loc += sizeof(BOX2DFLOAT4);
LWDEBUG(3, "lwline_serialize_buf added BBOX");
}
if (hasSRID)
{
memcpy(loc, &line->SRID, sizeof(int32));
loc += sizeof(int32);
LWDEBUG(3, "lwline_serialize_buf added SRID");
}
memcpy(loc, &line->points->npoints, sizeof(uint32));
loc += sizeof(uint32);
LWDEBUGF(3, "lwline_serialize_buf added npoints (%d)",
line->points->npoints);
/*copy in points */
size = line->points->npoints*ptsize;
memcpy(loc, getPoint_internal(line->points, 0), size);
loc += size;
LWDEBUGF(3, "lwline_serialize_buf copied serialized_pointlist (%d bytes)",
ptsize * line->points->npoints);
if (retsize) *retsize = loc-buf;
/*printBYTES((uchar *)result, loc-buf); */
LWDEBUGF(3, "lwline_serialize_buf returning (loc: %p, size: %d)",
loc, loc-buf);
}
/*
* Find bounding box (standard one)
* zmin=zmax=NO_Z_VALUE if 2d
*/
BOX3D *
lwline_compute_box3d(LWLINE *line)
{
BOX3D *ret;
if (line == NULL) return NULL;
ret = ptarray_compute_box3d(line->points);
return ret;
}
/* find length of this deserialized line */
size_t
lwline_serialize_size(LWLINE *line)
{
size_t size = 1; /* type */
LWDEBUG(2, "lwline_serialize_size called");
if ( line->SRID != -1 ) size += 4; /* SRID */
if ( line->bbox ) size += sizeof(BOX2DFLOAT4);
size += 4; /* npoints */
size += pointArray_ptsize(line->points)*line->points->npoints;
LWDEBUGF(3, "lwline_serialize_size returning %d", size);
return size;
}
void lwline_free (LWLINE *line)
{
if ( line->bbox )
lwfree(line->bbox);
ptarray_free(line->points);
lwfree(line);
}
/* find length of this serialized line */
size_t
lwgeom_size_line(const uchar *serialized_line)
{
int type = (uchar) serialized_line[0];
uint32 result = 1; /*type */
const uchar *loc;
uint32 npoints;
LWDEBUG(2, "lwgeom_size_line called");
if ( lwgeom_getType(type) != LINETYPE)
lwerror("lwgeom_size_line::attempt to find the length of a non-line");
loc = serialized_line+1;
if (lwgeom_hasBBOX(type))
{
loc += sizeof(BOX2DFLOAT4);
result +=sizeof(BOX2DFLOAT4);
}
if ( lwgeom_hasSRID(type))
{
loc += 4; /* type + SRID */
result +=4;
}
/* we've read the type (1 byte) and SRID (4 bytes, if present) */
npoints = lw_get_uint32(loc);
result += sizeof(uint32); /* npoints */
result += TYPE_NDIMS(type) * sizeof(double) * npoints;
LWDEBUGF(3, "lwgeom_size_line returning %d", result);
return result;
}
void printLWLINE(LWLINE *line)
{
lwnotice("LWLINE {");
lwnotice(" ndims = %i", (int)TYPE_NDIMS(line->type));
lwnotice(" SRID = %i", (int)line->SRID);
printPA(line->points);
lwnotice("}");
}
int
lwline_compute_box2d_p(LWLINE *line, BOX2DFLOAT4 *box)
{
return ptarray_compute_box2d_p(line->points, box);
}
/* Clone LWLINE object. POINTARRAY is not copied. */
LWLINE *
lwline_clone(const LWLINE *g)
{
LWLINE *ret = lwalloc(sizeof(LWLINE));
LWDEBUGF(2, "lwline_clone called with %p", g);
memcpy(ret, g, sizeof(LWLINE));
if ( g->bbox ) ret->bbox = box2d_clone(g->bbox);
return ret;
}
/*
* Add 'what' to this line at position 'where'.
* where=0 == prepend
* where=-1 == append
* Returns a MULTILINE or a GEOMETRYCOLLECTION
*/
LWGEOM *
lwline_add(const LWLINE *to, uint32 where, const LWGEOM *what)
{
LWCOLLECTION *col;
LWGEOM **geoms;
int newtype;
if ( where != -1 && where != 0 )
{
lwerror("lwline_add only supports 0 or -1 as second argument, got %d", where);
return NULL;
}
/* dimensions compatibility are checked by caller */
/* Construct geoms array */
geoms = lwalloc(sizeof(LWGEOM *)*2);
if ( where == -1 ) /* append */
{
geoms[0] = lwgeom_clone((LWGEOM *)to);
geoms[1] = lwgeom_clone(what);
}
else /* prepend */
{
geoms[0] = lwgeom_clone(what);
geoms[1] = lwgeom_clone((LWGEOM *)to);
}
/* reset SRID and wantbbox flag from component types */
geoms[0]->SRID = geoms[1]->SRID = -1;
TYPE_SETHASSRID(geoms[0]->type, 0);
TYPE_SETHASSRID(geoms[1]->type, 0);
TYPE_SETHASBBOX(geoms[0]->type, 0);
TYPE_SETHASBBOX(geoms[1]->type, 0);
/* Find appropriate geom type */
if ( TYPE_GETTYPE(what->type) == LINETYPE ) newtype = MULTILINETYPE;
else newtype = COLLECTIONTYPE;
col = lwcollection_construct(newtype,
to->SRID, NULL,
2, geoms);
return (LWGEOM *)col;
}
void
lwline_release(LWLINE *lwline)
{
lwgeom_release(lwline_as_lwgeom(lwline));
}
void
lwline_reverse(LWLINE *line)
{
ptarray_reverse(line->points);
}
LWLINE *
lwline_segmentize2d(LWLINE *line, double dist)
{
return lwline_construct(line->SRID, NULL,
ptarray_segmentize2d(line->points, dist));
}
/* check coordinate equality */
char
lwline_same(const LWLINE *l1, const LWLINE *l2)
{
return ptarray_same(l1->points, l2->points);
}
/*
* Construct a LWLINE from an array of LWPOINTs
* LWLINE dimensions are large enough to host all input dimensions.
*/
LWLINE *
lwline_from_lwpointarray(int SRID, unsigned int npoints, LWPOINT **points)
{
int zmflag=0;
unsigned int i;
POINTARRAY *pa;
uchar *newpoints, *ptr;
size_t ptsize, size;
/*
* Find output dimensions, check integrity
*/
for (i=0; itype) != POINTTYPE )
{
lwerror("lwline_from_lwpointarray: invalid input type: %s",
lwgeom_typename(TYPE_GETTYPE(points[i]->type)));
return NULL;
}
if ( TYPE_HASZ(points[i]->type) ) zmflag |= 2;
if ( TYPE_HASM(points[i]->type) ) zmflag |= 1;
if ( zmflag == 3 ) break;
}
if ( zmflag == 0 ) ptsize=2*sizeof(double);
else if ( zmflag == 3 ) ptsize=4*sizeof(double);
else ptsize=3*sizeof(double);
/*
* Allocate output points array
*/
size = ptsize*npoints;
newpoints = lwalloc(size);
memset(newpoints, 0, size);
ptr=newpoints;
for (i=0; ipoint);
memcpy(ptr, getPoint_internal(points[i]->point, 0), size);
ptr+=ptsize;
}
pa = pointArray_construct(newpoints, zmflag&2, zmflag&1, npoints);
return lwline_construct(SRID, NULL, pa);
}
/*
* Construct a LWLINE from a LWMPOINT
*/
LWLINE *
lwline_from_lwmpoint(int SRID, LWMPOINT *mpoint)
{
unsigned int i;
POINTARRAY *pa;
char zmflag = TYPE_GETZM(mpoint->type);
size_t ptsize, size;
uchar *newpoints, *ptr;
if ( zmflag == 0 ) ptsize=2*sizeof(double);
else if ( zmflag == 3 ) ptsize=4*sizeof(double);
else ptsize=3*sizeof(double);
/* Allocate space for output points */
size = ptsize*mpoint->ngeoms;
newpoints = lwalloc(size);
memset(newpoints, 0, size);
ptr=newpoints;
for (i=0; ingeoms; i++)
{
memcpy(ptr,
getPoint_internal(mpoint->geoms[i]->point, 0),
ptsize);
ptr+=ptsize;
}
pa = pointArray_construct(newpoints, zmflag&2, zmflag&1,
mpoint->ngeoms);
LWDEBUGF(3, "lwline_from_lwmpoint: constructed pointarray for %d points, %d zmflag", mpoint->ngeoms, zmflag);
return lwline_construct(SRID, NULL, pa);
}
LWLINE *
lwline_addpoint(LWLINE *line, LWPOINT *point, unsigned int where)
{
POINTARRAY *newpa;
LWLINE *ret;
newpa = ptarray_addPoint(line->points,
getPoint_internal(point->point, 0),
TYPE_NDIMS(point->type), where);
ret = lwline_construct(line->SRID, NULL, newpa);
return ret;
}
LWLINE *
lwline_removepoint(LWLINE *line, unsigned int index)
{
POINTARRAY *newpa;
LWLINE *ret;
newpa = ptarray_removePoint(line->points, index);
ret = lwline_construct(line->SRID, NULL, newpa);
return ret;
}
/*
* Note: input will be changed, make sure you have permissions for this.
*/
void
lwline_setPoint4d(LWLINE *line, unsigned int index, POINT4D *newpoint)
{
setPoint4d(line->points, index, newpoint);
}
================================================
FILE: src/liblwgeom/lwmcurve.c
================================================
/**********************************************************************
* $Id: lwmcurve.c 3639 2009-02-04 00:28:37Z pramsey $
*
* PostGIS - Spatial Types for PostgreSQL
* http://postgis.refractions.net
* Copyright 2001-2006 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.
*
**********************************************************************/
#include
#include
#include
#include "liblwgeom.h"
LWMCURVE *
lwmcurve_deserialize(uchar *srl)
{
LWMCURVE *result;
LWGEOM_INSPECTED *insp;
int stype;
int type = lwgeom_getType(srl[0]);
int i;
if(type != MULTICURVETYPE)
{
lwerror("lwmcurve_deserialize called on NON multicurve: %d", type);
return NULL;
}
insp = lwgeom_inspect(srl);
result = lwalloc(sizeof(LWMCURVE));
result->type = insp->type;
result->SRID = insp->SRID;
result->ngeoms = insp->ngeometries;
result->geoms = lwalloc(sizeof(LWGEOM *)*insp->ngeometries);
if(lwgeom_hasBBOX(srl[0]))
{
result->bbox = lwalloc(sizeof(BOX2DFLOAT4));
memcpy(result->bbox, srl+1, sizeof(BOX2DFLOAT4));
}
else result->bbox = NULL;
for(i = 0; i < insp->ngeometries; i++)
{
stype = lwgeom_getType(insp->sub_geoms[i][0]);
if(stype == CIRCSTRINGTYPE)
{
result->geoms[i] = (LWGEOM *)lwcircstring_deserialize(insp->sub_geoms[i]);
}
else if(stype == LINETYPE)
{
result->geoms[i] = (LWGEOM *)lwline_deserialize(insp->sub_geoms[i]);
}
else
{
lwerror("Only Circular and Line strings are currenly permitted in a MultiCurve.");
lwfree(result);
lwfree(insp);
return NULL;
}
if(TYPE_NDIMS(result->geoms[i]->type) != TYPE_NDIMS(result->type))
{
lwerror("Mixed dimensions (multicurve: %d, curve %d:%d)",
TYPE_NDIMS(result->type), i,
TYPE_NDIMS(result->geoms[i]->type));
lwfree(result);
lwfree(insp);
return NULL;
}
}
return result;
}
/*
* Add 'what' to this multicurve at position 'where'.
* where=0 == prepend
* where=-1 == append
* Returns a MULTICURVE or a COLLECTION
*/
LWGEOM *
lwmcurve_add(const LWMCURVE *to, uint32 where, const LWGEOM *what)
{
LWCOLLECTION *col;
LWGEOM **geoms;
int newtype;
uint32 i;
if(where == -1) where = to->ngeoms;
else if(where < -1 || where > to->ngeoms)
{
lwerror("lwmcurve_add: add position out of range %d..%d",
-1, to->ngeoms);
return NULL;
}
/* dimensions compatibility are checked by caller */
/* Construct geoms array */
geoms = lwalloc(sizeof(LWGEOM *)*(to->ngeoms+1));
for(i = 0; i < where; i++)
{
geoms[i] = lwgeom_clone((LWGEOM *)to->geoms[i]);
}
geoms[where] = lwgeom_clone(what);
for(i = where; i < to->ngeoms; i++)
{
geoms[i+1] = lwgeom_clone((LWGEOM *)to->geoms[i]);
}
if(TYPE_GETTYPE(what->type) == CIRCSTRINGTYPE) newtype = MULTICURVETYPE;
else newtype = COLLECTIONTYPE;
col = lwcollection_construct(newtype,
to->SRID, NULL,
to->ngeoms + 1, geoms);
return (LWGEOM *)col;
}
================================================
FILE: src/liblwgeom/lwmline.c
================================================
/**********************************************************************
* $Id: lwmline.c 3639 2009-02-04 00:28:37Z pramsey $
*
* PostGIS - Spatial Types for PostgreSQL
* http://postgis.refractions.net
* Copyright 2001-2006 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.
*
**********************************************************************/
#include
#include
#include
#include "liblwgeom.h"
void
lwmline_release(LWMLINE *lwmline)
{
lwgeom_release(lwmline_as_lwgeom(lwmline));
}
LWMLINE *
lwmline_deserialize(uchar *srl)
{
LWMLINE *result;
LWGEOM_INSPECTED *insp;
int type = lwgeom_getType(srl[0]);
int i;
if ( type != MULTILINETYPE )
{
lwerror("lwmline_deserialize called on NON multiline: %d",
type);
return NULL;
}
insp = lwgeom_inspect(srl);
result = lwalloc(sizeof(LWMLINE));
result->type = insp->type;
result->SRID = insp->SRID;
result->ngeoms = insp->ngeometries;
result->geoms = lwalloc(sizeof(LWLINE *)*insp->ngeometries);
if (lwgeom_hasBBOX(srl[0]))
{
result->bbox = lwalloc(sizeof(BOX2DFLOAT4));
memcpy(result->bbox, srl+1, sizeof(BOX2DFLOAT4));
}
else result->bbox = NULL;
for (i=0; ingeometries; i++)
{
result->geoms[i] = lwline_deserialize(insp->sub_geoms[i]);
if ( TYPE_NDIMS(result->geoms[i]->type) != TYPE_NDIMS(result->type) )
{
lwerror("Mixed dimensions (multiline:%d, line%d:%d)",
TYPE_NDIMS(result->type), i,
TYPE_NDIMS(result->geoms[i]->type)
);
return NULL;
}
}
return result;
}
/*
* Add 'what' to this multiline at position 'where'.
* where=0 == prepend
* where=-1 == append
* Returns a MULTILINE or a COLLECTION
*/
LWGEOM *
lwmline_add(const LWMLINE *to, uint32 where, const LWGEOM *what)
{
LWCOLLECTION *col;
LWGEOM **geoms;
int newtype;
uint32 i;
if ( where == -1 ) where = to->ngeoms;
else if ( where < -1 || where > to->ngeoms )
{
lwerror("lwmline_add: add position out of range %d..%d",
-1, to->ngeoms);
return NULL;
}
/* dimensions compatibility are checked by caller */
/* Construct geoms array */
geoms = lwalloc(sizeof(LWGEOM *)*(to->ngeoms+1));
for (i=0; igeoms[i]);
}
geoms[where] = lwgeom_clone(what);
for (i=where; ingeoms; i++)
{
geoms[i+1] = lwgeom_clone((LWGEOM *)to->geoms[i]);
}
if ( TYPE_GETTYPE(what->type) == LINETYPE ) newtype = MULTILINETYPE;
else newtype = COLLECTIONTYPE;
col = lwcollection_construct(newtype,
to->SRID, NULL,
to->ngeoms+1, geoms);
return (LWGEOM *)col;
}
void lwmline_free(LWMLINE *mline)
{
int i;
if( mline->bbox )
{
lwfree(mline->bbox);
}
for ( i = 0; i < mline->ngeoms; i++ )
{
if( mline->geoms[i] ) {
lwline_free(mline->geoms[i]);
}
}
if( mline->geoms )
{
lwfree(mline->geoms);
}
lwfree(mline);
};
================================================
FILE: src/liblwgeom/lwmpoint.c
================================================
/**********************************************************************
* $Id: lwmpoint.c 3639 2009-02-04 00:28:37Z pramsey $
*
* PostGIS - Spatial Types for PostgreSQL
* http://postgis.refractions.net
* Copyright 2001-2006 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.
*
**********************************************************************/
#include
#include
#include
#include "liblwgeom.h"
void
lwmpoint_release(LWMPOINT *lwmpoint)
{
lwgeom_release(lwmpoint_as_lwgeom(lwmpoint));
}
LWMPOINT *
lwmpoint_deserialize(uchar *srl)
{
LWMPOINT *result;
LWGEOM_INSPECTED *insp;
int type = lwgeom_getType(srl[0]);
int i;
if ( type != MULTIPOINTTYPE )
{
lwerror("lwmpoint_deserialize called on NON multipoint: %d",
type);
return NULL;
}
insp = lwgeom_inspect(srl);
result = lwalloc(sizeof(LWMPOINT));
result->type = insp->type;
result->SRID = insp->SRID;
result->ngeoms = insp->ngeometries;
result->geoms = lwalloc(sizeof(LWPOINT *)*result->ngeoms);
if (lwgeom_hasBBOX(srl[0]))
{
result->bbox = lwalloc(sizeof(BOX2DFLOAT4));
memcpy(result->bbox, srl+1, sizeof(BOX2DFLOAT4));
}
else result->bbox = NULL;
for (i=0; ingeometries; i++)
{
result->geoms[i] = lwpoint_deserialize(insp->sub_geoms[i]);
if ( TYPE_NDIMS(result->geoms[i]->type) != TYPE_NDIMS(result->type) )
{
lwerror("Mixed dimensions (multipoint:%d, point%d:%d)",
TYPE_NDIMS(result->type), i,
TYPE_NDIMS(result->geoms[i]->type)
);
return NULL;
}
}
return result;
}
/*
* Add 'what' to this multipoint at position 'where'.
* where=0 == prepend
* where=-1 == append
* Returns a MULTIPOINT or a COLLECTION
*/
LWGEOM *
lwmpoint_add(const LWMPOINT *to, uint32 where, const LWGEOM *what)
{
LWCOLLECTION *col;
LWGEOM **geoms;
int newtype;
uint32 i;
if ( where == -1 ) where = to->ngeoms;
else if ( where < -1 || where > to->ngeoms )
{
lwerror("lwmpoint_add: add position out of range %d..%d",
-1, to->ngeoms);
return NULL;
}
/* dimensions compatibility are checked by caller */
/* Construct geoms array */
geoms = lwalloc(sizeof(LWGEOM *)*(to->ngeoms+1));
for (i=0; igeoms[i]);
}
geoms[where] = lwgeom_clone(what);
for (i=where; ingeoms; i++)
{
geoms[i+1] = lwgeom_clone((LWGEOM *)to->geoms[i]);
}
if ( TYPE_GETTYPE(what->type) == POINTTYPE ) newtype = MULTIPOINTTYPE;
else newtype = COLLECTIONTYPE;
col = lwcollection_construct(newtype,
to->SRID, NULL,
to->ngeoms+1, geoms);
return (LWGEOM *)col;
}
void lwmpoint_free(LWMPOINT *mpt)
{
int i;
if( mpt->bbox )
{
lwfree(mpt->bbox);
}
for ( i = 0; i < mpt->ngeoms; i++ )
{
if( mpt->geoms[i] ) {
lwpoint_free(mpt->geoms[i]);
}
}
if( mpt->geoms )
{
lwfree(mpt->geoms);
}
lwfree(mpt);
};
================================================
FILE: src/liblwgeom/lwmpoly.c
================================================
/**********************************************************************
* $Id: lwmpoly.c 3639 2009-02-04 00:28:37Z pramsey $
*
* PostGIS - Spatial Types for PostgreSQL
* http://postgis.refractions.net
* Copyright 2001-2006 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.
*
**********************************************************************/
#include
#include
#include
#include "liblwgeom.h"
void
lwmpoly_release(LWMPOLY *lwmpoly)
{
lwgeom_release(lwmpoly_as_lwgeom(lwmpoly));
}
LWMPOLY *
lwmpoly_deserialize(uchar *srl)
{
LWMPOLY *result;
LWGEOM_INSPECTED *insp;
int type = lwgeom_getType(srl[0]);
int i;
LWDEBUG(2, "lwmpoly_deserialize called");
if ( type != MULTIPOLYGONTYPE )
{
lwerror("lwmpoly_deserialize called on NON multipoly: %d",
type);
return NULL;
}
insp = lwgeom_inspect(srl);
result = lwalloc(sizeof(LWMPOLY));
result->type = insp->type;
result->SRID = insp->SRID;
result->ngeoms = insp->ngeometries;
result->geoms = lwalloc(sizeof(LWPOLY *)*insp->ngeometries);
if (lwgeom_hasBBOX(srl[0]))
{
result->bbox = lwalloc(sizeof(BOX2DFLOAT4));
memcpy(result->bbox, srl+1, sizeof(BOX2DFLOAT4));
}
else result->bbox = NULL;
for (i=0; ingeometries; i++)
{
result->geoms[i] = lwpoly_deserialize(insp->sub_geoms[i]);
if ( TYPE_NDIMS(result->geoms[i]->type) != TYPE_NDIMS(result->type) )
{
lwerror("Mixed dimensions (multipoly:%d, poly%d:%d)",
TYPE_NDIMS(result->type), i,
TYPE_NDIMS(result->geoms[i]->type)
);
return NULL;
}
}
return result;
}
/*
* Add 'what' to this multiline at position 'where'.
* where=0 == prepend
* where=-1 == append
* Returns a MULTIPOLY or a COLLECTION
*/
LWGEOM *
lwmpoly_add(const LWMPOLY *to, uint32 where, const LWGEOM *what)
{
LWCOLLECTION *col;
LWGEOM **geoms;
int newtype;
uint32 i;
if ( where == -1 ) where = to->ngeoms;
else if ( where < -1 || where > to->ngeoms )
{
lwerror("lwmline_add: add position out of range %d..%d",
-1, to->ngeoms);
return NULL;
}
/* dimensions compatibility are checked by caller */
/* Construct geoms array */
geoms = lwalloc(sizeof(LWGEOM *)*(to->ngeoms+1));
for (i=0; igeoms[i]);
}
geoms[where] = lwgeom_clone(what);
for (i=where; ingeoms; i++)
{
geoms[i+1] = lwgeom_clone((LWGEOM *)to->geoms[i]);
}
if ( TYPE_GETTYPE(what->type) == POLYGONTYPE ) newtype = MULTIPOLYGONTYPE;
else newtype = COLLECTIONTYPE;
col = lwcollection_construct(newtype,
to->SRID, NULL,
to->ngeoms+1, geoms);
return (LWGEOM *)col;
}
void lwmpoly_free(LWMPOLY *mpoly)
{
int i;
if( mpoly->bbox )
{
lwfree(mpoly->bbox);
}
for ( i = 0; i < mpoly->ngeoms; i++ )
{
if( mpoly->geoms[i] ) {
lwpoly_free(mpoly->geoms[i]);
}
}
if( mpoly->geoms )
{
lwfree(mpoly->geoms);
}
lwfree(mpoly);
};
================================================
FILE: src/liblwgeom/lwmsurface.c
================================================
/**********************************************************************
* $Id: lwmsurface.c 3639 2009-02-04 00:28:37Z pramsey $
*
* PostGIS - Spatial Types for PostgreSQL
* http://postgis.refractions.net
* Copyright 2001-2006 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.
*
**********************************************************************/
#include
#include
#include
#include "liblwgeom.h"
LWMSURFACE *
lwmsurface_deserialize(uchar *srl)
{
LWMSURFACE *result;
LWGEOM_INSPECTED *insp;
int stype;
int type = lwgeom_getType(srl[0]);
int i;
LWDEBUG(2, "lwmsurface_deserialize called");
if(type != MULTISURFACETYPE)
{
lwerror("lwmsurface_deserialize called on a non-multisurface: %d", type);
return NULL;
}
insp = lwgeom_inspect(srl);
result = lwalloc(sizeof(LWMSURFACE));
result->type = insp->type;
result->SRID = insp->SRID;
result->ngeoms = insp->ngeometries;
result->geoms = lwalloc(sizeof(LWPOLY *)*insp->ngeometries);
if(lwgeom_hasBBOX(srl[0]))
{
result->bbox = lwalloc(sizeof(BOX2DFLOAT4));
memcpy(result->bbox, srl + 1, sizeof(BOX2DFLOAT4));
}
else result->bbox = NULL;
for(i = 0; i < insp->ngeometries; i++)
{
stype = lwgeom_getType(insp->sub_geoms[i][0]);
if(stype == POLYGONTYPE)
{
result->geoms[i] = (LWGEOM *)lwpoly_deserialize(insp->sub_geoms[i]);
}
else if(stype == CURVEPOLYTYPE)
{
result->geoms[i] = (LWGEOM *)lwcurvepoly_deserialize(insp->sub_geoms[i]);
}
else
{
lwerror("Only Polygons and Curved Polygons are supported in a MultiSurface.");
lwfree(result);
lwfree(insp);
return NULL;
}
if(TYPE_NDIMS(result->geoms[i]->type) != TYPE_NDIMS(result->type))
{
lwerror("Mixed dimensions (multisurface: %d, surface %d:%d",
TYPE_NDIMS(result->type), i,
TYPE_NDIMS(result->geoms[i]->type));
lwfree(result);
lwfree(insp);
return NULL;
}
}
return result;
}
/*
* Add 'what' to this multisurface at position 'where'
* where=0 == prepend
* where=-1 == append
* Returns a MULTISURFACE or a COLLECTION
*/
LWGEOM *
lwmsurface_add(const LWMSURFACE *to, uint32 where, const LWGEOM *what)
{
LWCOLLECTION *col;
LWGEOM **geoms;
int newtype;
uint32 i;
if(where == -1) where = to->ngeoms;
else if(where < -1 || where > to->ngeoms)
{
lwerror("lwmsurface_add: add position out of range %d..%d",
-1, to->ngeoms);
return NULL;
}
/* dimensions compatibility are checked by caller */
/* Construct geoms array */
geoms = lwalloc(sizeof(LWGEOM *)*(to->ngeoms+1));
for(i = 0; i < where; i++)
{
geoms[i] = lwgeom_clone((LWGEOM *)to->geoms[i]);
}
geoms[where] = lwgeom_clone(what);
for(i = where; i < to->ngeoms; i++)
{
geoms[i+1] = lwgeom_clone((LWGEOM *)to->geoms[i]);
}
if(TYPE_GETTYPE(what->type) == POLYGONTYPE
|| TYPE_GETTYPE(what->type) == CURVEPOLYTYPE)
newtype = MULTISURFACETYPE;
else newtype = COLLECTIONTYPE;
col = lwcollection_construct(newtype,
to->SRID, NULL, to->ngeoms + 1, geoms);
return (LWGEOM *)col;
}
================================================
FILE: src/liblwgeom/lwpoint.c
================================================
/**********************************************************************
* $Id: lwpoint.c 3812 2009-03-09 14:36:15Z pramsey $
*
* PostGIS - Spatial Types for PostgreSQL
* http://postgis.refractions.net
* Copyright 2001-2006 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.
*
**********************************************************************/
#include
#include
#include
#include "liblwgeom.h"
/*
* Convert this point into its serialize form
* result's first char will be the 8bit type. See serialized form doc
*/
uchar *
lwpoint_serialize(LWPOINT *point)
{
size_t size, retsize;
uchar *result;
size = lwpoint_serialize_size(point);
result = lwalloc(size);
lwpoint_serialize_buf(point, result, &retsize);
if ( retsize != size )
{
lwerror("lwpoint_serialize_size returned %d, ..serialize_buf returned %d", size, retsize);
}
return result;
}
/*
* Convert this point into its serialize form writing it into
* the given buffer, and returning number of bytes written into
* the given int pointer.
* result's first char will be the 8bit type. See serialized form doc
*/
void
lwpoint_serialize_buf(LWPOINT *point, uchar *buf, size_t *retsize)
{
int size=1;
char hasSRID;
uchar *loc;
int ptsize = pointArray_ptsize(point->point);
if ( TYPE_GETZM(point->type) != TYPE_GETZM(point->point->dims) )
lwerror("Dimensions mismatch in lwpoint");
LWDEBUGF(2, "lwpoint_serialize_buf(%p, %p) called", point, buf);
/*printLWPOINT(point); */
hasSRID = (point->SRID != -1);
if (hasSRID) size +=4; /*4 byte SRID */
if (point->bbox) size += sizeof(BOX2DFLOAT4); /* bvol */
size += sizeof(double)*TYPE_NDIMS(point->type);
buf[0] = (uchar) lwgeom_makeType_full(
TYPE_HASZ(point->type), TYPE_HASM(point->type),
hasSRID, POINTTYPE, point->bbox?1:0);
loc = buf+1;
if (point->bbox)
{
memcpy(loc, point->bbox, sizeof(BOX2DFLOAT4));
loc += sizeof(BOX2DFLOAT4);
}
if (hasSRID)
{
memcpy(loc, &point->SRID, sizeof(int32));
loc += 4;
}
/* copy in points */
memcpy(loc, getPoint_internal(point->point, 0), ptsize);
if (retsize) *retsize = size;
}
/*
* Find bounding box (standard one)
* zmin=zmax=NO_Z_VALUE if 2d
*/
BOX3D *
lwpoint_compute_box3d(LWPOINT *point)
{
LWDEBUGF(2, "lwpoint_compute_box3d called with point %p", point);
if (point == NULL)
{
LWDEBUG(3, "lwpoint_compute_box3d returning NULL");
return NULL;
}
LWDEBUG(3, "lwpoint_compute_box3d returning ptarray_compute_box3d return");
return ptarray_compute_box3d(point->point);
}
/*
* Convenience functions to hide the POINTARRAY
* TODO: obsolete this
*/
int
lwpoint_getPoint2d_p(const LWPOINT *point, POINT2D *out)
{
return getPoint2d_p(point->point, 0, out);
}
/* convenience functions to hide the POINTARRAY */
int
lwpoint_getPoint3dz_p(const LWPOINT *point, POINT3DZ *out)
{
return getPoint3dz_p(point->point,0,out);
}
int
lwpoint_getPoint3dm_p(const LWPOINT *point, POINT3DM *out)
{
return getPoint3dm_p(point->point,0,out);
}
int
lwpoint_getPoint4d_p(const LWPOINT *point, POINT4D *out)
{
return getPoint4d_p(point->point,0,out);
}
/* find length of this deserialized point */
size_t
lwpoint_serialize_size(LWPOINT *point)
{
size_t size = 1; /* type */
LWDEBUG(2, "lwpoint_serialize_size called");
if ( point->SRID != -1 ) size += 4; /* SRID */
if ( point->bbox ) size += sizeof(BOX2DFLOAT4);
size += TYPE_NDIMS(point->type) * sizeof(double); /* point */
LWDEBUGF(3, "lwpoint_serialize_size returning %d", size);
return size;
}
/*
* Construct a new point. point will not be copied
* use SRID=-1 for unknown SRID (will have 8bit type's S = 0)
*/
LWPOINT *
lwpoint_construct(int SRID, BOX2DFLOAT4 *bbox, POINTARRAY *point)
{
LWPOINT *result ;
if (point == NULL)
return NULL; /* error */
result = lwalloc(sizeof(LWPOINT));
result->type = lwgeom_makeType_full(TYPE_HASZ(point->dims), TYPE_HASM(point->dims), (SRID!=-1), POINTTYPE, 0);
result->SRID = SRID;
result->point = point;
result->bbox = bbox;
return result;
}
LWPOINT *
make_lwpoint2d(int SRID, double x, double y)
{
POINT2D p;
POINTARRAY *pa = ptarray_construct(0, 0, 1);
p.x = x;
p.y = y;
memcpy(getPoint_internal(pa, 0), &p, sizeof(POINT2D));
return lwpoint_construct(SRID, NULL, pa);
}
LWPOINT *
make_lwpoint3dz(int SRID, double x, double y, double z)
{
POINT3DZ p;
POINTARRAY *pa = ptarray_construct(1, 0, 1);
p.x = x;
p.y = y;
p.z = z;
memcpy(getPoint_internal(pa, 0), &p, sizeof(POINT3DZ));
return lwpoint_construct(SRID, NULL, pa);
}
LWPOINT *
make_lwpoint3dm(int SRID, double x, double y, double m)
{
POINTARRAY *pa = ptarray_construct(0, 1, 1);
POINT3DM p;
p.x = x;
p.y = y;
p.m = m;
memcpy(getPoint_internal(pa, 0), &p, sizeof(POINT3DM));
return lwpoint_construct(SRID, NULL, pa);
}
LWPOINT *
make_lwpoint4d(int SRID, double x, double y, double z, double m)
{
POINTARRAY *pa = ptarray_construct(1, 1, 1);
POINT4D p;
p.x = x;
p.y = y;
p.z = z;
p.m = m;
memcpy(getPoint_internal(pa, 0), &p, sizeof(POINT4D));
return lwpoint_construct(SRID, NULL, pa);
}
/*
* Given the LWPOINT serialized form (or a pointer into a muli* one)
* construct a proper LWPOINT.
* serialized_form should point to the 8bit type format (with type = 1)
* See serialized form doc
*/
LWPOINT *
lwpoint_deserialize(uchar *serialized_form)
{
uchar type;
int geom_type;
LWPOINT *result;
uchar *loc = NULL;
POINTARRAY *pa;
LWDEBUG(2, "lwpoint_deserialize called");
result = (LWPOINT*) lwalloc(sizeof(LWPOINT)) ;
type = serialized_form[0];
geom_type = lwgeom_getType(type);
if ( geom_type != POINTTYPE)
{
lwerror("lwpoint_deserialize: attempt to deserialize a point which is really a %s", lwgeom_typename(geom_type));
return NULL;
}
result->type = type;
loc = serialized_form+1;
if (lwgeom_hasBBOX(type))
{
LWDEBUG(3, "lwpoint_deserialize: input has bbox");
result->bbox = lwalloc(sizeof(BOX2DFLOAT4));
memcpy(result->bbox, loc, sizeof(BOX2DFLOAT4));
loc += sizeof(BOX2DFLOAT4);
}
else
{
result->bbox = NULL;
}
if ( lwgeom_hasSRID(type))
{
LWDEBUG(3, "lwpoint_deserialize: input has SRID");
result->SRID = lw_get_int32(loc);
loc += 4; /* type + SRID */
}
else
{
result->SRID = -1;
}
/* we've read the type (1 byte) and SRID (4 bytes, if present) */
pa = pointArray_construct(loc, TYPE_HASZ(type), TYPE_HASM(type), 1);
result->point = pa;
return result;
}
void lwpoint_free(LWPOINT *pt)
{
if(pt->point)
ptarray_free(pt->point);
lwfree(pt);
}
void printLWPOINT(LWPOINT *point)
{
lwnotice("LWPOINT {");
lwnotice(" ndims = %i", (int)TYPE_NDIMS(point->type));
lwnotice(" BBOX = %i", TYPE_HASBBOX(point->type) ? 1 : 0 );
lwnotice(" SRID = %i", (int)point->SRID);
printPA(point->point);
lwnotice("}");
}
int
lwpoint_compute_box2d_p(LWPOINT *point, BOX2DFLOAT4 *box)
{
return ptarray_compute_box2d_p(point->point, box);
}
/* Clone LWPOINT object. POINTARRAY is not copied. */
LWPOINT *
lwpoint_clone(const LWPOINT *g)
{
LWPOINT *ret = lwalloc(sizeof(LWPOINT));
LWDEBUG(2, "lwpoint_clone called");
memcpy(ret, g, sizeof(LWPOINT));
if ( g->bbox ) ret->bbox = box2d_clone(g->bbox);
return ret;
}
/*
* Add 'what' to this point at position 'where'.
* where=0 == prepend
* where=-1 == append
* Returns a MULTIPOINT or a GEOMETRYCOLLECTION
*/
LWGEOM *
lwpoint_add(const LWPOINT *to, uint32 where, const LWGEOM *what)
{
LWCOLLECTION *col;
LWGEOM **geoms;
int newtype;
if ( where != -1 && where != 0 )
{
lwerror("lwpoint_add only supports 0 or -1 as second argument, got %d", where);
return NULL;
}
/* dimensions compatibility are checked by caller */
/* Construct geoms array */
geoms = lwalloc(sizeof(LWGEOM *)*2);
if ( where == -1 ) /* append */
{
geoms[0] = lwgeom_clone((LWGEOM *)to);
geoms[1] = lwgeom_clone(what);
}
else /* prepend */
{
geoms[0] = lwgeom_clone(what);
geoms[1] = lwgeom_clone((LWGEOM *)to);
}
/* reset SRID and wantbbox flag from component types */
lwgeom_dropSRID(geoms[0]);
lwgeom_drop_bbox(geoms[0]);
lwgeom_dropSRID(geoms[1]);
lwgeom_drop_bbox(geoms[1]);
/* Find appropriate geom type */
if ( TYPE_GETTYPE(what->type) == POINTTYPE ) newtype = MULTIPOINTTYPE;
else newtype = COLLECTIONTYPE;
col = lwcollection_construct(newtype,
to->SRID, NULL,
2, geoms);
return (LWGEOM *)col;
}
/* Find length of this serialized point */
size_t
lwgeom_size_point(const uchar *serialized_point)
{
uint32 result = 1;
uchar type;
const uchar *loc;
type = serialized_point[0];
if ( lwgeom_getType(type) != POINTTYPE) return 0;
LWDEBUGF(2, "lwgeom_size_point called (%d)", result);
loc = serialized_point+1;
if (lwgeom_hasBBOX(type))
{
loc += sizeof(BOX2DFLOAT4);
result +=sizeof(BOX2DFLOAT4);
LWDEBUGF(3, "lwgeom_size_point: has bbox (%d)", result);
}
if ( lwgeom_hasSRID(type))
{
LWDEBUGF(3, "lwgeom_size_point: has srid (%d)", result);
loc +=4; /* type + SRID */
result +=4;
}
result += lwgeom_ndims(type)*sizeof(double);
return result;
}
void
lwpoint_release(LWPOINT *lwpoint)
{
lwgeom_release(lwpoint_as_lwgeom(lwpoint));
}
/* check coordinate equality */
char
lwpoint_same(const LWPOINT *p1, const LWPOINT *p2)
{
return ptarray_same(p1->point, p2->point);
}
================================================
FILE: src/liblwgeom/lwpoly.c
================================================
/**********************************************************************
* $Id: lwpoly.c 3639 2009-02-04 00:28:37Z pramsey $
*
* PostGIS - Spatial Types for PostgreSQL
* http://postgis.refractions.net
* Copyright 2001-2006 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.
*
**********************************************************************/
/* basic LWPOLY manipulation */
#include
#include
#include
#include "liblwgeom.h"
#define CHECK_POLY_RINGS_ZM 1
/* construct a new LWPOLY. arrays (points/points per ring) will NOT be copied
* use SRID=-1 for unknown SRID (will have 8bit type's S = 0)
*/
LWPOLY *
lwpoly_construct(int SRID, BOX2DFLOAT4 *bbox, unsigned int nrings, POINTARRAY **points)
{
LWPOLY *result;
int hasz, hasm;
#ifdef CHECK_POLY_RINGS_ZM
char zm;
unsigned int i;
#endif
if ( nrings < 1 ) lwerror("lwpoly_construct: need at least 1 ring");
hasz = TYPE_HASZ(points[0]->dims);
hasm = TYPE_HASM(points[0]->dims);
#ifdef CHECK_POLY_RINGS_ZM
zm = TYPE_GETZM(points[0]->dims);
for (i=1; idims) )
lwerror("lwpoly_construct: mixed dimensioned rings");
}
#endif
result = (LWPOLY*) lwalloc(sizeof(LWPOLY));
result->type = lwgeom_makeType_full(hasz, hasm, (SRID!=-1), POLYGONTYPE,
0);
result->SRID = SRID;
result->nrings = nrings;
result->rings = points;
result->bbox = bbox;
return result;
}
/*
* given the LWPOLY serialized form (or a pointer into a muli* one)
* construct a proper LWPOLY.
* serialized_form should point to the 8bit type format (with type = 3)
* See serialized form doc
*/
LWPOLY *
lwpoly_deserialize(uchar *serialized_form)
{
LWPOLY *result;
uint32 nrings;
int ndims, hasz, hasm;
uint32 npoints;
uchar type;
uchar *loc;
int t;
if (serialized_form == NULL)
{
lwerror("lwpoly_deserialize called with NULL arg");
return NULL;
}
result = (LWPOLY*) lwalloc(sizeof(LWPOLY));
type = serialized_form[0];
result->type = type;
ndims = TYPE_NDIMS(type);
hasz = TYPE_HASZ(type);
hasm = TYPE_HASM(type);
loc = serialized_form;
if ( TYPE_GETTYPE(type) != POLYGONTYPE)
{
lwerror("lwpoly_deserialize: attempt to deserialize a poly which is really a %s", lwgeom_typename(type));
return NULL;
}
loc = serialized_form+1;
if (lwgeom_hasBBOX(type)) {
LWDEBUG(3, "lwpoly_deserialize: input has bbox");
result->bbox = lwalloc(sizeof(BOX2DFLOAT4));
memcpy(result->bbox, loc, sizeof(BOX2DFLOAT4));
loc += sizeof(BOX2DFLOAT4);
} else {
result->bbox = NULL;
}
if ( lwgeom_hasSRID(type))
{
result->SRID = lw_get_int32(loc);
loc +=4; /* type + SRID */
}
else
{
result->SRID = -1;
}
nrings = lw_get_uint32(loc);
result->nrings = nrings;
loc +=4;
result->rings = (POINTARRAY**) lwalloc(nrings* sizeof(POINTARRAY*));
for (t =0;trings[t] = pointArray_construct(loc, hasz, hasm, npoints);
loc += sizeof(double)*ndims*npoints;
}
return result;
}
/*
* create the serialized form of the polygon
* result's first char will be the 8bit type. See serialized form doc
* points copied
*/
uchar *
lwpoly_serialize(LWPOLY *poly)
{
size_t size, retsize;
uchar *result;
size = lwpoly_serialize_size(poly);
result = lwalloc(size);
lwpoly_serialize_buf(poly, result, &retsize);
if ( retsize != size )
{
lwerror("lwpoly_serialize_size returned %d, ..serialize_buf returned %d", size, retsize);
}
return result;
}
/*
* create the serialized form of the polygon writing it into the
* given buffer, and returning number of bytes written into
* the given int pointer.
* result's first char will be the 8bit type. See serialized form doc
* points copied
*/
void
lwpoly_serialize_buf(LWPOLY *poly, uchar *buf, size_t *retsize)
{
size_t size=1; /* type byte */
char hasSRID;
int t;
uchar *loc;
int ptsize;
LWDEBUG(2, "lwpoly_serialize_buf called");
ptsize = sizeof(double)*TYPE_NDIMS(poly->type);
hasSRID = (poly->SRID != -1);
size += 4; /* nrings */
size += 4*poly->nrings; /* npoints/ring */
buf[0] = (uchar) lwgeom_makeType_full(
TYPE_HASZ(poly->type), TYPE_HASM(poly->type),
hasSRID, POLYGONTYPE, poly->bbox ? 1 : 0);
loc = buf+1;
if (poly->bbox)
{
memcpy(loc, poly->bbox, sizeof(BOX2DFLOAT4));
size += sizeof(BOX2DFLOAT4); /* bvol */
loc += sizeof(BOX2DFLOAT4);
}
if (hasSRID)
{
memcpy(loc, &poly->SRID, sizeof(int32));
loc += 4;
size +=4; /* 4 byte SRID */
}
memcpy(loc, &poly->nrings, sizeof(int32)); /* nrings */
loc+=4;
for (t=0;tnrings;t++)
{
POINTARRAY *pa = poly->rings[t];
size_t pasize;
uint32 npoints;
if ( TYPE_GETZM(poly->type) != TYPE_GETZM(pa->dims) )
lwerror("Dimensions mismatch in lwpoly");
npoints = pa->npoints;
memcpy(loc, &npoints, sizeof(uint32)); /* npoints this ring */
loc+=4;
pasize = npoints*ptsize;
size += pasize;
/* copy points */
memcpy(loc, getPoint_internal(pa, 0), pasize);
loc += pasize;
}
if (retsize) *retsize = size;
}
/* find bounding box (standard one) zmin=zmax=0 if 2d (might change to NaN) */
BOX3D *
lwpoly_compute_box3d(LWPOLY *poly)
{
BOX3D *result;
/* just need to check outer ring -- interior rings are inside */
POINTARRAY *pa = poly->rings[0];
result = ptarray_compute_box3d(pa);
return result;
}
/* find length of this serialized polygon */
size_t
lwgeom_size_poly(const uchar *serialized_poly)
{
uint32 result = 1; /* char type */
uint32 nrings;
int ndims;
int t;
uchar type;
uint32 npoints;
const uchar *loc;
if (serialized_poly == NULL)
return -9999;
type = (uchar) serialized_poly[0];
ndims = lwgeom_ndims(type);
if ( lwgeom_getType(type) != POLYGONTYPE)
return -9999;
loc = serialized_poly+1;
if (lwgeom_hasBBOX(type))
{
LWDEBUG(3, "lwgeom_size_poly: has bbox");
loc += sizeof(BOX2DFLOAT4);
result +=sizeof(BOX2DFLOAT4);
}
if ( lwgeom_hasSRID(type))
{
LWDEBUG(3, "lwgeom_size_poly: has srid");
loc +=4; /* type + SRID */
result += 4;
}
nrings = lw_get_uint32(loc);
loc +=4;
result +=4;
LWDEBUGF(3, "lwgeom_size_poly contains %d rings", nrings);
for (t =0;tSRID != -1 ) size += 4; /* SRID */
if ( poly->bbox ) size += sizeof(BOX2DFLOAT4);
LWDEBUGF(2, "lwpoly_serialize_size called with poly[%p] (%d rings)",
poly, poly->nrings);
size += 4; /* nrings */
for (i=0; inrings; i++)
{
size += 4; /* npoints */
size += poly->rings[i]->npoints*TYPE_NDIMS(poly->type)*sizeof(double);
}
LWDEBUGF(3, "lwpoly_serialize_size returning %d", size);
return size;
}
void lwpoly_free (LWPOLY *poly)
{
int t;
if ( poly->bbox )
lwfree(poly->bbox);
for (t=0;tnrings;t++)
{
if( poly->rings[t] )
ptarray_free(poly->rings[t]);
}
if ( poly->rings )
lwfree(poly->rings);
lwfree(poly);
}
void printLWPOLY(LWPOLY *poly)
{
int t;
lwnotice("LWPOLY {");
lwnotice(" ndims = %i", (int)TYPE_NDIMS(poly->type));
lwnotice(" SRID = %i", (int)poly->SRID);
lwnotice(" nrings = %i", (int)poly->nrings);
for (t=0;tnrings;t++)
{
lwnotice(" RING # %i :",t);
printPA(poly->rings[t]);
}
lwnotice("}");
}
int
lwpoly_compute_box2d_p(LWPOLY *poly, BOX2DFLOAT4 *box)
{
BOX2DFLOAT4 boxbuf;
uint32 i;
if ( ! poly->nrings ) return 0;
if ( ! ptarray_compute_box2d_p(poly->rings[0], box) ) return 0;
for (i=1; inrings; i++)
{
if ( ! ptarray_compute_box2d_p(poly->rings[0], &boxbuf) )
return 0;
if ( ! box2d_union_p(box, &boxbuf, box) )
return 0;
}
return 1;
}
/* Clone LWLINE object. POINTARRAY are not copied, it's ring array is. */
LWPOLY *
lwpoly_clone(const LWPOLY *g)
{
LWPOLY *ret = lwalloc(sizeof(LWPOLY));
memcpy(ret, g, sizeof(LWPOLY));
ret->rings = lwalloc(sizeof(POINTARRAY *)*g->nrings);
memcpy(ret->rings, g->rings, sizeof(POINTARRAY *)*g->nrings);
if ( g->bbox ) ret->bbox = box2d_clone(g->bbox);
return ret;
}
/*
* Add 'what' to this poly at position 'where'.
* where=0 == prepend
* where=-1 == append
* Returns a MULTIPOLYGON or a GEOMETRYCOLLECTION
*/
LWGEOM *
lwpoly_add(const LWPOLY *to, uint32 where, const LWGEOM *what)
{
LWCOLLECTION *col;
LWGEOM **geoms;
int newtype;
if ( where != -1 && where != 0 )
{
lwerror("lwpoly_add only supports 0 or -1 as second argument, got %d", where);
return NULL;
}
/* dimensions compatibility are checked by caller */
/* Construct geoms array */
geoms = lwalloc(sizeof(LWGEOM *)*2);
if ( where == -1 ) /* append */
{
geoms[0] = lwgeom_clone((LWGEOM *)to);
geoms[1] = lwgeom_clone(what);
}
else /* prepend */
{
geoms[0] = lwgeom_clone(what);
geoms[1] = lwgeom_clone((LWGEOM *)to);
}
/* reset SRID and wantbbox flag from component types */
geoms[0]->SRID = geoms[1]->SRID = -1;
TYPE_SETHASSRID(geoms[0]->type, 0);
TYPE_SETHASSRID(geoms[1]->type, 0);
TYPE_SETHASBBOX(geoms[0]->type, 0);
TYPE_SETHASBBOX(geoms[1]->type, 0);
/* Find appropriate geom type */
if ( TYPE_GETTYPE(what->type) == POLYGONTYPE ) newtype = MULTIPOLYGONTYPE;
else newtype = COLLECTIONTYPE;
col = lwcollection_construct(newtype,
to->SRID, NULL,
2, geoms);
return (LWGEOM *)col;
}
void
lwpoly_forceRHR(LWPOLY *poly)
{
int i;
if ( ptarray_isccw(poly->rings[0]) )
{
ptarray_reverse(poly->rings[0]);
}
for (i=1; inrings; i++)
{
if ( ! ptarray_isccw(poly->rings[i]) )
{
ptarray_reverse(poly->rings[i]);
}
}
}
void
lwpoly_release(LWPOLY *lwpoly)
{
lwgeom_release(lwpoly_as_lwgeom(lwpoly));
}
void
lwpoly_reverse(LWPOLY *poly)
{
int i;
for (i=0; inrings; i++)
ptarray_reverse(poly->rings[i]);
}
LWPOLY *
lwpoly_segmentize2d(LWPOLY *poly, double dist)
{
POINTARRAY **newrings;
unsigned int i;
newrings = lwalloc(sizeof(POINTARRAY *)*poly->nrings);
for (i=0; inrings; i++)
{
newrings[i] = ptarray_segmentize2d(poly->rings[i], dist);
}
return lwpoly_construct(poly->SRID, NULL,
poly->nrings, newrings);
}
/*
* check coordinate equality
* ring and coordinate order is considered
*/
char
lwpoly_same(const LWPOLY *p1, const LWPOLY *p2)
{
unsigned int i;
if ( p1->nrings != p2->nrings ) return 0;
for (i=0; inrings; i++)
{
if ( ! ptarray_same(p1->rings[i], p2->rings[i]) )
return 0;
}
return 1;
}
/*
* Construct a polygon from a LWLINE being
* the shell and an array of LWLINE (possibly NULL) being holes.
* Pointarrays from intput geoms are cloned.
* SRID must be the same for each input line.
* Input lines must have at least 4 points, and be closed.
*/
LWPOLY *
lwpoly_from_lwlines(const LWLINE *shell,
unsigned int nholes, const LWLINE **holes)
{
unsigned int nrings;
POINTARRAY **rings = lwalloc((nholes+1)*sizeof(POINTARRAY *));
int SRID = shell->SRID;
LWPOLY *ret;
if ( shell->points->npoints < 4 )
lwerror("lwpoly_from_lwlines: shell must have at least 4 points");
if ( ! ptarray_isclosed2d(shell->points) )
lwerror("lwpoly_from_lwlines: shell must be closed");
rings[0] = ptarray_clone(shell->points);
for (nrings=1; nrings<=nholes; nrings++)
{
const LWLINE *hole = holes[nrings-1];
if ( hole->SRID != SRID )
lwerror("lwpoly_from_lwlines: mixed SRIDs in input lines");
if ( hole->points->npoints < 4 )
lwerror("lwpoly_from_lwlines: holes must have at least 4 points");
if ( ! ptarray_isclosed2d(hole->points) )
lwerror("lwpoly_from_lwlines: holes must be closed");
rings[nrings] = ptarray_clone(hole->points);
}
ret = lwpoly_construct(SRID, NULL, nrings, rings);
return ret;
}
================================================
FILE: src/liblwgeom/lwsegmentize.c
================================================
/**********************************************************************
* $Id: lwsegmentize.c 3807 2009-03-08 21:15:00Z pramsey $
*
* PostGIS - Spatial Types for PostgreSQL
* http://postgis.refractions.net
* Copyright 2001-2006 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.
*
**********************************************************************/
#include
#include
#include
#include
#include
#include "liblwgeom.h"
double interpolate_arc(double angle, double zm1, double a1, double zm2, double a2);
POINTARRAY *lwcircle_segmentize(POINT4D *p1, POINT4D *p2, POINT4D *p3, uint32 perQuad);
LWLINE *lwcurve_segmentize(LWCIRCSTRING *icurve, uint32 perQuad);
LWLINE *lwcompound_segmentize(LWCOMPOUND *icompound, uint32 perQuad);
LWPOLY *lwcurvepoly_segmentize(LWCURVEPOLY *curvepoly, uint32 perQuad);
LWMLINE *lwmcurve_segmentize(LWMCURVE *mcurve, uint32 perQuad);
LWMPOLY *lwmsurface_segmentize(LWMSURFACE *msurface, uint32 perQuad);
LWCOLLECTION *lwcollection_segmentize(LWCOLLECTION *collection, uint32 perQuad);
LWGEOM *append_segment(LWGEOM *geom, POINTARRAY *pts, int type, int SRID);
LWGEOM *pta_desegmentize(POINTARRAY *points, int type, int SRID);
LWGEOM *lwline_desegmentize(LWLINE *line);
LWGEOM *lwpolygon_desegmentize(LWPOLY *poly);
LWGEOM *lwmline_desegmentize(LWMLINE *mline);
LWGEOM *lwmpolygon_desegmentize(LWMPOLY *mpoly);
LWGEOM *lwgeom_desegmentize(LWGEOM *geom);
/*
* Tolerance used to determine equality.
*/
#define EPSILON_SQLMM 1e-8
/*
* Determines (recursively in the case of collections) whether the geometry
* contains at least on arc geometry or segment.
*/
uint32
has_arc(LWGEOM *geom)
{
LWCOLLECTION *col;
int i;
LWDEBUG(2, "has_arc called.");
switch(lwgeom_getType(geom->type))
{
case POINTTYPE:
case LINETYPE:
case POLYGONTYPE:
case MULTIPOINTTYPE:
case MULTILINETYPE:
case MULTIPOLYGONTYPE:
return 0;
case CIRCSTRINGTYPE:
return 1;
/* It's a collection that MAY contain an arc */
default:
col = (LWCOLLECTION *)geom;
for(i=0; ingeoms; i++)
{
if(has_arc(col->geoms[i]) == 1) return 1;
}
return 0;
}
}
/*
* Determines the center of the circle defined by the three given points.
* In the event the circle is complete, the midpoint of the segment defined
* by the first and second points is returned. If the points are colinear,
* as determined by equal slopes, then NULL is returned. If the interior
* point is coincident with either end point, they are taken as colinear.
*/
double
lwcircle_center(POINT4D *p1, POINT4D *p2, POINT4D *p3, POINT4D **result)
{
POINT4D *c;
double cx, cy, cr;
double temp, bc, cd, det;
LWDEBUGF(2, "lwcircle_center called (%.16f, %.16f), (%.16f, %.16f), (%.16f, %.16f).", p1->x, p1->y, p2->x, p2->y, p3->x, p3->y);
/* Closed circle */
if(fabs(p1->x - p3->x) < EPSILON_SQLMM
&& fabs(p1->y - p3->y) < EPSILON_SQLMM)
{
cx = p1->x + (p2->x - p1->x) / 2.0;
cy = p1->y + (p2->y - p1->y) / 2.0;
c = lwalloc(sizeof(POINT2D));
c->x = cx;
c->y = cy;
*result = c;
cr = sqrt((cx-p1->x)*(cx-p1->x)+(cy-p1->y)*(cy-p1->y));
return cr;
}
temp = p2->x*p2->x + p2->y*p2->y;
bc = (p1->x*p1->x + p1->y*p1->y - temp) / 2.0;
cd = (temp - p3->x*p3->x - p3->y*p3->y) / 2.0;
det = (p1->x - p2->x)*(p2->y - p3->y)-(p2->x - p3->x)*(p1->y - p2->y);
/* Check colinearity */
if(fabs(det) < EPSILON_SQLMM)
{
*result = NULL;
return -1.0;
}
det = 1.0 / det;
cx = (bc*(p2->y - p3->y)-cd*(p1->y - p2->y))*det;
cy = ((p1->x - p2->x)*cd-(p2->x - p3->x)*bc)*det;
c = lwalloc(sizeof(POINT4D));
c->x = cx;
c->y = cy;
*result = c;
cr = sqrt((cx-p1->x)*(cx-p1->x)+(cy-p1->y)*(cy-p1->y));
return cr;
}
double
interpolate_arc(double angle, double zm1, double a1, double zm2, double a2)
{
double frac = fabs((angle - a1) / (a2 - a1));
double result = frac * (zm2 - zm1) + zm1;
LWDEBUG(2, "interpolate_arc called.");
LWDEBUGF(3, "interpolate_arc: angle=%.16f, a1=%.16f, a2=%.16f, z1=%.16f, z2=%.16f, frac=%.16f, result=%.16f", angle, a1, a2, zm1, zm2, frac, result);
return result;
}
/*******************************************************************************
* Begin curve segmentize functions
******************************************************************************/
POINTARRAY *
lwcircle_segmentize(POINT4D *p1, POINT4D *p2, POINT4D *p3, uint32 perQuad)
{
POINTARRAY *result;
POINT4D pbuf;
size_t ptsize = sizeof(POINT4D);
unsigned int ptcount;
uchar *pt;
POINT4D *center;
double radius = 0.0,
sweep = 0.0,
angle = 0.0,
increment = 0.0;
double a1, a2, a3, i;
LWDEBUG(2, "lwcircle_segmentize called. ");
radius = lwcircle_center(p1, p2, p3, ¢er);
LWDEBUGF(3, "lwcircle_segmentize, (%.16f, %.16f) radius=%.16f", center->x, center->y, radius);
if(radius < 0)
{
return NULL;
}
a1 = atan2(p1->y - center->y, p1->x - center->x);
a2 = atan2(p2->y - center->y, p2->x - center->x);
a3 = atan2(p3->y - center->y, p3->x - center->x);
LWDEBUGF(3, "a1 = %.16f, a2 = %.16f, a3 = %.16f", a1, a2, a3);
if(fabs(p1->x - p3->x) < EPSILON_SQLMM
&& fabs(p1->y - p3->y) < EPSILON_SQLMM)
{
sweep = 2*M_PI;
}
/* Clockwise */
else if(a1 > a2 && a2 > a3)
{
sweep = a3 - a1;
}
/* Counter-clockwise */
else if(a1 < a2 && a2 < a3)
{
sweep = a3 - a1;
}
/* Clockwise, wrap */
else if((a1 < a2 && a1 > a3) || (a2 < a3 && a1 > a3))
{
sweep = a3 - a1 + 2*M_PI;
}
/* Counter-clockwise, wrap */
else if((a1 > a2 && a1 < a3) || (a2 > a3 && a1 < a3))
{
sweep = a3 - a1 - 2*M_PI;
}
else
{
sweep = 0.0;
}
ptcount = ceil(fabs(perQuad * sweep / M_PI_2));
result = ptarray_construct(1, 1, ptcount);
increment = M_PI_2 / perQuad;
if(sweep < 0) increment *= -1.0;
angle = a1;
LWDEBUGF(3, "ptcount: %d, perQuad: %d, sweep: %.16f, increment: %.16f", ptcount, perQuad, sweep, increment);
for(i = 0; i < ptcount - 1; i++)
{
pt = getPoint_internal(result, i);
angle += increment;
if(increment > 0.0 && angle > M_PI) angle -= 2*M_PI;
if(increment < 0.0 && angle < -1*M_PI) angle -= 2*M_PI;
pbuf.x = center->x + radius*cos(angle);
pbuf.y = center->y + radius*sin(angle);
if((sweep > 0 && angle < a2) || (sweep < 0 && angle > a2))
{
if((sweep > 0 && a1 < a2) || (sweep < 0 && a1 > a2))
{
pbuf.z = interpolate_arc(angle, p1->z, a1, p2->z, a2);
pbuf.m = interpolate_arc(angle, p1->m, a1, p2->m, a2);
}
else
{
if(sweep > 0)
{
pbuf.z = interpolate_arc(angle, p1->z, a1-(2*M_PI), p2->z, a2);
pbuf.m = interpolate_arc(angle, p1->m, a1-(2*M_PI), p2->m, a2);
}
else
{
pbuf.z = interpolate_arc(angle, p1->z, a1+(2*M_PI), p2->z, a2);
pbuf.m = interpolate_arc(angle, p1->m, a1+(2*M_PI), p2->m, a2);
}
}
}
else
{
if((sweep > 0 && a2 < a3) || (sweep < 0 && a2 > a3))
{
pbuf.z = interpolate_arc(angle, p2->z, a2, p3->z, a3);
pbuf.m = interpolate_arc(angle, p2->m, a2, p3->m, a3);
}
else
{
if(sweep > 0)
{
pbuf.z = interpolate_arc(angle, p2->z, a2-(2*M_PI), p3->z, a3);
pbuf.m = interpolate_arc(angle, p2->m, a2-(2*M_PI), p3->m, a3);
}
else
{
pbuf.z = interpolate_arc(angle, p2->z, a2+(2*M_PI), p3->z, a3);
pbuf.m = interpolate_arc(angle, p2->m, a2+(2*M_PI), p3->m, a3);
}
}
}
memcpy(pt, (uchar *)&pbuf, ptsize);
}
pt = getPoint_internal(result, ptcount - 1);
memcpy(pt, (uchar *)p3, ptsize);
lwfree(center);
return result;
}
LWLINE *
lwcurve_segmentize(LWCIRCSTRING *icurve, uint32 perQuad)
{
LWLINE *oline;
DYNPTARRAY *ptarray;
POINTARRAY *tmp;
uint32 i, j;
POINT4D *p1 = lwalloc(sizeof(POINT4D));
POINT4D *p2 = lwalloc(sizeof(POINT4D));
POINT4D *p3 = lwalloc(sizeof(POINT4D));
POINT4D *p4 = lwalloc(sizeof(POINT4D));
LWDEBUGF(2, "lwcurve_segmentize called., dim = %d", icurve->points->dims);
ptarray = dynptarray_create(icurve->points->npoints, icurve->points->dims);
if(!getPoint4d_p(icurve->points, 0, p4))
{
lwerror("lwcurve_segmentize: Cannot extract point.");
}
dynptarray_addPoint4d(ptarray, p4, 1);
for(i = 2; i < icurve->points->npoints; i+=2)
{
LWDEBUGF(3, "lwcurve_segmentize: arc ending at point %d", i);
getPoint4d_p(icurve->points, i - 2, p1);
getPoint4d_p(icurve->points, i - 1, p2);
getPoint4d_p(icurve->points, i, p3);
tmp = lwcircle_segmentize(p1, p2, p3, perQuad);
LWDEBUGF(3, "lwcurve_segmentize: generated %d points", tmp->npoints);
for(j = 0; j < tmp->npoints; j++)
{
getPoint4d_p(tmp, j, p4);
dynptarray_addPoint4d(ptarray, p4, 1);
}
lwfree(tmp);
}
oline = lwline_construct(icurve->SRID, NULL, ptarray_clone(ptarray->pa));
lwfree(p1);
lwfree(p2);
lwfree(p3);
lwfree(p4);
lwfree(ptarray);
return oline;
}
LWLINE *
lwcompound_segmentize(LWCOMPOUND *icompound, uint32 perQuad)
{
LWLINE *oline;
LWGEOM *geom;
DYNPTARRAY *ptarray = NULL;
LWLINE *tmp = NULL;
uint32 i, j;
POINT4D *p = NULL;
LWDEBUG(2, "lwcompound_segmentize called.");
p = lwalloc(sizeof(POINT4D));
ptarray = dynptarray_create(2, ((POINTARRAY *)icompound->geoms[0]->data)->dims);
for(i = 0; i < icompound->ngeoms; i++)
{
geom = icompound->geoms[i];
if(lwgeom_getType(geom->type) == CIRCSTRINGTYPE)
{
tmp = lwcurve_segmentize((LWCIRCSTRING *)geom, perQuad);
for(j = 0; j < tmp->points->npoints; j++)
{
getPoint4d_p(tmp->points, j, p);
dynptarray_addPoint4d(ptarray, p, 0);
}
lwfree(tmp);
}
else if(lwgeom_getType(geom->type) == LINETYPE)
{
tmp = (LWLINE *)geom;
for(j = 0; j < tmp->points->npoints; j++)
{
getPoint4d_p(tmp->points, j, p);
dynptarray_addPoint4d(ptarray, p, 0);
}
}
else
{
lwerror("Unsupported geometry type %d found.", lwgeom_getType(geom->type));
return NULL;
}
}
oline = lwline_construct(icompound->SRID, NULL, ptarray_clone(ptarray->pa));
lwfree(ptarray);
lwfree(p);
return oline;
}
LWPOLY *
lwcurvepoly_segmentize(LWCURVEPOLY *curvepoly, uint32 perQuad)
{
LWPOLY *ogeom;
LWGEOM *tmp;
LWLINE *line;
POINTARRAY **ptarray;
int i;
LWDEBUG(2, "lwcurvepoly_segmentize called.");
ptarray = lwalloc(sizeof(POINTARRAY *)*curvepoly->nrings);
for(i = 0; i < curvepoly->nrings; i++)
{
tmp = curvepoly->rings[i];
if(lwgeom_getType(tmp->type) == CIRCSTRINGTYPE)
{
line = lwcurve_segmentize((LWCIRCSTRING *)tmp, perQuad);
ptarray[i] = ptarray_clone(line->points);
lwfree(line);
}
else if(lwgeom_getType(tmp->type) == LINETYPE)
{
line = (LWLINE *)tmp;
ptarray[i] = ptarray_clone(line->points);
}
else
{
lwerror("Invalid ring type found in CurvePoly.");
return NULL;
}
}
ogeom = lwpoly_construct(curvepoly->SRID, NULL, curvepoly->nrings, ptarray);
return ogeom;
}
LWMLINE *
lwmcurve_segmentize(LWMCURVE *mcurve, uint32 perQuad)
{
LWMLINE *ogeom;
LWGEOM *tmp;
LWGEOM **lines;
int i;
LWDEBUGF(2, "lwmcurve_segmentize called, geoms=%d, dim=%d.", mcurve->ngeoms, TYPE_NDIMS(mcurve->type));
lines = lwalloc(sizeof(LWGEOM *)*mcurve->ngeoms);
for(i = 0; i < mcurve->ngeoms; i++)
{
tmp = mcurve->geoms[i];
if(lwgeom_getType(tmp->type) == CIRCSTRINGTYPE)
{
lines[i] = (LWGEOM *)lwcurve_segmentize((LWCIRCSTRING *)tmp, perQuad);
}
else if(lwgeom_getType(tmp->type) == LINETYPE)
{
lines[i] = (LWGEOM *)lwline_construct(mcurve->SRID, NULL, ptarray_clone(((LWLINE *)tmp)->points));
}
else
{
lwerror("Unsupported geometry found in MultiCurve.");
return NULL;
}
}
ogeom = (LWMLINE *)lwcollection_construct(MULTILINETYPE, mcurve->SRID, NULL, mcurve->ngeoms, lines);
return ogeom;
}
LWMPOLY *
lwmsurface_segmentize(LWMSURFACE *msurface, uint32 perQuad)
{
LWMPOLY *ogeom;
LWGEOM *tmp;
LWPOLY *poly;
LWGEOM **polys;
POINTARRAY **ptarray;
int i, j;
LWDEBUG(2, "lwmsurface_segmentize called.");
polys = lwalloc(sizeof(LWGEOM *)*msurface->ngeoms);
for(i = 0; i < msurface->ngeoms; i++)
{
tmp = msurface->geoms[i];
if(lwgeom_getType(tmp->type) == CURVEPOLYTYPE)
{
polys[i] = (LWGEOM *)lwcurvepoly_segmentize((LWCURVEPOLY *)tmp, perQuad);
}
else if(lwgeom_getType(tmp->type) == POLYGONTYPE)
{
poly = (LWPOLY *)tmp;
ptarray = lwalloc(sizeof(POINTARRAY *)*poly->nrings);
for(j = 0; j < poly->nrings; j++)
{
ptarray[j] = ptarray_clone(poly->rings[j]);
}
polys[i] = (LWGEOM *)lwpoly_construct(msurface->SRID, NULL, poly->nrings, ptarray);
}
}
ogeom = (LWMPOLY *)lwcollection_construct(MULTIPOLYGONTYPE, msurface->SRID, NULL, msurface->ngeoms, polys);
return ogeom;
}
LWCOLLECTION *
lwcollection_segmentize(LWCOLLECTION *collection, uint32 perQuad)
{
LWCOLLECTION *ocol;
LWGEOM *tmp;
LWGEOM **geoms;
int i;
LWDEBUG(2, "lwcollection_segmentize called.");
geoms = lwalloc(sizeof(LWGEOM *)*collection->ngeoms);
for(i=0; ingeoms; i++)
{
tmp = collection->geoms[i];
switch(lwgeom_getType(tmp->type)) {
case CIRCSTRINGTYPE:
geoms[i] = (LWGEOM *)lwcurve_segmentize((LWCIRCSTRING *)tmp, perQuad);
break;
case COMPOUNDTYPE:
geoms[i] = (LWGEOM *)lwcompound_segmentize((LWCOMPOUND *)tmp, perQuad);
break;
case CURVEPOLYTYPE:
geoms[i] = (LWGEOM *)lwcurvepoly_segmentize((LWCURVEPOLY *)tmp, perQuad);
break;
case COLLECTIONTYPE:
geoms[i] = (LWGEOM *)lwcollection_segmentize((LWCOLLECTION *)tmp, perQuad);
break;
default:
geoms[i] = lwgeom_clone(tmp);
break;
}
}
ocol = lwcollection_construct(COLLECTIONTYPE, collection->SRID, NULL, collection->ngeoms, geoms);
return ocol;
}
LWGEOM *
lwgeom_segmentize(LWGEOM *geom, uint32 perQuad)
{
LWGEOM * ogeom = NULL;
switch(lwgeom_getType(geom->type)) {
case CIRCSTRINGTYPE:
ogeom = (LWGEOM *)lwcurve_segmentize((LWCIRCSTRING *)geom, perQuad);
break;
case COMPOUNDTYPE:
ogeom = (LWGEOM *)lwcompound_segmentize((LWCOMPOUND *)geom, perQuad);
break;
case CURVEPOLYTYPE:
ogeom = (LWGEOM *)lwcurvepoly_segmentize((LWCURVEPOLY *)geom, perQuad);
break;
case MULTICURVETYPE:
ogeom = (LWGEOM *)lwmcurve_segmentize((LWMCURVE *)geom, perQuad);
break;
case MULTISURFACETYPE:
ogeom = (LWGEOM *)lwmsurface_segmentize((LWMSURFACE *)geom, perQuad);
break;
case COLLECTIONTYPE:
ogeom = (LWGEOM *)lwcollection_segmentize((LWCOLLECTION *)geom, perQuad);
break;
default:
ogeom = lwgeom_clone(geom);
}
return ogeom;
}
/*******************************************************************************
* End curve segmentize functions
******************************************************************************/
LWGEOM *
append_segment(LWGEOM *geom, POINTARRAY *pts, int type, int SRID)
{
LWGEOM *result;
int currentType, i;
LWDEBUGF(2, "append_segment called %p, %p, %d, %d", geom, pts, type, SRID);
if(geom == NULL)
{
if(type == LINETYPE)
{
LWDEBUG(3, "append_segment: line to NULL");
return (LWGEOM *)lwline_construct(SRID, NULL, pts);
}
else if(type == CIRCSTRINGTYPE)
{
#if POSTGIS_DEBUG_LEVEL >= 4
POINT4D tmp;
LWDEBUGF(4, "append_segment: curve to NULL %d", pts->npoints);
for(i=0; inpoints; i++)
{
getPoint4d_p(pts, i, &tmp);
LWDEBUGF(4, "new point: (%.16f,%.16f)",tmp.x,tmp.y);
}
#endif
return (LWGEOM *)lwcircstring_construct(SRID, NULL, pts);
}
else
{
lwerror("Invalid segment type %d.", type);
}
}
currentType = lwgeom_getType(geom->type);
if(currentType == LINETYPE && type == LINETYPE)
{
POINTARRAY *newPoints;
POINT4D pt;
LWLINE *line = (LWLINE *)geom;
LWDEBUG(3, "append_segment: line to line");
newPoints = ptarray_construct(TYPE_HASZ(pts->dims), TYPE_HASM(pts->dims), pts->npoints + line->points->npoints - 1);
for(i=0; ipoints->npoints; i++)
{
getPoint4d_p(line->points, i, &pt);
LWDEBUGF(5, "copying to %p [%d]", newPoints, i);
setPoint4d(newPoints, i, &pt);
}
for(i=1; inpoints; i++)
{
getPoint4d_p(pts, i, &pt);
setPoint4d(newPoints, i + line->points->npoints - 1, &pt);
}
result = (LWGEOM *)lwline_construct(SRID, NULL, newPoints);
lwgeom_release(geom);
return result;
}
else if(currentType == CIRCSTRINGTYPE && type == CIRCSTRINGTYPE)
{
POINTARRAY *newPoints;
POINT4D pt;
LWCIRCSTRING *curve = (LWCIRCSTRING *)geom;
LWDEBUG(3, "append_segment: circularstring to circularstring");
newPoints = ptarray_construct(TYPE_HASZ(pts->dims), TYPE_HASM(pts->dims), pts->npoints + curve->points->npoints - 1);
LWDEBUGF(3, "New array length: %d", pts->npoints + curve->points->npoints - 1);
for(i=0; ipoints->npoints; i++)
{
getPoint4d_p(curve->points, i, &pt);
LWDEBUGF(3, "orig point %d: (%.16f,%.16f)", i, pt.x, pt.y);
setPoint4d(newPoints, i, &pt);
}
for(i=1; inpoints;i++)
{
getPoint4d_p(pts, i, &pt);
LWDEBUGF(3, "new point %d: (%.16f,%.16f)", i + curve->points->npoints - 1, pt.x, pt.y);
setPoint4d(newPoints, i + curve->points->npoints - 1, &pt);
}
result = (LWGEOM *)lwcircstring_construct(SRID, NULL, newPoints);
lwgeom_release(geom);
return result;
}
else if(currentType == CIRCSTRINGTYPE && type == LINETYPE)
{
LWLINE *line;
LWGEOM **geomArray;
LWDEBUG(3, "append_segment: line to curve");
geomArray = lwalloc(sizeof(LWGEOM *)*2);
geomArray[0] = lwgeom_clone(geom);
line = lwline_construct(SRID, NULL, pts);
geomArray[1] = lwgeom_clone((LWGEOM *)line);
result = (LWGEOM *)lwcollection_construct(COMPOUNDTYPE, SRID, NULL, 2, geomArray);
lwfree((LWGEOM *)line);
lwgeom_release(geom);
return result;
}
else if(currentType == LINETYPE && type == CIRCSTRINGTYPE)
{
LWCIRCSTRING *curve;
LWGEOM **geomArray;
LWDEBUG(3, "append_segment: circularstring to line");
geomArray = lwalloc(sizeof(LWGEOM *)*2);
geomArray[0] = lwgeom_clone(geom);
curve = lwcircstring_construct(SRID, NULL, pts);
geomArray[1] = lwgeom_clone((LWGEOM *)curve);
result = (LWGEOM *)lwcollection_construct(COMPOUNDTYPE, SRID, NULL, 2, geomArray);
lwfree((LWGEOM *)curve);
lwgeom_release(geom);
return result;
}
else if(currentType == COMPOUNDTYPE)
{
LWGEOM *newGeom;
LWCOMPOUND *compound;
int count;
LWGEOM **geomArray;
compound = (LWCOMPOUND *)geom;
count = compound->ngeoms + 1;
geomArray = lwalloc(sizeof(LWGEOM *)*count);
for(i=0; ingeoms; i++)
{
geomArray[i] = lwgeom_clone(compound->geoms[i]);
}
if(type == LINETYPE)
{
LWDEBUG(3, "append_segment: line to compound");
newGeom = (LWGEOM *)lwline_construct(SRID, NULL, pts);
}
else if(type == CIRCSTRINGTYPE)
{
LWDEBUG(3, "append_segment: circularstring to compound");
newGeom = (LWGEOM *)lwcircstring_construct(SRID, NULL, pts);
}
else
{
lwerror("Invalid segment type %d.", type);
return NULL;
}
geomArray[compound->ngeoms] = lwgeom_clone(newGeom);
result = (LWGEOM *)lwcollection_construct(COMPOUNDTYPE, SRID, NULL, count, geomArray);
lwfree(newGeom);
lwgeom_release(geom);
return result;
}
lwerror("Invalid state %d-%d", currentType, type);
return NULL;
}
LWGEOM *
pta_desegmentize(POINTARRAY *points, int type, int SRID)
{
int i, j, commit, isline, count;
double last_angle, last_length;
double dxab, dyab, dxbc, dybc, theta, length;
POINT4D a, b, c, tmp;
POINTARRAY *pts;
LWGEOM *geom = NULL;
LWDEBUG(2, "pta_desegmentize called.");
getPoint4d_p(points, 0, &a);
getPoint4d_p(points, 1, &b);
getPoint4d_p(points, 2, &c);
dxab = b.x - a.x;
dyab = b.y - a.y;
dxbc = c.x - b.x;
dybc = c.y - b.y;
theta = atan2(dyab, dxab);
last_angle = theta - atan2(dybc, dxbc);
last_length = sqrt(dxbc*dxbc+dybc*dybc);
length = sqrt(dxab*dxab+dyab*dyab);
if((last_length - length) < EPSILON_SQLMM)
{
isline = -1;
LWDEBUG(3, "Starting as unknown.");
}
else
{
isline = 1;
LWDEBUG(3, "Starting as line.");
}
commit = 0;
for(i=3; inpoints; i++)
{
getPoint4d_p(points, i-2, &a);
getPoint4d_p(points, i-1, &b);
getPoint4d_p(points, i, &c);
dxab = b.x - a.x;
dyab = b.y - a.y;
dxbc = c.x - b.x;
dybc = c.y - b.y;
LWDEBUGF(3, "(dxab, dyab, dxbc, dybc) (%.16f, %.16f, %.16f, %.16f)", dxab, dyab, dxbc, dybc);
theta = atan2(dyab, dxab);
theta = theta - atan2(dybc, dxbc);
length = sqrt(dxbc*dxbc+dybc*dybc);
LWDEBUGF(3, "Last/current length and angle %.16f/%.16f, %.16f/%.16f", last_angle, theta, last_length, length);
/* Found a line segment */
if(fabs(length - last_length) > EPSILON_SQLMM ||
fabs(theta - last_angle) > EPSILON_SQLMM)
{
last_length = length;
last_angle = theta;
/* We are tracking a line, keep going */
if(isline > 0)
{
}
/* We were tracking a circularstring, commit it and start line*/
else if(isline == 0)
{
LWDEBUGF(3, "Building circularstring, %d - %d", commit, i);
count = i - commit;
pts = ptarray_construct(
TYPE_HASZ(type),
TYPE_HASM(type),
3);
getPoint4d_p(points, commit, &tmp);
setPoint4d(pts, 0, &tmp);
getPoint4d_p(points,
commit + count/2, &tmp);
setPoint4d(pts, 1, &tmp);
getPoint4d_p(points, i - 1, &tmp);
setPoint4d(pts, 2, &tmp);
commit = i-1;
geom = append_segment(geom, pts, CIRCSTRINGTYPE, SRID);
isline = -1;
/*
* We now need to move ahead one point to
* determine if it's a potential new curve,
* since the last_angle value is corrupt.
*/
i++;
getPoint4d_p(points, i-2, &a);
getPoint4d_p(points, i-1, &b);
getPoint4d_p(points, i, &c);
dxab = b.x - a.x;
dyab = b.y - a.y;
dxbc = c.x - b.x;
dybc = c.y - b.y;
theta = atan2(dyab, dxab);
last_angle = theta - atan2(dybc, dxbc);
last_length = sqrt(dxbc*dxbc+dybc*dybc);
length = sqrt(dxab*dxab+dyab*dyab);
if((last_length - length) < EPSILON_SQLMM)
{
isline = -1;
LWDEBUG(3, "Restarting as unknown.");
}
else
{
isline = 1;
LWDEBUG(3, "Restarting as line.");
}
}
/* We didn't know what we were tracking, now we do. */
else
{
LWDEBUG(3, "It's a line");
isline = 1;
}
}
/* Found a circularstring segment */
else
{
/* We were tracking a circularstring, commit it and start line */
if(isline > 0)
{
LWDEBUGF(3, "Building line, %d - %d", commit, i-2);
count = i - commit - 2;
pts = ptarray_construct(
TYPE_HASZ(type),
TYPE_HASM(type),
count);
for(j=commit;j 2)
{
LWDEBUGF(3, "Finishing circularstring %d,%d.", commit, i);
pts = ptarray_construct(
TYPE_HASZ(type),
TYPE_HASM(type),
3);
getPoint4d_p(points, commit, &tmp);
setPoint4d(pts, 0, &tmp);
getPoint4d_p(points, commit + count/2, &tmp);
setPoint4d(pts, 1, &tmp);
getPoint4d_p(points, i - 1, &tmp);
setPoint4d(pts, 2, &tmp);
geom = append_segment(geom, pts, CIRCSTRINGTYPE, SRID);
}
else
{
LWDEBUGF(3, "Finishing line %d,%d.", commit, i);
pts = ptarray_construct(
TYPE_HASZ(type),
TYPE_HASM(type),
count);
for(j=commit;jpoints, line->type, line->SRID);
}
LWGEOM *
lwpolygon_desegmentize(LWPOLY *poly)
{
LWGEOM **geoms;
int i, hascurve = 0;
LWDEBUG(2, "lwpolygon_desegmentize called.");
geoms = lwalloc(sizeof(LWGEOM *)*poly->nrings);
for(i=0; inrings; i++)
{
geoms[i] = pta_desegmentize(poly->rings[i], poly->type, poly->SRID);
if(lwgeom_getType(geoms[i]->type) == CIRCSTRINGTYPE ||
lwgeom_getType(geoms[i]->type) == COMPOUNDTYPE)
{
hascurve = 1;
}
}
if(hascurve == 0)
{
for(i=0; inrings; i++)
{
lwfree(geoms[i]);
}
return lwgeom_clone((LWGEOM *)poly);
}
return (LWGEOM *)lwcollection_construct(CURVEPOLYTYPE, poly->SRID, NULL, poly->nrings, geoms);
}
LWGEOM *
lwmline_desegmentize(LWMLINE *mline)
{
LWGEOM **geoms;
int i, hascurve = 0;
LWDEBUG(2, "lwmline_desegmentize called.");
geoms = lwalloc(sizeof(LWGEOM *)*mline->ngeoms);
for(i=0; ingeoms; i++)
{
geoms[i] = lwline_desegmentize((LWLINE *)mline->geoms[i]);
if(lwgeom_getType(geoms[i]->type) == CIRCSTRINGTYPE ||
lwgeom_getType(geoms[i]->type) == COMPOUNDTYPE)
{
hascurve = 1;
}
}
if(hascurve == 0)
{
for(i=0; ingeoms; i++)
{
lwfree(geoms[i]);
}
return lwgeom_clone((LWGEOM *)mline);
}
return (LWGEOM *)lwcollection_construct(MULTICURVETYPE, mline->SRID, NULL, mline->ngeoms, geoms);
}
LWGEOM *
lwmpolygon_desegmentize(LWMPOLY *mpoly)
{
LWGEOM **geoms;
int i, hascurve = 0;
LWDEBUG(2, "lwmpoly_desegmentize called.");
geoms = lwalloc(sizeof(LWGEOM *)*mpoly->ngeoms);
for(i=0; ingeoms; i++)
{
geoms[i] = lwpolygon_desegmentize((LWPOLY *)mpoly->geoms[i]);
if(lwgeom_getType(geoms[i]->type) == CURVEPOLYTYPE)
{
hascurve = 1;
}
}
if(hascurve == 0)
{
for(i=0; ingeoms; i++)
{
lwfree(geoms[i]);
}
return lwgeom_clone((LWGEOM *)mpoly);
}
return (LWGEOM *)lwcollection_construct(MULTISURFACETYPE, mpoly->SRID, NULL, mpoly->ngeoms, geoms);
}
LWGEOM *
lwgeom_desegmentize(LWGEOM *geom)
{
int type = lwgeom_getType(geom->type);
LWDEBUG(2, "lwgeom_desegmentize called.");
switch(type) {
case LINETYPE:
return lwline_desegmentize((LWLINE *)geom);
case POLYGONTYPE:
return lwpolygon_desegmentize((LWPOLY *)geom);
case MULTILINETYPE:
return lwmline_desegmentize((LWMLINE *)geom);
case MULTIPOLYGONTYPE:
return lwmpolygon_desegmentize((LWMPOLY *)geom);
default:
return lwgeom_clone(geom);
}
}
================================================
FILE: src/liblwgeom/lwutil.c
================================================
#include
#include
#include
#include
/* Global variables */
#include "liblwgeom.h"
void *init_allocator(size_t size);
void init_freeor(void *mem);
void *init_reallocator(void *mem, size_t size);
void init_noticereporter(const char *fmt, va_list ap);
void init_errorreporter(const char *fmt, va_list ap);
lwallocator lwalloc_var = init_allocator;
lwreallocator lwrealloc_var = init_reallocator;
lwfreeor lwfree_var = init_freeor;
lwreporter lwnotice_var = init_noticereporter;
lwreporter lwerror_var = init_errorreporter;
static char *lwgeomTypeName[] = {
"Unknown",
"Point",
"Line",
"Polygon",
"MultiPoint",
"MultiLine",
"MultiPolygon",
"GeometryCollection",
"CircularString",
"CompoundString",
"Invalid Type", /* POINTTYPEI */
"Invalid Type", /* LINETYPEI */
"Invalid Type", /* POLYTYPEI */
"CurvePolygon",
"MultiCurve",
"MultiSurface"
};
/*
* lwnotice/lwerror handlers
*
* Since variadic functions cannot pass their parameters directly, we need
* wrappers for these functions to convert the arguments into a va_list
* structure.
*/
void
lwnotice(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
/* Call the supplied function */
(*lwnotice_var)(fmt, ap);
va_end(ap);
}
void
lwerror(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
/* Call the supplied function */
(*lwerror_var)(fmt, ap);
va_end(ap);
}
/*
* Initialisation allocators
*
* These are used the first time any of the allocators are called
* to enable executables/libraries that link into liblwgeom to
* be able to set up their own allocators. This is mainly useful
* for older PostgreSQL versions that don't have functions that
* are called upon startup.
*/
void *
init_allocator(size_t size)
{
lwgeom_init_allocators();
return lwalloc_var(size);
}
void
init_freeor(void *mem)
{
lwgeom_init_allocators();
lwfree_var(mem);
}
void *
init_reallocator(void *mem, size_t size)
{
lwgeom_init_allocators();
return lwrealloc_var(mem, size);
}
void
init_noticereporter(const char *fmt, va_list ap)
{
lwgeom_init_allocators();
(*lwnotice_var)(fmt, ap);
}
void
init_errorreporter(const char *fmt, va_list ap)
{
lwgeom_init_allocators();
(*lwerror_var)(fmt, ap);
}
/*
* Default allocators
*
* We include some default allocators that use malloc/free/realloc
* along with stdout/stderr since this is the most common use case
*
*/
void *
default_allocator(size_t size)
{
void *mem = malloc(size);
return mem;
}
void
default_freeor(void *mem)
{
free(mem);
}
void *
default_reallocator(void *mem, size_t size)
{
void *ret = realloc(mem, size);
return ret;
}
void
default_noticereporter(const char *fmt, va_list ap)
{
char *msg;
/*
* This is a GNU extension.
* Dunno how to handle errors here.
*/
if (!lw_vasprintf (&msg, fmt, ap))
{
va_end (ap);
return;
}
printf("%s\n", msg);
free(msg);
}
void
default_errorreporter(const char *fmt, va_list ap)
{
char *msg;
/*
* This is a GNU extension.
* Dunno how to handle errors here.
*/
if (!lw_vasprintf (&msg, fmt, ap))
{
va_end (ap);
return;
}
fprintf(stderr, "%s\n", msg);
free(msg);
exit(1);
}
/*
* This function should be called from lwgeom_init_allocators() by programs
* which wish to use the default allocators above
*/
void lwgeom_install_default_allocators(void)
{
lwalloc_var = default_allocator;
lwrealloc_var = default_reallocator;
lwfree_var = default_freeor;
lwerror_var = default_errorreporter;
lwnotice_var = default_noticereporter;
}
const char *
lwgeom_typename(int type)
{
/* something went wrong somewhere */
if ( type < 0 || type > 15 ) {
/* assert(0); */
return "Invalid type";
}
return lwgeomTypeName[type];
}
void *
lwalloc(size_t size)
{
void *mem = lwalloc_var(size);
LWDEBUGF(5, "lwalloc: %d@%p", size, mem);
return mem;
}
void *
lwrealloc(void *mem, size_t size)
{
LWDEBUGF(5, "lwrealloc: %d@%p", size, mem);
return lwrealloc_var(mem, size);
}
void
lwfree(void *mem)
{
lwfree_var(mem);
}
/*
* Removes trailing zeros and dot for a %f formatted number.
* Modifies input.
*/
void
trim_trailing_zeros(char *str)
{
char *ptr, *totrim=NULL;
int len;
int i;
LWDEBUGF(3, "input: %s", str);
ptr = strchr(str, '.');
if ( ! ptr ) return; /* no dot, no decimal digits */
LWDEBUGF(3, "ptr: %s", ptr);
len = strlen(ptr);
for (i=len-1; i; i--)
{
if ( ptr[i] != '0' ) break;
totrim=&ptr[i];
}
if ( totrim )
{
if ( ptr == totrim-1 ) *ptr = '\0';
else *totrim = '\0';
}
LWDEBUGF(3, "output: %s", str);
}
/*
* Returns a new string which contains a maximum of maxlength characters starting
* from startpos and finishing at endpos (0-based indexing). If the string is
* truncated then the first or last characters are replaced by "..." as
* appropriate.
*
* The caller should specify start or end truncation by setting the truncdirection
* parameter as follows:
* 0 - start truncation (i.e. characters are removed from the beginning)
* 1 - end trunctation (i.e. characters are removed from the end)
*/
char *lwmessage_truncate(char *str, int startpos, int endpos, int maxlength, int truncdirection)
{
char *output;
char *outstart;
/* Allocate space for new string */
output = lwalloc(maxlength + 4);
output[0] = '\0';
/* Start truncation */
if (truncdirection == 0)
{
/* Calculate the start position */
if (endpos - startpos < maxlength)
{
outstart = str + startpos;
strncat(output, outstart, endpos - startpos + 1);
}
else
{
if (maxlength >= 3)
{
/* Add "..." prefix */
outstart = str + endpos + 1 - maxlength + 3;
strncat(output, "...", 3);
strncat(output, outstart, maxlength - 3);
}
else
{
/* maxlength is too small; just output "..." */
strncat(output, "...", 3);
}
}
}
/* End truncation */
if (truncdirection == 1)
{
/* Calculate the end position */
if (endpos - startpos < maxlength)
{
outstart = str + startpos;
strncat(output, outstart, endpos - startpos + 1);
}
else
{
if (maxlength >= 3)
{
/* Add "..." suffix */
outstart = str + startpos;
strncat(output, outstart, maxlength - 3);
strncat(output, "...", 3);
}
else
{
/* maxlength is too small; just output "..." */
strncat(output, "...", 3);
}
}
}
return output;
}
char
getMachineEndian(void)
{
static int endian_check_int = 1; /* dont modify this!!! */
return *((char *) &endian_check_int); /* 0 = big endian | xdr,
* 1 = little endian | ndr
*/
}
void
errorIfSRIDMismatch(int srid1, int srid2)
{
if ( srid1 != srid2 )
{
lwerror("Operation on mixed SRID geometries");
}
}
================================================
FILE: src/liblwgeom/measures.c
================================================
/**********************************************************************
* $Id: measures.c 3639 2009-02-04 00:28:37Z pramsey $
*
* PostGIS - Spatial Types for PostgreSQL
* http://postgis.refractions.net
* Copyright 2001-2006 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.
*
**********************************************************************/
#include
#include
#include "liblwgeom.h"
/*
* pt_in_ring_2d(): crossing number test for a point in a polygon
* input: p = a point,
* pa = vertex points of a ring V[n+1] with V[n]=V[0]
* returns: 0 = outside, 1 = inside
*
* Our polygons have first and last point the same,
*
*/
int pt_in_ring_2d(POINT2D *p, POINTARRAY *ring)
{
int cn = 0; /* the crossing number counter */
int i;
POINT2D v1, v2;
#if INTEGRITY_CHECKS
POINT2D first, last;
getPoint2d_p(ring, 0, &first);
getPoint2d_p(ring, ring->npoints-1, &last);
if ( memcmp(&first, &last, sizeof(POINT2D)) )
{
lwerror("pt_in_ring_2d: V[n] != V[0] (%g %g != %g %g)",
first.x, first.y, last.x, last.y);
}
#endif
LWDEBUGF(2, "pt_in_ring_2d called with point: %g %g", p->x, p->y);
/* printPA(ring); */
/* loop through all edges of the polygon */
getPoint2d_p(ring, 0, &v1);
for (i=0; inpoints-1; i++)
{
double vt;
getPoint2d_p(ring, i+1, &v2);
/* edge from vertex i to vertex i+1 */
if
(
/* an upward crossing */
((v1.y <= p->y) && (v2.y > p->y))
/* a downward crossing */
|| ((v1.y > p->y) && (v2.y <= p->y))
)
{
vt = (double)(p->y - v1.y) / (v2.y - v1.y);
/* P.x x < v1.x + vt * (v2.x - v1.x))
{
/* a valid crossing of y=p.y right of p.x */
++cn;
}
}
v1 = v2;
}
LWDEBUGF(3, "pt_in_ring_2d returning %d", cn&1);
return (cn&1); /* 0 if even (out), and 1 if odd (in) */
}
double distance2d_pt_pt(POINT2D *p1, POINT2D *p2)
{
double hside = p2->x - p1->x;
double vside = p2->y - p1->y;
return sqrt ( hside*hside + vside*vside );
/* the above is more readable
return sqrt(
(p2->x-p1->x) * (p2->x-p1->x) + (p2->y-p1->y) * (p2->y-p1->y)
); */
}
/*distance2d from p to line A->B */
double distance2d_pt_seg(POINT2D *p, POINT2D *A, POINT2D *B)
{
double r,s;
/*if start==end, then use pt distance */
if ( ( A->x == B->x) && (A->y == B->y) )
return distance2d_pt_pt(p,A);
/*
* otherwise, we use comp.graphics.algorithms
* Frequently Asked Questions method
*
* (1) AC dot AB
* r = ---------
* ||AB||^2
* r has the following meaning:
* r=0 P = A
* r=1 P = B
* r<0 P is on the backward extension of AB
* r>1 P is on the forward extension of AB
* 0x-A->x) * (B->x-A->x) + (p->y-A->y) * (B->y-A->y) )/( (B->x-A->x)*(B->x-A->x) +(B->y-A->y)*(B->y-A->y) );
if (r<0) return distance2d_pt_pt(p,A);
if (r>1) return distance2d_pt_pt(p,B);
/*
* (2)
* (Ay-Cy)(Bx-Ax)-(Ax-Cx)(By-Ay)
* s = -----------------------------
* L^2
*
* Then the distance from C to P = |s|*L.
*
*/
s = ( (A->y-p->y)*(B->x-A->x)- (A->x-p->x)*(B->y-A->y) ) /
( (B->x-A->x)*(B->x-A->x) +(B->y-A->y)*(B->y-A->y) );
return LW_ABS(s) * sqrt(
(B->x-A->x)*(B->x-A->x) + (B->y-A->y)*(B->y-A->y)
);
}
/* find the minimum 2d distance from AB to CD */
double distance2d_seg_seg(POINT2D *A, POINT2D *B, POINT2D *C, POINT2D *D)
{
double s_top, s_bot,s;
double r_top, r_bot,r;
LWDEBUGF(2, "distance2d_seg_seg [%g,%g]->[%g,%g] by [%g,%g]->[%g,%g]",
A->x,A->y,B->x,B->y, C->x,C->y, D->x, D->y);
/*A and B are the same point */
if ( ( A->x == B->x) && (A->y == B->y) )
return distance2d_pt_seg(A,C,D);
/*U and V are the same point */
if ( ( C->x == D->x) && (C->y == D->y) )
return distance2d_pt_seg(D,A,B);
/* AB and CD are line segments */
/* from comp.graphics.algo
Solving the above for r and s yields
(Ay-Cy)(Dx-Cx)-(Ax-Cx)(Dy-Cy)
r = ----------------------------- (eqn 1)
(Bx-Ax)(Dy-Cy)-(By-Ay)(Dx-Cx)
(Ay-Cy)(Bx-Ax)-(Ax-Cx)(By-Ay)
s = ----------------------------- (eqn 2)
(Bx-Ax)(Dy-Cy)-(By-Ay)(Dx-Cx)
Let P be the position vector of the intersection point, then
P=A+r(B-A) or
Px=Ax+r(Bx-Ax)
Py=Ay+r(By-Ay)
By examining the values of r & s, you can also determine some other limiting conditions:
If 0<=r<=1 & 0<=s<=1, intersection exists
r<0 or r>1 or s<0 or s>1 line segments do not intersect
If the denominator in eqn 1 is zero, AB & CD are parallel
If the numerator in eqn 1 is also zero, AB & CD are collinear.
*/
r_top = (A->y-C->y)*(D->x-C->x) - (A->x-C->x)*(D->y-C->y) ;
r_bot = (B->x-A->x)*(D->y-C->y) - (B->y-A->y)*(D->x-C->x) ;
s_top = (A->y-C->y)*(B->x-A->x) - (A->x-C->x)*(B->y-A->y);
s_bot = (B->x-A->x)*(D->y-C->y) - (B->y-A->y)*(D->x-C->x);
if ( (r_bot==0) || (s_bot == 0) )
{
return (
LW_MIN(distance2d_pt_seg(A,C,D),
LW_MIN(distance2d_pt_seg(B,C,D),
LW_MIN(distance2d_pt_seg(C,A,B),
distance2d_pt_seg(D,A,B))
)
)
);
}
s = s_top/s_bot;
r= r_top/r_bot;
if ((r<0) || (r>1) || (s<0) || (s>1) )
{
/*no intersection */
return (
LW_MIN(distance2d_pt_seg(A,C,D),
LW_MIN(distance2d_pt_seg(B,C,D),
LW_MIN(distance2d_pt_seg(C,A,B),
distance2d_pt_seg(D,A,B))
)
)
);
}
else
return -0; /*intersection exists */
}
/*
* search all the segments of pointarray to see which one is closest to p1
* Returns minimum distance between point and pointarray
*/
double distance2d_pt_ptarray(POINT2D *p, POINTARRAY *pa)
{
double result = 0;
int t;
POINT2D start, end;
getPoint2d_p(pa, 0, &start);
for (t=1; tnpoints; t++)
{
double dist;
getPoint2d_p(pa, t, &end);
dist = distance2d_pt_seg(p, &start, &end);
if (t==1) result = dist;
else result = LW_MIN(result, dist);
if ( result == 0 ) return 0;
start = end;
}
return result;
}
/* test each segment of l1 against each segment of l2. Return min */
double distance2d_ptarray_ptarray(POINTARRAY *l1, POINTARRAY *l2)
{
double result = 99999999999.9;
char result_okay = 0; /*result is a valid min */
int t,u;
POINT2D start, end;
POINT2D start2, end2;
LWDEBUGF(2, "distance2d_ptarray_ptarray called (points: %d-%d)",
l1->npoints, l2->npoints);
getPoint2d_p(l1, 0, &start);
for (t=1; tnpoints; t++) /*for each segment in L1 */
{
getPoint2d_p(l1, t, &end);
getPoint2d_p(l2, 0, &start2);
for (u=1; unpoints; u++) /*for each segment in L2 */
{
double dist;
getPoint2d_p(l2, u, &end2);
dist = distance2d_seg_seg(&start, &end, &start2, &end2);
LWDEBUGF(4, "line_line; seg %i * seg %i, dist = %g\n",t,u,dist);
if (result_okay)
result = LW_MIN(result,dist);
else
{
result_okay = 1;
result = dist;
}
LWDEBUGF(3, " seg%d-seg%d dist: %f, mindist: %f",
t, u, dist, result);
if (result <= 0) return 0; /*intersection */
start2 = end2;
}
start = end;
}
return result;
}
/* true if point is in poly (and not in its holes) */
int pt_in_poly_2d(POINT2D *p, LWPOLY *poly)
{
int i;
/* Not in outer ring */
if ( ! pt_in_ring_2d(p, poly->rings[0]) ) return 0;
/* Check holes */
for (i=1; inrings; i++)
{
/* Inside a hole */
if ( pt_in_ring_2d(p, poly->rings[i]) ) return 0;
}
return 1; /* In outer ring, not in holes */
}
/*
* Brute force.
* Test line-ring distance against each ring.
* If there's an intersection (distance==0) then return 0 (crosses boundary).
* Otherwise, test to see if any point is inside outer rings of polygon,
* but not in inner rings.
* If so, return 0 (line inside polygon),
* otherwise return min distance to a ring (could be outside
* polygon or inside a hole)
*/
double distance2d_ptarray_poly(POINTARRAY *pa, LWPOLY *poly)
{
POINT2D pt;
int i;
double mindist = 0;
LWDEBUGF(2, "distance2d_ptarray_poly called (%d rings)", poly->nrings);
for (i=0; inrings; i++)
{
double dist = distance2d_ptarray_ptarray(pa, poly->rings[i]);
if (i) mindist = LW_MIN(mindist, dist);
else mindist = dist;
LWDEBUGF(3, " distance from ring %d: %f, mindist: %f",
i, dist, mindist);
if ( mindist <= 0 ) return 0.0; /* intersection */
}
/*
* No intersection, have to check if a point is
* inside polygon
*/
getPoint2d_p(pa, 0, &pt);
/*
* Outside outer ring, so min distance to a ring
* is the actual min distance
*/
if ( ! pt_in_ring_2d(&pt, poly->rings[0]) ) return mindist;
/*
* Its in the outer ring.
* Have to check if its inside a hole
*/
for (i=1; inrings; i++)
{
if ( pt_in_ring_2d(&pt, poly->rings[i]) )
{
/*
* Its inside a hole, then the actual
* distance is the min ring distance
*/
return mindist;
}
}
return 0.0; /* Not in hole, so inside polygon */
}
double distance2d_point_point(LWPOINT *point1, LWPOINT *point2)
{
POINT2D p1;
POINT2D p2;
getPoint2d_p(point1->point, 0, &p1);
getPoint2d_p(point2->point, 0, &p2);
return distance2d_pt_pt(&p1, &p2);
}
double distance2d_point_line(LWPOINT *point, LWLINE *line)
{
POINT2D p;
POINTARRAY *pa = line->points;
getPoint2d_p(point->point, 0, &p);
return distance2d_pt_ptarray(&p, pa);
}
double distance2d_line_line(LWLINE *line1, LWLINE *line2)
{
POINTARRAY *pa1 = line1->points;
POINTARRAY *pa2 = line2->points;
return distance2d_ptarray_ptarray(pa1, pa2);
}
/*
* 1. see if pt in outer boundary. if no, then treat the outer ring like a line
* 2. if in the boundary, test to see if its in a hole.
* if so, then return dist to hole, else return 0 (point in polygon)
*/
double distance2d_point_poly(LWPOINT *point, LWPOLY *poly)
{
POINT2D p;
int i;
getPoint2d_p(point->point, 0, &p);
LWDEBUG(2, "distance2d_point_poly called");
/* Return distance to outer ring if not inside it */
if ( ! pt_in_ring_2d(&p, poly->rings[0]) )
{
LWDEBUG(3, " not inside outer-ring");
return distance2d_pt_ptarray(&p, poly->rings[0]);
}
/*
* Inside the outer ring.
* Scan though each of the inner rings looking to
* see if its inside. If not, distance==0.
* Otherwise, distance = pt to ring distance
*/
for (i=1; inrings; i++)
{
/* Inside a hole. Distance = pt -> ring */
if ( pt_in_ring_2d(&p, poly->rings[i]) )
{
LWDEBUG(3, " inside an hole");
return distance2d_pt_ptarray(&p, poly->rings[i]);
}
}
LWDEBUG(3, " inside the polygon");
return 0.0; /* Is inside the polygon */
}
/*
* Brute force.
* Test to see if any rings intersect.
* If yes, dist=0.
* Test to see if one inside the other and if they are inside holes.
* Find min distance ring-to-ring.
*/
double distance2d_poly_poly(LWPOLY *poly1, LWPOLY *poly2)
{
POINT2D pt;
double mindist = -1;
int i;
LWDEBUG(2, "distance2d_poly_poly called");
/* if poly1 inside poly2 return 0 */
getPoint2d_p(poly1->rings[0], 0, &pt);
if ( pt_in_poly_2d(&pt, poly2) ) return 0.0;
/* if poly2 inside poly1 return 0 */
getPoint2d_p(poly2->rings[0], 0, &pt);
if ( pt_in_poly_2d(&pt, poly1) ) return 0.0;
LWDEBUG(3, " polys not inside each other");
/*
* foreach ring in Poly1
* foreach ring in Poly2
* if intersect, return 0
*/
for (i=0; inrings; i++)
{
int j;
for (j=0; jnrings; j++)
{
double d = distance2d_ptarray_ptarray(poly1->rings[i],
poly2->rings[j]);
if ( d <= 0 ) return 0.0;
/* mindist is -1 when not yet set */
if (mindist > -1) mindist = LW_MIN(mindist, d);
else mindist = d;
LWDEBUGF(3, " ring%i-%i dist: %f, mindist: %f", i, j, d, mindist);
}
}
/* otherwise return closest approach of rings (no intersection) */
return mindist;
}
double distance2d_line_poly(LWLINE *line, LWPOLY *poly)
{
return distance2d_ptarray_poly(line->points, poly);
}
/*find the 2d length of the given POINTARRAY (even if it's 3d) */
double lwgeom_pointarray_length2d(POINTARRAY *pts)
{
double dist = 0.0;
int i;
POINT2D frm;
POINT2D to;
if ( pts->npoints < 2 ) return 0.0;
for (i=0; inpoints-1;i++)
{
getPoint2d_p(pts, i, &frm);
getPoint2d_p(pts, i+1, &to);
dist += sqrt( ( (frm.x - to.x)*(frm.x - to.x) ) +
((frm.y - to.y)*(frm.y - to.y) ) );
}
return dist;
}
/*
* Find the 3d/2d length of the given POINTARRAY
* (depending on its dimensions)
*/
double
lwgeom_pointarray_length(POINTARRAY *pts)
{
double dist = 0.0;
int i;
POINT3DZ frm;
POINT3DZ to;
if ( pts->npoints < 2 ) return 0.0;
/* compute 2d length if 3d is not available */
if ( ! TYPE_HASZ(pts->dims) ) return lwgeom_pointarray_length2d(pts);
for (i=0; inpoints-1;i++)
{
getPoint3dz_p(pts, i, &frm);
getPoint3dz_p(pts, i+1, &to);
dist += sqrt( ( (frm.x - to.x)*(frm.x - to.x) ) +
((frm.y - to.y)*(frm.y - to.y) ) +
((frm.z - to.z)*(frm.z - to.z) ) );
}
return dist;
}
/*
* This should be rewritten to make use of the curve itself.
*/
double
lwgeom_curvepolygon_area(LWCURVEPOLY *curvepoly)
{
LWPOLY *poly = (LWPOLY *)lwgeom_segmentize((LWGEOM *)curvepoly, 32);
return lwgeom_polygon_area(poly);
}
/*
* Find the area of the outer ring - sum (area of inner rings).
* Could use a more numerically stable calculator...
*/
double
lwgeom_polygon_area(LWPOLY *poly)
{
double poly_area=0.0;
int i;
POINT2D p1;
POINT2D p2;
LWDEBUGF(2, "in lwgeom_polygon_area (%d rings)", poly->nrings);
for (i=0; inrings; i++)
{
int j;
POINTARRAY *ring = poly->rings[i];
double ringarea = 0.0;
LWDEBUGF(4, " rings %d has %d points", i, ring->npoints);
for (j=0; jnpoints-1; j++)
{
getPoint2d_p(ring, j, &p1);
getPoint2d_p(ring, j+1, &p2);
ringarea += ( p1.x * p2.y ) - ( p1.y * p2.x );
}
ringarea /= 2.0;
LWDEBUGF(4, " ring 1 has area %lf",ringarea);
ringarea = fabs(ringarea);
if (i != 0) /*outer */
ringarea = -1.0*ringarea ; /* its a hole */
poly_area += ringarea;
}
return poly_area;
}
/*
* Compute the sum of polygon rings length.
* Could use a more numerically stable calculator...
*/
double lwgeom_polygon_perimeter(LWPOLY *poly)
{
double result=0.0;
int i;
LWDEBUGF(2, "in lwgeom_polygon_perimeter (%d rings)", poly->nrings);
for (i=0; inrings; i++)
result += lwgeom_pointarray_length(poly->rings[i]);
return result;
}
/*
* Compute the sum of polygon rings length (forcing 2d computation).
* Could use a more numerically stable calculator...
*/
double lwgeom_polygon_perimeter2d(LWPOLY *poly)
{
double result=0.0;
int i;
LWDEBUGF(2, "in lwgeom_polygon_perimeter (%d rings)", poly->nrings);
for (i=0; inrings; i++)
result += lwgeom_pointarray_length2d(poly->rings[i]);
return result;
}
double
lwgeom_mindistance2d_recursive(uchar *lw1, uchar *lw2)
{
return lwgeom_mindistance2d_recursive_tolerance( lw1, lw2, 0.0 );
}
double
lwgeom_mindistance2d_recursive_tolerance(uchar *lw1, uchar *lw2, double tolerance)
{
LWGEOM_INSPECTED *in1, *in2;
int i, j;
double mindist = -1;
in1 = lwgeom_inspect(lw1);
in2 = lwgeom_inspect(lw2);
for (i=0; ingeometries; i++)
{
uchar *g1 = lwgeom_getsubgeometry_inspected(in1, i);
int t1 = lwgeom_getType(g1[0]);
double dist=tolerance;
/* it's a multitype... recurse */
if ( lwgeom_contains_subgeoms(t1) )
{
dist = lwgeom_mindistance2d_recursive_tolerance(g1, lw2, tolerance);
if ( dist <= tolerance ) return tolerance; /* can't be closer */
if ( mindist == -1 ) mindist = dist;
else mindist = LW_MIN(dist, mindist);
continue;
}
for (j=0; jngeometries; j++)
{
uchar *g2 = lwgeom_getsubgeometry_inspected(in2, j);
int t2 = lwgeom_getType(g2[0]);
if ( t1 == POINTTYPE )
{
if ( t2 == POINTTYPE )
{
dist = distance2d_point_point(
lwpoint_deserialize(g1),
lwpoint_deserialize(g2)
);
}
else if ( t2 == LINETYPE )
{
dist = distance2d_point_line(
lwpoint_deserialize(g1),
lwline_deserialize(g2)
);
}
else if ( t2 == POLYGONTYPE )
{
dist = distance2d_point_poly(
lwpoint_deserialize(g1),
lwpoly_deserialize(g2)
);
}
else
{
lwerror("Unsupported geometry type: %s", lwgeom_typename(t2));
}
}
else if ( t1 == LINETYPE )
{
if ( t2 == POINTTYPE )
{
dist = distance2d_point_line(
lwpoint_deserialize(g2),
lwline_deserialize(g1)
);
}
else if ( t2 == LINETYPE )
{
dist = distance2d_line_line(
lwline_deserialize(g1),
lwline_deserialize(g2)
);
}
else if ( t2 == POLYGONTYPE )
{
dist = distance2d_line_poly(
lwline_deserialize(g1),
lwpoly_deserialize(g2)
);
}
else
{
lwerror("Unsupported geometry type: %s", lwgeom_typename(t2));
}
}
else if ( t1 == POLYGONTYPE )
{
if ( t2 == POLYGONTYPE )
{
dist = distance2d_poly_poly(
lwpoly_deserialize(g2),
lwpoly_deserialize(g1)
);
}
else if ( t2 == POINTTYPE )
{
dist = distance2d_point_poly(
lwpoint_deserialize(g2),
lwpoly_deserialize(g1)
);
}
else if ( t2 == LINETYPE )
{
dist = distance2d_line_poly(
lwline_deserialize(g2),
lwpoly_deserialize(g1)
);
}
else
{
lwerror("Unsupported geometry type: %s", lwgeom_typename(t2));
}
}
else if (lwgeom_contains_subgeoms(t1)) /* it's a multitype... recurse */
{
dist = lwgeom_mindistance2d_recursive_tolerance(g1, g2, tolerance);
}
else
{
lwerror("Unsupported geometry type: %s", lwgeom_typename(t1));
}
if (mindist == -1 ) mindist = dist;
else mindist = LW_MIN(dist, mindist);
LWDEBUGF(3, "dist %d-%d: %f - mindist: %f",
i, j, dist, mindist);
if (mindist <= tolerance) return tolerance; /* can't be closer */
}
}
if (mindist<0) mindist = 0;
return mindist;
}
int
lwgeom_pt_inside_circle(POINT2D *p, double cx, double cy, double rad)
{
POINT2D center;
center.x = cx;
center.y = cy;
if ( distance2d_pt_pt(p, ¢er) < rad ) return 1;
else return 0;
}
/*
* Compute the azimuth of segment AB in radians.
* Return 0 on exception (same point), 1 otherwise.
*/
int
azimuth_pt_pt(POINT2D *A, POINT2D *B, double *d)
{
if ( A->x == B->x )
{
if ( A->y < B->y ) *d=0.0;
else if ( A->y > B->y ) *d=M_PI;
else return 0;
return 1;
}
if ( A->y == B->y )
{
if ( A->x < B->x ) *d=M_PI/2;
else if ( A->x > B->x ) *d=M_PI+(M_PI/2);
else return 0;
return 1;
}
if ( A->x < B->x )
{
if ( A->y < B->y )
{
*d=atan(fabs(A->x - B->x) / fabs(A->y - B->y) );
}
else /* ( A->y > B->y ) - equality case handled above */
{
*d=atan(fabs(A->y - B->y) / fabs(A->x - B->x) )
+ (M_PI/2);
}
}
else /* ( A->x > B->x ) - equality case handled above */
{
if ( A->y > B->y )
{
*d=atan(fabs(A->x - B->x) / fabs(A->y - B->y) )
+ M_PI;
}
else /* ( A->y < B->y ) - equality case handled above */
{
*d=atan(fabs(A->y - B->y) / fabs(A->x - B->x) )
+ (M_PI+(M_PI/2));
}
}
return 1;
}
================================================
FILE: src/liblwgeom/postgis_config.h
================================================
/* postgis_config.h. Generated from postgis_config.h.in by configure. */
/* postgis_config.h.in. Generated from configure.ac by autoheader. */
/* Define to 1 if you have the header file. */
#define HAVE_DLFCN_H 1
/* Defined if libiconv headers and library are present */
#define HAVE_ICONV 0
/* Define to 1 if you have the header file. */
#define HAVE_INTTYPES_H 1
/* Define to 1 if you have the `geos_c' library (-lgeos_c). */
#define HAVE_LIBGEOS_C 1
/* Define to 1 if you have the `pq' library (-lpq). */
#define HAVE_LIBPQ 0
/* Define to 1 if you have the `proj' library (-lproj). */
#define HAVE_LIBPROJ 0
/* Define to 1 if you have the header file. */
#define HAVE_MEMORY_H 1
/* Define to 1 if you have the header file. */
#define HAVE_STDINT_H 1
/* Define to 1 if you have the header file. */
#define HAVE_STDLIB_H 1
/* Define to 1 if you have the header file. */
#define HAVE_STRINGS_H 1
/* Define to 1 if you have the header file. */
#define HAVE_STRING_H 1
/* Define to 1 if you have the header file. */
#define HAVE_SYS_STAT_H 1
/* Define to 1 if you have the header file. */
#define HAVE_SYS_TYPES_H 1
/* Define to 1 if you have the header file. */
#define HAVE_UNISTD_H 1
/* Enable caching of bounding box within geometries */
#define POSTGIS_AUTOCACHE_BBOX 0
/* PostGIS build date */
#define POSTGIS_BUILD_DATE "2009-03-09 15:11:36"
/* PostGIS library debug level (0=disabled) */
#define POSTGIS_DEBUG_LEVEL 0
/* GEOS library version */
#define POSTGIS_GEOS_VERSION 30
/* PostGIS library version */
#define POSTGIS_LIB_VERSION "1.4.0SVN"
/* PostGIS major version */
#define POSTGIS_MAJOR_VERSION "1"
/* PostGIS micro version */
#define POSTGIS_MICRO_VERSION "0SVN"
/* PostGIS minor version */
#define POSTGIS_MINOR_VERSION "4"
/* PostgreSQL server version */
#define POSTGIS_PGSQL_VERSION 83
/* Enable GEOS profiling (0=disabled) */
#define POSTGIS_PROFILE 0
/* PROJ library version */
#define POSTGIS_PROJ_VERSION 46
/* PostGIS scripts version */
#define POSTGIS_SCRIPTS_VERSION "1.4.0SVN"
/* Enable use of ANALYZE statistics */
#define POSTGIS_USE_STATS 1
/* PostGIS version */
#define POSTGIS_VERSION "1.4 USE_GEOS=1 USE_PROJ=1 USE_STATS=1"
/* Define to 1 if you have the ANSI C header files. */
#define STDC_HEADERS 1
/* Define to 1 if `lex' declares `yytext' as a `char *' by default, not a
`char[]'. */
#define YYTEXT_POINTER 1
================================================
FILE: src/liblwgeom/ptarray.c
================================================
/**********************************************************************
* $Id: ptarray.c 3639 2009-02-04 00:28:37Z pramsey $
*
* PostGIS - Spatial Types for PostgreSQL
* http://postgis.refractions.net
* Copyright 2001-2006 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.
*
**********************************************************************/
#include
#include
#include "liblwgeom.h"
POINTARRAY *
ptarray_construct(char hasz, char hasm, unsigned int npoints)
{
uchar dims = 0;
size_t size;
uchar *ptlist;
POINTARRAY *pa;
TYPE_SETZM(dims, hasz?1:0, hasm?1:0);
size = TYPE_NDIMS(dims)*npoints*sizeof(double);
ptlist = (uchar *)lwalloc(size);
pa = lwalloc(sizeof(POINTARRAY));
pa->dims = dims;
pa->serialized_pointlist = ptlist;
pa->npoints = npoints;
return pa;
}
void ptarray_free(POINTARRAY *pa)
{
/* TODO
* Turn this on after retrofitting all calls to lwfree_ in /lwgeom
if( pa->serialized_pointlist )
lwfree(pa->serialized_pointlist);
*/
lwfree(pa);
}
void
ptarray_reverse(POINTARRAY *pa)
{
POINT4D pbuf;
uint32 i;
int ptsize = pointArray_ptsize(pa);
int last = pa->npoints-1;
int mid = last/2;
for (i=0; i<=mid; i++)
{
uchar *from, *to;
from = getPoint_internal(pa, i);
to = getPoint_internal(pa, (last-i));
memcpy((uchar *)&pbuf, to, ptsize);
memcpy(to, from, ptsize);
memcpy(from, (uchar *)&pbuf, ptsize);
}
}
/*
* calculate the 2d bounding box of a set of points
* write result to the provided BOX2DFLOAT4
* Return 0 if bounding box is NULL (empty geom)
*/
int
ptarray_compute_box2d_p(const POINTARRAY *pa, BOX2DFLOAT4 *result)
{
int t;
POINT2D pt;
BOX3D box;
if (pa->npoints == 0) return 0;
getPoint2d_p(pa, 0, &pt);
box.xmin = pt.x;
box.xmax = pt.x;
box.ymin = pt.y;
box.ymax = pt.y;
for (t=1; tnpoints; t++)
{
getPoint2d_p(pa, t, &pt);
if (pt.x < box.xmin) box.xmin = pt.x;
if (pt.y < box.ymin) box.ymin = pt.y;
if (pt.x > box.xmax) box.xmax = pt.x;
if (pt.y > box.ymax) box.ymax = pt.y;
}
box3d_to_box2df_p(&box, result);
return 1;
}
/*
* Calculate the 2d bounding box of a set of points.
* Return allocated BOX2DFLOAT4 or NULL (for empty array).
*/
BOX2DFLOAT4 *
ptarray_compute_box2d(const POINTARRAY *pa)
{
int t;
POINT2D pt;
BOX2DFLOAT4 *result;
if (pa->npoints == 0) return NULL;
result = lwalloc(sizeof(BOX2DFLOAT4));
getPoint2d_p(pa, 0, &pt);
result->xmin = pt.x;
result->xmax = pt.x;
result->ymin = pt.y;
result->ymax = pt.y;
for (t=1;tnpoints;t++)
{
getPoint2d_p(pa, t, &pt);
if (pt.x < result->xmin) result->xmin = pt.x;
if (pt.y < result->ymin) result->ymin = pt.y;
if (pt.x > result->xmax) result->xmax = pt.x;
if (pt.y > result->ymax) result->ymax = pt.y;
}
return result;
}
/*
* Returns a modified POINTARRAY so that no segment is
* longer then the given distance (computed using 2d).
* Every input point is kept.
* Z and M values for added points (if needed) are set to 0.
*/
POINTARRAY *
ptarray_segmentize2d(POINTARRAY *ipa, double dist)
{
double segdist;
POINT4D p1, p2;
void *ip, *op;
POINT4D pbuf;
POINTARRAY *opa;
int maxpoints = ipa->npoints;
int ptsize = pointArray_ptsize(ipa);
int ipoff=0; /* input point offset */
pbuf.x = pbuf.y = pbuf.z = pbuf.m = 0;
/* Initial storage */
opa = (POINTARRAY *)lwalloc(ptsize * maxpoints);
opa->dims = ipa->dims;
opa->npoints = 0;
opa->serialized_pointlist = (uchar *)lwalloc(maxpoints*ptsize);
/* Add first point */
opa->npoints++;
getPoint4d_p(ipa, ipoff, &p1);
op = getPoint_internal(opa, opa->npoints-1);
memcpy(op, &p1, ptsize);
ipoff++;
while (ipoffnpoints)
{
/*
* We use these pointers to avoid
* "strict-aliasing rules break" warning raised
* by gcc (3.3 and up).
*
* It looks that casting a variable address (also
* referred to as "type-punned pointer")
* breaks those "strict" rules.
*
*/
POINT4D *p1ptr=&p1, *p2ptr=&p2;
getPoint4d_p(ipa, ipoff, &p2);
segdist = distance2d_pt_pt((POINT2D *)p1ptr, (POINT2D *)p2ptr);
if (segdist > dist) /* add an intermediate point */
{
pbuf.x = p1.x + (p2.x-p1.x)/segdist * dist;
pbuf.y = p1.y + (p2.y-p1.y)/segdist * dist;
/* might also compute z and m if available... */
ip = &pbuf;
memcpy(&p1, ip, ptsize);
}
else /* copy second point */
{
ip = &p2;
p1 = p2;
ipoff++;
}
/* Add point */
if ( ++(opa->npoints) > maxpoints ) {
maxpoints *= 1.5;
opa->serialized_pointlist = (uchar *)lwrealloc(
opa->serialized_pointlist,
maxpoints*ptsize
);
}
op = getPoint_internal(opa, opa->npoints-1);
memcpy(op, ip, ptsize);
}
return opa;
}
char
ptarray_same(const POINTARRAY *pa1, const POINTARRAY *pa2)
{
unsigned int i;
size_t ptsize;
if ( TYPE_GETZM(pa1->dims) != TYPE_GETZM(pa2->dims) ) return 0;
if ( pa1->npoints != pa2->npoints ) return 0;
ptsize = pointArray_ptsize(pa1);
for (i=0; inpoints; i++)
{
if ( memcmp(getPoint_internal(pa1, i), getPoint_internal(pa2, i), ptsize) )
return 0;
}
return 1;
}
/*
* Add a point in a pointarray.
* 'where' is the offset (starting at 0)
* if 'where' == -1 append is required.
*/
POINTARRAY *
ptarray_addPoint(POINTARRAY *pa, uchar *p, size_t pdims, unsigned int where)
{
POINTARRAY *ret;
POINT4D pbuf;
size_t ptsize = pointArray_ptsize(pa);
LWDEBUGF(3, "pa %x p %x size %d where %d",
pa, p, pdims, where);
if ( pdims < 2 || pdims > 4 )
{
lwerror("ptarray_addPoint: point dimension out of range (%d)",
pdims);
return NULL;
}
if ( where > pa->npoints )
{
lwerror("ptarray_addPoint: offset out of range (%d)",
where);
return NULL;
}
LWDEBUG(3, "called with a %dD point");
pbuf.x = pbuf.y = pbuf.z = pbuf.m = 0.0;
memcpy((uchar *)&pbuf, p, pdims*sizeof(double));
LWDEBUG(3, "initialized point buffer");
ret = ptarray_construct(TYPE_HASZ(pa->dims),
TYPE_HASM(pa->dims), pa->npoints+1);
if ( where == -1 ) where = pa->npoints;
if ( where )
{
memcpy(getPoint_internal(ret, 0), getPoint_internal(pa, 0), ptsize*where);
}
memcpy(getPoint_internal(ret, where), (uchar *)&pbuf, ptsize);
if ( where+1 != ret->npoints )
{
memcpy(getPoint_internal(ret, where+1),
getPoint_internal(pa, where),
ptsize*(pa->npoints-where));
}
return ret;
}
/*
* Remove a point from a pointarray.
* 'which' is the offset (starting at 0)
* Returned pointarray is newly allocated
*/
POINTARRAY *
ptarray_removePoint(POINTARRAY *pa, unsigned int which)
{
POINTARRAY *ret;
size_t ptsize = pointArray_ptsize(pa);
LWDEBUGF(3, "pa %x which %d", pa, which);
#if PARANOIA_LEVEL > 0
if ( which > pa->npoints-1 )
{
lwerror("ptarray_removePoint: offset (%d) out of range (%d..%d)",
which, 0, pa->npoints-1);
return NULL;
}
if ( pa->npoints < 3 )
{
lwerror("ptarray_removePointe: can't remove a point from a 2-vertex POINTARRAY");
}
#endif
ret = ptarray_construct(TYPE_HASZ(pa->dims),
TYPE_HASM(pa->dims), pa->npoints-1);
/* copy initial part */
if ( which )
{
memcpy(getPoint_internal(ret, 0), getPoint_internal(pa, 0), ptsize*which);
}
/* copy final part */
if ( which < pa->npoints-1 )
{
memcpy(getPoint_internal(ret, which), getPoint_internal(pa, which+1),
ptsize*(pa->npoints-which-1));
}
return ret;
}
/*
* Clone a pointarray
*/
POINTARRAY *
ptarray_clone(const POINTARRAY *in)
{
POINTARRAY *out = lwalloc(sizeof(POINTARRAY));
size_t size;
LWDEBUG(3, "ptarray_clone called.");
out->dims = in->dims;
out->npoints = in->npoints;
size = in->npoints*sizeof(double)*TYPE_NDIMS(in->dims);
out->serialized_pointlist = lwalloc(size);
memcpy(out->serialized_pointlist, in->serialized_pointlist, size);
return out;
}
int
ptarray_isclosed2d(const POINTARRAY *in)
{
if ( memcmp(getPoint_internal(in, 0), getPoint_internal(in, in->npoints-1), sizeof(POINT2D)) ) return 0;
return 1;
}
/*
* calculate the BOX3D bounding box of a set of points
* returns a lwalloced BOX3D, or NULL on empty array.
* zmin/zmax values are set to NO_Z_VALUE if not available.
*/
BOX3D *
ptarray_compute_box3d(const POINTARRAY *pa)
{
BOX3D *result = lwalloc(sizeof(BOX3D));
if ( ! ptarray_compute_box3d_p(pa, result) )
{
lwfree(result);
return NULL;
}
return result;
}
/*
* calculate the BOX3D bounding box of a set of points
* zmin/zmax values are set to NO_Z_VALUE if not available.
* write result to the provided BOX3D
* Return 0 if bounding box is NULL (empty geom)
*/
int
ptarray_compute_box3d_p(const POINTARRAY *pa, BOX3D *result)
{
int t;
POINT3DZ pt;
LWDEBUGF(3, "ptarray_compute_box3d call (array has %d points)", pa->npoints);
if (pa->npoints == 0) return 0;
getPoint3dz_p(pa, 0, &pt);
LWDEBUG(3, "got point 0");
result->xmin = pt.x;
result->xmax = pt.x;
result->ymin = pt.y;
result->ymax = pt.y;
if ( TYPE_HASZ(pa->dims) ) {
result->zmin = pt.z;
result->zmax = pt.z;
} else {
result->zmin = NO_Z_VALUE;
result->zmax = NO_Z_VALUE;
}
LWDEBUGF(3, "scanning other %d points", pa->npoints);
for (t=1; tnpoints; t++)
{
getPoint3dz_p(pa,t,&pt);
if (pt.x < result->xmin) result->xmin = pt.x;
if (pt.y < result->ymin) result->ymin = pt.y;
if (pt.x > result->xmax) result->xmax = pt.x;
if (pt.y > result->ymax) result->ymax = pt.y;
if ( TYPE_HASZ(pa->dims) ) {
if (pt.z > result->zmax) result->zmax = pt.z;
if (pt.z < result->zmin) result->zmin = pt.z;
}
}
LWDEBUG(3, "returning box");
return 1;
}
/*
* TODO: implement point interpolation
*/
POINTARRAY *
ptarray_substring(POINTARRAY *ipa, double from, double to)
{
DYNPTARRAY *dpa;
POINTARRAY *opa;
POINT4D pt;
POINT4D p1, p2;
POINT4D *p1ptr=&p1; /* don't break strict-aliasing rule */
POINT4D *p2ptr=&p2;
int nsegs, i;
double length, slength, tlength;
int state = 0; /* 0=before, 1=inside */
/*
* Create a dynamic pointarray with an initial capacity
* equal to full copy of input points
*/
dpa = dynptarray_create(ipa->npoints, ipa->dims);
/* Compute total line length */
length = lwgeom_pointarray_length2d(ipa);
LWDEBUGF(3, "Total length: %g", length);
/* Get 'from' and 'to' lengths */
from = length*from;
to = length*to;
LWDEBUGF(3, "From/To: %g/%g", from, to);
tlength = 0;
getPoint4d_p(ipa, 0, &p1);
nsegs = ipa->npoints - 1;
for( i = 0; i < nsegs; i++ )
{
double dseg;
getPoint4d_p(ipa, i+1, &p2);
LWDEBUGF(3 ,"Segment %d: (%g,%g,%g,%g)-(%g,%g,%g,%g)",
i, p1.x, p1.y, p1.z, p1.m, p2.x, p2.y, p2.z, p2.m);
/* Find the length of this segment */
slength = distance2d_pt_pt((POINT2D *)p1ptr, (POINT2D *)p2ptr);
/*
* We are before requested start.
*/
if ( state == 0 ) /* before */
{
LWDEBUG(3, " Before start");
/*
* Didn't reach the 'from' point,
* nothing to do
*/
if ( from > tlength + slength ) goto END;
else if ( from == tlength + slength )
{
LWDEBUG(3, " Second point is our start");
/*
* Second point is our start
*/
dynptarray_addPoint4d(dpa, &p2, 1);
state=1; /* we're inside now */
goto END;
}
else if ( from == tlength )
{
LWDEBUG(3, " First point is our start");
/*
* First point is our start
*/
dynptarray_addPoint4d(dpa, &p1, 1);
/*
* We're inside now, but will check
* 'to' point as well
*/
state=1;
}
else /* tlength < from < tlength+slength */
{
LWDEBUG(3, " Seg contains first point");
/*
* Our start is between first and
* second point
*/
dseg = (from - tlength) / slength;
interpolate_point4d(&p1, &p2, &pt, dseg);
dynptarray_addPoint4d(dpa, &pt, 1);
/*
* We're inside now, but will check
* 'to' point as well
*/
state=1;
}
}
if ( state == 1 ) /* inside */
{
LWDEBUG(3, " Inside");
/*
* Didn't reach the 'end' point,
* just copy second point
*/
if ( to > tlength + slength )
{
dynptarray_addPoint4d(dpa, &p2, 0);
goto END;
}
/*
* 'to' point is our second point.
*/
else if ( to == tlength + slength )
{
LWDEBUG(3, " Second point is our end");
dynptarray_addPoint4d(dpa, &p2, 0);
break; /* substring complete */
}
/*
* 'to' point is our first point.
* (should only happen if 'to' is 0)
*/
else if ( to == tlength )
{
LWDEBUG(3, " First point is our end");
dynptarray_addPoint4d(dpa, &p1, 0);
break; /* substring complete */
}
/*
* 'to' point falls on this segment
* Interpolate and break.
*/
else if ( to < tlength + slength )
{
LWDEBUG(3, " Seg contains our end");
dseg = (to - tlength) / slength;
interpolate_point4d(&p1, &p2, &pt, dseg);
dynptarray_addPoint4d(dpa, &pt, 0);
break;
}
else
{
LWDEBUG(3, "Unhandled case");
}
}
END:
tlength += slength;
memcpy(&p1, &p2, sizeof(POINT4D));
}
/* Get constructed pointarray and release memory associated
* with the dynamic pointarray
*/
opa = dpa->pa;
lwfree(dpa);
LWDEBUGF(3, "Out of loop, ptarray has %d points", opa->npoints);
return opa;
}
/*
* Write into the *ret argument coordinates of the closes point on
* the given segment to the reference input point.
*/
void
closest_point_on_segment(POINT2D *p, POINT2D *A, POINT2D *B, POINT2D *ret)
{
double r;
if ( ( A->x == B->x) && (A->y == B->y) )
{
*ret = *A;
return;
}
/*
* We use comp.graphics.algorithms Frequently Asked Questions method
*
* (1) AC dot AB
* r = ----------
* ||AB||^2
* r has the following meaning:
* r=0 P = A
* r=1 P = B
* r<0 P is on the backward extension of AB
* r>1 P is on the forward extension of AB
* 0x-A->x) * (B->x-A->x) + (p->y-A->y) * (B->y-A->y) )/( (B->x-A->x)*(B->x-A->x) +(B->y-A->y)*(B->y-A->y) );
if (r<0) {
*ret = *A; return;
}
if (r>1) {
*ret = *B;
return;
}
ret->x = A->x + ( (B->x - A->x) * r );
ret->y = A->y + ( (B->y - A->y) * r );
}
/*
* Given a point, returns the location of closest point on pointarray
*/
double
ptarray_locate_point(POINTARRAY *pa, POINT2D *p)
{
double mindist=-1;
double tlen, plen;
int t, seg=-1;
POINT2D start, end;
POINT2D proj;
getPoint2d_p(pa, 0, &start);
for (t=1; tnpoints; t++)
{
double dist;
getPoint2d_p(pa, t, &end);
dist = distance2d_pt_seg(p, &start, &end);
if (t==1 || dist < mindist ) {
mindist = dist;
seg=t-1;
}
if ( mindist == 0 ) break;
start = end;
}
LWDEBUGF(3, "Closest segment: %d", seg);
/*
* If mindist is not 0 we need to project the
* point on the closest segment.
*/
if ( mindist > 0 )
{
getPoint2d_p(pa, seg, &start);
getPoint2d_p(pa, seg+1, &end);
closest_point_on_segment(p, &start, &end, &proj);
} else {
proj = *p;
}
LWDEBUGF(3, "Closest point on segment: %g,%g", proj.x, proj.y);
tlen = lwgeom_pointarray_length2d(pa);
LWDEBUGF(3, "tlen %g", tlen);
plen=0;
getPoint2d_p(pa, 0, &start);
for (t=0; t 180 becomes X - 360
*/
void
ptarray_longitude_shift(POINTARRAY *pa)
{
int i;
double x;
for (i=0; inpoints; i++) {
memcpy(&x, getPoint_internal(pa, i), sizeof(double));
if ( x < 0 ) x+= 360;
else if ( x > 180 ) x -= 360;
memcpy(getPoint_internal(pa, i), &x, sizeof(double));
}
}
DYNPTARRAY *
dynptarray_create(size_t initial_capacity, int dims)
{
DYNPTARRAY *ret=lwalloc(sizeof(DYNPTARRAY));
LWDEBUGF(3, "dynptarray_create called, dims=%d.", dims);
if ( initial_capacity < 1 ) initial_capacity=1;
ret->pa=lwalloc(sizeof(POINTARRAY));
ret->pa->dims=dims;
ret->ptsize=pointArray_ptsize(ret->pa);
ret->capacity=initial_capacity;
ret->pa->serialized_pointlist=lwalloc(ret->ptsize*ret->capacity);
ret->pa->npoints=0;
return ret;
}
/*
* Add a POINT4D to the dynamic pointarray.
*
* The dynamic pointarray may be of any dimension, only
* accepted dimensions will be copied.
*
* If allow_duplicates is set to 0 (false) a check
* is performed to see if last point in array is equal to the
* provided one. NOTE that the check is 4d based, with missing
* ordinates in the pointarray set to NO_Z_VALUE and NO_M_VALUE
* respectively.
*/
int
dynptarray_addPoint4d(DYNPTARRAY *dpa, POINT4D *p4d, int allow_duplicates)
{
POINTARRAY *pa=dpa->pa;
POINT4D tmp;
LWDEBUG(3, "dynptarray_addPoint4d called.");
if ( ! allow_duplicates && pa->npoints > 0 )
{
getPoint4d_p(pa, pa->npoints-1, &tmp);
/*
* return 0 and do nothing else if previous point in list is
* equal to this one (4D equality)
*/
if (tmp.x == p4d->x && tmp.y == p4d->y && tmp.z == p4d->z && tmp.m == p4d->m) return 0;
}
++pa->npoints;
if ( pa->npoints > dpa->capacity )
{
dpa->capacity*=2;
pa->serialized_pointlist = lwrealloc(
pa->serialized_pointlist,
dpa->capacity*dpa->ptsize);
}
setPoint4d(pa, pa->npoints-1, p4d);
return 1;
}
================================================
FILE: src/liblwgeom/vsprintf.c
================================================
/* Like vsprintf but provides a pointer to malloc'd storage, which must
be freed by the caller.
Copyright (C) 1994, 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
#ifdef HAVE_CONFIG_H
# include
#endif
#include
#include
#include
#if __STDC__
# include
#else
# include
#endif
#include
#ifdef TEST
int global_total_width;
#endif
int lw_vasprintf (char **result, const char *format, va_list args);
int lw_asprintf
#if __STDC__
(char **result, const char *format, ...);
#else
(result, va_alist);
char **result;
va_dcl
#endif
static int
int_vasprintf (result, format, args)
char **result;
const char *format;
va_list *args;
{
const char *p = format;
/* Add one to make sure that it is never zero, which might cause malloc
to return NULL. */
int total_width = strlen (format) + 1;
va_list ap;
memcpy (&ap, args, sizeof (va_list));
while (*p != '\0')
{
if (*p++ == '%')
{
while (strchr ("-+ #0", *p))
++p;
if (*p == '*')
{
++p;
total_width += abs (va_arg (ap, int));
}
else
total_width += strtoul (p, (char **) &p, 10);
if (*p == '.')
{
++p;
if (*p == '*')
{
++p;
total_width += abs (va_arg (ap, int));
}
else
total_width += strtoul (p, (char **) &p, 10);
}
while (strchr ("hlLjtz", *p))
++p;
/* Should be big enough for any format specifier except %s
and floats. */
total_width += 30;
switch (*p)
{
case 'd':
case 'i':
case 'o':
case 'u':
case 'x':
case 'X':
case 'c':
(void) va_arg (ap, int);
break;
case 'f':
{
double arg = va_arg (ap, double);
if (arg >= 1.0 || arg <= -1.0)
/* Since an ieee double can have an exponent of 307, we'll
make the buffer wide enough to cover the gross case. */
total_width += 307;
}
break;
case 'e':
case 'E':
case 'g':
case 'G':
(void) va_arg (ap, double);
break;
case 's':
total_width += strlen (va_arg (ap, char *));
break;
case 'p':
case 'n':
(void) va_arg (ap, char *);
break;
}
p++;
}
}
#ifdef TEST
global_total_width = total_width;
#endif
*result = malloc (total_width);
if (*result != NULL)
return vsprintf (*result, format, *args);
else
return 0;
}
int
lw_vasprintf (result, format, args)
char **result;
const char *format;
va_list args;
{
va_list temp;
/* Use va_copy for compatibility with both 32 and 64 bit args */
__va_copy(temp, args);
return int_vasprintf (result, format, &temp);
}
int
lw_asprintf
#if __STDC__
(char **result, const char *format, ...)
#else
(result, va_alist)
char **result;
va_dcl
#endif
{
va_list args;
int done;
#if __STDC__
va_start (args, format);
#else
char *format;
va_start (args);
format = va_arg (args, char *);
#endif
done = lw_vasprintf (result, format, args);
va_end (args);
return done;
}
================================================
FILE: src/liblwgeom/wktparse.h
================================================
/*
* Written by Ralph Mason ralph.masontelogis.com
*
* Copyright Telogis 2004
* www.telogis.com
*
*/
#ifndef _WKTPARSE_H
#define _WKTPARSE_H
#include
#ifndef _LIBLWGEOM_H
typedef unsigned char uchar;
typedef struct serialized_lwgeom {
uchar *lwgeom;
int size;
} SERIALIZED_LWGEOM;
typedef struct struct_lwgeom_parser_result
{
const char *wkinput;
SERIALIZED_LWGEOM *serialized_lwgeom;
int size;
const char *message;
int errlocation;
} LWGEOM_PARSER_RESULT;
typedef struct struct_lwgeom_unparser_result
{
uchar *serialized_lwgeom;
char *wkoutput;
int size;
const char *message;
int errlocation;
} LWGEOM_UNPARSER_RESULT;
#endif
typedef void* (*allocator)(size_t size);
typedef void (*freeor)(void* mem);
typedef void (*report_error)(const char* string, ...);
/*typedef unsigned long int4;*/
/* How much memory is allocated at a time(bytes) for tuples */
#define ALLOC_CHUNKS 8192
/* to shrink ints less than 0x7f to 1 byte */
/* #define SHRINK_INTS */
#define POINTTYPE 1
#define LINETYPE 2
#define POLYGONTYPE 3
#define MULTIPOINTTYPE 4
#define MULTILINETYPE 5
#define MULTIPOLYGONTYPE 6
#define COLLECTIONTYPE 7
/* Extended lwgeom integer types */
#define POINTTYPEI 10
#define LINETYPEI 11
#define POLYGONTYPEI 12
#define CIRCSTRINGTYPE 8
#define COMPOUNDTYPE 9
#define CURVEPOLYTYPE 13
#define MULTICURVETYPE 14
#define MULTISURFACETYPE 15
extern int srid;
/*
These functions are used by the
generated parser and are not meant
for public use
*/
void set_srid(double srid);
void alloc_lwgeom(int srid);
void alloc_point_2d(double x,double y);
void alloc_point_3d(double x,double y,double z);
void alloc_point_4d(double x,double y,double z,double m);
void alloc_point(void);
void alloc_linestring(void);
void alloc_linestring_closed(void);
void alloc_circularstring(void);
void alloc_circularstring_closed(void);
void alloc_polygon(void);
void alloc_compoundcurve(void);
void alloc_curvepolygon(void);
void alloc_multipoint(void);
void alloc_multilinestring(void);
void alloc_multicurve(void);
void alloc_multipolygon(void);
void alloc_multisurface(void);
void alloc_geomertycollection(void);
void alloc_empty();
void alloc_counter(void);
void pop(void);
void popc(void);
void alloc_wkb(const char* parser);
/*
Use these functions to parse and unparse lwgeoms
You are responsible for freeing the returned memory.
*/
int parse_lwg(LWGEOM_PARSER_RESULT *lwg_parser_result, const char* wkt, int flags, allocator allocfunc,report_error errfunc);
int parse_lwgi(LWGEOM_PARSER_RESULT *lwg_parser_result, const char* wkt, int flags, allocator allocfunc,report_error errfunc);
int unparse_WKT(LWGEOM_UNPARSER_RESULT *lwg_unparser_result, uchar* serialized, allocator alloc, freeor free, int flags);
int unparse_WKB(LWGEOM_UNPARSER_RESULT *lwg_unparser_result, uchar* serialized, allocator alloc, freeor free, int flags, char endian, uchar hexform);
int lwg_parse_yyparse(void);
int lwg_parse_yyerror(char* s);
void lwg_parse_yynotice(char* s);
#endif /* _WKTPARSE_H */
================================================
FILE: src/liblwgeom/wktparse.lex
================================================
/*
* Written by Ralph Mason ralph.masontelogis.com
*
* Copyright Telogis 2004
* www.telogis.com
*
*/
%x vals_ok
%{
#include "wktparse.tab.h"
#include
#include /* need stdlib for atof() definition */
void init_parser(const char *src);
void close_parser(void);
int lwg_parse_yywrap(void);
int lwg_parse_yylex(void);
static YY_BUFFER_STATE buf_state;
void init_parser(const char *src) { BEGIN(0);buf_state = lwg_parse_yy_scan_string(src); }
void close_parser() { lwg_parse_yy_delete_buffer(buf_state); }
int lwg_parse_yywrap(void){ return 1; }
/* Macro to keep track of the current parse position */
#define UPDATE_YYLLOC() (lwg_parse_yylloc.last_column += yyleng)
%}
%%
[-|\+]?[0-9]+(\.[0-9]+)?([Ee](\+|-)?[0-9]+)? { lwg_parse_yylval.value=atof(lwg_parse_yytext); UPDATE_YYLLOC(); return VALUE; }
[-|\+]?(\.[0-9]+)([Ee](\+|-)?[0-9]+)? { lwg_parse_yylval.value=atof(lwg_parse_yytext); UPDATE_YYLLOC(); return VALUE; }
00[0-9A-F]* { lwg_parse_yylval.wkb=lwg_parse_yytext; return WKB;}
01[0-9A-F]* { lwg_parse_yylval.wkb=lwg_parse_yytext; return WKB;}
<*>POINT { UPDATE_YYLLOC(); return POINT; }
<*>POINTM { UPDATE_YYLLOC(); return POINTM; }
<*>LINESTRING { UPDATE_YYLLOC(); return LINESTRING; }
<*>LINESTRINGM { UPDATE_YYLLOC(); return LINESTRINGM; }
<*>CIRCULARSTRING { UPDATE_YYLLOC(); return CIRCULARSTRING; }
<*>CIRCULARSTRINGM { UPDATE_YYLLOC(); return CIRCULARSTRINGM; }
<*>POLYGON { UPDATE_YYLLOC(); return POLYGON; }
<*>POLYGONM { UPDATE_YYLLOC(); return POLYGONM; }
<*>COMPOUNDCURVE { UPDATE_YYLLOC(); return COMPOUNDCURVE; }
<*>COMPOUNDCURVEM { UPDATE_YYLLOC(); return COMPOUNDCURVEM; }
<*>CURVEPOLYGON { UPDATE_YYLLOC(); return CURVEPOLYGON; }
<*>CURVEPOLYGONM { UPDATE_YYLLOC(); return CURVEPOLYGONM; }
<*>MULTIPOINT { UPDATE_YYLLOC(); return MULTIPOINT; }
<*>MULTIPOINTM { UPDATE_YYLLOC(); return MULTIPOINTM; }
<*>MULTILINESTRING { UPDATE_YYLLOC(); return MULTILINESTRING; }
<*>MULTILINESTRINGM { UPDATE_YYLLOC(); return MULTILINESTRINGM; }
<*>MULTICURVE { UPDATE_YYLLOC(); return MULTICURVE; }
<*>MULTICURVEM { UPDATE_YYLLOC(); return MULTICURVEM; }
<*>MULTIPOLYGON { UPDATE_YYLLOC(); return MULTIPOLYGON; }
<*>MULTIPOLYGONM { UPDATE_YYLLOC(); return MULTIPOLYGONM; }
<*>MULTISURFACE { UPDATE_YYLLOC(); return MULTISURFACE; }
<*>MULTISURFACEM { UPDATE_YYLLOC(); return MULTISURFACEM; }
<*>GEOMETRYCOLLECTION { UPDATE_YYLLOC(); return GEOMETRYCOLLECTION; }
<*>GEOMETRYCOLLECTIONM { UPDATE_YYLLOC(); return GEOMETRYCOLLECTIONM; }
<*>SRID { BEGIN(vals_ok); UPDATE_YYLLOC(); return SRID; }
<*>EMPTY { UPDATE_YYLLOC(); return EMPTY; }
<*>\( { BEGIN(vals_ok); UPDATE_YYLLOC(); return LPAREN; }
<*>\) { UPDATE_YYLLOC(); return RPAREN; }
<*>, { UPDATE_YYLLOC(); return COMMA ; }
<*>= { UPDATE_YYLLOC(); return EQUALS ; }
<*>; { BEGIN(0); UPDATE_YYLLOC(); return SEMICOLON; }
<*>[ \t\n\r]+ /*eat whitespace*/ { UPDATE_YYLLOC(); }
<*>. { return lwg_parse_yytext[0]; }
%%
================================================
FILE: src/liblwgeom/wktparse.tab.c
================================================
/* A Bison parser, made by GNU Bison 2.3. */
/* Skeleton implementation for Bison's Yacc-like parsers in C
Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA. */
/* As a special exception, you may create a larger work that contains
part or all of the Bison parser skeleton and distribute that work
under terms of your choice, so long as that work isn't itself a
parser generator using the skeleton or a modified version thereof
as a parser skeleton. Alternatively, if you modify or redistribute
the parser skeleton itself, you may (at your option) remove this
special exception, which will cause the skeleton and the resulting
Bison output files to be licensed under the GNU General Public
License without this special exception.
This special exception was added by the Free Software Foundation in
version 2.2 of Bison. */
/* C LALR(1) parser skeleton written by Richard Stallman, by
simplifying the original so-called "semantic" parser. */
/* All symbols defined below should begin with yy or YY, to avoid
infringing on user name space. This should be done even for local
variables, as they might otherwise be expanded by user macros.
There are some unavoidable exceptions within include files to
define necessary library symbols; they are noted "INFRINGES ON
USER NAME SPACE" below. */
/* Identify Bison output. */
#define YYBISON 1
/* Bison version. */
#define YYBISON_VERSION "2.3"
/* Skeleton name. */
#define YYSKELETON_NAME "yacc.c"
/* Pure parsers. */
#define YYPURE 0
/* Using locations. */
#define YYLSP_NEEDED 1
/* Substitute the variable and function names. */
#define yyparse lwg_parse_yyparse
#define yylex lwg_parse_yylex
#define yyerror lwg_parse_yyerror
#define yylval lwg_parse_yylval
#define yychar lwg_parse_yychar
#define yydebug lwg_parse_yydebug
#define yynerrs lwg_parse_yynerrs
#define yylloc lwg_parse_yylloc
/* Tokens. */
#ifndef YYTOKENTYPE
# define YYTOKENTYPE
/* Put the tokens into the symbol table, so that GDB and other debuggers
know about them. */
enum yytokentype {
POINT = 258,
LINESTRING = 259,
POLYGON = 260,
MULTIPOINT = 261,
MULTILINESTRING = 262,
MULTIPOLYGON = 263,
GEOMETRYCOLLECTION = 264,
CIRCULARSTRING = 265,
COMPOUNDCURVE = 266,
CURVEPOLYGON = 267,
MULTICURVE = 268,
MULTISURFACE = 269,
POINTM = 270,
LINESTRINGM = 271,
POLYGONM = 272,
MULTIPOINTM = 273,
MULTILINESTRINGM = 274,
MULTIPOLYGONM = 275,
GEOMETRYCOLLECTIONM = 276,
CIRCULARSTRINGM = 277,
COMPOUNDCURVEM = 278,
CURVEPOLYGONM = 279,
MULTICURVEM = 280,
MULTISURFACEM = 281,
SRID = 282,
EMPTY = 283,
VALUE = 284,
LPAREN = 285,
RPAREN = 286,
COMMA = 287,
EQUALS = 288,
SEMICOLON = 289,
WKB = 290
};
#endif
/* Tokens. */
#define POINT 258
#define LINESTRING 259
#define POLYGON 260
#define MULTIPOINT 261
#define MULTILINESTRING 262
#define MULTIPOLYGON 263
#define GEOMETRYCOLLECTION 264
#define CIRCULARSTRING 265
#define COMPOUNDCURVE 266
#define CURVEPOLYGON 267
#define MULTICURVE 268
#define MULTISURFACE 269
#define POINTM 270
#define LINESTRINGM 271
#define POLYGONM 272
#define MULTIPOINTM 273
#define MULTILINESTRINGM 274
#define MULTIPOLYGONM 275
#define GEOMETRYCOLLECTIONM 276
#define CIRCULARSTRINGM 277
#define COMPOUNDCURVEM 278
#define CURVEPOLYGONM 279
#define MULTICURVEM 280
#define MULTISURFACEM 281
#define SRID 282
#define EMPTY 283
#define VALUE 284
#define LPAREN 285
#define RPAREN 286
#define COMMA 287
#define EQUALS 288
#define SEMICOLON 289
#define WKB 290
/* Copy the first part of user declarations. */
#line 9 "wktparse.y"
#include "wktparse.h"
#include
#include
void set_zm(char z, char m);
int lwg_parse_yylex(void);
/* Enabling traces. */
#ifndef YYDEBUG
# define YYDEBUG 0
#endif
/* Enabling verbose error messages. */
#ifdef YYERROR_VERBOSE
# undef YYERROR_VERBOSE
# define YYERROR_VERBOSE 1
#else
# define YYERROR_VERBOSE 0
#endif
/* Enabling the token table. */
#ifndef YYTOKEN_TABLE
# define YYTOKEN_TABLE 0
#endif
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
typedef union YYSTYPE
#line 22 "wktparse.y"
{
double value;
const char* wkb;
}
/* Line 187 of yacc.c. */
#line 188 "y.tab.c"
YYSTYPE;
# define yystype YYSTYPE /* obsolescent; will be withdrawn */
# define YYSTYPE_IS_DECLARED 1
# define YYSTYPE_IS_TRIVIAL 1
#endif
#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED
typedef struct YYLTYPE
{
int first_line;
int first_column;
int last_line;
int last_column;
} YYLTYPE;
# define yyltype YYLTYPE /* obsolescent; will be withdrawn */
# define YYLTYPE_IS_DECLARED 1
# define YYLTYPE_IS_TRIVIAL 1
#endif
/* Copy the second part of user declarations. */
/* Line 216 of yacc.c. */
#line 213 "y.tab.c"
#ifdef short
# undef short
#endif
#ifdef YYTYPE_UINT8
typedef YYTYPE_UINT8 yytype_uint8;
#else
typedef unsigned char yytype_uint8;
#endif
#ifdef YYTYPE_INT8
typedef YYTYPE_INT8 yytype_int8;
#elif (defined __STDC__ || defined __C99__FUNC__ \
|| defined __cplusplus || defined _MSC_VER)
typedef signed char yytype_int8;
#else
typedef short int yytype_int8;
#endif
#ifdef YYTYPE_UINT16
typedef YYTYPE_UINT16 yytype_uint16;
#else
typedef unsigned short int yytype_uint16;
#endif
#ifdef YYTYPE_INT16
typedef YYTYPE_INT16 yytype_int16;
#else
typedef short int yytype_int16;
#endif
#ifndef YYSIZE_T
# ifdef __SIZE_TYPE__
# define YYSIZE_T __SIZE_TYPE__
# elif defined size_t
# define YYSIZE_T size_t
# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \
|| defined __cplusplus || defined _MSC_VER)
# include /* INFRINGES ON USER NAME SPACE */
# define YYSIZE_T size_t
# else
# define YYSIZE_T unsigned int
# endif
#endif
#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
#ifndef YY_
# if YYENABLE_NLS
# if ENABLE_NLS
# include /* INFRINGES ON USER NAME SPACE */
# define YY_(msgid) dgettext ("bison-runtime", msgid)
# endif
# endif
# ifndef YY_
# define YY_(msgid) msgid
# endif
#endif
/* Suppress unused-variable warnings by "using" E. */
#if ! defined lint || defined __GNUC__
# define YYUSE(e) ((void) (e))
#else
# define YYUSE(e) /* empty */
#endif
/* Identity function, used to suppress warnings about constant conditions. */
#ifndef lint
# define YYID(n) (n)
#else
#if (defined __STDC__ || defined __C99__FUNC__ \
|| defined __cplusplus || defined _MSC_VER)
static int
YYID (int i)
#else
static int
YYID (i)
int i;
#endif
{
return i;
}
#endif
#if ! defined yyoverflow || YYERROR_VERBOSE
/* The parser invokes alloca or malloc; define the necessary symbols. */
# ifdef YYSTACK_USE_ALLOCA
# if YYSTACK_USE_ALLOCA
# ifdef __GNUC__
# define YYSTACK_ALLOC __builtin_alloca
# elif defined __BUILTIN_VA_ARG_INCR
# include /* INFRINGES ON USER NAME SPACE */
# elif defined _AIX
# define YYSTACK_ALLOC __alloca
# elif defined _MSC_VER
# include /* INFRINGES ON USER NAME SPACE */
# define alloca _alloca
# else
# define YYSTACK_ALLOC alloca
# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
|| defined __cplusplus || defined _MSC_VER)
# include /* INFRINGES ON USER NAME SPACE */
# ifndef _STDLIB_H
# define _STDLIB_H 1
# endif
# endif
# endif
# endif
# endif
# ifdef YYSTACK_ALLOC
/* Pacify GCC's `empty if-body' warning. */
# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
# ifndef YYSTACK_ALLOC_MAXIMUM
/* The OS might guarantee only one guard page at the bottom of the stack,
and a page size can be as small as 4096 bytes. So we cannot safely
invoke alloca (N) if N exceeds 4096. Use a slightly smaller number
to allow for a few compiler-allocated temporary stack slots. */
# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
# endif
# else
# define YYSTACK_ALLOC YYMALLOC
# define YYSTACK_FREE YYFREE
# ifndef YYSTACK_ALLOC_MAXIMUM
# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
# endif
# if (defined __cplusplus && ! defined _STDLIB_H \
&& ! ((defined YYMALLOC || defined malloc) \
&& (defined YYFREE || defined free)))
# include /* INFRINGES ON USER NAME SPACE */
# ifndef _STDLIB_H
# define _STDLIB_H 1
# endif
# endif
# ifndef YYMALLOC
# define YYMALLOC malloc
# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
|| defined __cplusplus || defined _MSC_VER)
void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
# endif
# endif
# ifndef YYFREE
# define YYFREE free
# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
|| defined __cplusplus || defined _MSC_VER)
void free (void *); /* INFRINGES ON USER NAME SPACE */
# endif
# endif
# endif
#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
#if (! defined yyoverflow \
&& (! defined __cplusplus \
|| (defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \
&& defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
/* A type that is properly aligned for any stack member. */
union yyalloc
{
yytype_int16 yyss;
YYSTYPE yyvs;
YYLTYPE yyls;
};
/* The size of the maximum gap between one aligned stack and the next. */
# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
/* The size of an array large to enough to hold all stacks, each with
N elements. */
# define YYSTACK_BYTES(N) \
((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE) + sizeof (YYLTYPE)) \
+ 2 * YYSTACK_GAP_MAXIMUM)
/* Copy COUNT objects from FROM to TO. The source and destination do
not overlap. */
# ifndef YYCOPY
# if defined __GNUC__ && 1 < __GNUC__
# define YYCOPY(To, From, Count) \
__builtin_memcpy (To, From, (Count) * sizeof (*(From)))
# else
# define YYCOPY(To, From, Count) \
do \
{ \
YYSIZE_T yyi; \
for (yyi = 0; yyi < (Count); yyi++) \
(To)[yyi] = (From)[yyi]; \
} \
while (YYID (0))
# endif
# endif
/* Relocate STACK from its old location to the new one. The
local variables YYSIZE and YYSTACKSIZE give the old and new number of
elements in the stack, and YYPTR gives the new location of the
stack. Advance YYPTR to a properly aligned location for the next
stack. */
# define YYSTACK_RELOCATE(Stack) \
do \
{ \
YYSIZE_T yynewbytes; \
YYCOPY (&yyptr->Stack, Stack, yysize); \
Stack = &yyptr->Stack; \
yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
yyptr += yynewbytes / sizeof (*yyptr); \
} \
while (YYID (0))
#endif
/* YYFINAL -- State number of the termination state. */
#define YYFINAL 6
/* YYLAST -- Last index in YYTABLE. */
#define YYLAST 180
/* YYNTOKENS -- Number of terminals. */
#define YYNTOKENS 36
/* YYNNTS -- Number of nonterminals. */
#define YYNNTS 107
/* YYNRULES -- Number of rules. */
#define YYNRULES 169
/* YYNRULES -- Number of states. */
#define YYNSTATES 237
/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
#define YYUNDEFTOK 2
#define YYMAXUTOK 290
#define YYTRANSLATE(YYX) \
((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */
static const yytype_uint8 yytranslate[] =
{
0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
35
};
#if YYDEBUG
/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
YYRHS. */
static const yytype_uint16 yyprhs[] =
{
0, 0, 3, 4, 9, 10, 13, 15, 17, 19,
21, 23, 25, 27, 29, 31, 33, 35, 37, 39,
43, 45, 48, 49, 53, 55, 57, 58, 61, 62,
65, 69, 70, 74, 75, 79, 81, 82, 87, 89,
93, 95, 96, 99, 102, 103, 107, 109, 111, 112,
115, 116, 119, 120, 123, 124, 129, 131, 135, 138,
139, 143, 146, 147, 151, 153, 155, 157, 159, 160,
163, 164, 167, 168, 171, 172, 177, 179, 183, 184,
188, 189, 193, 195, 196, 201, 203, 205, 209, 213,
214, 218, 219, 223, 225, 226, 231, 233, 237, 238,
242, 243, 247, 249, 250, 255, 257, 259, 263, 267,
270, 271, 275, 277, 279, 280, 283, 284, 287, 288,
293, 295, 299, 300, 304, 305, 309, 311, 312, 317,
319, 321, 325, 329, 330, 334, 335, 339, 341, 342,
347, 349, 353, 354, 358, 359, 363, 365, 366, 371,
373, 375, 379, 383, 384, 388, 389, 393, 395, 396,
401, 403, 405, 409, 411, 413, 415, 418, 422, 427
};
/* YYRHS -- A `-1'-separated list of the rules' RHS. */
static const yytype_int16 yyrhs[] =
{
37, 0, -1, -1, 41, 34, 38, 40, -1, -1,
39, 40, -1, 42, -1, 43, -1, 59, -1, 71,
-1, 104, -1, 86, -1, 114, -1, 51, -1, 92,
-1, 98, -1, 120, -1, 126, -1, 132, -1, 27,
33, 29, -1, 35, -1, 3, 45, -1, -1, 15,
44, 45, -1, 46, -1, 48, -1, -1, 47, 142,
-1, -1, 49, 50, -1, 30, 138, 31, -1, -1,
6, 52, 54, -1, -1, 18, 53, 54, -1, 142,
-1, -1, 55, 30, 56, 31, -1, 57, -1, 56,
32, 57, -1, 48, -1, -1, 58, 138, -1, 4,
61, -1, -1, 16, 60, 61, -1, 62, -1, 64,
-1, -1, 63, 142, -1, -1, 65, 68, -1, -1,
67, 68, -1, -1, 69, 30, 70, 31, -1, 138,
-1, 70, 32, 138, -1, 10, 75, -1, -1, 22,
72, 75, -1, 10, 76, -1, -1, 22, 74, 76,
-1, 77, -1, 79, -1, 77, -1, 81, -1, -1,
78, 142, -1, -1, 80, 83, -1, -1, 82, 83,
-1, -1, 84, 30, 85, 31, -1, 138, -1, 85,
32, 138, -1, -1, 11, 87, 89, -1, -1, 23,
88, 89, -1, 142, -1, -1, 90, 30, 91, 31,
-1, 64, -1, 71, -1, 91, 32, 64, -1, 91,
32, 71, -1, -1, 7, 93, 95, -1, -1, 19,
94, 95, -1, 142, -1, -1, 96, 30, 97, 31,
-1, 64, -1, 97, 32, 64, -1, -1, 13, 99,
101, -1, -1, 25, 100, 101, -1, 142, -1, -1,
102, 30, 103, 31, -1, 64, -1, 71, -1, 103,
32, 64, -1, 103, 32, 71, -1, 5, 106, -1,
-1, 17, 105, 106, -1, 107, -1, 109, -1, -1,
108, 142, -1, -1, 110, 111, -1, -1, 112, 30,
113, 31, -1, 68, -1, 113, 32, 68, -1, -1,
12, 115, 117, -1, -1, 24, 116, 117, -1, 142,
-1, -1, 118, 30, 119, 31, -1, 66, -1, 73,
-1, 119, 32, 66, -1, 119, 32, 73, -1, -1,
8, 121, 123, -1, -1, 20, 122, 123, -1, 142,
-1, -1, 124, 30, 125, 31, -1, 109, -1, 125,
32, 109, -1, -1, 14, 127, 129, -1, -1, 26,
128, 129, -1, 142, -1, -1, 130, 30, 131, 31,
-1, 109, -1, 114, -1, 131, 32, 109, -1, 131,
32, 114, -1, -1, 9, 133, 135, -1, -1, 21,
134, 135, -1, 142, -1, -1, 136, 30, 137, 31,
-1, 142, -1, 40, -1, 137, 32, 40, -1, 139,
-1, 140, -1, 141, -1, 29, 29, -1, 29, 29,
29, -1, 29, 29, 29, 29, -1, 28, -1
};
/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
static const yytype_uint16 yyrline[] =
{
0, 38, 38, 38, 40, 40, 43, 45, 47, 49,
51, 53, 55, 57, 59, 61, 63, 65, 67, 70,
73, 79, 81, 81, 84, 86, 89, 89, 92, 92,
95, 100, 100, 102, 102, 105, 107, 107, 110, 112,
115, 118, 118, 124, 126, 126, 129, 131, 134, 134,
137, 137, 140, 140, 143, 143, 146, 148, 153, 155,
155, 158, 160, 160, 163, 165, 168, 170, 173, 173,
176, 176, 179, 179, 182, 182, 185, 187, 192, 192,
194, 194, 197, 199, 199, 202, 204, 206, 208, 213,
213, 216, 216, 220, 222, 222, 225, 227, 232, 232,
235, 235, 239, 241, 241, 244, 246, 248, 250, 255,
257, 257, 260, 262, 265, 265, 268, 268, 271, 271,
274, 276, 281, 281, 283, 283, 287, 289, 289, 292,
294, 296, 298, 303, 303, 305, 305, 309, 311, 311,
314, 316, 321, 321, 323, 323, 327, 329, 329, 332,
334, 336, 338, 343, 343, 346, 346, 350, 352, 352,
356, 358, 360, 364, 366, 368, 371, 374, 377, 380
};
#endif
#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
First, the terminals, then, starting at YYNTOKENS, nonterminals. */
static const char *const yytname[] =
{
"$end", "error", "$undefined", "POINT", "LINESTRING", "POLYGON",
"MULTIPOINT", "MULTILINESTRING", "MULTIPOLYGON", "GEOMETRYCOLLECTION",
"CIRCULARSTRING", "COMPOUNDCURVE", "CURVEPOLYGON", "MULTICURVE",
"MULTISURFACE", "POINTM", "LINESTRINGM", "POLYGONM", "MULTIPOINTM",
"MULTILINESTRINGM", "MULTIPOLYGONM", "GEOMETRYCOLLECTIONM",
"CIRCULARSTRINGM", "COMPOUNDCURVEM", "CURVEPOLYGONM", "MULTICURVEM",
"MULTISURFACEM", "SRID", "EMPTY", "VALUE", "LPAREN", "RPAREN", "COMMA",
"EQUALS", "SEMICOLON", "WKB", "$accept", "geometry", "@1", "@2",
"geometry_int", "srid", "geom_wkb", "geom_point", "@3", "point",
"empty_point", "@4", "nonempty_point", "@5", "point_int",
"geom_multipoint", "@6", "@7", "multipoint", "@8", "multipoint_int",
"mpoint_element", "@9", "geom_linestring", "@10", "linestring",
"empty_linestring", "@11", "nonempty_linestring", "@12",
"nonempty_linestring_closed", "@13", "linestring_1", "@14",
"linestring_int", "geom_circularstring", "@15",
"geom_circularstring_closed", "@16", "circularstring",
"circularstring_closed", "empty_circularstring", "@17",
"nonempty_circularstring", "@18", "nonempty_circularstring_closed",
"@19", "circularstring_1", "@20", "circularstring_int",
"geom_compoundcurve", "@21", "@22", "compoundcurve", "@23",
"compoundcurve_int", "geom_multilinestring", "@24", "@25",
"multilinestring", "@26", "multilinestring_int", "geom_multicurve",
"@27", "@28", "multicurve", "@29", "multicurve_int", "geom_polygon",
"@30", "polygon", "empty_polygon", "@31", "nonempty_polygon", "@32",
"polygon_1", "@33", "polygon_int", "geom_curvepolygon", "@34", "@35",
"curvepolygon", "@36", "curvepolygon_int", "geom_multipolygon", "@37",
"@38", "multipolygon", "@39", "multipolygon_int", "geom_multisurface",
"@40", "@41", "multisurface", "@42", "multisurface_int",
"geom_geometrycollection", "@43", "@44", "geometrycollection", "@45",
"geometrycollection_int", "a_point", "point_2d", "point_3d", "point_4d",
"empty", 0
};
#endif
# ifdef YYPRINT
/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
token YYLEX-NUM. */
static const yytype_uint16 yytoknum[] =
{
0, 256, 257, 258, 259, 260, 261, 262, 263, 264,
265, 266, 267, 268, 269, 270, 271, 272, 273, 274,
275, 276, 277, 278, 279, 280, 281, 282, 283, 284,
285, 286, 287, 288, 289, 290
};
# endif
/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
static const yytype_uint8 yyr1[] =
{
0, 36, 38, 37, 39, 37, 40, 40, 40, 40,
40, 40, 40, 40, 40, 40, 40, 40, 40, 41,
42, 43, 44, 43, 45, 45, 47, 46, 49, 48,
50, 52, 51, 53, 51, 54, 55, 54, 56, 56,
57, 58, 57, 59, 60, 59, 61, 61, 63, 62,
65, 64, 67, 66, 69, 68, 70, 70, 71, 72,
71, 73, 74, 73, 75, 75, 76, 76, 78, 77,
80, 79, 82, 81, 84, 83, 85, 85, 87, 86,
88, 86, 89, 90, 89, 91, 91, 91, 91, 93,
92, 94, 92, 95, 96, 95, 97, 97, 99, 98,
100, 98, 101, 102, 101, 103, 103, 103, 103, 104,
105, 104, 106, 106, 108, 107, 110, 109, 112, 111,
113, 113, 115, 114, 116, 114, 117, 118, 117, 119,
119, 119, 119, 121, 120, 122, 120, 123, 124, 123,
125, 125, 127, 126, 128, 126, 129, 130, 129, 131,
131, 131, 131, 133, 132, 134, 132, 135, 136, 135,
137, 137, 137, 138, 138, 138, 139, 140, 141, 142
};
/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
static const yytype_uint8 yyr2[] =
{
0, 2, 0, 4, 0, 2, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 3,
1, 2, 0, 3, 1, 1, 0, 2, 0, 2,
3, 0, 3, 0, 3, 1, 0, 4, 1, 3,
1, 0, 2, 2, 0, 3, 1, 1, 0, 2,
0, 2, 0, 2, 0, 4, 1, 3, 2, 0,
3, 2, 0, 3, 1, 1, 1, 1, 0, 2,
0, 2, 0, 2, 0, 4, 1, 3, 0, 3,
0, 3, 1, 0, 4, 1, 1, 3, 3, 0,
3, 0, 3, 1, 0, 4, 1, 3, 0, 3,
0, 3, 1, 0, 4, 1, 1, 3, 3, 2,
0, 3, 1, 1, 0, 2, 0, 2, 0, 4,
1, 3, 0, 3, 0, 3, 1, 0, 4, 1,
1, 3, 3, 0, 3, 0, 3, 1, 0, 4,
1, 3, 0, 3, 0, 3, 1, 0, 4, 1,
1, 3, 3, 0, 3, 0, 3, 1, 0, 4,
1, 1, 3, 1, 1, 1, 2, 3, 4, 1
};
/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
STATE-NUM when YYTABLE doesn't specify something else to do. Zero
means the default is an error. */
static const yytype_uint8 yydefact[] =
{
4, 0, 0, 0, 0, 0, 1, 26, 48, 114,
31, 89, 133, 153, 68, 78, 122, 98, 142, 22,
44, 110, 33, 91, 135, 155, 59, 80, 124, 100,
144, 20, 5, 6, 7, 13, 8, 9, 11, 14,
15, 10, 12, 16, 17, 18, 2, 19, 21, 24,
0, 25, 0, 43, 46, 0, 47, 54, 109, 112,
0, 113, 118, 36, 94, 138, 158, 58, 64, 0,
65, 74, 83, 127, 103, 147, 26, 48, 114, 36,
94, 138, 158, 68, 83, 127, 103, 147, 0, 169,
27, 0, 29, 49, 51, 0, 115, 117, 0, 32,
0, 35, 90, 0, 93, 134, 0, 137, 154, 0,
157, 69, 71, 0, 79, 0, 82, 123, 0, 126,
99, 0, 102, 143, 0, 146, 23, 45, 111, 34,
92, 136, 156, 60, 81, 125, 101, 145, 3, 0,
0, 163, 164, 165, 0, 54, 28, 50, 116, 0,
0, 50, 52, 50, 116, 166, 30, 0, 56, 120,
0, 40, 0, 38, 0, 96, 0, 140, 0, 161,
0, 160, 0, 76, 85, 86, 0, 68, 62, 129,
54, 130, 0, 105, 106, 0, 149, 150, 0, 167,
55, 0, 119, 54, 37, 28, 42, 95, 50, 139,
116, 159, 0, 75, 0, 84, 50, 61, 66, 67,
74, 68, 53, 128, 52, 104, 50, 148, 116, 168,
57, 121, 39, 97, 141, 162, 77, 87, 88, 73,
63, 131, 132, 107, 108, 151, 152
};
/* YYDEFGOTO[NTERM-NUM]. */
static const yytype_int16 yydefgoto[] =
{
-1, 2, 88, 3, 32, 4, 33, 34, 76, 48,
49, 50, 51, 52, 92, 35, 63, 79, 99, 100,
162, 163, 164, 36, 77, 53, 54, 55, 56, 57,
179, 180, 94, 95, 157, 37, 83, 181, 211, 67,
207, 68, 69, 70, 71, 209, 210, 112, 113, 172,
38, 72, 84, 114, 115, 176, 39, 64, 80, 102,
103, 166, 40, 74, 86, 120, 121, 185, 41, 78,
58, 59, 60, 61, 62, 97, 98, 160, 42, 73,
85, 117, 118, 182, 43, 65, 81, 105, 106, 168,
44, 75, 87, 123, 124, 188, 45, 66, 82, 108,
109, 170, 140, 141, 142, 143, 101
};
/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
STATE-NUM. */
#define YYPACT_NINF -166
static const yytype_int16 yypact[] =
{
-17, -14, 21, 145, -7, 5, -166, 29, 30, 40,
-166, -166, -166, -166, 51, -166, -166, -166, -166, -166,
-166, -166, -166, -166, -166, -166, -166, -166, -166, -166,
-166, -166, -166, -166, -166, -166, -166, -166, -166, -166,
-166, -166, -166, -166, -166, -166, -166, -166, -166, -166,
23, -166, 53, -166, -166, 23, -166, -166, -166, -166,
23, -166, -166, 23, 23, 23, 23, -166, -166, 23,
-166, -166, 23, 23, 23, 23, 29, 30, 40, 23,
23, 23, 23, 51, 23, 23, 23, 23, 145, -166,
-166, 55, -166, -166, -166, 56, -166, -166, 57, -166,
58, -166, -166, 59, -166, -166, 61, -166, -166, 62,
-166, -166, -166, 63, -166, 64, -166, -166, 65, -166,
-166, 66, -166, -166, 67, -166, -166, -166, -166, -166,
-166, -166, -166, -166, -166, -166, -166, -166, -166, 70,
54, -166, -166, -166, 55, -166, 72, -166, -166, 112,
55, 8, 19, 8, 28, 73, -166, -18, -166, -166,
16, -166, 18, -166, 55, -166, 24, -166, 31, -166,
33, -166, 35, -166, -166, -166, 42, 74, -166, -166,
-166, -166, 44, -166, -166, 46, -166, -166, 48, 76,
-166, 55, -166, -166, -166, 72, -166, -166, -166, -166,
-166, -166, 145, -166, 55, -166, 8, -166, -166, -166,
-166, 74, -166, -166, 19, -166, 8, -166, 28, -166,
-166, -166, -166, -166, -166, -166, -166, -166, -166, -166,
-166, -166, -166, -166, -166, -166, -166
};
/* YYPGOTO[NTERM-NUM]. */
static const yytype_int16 yypgoto[] =
{
-166, -166, -166, -166, -88, -166, -166, -166, -166, 27,
-166, -166, -142, -166, -166, -166, -166, -166, 32, -166,
-166, -89, -166, -166, -166, 36, -166, -166, -108, -166,
-107, -166, -136, -166, -166, -148, -166, -105, -166, 60,
-101, -165, -166, -166, -166, -166, -166, -98, -166, -166,
-166, -166, -166, 88, -166, -166, -166, -166, -166, 93,
-166, -166, -166, -166, -166, 89, -166, -166, -166, -166,
68, -166, -166, -146, -166, -166, -166, -166, -147, -166,
-166, 91, -166, -166, -166, -166, -166, 96, -166, -166,
-166, -166, -166, 52, -166, -166, -166, -166, -166, 92,
-166, -166, -122, -166, -166, -166, -49
};
/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
positive, shift that token. If negative, reduce the rule which
number is the opposite. If zero, do what YYDEFACT says.
If YYTABLE_NINF, syntax error. */
#define YYTABLE_NINF -117
static const yytype_int16 yytable[] =
{
138, 90, 167, 175, 161, 184, 93, 187, 186, 159,
1, 96, 208, 190, 191, 104, 107, 110, 14, 5,
111, 6, 158, 116, 119, 122, 125, 46, 173, 177,
26, 104, 107, 110, 47, 116, 119, 122, 125, 165,
16, 178, 196, 174, 212, 183, 208, 192, 193, 194,
195, 89, 28, 161, 224, 197, 198, 221, 228, -28,
-50, 169, 199, 200, 201, 202, 203, 204, 234, 220,
-116, 236, 235, 205, 206, 213, 214, 215, 216, 217,
218, -70, 226, 91, 139, 156, 144, 145, 146, 147,
223, 148, 149, 150, 151, 152, 153, 154, 227, 155,
171, -41, 189, 126, -72, 219, 222, 231, 233, 232,
230, 129, 229, 127, 225, 7, 8, 9, 10, 11,
12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 26, 27, 28, 29, 30, 137,
89, 0, 0, 133, 0, 0, 128, 31, 7, 8,
9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
19, 20, 21, 22, 23, 24, 25, 26, 27, 28,
29, 30, 134, 130, 132, 136, 135, 131, 0, 0,
31
};
static const yytype_int16 yycheck[] =
{
88, 50, 148, 151, 146, 153, 55, 154, 154, 145,
27, 60, 177, 31, 32, 64, 65, 66, 10, 33,
69, 0, 144, 72, 73, 74, 75, 34, 150, 10,
22, 80, 81, 82, 29, 84, 85, 86, 87, 147,
12, 22, 164, 151, 180, 153, 211, 31, 32, 31,
32, 28, 24, 195, 200, 31, 32, 193, 206, 30,
30, 149, 31, 32, 31, 32, 31, 32, 216, 191,
30, 218, 218, 31, 32, 31, 32, 31, 32, 31,
32, 30, 204, 30, 29, 31, 30, 30, 30, 30,
198, 30, 30, 30, 30, 30, 30, 30, 206, 29,
149, 29, 29, 76, 30, 29, 195, 214, 216, 214,
211, 79, 210, 77, 202, 3, 4, 5, 6, 7,
8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
18, 19, 20, 21, 22, 23, 24, 25, 26, 87,
28, -1, -1, 83, -1, -1, 78, 35, 3, 4,
5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 84, 80, 82, 86, 85, 81, -1, -1,
35
};
/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
symbol of state STATE-NUM. */
static const yytype_uint8 yystos[] =
{
0, 27, 37, 39, 41, 33, 0, 3, 4, 5,
6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
26, 35, 40, 42, 43, 51, 59, 71, 86, 92,
98, 104, 114, 120, 126, 132, 34, 29, 45, 46,
47, 48, 49, 61, 62, 63, 64, 65, 106, 107,
108, 109, 110, 52, 93, 121, 133, 75, 77, 78,
79, 80, 87, 115, 99, 127, 44, 60, 105, 53,
94, 122, 134, 72, 88, 116, 100, 128, 38, 28,
142, 30, 50, 142, 68, 69, 142, 111, 112, 54,
55, 142, 95, 96, 142, 123, 124, 142, 135, 136,
142, 142, 83, 84, 89, 90, 142, 117, 118, 142,
101, 102, 142, 129, 130, 142, 45, 61, 106, 54,
95, 123, 135, 75, 89, 117, 101, 129, 40, 29,
138, 139, 140, 141, 30, 30, 30, 30, 30, 30,
30, 30, 30, 30, 30, 29, 31, 70, 138, 68,
113, 48, 56, 57, 58, 64, 97, 109, 125, 40,
137, 142, 85, 138, 64, 71, 91, 10, 22, 66,
67, 73, 119, 64, 71, 103, 109, 114, 131, 29,
31, 32, 31, 32, 31, 32, 138, 31, 32, 31,
32, 31, 32, 31, 32, 31, 32, 76, 77, 81,
82, 74, 68, 31, 32, 31, 32, 31, 32, 29,
138, 68, 57, 64, 109, 40, 138, 64, 71, 83,
76, 66, 73, 64, 71, 109, 114
};
#define yyerrok (yyerrstatus = 0)
#define yyclearin (yychar = YYEMPTY)
#define YYEMPTY (-2)
#define YYEOF 0
#define YYACCEPT goto yyacceptlab
#define YYABORT goto yyabortlab
#define YYERROR goto yyerrorlab
/* Like YYERROR except do call yyerror. This remains here temporarily
to ease the transition to the new meaning of YYERROR, for GCC.
Once GCC version 2 has supplanted version 1, this can go. */
#define YYFAIL goto yyerrlab
#define YYRECOVERING() (!!yyerrstatus)
#define YYBACKUP(Token, Value) \
do \
if (yychar == YYEMPTY && yylen == 1) \
{ \
yychar = (Token); \
yylval = (Value); \
yytoken = YYTRANSLATE (yychar); \
YYPOPSTACK (1); \
goto yybackup; \
} \
else \
{ \
yyerror (YY_("syntax error: cannot back up")); \
YYERROR; \
} \
while (YYID (0))
#define YYTERROR 1
#define YYERRCODE 256
/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
If N is 0, then set CURRENT to the empty location which ends
the previous symbol: RHS[0] (always defined). */
#define YYRHSLOC(Rhs, K) ((Rhs)[K])
#ifndef YYLLOC_DEFAULT
# define YYLLOC_DEFAULT(Current, Rhs, N) \
do \
if (YYID (N)) \
{ \
(Current).first_line = YYRHSLOC (Rhs, 1).first_line; \
(Current).first_column = YYRHSLOC (Rhs, 1).first_column; \
(Current).last_line = YYRHSLOC (Rhs, N).last_line; \
(Current).last_column = YYRHSLOC (Rhs, N).last_column; \
} \
else \
{ \
(Current).first_line = (Current).last_line = \
YYRHSLOC (Rhs, 0).last_line; \
(Current).first_column = (Current).last_column = \
YYRHSLOC (Rhs, 0).last_column; \
} \
while (YYID (0))
#endif
/* YY_LOCATION_PRINT -- Print the location on the stream.
This macro was not mandated originally: define only if we know
we won't break user code: when these are the locations we know. */
#ifndef YY_LOCATION_PRINT
# if YYLTYPE_IS_TRIVIAL
# define YY_LOCATION_PRINT(File, Loc) \
fprintf (File, "%d.%d-%d.%d", \
(Loc).first_line, (Loc).first_column, \
(Loc).last_line, (Loc).last_column)
# else
# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
# endif
#endif
/* YYLEX -- calling `yylex' with the right arguments. */
#ifdef YYLEX_PARAM
# define YYLEX yylex (YYLEX_PARAM)
#else
# define YYLEX yylex ()
#endif
/* Enable debugging if requested. */
#if YYDEBUG
# ifndef YYFPRINTF
# include /* INFRINGES ON USER NAME SPACE */
# define YYFPRINTF fprintf
# endif
# define YYDPRINTF(Args) \
do { \
if (yydebug) \
YYFPRINTF Args; \
} while (YYID (0))
# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \
do { \
if (yydebug) \
{ \
YYFPRINTF (stderr, "%s ", Title); \
yy_symbol_print (stderr, \
Type, Value, Location); \
YYFPRINTF (stderr, "\n"); \
} \
} while (YYID (0))
/*--------------------------------.
| Print this symbol on YYOUTPUT. |
`--------------------------------*/
/*ARGSUSED*/
#if (defined __STDC__ || defined __C99__FUNC__ \
|| defined __cplusplus || defined _MSC_VER)
static void
yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp)
#else
static void
yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp)
FILE *yyoutput;
int yytype;
YYSTYPE const * const yyvaluep;
YYLTYPE const * const yylocationp;
#endif
{
if (!yyvaluep)
return;
YYUSE (yylocationp);
# ifdef YYPRINT
if (yytype < YYNTOKENS)
YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
# else
YYUSE (yyoutput);
# endif
switch (yytype)
{
default:
break;
}
}
/*--------------------------------.
| Print this symbol on YYOUTPUT. |
`--------------------------------*/
#if (defined __STDC__ || defined __C99__FUNC__ \
|| defined __cplusplus || defined _MSC_VER)
static void
yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp)
#else
static void
yy_symbol_print (yyoutput, yytype, yyvaluep, yylocationp)
FILE *yyoutput;
int yytype;
YYSTYPE const * const yyvaluep;
YYLTYPE const * const yylocationp;
#endif
{
if (yytype < YYNTOKENS)
YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
else
YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
YY_LOCATION_PRINT (yyoutput, *yylocationp);
YYFPRINTF (yyoutput, ": ");
yy_symbol_value_print (yyoutput, yytype, yyvaluep, yylocationp);
YYFPRINTF (yyoutput, ")");
}
/*------------------------------------------------------------------.
| yy_stack_print -- Print the state stack from its BOTTOM up to its |
| TOP (included). |
`------------------------------------------------------------------*/
#if (defined __STDC__ || defined __C99__FUNC__ \
|| defined __cplusplus || defined _MSC_VER)
static void
yy_stack_print (yytype_int16 *bottom, yytype_int16 *top)
#else
static void
yy_stack_print (bottom, top)
yytype_int16 *bottom;
yytype_int16 *top;
#endif
{
YYFPRINTF (stderr, "Stack now");
for (; bottom <= top; ++bottom)
YYFPRINTF (stderr, " %d", *bottom);
YYFPRINTF (stderr, "\n");
}
# define YY_STACK_PRINT(Bottom, Top) \
do { \
if (yydebug) \
yy_stack_print ((Bottom), (Top)); \
} while (YYID (0))
/*------------------------------------------------.
| Report that the YYRULE is going to be reduced. |
`------------------------------------------------*/
#if (defined __STDC__ || defined __C99__FUNC__ \
|| defined __cplusplus || defined _MSC_VER)
static void
yy_reduce_print (YYSTYPE *yyvsp, YYLTYPE *yylsp, int yyrule)
#else
static void
yy_reduce_print (yyvsp, yylsp, yyrule)
YYSTYPE *yyvsp;
YYLTYPE *yylsp;
int yyrule;
#endif
{
int yynrhs = yyr2[yyrule];
int yyi;
unsigned long int yylno = yyrline[yyrule];
YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
yyrule - 1, yylno);
/* The symbols being reduced. */
for (yyi = 0; yyi < yynrhs; yyi++)
{
fprintf (stderr, " $%d = ", yyi + 1);
yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
&(yyvsp[(yyi + 1) - (yynrhs)])
, &(yylsp[(yyi + 1) - (yynrhs)]) );
fprintf (stderr, "\n");
}
}
# define YY_REDUCE_PRINT(Rule) \
do { \
if (yydebug) \
yy_reduce_print (yyvsp, yylsp, Rule); \
} while (YYID (0))
/* Nonzero means print parse trace. It is left uninitialized so that
multiple parsers can coexist. */
int yydebug;
#else /* !YYDEBUG */
# define YYDPRINTF(Args)
# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
# define YY_STACK_PRINT(Bottom, Top)
# define YY_REDUCE_PRINT(Rule)
#endif /* !YYDEBUG */
/* YYINITDEPTH -- initial size of the parser's stacks. */
#ifndef YYINITDEPTH
# define YYINITDEPTH 200
#endif
/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
if the built-in stack extension method is used).
Do not make this value too large; the results are undefined if
YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
evaluated with infinite-precision integer arithmetic. */
#ifndef YYMAXDEPTH
# define YYMAXDEPTH 10000
#endif
#if YYERROR_VERBOSE
# ifndef yystrlen
# if defined __GLIBC__ && defined _STRING_H
# define yystrlen strlen
# else
/* Return the length of YYSTR. */
#if (defined __STDC__ || defined __C99__FUNC__ \
|| defined __cplusplus || defined _MSC_VER)
static YYSIZE_T
yystrlen (const char *yystr)
#else
static YYSIZE_T
yystrlen (yystr)
const char *yystr;
#endif
{
YYSIZE_T yylen;
for (yylen = 0; yystr[yylen]; yylen++)
continue;
return yylen;
}
# endif
# endif
# ifndef yystpcpy
# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
# define yystpcpy stpcpy
# else
/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
YYDEST. */
#if (defined __STDC__ || defined __C99__FUNC__ \
|| defined __cplusplus || defined _MSC_VER)
static char *
yystpcpy (char *yydest, const char *yysrc)
#else
static char *
yystpcpy (yydest, yysrc)
char *yydest;
const char *yysrc;
#endif
{
char *yyd = yydest;
const char *yys = yysrc;
while ((*yyd++ = *yys++) != '\0')
continue;
return yyd - 1;
}
# endif
# endif
# ifndef yytnamerr
/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
quotes and backslashes, so that it's suitable for yyerror. The
heuristic is that double-quoting is unnecessary unless the string
contains an apostrophe, a comma, or backslash (other than
backslash-backslash). YYSTR is taken from yytname. If YYRES is
null, do not copy; instead, return the length of what the result
would have been. */
static YYSIZE_T
yytnamerr (char *yyres, const char *yystr)
{
if (*yystr == '"')
{
YYSIZE_T yyn = 0;
char const *yyp = yystr;
for (;;)
switch (*++yyp)
{
case '\'':
case ',':
goto do_not_strip_quotes;
case '\\':
if (*++yyp != '\\')
goto do_not_strip_quotes;
/* Fall through. */
default:
if (yyres)
yyres[yyn] = *yyp;
yyn++;
break;
case '"':
if (yyres)
yyres[yyn] = '\0';
return yyn;
}
do_not_strip_quotes: ;
}
if (! yyres)
return yystrlen (yystr);
return yystpcpy (yyres, yystr) - yyres;
}
# endif
/* Copy into YYRESULT an error message about the unexpected token
YYCHAR while in state YYSTATE. Return the number of bytes copied,
including the terminating null byte. If YYRESULT is null, do not
copy anything; just return the number of bytes that would be
copied. As a special case, return 0 if an ordinary "syntax error"
message will do. Return YYSIZE_MAXIMUM if overflow occurs during
size calculation. */
static YYSIZE_T
yysyntax_error (char *yyresult, int yystate, int yychar)
{
int yyn = yypact[yystate];
if (! (YYPACT_NINF < yyn && yyn <= YYLAST))
return 0;
else
{
int yytype = YYTRANSLATE (yychar);
YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]);
YYSIZE_T yysize = yysize0;
YYSIZE_T yysize1;
int yysize_overflow = 0;
enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
int yyx;
# if 0
/* This is so xgettext sees the translatable formats that are
constructed on the fly. */
YY_("syntax error, unexpected %s");
YY_("syntax error, unexpected %s, expecting %s");
YY_("syntax error, unexpected %s, expecting %s or %s");
YY_("syntax error, unexpected %s, expecting %s or %s or %s");
YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s");
# endif
char *yyfmt;
char const *yyf;
static char const yyunexpected[] = "syntax error, unexpected %s";
static char const yyexpecting[] = ", expecting %s";
static char const yyor[] = " or %s";
char yyformat[sizeof yyunexpected
+ sizeof yyexpecting - 1
+ ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2)
* (sizeof yyor - 1))];
char const *yyprefix = yyexpecting;
/* Start YYX at -YYN if negative to avoid negative indexes in
YYCHECK. */
int yyxbegin = yyn < 0 ? -yyn : 0;
/* Stay within bounds of both yycheck and yytname. */
int yychecklim = YYLAST - yyn + 1;
int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
int yycount = 1;
yyarg[0] = yytname[yytype];
yyfmt = yystpcpy (yyformat, yyunexpected);
for (yyx = yyxbegin; yyx < yyxend; ++yyx)
if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
{
if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
{
yycount = 1;
yysize = yysize0;
yyformat[sizeof yyunexpected - 1] = '\0';
break;
}
yyarg[yycount++] = yytname[yyx];
yysize1 = yysize + yytnamerr (0, yytname[yyx]);
yysize_overflow |= (yysize1 < yysize);
yysize = yysize1;
yyfmt = yystpcpy (yyfmt, yyprefix);
yyprefix = yyor;
}
yyf = YY_(yyformat);
yysize1 = yysize + yystrlen (yyf);
yysize_overflow |= (yysize1 < yysize);
yysize = yysize1;
if (yysize_overflow)
return YYSIZE_MAXIMUM;
if (yyresult)
{
/* Avoid sprintf, as that infringes on the user's name space.
Don't have undefined behavior even if the translation
produced a string with the wrong number of "%s"s. */
char *yyp = yyresult;
int yyi = 0;
while ((*yyp = *yyf) != '\0')
{
if (*yyp == '%' && yyf[1] == 's' && yyi < yycount)
{
yyp += yytnamerr (yyp, yyarg[yyi++]);
yyf += 2;
}
else
{
yyp++;
yyf++;
}
}
}
return yysize;
}
}
#endif /* YYERROR_VERBOSE */
/*-----------------------------------------------.
| Release the memory associated to this symbol. |
`-----------------------------------------------*/
/*ARGSUSED*/
#if (defined __STDC__ || defined __C99__FUNC__ \
|| defined __cplusplus || defined _MSC_VER)
static void
yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp)
#else
static void
yydestruct (yymsg, yytype, yyvaluep, yylocationp)
const char *yymsg;
int yytype;
YYSTYPE *yyvaluep;
YYLTYPE *yylocationp;
#endif
{
YYUSE (yyvaluep);
YYUSE (yylocationp);
if (!yymsg)
yymsg = "Deleting";
YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
switch (yytype)
{
default:
break;
}
}
/* Prevent warnings from -Wmissing-prototypes. */
#ifdef YYPARSE_PARAM
#if defined __STDC__ || defined __cplusplus
int yyparse (void *YYPARSE_PARAM);
#else
int yyparse ();
#endif
#else /* ! YYPARSE_PARAM */
#if defined __STDC__ || defined __cplusplus
int yyparse (void);
#else
int yyparse ();
#endif
#endif /* ! YYPARSE_PARAM */
/* The look-ahead symbol. */
int yychar;
/* The semantic value of the look-ahead symbol. */
YYSTYPE yylval;
/* Number of syntax errors so far. */
int yynerrs;
/* Location data for the look-ahead symbol. */
YYLTYPE yylloc;
/*----------.
| yyparse. |
`----------*/
#ifdef YYPARSE_PARAM
#if (defined __STDC__ || defined __C99__FUNC__ \
|| defined __cplusplus || defined _MSC_VER)
int
yyparse (void *YYPARSE_PARAM)
#else
int
yyparse (YYPARSE_PARAM)
void *YYPARSE_PARAM;
#endif
#else /* ! YYPARSE_PARAM */
#if (defined __STDC__ || defined __C99__FUNC__ \
|| defined __cplusplus || defined _MSC_VER)
int
yyparse (void)
#else
int
yyparse ()
#endif
#endif
{
int yystate;
int yyn;
int yyresult;
/* Number of tokens to shift before error messages enabled. */
int yyerrstatus;
/* Look-ahead token as an internal (translated) token number. */
int yytoken = 0;
#if YYERROR_VERBOSE
/* Buffer for error messages, and its allocated size. */
char yymsgbuf[128];
char *yymsg = yymsgbuf;
YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
#endif
/* Three stacks and their tools:
`yyss': related to states,
`yyvs': related to semantic values,
`yyls': related to locations.
Refer to the stacks thru separate pointers, to allow yyoverflow
to reallocate them elsewhere. */
/* The state stack. */
yytype_int16 yyssa[YYINITDEPTH];
yytype_int16 *yyss = yyssa;
yytype_int16 *yyssp;
/* The semantic value stack. */
YYSTYPE yyvsa[YYINITDEPTH];
YYSTYPE *yyvs = yyvsa;
YYSTYPE *yyvsp;
/* The location stack. */
YYLTYPE yylsa[YYINITDEPTH];
YYLTYPE *yyls = yylsa;
YYLTYPE *yylsp;
/* The locations where the error started and ended. */
YYLTYPE yyerror_range[2];
#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N), yylsp -= (N))
YYSIZE_T yystacksize = YYINITDEPTH;
/* The variables used to return semantic value and location from the
action routines. */
YYSTYPE yyval;
YYLTYPE yyloc;
/* The number of symbols on the RHS of the reduced rule.
Keep to zero when no symbol should be popped. */
int yylen = 0;
YYDPRINTF ((stderr, "Starting parse\n"));
yystate = 0;
yyerrstatus = 0;
yynerrs = 0;
yychar = YYEMPTY; /* Cause a token to be read. */
/* Initialize stack pointers.
Waste one element of value and location stack
so that they stay on the same level as the state stack.
The wasted elements are never initialized. */
yyssp = yyss;
yyvsp = yyvs;
yylsp = yyls;
#if YYLTYPE_IS_TRIVIAL
/* Initialize the default location before parsing starts. */
yylloc.first_line = yylloc.last_line = 1;
yylloc.first_column = yylloc.last_column = 0;
#endif
goto yysetstate;
/*------------------------------------------------------------.
| yynewstate -- Push a new state, which is found in yystate. |
`------------------------------------------------------------*/
yynewstate:
/* In all cases, when you get here, the value and location stacks
have just been pushed. So pushing a state here evens the stacks. */
yyssp++;
yysetstate:
*yyssp = yystate;
if (yyss + yystacksize - 1 <= yyssp)
{
/* Get the current used size of the three stacks, in elements. */
YYSIZE_T yysize = yyssp - yyss + 1;
#ifdef yyoverflow
{
/* Give user a chance to reallocate the stack. Use copies of
these so that the &'s don't force the real ones into
memory. */
YYSTYPE *yyvs1 = yyvs;
yytype_int16 *yyss1 = yyss;
YYLTYPE *yyls1 = yyls;
/* Each stack pointer address is followed by the size of the
data in use in that stack, in bytes. This used to be a
conditional around just the two extra args, but that might
be undefined if yyoverflow is a macro. */
yyoverflow (YY_("memory exhausted"),
&yyss1, yysize * sizeof (*yyssp),
&yyvs1, yysize * sizeof (*yyvsp),
&yyls1, yysize * sizeof (*yylsp),
&yystacksize);
yyls = yyls1;
yyss = yyss1;
yyvs = yyvs1;
}
#else /* no yyoverflow */
# ifndef YYSTACK_RELOCATE
goto yyexhaustedlab;
# else
/* Extend the stack our own way. */
if (YYMAXDEPTH <= yystacksize)
goto yyexhaustedlab;
yystacksize *= 2;
if (YYMAXDEPTH < yystacksize)
yystacksize = YYMAXDEPTH;
{
yytype_int16 *yyss1 = yyss;
union yyalloc *yyptr =
(union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
if (! yyptr)
goto yyexhaustedlab;
YYSTACK_RELOCATE (yyss);
YYSTACK_RELOCATE (yyvs);
YYSTACK_RELOCATE (yyls);
# undef YYSTACK_RELOCATE
if (yyss1 != yyssa)
YYSTACK_FREE (yyss1);
}
# endif
#endif /* no yyoverflow */
yyssp = yyss + yysize - 1;
yyvsp = yyvs + yysize - 1;
yylsp = yyls + yysize - 1;
YYDPRINTF ((stderr, "Stack size increased to %lu\n",
(unsigned long int) yystacksize));
if (yyss + yystacksize - 1 <= yyssp)
YYABORT;
}
YYDPRINTF ((stderr, "Entering state %d\n", yystate));
goto yybackup;
/*-----------.
| yybackup. |
`-----------*/
yybackup:
/* Do appropriate processing given the current state. Read a
look-ahead token if we need one and don't already have one. */
/* First try to decide what to do without reference to look-ahead token. */
yyn = yypact[yystate];
if (yyn == YYPACT_NINF)
goto yydefault;
/* Not known => get a look-ahead token if don't already have one. */
/* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */
if (yychar == YYEMPTY)
{
YYDPRINTF ((stderr, "Reading a token: "));
yychar = YYLEX;
}
if (yychar <= YYEOF)
{
yychar = yytoken = YYEOF;
YYDPRINTF ((stderr, "Now at end of input.\n"));
}
else
{
yytoken = YYTRANSLATE (yychar);
YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
}
/* If the proper action on seeing token YYTOKEN is to reduce or to
detect an error, take that action. */
yyn += yytoken;
if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
goto yydefault;
yyn = yytable[yyn];
if (yyn <= 0)
{
if (yyn == 0 || yyn == YYTABLE_NINF)
goto yyerrlab;
yyn = -yyn;
goto yyreduce;
}
if (yyn == YYFINAL)
YYACCEPT;
/* Count tokens shifted since error; after three, turn off error
status. */
if (yyerrstatus)
yyerrstatus--;
/* Shift the look-ahead token. */
YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
/* Discard the shifted token unless it is eof. */
if (yychar != YYEOF)
yychar = YYEMPTY;
yystate = yyn;
*++yyvsp = yylval;
*++yylsp = yylloc;
goto yynewstate;
/*-----------------------------------------------------------.
| yydefault -- do the default action for the current state. |
`-----------------------------------------------------------*/
yydefault:
yyn = yydefact[yystate];
if (yyn == 0)
goto yyerrlab;
goto yyreduce;
/*-----------------------------.
| yyreduce -- Do a reduction. |
`-----------------------------*/
yyreduce:
/* yyn is the number of a rule to reduce with. */
yylen = yyr2[yyn];
/* If YYLEN is nonzero, implement the default value of the action:
`$$ = $1'.
Otherwise, the following line sets YYVAL to garbage.
This behavior is undocumented and Bison
users should not rely upon it. Assigning to YYVAL
unconditionally makes the parser a bit smaller, and it avoids a
GCC warning that YYVAL may be used uninitialized. */
yyval = yyvsp[1-yylen];
/* Default location. */
YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen);
YY_REDUCE_PRINT (yyn);
switch (yyn)
{
case 2:
#line 38 "wktparse.y"
{ alloc_lwgeom(srid); }
break;
case 4:
#line 40 "wktparse.y"
{ alloc_lwgeom(-1); }
break;
case 19:
#line 70 "wktparse.y"
{ set_srid((yyvsp[(3) - (3)].value)); }
break;
case 20:
#line 73 "wktparse.y"
{ alloc_wkb((yyvsp[(1) - (1)].wkb)); }
break;
case 22:
#line 81 "wktparse.y"
{ set_zm(0, 1); }
break;
case 26:
#line 89 "wktparse.y"
{ alloc_point(); }
break;
case 27:
#line 89 "wktparse.y"
{ pop(); }
break;
case 28:
#line 92 "wktparse.y"
{ alloc_point(); }
break;
case 29:
#line 92 "wktparse.y"
{ pop(); }
break;
case 31:
#line 100 "wktparse.y"
{ alloc_multipoint(); }
break;
case 32:
#line 100 "wktparse.y"
{ pop(); }
break;
case 33:
#line 102 "wktparse.y"
{ set_zm(0, 1); alloc_multipoint(); }
break;
case 34:
#line 102 "wktparse.y"
{pop(); }
break;
case 36:
#line 107 "wktparse.y"
{ alloc_counter(); }
break;
case 37:
#line 107 "wktparse.y"
{ pop(); }
break;
case 41:
#line 118 "wktparse.y"
{ alloc_point(); }
break;
case 42:
#line 118 "wktparse.y"
{ pop(); }
break;
case 44:
#line 126 "wktparse.y"
{ set_zm(0, 1); }
break;
case 48:
#line 134 "wktparse.y"
{ alloc_linestring(); }
break;
case 49:
#line 134 "wktparse.y"
{ pop(); }
break;
case 50:
#line 137 "wktparse.y"
{ alloc_linestring(); }
break;
case 51:
#line 137 "wktparse.y"
{ pop(); }
break;
case 52:
#line 140 "wktparse.y"
{ alloc_linestring_closed(); }
break;
case 53:
#line 140 "wktparse.y"
{ pop(); }
break;
case 54:
#line 143 "wktparse.y"
{ alloc_counter(); }
break;
case 55:
#line 143 "wktparse.y"
{ popc(); }
break;
case 59:
#line 155 "wktparse.y"
{set_zm(0, 1); }
break;
case 62:
#line 160 "wktparse.y"
{set_zm(0, 1); }
break;
case 68:
#line 173 "wktparse.y"
{ alloc_circularstring(); }
break;
case 69:
#line 173 "wktparse.y"
{ pop(); }
break;
case 70:
#line 176 "wktparse.y"
{ alloc_circularstring(); }
break;
case 71:
#line 176 "wktparse.y"
{ pop(); }
break;
case 72:
#line 179 "wktparse.y"
{ alloc_circularstring_closed(); }
break;
case 73:
#line 179 "wktparse.y"
{ pop(); }
break;
case 74:
#line 182 "wktparse.y"
{ alloc_counter(); }
break;
case 75:
#line 182 "wktparse.y"
{ popc(); }
break;
case 78:
#line 192 "wktparse.y"
{ alloc_compoundcurve(); }
break;
case 79:
#line 192 "wktparse.y"
{ pop(); }
break;
case 80:
#line 194 "wktparse.y"
{set_zm(0, 1); alloc_compoundcurve(); }
break;
case 81:
#line 194 "wktparse.y"
{ pop(); }
break;
case 83:
#line 199 "wktparse.y"
{ alloc_counter(); }
break;
case 84:
#line 199 "wktparse.y"
{ pop(); }
break;
case 89:
#line 213 "wktparse.y"
{ alloc_multilinestring(); }
break;
case 90:
#line 214 "wktparse.y"
{ pop(); }
break;
case 91:
#line 216 "wktparse.y"
{ set_zm(0, 1); alloc_multilinestring(); }
break;
case 92:
#line 217 "wktparse.y"
{ pop(); }
break;
case 94:
#line 222 "wktparse.y"
{ alloc_counter(); }
break;
case 95:
#line 222 "wktparse.y"
{ pop();}
break;
case 98:
#line 232 "wktparse.y"
{ alloc_multicurve(); }
break;
case 99:
#line 233 "wktparse.y"
{ pop(); }
break;
case 100:
#line 235 "wktparse.y"
{ set_zm(0, 1); alloc_multicurve(); }
break;
case 101:
#line 236 "wktparse.y"
{ pop(); }
break;
case 103:
#line 241 "wktparse.y"
{ alloc_counter(); }
break;
case 104:
#line 241 "wktparse.y"
{ pop(); }
break;
case 110:
#line 257 "wktparse.y"
{ set_zm(0, 1); }
break;
case 114:
#line 265 "wktparse.y"
{ alloc_polygon(); }
break;
case 115:
#line 265 "wktparse.y"
{ pop(); }
break;
case 116:
#line 268 "wktparse.y"
{ alloc_polygon(); }
break;
case 117:
#line 268 "wktparse.y"
{ pop(); }
break;
case 118:
#line 271 "wktparse.y"
{ alloc_counter(); }
break;
case 119:
#line 271 "wktparse.y"
{ pop();}
break;
case 122:
#line 281 "wktparse.y"
{ alloc_curvepolygon(); }
break;
case 123:
#line 281 "wktparse.y"
{ pop(); }
break;
case 124:
#line 283 "wktparse.y"
{ set_zm(0, 1); alloc_curvepolygon(); }
break;
case 125:
#line 284 "wktparse.y"
{ pop(); }
break;
case 127:
#line 289 "wktparse.y"
{ alloc_counter(); }
break;
case 128:
#line 289 "wktparse.y"
{ pop(); }
break;
case 133:
#line 303 "wktparse.y"
{ alloc_multipolygon(); }
break;
case 134:
#line 303 "wktparse.y"
{ pop(); }
break;
case 135:
#line 305 "wktparse.y"
{ set_zm(0, 1); alloc_multipolygon(); }
break;
case 136:
#line 306 "wktparse.y"
{ pop();}
break;
case 138:
#line 311 "wktparse.y"
{ alloc_counter(); }
break;
case 139:
#line 311 "wktparse.y"
{ pop(); }
break;
case 142:
#line 321 "wktparse.y"
{alloc_multisurface(); }
break;
case 143:
#line 321 "wktparse.y"
{ pop(); }
break;
case 144:
#line 323 "wktparse.y"
{ set_zm(0, 1); alloc_multisurface(); }
break;
case 145:
#line 324 "wktparse.y"
{ pop(); }
break;
case 147:
#line 329 "wktparse.y"
{ alloc_counter(); }
break;
case 148:
#line 329 "wktparse.y"
{ pop(); }
break;
case 153:
#line 343 "wktparse.y"
{ alloc_geomertycollection(); }
break;
case 154:
#line 344 "wktparse.y"
{ pop(); }
break;
case 155:
#line 346 "wktparse.y"
{ set_zm(0, 1); alloc_geomertycollection(); }
break;
case 156:
#line 347 "wktparse.y"
{ pop();}
break;
case 158:
#line 352 "wktparse.y"
{ alloc_counter(); }
break;
case 159:
#line 352 "wktparse.y"
{ pop(); }
break;
case 166:
#line 371 "wktparse.y"
{alloc_point_2d((yyvsp[(1) - (2)].value),(yyvsp[(2) - (2)].value)); }
break;
case 167:
#line 374 "wktparse.y"
{alloc_point_3d((yyvsp[(1) - (3)].value),(yyvsp[(2) - (3)].value),(yyvsp[(3) - (3)].value)); }
break;
case 168:
#line 377 "wktparse.y"
{alloc_point_4d((yyvsp[(1) - (4)].value),(yyvsp[(2) - (4)].value),(yyvsp[(3) - (4)].value),(yyvsp[(4) - (4)].value)); }
break;
case 169:
#line 380 "wktparse.y"
{ alloc_empty(); }
break;
/* Line 1267 of yacc.c. */
#line 2128 "y.tab.c"
default: break;
}
YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
YYPOPSTACK (yylen);
yylen = 0;
YY_STACK_PRINT (yyss, yyssp);
*++yyvsp = yyval;
*++yylsp = yyloc;
/* Now `shift' the result of the reduction. Determine what state
that goes to, based on the state we popped back to and the rule
number reduced by. */
yyn = yyr1[yyn];
yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
yystate = yytable[yystate];
else
yystate = yydefgoto[yyn - YYNTOKENS];
goto yynewstate;
/*------------------------------------.
| yyerrlab -- here on detecting error |
`------------------------------------*/
yyerrlab:
/* If not already recovering from an error, report this error. */
if (!yyerrstatus)
{
++yynerrs;
#if ! YYERROR_VERBOSE
yyerror (YY_("syntax error"));
#else
{
YYSIZE_T yysize = yysyntax_error (0, yystate, yychar);
if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM)
{
YYSIZE_T yyalloc = 2 * yysize;
if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM))
yyalloc = YYSTACK_ALLOC_MAXIMUM;
if (yymsg != yymsgbuf)
YYSTACK_FREE (yymsg);
yymsg = (char *) YYSTACK_ALLOC (yyalloc);
if (yymsg)
yymsg_alloc = yyalloc;
else
{
yymsg = yymsgbuf;
yymsg_alloc = sizeof yymsgbuf;
}
}
if (0 < yysize && yysize <= yymsg_alloc)
{
(void) yysyntax_error (yymsg, yystate, yychar);
yyerror (yymsg);
}
else
{
yyerror (YY_("syntax error"));
if (yysize != 0)
goto yyexhaustedlab;
}
}
#endif
}
yyerror_range[0] = yylloc;
if (yyerrstatus == 3)
{
/* If just tried and failed to reuse look-ahead token after an
error, discard it. */
if (yychar <= YYEOF)
{
/* Return failure if at end of input. */
if (yychar == YYEOF)
YYABORT;
}
else
{
yydestruct ("Error: discarding",
yytoken, &yylval, &yylloc);
yychar = YYEMPTY;
}
}
/* Else will try to reuse look-ahead token after shifting the error
token. */
goto yyerrlab1;
/*---------------------------------------------------.
| yyerrorlab -- error raised explicitly by YYERROR. |
`---------------------------------------------------*/
yyerrorlab:
/* Pacify compilers like GCC when the user code never invokes
YYERROR and the label yyerrorlab therefore never appears in user
code. */
if (/*CONSTCOND*/ 0)
goto yyerrorlab;
yyerror_range[0] = yylsp[1-yylen];
/* Do not reclaim the symbols of the rule which action triggered
this YYERROR. */
YYPOPSTACK (yylen);
yylen = 0;
YY_STACK_PRINT (yyss, yyssp);
yystate = *yyssp;
goto yyerrlab1;
/*-------------------------------------------------------------.
| yyerrlab1 -- common code for both syntax error and YYERROR. |
`-------------------------------------------------------------*/
yyerrlab1:
yyerrstatus = 3; /* Each real token shifted decrements this. */
for (;;)
{
yyn = yypact[yystate];
if (yyn != YYPACT_NINF)
{
yyn += YYTERROR;
if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
{
yyn = yytable[yyn];
if (0 < yyn)
break;
}
}
/* Pop the current state because it cannot handle the error token. */
if (yyssp == yyss)
YYABORT;
yyerror_range[0] = *yylsp;
yydestruct ("Error: popping",
yystos[yystate], yyvsp, yylsp);
YYPOPSTACK (1);
yystate = *yyssp;
YY_STACK_PRINT (yyss, yyssp);
}
if (yyn == YYFINAL)
YYACCEPT;
*++yyvsp = yylval;
yyerror_range[1] = yylloc;
/* Using YYLLOC is tempting, but would change the location of
the look-ahead. YYLOC is available though. */
YYLLOC_DEFAULT (yyloc, (yyerror_range - 1), 2);
*++yylsp = yyloc;
/* Shift the error token. */
YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
yystate = yyn;
goto yynewstate;
/*-------------------------------------.
| yyacceptlab -- YYACCEPT comes here. |
`-------------------------------------*/
yyacceptlab:
yyresult = 0;
goto yyreturn;
/*-----------------------------------.
| yyabortlab -- YYABORT comes here. |
`-----------------------------------*/
yyabortlab:
yyresult = 1;
goto yyreturn;
#ifndef yyoverflow
/*-------------------------------------------------.
| yyexhaustedlab -- memory exhaustion comes here. |
`-------------------------------------------------*/
yyexhaustedlab:
yyerror (YY_("memory exhausted"));
yyresult = 2;
/* Fall through. */
#endif
yyreturn:
if (yychar != YYEOF && yychar != YYEMPTY)
yydestruct ("Cleanup: discarding lookahead",
yytoken, &yylval, &yylloc);
/* Do not reclaim the symbols of the rule which action triggered
this YYABORT or YYACCEPT. */
YYPOPSTACK (yylen);
YY_STACK_PRINT (yyss, yyssp);
while (yyssp != yyss)
{
yydestruct ("Cleanup: popping",
yystos[*yyssp], yyvsp, yylsp);
YYPOPSTACK (1);
}
#ifndef yyoverflow
if (yyss != yyssa)
YYSTACK_FREE (yyss);
#endif
#if YYERROR_VERBOSE
if (yymsg != yymsgbuf)
YYSTACK_FREE (yymsg);
#endif
/* Make sure YYID is used. */
return YYID (yyresult);
}
#line 381 "wktparse.y"
================================================
FILE: src/liblwgeom/wktparse.tab.h
================================================
/* A Bison parser, made by GNU Bison 2.3. */
/* Skeleton interface for Bison's Yacc-like parsers in C
Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA. */
/* As a special exception, you may create a larger work that contains
part or all of the Bison parser skeleton and distribute that work
under terms of your choice, so long as that work isn't itself a
parser generator using the skeleton or a modified version thereof
as a parser skeleton. Alternatively, if you modify or redistribute
the parser skeleton itself, you may (at your option) remove this
special exception, which will cause the skeleton and the resulting
Bison output files to be licensed under the GNU General Public
License without this special exception.
This special exception was added by the Free Software Foundation in
version 2.2 of Bison. */
/* Tokens. */
#ifndef YYTOKENTYPE
# define YYTOKENTYPE
/* Put the tokens into the symbol table, so that GDB and other debuggers
know about them. */
enum yytokentype {
POINT = 258,
LINESTRING = 259,
POLYGON = 260,
MULTIPOINT = 261,
MULTILINESTRING = 262,
MULTIPOLYGON = 263,
GEOMETRYCOLLECTION = 264,
CIRCULARSTRING = 265,
COMPOUNDCURVE = 266,
CURVEPOLYGON = 267,
MULTICURVE = 268,
MULTISURFACE = 269,
POINTM = 270,
LINESTRINGM = 271,
POLYGONM = 272,
MULTIPOINTM = 273,
MULTILINESTRINGM = 274,
MULTIPOLYGONM = 275,
GEOMETRYCOLLECTIONM = 276,
CIRCULARSTRINGM = 277,
COMPOUNDCURVEM = 278,
CURVEPOLYGONM = 279,
MULTICURVEM = 280,
MULTISURFACEM = 281,
SRID = 282,
EMPTY = 283,
VALUE = 284,
LPAREN = 285,
RPAREN = 286,
COMMA = 287,
EQUALS = 288,
SEMICOLON = 289,
WKB = 290
};
#endif
/* Tokens. */
#define POINT 258
#define LINESTRING 259
#define POLYGON 260
#define MULTIPOINT 261
#define MULTILINESTRING 262
#define MULTIPOLYGON 263
#define GEOMETRYCOLLECTION 264
#define CIRCULARSTRING 265
#define COMPOUNDCURVE 266
#define CURVEPOLYGON 267
#define MULTICURVE 268
#define MULTISURFACE 269
#define POINTM 270
#define LINESTRINGM 271
#define POLYGONM 272
#define MULTIPOINTM 273
#define MULTILINESTRINGM 274
#define MULTIPOLYGONM 275
#define GEOMETRYCOLLECTIONM 276
#define CIRCULARSTRINGM 277
#define COMPOUNDCURVEM 278
#define CURVEPOLYGONM 279
#define MULTICURVEM 280
#define MULTISURFACEM 281
#define SRID 282
#define EMPTY 283
#define VALUE 284
#define LPAREN 285
#define RPAREN 286
#define COMMA 287
#define EQUALS 288
#define SEMICOLON 289
#define WKB 290
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
typedef union YYSTYPE
#line 22 "wktparse.y"
{
double value;
const char* wkb;
}
/* Line 1489 of yacc.c. */
#line 124 "y.tab.h"
YYSTYPE;
# define yystype YYSTYPE /* obsolescent; will be withdrawn */
# define YYSTYPE_IS_DECLARED 1
# define YYSTYPE_IS_TRIVIAL 1
#endif
extern YYSTYPE lwg_parse_yylval;
#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED
typedef struct YYLTYPE
{
int first_line;
int first_column;
int last_line;
int last_column;
} YYLTYPE;
# define yyltype YYLTYPE /* obsolescent; will be withdrawn */
# define YYLTYPE_IS_DECLARED 1
# define YYLTYPE_IS_TRIVIAL 1
#endif
extern YYLTYPE lwg_parse_yylloc;
================================================
FILE: src/liblwgeom/wktparse.y
================================================
/*
* Written by Ralph Mason ralph.masontelogis.com
*
* Copyright Telogis 2004
* www.telogis.com
*
*/
%{
#include "wktparse.h"
#include
#include
void set_zm(char z, char m);
int lwg_parse_yylex(void);
%}
%start geometry
%locations
%union {
double value;
const char* wkb;
}
%token POINT LINESTRING POLYGON MULTIPOINT MULTILINESTRING MULTIPOLYGON GEOMETRYCOLLECTION CIRCULARSTRING COMPOUNDCURVE CURVEPOLYGON MULTICURVE MULTISURFACE
%token POINTM LINESTRINGM POLYGONM MULTIPOINTM MULTILINESTRINGM MULTIPOLYGONM GEOMETRYCOLLECTIONM CIRCULARSTRINGM COMPOUNDCURVEM CURVEPOLYGONM MULTICURVEM MULTISURFACEM
%token SRID
%token EMPTY
%token VALUE
%token LPAREN RPAREN COMMA EQUALS SEMICOLON
%token WKB
%%
geometry :
srid SEMICOLON { alloc_lwgeom(srid); } geometry_int
|
{ alloc_lwgeom(-1); } geometry_int
geometry_int :
geom_wkb
|
geom_point
|
geom_linestring
|
geom_circularstring
|
geom_polygon
|
geom_compoundcurve
|
geom_curvepolygon
|
geom_multipoint
|
geom_multilinestring
|
geom_multicurve
|
geom_multipolygon
|
geom_multisurface
|
geom_geometrycollection
srid :
SRID EQUALS VALUE { set_srid($3); }
geom_wkb :
WKB { alloc_wkb($1); }
/* POINT */
geom_point :
POINT point
|
POINTM { set_zm(0, 1); } point
point :
empty_point
|
nonempty_point
empty_point :
{ alloc_point(); } empty { pop(); }
nonempty_point :
{ alloc_point(); } point_int { pop(); }
point_int :
LPAREN a_point RPAREN
/* MULTIPOINT */
geom_multipoint :
MULTIPOINT { alloc_multipoint(); } multipoint { pop(); }
|
MULTIPOINTM { set_zm(0, 1); alloc_multipoint(); } multipoint {pop(); }
multipoint :
empty
|
{ alloc_counter(); } LPAREN multipoint_int RPAREN { pop(); }
multipoint_int :
mpoint_element
|
multipoint_int COMMA mpoint_element
mpoint_element :
nonempty_point
|
/* this is to allow MULTIPOINT(0 0, 1 1) */
{ alloc_point(); } a_point { pop(); }
/* LINESTRING */
geom_linestring :
LINESTRING linestring
|
LINESTRINGM { set_zm(0, 1); } linestring
linestring :
empty_linestring
|
nonempty_linestring
empty_linestring :
{ alloc_linestring(); } empty { pop(); }
nonempty_linestring :
{ alloc_linestring(); } linestring_1 { pop(); }
nonempty_linestring_closed :
{ alloc_linestring_closed(); } linestring_1 { pop(); }
linestring_1 :
{ alloc_counter(); } LPAREN linestring_int RPAREN { popc(); }
linestring_int :
a_point
|
linestring_int COMMA a_point;
/* CIRCULARSTRING */
geom_circularstring :
CIRCULARSTRING circularstring
|
CIRCULARSTRINGM {set_zm(0, 1); } circularstring
geom_circularstring_closed :
CIRCULARSTRING circularstring_closed
|
CIRCULARSTRINGM {set_zm(0, 1); } circularstring_closed
circularstring :
empty_circularstring
|
nonempty_circularstring
circularstring_closed :
empty_circularstring
|
nonempty_circularstring_closed
empty_circularstring :
{ alloc_circularstring(); } empty { pop(); }
nonempty_circularstring :
{ alloc_circularstring(); } circularstring_1 { pop(); }
nonempty_circularstring_closed :
{ alloc_circularstring_closed(); } circularstring_1 { pop(); }
circularstring_1 :
{ alloc_counter(); } LPAREN circularstring_int RPAREN { popc(); }
circularstring_int :
a_point
|
circularstring_int COMMA a_point;
/* COMPOUNDCURVE */
geom_compoundcurve:
COMPOUNDCURVE { alloc_compoundcurve(); } compoundcurve { pop(); }
|
COMPOUNDCURVEM {set_zm(0, 1); alloc_compoundcurve(); } compoundcurve { pop(); }
compoundcurve:
empty
|
{ alloc_counter(); } LPAREN compoundcurve_int RPAREN { pop(); }
compoundcurve_int:
nonempty_linestring
|
geom_circularstring
|
compoundcurve_int COMMA nonempty_linestring
|
compoundcurve_int COMMA geom_circularstring
/* MULTILINESTRING */
geom_multilinestring :
MULTILINESTRING { alloc_multilinestring(); }
multilinestring { pop(); }
|
MULTILINESTRINGM { set_zm(0, 1); alloc_multilinestring(); }
multilinestring { pop(); }
multilinestring :
empty
|
{ alloc_counter(); } LPAREN multilinestring_int RPAREN{ pop();}
multilinestring_int :
nonempty_linestring
|
multilinestring_int COMMA nonempty_linestring
/* MULTICURVESTRING */
geom_multicurve :
MULTICURVE { alloc_multicurve(); }
multicurve { pop(); }
|
MULTICURVEM { set_zm(0, 1); alloc_multicurve(); }
multicurve { pop(); }
multicurve :
empty
|
{ alloc_counter(); } LPAREN multicurve_int RPAREN { pop(); }
multicurve_int :
nonempty_linestring
|
geom_circularstring
|
multicurve_int COMMA nonempty_linestring
|
multicurve_int COMMA geom_circularstring
/* POLYGON */
geom_polygon :
POLYGON polygon
|
POLYGONM { set_zm(0, 1); } polygon
polygon :
empty_polygon
|
nonempty_polygon
empty_polygon :
{ alloc_polygon(); } empty { pop(); }
nonempty_polygon :
{ alloc_polygon(); } polygon_1 { pop(); }
polygon_1 :
{ alloc_counter(); } LPAREN polygon_int RPAREN { pop();}
polygon_int :
linestring_1
|
polygon_int COMMA linestring_1
/* CURVEPOLYGON */
geom_curvepolygon :
CURVEPOLYGON { alloc_curvepolygon(); } curvepolygon { pop(); }
|
CURVEPOLYGONM { set_zm(0, 1); alloc_curvepolygon(); }
curvepolygon { pop(); }
curvepolygon :
empty
|
{ alloc_counter(); } LPAREN curvepolygon_int RPAREN { pop(); }
curvepolygon_int :
nonempty_linestring_closed
|
geom_circularstring_closed
|
curvepolygon_int COMMA nonempty_linestring_closed
|
curvepolygon_int COMMA geom_circularstring_closed
/* MULTIPOLYGON */
geom_multipolygon :
MULTIPOLYGON { alloc_multipolygon(); } multipolygon { pop(); }
|
MULTIPOLYGONM { set_zm(0, 1); alloc_multipolygon(); }
multipolygon { pop();}
multipolygon :
empty
|
{ alloc_counter(); } LPAREN multipolygon_int RPAREN { pop(); }
multipolygon_int :
nonempty_polygon
|
multipolygon_int COMMA nonempty_polygon
/* MULTISURFACE */
geom_multisurface :
MULTISURFACE {alloc_multisurface(); } multisurface { pop(); }
|
MULTISURFACEM { set_zm(0, 1); alloc_multisurface(); }
multisurface { pop(); }
multisurface :
empty
|
{ alloc_counter(); } LPAREN multisurface_int RPAREN { pop(); }
multisurface_int :
nonempty_polygon
|
geom_curvepolygon
|
multisurface_int COMMA nonempty_polygon
|
multisurface_int COMMA geom_curvepolygon
/* GEOMETRYCOLLECTION */
geom_geometrycollection :
GEOMETRYCOLLECTION { alloc_geomertycollection(); }
geometrycollection { pop(); }
|
GEOMETRYCOLLECTIONM { set_zm(0, 1); alloc_geomertycollection(); }
geometrycollection { pop();}
geometrycollection :
empty
|
{ alloc_counter(); } LPAREN geometrycollection_int RPAREN { pop(); }
geometrycollection_int :
/* to support GEOMETRYCOLLECTION(EMPTY) for backward compatibility */
empty
|
geometry_int
|
geometrycollection_int COMMA geometry_int
a_point :
point_2d
|
point_3d
|
point_4d
point_2d :
VALUE VALUE {alloc_point_2d($1,$2); }
point_3d :
VALUE VALUE VALUE {alloc_point_3d($1,$2,$3); }
point_4d :
VALUE VALUE VALUE VALUE {alloc_point_4d($1,$2,$3,$4); }
empty :
EMPTY { alloc_empty(); }
%%
================================================
FILE: src/libsqlite3_geocoder/Makefile
================================================
all: libsqlite3_geocoder.so
CC=gcc -fPIC
#extension.o:
# $(CC) -lm -lsqlite3 -shared $^ -o $@
libsqlite3_geocoder.so: extension.o wkb_compress.o util.o metaphon.o levenshtein.o
# $(CC) -lm -lsqlite3 -I/usr/include -shared $^ -o $@
$(CC) $^ -lm -lsqlite3 -shared -o $@
test: test_wkb_compress test_levenshtein
test_wkb_compress: wkb_compress.c
$(CC) -DTEST -o wkb_compress $^
test_levenshtein: levenshtein.c
$(CC) -DTEST -o levenshtein $^
clean:
rm -f *.o *.so wkb_compress levenshtein
install:
cp *.so ../../lib/geocoder/us/sqlite3.so
================================================
FILE: src/libsqlite3_geocoder/Makefile.nix
================================================
all: libsqlite3_geocoder.so
libsqlite3_geocoder.so: extension.o wkb_compress.o util.o metaphon.o levenshtein.o
$(CC) -shared $^ -o $@
test: test_wkb_compress test_levenshtein
test_wkb_compress: wkb_compress.c
$(CC) -DTEST -o wkb_compress $^
test_levenshtein: levenshtein.c
$(CC) -DTEST -o levenshtein $^
clean:
rm -f *.o *.so wkb_compress levenshtein
================================================
FILE: src/libsqlite3_geocoder/Makefile.redhat
================================================
all: libsqlite3_geocoder.so
CFLAGS=-fPIC
libsqlite3_geocoder.so: extension.o wkb_compress.o util.o metaphon.o levenshtein.o
$(CC) $(CFLAGS) -shared $^ -o $@
test: test_wkb_compress test_levenshtein
test_wkb_compress: wkb_compress.c
$(CC) -DTEST -o wkb_compress $^
test_levenshtein: levenshtein.c
$(CC) -DTEST -o levenshtein $^
clean:
rm -f *.o *.so wkb_compress levenshtein
================================================
FILE: src/libsqlite3_geocoder/extension.c
================================================
# include
# include
# include
# include
# include
# include "extension.h"
static SQLITE_EXTENSION_INIT1;
static void
sqlite3_metaphone (sqlite3_context *context, int argc, sqlite3_value **argv) {
const unsigned char *input = sqlite3_value_text(argv[0]);
int max_phones = 0;
char *output;
int len;
if (sqlite3_value_type(argv[0]) == SQLITE_NULL) {
sqlite3_result_null(context);
return;
}
if (argc > 1)
max_phones = sqlite3_value_int(argv[1]);
if (max_phones <= 0)
max_phones = strlen(input);
output = sqlite3_malloc((max_phones+1)*sizeof(char));
len = metaphone(input, output, max_phones);
sqlite3_result_text(context, output, len, sqlite3_free);
}
static void
sqlite3_levenshtein (sqlite3_context *context, int argc, sqlite3_value **argv) {
const unsigned char *s1 = sqlite3_value_text(argv[0]),
*s2 = sqlite3_value_text(argv[1]);
double dist;
if (sqlite3_value_type(argv[0]) == SQLITE_NULL ||
sqlite3_value_type(argv[1]) == SQLITE_NULL) {
sqlite3_result_null(context);
return;
}
dist = levenshtein_distance(s1, s2);
sqlite3_result_double(context, dist);
}
static void
sqlite3_digit_suffix (sqlite3_context *context,
int argc, sqlite3_value **argv) {
if (sqlite3_value_type(argv[0]) == SQLITE_NULL) {
sqlite3_result_null(context);
return;
}
const unsigned char *input = sqlite3_value_text(argv[0]);
char *output = sqlite3_malloc((strlen(input)+1) * sizeof(char));
size_t len = digit_suffix(input, output);
sqlite3_result_text(context, output, len, sqlite3_free);
}
static void
sqlite3_nondigit_prefix (sqlite3_context *context,
int argc, sqlite3_value **argv) {
if (sqlite3_value_type(argv[0]) == SQLITE_NULL) {
sqlite3_result_null(context);
return;
}
const unsigned char *input = sqlite3_value_text(argv[0]);
char *output = sqlite3_malloc((strlen(input)+1) * sizeof(char));
size_t len = nondigit_prefix(input, output);
sqlite3_result_text(context, output, len, sqlite3_free);
}
static void
sqlite3_compress_wkb_line (sqlite3_context *context,
int argc, sqlite3_value **argv) {
if (sqlite3_value_type(argv[0]) == SQLITE_NULL) {
sqlite3_result_null(context);
return;
}
unsigned long input_len = sqlite3_value_bytes(argv[0]);
const void *input = sqlite3_value_blob(argv[0]);
unsigned long output_len = ceil((input_len-9)/8.0) * 4;
unsigned long len = 0;
void *output = sqlite3_malloc(output_len);
len = compress_wkb_line(output, input, input_len);
assert(len == output_len);
sqlite3_result_blob(context, output, len, sqlite3_free);
}
static void
sqlite3_uncompress_wkb_line (sqlite3_context *context,
int argc, sqlite3_value **argv) {
unsigned long input_len = sqlite3_value_bytes(argv[0]);
const void *input = sqlite3_value_blob(argv[0]);
unsigned long output_len = input_len*2+9;
unsigned long len = 0;
void *output = sqlite3_malloc(output_len);
len = uncompress_wkb_line(output, input, input_len);
assert(len == output_len);
sqlite3_result_blob(context, output, len, sqlite3_free);
}
int sqlite3_extension_init (sqlite3 * db, char **pzErrMsg,
const sqlite3_api_routines *pApi) {
SQLITE_EXTENSION_INIT2(pApi);
sqlite3_create_function(db, "metaphone", 1, SQLITE_ANY,
NULL, sqlite3_metaphone, NULL, NULL);
sqlite3_create_function(db, "metaphone", 2, SQLITE_ANY,
NULL, sqlite3_metaphone, NULL, NULL);
sqlite3_create_function(db, "levenshtein", 2, SQLITE_ANY,
NULL, sqlite3_levenshtein, NULL, NULL);
sqlite3_create_function(db, "compress_wkb_line", 1, SQLITE_ANY,
NULL, sqlite3_compress_wkb_line, NULL, NULL);
sqlite3_create_function(db, "uncompress_wkb_line", 1, SQLITE_ANY,
NULL, sqlite3_uncompress_wkb_line, NULL, NULL);
sqlite3_create_function(db, "digit_suffix", 1, SQLITE_ANY,
NULL, sqlite3_digit_suffix, NULL, NULL);
sqlite3_create_function(db, "nondigit_prefix", 1, SQLITE_ANY,
NULL, sqlite3_nondigit_prefix, NULL, NULL);
return 0;
}
================================================
FILE: src/libsqlite3_geocoder/extension.h
================================================
#ifndef SQLITE3_GEOCODER
#define SQLITE3_GEOCODER
#include
int metaphone(const char *Word, char *Metaph, int max_phones);
double levenshtein_distance (const unsigned char *s1, const unsigned char *s2);
signed int rindex_nondigit (const char *string);
signed int nondigit_prefix (const char *input, char *output);
uint32_t compress_wkb_line (void *dest, const void *src, uint32_t len);
uint32_t uncompress_wkb_line (void *dest, const void *src, uint32_t len);
#endif
================================================
FILE: src/libsqlite3_geocoder/levenshtein.c
================================================
# include
# define STRLEN_MAX 256
# define min(x, y) ((x) < (y) ? (x) : (y))
# define max(x, y) ((x) > (y) ? (x) : (y))
# define NO_CASE (~(unsigned char)32)
# define eql(x, y) (((x) & NO_CASE) == ((y) & NO_CASE))
static int d[STRLEN_MAX][STRLEN_MAX]; // this isn't thread safe
double levenshtein_distance (const unsigned char *s1, const unsigned char *s2) {
const size_t len1 = min(strlen(s1), STRLEN_MAX-1),
len2 = min(strlen(s2), STRLEN_MAX-1);
int cost, i, j;
for (i = 1; i <= len1; ++i) d[i][0] = i;
for (i = 1; i <= len2; ++i) d[0][i] = i;
for (i = 1; i <= len1; ++i) {
for (j = 1; j <= len2; ++j) {
cost = (eql(s1[i-1], s2[j-1]) ? 0 : 1);
d[i][j] = min(min(
d[i-1][j ] + 1, /* deletion */
d[i ][j-1] + 1), /* insertion */
d[i-1][j-1] + cost); /* substitution */
if (i > 1 && j > 1 && eql(s1[i-1], s2[j-2]) && eql(s1[i-2], s2[j-1])) {
d[i][j] = min( d[i][j],
d[i-2][j-2] + cost ); /* transposition */
}
}
}
return (d[len1][len2] / (double) max(len1, len2));
}
#ifdef TEST
#include
int main (int argc, char **argv) {
if (argc < 3) return -1;
printf("%.1f%%\n", levenshtein_distance(argv[1],argv[2]) * 100);
return 0;
}
#endif
================================================
FILE: src/libsqlite3_geocoder/metaphon.c
================================================
/* +++Customized by SDE for sqlite3 use 09-Mar-2009 */
/* +++File obtained from http://www.shedai.net/c/new/METAPHON.C */
/* +++Date previously modified: 05-Jul-1997 */
/*
** METAPHON.C - Phonetic string matching
**
** The Metaphone algorithm was developed by Lawrence Phillips. Like the
** Soundex algorithm, it compares words that sound alike but are spelled
** differently. Metaphone was designed to overcome difficulties encountered
** with Soundex.
**
** This implementation was written by Gary A. Parker and originally published
** in the June/July, 1991 (vol. 5 nr. 4) issue of C Gazette. As published,
** this code was explicitly placed in the public domain by the author.
*/
#include