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? %> <% for record in @records %> <% end %>
Match Lat Lon # Qual Dir Type Street Type Dir Qual City St ZIP  
<%= 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 %> ================================================ 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? %> <% for record in @records %> <% end %>
Match Precision Lat Lon # Qual Dir Type Street Type Dir Qual City St ZIP  
<%= 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 %> <% 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? %> <% for record in @records %> <% end %>
Match Lat Lon # Qual Dir Type Street Type Dir Qual City St ZIP  
<%= 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 %> <% 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 #include /* strlen() */ #include #include #define malloc(x) sqlite3_malloc((x)) #define free(x) sqlite3_free((x)) /* ** Character coding array */ static char vsvfn[26] = { 1,16,4,16,9,2,4,16,9,2,0,2,2,2,1,4,0,2,4,4,1,0,0,0,8,0}; /* A B C D E F G H I J K L M N O P Q R S T U V W X Y Z */ /* ** Macros to access the character coding array */ #define vowel(x) (vsvfn[(x) - 'A'] & 1) /* AEIOU */ #define same(x) (vsvfn[(x) - 'A'] & 2) /* FJLMNR */ #define varson(x) (vsvfn[(x) - 'A'] & 4) /* CGPST */ #define frontv(x) (vsvfn[(x) - 'A'] & 8) /* EIY */ #define noghf(x) (vsvfn[(x) - 'A'] & 16) /* BDH */ int metaphone(const char *Word, char *Metaph, int max_phones) { char *n, *n_start, *n_end; /* Pointers to string */ char *metaph_start = Metaph, *metaph_end; /* Pointers to metaph */ int ntrans_len = strlen(Word)+4; char *ntrans = (char *)malloc(sizeof(char) * ntrans_len); /* Word with uppercase letters */ int KSflag; /* State flag for X translation */ /* SDE -- special case: if the word starts with a number, just * copy the leading digits and return. This means we don't * metaphone cardinal number suffixes (i.e. "st","nd","rd") */ int leading_digit = isdigit(*Word); /* SDE -- check for a leading semivowel. needed because * the copy in ntrans gets destroyed by the metaphone process. */ char leading_semivowel = '\0'; /* ** Copy word to internal buffer, dropping non-alphabetic characters ** and converting to upper case. */ for (n = ntrans + 1, n_end = ntrans + ntrans_len - 2; *Word && n < n_end; ++Word) { /* SDE -- see previous comment */ if (leading_digit && isalpha(*Word)) break; /* SDE -- copy numbers as well, for geocoding street names */ /* was: if (isalpha(*Word)) */ if (isalnum(*Word)) *n++ = toupper(*Word); } if (n == ntrans + 1) { free(ntrans); Metaph[0]='\0'; return 0; /* Return if zero characters */ } else n_end = n; /* Set end of string pointer */ /* ** Pad with '\0's, front and rear */ *n++ = '\0'; *n = '\0'; n = ntrans; *n++ = '\0'; /* SDE: check for leading semivowel here */ if (ntrans[1] == 'W' || ntrans[1] == 'Y') leading_semivowel = ntrans[1]; /* ** Check for PN, KN, GN, WR, WH, and X at start */ switch (*n) { case 'P': case 'K': case 'G': if ('N' == *(n + 1)) *n++ = '\0'; break; case 'A': if ('E' == *(n + 1)) *n++ = '\0'; break; case 'W': if ('R' == *(n + 1)) *n++ = '\0'; else if ('H' == *(n + 1)) { *(n + 1) = *n; *n++ = '\0'; } break; case 'X': *n = 'S'; break; } /* ** Now loop through the string, stopping at the end of the string ** or when the computed Metaphone code is max_phones characters long. */ KSflag = 0; /* State flag for KStranslation */ for (metaph_end = Metaph + max_phones, n_start = n; n <= n_end && Metaph < metaph_end; ++n) { if (KSflag) { KSflag = 0; *Metaph++ = *n; } else { /* SDE -- special case: copy numbers verbatim */ if (isdigit(*n)) { *Metaph++ = *n; continue; } /* Drop duplicates except for CC */ if (*(n - 1) == *n && *n != 'C') continue; /* Check for F J L M N R or first letter vowel */ if (same(*n) || (n == n_start && vowel(*n))) *Metaph++ = *n; else switch (*n) { case 'B': if (n < n_end || *(n - 1) != 'M') *Metaph++ = *n; break; case 'C': if (*(n - 1) != 'S' || !frontv(*(n + 1))) { if ('I' == *(n + 1) && 'A' == *(n + 2)) *Metaph++ = 'X'; else if (frontv(*(n + 1))) *Metaph++ = 'S'; else if ('H' == *(n + 1)) *Metaph++ = ((n == n_start && !vowel(*(n + 2))) || 'S' == *(n - 1)) ? 'K' : 'X'; else *Metaph++ = 'K'; } break; case 'D': *Metaph++ = ('G' == *(n + 1) && frontv(*(n + 2))) ? 'J' : 'T'; break; case 'G': if ((*(n + 1) != 'H' || vowel(*(n + 2))) && (*(n + 1) != 'N' || ((n + 1) < n_end && (*(n + 2) != 'E' || *(n + 3) != 'D'))) && (*(n - 1) != 'D' || !frontv(*(n + 1)))) { *Metaph++ = (frontv(*(n + 1)) && *(n + 2) != 'G') ? 'J' : 'K'; } else if ('H' == *(n + 1) && !noghf(*(n - 3)) && *(n - 4) != 'H') { *Metaph++ = 'F'; } break; case 'H': if (!varson(*(n - 1)) && (!vowel(*(n - 1)) || vowel(*(n + 1)))) { *Metaph++ = 'H'; } break; case 'K': if (*(n - 1) != 'C') *Metaph++ = 'K'; break; case 'P': *Metaph++ = ('H' == *(n + 1)) ? 'F' : 'P'; break; case 'Q': *Metaph++ = 'K'; break; case 'S': *Metaph++ = ('H' == *(n + 1) || ('I' == *(n + 1) && ('O' == *(n + 2) || 'A' == *(n + 2)))) ? 'X' : 'S'; break; case 'T': if ('I' == *(n + 1) && ('O' == *(n + 2) || 'A' == *(n + 2))) { *Metaph++ = 'X'; } else if ('H' == *(n + 1)) /* SDE: was: *Metaph++ = 'O'; but that's WRONG. */ *Metaph++ = '0'; else if (*(n + 1) != 'C' || *(n + 2) != 'H') *Metaph++ = 'T'; break; case 'V': *Metaph++ = 'F'; break; case 'W': case 'Y': if (vowel(*(n + 1))) *Metaph++ = *n; break; case 'X': if (n == n_start) *Metaph++ = 'S'; else { *Metaph++ = 'K'; KSflag = 1; } break; case 'Z': *Metaph++ = 'S'; break; } } } /* SDE: special case: if word consists solely of W or Y, use that. */ if (Metaph == metaph_start && leading_semivowel) *Metaph++ = leading_semivowel; *Metaph = '\0'; free(ntrans); return strlen(metaph_start); } ================================================ FILE: src/libsqlite3_geocoder/util.c ================================================ # include # include int address_metaphone(const char *input, char *output, int max_phones) { const char *n = input; int i = 0; if (isdigit(*n)) { while (i < max_phones && isdigit(n[i]) && n[i] != '\0') *output++ = n[i++]; *output = '\0'; return 1; } else { return metaphone(input, output, max_phones); } } signed int rindex_nondigit (const char *string) { signed int i = strlen(string); if (!i) return -1; for (i--; i >= 0 && isdigit(string[i]); i--); return i; } signed int digit_suffix (const char *input, char *output) { signed int i = rindex_nondigit(input); strcpy(output, input+i+1); return strlen(output); } signed int nondigit_prefix (const char *input, char *output) { signed int i = rindex_nondigit(input); if (i++ >= 0) { strncpy(output, input, i); output[i] = '\0'; } return i; } ================================================ FILE: src/libsqlite3_geocoder/wkb_compress.c ================================================ #include #include uint32_t compress_wkb_line (void *dest, const void *src, uint32_t len) { uint32_t d, s; double value; if (!len) return 0; for (s = 9, d = 0; s < len; d += 4, s += 8) { value = *(double *)(src + s); value *= 1000000; *(int32_t *)(dest + d) = (int32_t) value; } return d; } uint32_t uncompress_wkb_line (void *dest, const void *src, uint32_t len) { uint32_t d, s; double value; if (!len) return 0; memcpy(dest, "\01\02\00\00\00\06\00\00\00", 10); for (s = 0, d = 9; s < len; s += 4, d += 8) { value = (double) *(int32_t *)(src + s); value /= 1000000; *(double *)(dest + d) = value; } return d; } #ifdef TEST #include int main (int argc, char *argv) { char hex[1024], *scan; char wkb[512]; unsigned long len, clen; while (!feof(stdin)) { fgets(hex, sizeof(hex), stdin); for (scan = hex, len = 0; *scan && sizeof(wkb)>len; scan += 2, len++) { if (sscanf(scan, "%2x", (uint32_t *)(wkb+len)) != 1) break; } clen = compress_wkb_line(hex, wkb, len); printf("before: %lu, after: %lu\n", len, clen); len = uncompress_wkb_line(wkb, hex, clen); printf("before: %lu, after: %lu\n", clen, len); for (scan = wkb + 9; scan < wkb + len; scan += 8) { printf("%.6f ", *(double *)scan); } printf("\n"); } } #endif ================================================ FILE: src/metaphone/Makefile ================================================ all: metaphone.so metaphone.so: extension.o metaphon.o $(CC) -fPIC -shared $^ -o $@ clean: rm -f *.o *.so ================================================ FILE: src/metaphone/README ================================================ = SQLite 3 Metaphone extension = * This library implements the Metaphone algorithm, originally developed by Laurence Phillips, as an SQLite 3 extension function: http://en.wikipedia.org/wiki/Metaphone * This code is based around the original public domain implementation in C by Gary Phillips, as provided by Sadi Evren Seker: http://www.shedai.net/c/new/METAPHON.C * Like SQLite and the Phillips implementation of Metaphone, this code is provided in the public domain, in the hope that it will be useful. * To compile the code, simply run `make`. You must have GNU Make and GCC installed. * The module implements one function, metaphone(), which takes a string to convert to a metaphone representation as its first argument, and an optional second argument to specify the maximum length of the output. * To use the code, run `sqlite3` and enter the following: {{{ sqlite> .load /path/to/metaphone.so -- you can use ./metaphone.so if the .so is in your -- current directory, or just metaphone.so if it's -- somewhere in your library path. sqlite> select metaphone("Schuyler"); SKLR sqlite> select metaphone("Schuyler", 3); SKL sqlite> select metaphone("Skyler"); SKLR sqlite> select metaphone("Skylar"); SKLR sqlite> select metaphone("SQLite rules!"); SKLTRLS sqlite> select metaphone("SQLite roolz!!!1!"); SKLTRLS }}} Questions? Comments? Complaints? Approbation? Email schuyler@nocat.net. Schuyler Erle 9 March 2008 =30= ================================================ FILE: src/metaphone/extension.c ================================================ # include # include # include # include 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, SQLITE_TRANSIENT); } 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); return 0; } ================================================ FILE: src/metaphone/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 #include /* strlen() */ #include #define malloc(x) sqlite3_malloc((x)) #define free(x) sqlite3_free((x)) /* ** Character coding array */ static char vsvfn[26] = { 1,16,4,16,9,2,4,16,9,2,0,2,2,2,1,4,0,2,4,4,1,0,0,0,8,0}; /* A B C D E F G H I J K L M N O P Q R S T U V W X Y Z */ /* ** Macros to access the character coding array */ #define vowel(x) (vsvfn[(x) - 'A'] & 1) /* AEIOU */ #define same(x) (vsvfn[(x) - 'A'] & 2) /* FJLMNR */ #define varson(x) (vsvfn[(x) - 'A'] & 4) /* CGPST */ #define frontv(x) (vsvfn[(x) - 'A'] & 8) /* EIY */ #define noghf(x) (vsvfn[(x) - 'A'] & 16) /* BDH */ int metaphone(const char *Word, char *Metaph, int max_phones) { char *n, *n_start, *n_end; /* Pointers to string */ char *metaph_start = Metaph, *metaph_end; /* Pointers to metaph */ int ntrans_len = strlen(Word)+4; char *ntrans = (char *)malloc(sizeof(char) * ntrans_len); /* Word with uppercase letters */ int KSflag; /* State flag for X translation */ /* ** Copy word to internal buffer, dropping non-alphabetic characters ** and converting to upper case. */ for (n = ntrans + 1, n_end = ntrans + ntrans_len - 2; *Word && n < n_end; ++Word) { if (isalpha(*Word)) *n++ = toupper(*Word); } if (n == ntrans + 1) { free(ntrans); Metaph[0]='\0'; return 1; /* Return if zero characters */ } else n_end = n; /* Set end of string pointer */ /* ** Pad with '\0's, front and rear */ *n++ = '\0'; *n = '\0'; n = ntrans; *n++ = '\0'; /* ** Check for PN, KN, GN, WR, WH, and X at start */ switch (*n) { case 'P': case 'K': case 'G': if ('N' == *(n + 1)) *n++ = '\0'; break; case 'A': if ('E' == *(n + 1)) *n++ = '\0'; break; case 'W': if ('R' == *(n + 1)) *n++ = '\0'; else if ('H' == *(n + 1)) { *(n + 1) = *n; *n++ = '\0'; } break; case 'X': *n = 'S'; break; } /* ** Now loop through the string, stopping at the end of the string ** or when the computed Metaphone code is max_phones characters long. */ KSflag = 0; /* State flag for KStranslation */ for (metaph_end = Metaph + max_phones, n_start = n; n <= n_end && Metaph < metaph_end; ++n) { if (KSflag) { KSflag = 0; *Metaph++ = *n; } else { /* Drop duplicates except for CC */ if (*(n - 1) == *n && *n != 'C') continue; /* Check for F J L M N R or first letter vowel */ if (same(*n) || (n == n_start && vowel(*n))) *Metaph++ = *n; else switch (*n) { case 'B': if (n < n_end || *(n - 1) != 'M') *Metaph++ = *n; break; case 'C': if (*(n - 1) != 'S' || !frontv(*(n + 1))) { if ('I' == *(n + 1) && 'A' == *(n + 2)) *Metaph++ = 'X'; else if (frontv(*(n + 1))) *Metaph++ = 'S'; else if ('H' == *(n + 1)) *Metaph++ = ((n == n_start && !vowel(*(n + 2))) || 'S' == *(n - 1)) ? 'K' : 'X'; else *Metaph++ = 'K'; } break; case 'D': *Metaph++ = ('G' == *(n + 1) && frontv(*(n + 2))) ? 'J' : 'T'; break; case 'G': if ((*(n + 1) != 'H' || vowel(*(n + 2))) && (*(n + 1) != 'N' || ((n + 1) < n_end && (*(n + 2) != 'E' || *(n + 3) != 'D'))) && (*(n - 1) != 'D' || !frontv(*(n + 1)))) { *Metaph++ = (frontv(*(n + 1)) && *(n + 2) != 'G') ? 'J' : 'K'; } else if ('H' == *(n + 1) && !noghf(*(n - 3)) && *(n - 4) != 'H') { *Metaph++ = 'F'; } break; case 'H': if (!varson(*(n - 1)) && (!vowel(*(n - 1)) || vowel(*(n + 1)))) { *Metaph++ = 'H'; } break; case 'K': if (*(n - 1) != 'C') *Metaph++ = 'K'; break; case 'P': *Metaph++ = ('H' == *(n + 1)) ? 'F' : 'P'; break; case 'Q': *Metaph++ = 'K'; break; case 'S': *Metaph++ = ('H' == *(n + 1) || ('I' == *(n + 1) && ('O' == *(n + 2) || 'A' == *(n + 2)))) ? 'X' : 'S'; break; case 'T': if ('I' == *(n + 1) && ('O' == *(n + 2) || 'A' == *(n + 2))) { *Metaph++ = 'X'; } else if ('H' == *(n + 1)) *Metaph++ = 'O'; else if (*(n + 1) != 'C' || *(n + 2) != 'H') *Metaph++ = 'T'; break; case 'V': *Metaph++ = 'F'; break; case 'W': case 'Y': if (vowel(*(n + 1))) *Metaph++ = *n; break; case 'X': if (n == n_start) *Metaph++ = 'S'; else { *Metaph++ = 'K'; KSflag = 1; } break; case 'Z': *Metaph++ = 'S'; break; } } } *Metaph = '\0'; free(ntrans); return strlen(metaph_start); } ================================================ FILE: src/shp2sqlite/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 # Filenames with extension as determined by the OS SHP2SQLITE=shp2sqlite LIBLWGEOM=../liblwgeom/liblwgeom.a # iconv flags ICONV_LDFLAGS=-lc all: $(SHP2SQLITE) $(LIBLWGEOM): make -C ../liblwgeom $(SHP2SQLITE): shpopen.o dbfopen.o getopt.o shp2sqlite.o $(LIBLWGEOM) $(CC) -I/usr/include $(CFLAGS) $^ $(ICONV_LDFLAGS) -lm -o $@ install: all @cp $(SHP2SQLITE) ../../build/ clean: @rm -f *.o $(SHP2SQLITE) ================================================ FILE: src/shp2sqlite/Makefile.macosx ================================================ # ********************************************************************** # * $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. # * # ********************************************************************** # # This Makefile was modified by theduckylittle to build on Mac OS/X # with Iconv installed using brew. YMMV using ports and fink. # CC=gcc CFLAGS=-g -O2 -fPIC -DPIC -Wall -Wmissing-prototypes # Filenames with extension as determined by the OS SHP2SQLITE=shp2sqlite LIBLWGEOM=../liblwgeom/liblwgeom.a # iconv flags ICONV_LDFLAGS=-I/usr/local/Cellar/libiconv/1.14/include/ -L/usr/local/Cellar/libiconv/1.14/lib/ ICONV_LDFLAGS=-liconv -lc all: $(SHP2SQLITE) $(LIBLWGEOM): make -C ../liblwgeom $(SHP2SQLITE): shpopen.o dbfopen.o getopt.o shp2sqlite.o $(LIBLWGEOM) $(CC) $(CFLAGS) $(ICONV_LDFLAGS) -lm $^ -o $@ install: all @cp $(SHP2SQLITE) ../../build/ clean: @rm -f *.o $(SHP2SQLITE) ================================================ FILE: src/shp2sqlite/Makefile.nix ================================================ # ********************************************************************** # * $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. # * # ********************************************************************** CFLAGS=-g -O2 -fPIC -DPIC -Wall -Wmissing-prototypes # Filenames with extension as determined by the OS SHP2SQLITE=shp2sqlite LIBLWGEOM=../liblwgeom/liblwgeom.a # iconv flags ICONV_LDFLAGS=-lc all: $(SHP2SQLITE) $(LIBLWGEOM): make -C ../liblwgeom $(SHP2SQLITE): shpopen.o dbfopen.o getopt.o shp2sqlite.o $(LIBLWGEOM) $(CC) $(CFLAGS) $^ $(ICONV_LDFLAGS) -lm -o $@ install: all @cp $(SHP2SQLITE) ../../bin clean: @rm -f *.o $(SHP2SQLITE) ================================================ FILE: src/shp2sqlite/Makefile.redhat ================================================ # ********************************************************************** # * $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 # Filenames with extension as determined by the OS SHP2SQLITE=shp2sqlite LIBLWGEOM=../liblwgeom/liblwgeom.a # iconv flags ICONV_LDFLAGS=-lc all: $(SHP2SQLITE) $(LIBLWGEOM): make -C ../liblwgeom $(SHP2SQLITE): shpopen.o dbfopen.o getopt.o shp2sqlite.o $(LIBLWGEOM) $(CC) $(CFLAGS) $^ $(ICONV_LDFLAGS) -lm -o $@ install: all @cp $(SHP2SQLITE) ../../bin clean: @rm -f *.o $(SHP2SQLITE) ================================================ FILE: src/shp2sqlite/dbfopen.c ================================================ /****************************************************************************** * $Id: dbfopen.c 3750 2009-02-19 21:12:22Z pramsey $ * * Project: Shapelib * Purpose: Implementation of .dbf access API documented in dbf_api.html. * Author: Frank Warmerdam, warmerdam@pobox.com * ****************************************************************************** * Copyright (c) 1999, Frank Warmerdam * * This software is available under the following "MIT Style" license, * or at the option of the licensee under the LGPL (see LICENSE.LGPL). This * option is discussed in more detail in shapelib.html. * * -- * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. ****************************************************************************** * * $Log$ * Revision 1.8 2006/01/16 10:42:57 strk * Added support for Bool and Date DBF<=>PGIS mapping * * Revision 1.7 2006/01/09 16:40:16 strk * ISO C90 comments, signedness mismatch fixes * * Revision 1.6 2003/12/01 20:52:00 strk * shapelib put in sync with gdal cvs * * Revision 1.52 2003/07/08 15:20:03 warmerda * avoid warnings about downcasting to unsigned char * * Revision 1.51 2003/07/08 13:50:15 warmerda * DBFIsAttributeNULL check for pszValue==NULL - bug 360 * * Revision 1.50 2003/04/21 18:58:25 warmerda * ensure current record is flushed at same time as header is updated * * Revision 1.49 2003/04/21 18:30:37 warmerda * added header write/update public methods * * Revision 1.48 2003/03/10 14:51:27 warmerda * DBFWrite* calls now return FALSE if they have to truncate * * Revision 1.47 2002/11/20 03:32:22 warmerda * Ensure field name in DBFGetFieldIndex() is properly terminated. * * Revision 1.46 2002/10/09 13:10:21 warmerda * Added check that width is positive. * * Revision 1.45 2002/09/29 00:00:08 warmerda * added FTLogical and logical attribute read/write calls * * Revision 1.44 2002/05/07 13:46:11 warmerda * Added DBFWriteAttributeDirectly(). * * Revision 1.43 2002/02/13 19:39:21 warmerda * Fix casting issues in DBFCloneEmpty(). * * Revision 1.42 2002/01/15 14:36:07 warmerda * updated email address * * Revision 1.41 2002/01/15 14:31:49 warmerda * compute rather than copying nHeaderLength in DBFCloneEmpty() * * Revision 1.40 2002/01/09 04:32:35 warmerda * fixed to read correct amount of header * * Revision 1.39 2001/12/11 22:41:03 warmerda * improve io related error checking when reading header * * Revision 1.38 2001/11/28 16:07:31 warmerda * Cleanup to avoid compiler warnings as suggested by Richard Hash. * * Revision 1.37 2001/07/04 05:18:09 warmerda * do last fix properly * * Revision 1.36 2001/07/04 05:16:09 warmerda * fixed fieldname comparison in DBFGetFieldIndex * * Revision 1.35 2001/06/22 02:10:06 warmerda * fixed NULL shape support with help from Jim Matthews * * Revision 1.33 2001/05/31 19:20:13 warmerda * added DBFGetFieldIndex() * * Revision 1.32 2001/05/31 18:15:40 warmerda * Added support for NULL fields in DBF files * * Revision 1.31 2001/05/23 13:36:52 warmerda * added use of SHPAPI_CALL * * Revision 1.30 2000/12/05 14:43:38 warmerda * DBReadAttribute() white space trimming bug fix * * Revision 1.29 2000/10/05 14:36:44 warmerda * fix bug with writing very wide numeric fields * * Revision 1.28 2000/09/25 14:18:07 warmerda * Added some casts of strlen() return result to fix warnings on some * systems, as submitted by Daniel. * * Revision 1.27 2000/09/25 14:15:51 warmerda * added DBFGetNativeFieldType() * * Revision 1.26 2000/07/07 13:39:45 warmerda * removed unused variables, and added system include files * * Revision 1.25 2000/05/29 18:19:13 warmerda * avoid use of uchar, and adding casting fix * * Revision 1.24 2000/05/23 13:38:27 warmerda * Added error checks on return results of fread() and fseek(). * * Revision 1.23 2000/05/23 13:25:49 warmerda * Avoid crashing if field or record are out of range in dbfread*attribute(). * * Revision 1.22 1999/12/15 13:47:24 warmerda * Added stdlib.h to ensure that atof() is prototyped. * * Revision 1.21 1999/12/13 17:25:46 warmerda * Added support for upper case .DBF extention. * * Revision 1.20 1999/11/30 16:32:11 warmerda * Use atof() instead of sscanf(). * * Revision 1.19 1999/11/05 14:12:04 warmerda * updated license terms * * Revision 1.18 1999/07/27 00:53:28 warmerda * ensure that whole old field value clear on write of string * * Revision 1.1 1999/07/05 18:58:07 warmerda * New * * Revision 1.17 1999/06/11 19:14:12 warmerda * Fixed some memory leaks. * * Revision 1.16 1999/06/11 19:04:11 warmerda * Remoted some unused variables. * * Revision 1.15 1999/05/11 03:19:28 warmerda * added new Tuple api, and improved extension handling - add from candrsn * * Revision 1.14 1999/05/04 15:01:48 warmerda * Added 'F' support. * * Revision 1.13 1999/03/23 17:38:59 warmerda * DBFAddField() now actually does return the new field number, or -1 if * it fails. * * Revision 1.12 1999/03/06 02:54:46 warmerda * Added logic to convert shapefile name to dbf filename in DBFOpen() * for convenience. * * Revision 1.11 1998/12/31 15:30:34 warmerda * Improved the interchangability of numeric and string attributes. Add * white space trimming option for attributes. * * Revision 1.10 1998/12/03 16:36:44 warmerda * Use r+b instead of rb+ for binary access. * * Revision 1.9 1998/12/03 15:34:23 warmerda * Updated copyright message. * * Revision 1.8 1997/12/04 15:40:15 warmerda * Added newline character after field definitions. * * Revision 1.7 1997/03/06 14:02:10 warmerda * Ensure bUpdated is initialized. * * Revision 1.6 1996/02/12 04:54:41 warmerda * Ensure that DBFWriteAttribute() returns TRUE if it succeeds. * * Revision 1.5 1995/10/21 03:15:12 warmerda * Changed to use binary file access, and ensure that the * field name field is zero filled, and limited to 10 chars. * * Revision 1.4 1995/08/24 18:10:42 warmerda * Added use of SfRealloc() to avoid pre-ANSI realloc() functions such * as on the Sun. * * Revision 1.3 1995/08/04 03:15:16 warmerda * Fixed up header. * * Revision 1.2 1995/08/04 03:14:43 warmerda * Added header. */ #include "shapefil.h" #include #include #include #include #ifndef FALSE # define FALSE 0 # define TRUE 1 #endif static int nStringFieldLen = 0; static char * pszStringField = NULL; /************************************************************************/ /* SfRealloc() */ /* */ /* A realloc cover function that will access a NULL pointer as */ /* a valid input. */ /************************************************************************/ static void * SfRealloc( void * pMem, int nNewSize ) { if( pMem == NULL ) return( (void *) malloc(nNewSize) ); else return( (void *) realloc(pMem,nNewSize) ); } /************************************************************************/ /* DBFWriteHeader() */ /* */ /* This is called to write out the file header, and field */ /* descriptions before writing any actual data records. This */ /* also computes all the DBFDataSet field offset/size/decimals */ /* and so forth values. */ /************************************************************************/ static void DBFWriteHeader(DBFHandle psDBF) { unsigned char abyHeader[XBASE_FLDHDR_SZ]; int i; if( !psDBF->bNoHeader ) return; psDBF->bNoHeader = FALSE; /* -------------------------------------------------------------------- */ /* Initialize the file header information. */ /* -------------------------------------------------------------------- */ for( i = 0; i < XBASE_FLDHDR_SZ; i++ ) abyHeader[i] = 0; abyHeader[0] = 0x03; /* memo field? - just copying */ /* write out a dummy date */ abyHeader[1] = 95; /* YY */ abyHeader[2] = 7; /* MM */ abyHeader[3] = 26; /* DD */ /* record count preset at zero */ abyHeader[8] = (unsigned char) (psDBF->nHeaderLength % 256); abyHeader[9] = (unsigned char) (psDBF->nHeaderLength / 256); abyHeader[10] = (unsigned char) (psDBF->nRecordLength % 256); abyHeader[11] = (unsigned char) (psDBF->nRecordLength / 256); /* -------------------------------------------------------------------- */ /* Write the initial 32 byte file header, and all the field */ /* descriptions. */ /* -------------------------------------------------------------------- */ fseek( psDBF->fp, 0, 0 ); fwrite( abyHeader, XBASE_FLDHDR_SZ, 1, psDBF->fp ); fwrite( psDBF->pszHeader, XBASE_FLDHDR_SZ, psDBF->nFields, psDBF->fp ); /* -------------------------------------------------------------------- */ /* Write out the newline character if there is room for it. */ /* -------------------------------------------------------------------- */ if( psDBF->nHeaderLength > 32*psDBF->nFields + 32 ) { char cNewline; cNewline = 0x0d; fwrite( &cNewline, 1, 1, psDBF->fp ); } } /************************************************************************/ /* DBFFlushRecord() */ /* */ /* Write out the current record if there is one. */ /************************************************************************/ static void DBFFlushRecord( DBFHandle psDBF ) { int nRecordOffset; if( psDBF->bCurrentRecordModified && psDBF->nCurrentRecord > -1 ) { psDBF->bCurrentRecordModified = FALSE; nRecordOffset = psDBF->nRecordLength * psDBF->nCurrentRecord + psDBF->nHeaderLength; fseek( psDBF->fp, nRecordOffset, 0 ); fwrite( psDBF->pszCurrentRecord, psDBF->nRecordLength, 1, psDBF->fp ); } } /************************************************************************/ /* DBFUpdateHeader() */ /************************************************************************/ void SHPAPI_CALL DBFUpdateHeader( DBFHandle psDBF ) { unsigned char abyFileHeader[32]; if( psDBF->bNoHeader ) DBFWriteHeader( psDBF ); DBFFlushRecord( psDBF ); fseek( psDBF->fp, 0, 0 ); fread( abyFileHeader, 32, 1, psDBF->fp ); abyFileHeader[4] = (unsigned char) (psDBF->nRecords % 256); abyFileHeader[5] = (unsigned char) ((psDBF->nRecords/256) % 256); abyFileHeader[6] = (unsigned char) ((psDBF->nRecords/(256*256)) % 256); abyFileHeader[7] = (unsigned char) ((psDBF->nRecords/(256*256*256)) % 256); fseek( psDBF->fp, 0, 0 ); fwrite( abyFileHeader, 32, 1, psDBF->fp ); fflush( psDBF->fp ); } /************************************************************************/ /* DBFOpen() */ /* */ /* Open a .dbf file. */ /************************************************************************/ DBFHandle SHPAPI_CALL DBFOpen( const char * pszFilename, const char * pszAccess ) { DBFHandle psDBF; unsigned char *pabyBuf; int nFields, nHeadLen, nRecLen, iField, i; char *pszBasename, *pszFullname; /* -------------------------------------------------------------------- */ /* We only allow the access strings "rb" and "r+". */ /* -------------------------------------------------------------------- */ if( strcmp(pszAccess,"r") != 0 && strcmp(pszAccess,"r+") != 0 && strcmp(pszAccess,"rb") != 0 && strcmp(pszAccess,"rb+") != 0 && strcmp(pszAccess,"r+b") != 0 ) return( NULL ); if( strcmp(pszAccess,"r") == 0 ) pszAccess = "rb"; if( strcmp(pszAccess,"r+") == 0 ) pszAccess = "rb+"; /* -------------------------------------------------------------------- */ /* Compute the base (layer) name. If there is any extension */ /* on the passed in filename we will strip it off. */ /* -------------------------------------------------------------------- */ pszBasename = (char *) malloc(strlen(pszFilename)+5); strcpy( pszBasename, pszFilename ); for( i = strlen(pszBasename)-1; i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/' && pszBasename[i] != '\\'; i-- ) {} if( pszBasename[i] == '.' ) pszBasename[i] = '\0'; pszFullname = (char *) malloc(strlen(pszBasename) + 5); sprintf( pszFullname, "%s.dbf", pszBasename ); psDBF = (DBFHandle) calloc( 1, sizeof(DBFInfo) ); psDBF->fp = fopen( pszFullname, pszAccess ); if( psDBF->fp == NULL ) { sprintf( pszFullname, "%s.DBF", pszBasename ); psDBF->fp = fopen(pszFullname, pszAccess ); } free( pszBasename ); free( pszFullname ); if( psDBF->fp == NULL ) { free( psDBF ); return( NULL ); } psDBF->bNoHeader = FALSE; psDBF->nCurrentRecord = -1; psDBF->bCurrentRecordModified = FALSE; /* -------------------------------------------------------------------- */ /* Read Table Header info */ /* -------------------------------------------------------------------- */ pabyBuf = (unsigned char *) malloc(500); if( fread( pabyBuf, 32, 1, psDBF->fp ) != 1 ) { fclose( psDBF->fp ); free( pabyBuf ); free( psDBF ); return NULL; } psDBF->nRecords = pabyBuf[4] + pabyBuf[5]*256 + pabyBuf[6]*256*256 + pabyBuf[7]*256*256*256; psDBF->nHeaderLength = nHeadLen = pabyBuf[8] + pabyBuf[9]*256; psDBF->nRecordLength = nRecLen = pabyBuf[10] + pabyBuf[11]*256; psDBF->nFields = nFields = (nHeadLen - 32) / 32; psDBF->pszCurrentRecord = (char *) malloc(nRecLen); /* -------------------------------------------------------------------- */ /* Read in Field Definitions */ /* -------------------------------------------------------------------- */ pabyBuf = (unsigned char *) SfRealloc(pabyBuf,nHeadLen); psDBF->pszHeader = (char *) pabyBuf; fseek( psDBF->fp, 32, 0 ); if( fread( pabyBuf, nHeadLen-32, 1, psDBF->fp ) != 1 ) { fclose( psDBF->fp ); free( pabyBuf ); free( psDBF ); return NULL; } psDBF->panFieldOffset = (int *) malloc(sizeof(int) * nFields); psDBF->panFieldSize = (int *) malloc(sizeof(int) * nFields); psDBF->panFieldDecimals = (int *) malloc(sizeof(int) * nFields); psDBF->pachFieldType = (char *) malloc(sizeof(char) * nFields); for( iField = 0; iField < nFields; iField++ ) { unsigned char *pabyFInfo; pabyFInfo = pabyBuf+iField*32; if( pabyFInfo[11] == 'N' || pabyFInfo[11] == 'F' ) { psDBF->panFieldSize[iField] = pabyFInfo[16]; psDBF->panFieldDecimals[iField] = pabyFInfo[17]; } else { psDBF->panFieldSize[iField] = pabyFInfo[16] + pabyFInfo[17]*256; psDBF->panFieldDecimals[iField] = 0; } psDBF->pachFieldType[iField] = (char) pabyFInfo[11]; if( iField == 0 ) psDBF->panFieldOffset[iField] = 1; else psDBF->panFieldOffset[iField] = psDBF->panFieldOffset[iField-1] + psDBF->panFieldSize[iField-1]; } return( psDBF ); } /************************************************************************/ /* DBFClose() */ /************************************************************************/ void SHPAPI_CALL DBFClose(DBFHandle psDBF) { static char eof = 0x1a; char eof_test; /* -------------------------------------------------------------------- */ /* Write out header if not already written. */ /* -------------------------------------------------------------------- */ if( psDBF->bNoHeader ) DBFWriteHeader( psDBF ); DBFFlushRecord( psDBF ); /* -------------------------------------------------------------------- */ /* Update last access date, and number of records if we have */ /* write access. */ /* -------------------------------------------------------------------- */ if( psDBF->bUpdated ) DBFUpdateHeader( psDBF ); /* -------------------------------------------------------------------- */ /* Add the DBF end-of-file marker after the last record. */ /* -------------------------------------------------------------------- */ fseek(psDBF->fp, -1, SEEK_END); fread(&eof_test, 1, 1, psDBF->fp); if( eof_test != 0x1a ) /* no EOF exists, so write one */ { fseek(psDBF->fp, 0, SEEK_END); fwrite(&eof, 1, 1, psDBF->fp); } /* -------------------------------------------------------------------- */ /* Close, and free resources. */ /* -------------------------------------------------------------------- */ fclose( psDBF->fp ); if( psDBF->panFieldOffset != NULL ) { free( psDBF->panFieldOffset ); free( psDBF->panFieldSize ); free( psDBF->panFieldDecimals ); free( psDBF->pachFieldType ); } free( psDBF->pszHeader ); free( psDBF->pszCurrentRecord ); free( psDBF ); if( pszStringField != NULL ) { free( pszStringField ); pszStringField = NULL; nStringFieldLen = 0; } } /************************************************************************/ /* DBFCreate() */ /* */ /* Create a new .dbf file. */ /************************************************************************/ DBFHandle SHPAPI_CALL DBFCreate( const char * pszFilename ) { DBFHandle psDBF; FILE *fp; char *pszFullname, *pszBasename; int i; /* -------------------------------------------------------------------- */ /* Compute the base (layer) name. If there is any extension */ /* on the passed in filename we will strip it off. */ /* -------------------------------------------------------------------- */ pszBasename = (char *) malloc(strlen(pszFilename)+5); strcpy( pszBasename, pszFilename ); for( i = strlen(pszBasename)-1; i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/' && pszBasename[i] != '\\'; i-- ) {} if( pszBasename[i] == '.' ) pszBasename[i] = '\0'; pszFullname = (char *) malloc(strlen(pszBasename) + 5); sprintf( pszFullname, "%s.dbf", pszBasename ); free( pszBasename ); /* -------------------------------------------------------------------- */ /* Create the file. */ /* -------------------------------------------------------------------- */ fp = fopen( pszFullname, "wb" ); if( fp == NULL ) return( NULL ); fputc( 0, fp ); fclose( fp ); fp = fopen( pszFullname, "rb+" ); if( fp == NULL ) return( NULL ); free( pszFullname ); /* -------------------------------------------------------------------- */ /* Create the info structure. */ /* -------------------------------------------------------------------- */ psDBF = (DBFHandle) malloc(sizeof(DBFInfo)); psDBF->fp = fp; psDBF->nRecords = 0; psDBF->nFields = 0; psDBF->nRecordLength = 1; psDBF->nHeaderLength = 33; psDBF->bUpdated = FALSE; psDBF->panFieldOffset = NULL; psDBF->panFieldSize = NULL; psDBF->panFieldDecimals = NULL; psDBF->pachFieldType = NULL; psDBF->pszHeader = NULL; psDBF->nCurrentRecord = -1; psDBF->bCurrentRecordModified = FALSE; psDBF->pszCurrentRecord = NULL; psDBF->bNoHeader = TRUE; return( psDBF ); } /************************************************************************/ /* DBFAddField() */ /* */ /* Add a field to a newly created .dbf file before any records */ /* are written. */ /************************************************************************/ int SHPAPI_CALL DBFAddField(DBFHandle psDBF, const char * pszFieldName, DBFFieldType eType, int nWidth, int nDecimals ) { char *pszFInfo; int i; /* -------------------------------------------------------------------- */ /* Do some checking to ensure we can add records to this file. */ /* -------------------------------------------------------------------- */ if( psDBF->nRecords > 0 ) return( -1 ); if( !psDBF->bNoHeader ) return( -1 ); if( eType != FTDouble && nDecimals != 0 ) return( -1 ); if( nWidth < 1 ) return -1; /* -------------------------------------------------------------------- */ /* SfRealloc all the arrays larger to hold the additional field */ /* information. */ /* -------------------------------------------------------------------- */ psDBF->nFields++; psDBF->panFieldOffset = (int *) SfRealloc( psDBF->panFieldOffset, sizeof(int) * psDBF->nFields ); psDBF->panFieldSize = (int *) SfRealloc( psDBF->panFieldSize, sizeof(int) * psDBF->nFields ); psDBF->panFieldDecimals = (int *) SfRealloc( psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields ); psDBF->pachFieldType = (char *) SfRealloc( psDBF->pachFieldType, sizeof(char) * psDBF->nFields ); /* -------------------------------------------------------------------- */ /* Assign the new field information fields. */ /* -------------------------------------------------------------------- */ psDBF->panFieldOffset[psDBF->nFields-1] = psDBF->nRecordLength; psDBF->nRecordLength += nWidth; psDBF->panFieldSize[psDBF->nFields-1] = nWidth; psDBF->panFieldDecimals[psDBF->nFields-1] = nDecimals; if( eType == FTLogical ) psDBF->pachFieldType[psDBF->nFields-1] = 'L'; else if( eType == FTString ) psDBF->pachFieldType[psDBF->nFields-1] = 'C'; else if( eType == FTDate ) psDBF->pachFieldType[psDBF->nFields-1] = 'D'; else psDBF->pachFieldType[psDBF->nFields-1] = 'N'; /* -------------------------------------------------------------------- */ /* Extend the required header information. */ /* -------------------------------------------------------------------- */ psDBF->nHeaderLength += 32; psDBF->bUpdated = FALSE; psDBF->pszHeader = (char *) SfRealloc(psDBF->pszHeader,psDBF->nFields*32); pszFInfo = psDBF->pszHeader + 32 * (psDBF->nFields-1); for( i = 0; i < 32; i++ ) pszFInfo[i] = '\0'; if( (int) strlen(pszFieldName) < 10 ) strncpy( pszFInfo, pszFieldName, strlen(pszFieldName)); else strncpy( pszFInfo, pszFieldName, 10); pszFInfo[11] = psDBF->pachFieldType[psDBF->nFields-1]; if( eType == FTString ) { pszFInfo[16] = (unsigned char) (nWidth % 256); pszFInfo[17] = (unsigned char) (nWidth / 256); } else { pszFInfo[16] = (unsigned char) nWidth; pszFInfo[17] = (unsigned char) nDecimals; } /* -------------------------------------------------------------------- */ /* Make the current record buffer appropriately larger. */ /* -------------------------------------------------------------------- */ psDBF->pszCurrentRecord = (char *) SfRealloc(psDBF->pszCurrentRecord, psDBF->nRecordLength); return( psDBF->nFields-1 ); } /************************************************************************/ /* DBFReadSetup() */ /* */ /* Prep a record for reading. */ /************************************************************************/ int DBFReadSetup(DBFHandle psDBF, int hEntity) { int nRecordOffset; /* -------------------------------------------------------------------- */ /* Verify selection. */ /* -------------------------------------------------------------------- */ if( hEntity < 0 || hEntity >= psDBF->nRecords ) return( 0 ); /* -------------------------------------------------------------------- */ /* Have we read the record? */ /* -------------------------------------------------------------------- */ if( psDBF->nCurrentRecord != hEntity ) { DBFFlushRecord( psDBF ); nRecordOffset = psDBF->nRecordLength * hEntity + psDBF->nHeaderLength; if( fseek( psDBF->fp, nRecordOffset, 0 ) != 0 ) { fprintf( stderr, "fseek(%d) failed on DBF file.\n", nRecordOffset ); return 0; } if( fread( psDBF->pszCurrentRecord, psDBF->nRecordLength, 1, psDBF->fp ) != 1 ) { fprintf( stderr, "fread(%d) failed on DBF file.\n", psDBF->nRecordLength ); return 0; } psDBF->nCurrentRecord = hEntity; } return 1; } /************************************************************************/ /* DBFReadDeleted() */ /* */ /* Read whether a record is deleted. */ /************************************************************************/ int DBFReadDeleted(DBFHandle psDBF, int hEntity) { unsigned char *pabyRec; if( ! DBFReadSetup( psDBF, hEntity) ) return 0; /* get reference to current record */ pabyRec = (unsigned char *) psDBF->pszCurrentRecord; /* 0x20 => not deleted, 0x24 => deleted */ return *pabyRec == 0x20 ? 0 : 1; } /************************************************************************/ /* DBFReadAttribute() */ /* */ /* Read one of the attribute fields of a record. */ /************************************************************************/ static void *DBFReadAttribute(DBFHandle psDBF, int hEntity, int iField, char chReqType ) { unsigned char *pabyRec; void *pReturnField = NULL; static double dDoubleField; /* -------------------------------------------------------------------- */ /* Verify selection. */ /* -------------------------------------------------------------------- */ if( iField < 0 || iField >= psDBF->nFields ) return( NULL ); if( ! DBFReadSetup( psDBF, hEntity) ) return( NULL ); /* get reference to current record */ pabyRec = (unsigned char *) psDBF->pszCurrentRecord; /* -------------------------------------------------------------------- */ /* Ensure our field buffer is large enough to hold this buffer. */ /* -------------------------------------------------------------------- */ if( psDBF->panFieldSize[iField]+1 > nStringFieldLen ) { nStringFieldLen = psDBF->panFieldSize[iField]*2 + 10; pszStringField = (char *) SfRealloc(pszStringField,nStringFieldLen); } /* -------------------------------------------------------------------- */ /* Extract the requested field. */ /* -------------------------------------------------------------------- */ strncpy( pszStringField, ((const char *) pabyRec) + psDBF->panFieldOffset[iField], psDBF->panFieldSize[iField] ); pszStringField[psDBF->panFieldSize[iField]] = '\0'; pReturnField = pszStringField; /* -------------------------------------------------------------------- */ /* Decode the field. */ /* -------------------------------------------------------------------- */ if( chReqType == 'N' ) { dDoubleField = atof(pszStringField); pReturnField = &dDoubleField; } /* -------------------------------------------------------------------- */ /* Should we trim white space off the string attribute value? */ /* -------------------------------------------------------------------- */ #ifdef TRIM_DBF_WHITESPACE else { char *pchSrc, *pchDst; pchDst = pchSrc = pszStringField; while( *pchSrc == ' ' ) pchSrc++; while( *pchSrc != '\0' ) *(pchDst++) = *(pchSrc++); *pchDst = '\0'; while( pchDst != pszStringField && *(--pchDst) == ' ' ) *pchDst = '\0'; } #endif return( pReturnField ); } /************************************************************************/ /* DBFReadIntAttribute() */ /* */ /* Read an integer attribute. */ /************************************************************************/ int SHPAPI_CALL DBFReadIntegerAttribute( DBFHandle psDBF, int iRecord, int iField ) { double *pdValue; pdValue = (double *) DBFReadAttribute( psDBF, iRecord, iField, 'N' ); if( pdValue == NULL ) return 0; else return( (int) *pdValue ); } /************************************************************************/ /* DBFReadDoubleAttribute() */ /* */ /* Read a double attribute. */ /************************************************************************/ double SHPAPI_CALL DBFReadDoubleAttribute( DBFHandle psDBF, int iRecord, int iField ) { double *pdValue; pdValue = (double *) DBFReadAttribute( psDBF, iRecord, iField, 'N' ); if( pdValue == NULL ) return 0.0; else return( *pdValue ); } /************************************************************************/ /* DBFReadStringAttribute() */ /* */ /* Read a string attribute. */ /************************************************************************/ const char SHPAPI_CALL1(*) DBFReadStringAttribute( DBFHandle psDBF, int iRecord, int iField ) { return( (const char *) DBFReadAttribute( psDBF, iRecord, iField, 'C' ) ); } /************************************************************************/ /* DBFReadLogicalAttribute() */ /* */ /* Read a logical attribute. */ /************************************************************************/ const char SHPAPI_CALL1(*) DBFReadLogicalAttribute( DBFHandle psDBF, int iRecord, int iField ) { return( (const char *) DBFReadAttribute( psDBF, iRecord, iField, 'L' ) ); } /************************************************************************/ /* DBFIsAttributeNULL() */ /* */ /* Return TRUE if value for field is NULL. */ /* */ /* Contributed by Jim Matthews. */ /************************************************************************/ int SHPAPI_CALL DBFIsAttributeNULL( DBFHandle psDBF, int iRecord, int iField ) { const char *pszValue; pszValue = DBFReadStringAttribute( psDBF, iRecord, iField ); if( pszValue == NULL ) return TRUE; switch(psDBF->pachFieldType[iField]) { case 'N': case 'F': /* NULL numeric fields have value "****************" */ return pszValue[0] == '*'; case 'D': /* NULL date fields have value "00000000" */ return (strncmp(pszValue,"00000000",8) == 0 || strlen(pszValue) == 0); case 'L': /* NULL boolean fields have value "?" */ return pszValue[0] == '?'; default: /* empty string fields are considered NULL */ return strlen(pszValue) == 0; } } /************************************************************************/ /* DBFGetFieldCount() */ /* */ /* Return the number of fields in this table. */ /************************************************************************/ int SHPAPI_CALL DBFGetFieldCount( DBFHandle psDBF ) { return( psDBF->nFields ); } /************************************************************************/ /* DBFGetRecordCount() */ /* */ /* Return the number of records in this table. */ /************************************************************************/ int SHPAPI_CALL DBFGetRecordCount( DBFHandle psDBF ) { return( psDBF->nRecords ); } /************************************************************************/ /* DBFGetFieldInfo() */ /* */ /* Return any requested information about the field. */ /************************************************************************/ DBFFieldType SHPAPI_CALL DBFGetFieldInfo( DBFHandle psDBF, int iField, char * pszFieldName, int * pnWidth, int * pnDecimals ) { if( iField < 0 || iField >= psDBF->nFields ) return( FTInvalid ); if( pnWidth != NULL ) *pnWidth = psDBF->panFieldSize[iField]; if( pnDecimals != NULL ) *pnDecimals = psDBF->panFieldDecimals[iField]; if( pszFieldName != NULL ) { int i; strncpy( pszFieldName, (char *) psDBF->pszHeader+iField*32, 11 ); pszFieldName[11] = '\0'; for( i = 10; i > 0 && pszFieldName[i] == ' '; i-- ) pszFieldName[i] = '\0'; } if ( psDBF->pachFieldType[iField] == 'L' ) return( FTLogical); else if( psDBF->pachFieldType[iField] == 'D' ) return ( FTDate ); else if( psDBF->pachFieldType[iField] == 'N' || psDBF->pachFieldType[iField] == 'F' ) { if( psDBF->panFieldDecimals[iField] > 0 ) return( FTDouble ); else return( FTInteger ); } else { return( FTString ); } } /************************************************************************/ /* DBFWriteAttribute() */ /* */ /* Write an attribute record to the file. */ /************************************************************************/ static int DBFWriteAttribute(DBFHandle psDBF, int hEntity, int iField, void * pValue ) { int nRecordOffset, i, j, nRetResult = TRUE; unsigned char *pabyRec; char szSField[400], szFormat[20]; /* -------------------------------------------------------------------- */ /* Is this a valid record? */ /* -------------------------------------------------------------------- */ if( hEntity < 0 || hEntity > psDBF->nRecords ) return( FALSE ); if( psDBF->bNoHeader ) DBFWriteHeader(psDBF); /* -------------------------------------------------------------------- */ /* Is this a brand new record? */ /* -------------------------------------------------------------------- */ if( hEntity == psDBF->nRecords ) { DBFFlushRecord( psDBF ); psDBF->nRecords++; for( i = 0; i < psDBF->nRecordLength; i++ ) psDBF->pszCurrentRecord[i] = ' '; psDBF->nCurrentRecord = hEntity; } /* -------------------------------------------------------------------- */ /* Is this an existing record, but different than the last one */ /* we accessed? */ /* -------------------------------------------------------------------- */ if( psDBF->nCurrentRecord != hEntity ) { DBFFlushRecord( psDBF ); nRecordOffset = psDBF->nRecordLength * hEntity + psDBF->nHeaderLength; fseek( psDBF->fp, nRecordOffset, 0 ); fread( psDBF->pszCurrentRecord, psDBF->nRecordLength, 1, psDBF->fp ); psDBF->nCurrentRecord = hEntity; } pabyRec = (unsigned char *) psDBF->pszCurrentRecord; psDBF->bCurrentRecordModified = TRUE; psDBF->bUpdated = TRUE; /* -------------------------------------------------------------------- */ /* Translate NULL value to valid DBF file representation. */ /* */ /* Contributed by Jim Matthews. */ /* -------------------------------------------------------------------- */ if( pValue == NULL ) { switch(psDBF->pachFieldType[iField]) { case 'N': case 'F': /* NULL numeric fields have value "****************" */ memset( (char *) (pabyRec+psDBF->panFieldOffset[iField]), '*', psDBF->panFieldSize[iField] ); break; case 'D': /* NULL date fields have value "00000000" */ memset( (char *) (pabyRec+psDBF->panFieldOffset[iField]), '0', psDBF->panFieldSize[iField] ); break; case 'L': /* NULL boolean fields have value "?" */ memset( (char *) (pabyRec+psDBF->panFieldOffset[iField]), '?', psDBF->panFieldSize[iField] ); break; default: /* empty string fields are considered NULL */ memset( (char *) (pabyRec+psDBF->panFieldOffset[iField]), '\0', psDBF->panFieldSize[iField] ); break; } return TRUE; } /* -------------------------------------------------------------------- */ /* Assign all the record fields. */ /* -------------------------------------------------------------------- */ switch( psDBF->pachFieldType[iField] ) { case 'D': case 'N': case 'F': if( psDBF->panFieldDecimals[iField] == 0 ) { int nWidth = psDBF->panFieldSize[iField]; if( sizeof(szSField)-2 < nWidth ) nWidth = sizeof(szSField)-2; sprintf( szFormat, "%%%dd", nWidth ); sprintf(szSField, szFormat, (int) *((double *) pValue) ); if( (int)strlen(szSField) > psDBF->panFieldSize[iField] ) { szSField[psDBF->panFieldSize[iField]] = '\0'; nRetResult = FALSE; } strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]), szSField, strlen(szSField) ); } else { int nWidth = psDBF->panFieldSize[iField]; if( sizeof(szSField)-2 < nWidth ) nWidth = sizeof(szSField)-2; sprintf( szFormat, "%%%d.%df", nWidth, psDBF->panFieldDecimals[iField] ); sprintf(szSField, szFormat, *((double *) pValue) ); if( (int) strlen(szSField) > psDBF->panFieldSize[iField] ) { szSField[psDBF->panFieldSize[iField]] = '\0'; nRetResult = FALSE; } strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]), szSField, strlen(szSField) ); } break; case 'L': if (psDBF->panFieldSize[iField] >= 1 && (*(char*)pValue == 'F' || *(char*)pValue == 'T')) *(pabyRec+psDBF->panFieldOffset[iField]) = *(char*)pValue; break; default: if( (int) strlen((char *) pValue) > psDBF->panFieldSize[iField] ) { j = psDBF->panFieldSize[iField]; nRetResult = FALSE; } else { memset( pabyRec+psDBF->panFieldOffset[iField], ' ', psDBF->panFieldSize[iField] ); j = strlen((char *) pValue); } strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]), (char *) pValue, j ); break; } return( nRetResult ); } /************************************************************************/ /* DBFWriteAttributeDirectly() */ /* */ /* Write an attribute record to the file, but without any */ /* reformatting based on type. The provided buffer is written */ /* as is to the field position in the record. */ /************************************************************************/ int DBFWriteAttributeDirectly(DBFHandle psDBF, int hEntity, int iField, const void * pValue ) { int nRecordOffset, i, j; unsigned char *pabyRec; /* -------------------------------------------------------------------- */ /* Is this a valid record? */ /* -------------------------------------------------------------------- */ if( hEntity < 0 || hEntity > psDBF->nRecords ) return( FALSE ); if( psDBF->bNoHeader ) DBFWriteHeader(psDBF); /* -------------------------------------------------------------------- */ /* Is this a brand new record? */ /* -------------------------------------------------------------------- */ if( hEntity == psDBF->nRecords ) { DBFFlushRecord( psDBF ); psDBF->nRecords++; for( i = 0; i < psDBF->nRecordLength; i++ ) psDBF->pszCurrentRecord[i] = ' '; psDBF->nCurrentRecord = hEntity; } /* -------------------------------------------------------------------- */ /* Is this an existing record, but different than the last one */ /* we accessed? */ /* -------------------------------------------------------------------- */ if( psDBF->nCurrentRecord != hEntity ) { DBFFlushRecord( psDBF ); nRecordOffset = psDBF->nRecordLength * hEntity + psDBF->nHeaderLength; fseek( psDBF->fp, nRecordOffset, 0 ); fread( psDBF->pszCurrentRecord, psDBF->nRecordLength, 1, psDBF->fp ); psDBF->nCurrentRecord = hEntity; } pabyRec = (unsigned char *) psDBF->pszCurrentRecord; /* -------------------------------------------------------------------- */ /* Assign all the record fields. */ /* -------------------------------------------------------------------- */ if( (int)strlen((char *) pValue) > psDBF->panFieldSize[iField] ) j = psDBF->panFieldSize[iField]; else { memset( pabyRec+psDBF->panFieldOffset[iField], ' ', psDBF->panFieldSize[iField] ); j = strlen((char *) pValue); } strncpy((char *) (pabyRec+psDBF->panFieldOffset[iField]), (char *) pValue, j ); psDBF->bCurrentRecordModified = TRUE; psDBF->bUpdated = TRUE; return( TRUE ); } /************************************************************************/ /* DBFWriteDoubleAttribute() */ /* */ /* Write a double attribute. */ /************************************************************************/ int SHPAPI_CALL DBFWriteDoubleAttribute( DBFHandle psDBF, int iRecord, int iField, double dValue ) { return( DBFWriteAttribute( psDBF, iRecord, iField, (void *) &dValue ) ); } /************************************************************************/ /* DBFWriteIntegerAttribute() */ /* */ /* Write a integer attribute. */ /************************************************************************/ int SHPAPI_CALL DBFWriteIntegerAttribute( DBFHandle psDBF, int iRecord, int iField, int nValue ) { double dValue = nValue; return( DBFWriteAttribute( psDBF, iRecord, iField, (void *) &dValue ) ); } /************************************************************************/ /* DBFWriteStringAttribute() */ /* */ /* Write a string attribute. */ /************************************************************************/ int SHPAPI_CALL DBFWriteStringAttribute( DBFHandle psDBF, int iRecord, int iField, const char * pszValue ) { return( DBFWriteAttribute( psDBF, iRecord, iField, (void *) pszValue ) ); } /************************************************************************/ /* DBFWriteNULLAttribute() */ /* */ /* Write a string attribute. */ /************************************************************************/ int SHPAPI_CALL DBFWriteNULLAttribute( DBFHandle psDBF, int iRecord, int iField ) { return( DBFWriteAttribute( psDBF, iRecord, iField, NULL ) ); } /************************************************************************/ /* DBFWriteLogicalAttribute() */ /* */ /* Write a logical attribute. */ /************************************************************************/ int SHPAPI_CALL DBFWriteLogicalAttribute( DBFHandle psDBF, int iRecord, int iField, const char lValue) { return( DBFWriteAttribute( psDBF, iRecord, iField, (void *) (&lValue) ) ); } /************************************************************************/ /* DBFWriteTuple() */ /* */ /* Write an attribute record to the file. */ /************************************************************************/ int SHPAPI_CALL DBFWriteTuple(DBFHandle psDBF, int hEntity, void * pRawTuple ) { int nRecordOffset, i; unsigned char *pabyRec; /* -------------------------------------------------------------------- */ /* Is this a valid record? */ /* -------------------------------------------------------------------- */ if( hEntity < 0 || hEntity > psDBF->nRecords ) return( FALSE ); if( psDBF->bNoHeader ) DBFWriteHeader(psDBF); /* -------------------------------------------------------------------- */ /* Is this a brand new record? */ /* -------------------------------------------------------------------- */ if( hEntity == psDBF->nRecords ) { DBFFlushRecord( psDBF ); psDBF->nRecords++; for( i = 0; i < psDBF->nRecordLength; i++ ) psDBF->pszCurrentRecord[i] = ' '; psDBF->nCurrentRecord = hEntity; } /* -------------------------------------------------------------------- */ /* Is this an existing record, but different than the last one */ /* we accessed? */ /* -------------------------------------------------------------------- */ if( psDBF->nCurrentRecord != hEntity ) { DBFFlushRecord( psDBF ); nRecordOffset = psDBF->nRecordLength * hEntity + psDBF->nHeaderLength; fseek( psDBF->fp, nRecordOffset, 0 ); fread( psDBF->pszCurrentRecord, psDBF->nRecordLength, 1, psDBF->fp ); psDBF->nCurrentRecord = hEntity; } pabyRec = (unsigned char *) psDBF->pszCurrentRecord; memcpy ( pabyRec, pRawTuple, psDBF->nRecordLength ); psDBF->bCurrentRecordModified = TRUE; psDBF->bUpdated = TRUE; return( TRUE ); } /************************************************************************/ /* DBFReadTuple() */ /* */ /* Read one of the attribute fields of a record. */ /************************************************************************/ const char SHPAPI_CALL1(*) DBFReadTuple(DBFHandle psDBF, int hEntity ) { int nRecordOffset; unsigned char *pabyRec; static char *pReturnTuple = NULL; static int nTupleLen = 0; /* -------------------------------------------------------------------- */ /* Have we read the record? */ /* -------------------------------------------------------------------- */ if( hEntity < 0 || hEntity >= psDBF->nRecords ) return( NULL ); if( psDBF->nCurrentRecord != hEntity ) { DBFFlushRecord( psDBF ); nRecordOffset = psDBF->nRecordLength * hEntity + psDBF->nHeaderLength; fseek( psDBF->fp, nRecordOffset, 0 ); fread( psDBF->pszCurrentRecord, psDBF->nRecordLength, 1, psDBF->fp ); psDBF->nCurrentRecord = hEntity; } pabyRec = (unsigned char *) psDBF->pszCurrentRecord; if ( nTupleLen < psDBF->nRecordLength) { nTupleLen = psDBF->nRecordLength; pReturnTuple = (char *) SfRealloc(pReturnTuple, psDBF->nRecordLength); } memcpy ( pReturnTuple, pabyRec, psDBF->nRecordLength ); return( pReturnTuple ); } /************************************************************************/ /* DBFCloneEmpty() */ /* */ /* Read one of the attribute fields of a record. */ /************************************************************************/ DBFHandle SHPAPI_CALL DBFCloneEmpty(DBFHandle psDBF, const char * pszFilename ) { DBFHandle newDBF; newDBF = DBFCreate ( pszFilename ); if ( newDBF == NULL ) return ( NULL ); newDBF->pszHeader = (char *) malloc ( 32 * psDBF->nFields ); memcpy ( newDBF->pszHeader, psDBF->pszHeader, 32 * psDBF->nFields ); newDBF->nFields = psDBF->nFields; newDBF->nRecordLength = psDBF->nRecordLength; newDBF->nHeaderLength = 32 * (psDBF->nFields+1); newDBF->panFieldOffset = (int *) malloc ( sizeof(int) * psDBF->nFields ); memcpy ( newDBF->panFieldOffset, psDBF->panFieldOffset, sizeof(int) * psDBF->nFields ); newDBF->panFieldSize = (int *) malloc ( sizeof(int) * psDBF->nFields ); memcpy ( newDBF->panFieldSize, psDBF->panFieldSize, sizeof(int) * psDBF->nFields ); newDBF->panFieldDecimals = (int *) malloc ( sizeof(int) * psDBF->nFields ); memcpy ( newDBF->panFieldDecimals, psDBF->panFieldDecimals, sizeof(int) * psDBF->nFields ); newDBF->pachFieldType = (char *) malloc ( sizeof(int) * psDBF->nFields ); memcpy ( newDBF->pachFieldType, psDBF->pachFieldType, sizeof(int) * psDBF->nFields ); newDBF->bNoHeader = TRUE; newDBF->bUpdated = TRUE; DBFWriteHeader ( newDBF ); DBFClose ( newDBF ); newDBF = DBFOpen ( pszFilename, "rb+" ); return ( newDBF ); } /************************************************************************/ /* DBFGetNativeFieldType() */ /* */ /* Return the DBase field type for the specified field. */ /* */ /* Value can be one of: 'C' (String), 'D' (Date), 'F' (Float), */ /* 'N' (Numeric, with or without decimal), */ /* 'L' (Logical), */ /* 'M' (Memo: 10 digits .DBT block ptr) */ /************************************************************************/ char SHPAPI_CALL DBFGetNativeFieldType( DBFHandle psDBF, int iField ) { if( iField >=0 && iField < psDBF->nFields ) return psDBF->pachFieldType[iField]; return ' '; } /************************************************************************/ /* str_to_upper() */ /************************************************************************/ static void str_to_upper (char *string) { int len; short i = -1; len = strlen (string); while (++i < len) if (isalpha(string[i]) && islower(string[i])) string[i] = (char) toupper ((int)string[i]); } /************************************************************************/ /* DBFGetFieldIndex() */ /* */ /* Get the index number for a field in a .dbf file. */ /* */ /* Contributed by Jim Matthews. */ /************************************************************************/ int SHPAPI_CALL DBFGetFieldIndex(DBFHandle psDBF, const char *pszFieldName) { char name[12], name1[12], name2[12]; int i; strncpy(name1, pszFieldName,11); name1[11] = '\0'; str_to_upper(name1); for( i = 0; i < DBFGetFieldCount(psDBF); i++ ) { DBFGetFieldInfo( psDBF, i, name, NULL, NULL ); strncpy(name2,name,11); str_to_upper(name2); if(!strncmp(name1,name2,10)) return(i); } return(-1); } ================================================ FILE: src/shp2sqlite/getopt.c ================================================ /* This version of `getopt' appears to the caller like standard Unix `getopt' but it behaves differently for the user, since it allows the user to intersperse the options with the other arguments. As `getopt' works, it permutes the elements of ARGV so that, when it is done, all the options precede everything else. Thus all application programs are extended to handle flexible argument order. Setting the environment variable POSIXLY_CORRECT disables permutation. Then the behavior is completely standard. GNU application programs can use a third alternative mode in which they can distinguish the relative order of options and other arguments. */ #include #include "getopt.h" /* For communication from `getopt' to the caller. When `getopt' finds an option that takes an argument, the argument value is returned here. Also, when `ordering' is RETURN_IN_ORDER, each non-option ARGV-element is returned here. */ char *optarg = 0; /* Index in ARGV of the next element to be scanned. This is used for communication to and from the caller and for communication between successive calls to `getopt'. On entry to `getopt', zero means this is the first call; initialize. When `getopt' returns EOF, this is the index of the first of the non-option elements that the caller should itself scan. Otherwise, `optind' communicates from one call to the next how much of ARGV has been scanned so far. */ /* XXX 1003.2 says this must be 1 before any call. */ int optind = 0; /* The next char to be scanned in the option-element in which the last option character we returned was found. This allows us to pick up the scan where we left off. If this is zero, or a null string, it means resume the scan by advancing to the next ARGV-element. */ static char *nextchar; /* Callers store zero here to inhibit the error message for unrecognized options. */ int opterr = 1; /* Set to an option character which was unrecognized. This must be initialized on some systems to avoid linking in the system's own getopt implementation. */ #define BAD_OPTION '\0' int optopt = BAD_OPTION; /* Describe how to deal with options that follow non-option ARGV-elements. If the caller did not specify anything, the default is REQUIRE_ORDER if the environment variable POSIXLY_CORRECT is defined, PERMUTE otherwise. REQUIRE_ORDER means don't recognize them as options; stop option processing when the first non-option is seen. This is what Unix does. This mode of operation is selected by either setting the environment variable POSIXLY_CORRECT, or using `+' as the first character of the list of option characters. PERMUTE is the default. We permute the contents of ARGV as we scan, so that eventually all the non-options are at the end. This allows options to be given in any order, even with programs that were not written to expect this. RETURN_IN_ORDER is an option available to programs that were written to expect options and other ARGV-elements in any order and that care about the ordering of the two. We describe each non-option ARGV-element as if it were the argument of an option with character code 1. Using `-' as the first character of the list of option characters selects this mode of operation. The special argument `--' forces an end of option-scanning regardless of the value of `ordering'. In the case of RETURN_IN_ORDER, only `--' can cause `getopt' to return EOF with `optind' != ARGC. */ static enum { REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER } ordering; #ifdef __GNU_LIBRARY__ /* We want to avoid inclusion of string.h with non-GNU libraries because there are many ways it can cause trouble. On some systems, it contains special magic macros that don't work in GCC. */ #include #define my_index strchr #define my_strlen strlen #else /* Avoid depending on library functions or files whose names are inconsistent. */ #if __STDC__ || defined(PROTO) extern int strcmp (const char *s1, const char *s2); extern int strncmp(const char *s1, const char *s2, int n); static int my_strlen(const char *s); static char *my_index (const char *str, int chr); #endif static int my_strlen (str) const char *str; { int n = 0; while (*str++) n++; return n; } static char * my_index (str, chr) const char *str; int chr; { while (*str) { if (*str == chr) return (char *) str; str++; } return 0; } #endif /* GNU C library. */ extern char *getenv(const char *name); /* Handle permutation of arguments. */ /* Describe the part of ARGV that contains non-options that have been skipped. `first_nonopt' is the index in ARGV of the first of them; `last_nonopt' is the index after the last of them. */ static int first_nonopt; static int last_nonopt; /* Exchange two adjacent subsequences of ARGV. One subsequence is elements [first_nonopt,last_nonopt) which contains all the non-options that have been skipped so far. The other is elements [last_nonopt,optind), which contains all the options processed since those non-options were skipped. `first_nonopt' and `last_nonopt' are relocated so that they describe the new indices of the non-options in ARGV after they are moved. To perform the swap, we first reverse the order of all elements. So all options now come before all non options, but they are in the wrong order. So we put back the options and non options in original order by reversing them again. For example: original input: a b c -x -y reverse all: -y -x c b a reverse options: -x -y c b a reverse non options: -x -y a b c */ #if __STDC__ || defined(PROTO) static void exchange (char **argv); #endif static void exchange (argv) char **argv; { char *temp, **first, **last; /* Reverse all the elements [first_nonopt, optind) */ first = &argv[first_nonopt]; last = &argv[optind-1]; while (first < last) { temp = *first; *first = *last; *last = temp; first++; last--; } /* Put back the options in order */ first = &argv[first_nonopt]; first_nonopt += (optind - last_nonopt); last = &argv[first_nonopt - 1]; while (first < last) { temp = *first; *first = *last; *last = temp; first++; last--; } /* Put back the non options in order */ first = &argv[first_nonopt]; last_nonopt = optind; last = &argv[last_nonopt-1]; while (first < last) { temp = *first; *first = *last; *last = temp; first++; last--; } } /* Scan elements of ARGV (whose length is ARGC) for option characters given in OPTSTRING. If an element of ARGV starts with '-', and is not exactly "-" or "--", then it is an option element. The characters of this element (aside from the initial '-') are option characters. If `getopt' is called repeatedly, it returns successively each of the option characters from each of the option elements. If `getopt' finds another option character, it returns that character, updating `optind' and `nextchar' so that the next call to `getopt' can resume the scan with the following option character or ARGV-element. If there are no more option characters, `getopt' returns `EOF'. Then `optind' is the index in ARGV of the first ARGV-element that is not an option. (The ARGV-elements have been permuted so that those that are not options now come last.) OPTSTRING is a string containing the legitimate option characters. If an option character is seen that is not listed in OPTSTRING, return BAD_OPTION after printing an error message. If you set `opterr' to zero, the error message is suppressed but we still return BAD_OPTION. If a char in OPTSTRING is followed by a colon, that means it wants an arg, so the following text in the same ARGV-element, or the text of the following ARGV-element, is returned in `optarg'. Two colons mean an option that wants an optional arg; if there is text in the current ARGV-element, it is returned in `optarg', otherwise `optarg' is set to zero. If OPTSTRING starts with `-' or `+', it requests different methods of handling the non-option ARGV-elements. See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. Long-named options begin with `--' instead of `-'. Their names may be abbreviated as long as the abbreviation is unique or is an exact match for some defined option. If they have an argument, it follows the option name in the same ARGV-element, separated from the option name by a `=', or else the in next ARGV-element. When `getopt' finds a long-named option, it returns 0 if that option's `flag' field is nonzero, the value of the option's `val' field if the `flag' field is zero. The elements of ARGV aren't really const, because we permute them. But we pretend they're const in the prototype to be compatible with other systems. LONGOPTS is a vector of `struct option' terminated by an element containing a name which is zero. LONGIND returns the index in LONGOPT of the long-named option found. It is only valid when a long-named option has been found by the most recent call. If LONG_ONLY is nonzero, '-' as well as '--' can introduce long-named options. */ int _pgis_getopt_internal (argc, argv, optstring, longopts, longind, long_only) int argc; char *const *argv; const char *optstring; const struct option *longopts; int *longind; int long_only; { int option_index; optarg = 0; /* Initialize the internal data when the first call is made. Start processing options with ARGV-element 1 (since ARGV-element 0 is the program name); the sequence of previously skipped non-option ARGV-elements is empty. */ if (optind == 0) { first_nonopt = last_nonopt = optind = 1; nextchar = NULL; /* Determine how to handle the ordering of options and nonoptions. */ if (optstring[0] == '-') { ordering = RETURN_IN_ORDER; ++optstring; } else if (optstring[0] == '+') { ordering = REQUIRE_ORDER; ++optstring; } else if (getenv ("POSIXLY_CORRECT") != NULL) ordering = REQUIRE_ORDER; else ordering = PERMUTE; } if (nextchar == NULL || *nextchar == '\0') { if (ordering == PERMUTE) { /* If we have just processed some options following some non-options, exchange them so that the options come first. */ if (first_nonopt != last_nonopt && last_nonopt != optind) exchange ((char **) argv); else if (last_nonopt != optind) first_nonopt = optind; /* Now skip any additional non-options and extend the range of non-options previously skipped. */ while (optind < argc && (argv[optind][0] != '-' || argv[optind][1] == '\0') #ifdef GETOPT_COMPAT && (longopts == NULL || argv[optind][0] != '+' || argv[optind][1] == '\0') #endif /* GETOPT_COMPAT */ ) optind++; last_nonopt = optind; } /* Special ARGV-element `--' means premature end of options. Skip it like a null option, then exchange with previous non-options as if it were an option, then skip everything else like a non-option. */ if (optind != argc && !strcmp (argv[optind], "--")) { optind++; if (first_nonopt != last_nonopt && last_nonopt != optind) exchange ((char **) argv); else if (first_nonopt == last_nonopt) first_nonopt = optind; last_nonopt = argc; optind = argc; } /* If we have done all the ARGV-elements, stop the scan and back over any non-options that we skipped and permuted. */ if (optind == argc) { /* Set the next-arg-index to point at the non-options that we previously skipped, so the caller will digest them. */ if (first_nonopt != last_nonopt) optind = first_nonopt; return EOF; } /* If we have come to a non-option and did not permute it, either stop the scan or describe it to the caller and pass it by. */ if ((argv[optind][0] != '-' || argv[optind][1] == '\0') #ifdef GETOPT_COMPAT && (longopts == NULL || argv[optind][0] != '+' || argv[optind][1] == '\0') #endif /* GETOPT_COMPAT */ ) { if (ordering == REQUIRE_ORDER) return EOF; optarg = argv[optind++]; return 1; } /* We have found another option-ARGV-element. Start decoding its characters. */ nextchar = (argv[optind] + 1 + (longopts != NULL && argv[optind][1] == '-')); } if (longopts != NULL && ((argv[optind][0] == '-' && (argv[optind][1] == '-' || long_only)) #ifdef GETOPT_COMPAT || argv[optind][0] == '+' #endif /* GETOPT_COMPAT */ )) { const struct option *p; char *s = nextchar; int exact = 0; int ambig = 0; const struct option *pfound = NULL; int indfound = 0; while (*s && *s != '=') s++; /* Test all options for either exact match or abbreviated matches. */ for (p = longopts, option_index = 0; p->name; p++, option_index++) if (!strncmp (p->name, nextchar, s - nextchar)) { if (s - nextchar == my_strlen (p->name)) { /* Exact match found. */ pfound = p; indfound = option_index; exact = 1; break; } else if (pfound == NULL) { /* First nonexact match found. */ pfound = p; indfound = option_index; } else /* Second nonexact match found. */ ambig = 1; } if (ambig && !exact) { if (opterr) fprintf (stderr, "%s: option `%s' is ambiguous\n", argv[0], argv[optind]); nextchar += my_strlen (nextchar); optind++; return BAD_OPTION; } if (pfound != NULL) { option_index = indfound; optind++; if (*s) { /* Don't test has_arg with >, because some C compilers don't allow it to be used on enums. */ if (pfound->has_arg) optarg = s + 1; else { if (opterr) { if (argv[optind - 1][1] == '-') /* --option */ fprintf (stderr, "%s: option `--%s' doesn't allow an argument\n", argv[0], pfound->name); else /* +option or -option */ fprintf (stderr, "%s: option `%c%s' doesn't allow an argument\n", argv[0], argv[optind - 1][0], pfound->name); } nextchar += my_strlen (nextchar); return BAD_OPTION; } } else if (pfound->has_arg == 1) { if (optind < argc) optarg = argv[optind++]; else { if (opterr) fprintf (stderr, "%s: option `%s' requires an argument\n", argv[0], argv[optind - 1]); nextchar += my_strlen (nextchar); return optstring[0] == ':' ? ':' : BAD_OPTION; } } nextchar += my_strlen (nextchar); if (longind != NULL) *longind = option_index; if (pfound->flag) { *(pfound->flag) = pfound->val; return 0; } return pfound->val; } /* Can't find it as a long option. If this is not getopt_long_only, or the option starts with '--' or is not a valid short option, then it's an error. Otherwise interpret it as a short option. */ if (!long_only || argv[optind][1] == '-' #ifdef GETOPT_COMPAT || argv[optind][0] == '+' #endif /* GETOPT_COMPAT */ || my_index (optstring, *nextchar) == NULL) { if (opterr) { if (argv[optind][1] == '-') /* --option */ fprintf (stderr, "%s: unrecognized option `--%s'\n", argv[0], nextchar); else /* +option or -option */ fprintf (stderr, "%s: unrecognized option `%c%s'\n", argv[0], argv[optind][0], nextchar); } nextchar = (char *) ""; optind++; return BAD_OPTION; } } /* Look at and handle the next option-character. */ { char c = *nextchar++; char *temp = my_index (optstring, c); /* Increment `optind' when we start to process its last character. */ if (*nextchar == '\0') ++optind; if (temp == NULL || c == ':') { if (opterr) { #if 0 if (c < 040 || c >= 0177) fprintf (stderr, "%s: unrecognized option, character code 0%o\n", argv[0], c); else fprintf (stderr, "%s: unrecognized option `-%c'\n", argv[0], c); #else /* 1003.2 specifies the format of this message. */ fprintf (stderr, "%s: illegal option -- %c\n", argv[0], c); #endif } optopt = c; return BAD_OPTION; } if (temp[1] == ':') { if (temp[2] == ':') { /* This is an option that accepts an argument optionally. */ if (*nextchar != '\0') { optarg = nextchar; optind++; } else optarg = 0; nextchar = NULL; } else { /* This is an option that requires an argument. */ if (*nextchar != '\0') { optarg = nextchar; /* If we end this ARGV-element by taking the rest as an arg, we must advance to the next element now. */ optind++; } else if (optind == argc) { if (opterr) { #if 0 fprintf (stderr, "%s: option `-%c' requires an argument\n", argv[0], c); #else /* 1003.2 specifies the format of this message. */ fprintf (stderr, "%s: option requires an argument -- %c\n", argv[0], c); #endif } optopt = c; if (optstring[0] == ':') c = ':'; else c = BAD_OPTION; } else /* We already incremented `optind' once; increment it again when taking next ARGV-elt as argument. */ optarg = argv[optind++]; nextchar = NULL; } } return c; } } int pgis_getopt (argc, argv, optstring) int argc; char *const *argv; const char *optstring; { return _pgis_getopt_internal (argc, argv, optstring, (const struct option *) 0, (int *) 0, 0); } int pgis_getopt_long (argc, argv, options, long_options, opt_index) int argc; char *const *argv; const char *options; const struct option *long_options; int *opt_index; { return _pgis_getopt_internal (argc, argv, options, long_options, opt_index, 0); } /* #endif _LIBC or not __GNU_LIBRARY__. */ #ifdef TEST /* Compile with -DTEST to make an executable for use in testing the above definition of `getopt'. */ int main (argc, argv) int argc; char **argv; { int c; int digit_optind = 0; while (1) { int this_option_optind = optind ? optind : 1; c = pgis_getopt (argc, argv, "abc:d:0123456789"); if (c == EOF) break; switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (digit_optind != 0 && digit_optind != this_option_optind) printf ("digits occur in two different argv-elements.\n"); digit_optind = this_option_optind; printf ("option %c\n", c); break; case 'a': printf ("option a\n"); break; case 'b': printf ("option b\n"); break; case 'c': printf ("option c with value `%s'\n", optarg); break; case BAD_OPTION: break; default: printf ("?? getopt returned character code 0%o ??\n", c); } } if (optind < argc) { printf ("non-option ARGV-elements: "); while (optind < argc) printf ("%s ", argv[optind++]); printf ("\n"); } exit (0); } #endif /* TEST */ ================================================ FILE: src/shp2sqlite/getopt.h ================================================ /* Declarations for getopt. Copyright (C) 1989, 1990, 1991, 1992, 1993 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef _GETOPT_H #define _GETOPT_H 1 #ifdef __cplusplus extern "C" { #endif /* For communication from `getopt' to the caller. When `getopt' finds an option that takes an argument, the argument value is returned here. Also, when `ordering' is RETURN_IN_ORDER, each non-option ARGV-element is returned here. */ extern char *optarg; /* Index in ARGV of the next element to be scanned. This is used for communication to and from the caller and for communication between successive calls to `getopt'. On entry to `getopt', zero means this is the first call; initialize. When `getopt' returns EOF, this is the index of the first of the non-option elements that the caller should itself scan. Otherwise, `optind' communicates from one call to the next how much of ARGV has been scanned so far. */ extern int optind; /* Callers store zero here to inhibit the error message `getopt' prints for unrecognized options. */ extern int opterr; /* Set to an option character which was unrecognized. */ extern int optopt; /* Describe the long-named options requested by the application. The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector of `struct option' terminated by an element containing a name which is zero. The field `has_arg' is: no_argument (or 0) if the option does not take an argument, required_argument (or 1) if the option requires an argument, optional_argument (or 2) if the option takes an optional argument. If the field `flag' is not NULL, it points to a variable that is set to the value given in the field `val' when the option is found, but left unchanged if the option is not found. To have a long-named option do something other than set an `int' to a compiled-in constant, such as set a value from `optarg', set the option's `flag' field to zero and its `val' field to a nonzero value (the equivalent single-letter option character, if there is one). For long options that have a zero `flag' field, `getopt' returns the contents of the `val' field. */ struct option { #if __STDC__ const char *name; #else char *name; #endif /* has_arg can't be an enum because some compilers complain about type mismatches in all the code that assumes it is an int. */ int has_arg; int *flag; int val; }; /* Names for the values of the `has_arg' field of `struct option'. */ #define no_argument 0 #define required_argument 1 #define optional_argument 2 #if __STDC__ || defined(PROTO) #if defined(__GNU_LIBRARY__) /* Many other libraries have conflicting prototypes for getopt, with differences in the consts, in stdlib.h. To avoid compilation errors, only prototype getopt for the GNU C library. */ extern int pgis_getopt (int argc, char *const *argv, const char *shortopts); #endif /* not __GNU_LIBRARY__ */ extern int pgis_getopt_long (int argc, char *const *argv, const char *shortopts, const struct option *longopts, int *longind); extern int pgis_getopt_long_only (int argc, char *const *argv, const char *shortopts, const struct option *longopts, int *longind); /* Internal only. Users should not call this directly. */ extern int _pgis_getopt_internal (int argc, char *const *argv, const char *shortopts, const struct option *longopts, int *longind, int long_only); #else /* not __STDC__ */ extern int pgis_getopt (); extern int pgis_getopt_long (); extern int pgis_getopt_long_only (); extern int _pgis_getopt_internal (); #endif /* not __STDC__ */ #ifdef __cplusplus } #endif #endif /* _GETOPT_H */ ================================================ FILE: src/shp2sqlite/shapefil.h ================================================ #ifndef _SHAPEFILE_H_INCLUDED #define _SHAPEFILE_H_INCLUDED /****************************************************************************** * $Id: shapefil.h 2785 2008-05-27 15:08:20Z mcayland $ * * Project: Shapelib * Purpose: Primary include file for Shapelib. * Author: Frank Warmerdam, warmerdam@pobox.com * ****************************************************************************** * Copyright (c) 1999, Frank Warmerdam * * This software is available under the following "MIT Style" license, * or at the option of the licensee under the LGPL (see LICENSE.LGPL). This * option is discussed in more detail in shapelib.html. * * -- * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. ****************************************************************************** * * $Log$ * Revision 1.5 2006/01/16 10:42:58 strk * Added support for Bool and Date DBF<=>PGIS mapping * * Revision 1.4 2003/12/01 20:52:00 strk * shapelib put in sync with gdal cvs * * Revision 1.27 2003/04/21 18:30:37 warmerda * added header write/update public methods * * Revision 1.26 2002/09/29 00:00:08 warmerda * added FTLogical and logical attribute read/write calls * * Revision 1.25 2002/05/07 13:46:30 warmerda * added DBFWriteAttributeDirectly(). * * Revision 1.24 2002/04/10 16:59:54 warmerda * added SHPRewindObject * * Revision 1.23 2002/01/15 14:36:07 warmerda * updated email address * * Revision 1.22 2002/01/15 14:32:00 warmerda * try to improve SHPAPI_CALL docs * * Revision 1.21 2001/11/01 16:29:55 warmerda * move pabyRec into SHPInfo for thread safety * * Revision 1.20 2001/07/20 13:06:02 warmerda * fixed SHPAPI attribute for SHPTreeFindLikelyShapes * * Revision 1.19 2001/05/31 19:20:13 warmerda * added DBFGetFieldIndex() * * Revision 1.18 2001/05/31 18:15:40 warmerda * Added support for NULL fields in DBF files * * Revision 1.17 2001/05/23 13:36:52 warmerda * added use of SHPAPI_CALL * * Revision 1.16 2000/09/25 14:15:59 warmerda * added DBFGetNativeFieldType() * * Revision 1.15 2000/02/16 16:03:51 warmerda * added null shape support * * Revision 1.14 1999/11/05 14:12:05 warmerda * updated license terms * * Revision 1.13 1999/06/02 18:24:21 warmerda * added trimming code * * Revision 1.12 1999/06/02 17:56:12 warmerda * added quad'' subnode support for trees * * Revision 1.11 1999/05/18 19:11:11 warmerda * Added example searching capability * * Revision 1.10 1999/05/18 17:49:38 warmerda * added initial quadtree support * * Revision 1.9 1999/05/11 03:19:28 warmerda * added new Tuple api, and improved extension handling - add from candrsn * * Revision 1.8 1999/03/23 17:22:27 warmerda * Added extern "C" protection for C++ users of shapefil.h. * * Revision 1.7 1998/12/31 15:31:07 warmerda * Added the TRIM_DBF_WHITESPACE and DISABLE_MULTIPATCH_MEASURE options. * * Revision 1.6 1998/12/03 15:48:15 warmerda * Added SHPCalculateExtents(). * * Revision 1.5 1998/11/09 20:57:16 warmerda * Altered SHPGetInfo() call. * * Revision 1.4 1998/11/09 20:19:33 warmerda * Added 3D support, and use of SHPObject. * * Revision 1.3 1995/08/23 02:24:05 warmerda * Added support for reading bounds. * * Revision 1.2 1995/08/04 03:17:39 warmerda * Added header. * */ #include #ifdef USE_DBMALLOC #include #endif #ifdef __cplusplus extern "C" { #endif /************************************************************************/ /* Configuration options. */ /************************************************************************/ /* -------------------------------------------------------------------- */ /* Should the DBFReadStringAttribute() strip leading and */ /* trailing white space? */ /* -------------------------------------------------------------------- */ #define TRIM_DBF_WHITESPACE /* -------------------------------------------------------------------- */ /* Should we write measure values to the Multipatch object? */ /* Reportedly ArcView crashes if we do write it, so for now it */ /* is disabled. */ /* -------------------------------------------------------------------- */ #define DISABLE_MULTIPATCH_MEASURE /* -------------------------------------------------------------------- */ /* SHPAPI_CALL */ /* */ /* The following two macros are present to allow forcing */ /* various calling conventions on the Shapelib API. */ /* */ /* To force __stdcall conventions (needed to call Shapelib */ /* from Visual Basic and/or Dephi I believe) the makefile could */ /* be modified to define: */ /* */ /* /DSHPAPI_CALL=__stdcall */ /* */ /* If it is desired to force export of the Shapelib API without */ /* using the shapelib.def file, use the following definition. */ /* */ /* /DSHAPELIB_DLLEXPORT */ /* */ /* To get both at once it will be necessary to hack this */ /* include file to define: */ /* */ /* #define SHPAPI_CALL __declspec(dllexport) __stdcall */ /* #define SHPAPI_CALL1 __declspec(dllexport) * __stdcall */ /* */ /* The complexity of the situtation is partly caused by the */ /* peculiar requirement of Visual C++ that __stdcall appear */ /* after any "*"'s in the return value of a function while the */ /* __declspec(dllexport) must appear before them. */ /* -------------------------------------------------------------------- */ #ifdef SHAPELIB_DLLEXPORT # define SHPAPI_CALL __declspec(dllexport) # define SHPAPI_CALL1(x) __declspec(dllexport) x #endif #ifndef SHPAPI_CALL # define SHPAPI_CALL #endif #ifndef SHPAPI_CALL1 # define SHPAPI_CALL1(x) x SHPAPI_CALL #endif /************************************************************************/ /* SHP Support. */ /************************************************************************/ typedef struct { FILE *fpSHP; FILE *fpSHX; int nShapeType; /* SHPT_* */ int nFileSize; /* SHP file */ int nRecords; int nMaxRecords; int *panRecOffset; int *panRecSize; double adBoundsMin[4]; double adBoundsMax[4]; int bUpdated; unsigned char *pabyRec; int nBufSize; } SHPInfo; typedef SHPInfo * SHPHandle; /* -------------------------------------------------------------------- */ /* Shape types (nSHPType) */ /* -------------------------------------------------------------------- */ #define SHPT_NULL 0 #define SHPT_POINT 1 #define SHPT_ARC 3 #define SHPT_POLYGON 5 #define SHPT_MULTIPOINT 8 #define SHPT_POINTZ 11 #define SHPT_ARCZ 13 #define SHPT_POLYGONZ 15 #define SHPT_MULTIPOINTZ 18 #define SHPT_POINTM 21 #define SHPT_ARCM 23 #define SHPT_POLYGONM 25 #define SHPT_MULTIPOINTM 28 #define SHPT_MULTIPATCH 31 /* -------------------------------------------------------------------- */ /* Part types - everything but SHPT_MULTIPATCH just uses */ /* SHPP_RING. */ /* -------------------------------------------------------------------- */ #define SHPP_TRISTRIP 0 #define SHPP_TRIFAN 1 #define SHPP_OUTERRING 2 #define SHPP_INNERRING 3 #define SHPP_FIRSTRING 4 #define SHPP_RING 5 /* -------------------------------------------------------------------- */ /* SHPObject - represents on shape (without attributes) read */ /* from the .shp file. */ /* -------------------------------------------------------------------- */ typedef struct { int nSHPType; int nShapeId; /* -1 is unknown/unassigned */ int nParts; int *panPartStart; int *panPartType; int nVertices; double *padfX; double *padfY; double *padfZ; double *padfM; double dfXMin; double dfYMin; double dfZMin; double dfMMin; double dfXMax; double dfYMax; double dfZMax; double dfMMax; } SHPObject; /* -------------------------------------------------------------------- */ /* SHP API Prototypes */ /* -------------------------------------------------------------------- */ SHPHandle SHPAPI_CALL SHPOpen( const char * pszShapeFile, const char * pszAccess ); SHPHandle SHPAPI_CALL SHPCreate( const char * pszShapeFile, int nShapeType ); void SHPAPI_CALL SHPGetInfo( SHPHandle hSHP, int * pnEntities, int * pnShapeType, double * padfMinBound, double * padfMaxBound ); SHPObject SHPAPI_CALL1(*) SHPReadObject( SHPHandle hSHP, int iShape ); int SHPAPI_CALL SHPWriteObject( SHPHandle hSHP, int iShape, SHPObject * psObject ); void SHPAPI_CALL SHPDestroyObject( SHPObject * psObject ); void SHPAPI_CALL SHPComputeExtents( SHPObject * psObject ); SHPObject SHPAPI_CALL1(*) SHPCreateObject( int nSHPType, int nShapeId, int nParts, int * panPartStart, int * panPartType, int nVertices, double * padfX, double * padfY, double * padfZ, double * padfM ); SHPObject SHPAPI_CALL1(*) SHPCreateSimpleObject( int nSHPType, int nVertices, double * padfX, double * padfY, double * padfZ ); int SHPAPI_CALL SHPRewindObject( SHPHandle hSHP, SHPObject * psObject ); void SHPAPI_CALL SHPClose( SHPHandle hSHP ); void SHPAPI_CALL SHPWriteHeader( SHPHandle hSHP ); const char SHPAPI_CALL1(*) SHPTypeName( int nSHPType ); const char SHPAPI_CALL1(*) SHPPartTypeName( int nPartType ); /* -------------------------------------------------------------------- */ /* Shape quadtree indexing API. */ /* -------------------------------------------------------------------- */ /* this can be two or four for binary or quad tree */ #define MAX_SUBNODE 4 typedef struct shape_tree_node { /* region covered by this node */ double adfBoundsMin[4]; double adfBoundsMax[4]; /* list of shapes stored at this node. The papsShapeObj pointers or the whole list can be NULL */ int nShapeCount; int *panShapeIds; SHPObject **papsShapeObj; int nSubNodes; struct shape_tree_node *apsSubNode[MAX_SUBNODE]; } SHPTreeNode; typedef struct { SHPHandle hSHP; int nMaxDepth; int nDimension; SHPTreeNode *psRoot; } SHPTree; SHPTree SHPAPI_CALL1(*) SHPCreateTree( SHPHandle hSHP, int nDimension, int nMaxDepth, double *padfBoundsMin, double *padfBoundsMax ); void SHPAPI_CALL SHPDestroyTree( SHPTree * hTree ); int SHPAPI_CALL SHPWriteTree( SHPTree *hTree, const char * pszFilename ); SHPTree SHPAPI_CALL SHPReadTree( const char * pszFilename ); int SHPAPI_CALL SHPTreeAddObject( SHPTree * hTree, SHPObject * psObject ); int SHPAPI_CALL SHPTreeAddShapeId( SHPTree * hTree, SHPObject * psObject ); int SHPAPI_CALL SHPTreeRemoveShapeId( SHPTree * hTree, int nShapeId ); void SHPAPI_CALL SHPTreeTrimExtraNodes( SHPTree * hTree ); int SHPAPI_CALL1(*) SHPTreeFindLikelyShapes( SHPTree * hTree, double * padfBoundsMin, double * padfBoundsMax, int * ); int SHPAPI_CALL SHPCheckBoundsOverlap( double *, double *, double *, double *, int ); /************************************************************************/ /* DBF Support. */ /************************************************************************/ typedef struct { FILE *fp; int nRecords; int nRecordLength; int nHeaderLength; int nFields; int *panFieldOffset; int *panFieldSize; int *panFieldDecimals; char *pachFieldType; char *pszHeader; int nCurrentRecord; int bCurrentRecordModified; char *pszCurrentRecord; int bNoHeader; int bUpdated; } DBFInfo; typedef DBFInfo * DBFHandle; typedef enum { FTString, FTInteger, FTDouble, FTLogical, FTInvalid, FTDate } DBFFieldType; #define XBASE_FLDHDR_SZ 32 DBFHandle SHPAPI_CALL DBFOpen( const char * pszDBFFile, const char * pszAccess ); DBFHandle SHPAPI_CALL DBFCreate( const char * pszDBFFile ); int SHPAPI_CALL DBFGetFieldCount( DBFHandle psDBF ); int SHPAPI_CALL DBFGetRecordCount( DBFHandle psDBF ); int SHPAPI_CALL DBFAddField( DBFHandle hDBF, const char * pszFieldName, DBFFieldType eType, int nWidth, int nDecimals ); DBFFieldType SHPAPI_CALL DBFGetFieldInfo( DBFHandle psDBF, int iField, char * pszFieldName, int * pnWidth, int * pnDecimals ); int SHPAPI_CALL DBFGetFieldIndex(DBFHandle psDBF, const char *pszFieldName); int SHPAPI_CALL DBFReadIntegerAttribute( DBFHandle hDBF, int iShape, int iField ); double SHPAPI_CALL DBFReadDoubleAttribute( DBFHandle hDBF, int iShape, int iField ); const char SHPAPI_CALL1(*) DBFReadStringAttribute( DBFHandle hDBF, int iShape, int iField ); const char SHPAPI_CALL1(*) DBFReadLogicalAttribute( DBFHandle hDBF, int iShape, int iField ); int SHPAPI_CALL DBFIsAttributeNULL( DBFHandle hDBF, int iShape, int iField ); int SHPAPI_CALL DBFReadSetup(DBFHandle psDBF, int hEntity); int SHPAPI_CALL DBFReadDeleted(DBFHandle psDBF, int hEntity); int SHPAPI_CALL DBFWriteIntegerAttribute( DBFHandle hDBF, int iShape, int iField, int nFieldValue ); int SHPAPI_CALL DBFWriteDoubleAttribute( DBFHandle hDBF, int iShape, int iField, double dFieldValue ); int SHPAPI_CALL DBFWriteStringAttribute( DBFHandle hDBF, int iShape, int iField, const char * pszFieldValue ); int SHPAPI_CALL DBFWriteNULLAttribute( DBFHandle hDBF, int iShape, int iField ); int SHPAPI_CALL DBFWriteLogicalAttribute( DBFHandle hDBF, int iShape, int iField, const char lFieldValue); int SHPAPI_CALL DBFWriteAttributeDirectly(DBFHandle psDBF, int hEntity, int iField, const void * pValue ); const char SHPAPI_CALL1(*) DBFReadTuple(DBFHandle psDBF, int hEntity ); int SHPAPI_CALL DBFWriteTuple(DBFHandle psDBF, int hEntity, void * pRawTuple ); DBFHandle SHPAPI_CALL DBFCloneEmpty(DBFHandle psDBF, const char * pszFilename ); void SHPAPI_CALL DBFClose( DBFHandle hDBF ); void SHPAPI_CALL DBFUpdateHeader( DBFHandle hDBF ); char SHPAPI_CALL DBFGetNativeFieldType( DBFHandle hDBF, int iField ); #ifdef __cplusplus } #endif #endif /* ndef _SHAPEFILE_H_INCLUDED */ ================================================ FILE: src/shp2sqlite/shp2sqlite.c ================================================ /********************************************************************** * $Id: shp2pgsql.c 3623 2009-02-03 07:20:16Z pramsey $ * * PostGIS - Spatial Types for PostgreSQL * http://postgis.refractions.net * Copyright 2001-2003 Refractions Research Inc. * * This is free software; you can redistribute and/or modify it under * the terms of the GNU General Public Licence. See the COPYING file. * ********************************************************************** * Using shapelib 1.2.8, this program reads in shape files and * processes it's contents into a Insert statements which can be * easily piped into a database frontend. * Specifically designed to insert type 'geometry' (a custom * written PostgreSQL type) for the shape files and PostgreSQL * standard types for all attributes of the entity. * * Original Author: Jeff Lounsbury, jeffloun@refractions.net * * Maintainer: Sandro Santilli, strk@refractions.net * **********************************************************************/ #include "../liblwgeom/postgis_config.h" #include "shapefil.h" #include #include #include #include #include #include #include #include #include "getopt.h" #ifdef HAVE_ICONV #include #endif #include "../liblwgeom/liblwgeom.h" #define POINTTYPE 1 #define LINETYPE 2 #define POLYGONTYPE 3 #define MULTIPOINTTYPE 4 #define MULTILINETYPE 5 #define MULTIPOLYGONTYPE 6 #define COLLECTIONTYPE 7 #define WKBZOFFSET 0x80000000 #define WKBMOFFSET 0x40000000 typedef struct {double x, y, z, m;} Point; typedef struct Ring { Point *list; /* list of points */ struct Ring *next; int n; /* number of points in list */ unsigned int linked; /* number of "next" rings */ } Ring; /* Values for null_policy global */ enum { insert_null, skip_null, abort_on_null }; /* globals */ int dump_format = 0; /* 0=insert statements, 1 = dump */ int sqlite_format = 1; /* 0=PostGIS GEOMETRY, 1=SQLite BLOB */ int simple_geometries = 0; /* 0 = MULTILINESTRING/MULTIPOLYGON, 1 = LINESTRING/POLYGON */ int quoteidentifiers = 0; int forceint4 = 0; int createindex = 0; int readshape = 1; char opt = ' '; char *col_names = NULL; char *pgtype; int istypeM = 0; int pgdims; unsigned int wkbtype; char *shp_file = NULL; int hwgeom = 0; /* old (hwgeom) mode */ #ifdef HAVE_ICONV char *encoding=NULL; #endif int null_policy = insert_null; DBFFieldType *types; /* Fields type, width and precision */ SHPHandle hSHPHandle; DBFHandle hDBFHandle; int shpfiletype; SHPObject *obj=NULL; int *widths; int *precisions; char *table=NULL,*schema=NULL,*geom=NULL; int num_fields,num_records,num_entities; char **field_names; int sr_id = 0; /* Prototypes */ int Insert_attributes(DBFHandle hDBFHandle, int row); char *make_good_string(char *str); char *protect_quotes_string(char *str); int PIP( Point P, Point* V, int n ); void *safe_malloc(size_t size); void CreateTable(void); void CreateIndex(void); void usage(char *me, int exitcode, FILE* out); void InsertPoint(void); void InsertMultiPoint(void); void InsertPolygon(void); void InsertLineString(void); void OutputGeometry(char *geometry); int ParseCmdline(int ARGC, char **ARGV); void SetPgType(void); char *dump_ring(Ring *ring); #ifdef HAVE_ICONV char *utf8(const char *fromcode, char *inputbuf); #endif int FindPolygons(SHPObject *obj, Ring ***Out); void ReleasePolygons(Ring **polys, int npolys); void DropTable(char *schema, char *table, char *geom); void GetFieldsSpec(void); void LoadData(void); void OpenShape(void); void LowerCase(char *s); void Cleanup(void); /* static char rcsid[] = "$Id: shp2pgsql.c 3623 2009-02-03 07:20:16Z pramsey $"; */ /* liblwgeom allocator callback - install the defaults (malloc/free/stdout/stderr) */ void lwgeom_init_allocators() { lwgeom_install_default_allocators(); } void *safe_malloc(size_t size) { void *ret = malloc(size); if ( ! ret ) { fprintf(stderr, "Out of virtual memory\n"); exit(1); } return ret; } #define malloc(x) safe_malloc(x) char * make_good_string(char *str) { /* * find all the tabs and make them \s * * 1. find # of tabs * 2. make new string * * we dont escape already escaped tabs */ char *result; char *ptr, *optr; int toescape = 0; size_t size; #ifdef HAVE_ICONV char *utf8str=NULL; if ( encoding ) { utf8str=utf8(encoding, str); if ( ! utf8str ) exit(1); str = utf8str; } #endif ptr = str; while (*ptr) { if ( *ptr == '\t' || *ptr == '\\' ) toescape++; ptr++; } if (toescape == 0) return str; size = ptr-str+toescape+1; result = calloc(1, size); optr=result; ptr=str; while (*ptr) { if ( *ptr == '\t' || *ptr == '\\' ) *optr++='\\'; *optr++=*ptr++; } *optr='\0'; #ifdef HAVE_ICONV if ( encoding ) free(str); #endif return result; } char * protect_quotes_string(char *str) { /* * find all quotes and make them \quotes * find all '\' and make them '\\' * * 1. find # of characters * 2. make new string */ char *result; char *ptr, *optr; int toescape = 0; size_t size; #ifdef HAVE_ICONV char *utf8str=NULL; if ( encoding ) { utf8str=utf8(encoding, str); if ( ! utf8str ) exit(1); str = utf8str; } #endif ptr = str; while (*ptr) { if ( *ptr == '\'' || *ptr == '\\' ) toescape++; ptr++; } if (toescape == 0) return str; size = ptr-str+toescape+1; result = calloc(1, size); optr=result; ptr=str; while (*ptr) { /* SQLite doesn't support backslash escaping in strings */ if ( *ptr == '\\' && !sqlite_format ) *optr++='\\'; if ( *ptr == '\'') *optr++='\''; *optr++=*ptr++; } *optr='\0'; #ifdef HAVE_ICONV if ( encoding ) free(str); #endif return result; } /* * PIP(): crossing number test for a point in a polygon * input: P = a point, * V[] = vertex points of a polygon V[n+1] with V[n]=V[0] * returns: 0 = outside, 1 = inside */ int PIP( Point P, Point* V, int n ) { int cn = 0; /* the crossing number counter */ int i; /* loop through all edges of the polygon */ for (i=0; i P.y)) /* an upward crossing */ || ((V[i].y > P.y) && (V[i+1].y <= P.y))) { /* a downward crossing */ double vt = (float)(P.y - V[i].y) / (V[i+1].y - V[i].y); if (P.x < V[i].x + vt * (V[i+1].x - V[i].x)) /* P.x < intersect */ ++cn; /* a valid crossing of y=P.y right of P.x */ } } return (cn&1); /* 0 if even (out), and 1 if odd (in) */ } /*Insert the attributes from the correct row of dbf file */ int Insert_attributes(DBFHandle hDBFHandle, int row) { int i,num_fields; char val[1024]; char *escval; num_fields = DBFGetFieldCount( hDBFHandle ); for( i = 0; i < num_fields; i++ ) { if(DBFIsAttributeNULL( hDBFHandle, row, i)) { if(dump_format) { printf("\\N"); //printf("\t"); } else { printf("NULL"); //printf(","); } } else /* Attribute NOT NULL */ { switch (types[i]) { case FTInteger: case FTDouble: if ( -1 == snprintf(val, 1024, "%s", DBFReadStringAttribute(hDBFHandle, row, i)) ) { fprintf(stderr, "Warning: field %d name truncated\n", i); val[1023] = '\0'; } /* pg_atoi() does not do this */ if ( val[0] == '\0' ) { val[0] = '0'; val[1] = '\0'; } if ( val[strlen(val)-1] == '.' ) val[strlen(val)-1] = '\0'; break; case FTString: case FTLogical: case FTDate: if ( -1 == snprintf(val, 1024, "%s", DBFReadStringAttribute(hDBFHandle, row, i)) ) { fprintf(stderr, "Warning: field %d name truncated\n", i); val[1023] = '\0'; } break; default: fprintf(stderr, "Error: field %d has invalid or unknown field type (%d)\n", i, types[i]); exit(1); } if (dump_format) { escval = make_good_string(val); printf("%s", escval); //printf("\t"); } else { escval = protect_quotes_string(val); if (sqlite_format) printf("'%s'", escval); else printf("E'%s'", escval); //printf(","); } if ( val != escval ) free(escval); } //only put in delimeter if not last field or a shape will follow if(readshape == 1 || i < (num_fields - 1)) { if (dump_format){ printf("\t"); } else { printf(","); } } } return 1; } /* * main() * see description at the top of this file */ int main (int ARGC, char **ARGV) { /* * Parse command line */ if ( ! ParseCmdline(ARGC, ARGV) ) usage(ARGV[0], 2, stderr); /* * Open shapefile and initialize globals */ OpenShape(); if (readshape == 1){ /* * Compute output geometry type */ SetPgType(); /* fprintf(stderr, "Shapefile type: %s\n", SHPTypeName(shpfiletype)); fprintf(stderr, "Postgis type: %s[%d]\n", pgtype, pgdims); */ } #ifdef HAVE_ICONV if ( encoding ) { printf("SET CLIENT_ENCODING TO UTF8;\n"); } #endif /* defined HAVE_ICONV */ /* * Drop table if requested */ if(opt == 'd') DropTable(schema, table, geom); /* * Get col names and types for table creation * and data load. */ GetFieldsSpec(); printf("BEGIN;\n"); /* * If not in 'append' mode create the spatial table */ if(opt != 'a') CreateTable(); /* * Generate INSERT or COPY lines */ if(opt != 'p') LoadData(); /* * Create GiST index if requested */ if(createindex) CreateIndex(); printf("END;\n"); /* End the last transaction */ return 0; }/*end main() */ void LowerCase(char *s) { int j; for (j=0; jnVertices == 0 ) { fprintf(stderr, "Empty geometries found, aborted.\n"); exit(1); } SHPDestroyObject(obj); } } } else { num_entities = DBFGetRecordCount(hDBFHandle); } } void CreateTable(void) { int j; int field_precision, field_width; DBFFieldType type = -1; const char *pk_type; /* * Create a table for inserting the shapes into with appropriate * columns and types */ if (sqlite_format) pk_type = "integer"; else pk_type = "serial"; if ( schema ) { printf("CREATE TABLE \"%s\".\"%s\" (gid %s PRIMARY KEY", schema, table, pk_type); } else { printf("CREATE TABLE \"%s\" (gid %s PRIMARY KEY", table, pk_type); } for(j=0;j 18 ) { printf ("numeric"); } else { printf ("float8"); } } else if(type == FTLogical) { printf ("boolean"); } else { printf ("Invalid type in DBF file"); } } printf (");\n"); /* Create the geometry column with an addgeometry call */ if (sqlite_format) { /* SQLite doesn't have a GEOMETRY type */ printf("ALTER TABLE %s ADD COLUMN %s blob;", table, geom); } else { if ( schema && readshape == 1 ) { printf("SELECT AddGeometryColumn('%s','%s','%s','%d',", schema, table, geom, sr_id); } else if (readshape == 1) { printf("SELECT AddGeometryColumn('','%s','%s','%d',", table, geom, sr_id); } if (pgtype) { //pgtype will only be set if we are loading geometries printf("'%s',%d);\n", pgtype, pgdims); } } } void CreateIndex(void) { /* * Create gist index */ if ( schema ) { printf("CREATE INDEX \"%s_%s_gist\" ON \"%s\".\"%s\" using gist (\"%s\" gist_geometry_ops);\n", table, geom, schema, table, geom); } else { printf("CREATE INDEX \"%s_%s_gist\" ON \"%s\" using gist (\"%s\" gist_geometry_ops);\n", table, geom, table, geom); } } void LoadData(void) { int j, trans=0; if (dump_format){ if ( schema ) { printf("COPY \"%s\".\"%s\" %s FROM stdin;\n", schema, table, col_names); } else { printf("COPY \"%s\" %s FROM stdin;\n", table, col_names); } } /************************************************************** * * MAIN SHAPE OBJECTS SCAN * **************************************************************/ for (j=0; jnVertices == 0 ) { SHPDestroyObject(obj); continue; } } if (!dump_format) { if ( schema ) { printf("INSERT INTO \"%s\".\"%s\" %s VALUES (", schema, table, col_names); } else { printf("INSERT INTO \"%s\" %s VALUES (", table, col_names); } } Insert_attributes(hDBFHandle,j); if (readshape == 1) { /* ---------- NULL SHAPE ----------------- */ if (obj->nVertices == 0) { if (dump_format) printf("\\N\n"); else printf("NULL);\n"); SHPDestroyObject(obj); continue; } switch (obj->nSHPType) { case SHPT_POLYGON: case SHPT_POLYGONM: case SHPT_POLYGONZ: InsertPolygon(); break; case SHPT_POINT: case SHPT_POINTM: case SHPT_POINTZ: case SHPT_MULTIPOINT: case SHPT_MULTIPOINTM: case SHPT_MULTIPOINTZ: InsertPoint(); break; case SHPT_ARC: case SHPT_ARCM: case SHPT_ARCZ: InsertLineString(); break; default: printf ("\n\n**** Type is NOT SUPPORTED, type id = %d ****\n\n", obj->nSHPType); break; } SHPDestroyObject(obj); } else if (dump_format){ /*close for dbf only dump format */ printf("\n"); } else { /*close for dbf only sql insert format */ printf(");\n"); } } /* END of MAIN SHAPE OBJECT LOOP */ if ((dump_format) ) { printf("\\.\n"); } } void usage(char *me, int exitcode, FILE* out) { /* fprintf(out, "RCSID: %s RELEASE: %s\n", rcsid, POSTGIS_VERSION); */ fprintf(out, "USAGE: %s [] [.]\n", me); fprintf(out, "OPTIONS:\n"); fprintf(out, " -s Set the SRID field. If not specified it defaults to -1.\n"); fprintf(out, " (-d|a|c|p) These are mutually exclusive options:\n"); fprintf(out, " -d Drops the table, then recreates it and populates\n"); fprintf(out, " it with current shape file data.\n"); fprintf(out, " -a Appends shape file into current table, must be\n"); fprintf(out, " exactly the same table schema.\n"); fprintf(out, " -c Creates a new table and populates it, this is the\n"); fprintf(out, " default if you do not specify any options.\n"); fprintf(out, " -p Prepare mode, only creates the table.\n"); fprintf(out, " -g Specify the name of the geometry column\n"); fprintf(out, " (mostly useful in append mode).\n"); fprintf(out, " -i Use int4 type for all integer dbf fields.\n"); fprintf(out, " -S Generate simple geometries instead of MULTI geometries.\n"); fprintf(out, " -w Use wkt format (drops M - drifts coordinates).\n"); #ifdef HAVE_ICONV fprintf(out, " -W Specify the character encoding of Shape's\n"); fprintf(out, " attribute column. (default : \"ASCII\")\n"); #endif fprintf(out, " -N Specify NULL geometries handling policy (insert,skip,abort)\n"); fprintf(out, " -n Only import DBF file.\n"); fprintf(out, " -? Display this help screen\n"); exit (exitcode); } void InsertLineString() { LWCOLLECTION *lwcollection; LWGEOM **lwmultilinestrings; uchar *serialized_lwgeom; LWGEOM_UNPARSER_RESULT lwg_unparser_result; DYNPTARRAY **dpas; POINT4D point4d; int dims = 0, hasz = 0, hasm = 0; int result; int u, v, start_vertex, end_vertex; /* Determine the correct dimensions: note that in hwgeom-compatible mode we cannot use the M coordinate */ if (wkbtype & WKBZOFFSET) hasz = 1; if (!hwgeom) if (wkbtype & WKBMOFFSET) hasm = 1; TYPE_SETZM(dims, hasz, hasm); if (simple_geometries == 1 && obj->nParts > 1) { fprintf(stderr, "We have a Multilinestring with %d parts, can't use -S switch!\n", obj->nParts); exit(1); } /* Allocate memory for our array of LWLINEs and our dynptarrays */ lwmultilinestrings = malloc(sizeof(LWPOINT *) * obj->nParts); dpas = malloc(sizeof(DYNPTARRAY *) * obj->nParts); /* We need an array of pointers to each of our sub-geometries */ for (u = 0; u < obj->nParts; u++) { /* Create a dynptarray containing the line points */ dpas[u] = dynptarray_create(obj->nParts, dims); /* Set the start/end vertices depending upon whether this is a MULTILINESTRING or not */ if ( u == obj->nParts-1 ) end_vertex = obj->nVertices; else end_vertex = obj->panPartStart[u + 1]; start_vertex = obj->panPartStart[u]; for (v = start_vertex; v < end_vertex; v++) { /* Generate the point */ point4d.x = obj->padfX[v]; point4d.y = obj->padfY[v]; if (wkbtype & WKBZOFFSET) point4d.z = obj->padfZ[v]; if (wkbtype & WKBMOFFSET) point4d.m = obj->padfM[v]; dynptarray_addPoint4d(dpas[u], &point4d, 0); } /* Generate the LWLINE */ lwmultilinestrings[u] = lwline_as_lwgeom(lwline_construct(sr_id, NULL, dpas[u]->pa)); } /* If using MULTILINESTRINGs then generate the serialized collection, otherwise just a single LINESTRING */ if (simple_geometries == 0) { lwcollection = lwcollection_construct(MULTILINETYPE, sr_id, NULL, obj->nParts, lwmultilinestrings); serialized_lwgeom = lwgeom_serialize(lwcollection_as_lwgeom(lwcollection)); } else { serialized_lwgeom = lwgeom_serialize(lwmultilinestrings[0]); } if (!hwgeom) result = serialized_lwgeom_to_hexwkb(&lwg_unparser_result, serialized_lwgeom, PARSER_CHECK_ALL, -1); else result = serialized_lwgeom_to_ewkt(&lwg_unparser_result, serialized_lwgeom, PARSER_CHECK_ALL); if (result) { fprintf(stderr, "ERROR: %s\n", lwg_unparser_result.message); exit(1); } OutputGeometry(lwg_unparser_result.wkoutput); /* Free all of the allocated items */ lwfree(lwg_unparser_result.wkoutput); lwfree(serialized_lwgeom); for (u = 0; u < obj->nParts; u++) { lwline_free(lwgeom_as_lwline(lwmultilinestrings[u])); lwfree(dpas[u]); } lwfree(dpas); lwfree(lwmultilinestrings); } int FindPolygons(SHPObject *obj, Ring ***Out) { Ring **Outer; /* Pointers to Outer rings */ int out_index=0; /* Count of Outer rings */ Ring **Inner; /* Pointers to Inner rings */ int in_index=0; /* Count of Inner rings */ int pi; /* part index */ #if POSTGIS_DEBUG_LEVEL > 0 static int call = -1; call++; #endif LWDEBUGF(4, "FindPolygons[%d]: allocated space for %d rings\n", call, obj->nParts); /* Allocate initial memory */ Outer = (Ring**)malloc(sizeof(Ring*)*obj->nParts); Inner = (Ring**)malloc(sizeof(Ring*)*obj->nParts); /* Iterate over rings dividing in Outers and Inners */ for (pi=0; pinParts; pi++) { int vi; /* vertex index */ int vs; /* start index */ int ve; /* end index */ int nv; /* number of vertex */ double area = 0.0; Ring *ring; /* Set start and end vertexes */ if ( pi==obj->nParts-1 ) ve = obj->nVertices; else ve = obj->panPartStart[pi+1]; vs = obj->panPartStart[pi]; /* Compute number of vertexes */ nv = ve-vs; /* Allocate memory for a ring */ ring = (Ring*)malloc(sizeof(Ring)); ring->list = (Point*)malloc(sizeof(Point)*nv); ring->n = nv; ring->next = NULL; ring->linked = 0; /* Iterate over ring vertexes */ for ( vi=vs; vilist[vi-vs].x = obj->padfX[vi]; ring->list[vi-vs].y = obj->padfY[vi]; ring->list[vi-vs].z = obj->padfZ[vi]; ring->list[vi-vs].m = obj->padfM[vi]; area += (obj->padfX[vi] * obj->padfY[vn]) - (obj->padfY[vi] * obj->padfX[vn]); } /* Close the ring with first vertex */ /*ring->list[vi].x = obj->padfX[vs]; */ /*ring->list[vi].y = obj->padfY[vs]; */ /*ring->list[vi].z = obj->padfZ[vs]; */ /*ring->list[vi].m = obj->padfM[vs]; */ /* Clockwise (or single-part). It's an Outer Ring ! */ if(area < 0.0 || obj->nParts ==1) { Outer[out_index] = ring; out_index++; } /* Counterclockwise. It's an Inner Ring ! */ else { Inner[in_index] = ring; in_index++; } } LWDEBUGF(4, "FindPolygons[%d]: found %d Outer, %d Inners\n", call, out_index, in_index); /* Put the inner rings into the list of the outer rings */ /* of which they are within */ for(pi=0; pilist[0].x; pt.y = inner->list[0].y; pt2.x = inner->list[1].x; pt2.y = inner->list[1].y; for(i=0; ilist, Outer[i]->n); if( in || PIP(pt2, Outer[i]->list, Outer[i]->n) ) { outer = Outer[i]; break; } /*fprintf(stderr, "!PIP %s\nOUTE %s\n", dump_ring(inner), dump_ring(Outer[i])); */ } if ( outer ) { outer->linked++; while(outer->next) outer = outer->next; outer->next = inner; } else { /* The ring wasn't within any outer rings, */ /* assume it is a new outer ring. */ LWDEBUGF(4, "FindPolygons[%d]: hole %d is orphan\n", call, pi); Outer[out_index] = inner; out_index++; } } *Out = Outer; free(Inner); return out_index; } void ReleasePolygons(Ring **polys, int npolys) { int pi; /* Release all memory */ for(pi=0; pinext; free(temp->list); free(temp); } } free(polys); } /*This function basically deals with the polygon case. */ /*it sorts the polys in order of outer,inner,inner, so that inners */ /*always come after outers they are within */ void InsertPolygon(void) { Ring **Outer; int polygon_total, ring_total; int pi, vi; // part index and vertex index int u; LWCOLLECTION *lwcollection = NULL; LWGEOM **lwpolygons; uchar *serialized_lwgeom; LWGEOM_UNPARSER_RESULT lwg_unparser_result; LWPOLY *lwpoly; DYNPTARRAY *dpas; POINTARRAY ***pas; POINT4D point4d; int dims = 0, hasz = 0, hasm = 0; int result; /* Determine the correct dimensions: note that in hwgeom-compatible mode we cannot use the M coordinate */ if (wkbtype & WKBZOFFSET) hasz = 1; if (!hwgeom) if (wkbtype & WKBMOFFSET) hasm = 1; TYPE_SETZM(dims, hasz, hasm); polygon_total = FindPolygons(obj, &Outer); if (simple_geometries == 1 && polygon_total != 1) /* We write Non-MULTI geometries, but have several parts: */ { fprintf(stderr, "We have a Multipolygon with %d parts, can't use -S switch!\n", polygon_total); exit(1); } /* Allocate memory for our array of LWPOLYs */ lwpolygons = malloc(sizeof(LWPOLY *) * polygon_total); /* Allocate memory for our POINTARRAY pointers for each polygon */ pas = malloc(sizeof(POINTARRAY **) * polygon_total); /* Cycle through each individual polygon */ for(pi = 0; pi < polygon_total; pi++) { Ring *polyring; int ring_index = 0; /* Firstly count through the total number of rings in this polygon */ ring_total = 0; polyring = Outer[pi]; while (polyring) { ring_total++; polyring = polyring->next; } /* Reserve memory for the POINTARRAYs representing each ring */ pas[pi] = malloc(sizeof(POINTARRAY *) * ring_total); /* Cycle through each ring within the polygon, starting with the outer */ polyring = Outer[pi]; while (polyring) { /* Create a DYNPTARRAY containing the points making up the ring */ dpas = dynptarray_create(polyring->n, dims); for(vi = 0; vi < polyring->n; vi++) { /* Build up a point array of all the points in this ring */ point4d.x = polyring->list[vi].x; point4d.y = polyring->list[vi].y; if (wkbtype & WKBZOFFSET) point4d.z = polyring->list[vi].z; if (wkbtype & WKBMOFFSET) point4d.m = polyring->list[vi].m; dynptarray_addPoint4d(dpas, &point4d, 0); } /* Copy the POINTARRAY pointer from the DYNPTARRAY structure so we can use the LWPOLY constructor */ pas[pi][ring_index] = dpas->pa; /* Free the DYNPTARRAY structure (we don't need this part anymore as we have the reference to the internal POINTARRAY) */ lwfree(dpas); polyring = polyring->next; ring_index++; } /* Generate the LWGEOM */ lwpoly = lwpoly_construct(sr_id, NULL, ring_total, pas[pi]); lwpolygons[pi] = lwpoly_as_lwgeom(lwpoly); } ReleasePolygons(Outer, polygon_total); /* If using MULTIPOLYGONS then generate the serialized collection, otherwise just a single POLYGON */ if (simple_geometries == 0) { lwcollection = lwcollection_construct(MULTIPOLYGONTYPE, sr_id, NULL, polygon_total, lwpolygons); serialized_lwgeom = lwgeom_serialize(lwcollection_as_lwgeom(lwcollection)); } else { serialized_lwgeom = lwgeom_serialize(lwpolygons[0]); } if (!hwgeom) result = serialized_lwgeom_to_hexwkb(&lwg_unparser_result, serialized_lwgeom, PARSER_CHECK_ALL, -1); else result = serialized_lwgeom_to_ewkt(&lwg_unparser_result, serialized_lwgeom, PARSER_CHECK_ALL); if (result) { fprintf(stderr, "ERROR: %s\n", lwg_unparser_result.message); exit(1); } OutputGeometry(lwg_unparser_result.wkoutput); /* Free all of the allocated items */ lwfree(lwg_unparser_result.wkoutput); lwfree(serialized_lwgeom); /* Cycle through each polygon, freeing everything we need... */ for (u = 0; u < polygon_total; u++) lwpoly_free(lwgeom_as_lwpoly(lwpolygons[u])); /* Free the pointer arrays */ lwfree(pas); lwfree(lwpolygons); if (simple_geometries == 0) lwfree(lwcollection); } /* * Insert either a POINT or MULTIPOINT into the output stream */ void InsertPoint(void) { LWCOLLECTION *lwcollection; LWGEOM **lwmultipoints; uchar *serialized_lwgeom; LWGEOM_UNPARSER_RESULT lwg_unparser_result; DYNPTARRAY **dpas; POINT4D point4d; int dims = 0, hasz = 0, hasm = 0; int result; int u; /* Determine the correct dimensions: note that in hwgeom-compatible mode we cannot use the M coordinate */ if (wkbtype & WKBZOFFSET) hasz = 1; if (!hwgeom) if (wkbtype & WKBMOFFSET) hasm = 1; TYPE_SETZM(dims, hasz, hasm); /* Allocate memory for our array of LWPOINTs and our dynptarrays */ lwmultipoints = malloc(sizeof(LWPOINT *) * obj->nVertices); dpas = malloc(sizeof(DYNPTARRAY *) * obj->nVertices); /* We need an array of pointers to each of our sub-geometries */ for (u = 0; u < obj->nVertices; u++) { /* Generate the point */ point4d.x = obj->padfX[u]; point4d.y = obj->padfY[u]; if (wkbtype & WKBZOFFSET) point4d.z = obj->padfZ[u]; if (wkbtype & WKBMOFFSET) point4d.m = obj->padfM[u]; /* Create a dynptarray containing a single point */ dpas[u] = dynptarray_create(1, dims); dynptarray_addPoint4d(dpas[u], &point4d, 0); /* Generate the LWPOINT */ lwmultipoints[u] = lwpoint_as_lwgeom(lwpoint_construct(sr_id, NULL, dpas[u]->pa)); } /* If we have more than 1 vertex then we are working on a MULTIPOINT and so generate a MULTIPOINT rather than a POINT */ if (obj->nVertices > 1) { lwcollection = lwcollection_construct(MULTIPOINTTYPE, sr_id, NULL, obj->nVertices, lwmultipoints); serialized_lwgeom = lwgeom_serialize(lwcollection_as_lwgeom(lwcollection)); } else { serialized_lwgeom = lwgeom_serialize(lwmultipoints[0]); } if (!hwgeom) result = serialized_lwgeom_to_hexwkb(&lwg_unparser_result, serialized_lwgeom, PARSER_CHECK_ALL, -1); else result = serialized_lwgeom_to_ewkt(&lwg_unparser_result, serialized_lwgeom, PARSER_CHECK_ALL); if (result) { fprintf(stderr, "ERROR: %s\n", lwg_unparser_result.message); exit(1); } OutputGeometry(lwg_unparser_result.wkoutput); /* Free all of the allocated items */ lwfree(lwg_unparser_result.wkoutput); lwfree(serialized_lwgeom); for (u = 0; u < obj->nVertices; u++) { lwpoint_free(lwgeom_as_lwpoint(lwmultipoints[u])); lwfree(dpas[u]); } lwfree(dpas); lwfree(lwmultipoints); } void OutputGeometry(char *geometry) { /* This function outputs the specified geometry string (WKB or WKT) formatted * according to whether we have specified dump format or hwgeom format */ if (hwgeom) { if (!dump_format) printf("GeomFromText('"); else { /* Output SRID if relevant */ if (sr_id != 0) printf("SRID=%d;", sr_id); } printf("%s", geometry); if (!dump_format) { printf("'"); /* Output SRID if relevant */ if (sr_id != 0) printf(", %d)", sr_id); printf(");\n"); } else printf("\n"); } else { if (!dump_format) { if (sqlite_format) /* SQLite BLOBs are formatted as a string of hex * digits with an X before the leading quote. */ printf("X'"); else printf("'"); } printf("%s", geometry); if (!dump_format) printf("');\n"); else printf("\n"); } } int ParseCmdline(int ARGC, char **ARGV) { int c; int curindex=0; char *ptr; extern char *optarg; extern int optind; if ( ARGC == 1 ) { usage(ARGV[0], 0, stdout); } while ((c = pgis_getopt(ARGC, ARGV, "kcdapDLs:Sg:iW:wIN:n")) != EOF){ switch (c) { case 'c': if (opt == ' ') opt ='c'; else return 0; break; case 'd': if (opt == ' ') opt ='d'; else return 0; break; case 'a': if (opt == ' ') opt ='a'; else return 0; break; case 'p': if (opt == ' ') opt ='p'; else return 0; break; case 'D': dump_format =1; break; case 'L': sqlite_format=1; break; case 'S': simple_geometries =1; break; case 's': (void)sscanf(optarg, "%d", &sr_id); break; case 'g': geom = optarg; break; case 'k': quoteidentifiers = 1; break; case 'i': forceint4 = 1; break; case 'I': createindex = 1; break; case 'w': hwgeom = 1; break; case 'n': readshape = 0; break; case 'W': #ifdef HAVE_ICONV encoding = optarg; #else fprintf(stderr, "WARNING: the -W switch will have no effect. UTF8 disabled at compile time\n"); #endif break; case 'N': switch (optarg[0]) { case 'a': null_policy = abort_on_null; break; case 'i': null_policy = insert_null; break; case 's': null_policy = skip_null; break; default: fprintf(stderr, "Unsupported NULL geometry handling policy.\nValid policies: insert, skip, abort\n"); exit(1); } break; case '?': usage(ARGV[0], 0, stdout); default: return 0; } } if ( !sr_id ) sr_id = -1; if ( !geom ) geom = "the_geom"; if ( opt==' ' ) opt = 'c'; for (; optind < ARGC; optind++){ if(curindex ==0){ shp_file = ARGV[optind]; }else if(curindex == 1){ table = ARGV[optind]; if ( (ptr=strchr(table, '.')) ) { *ptr = '\0'; schema = table; table = ptr+1; } } curindex++; } /* * Third argument (if present) is supported for compatibility * with old shp2pgsql versions taking also database name. */ if(curindex < 2 || curindex > 3){ return 0; } /* * Transform table name to lower case unless asked * to keep original case (we'll quote it later on) */ if ( ! quoteidentifiers ) { LowerCase(table); if ( schema ) LowerCase(schema); } return 1; } void SetPgType(void) { switch(shpfiletype) { case SHPT_POINT: /* Point */ pgtype = "POINT"; wkbtype = POINTTYPE; pgdims = 2; break; case SHPT_ARC: /* PolyLine */ pgtype = "MULTILINESTRING"; wkbtype = MULTILINETYPE ; pgdims = 2; break; case SHPT_POLYGON: /* Polygon */ pgtype = "MULTIPOLYGON"; wkbtype = MULTIPOLYGONTYPE; pgdims = 2; break; case SHPT_MULTIPOINT: /* MultiPoint */ pgtype = "MULTIPOINT"; wkbtype = MULTIPOINTTYPE; pgdims = 2; break; case SHPT_POINTM: /* PointM */ wkbtype = POINTTYPE | WKBMOFFSET; if ( ! hwgeom ) { pgtype = "POINTM"; pgdims = 3; istypeM = 1; } else { pgtype = "POINT"; pgdims = 2; } break; case SHPT_ARCM: /* PolyLineM */ wkbtype = MULTILINETYPE | WKBMOFFSET; if ( ! hwgeom ) { pgtype = "MULTILINESTRINGM"; pgdims = 3; istypeM = 1; } else { pgtype = "MULTILINESTRING"; pgdims = 2; } break; case SHPT_POLYGONM: /* PolygonM */ wkbtype = MULTIPOLYGONTYPE | WKBMOFFSET; if ( ! hwgeom ) { pgtype = "MULTIPOLYGONM"; pgdims = 3; istypeM = 1; } else { pgtype = "MULTIPOLYGON"; pgdims = 2; } break; case SHPT_MULTIPOINTM: /* MultiPointM */ wkbtype = MULTIPOINTTYPE | WKBMOFFSET; if ( ! hwgeom ) { pgtype = "MULTIPOINTM"; pgdims = 3; istypeM = 1; } else { pgtype = "MULTIPOINT"; pgdims = 2; } break; case SHPT_POINTZ: /* PointZ */ wkbtype = POINTTYPE | WKBMOFFSET | WKBZOFFSET; pgtype = "POINT"; if ( ! hwgeom ) pgdims = 4; else pgdims = 3; break; case SHPT_ARCZ: /* PolyLineZ */ pgtype = "MULTILINESTRING"; wkbtype = MULTILINETYPE | WKBZOFFSET | WKBMOFFSET; if ( ! hwgeom ) pgdims = 4; else pgdims = 3; break; case SHPT_POLYGONZ: /* MultiPolygonZ */ pgtype = "MULTIPOLYGON"; wkbtype = MULTIPOLYGONTYPE | WKBZOFFSET | WKBMOFFSET; if ( ! hwgeom ) pgdims = 4; else pgdims = 3; break; case SHPT_MULTIPOINTZ: /* MultiPointZ */ pgtype = "MULTIPOINT"; wkbtype = MULTIPOINTTYPE | WKBZOFFSET | WKBMOFFSET; if ( ! hwgeom ) pgdims = 4; else pgdims = 3; break; default: pgtype = "GEOMETRY"; wkbtype = COLLECTIONTYPE | WKBZOFFSET | WKBMOFFSET; pgdims = 4; fprintf(stderr, "Unknown geometry type: %d\n", shpfiletype); break; } if (simple_geometries) { // adjust geometry name for CREATE TABLE by skipping MULTI if ((wkbtype & 0x7) == MULTIPOLYGONTYPE) pgtype += 5; if ((wkbtype & 0x7) == MULTILINETYPE) pgtype += 5; } } char * dump_ring(Ring *ring) { char *buf = malloc(256*ring->n); int i; buf[0] = '\0'; for (i=0; in; i++) { if (i) strcat(buf, ","); sprintf(buf+strlen(buf), "%g %g", ring->list[i].x, ring->list[i].y); } return buf; } void DropTable(char *schema, char *table, char *geom) { /*---------------Drop the table-------------------------- * TODO: if the table has more then one geometry column * the DROP TABLE call will leave spurious records in * geometry_columns. * * If the geometry column in the table being dropped * does not match 'the_geom' or the name specified with * -g an error is returned by DropGeometryColumn. * * The table to be dropped might not exist. * */ if ( schema ) { if (readshape == 1){ printf("SELECT DropGeometryColumn('%s','%s','%s');\n", schema, table, geom); } printf("DROP TABLE \"%s\".\"%s\";\n", schema, table); } else { if (readshape == 1){ printf("SELECT DropGeometryColumn('','%s','%s');\n", table, geom); } printf("DROP TABLE \"%s\";\n", table); } } void GetFieldsSpec(void) { /* * Shapefile (dbf) field name are at most 10chars + 1 NULL. * Postgresql field names are at most 63 bytes + 1 NULL. */ #define MAXFIELDNAMELEN 64 int field_precision, field_width; int j, z; char name[MAXFIELDNAMELEN]; char name2[MAXFIELDNAMELEN]; DBFFieldType type = -1; #ifdef HAVE_ICONV char *utf8str; #endif num_fields = DBFGetFieldCount( hDBFHandle ); num_records = DBFGetRecordCount(hDBFHandle); field_names = malloc(num_fields*sizeof(char*)); types = (DBFFieldType *)malloc(num_fields*sizeof(int)); widths = malloc(num_fields*sizeof(int)); precisions = malloc(num_fields*sizeof(int)); if (readshape == 1) { col_names = malloc((num_fields+2) * sizeof(char) * MAXFIELDNAMELEN); } { //for dbf only, we do not need to allocate slot for the_geom col_names = malloc((num_fields+1) * sizeof(char) * MAXFIELDNAMELEN); } strcpy(col_names, "(" ); /*fprintf(stderr, "Number of fields from DBF: %d\n", num_fields); */ for(j=0;jPGIS mapping * * Revision 1.105 2006/01/09 16:40:16 strk * ISO C90 comments, signedness mismatch fixes * * Revision 1.104 2005/11/01 09:25:47 strk * Reworked NULL geometries handling code letting user specify policy (insert,skip,abort). Insert is the default. * * Revision 1.103 2005/10/24 15:54:22 strk * fixed wrong assumption about maximum size of integer attributes (width is maximum size of text representation) * * Revision 1.102 2005/10/24 11:30:59 strk * * Fixed a bug in string attributes handling truncating values of maximum * allowed length, curtesy of Lars Roessiger. * Reworked integer attributes handling to be stricter in dbf->sql mapping * and to allow for big int8 values in sql->dbf conversion * * Revision 1.101 2005/10/21 11:33:55 strk * Applied patch by Lars Roessiger handling numerical values with a trailing decima * l dot * * Revision 1.100 2005/10/13 13:40:20 strk * Fixed return code from shp2pgsql * * Revision 1.99 2005/10/03 18:08:55 strk * Stricter string attributes lenght handling. DBF header will be used * to set varchar maxlenght, (var)char typmod will be used to set DBF header * len. * * Revision 1.98 2005/10/03 07:45:58 strk * Issued a warning when -W is specified and no UTF8 support has been compiled in. * * Revision 1.97 2005/09/30 08:59:29 strk * Fixed release of stack memory occurring when shp2pgsql is compiled with USE_ICONV defined, an attribute value needs to be escaped and no -W is used * * Revision 1.96 2005/08/29 22:36:25 strk * Removed premature object destruction in InsertLineString{WKT,} causing segfault * * Revision 1.95 2005/08/29 11:48:33 strk * Fixed sprintf() calls to avoid overlapping memory, * reworked not-null objects existance check to reduce startup costs. * * Revision 1.94 2005/07/27 02:47:14 strk * Support for multibyte field names in loader * * Revision 1.93 2005/07/27 02:35:50 strk * Minor cleanups in loader * * Revision 1.92 2005/07/27 02:07:01 strk * Fixed handling of POINT types as WKT (-w) in loader * * Revision 1.91 2005/07/04 09:47:03 strk * Added conservative iconv detection code * * Revision 1.90 2005/06/16 17:55:58 strk * Added -I switch for GiST index creation in loader * * Revision 1.89 2005/04/21 09:08:34 strk * Applied patch from Ron Mayer fixing a segfault in string escaper funx * * Revision 1.88 2005/04/14 12:58:59 strk * Applied patch by Gino Lucrezi fixing bug in string escaping code. * * Revision 1.87 2005/04/06 14:16:43 strk * Removed manual update of gid field. * * Revision 1.86 2005/04/06 14:02:08 mschaber * added -p option (prepare mode) that spits out the table schema without * inserting any data. * * Revision 1.85 2005/04/06 10:46:10 strk * Bugfix in -w (hwgeom) handling of ZM shapefiles. * Big reorganizzation of code to easy maintainance. * * Revision 1.84 2005/04/04 20:51:26 strk * Added -w flag to output old (WKT/HWGEOM) sql. * * Revision 1.83 2005/03/15 12:24:40 strk * hole-in-ring detector made more readable * * Revision 1.82 2005/03/14 22:02:31 strk * Fixed holes handling. * * Revision 1.81 2005/03/08 11:06:33 strk * modernized old-style parameter declarations * * Revision 1.80 2005/03/04 14:48:22 strk * Applied patch from Jonne Savolainen fixing multilines handling * * Revision 1.79 2005/01/31 22:15:22 strk * Added maintainer notice, to reduce Jeff-strk mail bounces * * Revision 1.78 2005/01/17 09:21:13 strk * Added one more bytes for terminating NULL in utf8 encoder * * Revision 1.77 2005/01/16 16:50:01 strk * String escaping algorithm made simpler and more robust. * Removed escaped strings leaking. * Fixed UTF8 encoder to allocate enough space for 3bytes chars strings. * * Revision 1.76 2005/01/12 17:03:20 strk * Added optional UTF8 output support as suggested by IIDA Tetsushi * * Revision 1.75 2004/11/15 10:51:35 strk * Fixed a bug in PIP invocation, added some debugging lines. * * Revision 1.74 2004/10/17 13:25:44 strk * removed USE_WKB partially-used define * * Revision 1.73 2004/10/17 13:24:44 strk * HEXWKB polygon * * Revision 1.72 2004/10/17 12:59:12 strk * HEXWKB multiline output * * Revision 1.71 2004/10/17 12:26:02 strk * Point and MultiPoint loaded using HEXWKB. * * Revision 1.70 2004/10/15 22:01:35 strk * Initial WKB functionalities * * Revision 1.69 2004/10/07 21:52:28 strk * Lots of rewriting/cleanup. TypeM/TypeZ supports. * * Revision 1.68 2004/10/07 06:54:24 strk * cleanups * * Revision 1.67 2004/10/06 10:11:16 strk * Other separator fixes * * Revision 1.66 2004/10/06 09:40:27 strk * Handled 0-DBF-attributes corner case. * * Revision 1.65 2004/09/20 17:13:31 strk * changed comments to better show shape type handling * * Revision 1.64 2004/08/20 08:14:37 strk * Whole output wrapped in transaction blocks. * Drops are out of transaction, and multiple transactions are used * for INSERT mode. * **********************************************************************/ ================================================ FILE: src/shp2sqlite/shpopen.c ================================================ /****************************************************************************** * $Id: shpopen.c 2785 2008-05-27 15:08:20Z mcayland $ * * Project: Shapelib * Purpose: Implementation of core Shapefile read/write functions. * Author: Frank Warmerdam, warmerdam@pobox.com * ****************************************************************************** * Copyright (c) 1999, 2001, Frank Warmerdam * * This software is available under the following "MIT Style" license, * or at the option of the licensee under the LGPL (see LICENSE.LGPL). This * option is discussed in more detail in shapelib.html. * * -- * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. ****************************************************************************** * * $Log$ * Revision 1.5 2003/12/01 20:52:00 strk * shapelib put in sync with gdal cvs * * Revision 1.43 2003/12/01 16:20:08 warmerda * be careful of zero vertex shapes * * Revision 1.42 2003/12/01 14:58:27 warmerda * added degenerate object check in SHPRewindObject() * * Revision 1.41 2003/07/08 15:22:43 warmerda * avoid warning * * Revision 1.40 2003/04/21 18:30:37 warmerda * added header write/update public methods * * Revision 1.39 2002/08/26 06:46:56 warmerda * avoid c++ comments * * Revision 1.38 2002/05/07 16:43:39 warmerda * Removed debugging printf. * * Revision 1.37 2002/04/10 17:35:22 warmerda * fixed bug in ring reversal code * * Revision 1.36 2002/04/10 16:59:54 warmerda * added SHPRewindObject * * Revision 1.35 2001/12/07 15:10:44 warmerda * fix if .shx fails to open * * Revision 1.34 2001/11/01 16:29:55 warmerda * move pabyRec into SHPInfo for thread safety * * Revision 1.33 2001/07/03 12:18:15 warmerda * Improved cleanup if SHX not found, provied by Riccardo Cohen. * * Revision 1.32 2001/06/22 01:58:07 warmerda * be more careful about establishing initial bounds in face of NULL shapes * * Revision 1.31 2001/05/31 19:35:29 warmerda * added support for writing null shapes * * Revision 1.30 2001/05/28 12:46:29 warmerda * Add some checking on reasonableness of record count when opening. * * Revision 1.29 2001/05/23 13:36:52 warmerda * added use of SHPAPI_CALL * * Revision 1.28 2001/02/06 22:25:06 warmerda * fixed memory leaks when SHPOpen() fails * * Revision 1.27 2000/07/18 15:21:33 warmerda * added better enforcement of -1 for append in SHPWriteObject * * Revision 1.26 2000/02/16 16:03:51 warmerda * added null shape support * * Revision 1.25 1999/12/15 13:47:07 warmerda * Fixed record size settings in .shp file (was 4 words too long) * Added stdlib.h. * * Revision 1.24 1999/11/05 14:12:04 warmerda * updated license terms * * Revision 1.23 1999/07/27 00:53:46 warmerda * added support for rewriting shapes * * Revision 1.22 1999/06/11 19:19:11 warmerda * Cleanup pabyRec static buffer on SHPClose(). * * Revision 1.21 1999/06/02 14:57:56 kshih * Remove unused variables * * Revision 1.20 1999/04/19 21:04:17 warmerda * Fixed syntax error. * * Revision 1.19 1999/04/19 21:01:57 warmerda * Force access string to binary in SHPOpen(). * * Revision 1.18 1999/04/01 18:48:07 warmerda * Try upper case extensions if lower case doesn't work. * * Revision 1.17 1998/12/31 15:29:39 warmerda * Disable writing measure values to multipatch objects if * DISABLE_MULTIPATCH_MEASURE is defined. * * Revision 1.16 1998/12/16 05:14:33 warmerda * Added support to write MULTIPATCH. Fixed reading Z coordinate of * MULTIPATCH. Fixed record size written for all feature types. * * Revision 1.15 1998/12/03 16:35:29 warmerda * r+b is proper binary access string, not rb+. * * Revision 1.14 1998/12/03 15:47:56 warmerda * Fixed setting of nVertices in SHPCreateObject(). * * Revision 1.13 1998/12/03 15:33:54 warmerda * Made SHPCalculateExtents() separately callable. * * Revision 1.12 1998/11/11 20:01:50 warmerda * Fixed bug writing ArcM/Z, and PolygonM/Z for big endian machines. * * Revision 1.11 1998/11/09 20:56:44 warmerda * Fixed up handling of file wide bounds. * * Revision 1.10 1998/11/09 20:18:51 warmerda * Converted to support 3D shapefiles, and use of SHPObject. * * Revision 1.9 1998/02/24 15:09:05 warmerda * Fixed memory leak. * * Revision 1.8 1997/12/04 15:40:29 warmerda * Fixed byte swapping of record number, and record length fields in the * .shp file. * * Revision 1.7 1995/10/21 03:15:58 warmerda * Added support for binary file access, the magic cookie 9997 * and tried to improve the int32 selection logic for 16bit systems. * * Revision 1.6 1995/09/04 04:19:41 warmerda * Added fix for file bounds. * * Revision 1.5 1995/08/25 15:16:44 warmerda * Fixed a couple of problems with big endian systems ... one with bounds * and the other with multipart polygons. * * Revision 1.4 1995/08/24 18:10:17 warmerda * Switch to use SfRealloc() to avoid problems with pre-ANSI realloc() * functions (such as on the Sun). * * Revision 1.3 1995/08/23 02:23:15 warmerda * Added support for reading bounds, and fixed up problems in setting the * file wide bounds. * * Revision 1.2 1995/08/04 03:16:57 warmerda * Added header. * */ #include "shapefil.h" #include #include #include #include #include typedef unsigned char uchar; #if UINT_MAX == 65535 typedef long int32; #else typedef int int32; #endif #ifndef FALSE # define FALSE 0 # define TRUE 1 #endif #define ByteCopy( a, b, c ) memcpy( b, a, c ) #ifndef MAX # define MIN(a,b) ((ab) ? a : b) #endif static int bBigEndian; /************************************************************************/ /* SwapWord() */ /* */ /* Swap a 2, 4 or 8 byte word. */ /************************************************************************/ static void SwapWord( int length, void * wordP ) { int i; uchar temp; for( i=0; i < length/2; i++ ) { temp = ((uchar *) wordP)[i]; ((uchar *)wordP)[i] = ((uchar *) wordP)[length-i-1]; ((uchar *) wordP)[length-i-1] = temp; } } /************************************************************************/ /* SfRealloc() */ /* */ /* A realloc cover function that will access a NULL pointer as */ /* a valid input. */ /************************************************************************/ static void * SfRealloc( void * pMem, int nNewSize ) { if( pMem == NULL ) return( (void *) malloc(nNewSize) ); else return( (void *) realloc(pMem,nNewSize) ); } /************************************************************************/ /* SHPWriteHeader() */ /* */ /* Write out a header for the .shp and .shx files as well as the */ /* contents of the index (.shx) file. */ /************************************************************************/ void SHPWriteHeader( SHPHandle psSHP ) { uchar abyHeader[100]; int i; int32 i32; double dValue; int32 *panSHX; /* -------------------------------------------------------------------- */ /* Prepare header block for .shp file. */ /* -------------------------------------------------------------------- */ for( i = 0; i < 100; i++ ) abyHeader[i] = 0; abyHeader[2] = 0x27; /* magic cookie */ abyHeader[3] = 0x0a; i32 = psSHP->nFileSize/2; /* file size */ ByteCopy( &i32, abyHeader+24, 4 ); if( !bBigEndian ) SwapWord( 4, abyHeader+24 ); i32 = 1000; /* version */ ByteCopy( &i32, abyHeader+28, 4 ); if( bBigEndian ) SwapWord( 4, abyHeader+28 ); i32 = psSHP->nShapeType; /* shape type */ ByteCopy( &i32, abyHeader+32, 4 ); if( bBigEndian ) SwapWord( 4, abyHeader+32 ); dValue = psSHP->adBoundsMin[0]; /* set bounds */ ByteCopy( &dValue, abyHeader+36, 8 ); if( bBigEndian ) SwapWord( 8, abyHeader+36 ); dValue = psSHP->adBoundsMin[1]; ByteCopy( &dValue, abyHeader+44, 8 ); if( bBigEndian ) SwapWord( 8, abyHeader+44 ); dValue = psSHP->adBoundsMax[0]; ByteCopy( &dValue, abyHeader+52, 8 ); if( bBigEndian ) SwapWord( 8, abyHeader+52 ); dValue = psSHP->adBoundsMax[1]; ByteCopy( &dValue, abyHeader+60, 8 ); if( bBigEndian ) SwapWord( 8, abyHeader+60 ); dValue = psSHP->adBoundsMin[2]; /* z */ ByteCopy( &dValue, abyHeader+68, 8 ); if( bBigEndian ) SwapWord( 8, abyHeader+68 ); dValue = psSHP->adBoundsMax[2]; ByteCopy( &dValue, abyHeader+76, 8 ); if( bBigEndian ) SwapWord( 8, abyHeader+76 ); dValue = psSHP->adBoundsMin[3]; /* m */ ByteCopy( &dValue, abyHeader+84, 8 ); if( bBigEndian ) SwapWord( 8, abyHeader+84 ); dValue = psSHP->adBoundsMax[3]; ByteCopy( &dValue, abyHeader+92, 8 ); if( bBigEndian ) SwapWord( 8, abyHeader+92 ); /* -------------------------------------------------------------------- */ /* Write .shp file header. */ /* -------------------------------------------------------------------- */ fseek( psSHP->fpSHP, 0, 0 ); fwrite( abyHeader, 100, 1, psSHP->fpSHP ); /* -------------------------------------------------------------------- */ /* Prepare, and write .shx file header. */ /* -------------------------------------------------------------------- */ i32 = (psSHP->nRecords * 2 * sizeof(int32) + 100)/2; /* file size */ ByteCopy( &i32, abyHeader+24, 4 ); if( !bBigEndian ) SwapWord( 4, abyHeader+24 ); fseek( psSHP->fpSHX, 0, 0 ); fwrite( abyHeader, 100, 1, psSHP->fpSHX ); /* -------------------------------------------------------------------- */ /* Write out the .shx contents. */ /* -------------------------------------------------------------------- */ panSHX = (int32 *) malloc(sizeof(int32) * 2 * psSHP->nRecords); for( i = 0; i < psSHP->nRecords; i++ ) { panSHX[i*2 ] = psSHP->panRecOffset[i]/2; panSHX[i*2+1] = psSHP->panRecSize[i]/2; if( !bBigEndian ) SwapWord( 4, panSHX+i*2 ); if( !bBigEndian ) SwapWord( 4, panSHX+i*2+1 ); } fwrite( panSHX, sizeof(int32) * 2, psSHP->nRecords, psSHP->fpSHX ); free( panSHX ); /* -------------------------------------------------------------------- */ /* Flush to disk. */ /* -------------------------------------------------------------------- */ fflush( psSHP->fpSHP ); fflush( psSHP->fpSHX ); } /************************************************************************/ /* SHPOpen() */ /* */ /* Open the .shp and .shx files based on the basename of the */ /* files or either file name. */ /************************************************************************/ SHPHandle SHPAPI_CALL SHPOpen( const char * pszLayer, const char * pszAccess ) { char *pszFullname, *pszBasename; SHPHandle psSHP; uchar *pabyBuf; int i; double dValue; /* -------------------------------------------------------------------- */ /* Ensure the access string is one of the legal ones. We */ /* ensure the result string indicates binary to avoid common */ /* problems on Windows. */ /* -------------------------------------------------------------------- */ if( strcmp(pszAccess,"rb+") == 0 || strcmp(pszAccess,"r+b") == 0 || strcmp(pszAccess,"r+") == 0 ) pszAccess = "r+b"; else pszAccess = "rb"; /* -------------------------------------------------------------------- */ /* Establish the byte order on this machine. */ /* -------------------------------------------------------------------- */ i = 1; if( *((uchar *) &i) == 1 ) bBigEndian = FALSE; else bBigEndian = TRUE; /* -------------------------------------------------------------------- */ /* Initialize the info structure. */ /* -------------------------------------------------------------------- */ psSHP = (SHPHandle) calloc(sizeof(SHPInfo),1); psSHP->bUpdated = FALSE; /* -------------------------------------------------------------------- */ /* Compute the base (layer) name. If there is any extension */ /* on the passed in filename we will strip it off. */ /* -------------------------------------------------------------------- */ pszBasename = (char *) malloc(strlen(pszLayer)+5); strcpy( pszBasename, pszLayer ); for( i = strlen(pszBasename)-1; i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/' && pszBasename[i] != '\\'; i-- ) {} if( pszBasename[i] == '.' ) pszBasename[i] = '\0'; /* -------------------------------------------------------------------- */ /* Open the .shp and .shx files. Note that files pulled from */ /* a PC to Unix with upper case filenames won't work! */ /* -------------------------------------------------------------------- */ pszFullname = (char *) malloc(strlen(pszBasename) + 5); sprintf( pszFullname, "%s.shp", pszBasename ); psSHP->fpSHP = fopen(pszFullname, pszAccess ); if( psSHP->fpSHP == NULL ) { sprintf( pszFullname, "%s.SHP", pszBasename ); psSHP->fpSHP = fopen(pszFullname, pszAccess ); } if( psSHP->fpSHP == NULL ) { free( psSHP ); free( pszBasename ); free( pszFullname ); return( NULL ); } sprintf( pszFullname, "%s.shx", pszBasename ); psSHP->fpSHX = fopen(pszFullname, pszAccess ); if( psSHP->fpSHX == NULL ) { sprintf( pszFullname, "%s.SHX", pszBasename ); psSHP->fpSHX = fopen(pszFullname, pszAccess ); } if( psSHP->fpSHX == NULL ) { fclose( psSHP->fpSHP ); free( psSHP ); free( pszBasename ); free( pszFullname ); return( NULL ); } free( pszFullname ); free( pszBasename ); /* -------------------------------------------------------------------- */ /* Read the file size from the SHP file. */ /* -------------------------------------------------------------------- */ pabyBuf = (uchar *) malloc(100); fread( pabyBuf, 100, 1, psSHP->fpSHP ); psSHP->nFileSize = (pabyBuf[24] * 256 * 256 * 256 + pabyBuf[25] * 256 * 256 + pabyBuf[26] * 256 + pabyBuf[27]) * 2; /* -------------------------------------------------------------------- */ /* Read SHX file Header info */ /* -------------------------------------------------------------------- */ fread( pabyBuf, 100, 1, psSHP->fpSHX ); if( pabyBuf[0] != 0 || pabyBuf[1] != 0 || pabyBuf[2] != 0x27 || (pabyBuf[3] != 0x0a && pabyBuf[3] != 0x0d) ) { fclose( psSHP->fpSHP ); fclose( psSHP->fpSHX ); free( psSHP ); return( NULL ); } psSHP->nRecords = pabyBuf[27] + pabyBuf[26] * 256 + pabyBuf[25] * 256 * 256 + pabyBuf[24] * 256 * 256 * 256; psSHP->nRecords = (psSHP->nRecords*2 - 100) / 8; psSHP->nShapeType = pabyBuf[32]; if( psSHP->nRecords < 0 || psSHP->nRecords > 256000000 ) { /* this header appears to be corrupt. Give up. */ fclose( psSHP->fpSHP ); fclose( psSHP->fpSHX ); free( psSHP ); return( NULL ); } /* -------------------------------------------------------------------- */ /* Read the bounds. */ /* -------------------------------------------------------------------- */ if( bBigEndian ) SwapWord( 8, pabyBuf+36 ); memcpy( &dValue, pabyBuf+36, 8 ); psSHP->adBoundsMin[0] = dValue; if( bBigEndian ) SwapWord( 8, pabyBuf+44 ); memcpy( &dValue, pabyBuf+44, 8 ); psSHP->adBoundsMin[1] = dValue; if( bBigEndian ) SwapWord( 8, pabyBuf+52 ); memcpy( &dValue, pabyBuf+52, 8 ); psSHP->adBoundsMax[0] = dValue; if( bBigEndian ) SwapWord( 8, pabyBuf+60 ); memcpy( &dValue, pabyBuf+60, 8 ); psSHP->adBoundsMax[1] = dValue; if( bBigEndian ) SwapWord( 8, pabyBuf+68 ); /* z */ memcpy( &dValue, pabyBuf+68, 8 ); psSHP->adBoundsMin[2] = dValue; if( bBigEndian ) SwapWord( 8, pabyBuf+76 ); memcpy( &dValue, pabyBuf+76, 8 ); psSHP->adBoundsMax[2] = dValue; if( bBigEndian ) SwapWord( 8, pabyBuf+84 ); /* z */ memcpy( &dValue, pabyBuf+84, 8 ); psSHP->adBoundsMin[3] = dValue; if( bBigEndian ) SwapWord( 8, pabyBuf+92 ); memcpy( &dValue, pabyBuf+92, 8 ); psSHP->adBoundsMax[3] = dValue; free( pabyBuf ); /* -------------------------------------------------------------------- */ /* Read the .shx file to get the offsets to each record in */ /* the .shp file. */ /* -------------------------------------------------------------------- */ psSHP->nMaxRecords = psSHP->nRecords; psSHP->panRecOffset = (int *) malloc(sizeof(int) * MAX(1,psSHP->nMaxRecords) ); psSHP->panRecSize = (int *) malloc(sizeof(int) * MAX(1,psSHP->nMaxRecords) ); pabyBuf = (uchar *) malloc(8 * MAX(1,psSHP->nRecords) ); fread( pabyBuf, 8, psSHP->nRecords, psSHP->fpSHX ); for( i = 0; i < psSHP->nRecords; i++ ) { int32 nOffset, nLength; memcpy( &nOffset, pabyBuf + i * 8, 4 ); if( !bBigEndian ) SwapWord( 4, &nOffset ); memcpy( &nLength, pabyBuf + i * 8 + 4, 4 ); if( !bBigEndian ) SwapWord( 4, &nLength ); psSHP->panRecOffset[i] = nOffset*2; psSHP->panRecSize[i] = nLength*2; } free( pabyBuf ); return( psSHP ); } /************************************************************************/ /* SHPClose() */ /* */ /* Close the .shp and .shx files. */ /************************************************************************/ void SHPAPI_CALL SHPClose(SHPHandle psSHP ) { /* -------------------------------------------------------------------- */ /* Update the header if we have modified anything. */ /* -------------------------------------------------------------------- */ if( psSHP->bUpdated ) SHPWriteHeader( psSHP ); /* -------------------------------------------------------------------- */ /* Free all resources, and close files. */ /* -------------------------------------------------------------------- */ free( psSHP->panRecOffset ); free( psSHP->panRecSize ); fclose( psSHP->fpSHX ); fclose( psSHP->fpSHP ); if( psSHP->pabyRec != NULL ) { free( psSHP->pabyRec ); } free( psSHP ); } /************************************************************************/ /* SHPGetInfo() */ /* */ /* Fetch general information about the shape file. */ /************************************************************************/ void SHPAPI_CALL SHPGetInfo(SHPHandle psSHP, int * pnEntities, int * pnShapeType, double * padfMinBound, double * padfMaxBound ) { int i; if( pnEntities != NULL ) *pnEntities = psSHP->nRecords; if( pnShapeType != NULL ) *pnShapeType = psSHP->nShapeType; for( i = 0; i < 4; i++ ) { if( padfMinBound != NULL ) padfMinBound[i] = psSHP->adBoundsMin[i]; if( padfMaxBound != NULL ) padfMaxBound[i] = psSHP->adBoundsMax[i]; } } /************************************************************************/ /* SHPCreate() */ /* */ /* Create a new shape file and return a handle to the open */ /* shape file with read/write access. */ /************************************************************************/ SHPHandle SHPAPI_CALL SHPCreate( const char * pszLayer, int nShapeType ) { char *pszBasename, *pszFullname; int i; FILE *fpSHP, *fpSHX; uchar abyHeader[100]; int32 i32; double dValue; /* -------------------------------------------------------------------- */ /* Establish the byte order on this system. */ /* -------------------------------------------------------------------- */ i = 1; if( *((uchar *) &i) == 1 ) bBigEndian = FALSE; else bBigEndian = TRUE; /* -------------------------------------------------------------------- */ /* Compute the base (layer) name. If there is any extension */ /* on the passed in filename we will strip it off. */ /* -------------------------------------------------------------------- */ pszBasename = (char *) malloc(strlen(pszLayer)+5); strcpy( pszBasename, pszLayer ); for( i = strlen(pszBasename)-1; i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/' && pszBasename[i] != '\\'; i-- ) {} if( pszBasename[i] == '.' ) pszBasename[i] = '\0'; /* -------------------------------------------------------------------- */ /* Open the two files so we can write their headers. */ /* -------------------------------------------------------------------- */ pszFullname = (char *) malloc(strlen(pszBasename) + 5); sprintf( pszFullname, "%s.shp", pszBasename ); fpSHP = fopen(pszFullname, "wb" ); if( fpSHP == NULL ) return( NULL ); sprintf( pszFullname, "%s.shx", pszBasename ); fpSHX = fopen(pszFullname, "wb" ); if( fpSHX == NULL ) return( NULL ); free( pszFullname ); free( pszBasename ); /* -------------------------------------------------------------------- */ /* Prepare header block for .shp file. */ /* -------------------------------------------------------------------- */ for( i = 0; i < 100; i++ ) abyHeader[i] = 0; abyHeader[2] = 0x27; /* magic cookie */ abyHeader[3] = 0x0a; i32 = 50; /* file size */ ByteCopy( &i32, abyHeader+24, 4 ); if( !bBigEndian ) SwapWord( 4, abyHeader+24 ); i32 = 1000; /* version */ ByteCopy( &i32, abyHeader+28, 4 ); if( bBigEndian ) SwapWord( 4, abyHeader+28 ); i32 = nShapeType; /* shape type */ ByteCopy( &i32, abyHeader+32, 4 ); if( bBigEndian ) SwapWord( 4, abyHeader+32 ); dValue = 0.0; /* set bounds */ ByteCopy( &dValue, abyHeader+36, 8 ); ByteCopy( &dValue, abyHeader+44, 8 ); ByteCopy( &dValue, abyHeader+52, 8 ); ByteCopy( &dValue, abyHeader+60, 8 ); /* -------------------------------------------------------------------- */ /* Write .shp file header. */ /* -------------------------------------------------------------------- */ fwrite( abyHeader, 100, 1, fpSHP ); /* -------------------------------------------------------------------- */ /* Prepare, and write .shx file header. */ /* -------------------------------------------------------------------- */ i32 = 50; /* file size */ ByteCopy( &i32, abyHeader+24, 4 ); if( !bBigEndian ) SwapWord( 4, abyHeader+24 ); fwrite( abyHeader, 100, 1, fpSHX ); /* -------------------------------------------------------------------- */ /* Close the files, and then open them as regular existing files. */ /* -------------------------------------------------------------------- */ fclose( fpSHP ); fclose( fpSHX ); return( SHPOpen( pszLayer, "r+b" ) ); } /************************************************************************/ /* _SHPSetBounds() */ /* */ /* Compute a bounds rectangle for a shape, and set it into the */ /* indicated location in the record. */ /************************************************************************/ static void _SHPSetBounds( uchar * pabyRec, SHPObject * psShape ) { ByteCopy( &(psShape->dfXMin), pabyRec + 0, 8 ); ByteCopy( &(psShape->dfYMin), pabyRec + 8, 8 ); ByteCopy( &(psShape->dfXMax), pabyRec + 16, 8 ); ByteCopy( &(psShape->dfYMax), pabyRec + 24, 8 ); if( bBigEndian ) { SwapWord( 8, pabyRec + 0 ); SwapWord( 8, pabyRec + 8 ); SwapWord( 8, pabyRec + 16 ); SwapWord( 8, pabyRec + 24 ); } } /************************************************************************/ /* SHPComputeExtents() */ /* */ /* Recompute the extents of a shape. Automatically done by */ /* SHPCreateObject(). */ /************************************************************************/ void SHPAPI_CALL SHPComputeExtents( SHPObject * psObject ) { int i; /* -------------------------------------------------------------------- */ /* Build extents for this object. */ /* -------------------------------------------------------------------- */ if( psObject->nVertices > 0 ) { psObject->dfXMin = psObject->dfXMax = psObject->padfX[0]; psObject->dfYMin = psObject->dfYMax = psObject->padfY[0]; psObject->dfZMin = psObject->dfZMax = psObject->padfZ[0]; psObject->dfMMin = psObject->dfMMax = psObject->padfM[0]; } for( i = 0; i < psObject->nVertices; i++ ) { psObject->dfXMin = MIN(psObject->dfXMin, psObject->padfX[i]); psObject->dfYMin = MIN(psObject->dfYMin, psObject->padfY[i]); psObject->dfZMin = MIN(psObject->dfZMin, psObject->padfZ[i]); psObject->dfMMin = MIN(psObject->dfMMin, psObject->padfM[i]); psObject->dfXMax = MAX(psObject->dfXMax, psObject->padfX[i]); psObject->dfYMax = MAX(psObject->dfYMax, psObject->padfY[i]); psObject->dfZMax = MAX(psObject->dfZMax, psObject->padfZ[i]); psObject->dfMMax = MAX(psObject->dfMMax, psObject->padfM[i]); } } /************************************************************************/ /* SHPCreateObject() */ /* */ /* Create a shape object. It should be freed with */ /* SHPDestroyObject(). */ /************************************************************************/ SHPObject SHPAPI_CALL1(*) SHPCreateObject( int nSHPType, int nShapeId, int nParts, int * panPartStart, int * panPartType, int nVertices, double * padfX, double * padfY, double * padfZ, double * padfM ) { SHPObject *psObject; int i, bHasM, bHasZ; psObject = (SHPObject *) calloc(1,sizeof(SHPObject)); psObject->nSHPType = nSHPType; psObject->nShapeId = nShapeId; /* -------------------------------------------------------------------- */ /* Establish whether this shape type has M, and Z values. */ /* -------------------------------------------------------------------- */ if( nSHPType == SHPT_ARCM || nSHPType == SHPT_POINTM || nSHPType == SHPT_POLYGONM || nSHPType == SHPT_MULTIPOINTM ) { bHasM = TRUE; bHasZ = FALSE; } else if( nSHPType == SHPT_ARCZ || nSHPType == SHPT_POINTZ || nSHPType == SHPT_POLYGONZ || nSHPType == SHPT_MULTIPOINTZ || nSHPType == SHPT_MULTIPATCH ) { bHasM = TRUE; bHasZ = TRUE; } else { bHasM = FALSE; bHasZ = FALSE; } /* -------------------------------------------------------------------- */ /* Capture parts. Note that part type is optional, and */ /* defaults to ring. */ /* -------------------------------------------------------------------- */ if( nSHPType == SHPT_ARC || nSHPType == SHPT_POLYGON || nSHPType == SHPT_ARCM || nSHPType == SHPT_POLYGONM || nSHPType == SHPT_ARCZ || nSHPType == SHPT_POLYGONZ || nSHPType == SHPT_MULTIPATCH ) { psObject->nParts = MAX(1,nParts); psObject->panPartStart = (int *) malloc(sizeof(int) * psObject->nParts); psObject->panPartType = (int *) malloc(sizeof(int) * psObject->nParts); psObject->panPartStart[0] = 0; psObject->panPartType[0] = SHPP_RING; for( i = 0; i < nParts; i++ ) { psObject->panPartStart[i] = panPartStart[i]; if( panPartType != NULL ) psObject->panPartType[i] = panPartType[i]; else psObject->panPartType[i] = SHPP_RING; } } /* -------------------------------------------------------------------- */ /* Capture vertices. Note that Z and M are optional, but X and */ /* Y are not. */ /* -------------------------------------------------------------------- */ if( nVertices > 0 ) { psObject->padfX = (double *) calloc(sizeof(double),nVertices); psObject->padfY = (double *) calloc(sizeof(double),nVertices); psObject->padfZ = (double *) calloc(sizeof(double),nVertices); psObject->padfM = (double *) calloc(sizeof(double),nVertices); assert( padfX != NULL ); assert( padfY != NULL ); for( i = 0; i < nVertices; i++ ) { psObject->padfX[i] = padfX[i]; psObject->padfY[i] = padfY[i]; if( padfZ != NULL && bHasZ ) psObject->padfZ[i] = padfZ[i]; if( padfM != NULL && bHasM ) psObject->padfM[i] = padfM[i]; } } /* -------------------------------------------------------------------- */ /* Compute the extents. */ /* -------------------------------------------------------------------- */ psObject->nVertices = nVertices; SHPComputeExtents( psObject ); return( psObject ); } /************************************************************************/ /* SHPCreateSimpleObject() */ /* */ /* Create a simple (common) shape object. Destroy with */ /* SHPDestroyObject(). */ /************************************************************************/ SHPObject SHPAPI_CALL1(*) SHPCreateSimpleObject( int nSHPType, int nVertices, double * padfX, double * padfY, double * padfZ ) { return( SHPCreateObject( nSHPType, -1, 0, NULL, NULL, nVertices, padfX, padfY, padfZ, NULL ) ); } /************************************************************************/ /* SHPWriteObject() */ /* */ /* Write out the vertices of a new structure. Note that it is */ /* only possible to write vertices at the end of the file. */ /************************************************************************/ int SHPAPI_CALL SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject * psObject ) { int nRecordOffset, i, nRecordSize=0; uchar *pabyRec; int32 i32; psSHP->bUpdated = TRUE; /* -------------------------------------------------------------------- */ /* Ensure that shape object matches the type of the file it is */ /* being written to. */ /* -------------------------------------------------------------------- */ assert( psObject->nSHPType == psSHP->nShapeType || psObject->nSHPType == SHPT_NULL ); /* -------------------------------------------------------------------- */ /* Ensure that -1 is used for appends. Either blow an */ /* assertion, or if they are disabled, set the shapeid to -1 */ /* for appends. */ /* -------------------------------------------------------------------- */ assert( nShapeId == -1 || (nShapeId >= 0 && nShapeId < psSHP->nRecords) ); if( nShapeId != -1 && nShapeId >= psSHP->nRecords ) nShapeId = -1; /* -------------------------------------------------------------------- */ /* Add the new entity to the in memory index. */ /* -------------------------------------------------------------------- */ if( nShapeId == -1 && psSHP->nRecords+1 > psSHP->nMaxRecords ) { psSHP->nMaxRecords =(int) ( psSHP->nMaxRecords * 1.3 + 100); psSHP->panRecOffset = (int *) SfRealloc(psSHP->panRecOffset,sizeof(int) * psSHP->nMaxRecords ); psSHP->panRecSize = (int *) SfRealloc(psSHP->panRecSize,sizeof(int) * psSHP->nMaxRecords ); } /* -------------------------------------------------------------------- */ /* Initialize record. */ /* -------------------------------------------------------------------- */ pabyRec = (uchar *) malloc(psObject->nVertices * 4 * sizeof(double) + psObject->nParts * 8 + 128); /* -------------------------------------------------------------------- */ /* Extract vertices for a Polygon or Arc. */ /* -------------------------------------------------------------------- */ if( psObject->nSHPType == SHPT_POLYGON || psObject->nSHPType == SHPT_POLYGONZ || psObject->nSHPType == SHPT_POLYGONM || psObject->nSHPType == SHPT_ARC || psObject->nSHPType == SHPT_ARCZ || psObject->nSHPType == SHPT_ARCM || psObject->nSHPType == SHPT_MULTIPATCH ) { int32 nPoints, nParts; int i; nPoints = psObject->nVertices; nParts = psObject->nParts; _SHPSetBounds( pabyRec + 12, psObject ); if( bBigEndian ) SwapWord( 4, &nPoints ); if( bBigEndian ) SwapWord( 4, &nParts ); ByteCopy( &nPoints, pabyRec + 40 + 8, 4 ); ByteCopy( &nParts, pabyRec + 36 + 8, 4 ); nRecordSize = 52; /* * Write part start positions. */ ByteCopy( psObject->panPartStart, pabyRec + 44 + 8, 4 * psObject->nParts ); for( i = 0; i < psObject->nParts; i++ ) { if( bBigEndian ) SwapWord( 4, pabyRec + 44 + 8 + 4*i ); nRecordSize += 4; } /* * Write multipatch part types if needed. */ if( psObject->nSHPType == SHPT_MULTIPATCH ) { memcpy( pabyRec + nRecordSize, psObject->panPartType, 4*psObject->nParts ); for( i = 0; i < psObject->nParts; i++ ) { if( bBigEndian ) SwapWord( 4, pabyRec + nRecordSize ); nRecordSize += 4; } } /* * Write the (x,y) vertex values. */ for( i = 0; i < psObject->nVertices; i++ ) { ByteCopy( psObject->padfX + i, pabyRec + nRecordSize, 8 ); ByteCopy( psObject->padfY + i, pabyRec + nRecordSize + 8, 8 ); if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize + 8 ); nRecordSize += 2 * 8; } /* * Write the Z coordinates (if any). */ if( psObject->nSHPType == SHPT_POLYGONZ || psObject->nSHPType == SHPT_ARCZ || psObject->nSHPType == SHPT_MULTIPATCH ) { ByteCopy( &(psObject->dfZMin), pabyRec + nRecordSize, 8 ); if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); nRecordSize += 8; ByteCopy( &(psObject->dfZMax), pabyRec + nRecordSize, 8 ); if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); nRecordSize += 8; for( i = 0; i < psObject->nVertices; i++ ) { ByteCopy( psObject->padfZ + i, pabyRec + nRecordSize, 8 ); if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); nRecordSize += 8; } } /* * Write the M values, if any. */ if( psObject->nSHPType == SHPT_POLYGONM || psObject->nSHPType == SHPT_ARCM #ifndef DISABLE_MULTIPATCH_MEASURE || psObject->nSHPType == SHPT_MULTIPATCH #endif || psObject->nSHPType == SHPT_POLYGONZ || psObject->nSHPType == SHPT_ARCZ ) { ByteCopy( &(psObject->dfMMin), pabyRec + nRecordSize, 8 ); if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); nRecordSize += 8; ByteCopy( &(psObject->dfMMax), pabyRec + nRecordSize, 8 ); if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); nRecordSize += 8; for( i = 0; i < psObject->nVertices; i++ ) { ByteCopy( psObject->padfM + i, pabyRec + nRecordSize, 8 ); if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); nRecordSize += 8; } } } /* -------------------------------------------------------------------- */ /* Extract vertices for a MultiPoint. */ /* -------------------------------------------------------------------- */ else if( psObject->nSHPType == SHPT_MULTIPOINT || psObject->nSHPType == SHPT_MULTIPOINTZ || psObject->nSHPType == SHPT_MULTIPOINTM ) { int32 nPoints; int i; nPoints = psObject->nVertices; _SHPSetBounds( pabyRec + 12, psObject ); if( bBigEndian ) SwapWord( 4, &nPoints ); ByteCopy( &nPoints, pabyRec + 44, 4 ); for( i = 0; i < psObject->nVertices; i++ ) { ByteCopy( psObject->padfX + i, pabyRec + 48 + i*16, 8 ); ByteCopy( psObject->padfY + i, pabyRec + 48 + i*16 + 8, 8 ); if( bBigEndian ) SwapWord( 8, pabyRec + 48 + i*16 ); if( bBigEndian ) SwapWord( 8, pabyRec + 48 + i*16 + 8 ); } nRecordSize = 48 + 16 * psObject->nVertices; if( psObject->nSHPType == SHPT_MULTIPOINTZ ) { ByteCopy( &(psObject->dfZMin), pabyRec + nRecordSize, 8 ); if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); nRecordSize += 8; ByteCopy( &(psObject->dfZMax), pabyRec + nRecordSize, 8 ); if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); nRecordSize += 8; for( i = 0; i < psObject->nVertices; i++ ) { ByteCopy( psObject->padfZ + i, pabyRec + nRecordSize, 8 ); if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); nRecordSize += 8; } } if( psObject->nSHPType == SHPT_MULTIPOINTZ || psObject->nSHPType == SHPT_MULTIPOINTM ) { ByteCopy( &(psObject->dfMMin), pabyRec + nRecordSize, 8 ); if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); nRecordSize += 8; ByteCopy( &(psObject->dfMMax), pabyRec + nRecordSize, 8 ); if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); nRecordSize += 8; for( i = 0; i < psObject->nVertices; i++ ) { ByteCopy( psObject->padfM + i, pabyRec + nRecordSize, 8 ); if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); nRecordSize += 8; } } } /* -------------------------------------------------------------------- */ /* Write point. */ /* -------------------------------------------------------------------- */ else if( psObject->nSHPType == SHPT_POINT || psObject->nSHPType == SHPT_POINTZ || psObject->nSHPType == SHPT_POINTM ) { ByteCopy( psObject->padfX, pabyRec + 12, 8 ); ByteCopy( psObject->padfY, pabyRec + 20, 8 ); if( bBigEndian ) SwapWord( 8, pabyRec + 12 ); if( bBigEndian ) SwapWord( 8, pabyRec + 20 ); nRecordSize = 28; if( psObject->nSHPType == SHPT_POINTZ ) { ByteCopy( psObject->padfZ, pabyRec + nRecordSize, 8 ); if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); nRecordSize += 8; } if( psObject->nSHPType == SHPT_POINTZ || psObject->nSHPType == SHPT_POINTM ) { ByteCopy( psObject->padfM, pabyRec + nRecordSize, 8 ); if( bBigEndian ) SwapWord( 8, pabyRec + nRecordSize ); nRecordSize += 8; } } /* -------------------------------------------------------------------- */ /* Not much to do for null geometries. */ /* -------------------------------------------------------------------- */ else if( psObject->nSHPType == SHPT_NULL ) { nRecordSize = 12; } else { /* unknown type */ assert( FALSE ); } /* -------------------------------------------------------------------- */ /* Establish where we are going to put this record. If we are */ /* rewriting and existing record, and it will fit, then put it */ /* back where the original came from. Otherwise write at the end. */ /* -------------------------------------------------------------------- */ if( nShapeId == -1 || psSHP->panRecSize[nShapeId] < nRecordSize-8 ) { if( nShapeId == -1 ) nShapeId = psSHP->nRecords++; psSHP->panRecOffset[nShapeId] = nRecordOffset = psSHP->nFileSize; psSHP->panRecSize[nShapeId] = nRecordSize-8; psSHP->nFileSize += nRecordSize; } else { nRecordOffset = psSHP->panRecOffset[nShapeId]; } /* -------------------------------------------------------------------- */ /* Set the shape type, record number, and record size. */ /* -------------------------------------------------------------------- */ i32 = nShapeId+1; /* record # */ if( !bBigEndian ) SwapWord( 4, &i32 ); ByteCopy( &i32, pabyRec, 4 ); i32 = (nRecordSize-8)/2; /* record size */ if( !bBigEndian ) SwapWord( 4, &i32 ); ByteCopy( &i32, pabyRec + 4, 4 ); i32 = psObject->nSHPType; /* shape type */ if( bBigEndian ) SwapWord( 4, &i32 ); ByteCopy( &i32, pabyRec + 8, 4 ); /* -------------------------------------------------------------------- */ /* Write out record. */ /* -------------------------------------------------------------------- */ if( fseek( psSHP->fpSHP, nRecordOffset, 0 ) != 0 || fwrite( pabyRec, nRecordSize, 1, psSHP->fpSHP ) < 1 ) { printf( "Error in fseek() or fwrite().\n" ); free( pabyRec ); return -1; } free( pabyRec ); /* -------------------------------------------------------------------- */ /* Expand file wide bounds based on this shape. */ /* -------------------------------------------------------------------- */ if( psSHP->adBoundsMin[0] == 0.0 && psSHP->adBoundsMax[0] == 0.0 && psSHP->adBoundsMin[1] == 0.0 && psSHP->adBoundsMax[1] == 0.0 ) { if( psObject->nSHPType == SHPT_NULL || psObject->nVertices == 0 ) { psSHP->adBoundsMin[0] = psSHP->adBoundsMax[0] = 0.0; psSHP->adBoundsMin[1] = psSHP->adBoundsMax[1] = 0.0; psSHP->adBoundsMin[2] = psSHP->adBoundsMax[2] = 0.0; psSHP->adBoundsMin[3] = psSHP->adBoundsMax[3] = 0.0; } else { psSHP->adBoundsMin[0] = psSHP->adBoundsMax[0] = psObject->padfX[0]; psSHP->adBoundsMin[1] = psSHP->adBoundsMax[1] = psObject->padfY[0]; psSHP->adBoundsMin[2] = psSHP->adBoundsMax[2] = psObject->padfZ[0]; psSHP->adBoundsMin[3] = psSHP->adBoundsMax[3] = psObject->padfM[0]; } } for( i = 0; i < psObject->nVertices; i++ ) { psSHP->adBoundsMin[0] = MIN(psSHP->adBoundsMin[0],psObject->padfX[i]); psSHP->adBoundsMin[1] = MIN(psSHP->adBoundsMin[1],psObject->padfY[i]); psSHP->adBoundsMin[2] = MIN(psSHP->adBoundsMin[2],psObject->padfZ[i]); psSHP->adBoundsMin[3] = MIN(psSHP->adBoundsMin[3],psObject->padfM[i]); psSHP->adBoundsMax[0] = MAX(psSHP->adBoundsMax[0],psObject->padfX[i]); psSHP->adBoundsMax[1] = MAX(psSHP->adBoundsMax[1],psObject->padfY[i]); psSHP->adBoundsMax[2] = MAX(psSHP->adBoundsMax[2],psObject->padfZ[i]); psSHP->adBoundsMax[3] = MAX(psSHP->adBoundsMax[3],psObject->padfM[i]); } return( nShapeId ); } /************************************************************************/ /* SHPReadObject() */ /* */ /* Read the vertices, parts, and other non-attribute information */ /* for one shape. */ /************************************************************************/ SHPObject SHPAPI_CALL1(*) SHPReadObject( SHPHandle psSHP, int hEntity ) { SHPObject *psShape; /* -------------------------------------------------------------------- */ /* Validate the record/entity number. */ /* -------------------------------------------------------------------- */ if( hEntity < 0 || hEntity >= psSHP->nRecords ) return( NULL ); /* -------------------------------------------------------------------- */ /* Ensure our record buffer is large enough. */ /* -------------------------------------------------------------------- */ if( psSHP->panRecSize[hEntity]+8 > psSHP->nBufSize ) { psSHP->nBufSize = psSHP->panRecSize[hEntity]+8; psSHP->pabyRec = (uchar *) SfRealloc(psSHP->pabyRec,psSHP->nBufSize); } /* -------------------------------------------------------------------- */ /* Read the record. */ /* -------------------------------------------------------------------- */ fseek( psSHP->fpSHP, psSHP->panRecOffset[hEntity], 0 ); fread( psSHP->pabyRec, psSHP->panRecSize[hEntity]+8, 1, psSHP->fpSHP ); /* -------------------------------------------------------------------- */ /* Allocate and minimally initialize the object. */ /* -------------------------------------------------------------------- */ psShape = (SHPObject *) calloc(1,sizeof(SHPObject)); psShape->nShapeId = hEntity; memcpy( &psShape->nSHPType, psSHP->pabyRec + 8, 4 ); if( bBigEndian ) SwapWord( 4, &(psShape->nSHPType) ); /* ==================================================================== */ /* Extract vertices for a Polygon or Arc. */ /* ==================================================================== */ if( psShape->nSHPType == SHPT_POLYGON || psShape->nSHPType == SHPT_ARC || psShape->nSHPType == SHPT_POLYGONZ || psShape->nSHPType == SHPT_POLYGONM || psShape->nSHPType == SHPT_ARCZ || psShape->nSHPType == SHPT_ARCM || psShape->nSHPType == SHPT_MULTIPATCH ) { int32 nPoints, nParts; int i, nOffset; /* -------------------------------------------------------------------- */ /* Get the X/Y bounds. */ /* -------------------------------------------------------------------- */ memcpy( &(psShape->dfXMin), psSHP->pabyRec + 8 + 4, 8 ); memcpy( &(psShape->dfYMin), psSHP->pabyRec + 8 + 12, 8 ); memcpy( &(psShape->dfXMax), psSHP->pabyRec + 8 + 20, 8 ); memcpy( &(psShape->dfYMax), psSHP->pabyRec + 8 + 28, 8 ); if( bBigEndian ) SwapWord( 8, &(psShape->dfXMin) ); if( bBigEndian ) SwapWord( 8, &(psShape->dfYMin) ); if( bBigEndian ) SwapWord( 8, &(psShape->dfXMax) ); if( bBigEndian ) SwapWord( 8, &(psShape->dfYMax) ); /* -------------------------------------------------------------------- */ /* Extract part/point count, and build vertex and part arrays */ /* to proper size. */ /* -------------------------------------------------------------------- */ memcpy( &nPoints, psSHP->pabyRec + 40 + 8, 4 ); memcpy( &nParts, psSHP->pabyRec + 36 + 8, 4 ); if( bBigEndian ) SwapWord( 4, &nPoints ); if( bBigEndian ) SwapWord( 4, &nParts ); psShape->nVertices = nPoints; psShape->padfX = (double *) calloc(nPoints,sizeof(double)); psShape->padfY = (double *) calloc(nPoints,sizeof(double)); psShape->padfZ = (double *) calloc(nPoints,sizeof(double)); psShape->padfM = (double *) calloc(nPoints,sizeof(double)); psShape->nParts = nParts; psShape->panPartStart = (int *) calloc(nParts,sizeof(int)); psShape->panPartType = (int *) calloc(nParts,sizeof(int)); for( i = 0; i < nParts; i++ ) psShape->panPartType[i] = SHPP_RING; /* -------------------------------------------------------------------- */ /* Copy out the part array from the record. */ /* -------------------------------------------------------------------- */ memcpy( psShape->panPartStart, psSHP->pabyRec + 44 + 8, 4 * nParts ); for( i = 0; i < nParts; i++ ) { if( bBigEndian ) SwapWord( 4, psShape->panPartStart+i ); } nOffset = 44 + 8 + 4*nParts; /* -------------------------------------------------------------------- */ /* If this is a multipatch, we will also have parts types. */ /* -------------------------------------------------------------------- */ if( psShape->nSHPType == SHPT_MULTIPATCH ) { memcpy( psShape->panPartType, psSHP->pabyRec + nOffset, 4*nParts ); for( i = 0; i < nParts; i++ ) { if( bBigEndian ) SwapWord( 4, psShape->panPartType+i ); } nOffset += 4*nParts; } /* -------------------------------------------------------------------- */ /* Copy out the vertices from the record. */ /* -------------------------------------------------------------------- */ for( i = 0; i < nPoints; i++ ) { memcpy(psShape->padfX + i, psSHP->pabyRec + nOffset + i * 16, 8 ); memcpy(psShape->padfY + i, psSHP->pabyRec + nOffset + i * 16 + 8, 8 ); if( bBigEndian ) SwapWord( 8, psShape->padfX + i ); if( bBigEndian ) SwapWord( 8, psShape->padfY + i ); } nOffset += 16*nPoints; /* -------------------------------------------------------------------- */ /* If we have a Z coordinate, collect that now. */ /* -------------------------------------------------------------------- */ if( psShape->nSHPType == SHPT_POLYGONZ || psShape->nSHPType == SHPT_ARCZ || psShape->nSHPType == SHPT_MULTIPATCH ) { memcpy( &(psShape->dfZMin), psSHP->pabyRec + nOffset, 8 ); memcpy( &(psShape->dfZMax), psSHP->pabyRec + nOffset + 8, 8 ); if( bBigEndian ) SwapWord( 8, &(psShape->dfZMin) ); if( bBigEndian ) SwapWord( 8, &(psShape->dfZMax) ); for( i = 0; i < nPoints; i++ ) { memcpy( psShape->padfZ + i, psSHP->pabyRec + nOffset + 16 + i*8, 8 ); if( bBigEndian ) SwapWord( 8, psShape->padfZ + i ); } nOffset += 16 + 8*nPoints; } /* -------------------------------------------------------------------- */ /* If we have a M measure value, then read it now. We assume */ /* that the measure can be present for any shape if the size is */ /* big enough, but really it will only occur for the Z shapes */ /* (options), and the M shapes. */ /* -------------------------------------------------------------------- */ if( psSHP->panRecSize[hEntity]+8 >= nOffset + 16 + 8*nPoints ) { memcpy( &(psShape->dfMMin), psSHP->pabyRec + nOffset, 8 ); memcpy( &(psShape->dfMMax), psSHP->pabyRec + nOffset + 8, 8 ); if( bBigEndian ) SwapWord( 8, &(psShape->dfMMin) ); if( bBigEndian ) SwapWord( 8, &(psShape->dfMMax) ); for( i = 0; i < nPoints; i++ ) { memcpy( psShape->padfM + i, psSHP->pabyRec + nOffset + 16 + i*8, 8 ); if( bBigEndian ) SwapWord( 8, psShape->padfM + i ); } } } /* ==================================================================== */ /* Extract vertices for a MultiPoint. */ /* ==================================================================== */ else if( psShape->nSHPType == SHPT_MULTIPOINT || psShape->nSHPType == SHPT_MULTIPOINTM || psShape->nSHPType == SHPT_MULTIPOINTZ ) { int32 nPoints; int i, nOffset; memcpy( &nPoints, psSHP->pabyRec + 44, 4 ); if( bBigEndian ) SwapWord( 4, &nPoints ); psShape->nVertices = nPoints; psShape->padfX = (double *) calloc(nPoints,sizeof(double)); psShape->padfY = (double *) calloc(nPoints,sizeof(double)); psShape->padfZ = (double *) calloc(nPoints,sizeof(double)); psShape->padfM = (double *) calloc(nPoints,sizeof(double)); for( i = 0; i < nPoints; i++ ) { memcpy(psShape->padfX+i, psSHP->pabyRec + 48 + 16 * i, 8 ); memcpy(psShape->padfY+i, psSHP->pabyRec + 48 + 16 * i + 8, 8 ); if( bBigEndian ) SwapWord( 8, psShape->padfX + i ); if( bBigEndian ) SwapWord( 8, psShape->padfY + i ); } nOffset = 48 + 16*nPoints; /* -------------------------------------------------------------------- */ /* Get the X/Y bounds. */ /* -------------------------------------------------------------------- */ memcpy( &(psShape->dfXMin), psSHP->pabyRec + 8 + 4, 8 ); memcpy( &(psShape->dfYMin), psSHP->pabyRec + 8 + 12, 8 ); memcpy( &(psShape->dfXMax), psSHP->pabyRec + 8 + 20, 8 ); memcpy( &(psShape->dfYMax), psSHP->pabyRec + 8 + 28, 8 ); if( bBigEndian ) SwapWord( 8, &(psShape->dfXMin) ); if( bBigEndian ) SwapWord( 8, &(psShape->dfYMin) ); if( bBigEndian ) SwapWord( 8, &(psShape->dfXMax) ); if( bBigEndian ) SwapWord( 8, &(psShape->dfYMax) ); /* -------------------------------------------------------------------- */ /* If we have a Z coordinate, collect that now. */ /* -------------------------------------------------------------------- */ if( psShape->nSHPType == SHPT_MULTIPOINTZ ) { memcpy( &(psShape->dfZMin), psSHP->pabyRec + nOffset, 8 ); memcpy( &(psShape->dfZMax), psSHP->pabyRec + nOffset + 8, 8 ); if( bBigEndian ) SwapWord( 8, &(psShape->dfZMin) ); if( bBigEndian ) SwapWord( 8, &(psShape->dfZMax) ); for( i = 0; i < nPoints; i++ ) { memcpy( psShape->padfZ + i, psSHP->pabyRec + nOffset + 16 + i*8, 8 ); if( bBigEndian ) SwapWord( 8, psShape->padfZ + i ); } nOffset += 16 + 8*nPoints; } /* -------------------------------------------------------------------- */ /* If we have a M measure value, then read it now. We assume */ /* that the measure can be present for any shape if the size is */ /* big enough, but really it will only occur for the Z shapes */ /* (options), and the M shapes. */ /* -------------------------------------------------------------------- */ if( psSHP->panRecSize[hEntity]+8 >= nOffset + 16 + 8*nPoints ) { memcpy( &(psShape->dfMMin), psSHP->pabyRec + nOffset, 8 ); memcpy( &(psShape->dfMMax), psSHP->pabyRec + nOffset + 8, 8 ); if( bBigEndian ) SwapWord( 8, &(psShape->dfMMin) ); if( bBigEndian ) SwapWord( 8, &(psShape->dfMMax) ); for( i = 0; i < nPoints; i++ ) { memcpy( psShape->padfM + i, psSHP->pabyRec + nOffset + 16 + i*8, 8 ); if( bBigEndian ) SwapWord( 8, psShape->padfM + i ); } } } /* ==================================================================== */ /* Extract vertices for a point. */ /* ==================================================================== */ else if( psShape->nSHPType == SHPT_POINT || psShape->nSHPType == SHPT_POINTM || psShape->nSHPType == SHPT_POINTZ ) { int nOffset; psShape->nVertices = 1; psShape->padfX = (double *) calloc(1,sizeof(double)); psShape->padfY = (double *) calloc(1,sizeof(double)); psShape->padfZ = (double *) calloc(1,sizeof(double)); psShape->padfM = (double *) calloc(1,sizeof(double)); memcpy( psShape->padfX, psSHP->pabyRec + 12, 8 ); memcpy( psShape->padfY, psSHP->pabyRec + 20, 8 ); if( bBigEndian ) SwapWord( 8, psShape->padfX ); if( bBigEndian ) SwapWord( 8, psShape->padfY ); nOffset = 20 + 8; /* -------------------------------------------------------------------- */ /* If we have a Z coordinate, collect that now. */ /* -------------------------------------------------------------------- */ if( psShape->nSHPType == SHPT_POINTZ ) { memcpy( psShape->padfZ, psSHP->pabyRec + nOffset, 8 ); if( bBigEndian ) SwapWord( 8, psShape->padfZ ); nOffset += 8; } /* -------------------------------------------------------------------- */ /* If we have a M measure value, then read it now. We assume */ /* that the measure can be present for any shape if the size is */ /* big enough, but really it will only occur for the Z shapes */ /* (options), and the M shapes. */ /* -------------------------------------------------------------------- */ if( psSHP->panRecSize[hEntity]+8 >= nOffset + 8 ) { memcpy( psShape->padfM, psSHP->pabyRec + nOffset, 8 ); if( bBigEndian ) SwapWord( 8, psShape->padfM ); } /* -------------------------------------------------------------------- */ /* Since no extents are supplied in the record, we will apply */ /* them from the single vertex. */ /* -------------------------------------------------------------------- */ psShape->dfXMin = psShape->dfXMax = psShape->padfX[0]; psShape->dfYMin = psShape->dfYMax = psShape->padfY[0]; psShape->dfZMin = psShape->dfZMax = psShape->padfZ[0]; psShape->dfMMin = psShape->dfMMax = psShape->padfM[0]; } return( psShape ); } /************************************************************************/ /* SHPTypeName() */ /************************************************************************/ const char SHPAPI_CALL1(*) SHPTypeName( int nSHPType ) { switch( nSHPType ) { case SHPT_NULL: return "NullShape"; case SHPT_POINT: return "Point"; case SHPT_ARC: return "Arc"; case SHPT_POLYGON: return "Polygon"; case SHPT_MULTIPOINT: return "MultiPoint"; case SHPT_POINTZ: return "PointZ"; case SHPT_ARCZ: return "ArcZ"; case SHPT_POLYGONZ: return "PolygonZ"; case SHPT_MULTIPOINTZ: return "MultiPointZ"; case SHPT_POINTM: return "PointM"; case SHPT_ARCM: return "ArcM"; case SHPT_POLYGONM: return "PolygonM"; case SHPT_MULTIPOINTM: return "MultiPointM"; case SHPT_MULTIPATCH: return "MultiPatch"; default: return "UnknownShapeType"; } } /************************************************************************/ /* SHPPartTypeName() */ /************************************************************************/ const char SHPAPI_CALL1(*) SHPPartTypeName( int nPartType ) { switch( nPartType ) { case SHPP_TRISTRIP: return "TriangleStrip"; case SHPP_TRIFAN: return "TriangleFan"; case SHPP_OUTERRING: return "OuterRing"; case SHPP_INNERRING: return "InnerRing"; case SHPP_FIRSTRING: return "FirstRing"; case SHPP_RING: return "Ring"; default: return "UnknownPartType"; } } /************************************************************************/ /* SHPDestroyObject() */ /************************************************************************/ void SHPAPI_CALL SHPDestroyObject( SHPObject * psShape ) { if( psShape == NULL ) return; if( psShape->padfX != NULL ) free( psShape->padfX ); if( psShape->padfY != NULL ) free( psShape->padfY ); if( psShape->padfZ != NULL ) free( psShape->padfZ ); if( psShape->padfM != NULL ) free( psShape->padfM ); if( psShape->panPartStart != NULL ) free( psShape->panPartStart ); if( psShape->panPartType != NULL ) free( psShape->panPartType ); free( psShape ); } /************************************************************************/ /* SHPRewindObject() */ /* */ /* Reset the winding of polygon objects to adhere to the */ /* specification. */ /************************************************************************/ int SHPAPI_CALL SHPRewindObject( SHPHandle hSHP, SHPObject * psObject ) { int iOpRing, bAltered = 0; /* -------------------------------------------------------------------- */ /* Do nothing if this is not a polygon object. */ /* -------------------------------------------------------------------- */ if( psObject->nSHPType != SHPT_POLYGON && psObject->nSHPType != SHPT_POLYGONZ && psObject->nSHPType != SHPT_POLYGONM ) return 0; if( psObject->nVertices == 0 || psObject->nParts == 0 ) return 0; /* -------------------------------------------------------------------- */ /* Process each of the rings. */ /* -------------------------------------------------------------------- */ for( iOpRing = 0; iOpRing < psObject->nParts; iOpRing++ ) { int bInner, iVert, nVertCount, nVertStart, iCheckRing; double dfSum, dfTestX, dfTestY; /* -------------------------------------------------------------------- */ /* Determine if this ring is an inner ring or an outer ring */ /* relative to all the other rings. For now we assume the */ /* first ring is outer and all others are inner, but eventually */ /* we need to fix this to handle multiple island polygons and */ /* unordered sets of rings. */ /* -------------------------------------------------------------------- */ dfTestX = psObject->padfX[psObject->panPartStart[iOpRing]]; dfTestY = psObject->padfY[psObject->panPartStart[iOpRing]]; bInner = FALSE; for( iCheckRing = 0; iCheckRing < psObject->nParts; iCheckRing++ ) { int iEdge; if( iCheckRing == iOpRing ) continue; nVertStart = psObject->panPartStart[iCheckRing]; if( iCheckRing == psObject->nParts-1 ) nVertCount = psObject->nVertices - psObject->panPartStart[iCheckRing]; else nVertCount = psObject->panPartStart[iCheckRing+1] - psObject->panPartStart[iCheckRing]; for( iEdge = 0; iEdge < nVertCount; iEdge++ ) { int iNext; if( iEdge < nVertCount-1 ) iNext = iEdge+1; else iNext = 0; if( (psObject->padfY[iEdge+nVertStart] < dfTestY && psObject->padfY[iNext+nVertStart] >= dfTestY) || (psObject->padfY[iNext+nVertStart] < dfTestY && psObject->padfY[iEdge+nVertStart] >= dfTestY) ) { if( psObject->padfX[iEdge+nVertStart] + (dfTestY - psObject->padfY[iEdge+nVertStart]) / (psObject->padfY[iNext+nVertStart] - psObject->padfY[iEdge+nVertStart]) * (psObject->padfX[iNext+nVertStart] - psObject->padfX[iEdge+nVertStart]) < dfTestX ) bInner = !bInner; } } } /* -------------------------------------------------------------------- */ /* Determine the current order of this ring so we will know if */ /* it has to be reversed. */ /* -------------------------------------------------------------------- */ nVertStart = psObject->panPartStart[iOpRing]; if( iOpRing == psObject->nParts-1 ) nVertCount = psObject->nVertices - psObject->panPartStart[iOpRing]; else nVertCount = psObject->panPartStart[iOpRing+1] - psObject->panPartStart[iOpRing]; dfSum = 0.0; for( iVert = nVertStart; iVert < nVertStart+nVertCount-1; iVert++ ) { dfSum += psObject->padfX[iVert] * psObject->padfY[iVert+1] - psObject->padfY[iVert] * psObject->padfX[iVert+1]; } dfSum += psObject->padfX[iVert] * psObject->padfY[nVertStart] - psObject->padfY[iVert] * psObject->padfX[nVertStart]; /* -------------------------------------------------------------------- */ /* Reverse if necessary. */ /* -------------------------------------------------------------------- */ if( (dfSum < 0.0 && bInner) || (dfSum > 0.0 && !bInner) ) { int i; bAltered++; for( i = 0; i < nVertCount/2; i++ ) { double dfSaved; /* Swap X */ dfSaved = psObject->padfX[nVertStart+i]; psObject->padfX[nVertStart+i] = psObject->padfX[nVertStart+nVertCount-i-1]; psObject->padfX[nVertStart+nVertCount-i-1] = dfSaved; /* Swap Y */ dfSaved = psObject->padfY[nVertStart+i]; psObject->padfY[nVertStart+i] = psObject->padfY[nVertStart+nVertCount-i-1]; psObject->padfY[nVertStart+nVertCount-i-1] = dfSaved; /* Swap Z */ if( psObject->padfZ ) { dfSaved = psObject->padfZ[nVertStart+i]; psObject->padfZ[nVertStart+i] = psObject->padfZ[nVertStart+nVertCount-i-1]; psObject->padfZ[nVertStart+nVertCount-i-1] = dfSaved; } /* Swap M */ if( psObject->padfM ) { dfSaved = psObject->padfM[nVertStart+i]; psObject->padfM[nVertStart+i] = psObject->padfM[nVertStart+nVertCount-i-1]; psObject->padfM[nVertStart+nVertCount-i-1] = dfSaved; } } } } return bAltered; } ================================================ FILE: test/address.rb ================================================ $LOAD_PATH.unshift '../lib' require 'test/unit' require 'set' require 'geocoder/us/address' include Geocoder::US class TestAddress < Test::Unit::TestCase def test_new addr = Address.new("1600 Pennsylvania Av., Washington DC") assert_equal "1600 Pennsylvania Av, Washington DC", addr.text end def test_clean fixtures = [ [ "cleaned text", "cleaned: text!" ], [ "cleaned-text 2", "cleaned-text: #2?" ], [ "it's working 1/2", "~it's working 1/2~" ], [ "it's working, yes", "it's working, yes...?" ], [ "it's working & well", "it's working & well?" ] ] fixtures.each {|output, given| assert_equal output, Address.new(given).text } end def test_expand_numbers num_list = ["5", "fifth", "five"] num_list.each {|n| addr = Address.new(n) assert_equal num_list, addr.expand_numbers(n).to_a.sort } end def test_city_parse places = [ [ "New York, NY", "New York", "NY", "" ], [ "NY", "", "NY", "" ], [ "New York", "New York", "NY", "" ], [ "Philadelphia", "Philadelphia", "", "" ], [ "Philadelphia PA", "Philadelphia", "PA", "" ], [ "Philadelphia, PA", "Philadelphia", "PA", "" ], [ "Philadelphia, Pennsylvania", "Philadelphia", "PA", "" ], [ "Philadelphia, Pennsylvania 19131", "Philadelphia", "PA", "19131" ], [ "Philadelphia 19131", "Philadelphia", "", "19131" ], [ "Pennsylvania 19131", "Pennsylvania", "PA", "19131" ], # kind of a misfeature [ "19131", "", "", "19131" ], [ "19131-9999", "", "", "19131" ], ] for fixture in places addr = Address.new fixture[0] [:city, :state, :zip].zip(fixture[1..3]).each {|key,val| result = addr.send key result = [result.downcase] unless result.kind_of? Array if result.empty? assert_equal val, "", key.to_s + " test no result " + fixture.join("/") else assert result.member?(val.downcase), key.to_s + " test " + result.inspect + fixture.join("/") end } end end def test_po_box addr_po = Address.new "PO Box 1111 Herndon VA 20171" assert addr_po.po_box?, true end def test_parse addrs = [ {:text => "1600 Pennsylvania Av., Washington DC 20050", :number => "1600", :street => "Pennsylvania Ave", :city => "Washington", :state => "DC", :zip => "20050"}, {:text => "1600 Pennsylvania, Washington DC", :number => "1600", :street => "Pennsylvania", :city => "Washington", :state => "DC"}, {:text => "1600 Pennsylvania Washington DC", :number => "1600", :street => "Pennsylvania Washington", :city => "Pennsylvania Washington", # FIXME :state => "DC"}, {:text => "1600 Pennsylvania Washington", :number => "1600", :street => "Pennsylvania", :city => "Washington", :state => "WA"}, # FIXME {:text => "1600 Pennsylvania 20050", :number => "1600", :street => "Pennsylvania", # FIXME :zip => "20050"}, {:text => "1600 Pennsylvania Av, 20050-9999", :number => "1600", :street => "Pennsylvania Ave", :zip => "20050"}, {:text => "1005 Gravenstein Highway North, Sebastopol CA", :number => "1005", :street => "Gravenstein Hwy N", :city => "Sebastopol", :state => "CA"}, {:text => "100 N 7th St, Brooklyn", :number => "100", :street => "N 7 St", :city => "Brooklyn"}, {:text => "100 N Seventh St, Brooklyn", :number => "100", :street => "N 7 St", :city => "Brooklyn"}, {:text => "100 Central Park West, New York, NY", :number => "100", :street => "Central Park W", :city => "New York", :state => "NY"}, {:text => "100 Central Park West, 10010", :number => "100", :street => "Central Park W", :zip => "10010"}, {:text => "1400 Avenue of the Americas, New York, NY 10019", :number => "1400", :street => "Ave of the Americas", :city => "New York", :state => "NY"}, {:text => "1400 Avenue of the Americas, New York", :number => "1400", :street => "Ave of the Americas", :city => "New York"}, {:text => "1400 Ave of the Americas, New York", :number => "1400", :street => "Ave of the Americas", :city => "New York"}, {:text => "1400 Av of the Americas, New York", :number => "1400", :street => "Ave of the Americas", :city => "New York"}, {:text => "1400 Av of the Americas New York", :number => "1400", :street => "Ave of the Americas", :city => "New York"}, ] for fixture in addrs text = fixture.delete(:text) addr = Address.new(text) for key, val in fixture result = addr.send key if result.kind_of? Array result.map! {|str| str.downcase} assert result.member?(val.downcase), "#{text} (#{key}) = #{result.inspect}" else assert_equal val, result, "#{text} (#{key}) = #{result.inspect}" end end end end def test_skip_parse addresses = [ {:street => "1233 Main St", :city => "Springfield", :region => "VA", :postal_code => "12345", :final_number => "1233", :parsed_street => "main st"}, {:street => "somewhere Ln", :city => "Somewhere", :region => "WI", :postal_code => "22222", :number => "402", :parsed_street => "somewhere ln", :final_number => "402"}, ] for preparsed_address in addresses address_for_geocode = Address.new preparsed_address assert_equal preparsed_address[:parsed_street],address_for_geocode.street[0] assert_equal preparsed_address[:final_number],address_for_geocode.number assert_equal preparsed_address[:city],address_for_geocode.city[0] assert_equal preparsed_address[:region],address_for_geocode.state assert_equal preparsed_address[:postal_code],address_for_geocode.zip end end def test_states_abbreviated_in_skip_parse addresses = [ {:street => "123 Main St", :city => "Springfield", :region => "Virginia", :postal_code => "12345",:state_abbrev => "VA"}, {:street => "402 Somewhere Ln", :city => "Somewhere", :region => "WI", :postal_code => "22222", :state_abbrev => "WI"}, ] for preparsed_address in addresses address_for_geocode = Address.new preparsed_address assert_equal preparsed_address[:state_abbrev],address_for_geocode.state end end def test_address_hash addresses = [ {:address => "Herndon, VA", :place_check => ["herndon"]}, {:address => "Arlington, VA", :place_check => ["arlington"]} ] for preparsed_address in addresses address_for_geocode = Address.new preparsed_address assert_equal preparsed_address[:place_check],address_for_geocode.city end end def test_partial_address addresses = [ {:street => "2200 Wilson Blvd", :postal_code => "22201"}, ] for preparsed_address in addresses address_for_geocode = Address.new preparsed_address assert_equal preparsed_address[:postal_code],address_for_geocode.zip end end def test_country_parse addresses = [ {:city => "Paris", :country => "FR"}, ] for preparsed_address in addresses address_for_geocode = Address.new preparsed_address assert_equal preparsed_address[:country],address_for_geocode.state end end end ================================================ FILE: test/benchmark.rb ================================================ #!/usr/bin/ruby require 'test/unit' require 'geocoder/us/database' require 'benchmark' include Benchmark # we need the CAPTION and FMTSTR constants db = Geocoder::US::Database.new("/mnt/tiger2008/geocoder.db") n = 50 s = "1005 Gravenstein Hwy N, Sebastopol CA 95472" a = Geocoder::US::Address.new(s) print db.geocode(s) Benchmark.bmbm do |x| x.report("parse max_penalty=0") { n.times{a.parse(0)} } x.report("parse max_penalty=1") { n.times{a.parse(1)} } x.report("geocode") { n.times{db.geocode(s)} } end ================================================ FILE: test/constants.rb ================================================ $LOAD_PATH.unshift '../lib' require 'test/unit' require 'geocoder/us/constants' include Geocoder::US class TestConstants < Test::Unit::TestCase def initialize (*args) @map = Map[ "Abbreviation" => "abbr", "Two words" => "2words", "Some three words" => "3words" ] super(*args) end def test_class_constructor assert_kind_of Map, @map assert_kind_of Hash, @map end def test_key assert @map.key?( "Abbreviation" ) assert @map.key?( "abbreviation" ) assert !(@map.key? "abbreviation?") assert @map.key?( "abbr" ) assert @map.key?( "Two words" ) assert @map.key?( "2words" ) end def test_fetch assert_equal "abbr", @map["Abbreviation"] assert_equal "abbr", @map["abbreviation"] assert_nil @map["abbreviation?"] assert_equal "abbr", @map["abbr"] assert_equal "2words", @map["Two words"] assert_equal "2words", @map["2words"] end # def test_partial # assert @map.partial?( "Abbreviation" ) # assert @map.partial?( "Two" ) # assert @map.partial?( "two" ) # assert !(@map.partial? "words") # assert @map.partial?( "Some" ) # assert !(@map.partial? "words") # assert @map.partial?( "Some three" ) # assert @map.partial?( "SOME THREE WORDS" ) # end def test_constants assert_kind_of Map, Directional assert_kind_of Map, Prefix_Qualifier assert_kind_of Map, Suffix_Qualifier assert_kind_of Map, Prefix_Type assert_kind_of Map, Suffix_Type assert_kind_of Map, Unit_Type assert_kind_of Map, Name_Abbr assert_kind_of Map, State end end ================================================ FILE: test/data/address-sample.csv ================================================ address,number,predir,prequal,pretyp,street,suftyp,sufqual,sufdir,unittyp,unit,city,state,zip,lon,lat,count,comment "93 NORTH 9TH STREET, BROOKLYN NY 11211",93,N,,,9th,St,,,,,Brooklyn,NY,11211,,,, "380 WESTMINSTER ST, PROVIDENCE RI 02903",380,,,,Westminster,St,,,,,Providence,RI,02903,,,, "177 MAIN STREET, LITTLETON NH 03561",177,,,,Main,St,,,,,Littleton,NH,03561,,,, "202 HARLOW ST, BANGOR ME 04401",202,,,,Harlow,St,,,,,Bangor,ME,04401,,,, "46 FRONT STREET, WATERVILLE, ME 04901",46,,,,Front,St,,,,,Waterville,ME,04901,,,, "22 SUSSEX ST, HACKENSACK NJ 07601",22,,,,Sussex,St,,,,,Hackensack,NJ,07601,,,, "75 OAK STREET, PATCHOGUE NY 11772",75,,,,Oak,St,,,,,Patchogue,NY,11772,,,, "1 CLINTON AVE, ALBANY NY 12207",1,,,,Clinton,Ave,,,,,Albany,NY,12207,,,, "7242 ROUTE 9, PLATTSBURGH NY 12901",7242,,,US Hwy,9,,,,,,Plattsburgh,NY,12901,,,, "520 5TH AVE, MCKEESPORT PA 15132",520,,,,5th,Ave,,,,,McKeesport,PA,15132,,,, "122 W 3RD STREET, GREENSBURG PA 15601",122,W,,,3rd,St,,,,,Greensburg,PA,15601,,,, "901 UNIVERSITY DR, STATE COLLEGE PA 16801",901,,,,University,Dr,,,,,"State College",PA,16801,,,, "240 W 3RD ST, WILLIAMSPORT PA 17701",240,W,,,3rd,St,,,,,Williamsport,PA,17701,,,, "41 N 4TH ST, ALLENTOWN PA 18102",41,N,,,4th,St,,,,,Allentown,PA,18102,,,, "2221 W. MARKET STREET, POTTSVILLE PA 17901",2221,W,,,Market,St,,,,,Pottsville,PA,17901,,,, "337 BRIGHTSEAT ROAD, LANDOVER MD 20785",337,,,,Brightseat,Rd,,,,,Hyattsville,MD,20785,,,,"canonical place" "101 CHESAPEAKE BLVD, ELKTON MD 21921",103,,,,Chesapeake,Blvd,,,,,Elkton,MD,21921,,,,"find nearest corner" "2875 SABRE ST, VIRGINIA BEACH VA 23452",2809,,,,Sabre,St,,,,,"Virginia Beach",VA,23452,,,,"find nearest corner" "324 COMMERCE ROAD, FARMVILLE VA 23901",324,,,,Commerce,St,,,,,Clarksville,VA,23927,,,,"nearby address; might be TIGER omission" "1480 EAST MAIN STREET, WYTHEVILLE VA 24382",1480,W,,,Main,St,,,,,Wytheville,VA,24382,,,,"nearby address; TIGER omission" "116 N JEFFERSON STREET, ROANOKE VA 24016",116,N,,,Jefferson,St,,,,,Roanoke,VA,24016,,,, "50 MCDOWELL STREET, WELCH WV 24801",50,,,,"Mc Dowell",St,,,,,Welch,WV,24801,,,, "146 EAST FIRST AVE, WILLIAMSON WV 25661",200,E,,,1st,Ave,,,,,Williamson,WV,25661,,,,"find nearest corner" "1925 E MAIN ST, ALBEMARLE NC 28001",1925,E,,,Main,St,,,,,Albemarle,NC,28001,,,, "1013 SPRING LANE, SANFORD NC 27330",1013,,,,Spring,Ln,,,,,Sanford,NC,27330,,,, "145 ROWAN STREET, FAYETTEVILLE NC 28301",145,,,,Rowan,St,,,,,Fayetteville,NC,28301,,,, "1420 MCCARTHY BLVD, NEW BERN NC 28562",1420,,,,McCarthy,Blvd,,,,,"New Bern",NC,28562,,,, "115 ENTERPRISE COURT, GREENWOOD SC 29649",115,,,,Enterprise,Ct,,,,,Greenwood,SC,29649,,,, "732 W 2ND ST, TIFTON GA 31794",732,,,,2nd,St,,W,,,Tifton,GA,31793,,,,"TIGER artifact" "97 WEST OAK AVE, PANAMA CITY FL 32401",97,,,,Oak,Ave,,,,,"Panama City",FL,32401,,,,"predir is TIGER artifact" "2276 WILTON DR, WILTON MANORS FL 33305",2276,,,,Wilton,Dr,,,,,"Fort Lauderdale",FL,33305,,,,"canonical place" "203 SOUTH WALNUT ST, FLORENCE AL 35630",203,S,,,Walnut,St,,,,,Florence,AL,35630,,,, "108 CENTER POINTE DR, CLARKSVILLE TN 37040",108,,,,"Center Pointe",Dr,,,,,Clarksville,TN,37040,,,, "1800 OLD TROY RD, UNION CITY TN 38261",1800,,Old,,Troy,Rd,,,,,"Union City",TN,38261,,,, "931 OLD SMITHVILLE HWY, MCMINNVILLE TN 37110",931,,Old,,Smithville,Rd,,,,,McMinnville,TN,37110,,,, "1301 GREENE STREET, MARIETTA OH 45750",1301,,,,Greene,St,,,,,Marietta,OH,45750,,,, "602 SOUTH MICHIGAN ST, SOUTH BEND IN 46601",602,S,,,Michigan,St,,,,,"South Bend",IN,46601,,,, "500 NORTH A STREET, RICHMOND IN 47374",500,N,,,A,St,,,,,Richmond,IN,47374,,,, "317 SOUTH DRAKE ROAD, KALAMAZOO MI 49009",317,S,,,Drake,Rd,,,,,Kalamazoo,MI,49009,,,, "105 Amity Way, Wayne PA 19087",105,,,,Amity,Dr,,,,,Wayne,PA,19087,,,, "305 W 45th St, New York NY 10036",305,W,,,45,St,,,,,"New York",NY,10036,,,, "11839 Federalist Way, Fairfax VA 22030",11839,,,,Federalist,Way,,,,,Fairfax,VA,22030,,,, "400 Monroe St, Hoboken, NJ 07030",400,,,,Monroe,St,,,,,Hoboken,NJ,07030,,,, "101 West End Avenue, New York NY 10023",101,W,,,End,Ave,,,,,"New York",NY,10023,,,,"predir is TIGER artifact" "2900 4TH AVE, BILLINGS MT 59101",2900,,,,4th,Ave,,N,,,Billings,MT,59101,,,,"returns 2 results" "158 N SCOTT STREET, JOLIET IL 60432",158,N,,,Scott,St,,,,,Joliet,IL,60432,,,, "1207 NETWORK CENTRE DR, EFFINGHAM IL 62401",1207,,,,"Network Centre",Dr,,,,,Effingham,IL,62401,,,, "3555 SOUTHERN HILLS DR, SIOUX CITY IA 51106",3555,,,,"Southern Hills",Dr,,,,,"Sioux City",IA,51106,,,, "300 E 3RD ST, NORTH PLATTE NE 69101",300,E,,,3rd,St,,,,,"North Platte",NE,69101,,,, "115 N WEBB RD, GRAND ISLAND NE 68803",115,N,,,Webb,Rd,,,,,"Grand Island",NE,68803,,,, "415 VALLEY VIEW DR, SCOTTSBLUFF NE 69361",501,,,,"Valley View",Dr,,,,,"Scottsbluff",NE,69361,,,,"find nearest corner" ================================================ FILE: test/data/db-test.csv ================================================ address,number,street,city,state,zip,lon,lat,count,comment "93 NORTH 9TH STREET, BROOKLYN NY 11211",93,N 9th St,Brooklyn,NY,11211,-73.958096,40.720064,1, "380 WESTMINSTER ST, PROVIDENCE RI 02903",380,Westminster St,Providence,RI,02903,-71.415171,41.821004,1, "177 MAIN STREET, LITTLETON NH 03561",177,Main St,Littleton,NH,03561,-71.776393,44.307299,1,range "202 HARLOW ST, BANGOR ME 04401",202,Harlow St,Bangor,ME,04401,-68.773934,44.805202,1, "46 FRONT STREET, WATERVILLE, ME 04901",46,Front St,Waterville,ME,04901,-69.628598,44.550988,1, "22 SUSSEX ST, HACKENSACK NJ 07601",22,Sussex St,Hackensack,NJ,07601,-74.04821,40.880328,1, "75 OAK STREET, PATCHOGUE NY 11772",75,Oak St,Patchogue,NY,11772,-73.01036,40.768522,1, "1 CLINTON AVE, ALBANY NY 12207",1,Clinton Ave,Albany,NY,12207,-73.750031,42.654244,1, "7242 ROUTE 9, PLATTSBURGH NY 12901",7242,US Hwy 9,Plattsburgh,NY,12901,-73.428066,44.735338,1, "520 5TH AVE, MCKEESPORT PA 15132",520,5th Ave,McKeesport,PA,15132,-79.861023,40.351228,1, "122 W 3RD STREET, GREENSBURG PA 15601",122,W 3rd St,Greensburg,PA,15601,-79.546244,40.299681,1, "901 UNIVERSITY DR, STATE COLLEGE PA 16801",901,University Dr,State College,PA,16801,-77.844056,40.797191,1, "240 W 3RD ST, WILLIAMSPORT PA 17701",240,W 3rd St,Williamsport,PA,17701,-77.005601,41.238969,1, "41 N 4TH ST, ALLENTOWN PA 18102",41,N 4th St,Allentown,PA,18102,-75.466113,40.605368,1, "2221 W. MARKET STREET, POTTSVILLE PA 17901",2221,W Market St,Pottsville,PA,17901,-76.226401,40.674702,1, "337 BRIGHTSEAT ROAD, LANDOVER MD 20785",337,Brightseat Rd,Hyattsville,MD,20785,-76.850995,38.892762,1,canonical place "101 CHESAPEAKE BLVD, ELKTON MD 21921",109,Chesapeake Blvd,Elkton,MD,21921,-75.786853,39.6045,1,find nearest corner "2875 SABRE ST, VIRGINIA BEACH VA 23452",2809,Sabre St,Virginia Beach,VA,23452,-76.067835,36.822959,1,find nearest corner "324 COMMERCE ROAD, FARMVILLE VA 23901",324,Commerce Rd,Farmville,VA,23901,-78.423296,37.273311,1,fixed in TIGER 2010 "1480 EAST MAIN STREET, WYTHEVILLE VA 24382",1168,E Main St,Wytheville,VA,24382,-81.069279,36.951346,1,nearby address; TIGER omission "116 N JEFFERSON STREET, ROANOKE VA 24016",116,N Jefferson St,Roanoke,VA,24016,-79.940537,37.275163,1, "50 MCDOWELL STREET, WELCH WV 24801",50,Mc Dowell St,Welch,WV,24801,-81.585586,37.433465,1, "146 EAST FIRST AVE, WILLIAMSON WV 25661",200,E 1st Ave,Williamson,WV,25661,-82.277886,37.670798,1,find nearest corner "1925 E MAIN ST, ALBEMARLE NC 28001",1925,E Main St,Albemarle,NC,28001,-80.163859,35.348818,1, "1013 SPRING LANE, SANFORD NC 27330",1013,Spring Ln,Sanford,NC,27330,-79.198776,35.487444,1, "145 ROWAN STREET, FAYETTEVILLE NC 28301",145,Rowan St,Fayetteville,NC,28301,-78.878696,35.057767,1, "1420 MCCARTHY BLVD, NEW BERN NC 28562",1399,McCarthy Blvd,New Bern,NC,28562,-77.094901,35.097183,1,broken in TIGER 2010 "115 ENTERPRISE COURT, GREENWOOD SC 29649",115,Enterprise Ct,Greenwood,SC,29649,-82.164828,34.216732,1, "732 W 2ND ST, TIFTON GA 31794",732,W 2nd St,Tifton,GA,31794,-83.523812,31.457889,1,ZIP was fixed in TIGER 2010 "97 WEST OAK AVE, PANAMA CITY FL 32401",95,W Oak Ave,Panama City,FL,32401,-85.661436,30.154306,1,broken in TIGER 2010 "2276 WILTON DR, WILTON MANORS FL 33305",2276,Wilton Dr,Fort Lauderdale,FL,33305,-80.137273,26.156993,1,canonical place "203 SOUTH WALNUT ST, FLORENCE AL 35630",203,S Walnut St,Florence,AL,35630,-87.670768,34.800112,1, "108 CENTER POINTE DR, CLARKSVILLE TN 37040",108,Center Pointe Dr,Clarksville,TN,37040,-87.30888,36.56967,1, "1800 OLD TROY RD, UNION CITY TN 38261",1800,Old Troy Rd,Union City,TN,38261,-89.083201,36.416592,1, "931 OLD SMITHVILLE HWY, MCMINNVILLE TN 37110",931,Old Smithville Rd,McMinnville,TN,37110,-85.788518,35.701731,1, "1301 GREENE STREET, MARIETTA OH 45750",1301,Greene St,Marietta,OH,45750,-81.424821,39.426052,1, "602 SOUTH MICHIGAN ST, SOUTH BEND IN 46601",598,S Michigan St,South Bend,IN,46601,-86.25025,41.670964,1,broken in TIGER 2010 "500 NORTH A STREET, RICHMOND IN 47374",500,N A St,Richmond,IN,47374,-84.89517,39.830625,1, "317 SOUTH DRAKE ROAD, KALAMAZOO MI 49009",317,S Drake Rd,Kalamazoo,MI,49009,-85.648132,42.288772,1, "105 Amity Way, Wayne PA 19087",105,Amity Dr,Wayne,PA,19087,-75.455425,40.076446,1, "305 W 45th St, New York NY 10036",305,W 45 St,New York,NY,10036,-73.991106,40.760371,1, "11839 Federalist Way, Fairfax VA 22030",11839,Federalist Way,Fairfax,VA,22030,-77.353695,38.849858,1, "400 Monroe St, Hoboken, NJ 07030",400,Monroe St,Hoboken,NJ,07030,-74.038654,40.743789,1, "101 West End Avenue, New York NY 10023",101,W End Ave,New York,NY,10023,-73.987822,40.775325,1,predir is TIGER artifact "2900 4TH AVE, BILLINGS MT 59101",2900,4th Ave N,Billings,MT,59101,-108.51073,45.783452,2,returns 2 results "158 N SCOTT STREET, JOLIET IL 60432",158,N Scott St,Joliet,IL,60432,-88.080083,41.526353,1, "1207 NETWORK CENTRE DR, EFFINGHAM IL 62401",1207,Network Centre Dr,Effingham,IL,62401,-88.526702,39.143248,1, "3555 SOUTHERN HILLS DR, SIOUX CITY IA 51106",3555,Southern Hills Dr,Sioux City,IA,51106,-96.353014,42.449259,1, "300 E 3RD ST, NORTH PLATTE NE 69101",300,E 3rd St,North Platte,NE,69101,-100.761028,41.135235,1, "115 N WEBB RD, GRAND ISLAND NE 68803",115,N Webb Rd,Grand Island,NE,68803,-98.378361,40.917627,1, "415 VALLEY VIEW DR, SCOTTSBLUFF NE 69361",501,Valley View Dr,Scottsbluff,NE,69361,-103.656078,41.879011,1,find nearest corner "4018 W Ustick Rd, Meridian ID",4018,W Ustick Rd,Meridian,ID,83646,-116.443792,43.634096,1,fixed in TIGER 2010 "2518 S Pacific Hwy, Medford OR",2518,S Pacific Hwy,Medford,OR,97501,-122.855426,42.307241,1,fixed in TIGER 2010 "1111 River Rd Apt A17, Edgewater NJ 07020",1111,River Rd,Edgewater,NJ,07020,-73.972261,40.830852,1,FIXME: parsing "460 West St, Amherst MA 01002-2964",460,West St,Amherst,MA,01002,-72.520228,42.34014,1,address is all abbreviations "23 2nd St, Brooklyn NY",23,2nd St,Brooklyn,NY,11231,-73.993897,40.67895,1,regression caused it to point to East Otto "23 2nd St, Brooklyn, New York",23,2nd St,Brooklyn,NY,11231,-73.993897,40.67895,1,regression caused it to point to Manhattan "100 Central Park W, 10023",100,Central Park W,New York,NY,10023,-73.975461,40.776899,1,the usual Central Park West parsing issues "100 Central Park W, New York",100,Central Park W,New York,NY,10023,-73.975461,40.776899,1,the usual Central Park West parsing issues ================================================ FILE: test/data/locations.csv ================================================ name,address "Home","2026 21st St. N, Arlington, VA 22201" "Work","2200 Wilson Blvd., Arlington, VA 22201" "RTI","1506 N Main St., Royal Oak, MI 48067" ================================================ FILE: test/database.rb ================================================ $LOAD_PATH.unshift '../lib' require 'test/unit' require 'geocoder/us/database' require 'csv' Base = File.dirname(__FILE__) Debug = false module Geocoder::US Database_File = ( (ARGV[0] and !ARGV[0].empty?) ? ARGV[0] : "../geocoderdata/geocoder.db") end class TestDatabase < Test::Unit::TestCase def get_db Geocoder::US::Database.new(Geocoder::US::Database_File, {:debug => Debug}) end # def get_international_db # Geocoder::US::Database.new("/Users/katechapman/Desktop/geonames1.db", {:debug => true}) # end def setup @db = get_db #@db_intl = get_international_db #assert_not_nil @db_intl assert_not_nil @db end def test_load return if @db.nil? assert_kind_of Geocoder::US::Database, @db end def test_zip return if @db.nil? [ {:city=>"Chicago", :zip=>"60601", :state=>"IL", :precision=>:zip, :fips_county=>"17031", :lon=>-87.622130,:lat=>41.885310, :score => 0.714}, {:city=>"Philadelphia", :zip=>"19019", :state=>"PA", :precision=>:zip, :fips_county=>"42101", :lon=>-75.11787, :lat=>40.001811, :score => 0.714} ].each {|record| result = @db.geocode(record[:zip]) assert_equal result.length, 1 record.keys.each {|key| assert_equal record[key], result[0][key]} } end # def test_international_place # return if @db_intl.nil? # [ {:city=>"Paris", :state=>"FR"}, # {:city=>"Paris", :state=>"FR"} # ].each {|record| # result = @db_intl.geocode(record) # assert_equal result.length, 1 # record.keys.each {|key| assert_equal record[key], result[0][key]} # } # end def test_place return if @db.nil? [ {:city=>"Chicago", :state=>"IL", :precision=>:city, :fips_county=>"17031", :score => 0.857}, {:city=>"Philadelphia", :state=>"PA", :precision=>:city, :fips_county=>"42101", :score => 0.857} ].each {|record| result = @db.geocode(record[:city] + ", " + record[:state]) assert_equal result.length, 1 record.keys.each {|key| assert_equal record[key], result[0][key]} } end # def test_international_place # return if @db_intl.nil? # [ {:city=>"Kabul", :state=>"AF", :precision=>:city}, # {:city=>"Paris", :state=>"FR", :precision=>:city} # ].each {|record| # result = @db_intl.geocode({:city => record[:city] , :state => record[:state]}) # puts result # assert_equal result.length, 1 # record.keys.each {|key| assert_equal record[key], result[0][key]} # } # end def test_sample return if @db.nil? # This test won't run properly on 1.8.7 or lower (?) - APS return if RUBY_VERSION.split(".")[1] <= '8' CSV.foreach(Base + "/data/db-test.csv", {:headers=>true}) do |row| result = @db.geocode(row[0], true) result[0][:count] = result.map{|a|[a[:lat], a[:lon]]}.to_set.length fields = row.headers - ["comment", "address"] fields.each {|f| sample = row[f] || "" given = result[0][f.to_sym] || "" sample = sample.to_f if given.kind_of? Float or given.kind_of? Fixnum assert_equal sample, given, "row: #{row.inspect}\nfield: #{f.inspect} sample: #{sample.inspect}, given: #{given.inspect}" } end end def test_city_with_street_type_in_name result = @db.geocode("Mountain View, CA") assert_equal result.length, 1 assert_equal result[0][:city], "Mountain View" # (and not "Mountain View Acres, CA") assert_equal result[0][:state], "CA" end def test_should_get_street_number_correctly result = @db.geocode("460 West St, Amherst MA 01002-2964", true) assert_equal '460', result[0][:number] end def test_should_geocode_with_hash result = @db.geocode({:street => "2200 Wilson Blvd", :city => "Arlington", :region => "VA", :postal_code => "22201"}, true) result2 = @db.geocode("2200 Wilson Blvd, Arlington, VA 22201") assert_equal result2,result end def test_should_work_with_partial_hash result = @db.geocode({:street => "2200 Wilson Blvd", :postal_code => "22201"}) assert_equal result[0][:precision],:range end def test_weird_edge_case_explosion result = @db.geocode({:street => "1410 Spring Hill Rd", :postal_code => "20221"}) result1 = @db.geocode(:street => "402 Valley View Ave", :postal_code => "12345") assert_equal result[0][:precision],:zip end def test_city_state_together result = @db.geocode({:city => "Richmond", :state => "IN"}) assert_equal result[0][:precision],:city end def test_state_street_together result = @db.geocode({:region => "VA", :street => "14333 Lee Jackson Memorial Hwy"}) #assert_equal result[0][:precision],:range end def test_intersection result = @db.geocode("Decatur St and Bryant St, San Francisco, CA 94103") assert_equal result[0][:precision], :intersection end end ================================================ FILE: test/generate.rb ================================================ #!/usr/bin/ruby require 'test/unit' require 'geocoder/us/database' require 'fastercsv' db = Geocoder::US::Database.new("/mnt/tiger2008/geocoder.db", "/home/sderle/geocoder/lib/libsqlite3_geocoder.so") if ARGV.length == 1 result = db.geocode(ARGV[0], 0, 50) p result else FasterCSV.open(ARGV[1], "w", {:headers => true, :write_headers => true}) do |output| FasterCSV.foreach(ARGV[0], {:headers => true}) do |row| result = db.geocode(row[0]) count = result.map{|a|[a[:lat], a[:lon]]}.to_set.length if !result.empty? row.headers[1..13].each_with_index {|f,i| if result[0][f.to_sym] != row[i+1] print "#{row[0]} !#{f} -> #{result[0][f]} != #{row[i+1]}\n" end } result[0][:count] = count result[0][:address] = row[0] result[0][:comment] = row[-1] columns = row.headers.map{|col|col.to_sym} output << result[0].values_at(*columns) else print "!!! #{row[0]}\n" end end end end ================================================ FILE: test/numbers.rb ================================================ $LOAD_PATH.unshift '../lib' require 'test/unit' require 'geocoder/us/numbers' include Geocoder::US class TestAddress < Test::Unit::TestCase def test_number_to_cardinal assert_equal 'one', Cardinals[1] assert_equal 'ten', Cardinals[10] assert_equal 'twelve', Cardinals[12] assert_equal 'eighty-seven', Cardinals[87] end def test_cardinal_to_number assert_equal 1, Cardinals['one'] assert_equal 1, Cardinals['One'] assert_equal 10, Cardinals['ten'] assert_equal 12, Cardinals['twelve'] assert_equal 87, Cardinals['eighty-seven'] assert_equal 87, Cardinals['eighty seven'] assert_equal 87, Cardinals['eightyseven'] end def test_number_to_ordinal assert_equal 'first', Ordinals[1] assert_equal 'second', Ordinals[2] assert_equal 'tenth', Ordinals[10] assert_equal 'twelfth', Ordinals[12] assert_equal 'twentieth', Ordinals[20] assert_equal 'twenty-second', Ordinals[22] assert_equal 'eighty-seventh', Ordinals[87] end def test_ordinal_to_number assert_equal 1, Ordinals['first'] assert_equal 1, Ordinals['First'] assert_equal 10, Ordinals['tenth'] assert_equal 12, Ordinals['twelfth'] assert_equal 73, Ordinals['seventy-third'] assert_equal 74, Ordinals['seventy fourth'] assert_equal 75, Ordinals['seventyfifth'] assert_equal nil, Ordinals['seventy-eleventh'] end end ================================================ FILE: test/run.rb ================================================ #!/usr/bin/ruby $LOAD_PATH << File.dirname(__FILE__) $LOAD_PATH << File.dirname(__FILE__) + "/../lib" puts "Loadpath=#{$LOAD_PATH.inspect}" require 'test/unit' require 'numbers' require 'constants' require 'address' require 'database'