diff --git a/docker-osmupdate/Dockerfile b/docker-osmupdate/Dockerfile index c12aa62..f83ca3f 100644 --- a/docker-osmupdate/Dockerfile +++ b/docker-osmupdate/Dockerfile @@ -21,7 +21,9 @@ RUN apt-get -y install ca-certificates rpl pwgen RUN apt-get -y install osmctools wget gzip gcc libc-dev zlib1g-dev WORKDIR /home ADD osmupdate.c /home/osmupdate.c +ADD osmconvert.c /home/osmconvert.c RUN gcc -x c - -o osmupdate osmupdate.c +RUN gcc -x c - -O3 -o osmconvert osmconvert.c -lz # Add the python script which will call osmupdate ADD download.py /home/download.py diff --git a/docker-osmupdate/osmconvert.c b/docker-osmupdate/osmconvert.c new file mode 100644 index 0000000..94c31ed --- /dev/null +++ b/docker-osmupdate/osmconvert.c @@ -0,0 +1,11941 @@ +// osmconvert 2015-04-13 14:20 +#define VERSION "0.8.4" +// +// compile this file: +// gcc osmconvert.c -lz -O3 -o osmconvert +// +// (c) 2011..2015 Markus Weber, Nuernberg +// Richard Russo contributed the initiative to --add-bbox-tags option +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU Affero General Public License +// version 3 as published by the Free Software Foundation. +// 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 Affero General Public License for more details. +// You should have received a copy of this license along +// with this program; if not, see http://www.gnu.org/licenses/. +// +// Other licenses are available on request; please ask the author. + +#define MAXLOGLEVEL 2 +const char* shorthelptext= +"\nosmconvert " VERSION " Parameter Overview\n" +"(Please use --help to get more information.)\n" +"\n" +" input file name\n" +"- read from standard input\n" +"-b=,,, apply a border box\n" +"-B= apply a border polygon\n" +"--complete-ways do not clip ways at the borders\n" +"--complex-ways do not clip multipolygons at the borders\n" +"--all-to-nodes convert ways and relations to nodes\n" +"--add-bbox-tags adds bbox tags to ways and relations\n" +"--add-bboxarea-tags adds tags for estimated bbox areas\n" +"--add-bboxweight-tags adds tags for log2 of bbox areas\n" +"--object-type-offset= offset for ways/relations if --all-to-nodes\n" +"--max-objects= space for --all-to-nodes, 1 obj. = 16 bytes\n" +"--drop-broken-refs delete references to excluded nodes\n" +"--drop-author delete changeset and user information\n" +"--drop-version same as before, but delete version as well\n" +"--drop-nodes delete all nodes\n" +"--drop-ways delete all ways\n" +"--drop-relations delete all relations\n" +"--diff calculate differences between two files\n" +"--diff-contents same as before, but compare whole contents\n" +"--subtract subtract objects given by following files\n" +"--pbf-granularity= lon/lat granularity of .pbf input file\n" +"--emulate-osmosis emulate Osmosis XML output format\n" +"--emulate-pbf2osm emulate pbf2osm output format\n" +"--fake-author set changeset to 1 and timestamp to 1970\n" +"--fake-version set version number to 1\n" +"--fake-lonlat set lon to 0 and lat to 0\n" +"-h display this parameter overview\n" +"--help display a more detailed help\n" +"--merge-versions merge versions of each object in a file\n" +"--out-osm write output in .osm format (default)\n" +"--out-osc write output in .osc format (OSMChangefile)\n" +"--out-osh write output in .osh format (visible-tags)\n" +"--out-o5m write output in .o5m format (fast binary)\n" +"--out-o5c write output in .o5c format (bin. Changef.)\n" +"--out-pbf write output in .pbf format (bin. standard)\n" +"--out-csv write output in .csv format (plain table)\n" +"--out-none no standard output (for testing purposes)\n" +"--csv= choose columns for csv format\n" +"--csv-headline start csv output with a headline\n" +"--csv-separator= separator character(s) for csv format\n" +"--timestamp= add a timestamp to the data\n" +"--timestamp=NOW- add a timestamp in seconds before now\n" +"--out-timestamp output the file\'s timestamp, nothing else\n" +"--out-statistics write statistics, nothing else\n" +"--statistics write statistics to stderr\n" +"-o= reroute standard output to a file\n" +"-t= define tempfile prefix\n" +"--parameter-file= param. in file, separated by empty lines\n" +"--verbose activate verbose mode\n"; +const char* helptext= +"\nosmconvert " VERSION "\n" +"\n" +"This program reads different file formats of the OpenStreetMap\n" +"project and converts the data to the selected output file format.\n" +"These formats can be read:\n" +" .osm .osc .osc.gz .osh .o5m .o5c .pbf\n" +"These formats can be written:\n" +" .osm (default) .osc .osh .o5m .o5c .pbf\n" +"\n" +"Names of input files must be specified as command line parameters.\n" +"Use - to read from standard input. You do not need to specify the\n" +"input formats, osmconvert will recognize them by itself.\n" +"The output format is .osm by default. If you want a different format,\n" +"please specify it using the appropriate command line parameter.\n" +"\n" +"-b=,,,\n" +" If you want to limit the geographical region, you can define\n" +" a bounding box. To do this, enter the southwestern and the\n" +" northeastern corners of that area. For example:\n" +" -b=-0.5,51,0.5,52\n" +"\n" +"-B=\n" +" Alternatively to a bounding box you can use a border polygon\n" +" to limit the geographical region.\n" +" The format of a border polygon file can be found in the OSM\n" +" Wiki: http://wiki.openstreetmap.org/wiki/Osmosis/\n" +" Polygon_Filter_File_Format\n" +" You do not need to strictly follow the format description,\n" +" you must ensure that every line of coordinates starts with\n" +" blanks.\n" +"\n" +"--complete-ways\n" +" If applying a border box or a border polygon, all nodes\n" +" the borders are excluded; even then if they belong to a way\n" +" which is not entirely excluded because it has some nodes\n" +" inside the borders.\n" +" This option will ensure that every way stays complete, even\n" +" it it intersects the borders. This will result in slower\n" +" processing, and the program will loose its ability to read\n" +" from standard input. It is recommended to use .o5m format as\n" +" input format to compensate most of the speed disadvantage.\n" +"\n" +"--complex-ways\n" +" Same as before, but multipolygons will not be cut at the\n" +" borders too.\n" +"\n" +"--all-to-nodes\n" +" Some applications do not have the ability to process ways or\n" +" relations, they just accept nodes as input. However, more and\n" +" more complex object are mapped as ways or even relations in\n" +" order to get all their details into the database.\n" +" Apply this option if you want to convert ways and relations\n" +" to nodes and thereby make them available to applications\n" +" which can only deal with nodes.\n" +" For each way a node is created. The way's id is increased by\n" +" 10^15 and taken as id for the new node. The node's longitude\n" +" and latitude are set to the way's geographical center. Same\n" +" applies to relations, however they get 2*10^15 as id offset.\n" +"\n" +"--add-bbox-tags\n" +" This option adds a tag with a bounding box to each object.\n" +" The tag will contain the border coordinates in this order:\n" +" min Longitude, min Latitude, max Longitude , max Latitude.\n" +" e.g.: \n" +"\n" +"--add-bboxarea-tags\n" +" A tag for an estimated area value for the bbox is added to\n" +" each way and each relation. The unit is square meters.\n" +" For example: \n" +"\n" +"--add-bboxweight-tags\n" +" This option will add the binary logarithm of the bbox area\n" +" of each way and each relation.\n" +" For example: \n" +"\n" +"--object-type-offset=\n" +" If applying the --all-to-nodes option as explained above, you\n" +" may adjust the id offset. For example:\n" +" --object-type-offset=4000000000\n" +" By appending \"+1\" to the offset, the program will create\n" +" ids in a sequence with step 1. This might be useful if the\n" +" there is a subsequently running application which cannot\n" +" process large id numbers. Example:\n" +" --object-type-offset=1900000000+1\n" +"\n" +"--drop-broken-refs\n" +" Use this option if you need to delete references to nodes\n" +" which have been excluded because lying outside the borders\n" +" (mandatory for some applications, e.g. Map Composer, JOSM).\n" +"\n" +"--drop-author\n" +" For most applications the author tags are not needed. If you\n" +" specify this option, no author information will be written:\n" +" no changeset, user or timestamp.\n" +"\n" +"--drop-version\n" +" If you want to exclude not only the author information but\n" +" also the version number, specify this option.\n" +"\n" +"--drop-nodes\n" +"--drop-ways\n" +"--drop-relations\n" +" According to the combination of these parameters, no members\n" +" of the referred section will be written.\n" +"\n" +"--diff\n" +" Calculate difference between two files and create a new .osc\n" +" or .o5c file.\n" +" There must be TWO input files and borders cannot be applied.\n" +" Both files must be sorted by object type and id. Created\n" +" objects will appear in the output file as \"modified\", unless\n" +" having version number 1.\n" +"\n" +"--diff-contents\n" +" Similar to --diff, this option calculates differences between\n" +" two OSM files. Here, to determine the differences complete\n" +" OSM objects are consulted, not only the version numbers.\n" +" Unfortunately, this option strictly requires both input files\n" +" to have .o5m format.\n" +"\n" +"--subtract\n" +" The output file will not contain any object which exists in\n" +" one of the input files following this directive. For example:\n" +" osmconvert input.o5m --subtract minus.o5m -o=output.o5m\n" +"\n" +"--pbf-granularity=\n" +" Rarely .pbf files come with non-standard granularity.\n" +" osmconvert will recognize this and suggest to specify the\n" +" abnormal lon/lat granularity using this command line option.\n" +" Allowed values are: 100 (default), 1000, 10000, ..., 10000000.\n" +"\n" +"--emulate-osmosis\n" +"--emulate-pbf2osm\n" +" In case of .osm output format, the program will try to use\n" +" the same data syntax as Osmosis, resp. pbf2osm.\n" +"\n" +"--fake-author\n" +" If you have dropped author information (--drop-author) that\n" +" data will be lost, of course. Some programs however require\n" +" author information on input although they do not need that\n" +" data. For this purpose, you can fake the author information.\n" +" osmconvert will write changeset 1, timestamp 1970.\n" +"\n" +"--fake-version\n" +" Same as --fake-author, but - if .osm xml is used as output\n" +" format - only the version number will be written (version 1).\n" +" This is useful if you want to inspect the data with JOSM.\n" +"\n" +"--fake-lonlat\n" +" Some programs depend on getting longitude/latitude values,\n" +" even when the object in question shall be deleted. With this\n" +" option you can have osmconvert to fake these values:\n" +" ... lat=\"0\" lon=\"0\" ...\n" +" Note that this is for XML files only (.osc and .osh).\n" +"\n" +"-h\n" +" Display a short parameter overview.\n" +"\n" +"--help\n" +" Display this help.\n" +"\n" +"--merge-versions\n" +" Some .osc files contain different versions of one object.\n" +" Use this option to accept such duplicates on input.\n" +"\n" +"--out-osm\n" +" Data will be written in .osm format. This is the default\n" +" output format.\n" +"\n" +"--out-osc\n" +" The OSM Change format will be used for output. Please note\n" +" that OSM objects which are to be deleted will be represented\n" +" by their ids only.\n" +"\n" +"--out-osh\n" +" For every OSM object, the appropriate \'visible\' tag will be\n" +" added to meet \'full planet history\' specification.\n" +"\n" +"--out-o5m\n" +" The .o5m format will be used. This format has the same\n" +" structure as the conventional .osm format, but the data are\n" +" stored as binary numbers and are therefore much more compact\n" +" than in .osm format. No packing is used, so you can pack .o5m\n" +" files using every file packer you want, e.g. lzo, bz2, etc.\n" +"\n" +"--out-o5c\n" +" This is the change file format of .o5m data format. All\n" +" tags will not be performed as delete actions but\n" +" converted into .o5c data format.\n" +"\n" +"--out-pbf\n" +" For output, PBF format will be used.\n" +"\n" +"--out-csv\n" +" A character separated list will be written to output.\n" +" The default separator is Tab, the default columns are:\n" +" type, id, name. You can change both by using the options\n" +" --csv-separator= and --csv=\n" +"\n" +"--csv-headline\n" +" Choose this option to print a headline to csv output.\n" +"\n" +"--csv-separator=\n" +" You may change the default separator (Tab) to a different\n" +" character or character sequence. For example:\n" +" --csv-separator=\"; \"\n" +"\n" +"--csv=\n" +" If you want to have certain columns in your csv list, please \n" +" specify their names as shown in this example:\n" +" --csv=\"@id name ref description\"\n" +" There are a few special column names for header data:\n" +" @otype (object type 0..2), @oname (object type name), @id\n" +" @lon, @lat, @version, @timestamp, @changeset, @uid, @user\n" +"\n" +"--out-none\n" +" This will be no standard output. This option is for testing\n" +" purposes only.\n" +"\n" +"--timestamp=\n" +"--timestamp=NOW\n" +" If you want to set the OSM timestamp of your output file,\n" +" supply it with this option. Date and time must be formatted\n" +" according OSM date/time specifications. For example:\n" +" --timestamp=2011-01-31T23:59:30Z\n" +" You also can supply a relative time in seconds, e.g. 24h ago:\n" +" --timestamp=NOW-86400\n" +"\n" +"--out-timestamp\n" +" With this option set, osmconvert prints just the time stamp\n" +" of the input file, nothing else.\n" +"\n" +"--statistics\n" +" This option activates a statistics counter. The program will\n" +" print statistical data to stderr.\n" +"\n" +"--out-statistics\n" +" Same as --statistics, but the statistical data will be\n" +" written to standard output.\n" +"\n" +"-o=\n" +" Standard output will be rerouted to the specified file.\n" +" If no output format has been specified, the program will\n" +" rely the file name extension.\n" +"\n" +"-t=\n" +" If borders are to be applied or broken references to be\n" +" eliminated, osmconvert creates and uses two temporary files.\n" +" This parameter defines their name prefix. The default value\n" +" is \"osmconvert_tempfile\".\n" +"\n" +"--parameter-file=FILE\n" +" If you want to supply one ore more command line arguments\n" +" by a parameter file, please use this option and specify the\n" +" file name. Within the parameter file, parameters must be\n" +" separated by empty lines. Line feeds inside a parameter will\n" +" be converted to spaces.\n" +" Lines starting with \"// \" will be treated as comments.\n" +"\n" +"-v\n" +"--verbose\n" +" With activated \'verbose\' mode, some statistical data and\n" +" diagnosis data will be displayed.\n" +" If -v resp. --verbose is the first parameter in the line,\n" +" osmconvert will display all input parameters.\n" +"\n" +"Examples\n" +"\n" +"./osmconvert europe.pbf --drop-author >europe.osm\n" +"./osmconvert europe.pbf |gzip >europe.osm.gz\n" +"bzcat europe.osm.bz2 |./osmconvert --out-pbf >europe.pbf\n" +"./osmconvert europe.pbf -B=ch.poly >switzerland.osm\n" +"./osmconvert switzerland.osm --out-o5m >switzerland.o5m\n" +"./osmconvert june_july.osc --out-o5c >june_july.o5c\n" +"./osmconvert june.o5m june_july.o5c.gz --out-o5m >july.o5m\n" +"./osmconvert sep.osm sep_oct.osc oct_nov.osc >nov.osm\n" +"./osmconvert northamerica.osm southamerica.osm >americas.osm\n" +"\n" +"Tuning\n" +"\n" +"To speed-up the process, the program uses some main memory for a\n" +"hash table. By default, it uses 900 MB for storing a flag for every\n" +"possible node, 90 for the way flags, and 10 relation flags.\n" +"Every byte holds the flags for 8 ID numbers, i.e., in 900 MB the\n" +"program can store 7200 million flags. As there are less than 3200\n" +"million IDs for nodes at present (Oct 2014), 400 MB would suffice.\n" +"So, for example, you can decrease the hash sizes to e.g. 400, 50 and\n" +"2 MB using this option:\n" +"\n" +" --hash-memory=400-50-2\n" +"\n" +"But keep in mind that the OSM database is continuously expanding. For\n" +"this reason the program-own default value is higher than shown in the\n" +"example, and it may be appropriate to increase it in the future.\n" +"If you do not want to bother with the details, you can enter the\n" +"amount of memory as a sum, and the program will divide it by itself.\n" +"For example:\n" +"\n" +" --hash-memory=1500\n" +"\n" +"These 1500 MB will be split in three parts: 1350 for nodes, 135 for\n" +"ways, and 15 for relations.\n" +"\n" +"Because we are taking hashes, it is not necessary to provide all the\n" +"suggested memory; the program will operate with less hash memory too.\n" +"But, in this case, the border filter will be less effective, i.e.,\n" +"some ways and some relations will be left in the output file although\n" +"they should have been excluded.\n" +"The maximum value the program accepts for the hash size is 4000 MiB;\n" +"If you exceed the maximum amount of memory available on your system,\n" +"the program will try to reduce this amount and display a warning\n" +"message.\n" +"\n" +"There is another temporary memory space which is used only for the\n" +"conversion of ways and relations to nodes (option --all-to-nodes).\n" +"This space is sufficient for up to 25 Mio. OSM objects, 400 MB of\n" +"main memory are needed for this purpose, 800 MB if extended option\n" +"--add-bbox-tags has been invoked. If this is not sufficient or\n" +"if you want to save memory, you can configure the maximum number of\n" +"OSM objects by yourself. For example:\n" +"\n" +" --max-objects=35000000\n" +"\n" +"The number of references per object is limited to 100,000. This will\n" +"be sufficient for all OSM files. If you are going to create your own\n" +"OSM files by converting shapefiles or other files to OSM format, this\n" +"might result in way objects with more than 100,000 nodes. For this\n" +"reason you will need to increase the maximum accordingly. Example:\n" +"\n" +" --max-refs=400000\n" +"\n" +"Limitations\n" +"\n" +"When extracting a geographical region (using -b or -B), the input\n" +"file must contain the objects ordered by their type: first, all\n" +"nodes, next, all ways, followed by all relations. Within each of\n" +"these sections, the objects section must be sorted by their id in\n" +"ascending order.\n" +"\n" +"Usual .osm, .osc, .o5m, o5c and .pbf files adhere to this condition.\n" +"This means that you do not have to worry about this limitation.\n" +"osmconvert will display an error message if this sequence is broken.\n" +"\n" +"If a polygon file for borders is supplied, the maximum number of\n" +"polygon points is about 40,000.\n" +"\n" +"This program is for experimental use. Expect malfunctions and data\n" +"loss. Do not use the program in productive or commercial systems.\n" +"\n" +"There is NO WARRANTY, to the extent permitted by law.\n" +"Please send any bug reports to markus.weber@gmx.com\n\n"; + +#define _FILE_OFFSET_BITS 64 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef enum {false= 0,true= 1} bool; +typedef uint8_t byte; +typedef unsigned int uint; +#define isdig(x) isdigit((unsigned char)(x)) +static int loglevel= 0; // logging to stderr; + // 0: no logging; 1: small logging; 2: normal logging; + // 3: extended logging; +#define DP(f) fprintf(stderr,"Debug: " #f "\n"); +#define DPv(f,...) fprintf(stderr,"Debug: " #f "\n",__VA_ARGS__); +#define DPM(f,p,m) { byte* pp; int i,mm; static int msgn= 3; \ + if(--msgn>=0) { fprintf(stderr,"Debug memory: " #f); \ + pp= (byte*)(p); mm= (m); if(pp==NULL) fprintf(stderr,"\n (null)"); \ + else for(i= 0; i works as non-delete and no- works as "stay"; + // be sure to have set this variable back to false before starting + // processing, to exclude unwanted effects on temporary files; +static bool global_mergeversions= false; // accept duplicate versions +static bool global_dropversion= false; // exclude version +static bool global_dropauthor= false; // exclude author information +static bool global_fakeauthor= false; // fake author information +static bool global_fakeversion= false; // fake just the version number +static bool global_fakelonlat= false; + // fake longitude and latitude in case of delete actions (.osc); +static bool global_dropbrokenrefs= false; // exclude broken references +static bool global_dropnodes= false; // exclude nodes section +static bool global_dropways= false; // exclude ways section +static bool global_droprelations= false; // exclude relations section +static bool global_outo5m= false; // output shall have .o5m format +static bool global_outo5c= false; // output shall have .o5c format +static bool global_outosm= false; // output shall have .osm format +static bool global_outosc= false; // output shall have .osc format +static bool global_outosh= false; // output shall have .osh format +static bool global_outpbf= false; // output shall have .pbf format +static bool global_outcsv= false; // output shall have .csv format +static bool global_outnone= false; // no standard output at all +static int32_t global_pbfgranularity= 100; + // granularity of lon/lat in .pbf files; unit: 1 nanodegree; +static int32_t global_pbfgranularity100= 0; + // granularity of lon/lat in .pbf files; unit: 100 nanodegrees; + // 0: default: 100 nanodegrees; +static bool global_emulatepbf2osm= false; + // emulate pbf2osm compatible output +static bool global_emulateosmosis= false; + // emulate Osmosis compatible output +static bool global_emulateosmium= false; + // emulate Osmium compatible output +static int64_t global_timestamp= 0; + // manually chosen file timestamp; ==0: no file timestamp given; +static bool global_outtimestamp= false; + // print only the file timestamp, nothing else +static bool global_statistics= false; // print statistics to stderr +static bool global_outstatistics= false; // print statistics to stdout +static bool global_csvheadline= false; // headline for csv +static char global_csvseparator[16]= "\t"; // separator for csv +static bool global_completeways= false; // when applying borders, + // do not clip ways but include them as whole if at least a single + // of its nodes lies inside the borders; +static bool global_complexways= false; // same as global_completeways, + // but multipolygons are included completely (with all ways and their + // nodes), even when only a single nodes lies inside the borders; +static int global_calccoords= 0; + // calculate coordinates for all objects; + // 0: no coordinates to calculate; 1: calculate coordinates; + // -1: calculate coordinates and bbox; +static bool global_alltonodes= false; + // convert all ways and all relations to nodes +static bool global_add= false; + // add at least one tag shall be added; + // global_add==global_addbbox|global_addbboxarea|global_addbboxweight +static bool global_addbbox= false; + // add bBox tags to ways and relations +static bool global_addbboxarea= false; + // add bBoxArea tags to ways and relations +static bool global_addbboxweight= false; + // add bBoxWeight tags to ways and relations +static int64_t global_maxobjects= 25000000; +static int64_t global_otypeoffset10= INT64_C(1000000000000000); + // if global_calccoords!=0: + // id offset for ways; *2: id offset for relations; +static int64_t global_otypeoffset05, + global_otypeoffset15,global_otypeoffset20; + // (just to save CPU time for calculating the offset of relations) +static int64_t global_otypeoffsetstep= 0; + // if !=0, the program will not create the new id by adding + // global_otypeoffset but by starting at global_otypeoffset + // and adding 1 for every new way, resp. relation: +static char global_tempfilename[350]= "osmconvert_tempfile"; + // prefix of names for temporary files +static int64_t global_maxrefs= 100000; +#define PERR(f) { static int msgn= 3; if(--msgn>=0) \ + fprintf(stderr,"osmconvert Error: " f "\n"); } + // print error message +#define PERRv(f,...) { static int msgn= 3; if(--msgn>=0) \ + fprintf(stderr,"osmconvert Error: " f "\n",__VA_ARGS__); } + // print error message with value(s) +#define WARN(f) { static int msgn= 3; if(--msgn>=0) \ + fprintf(stderr,"osmconvert Warning: " f "\n"); } + // print a warning message, do it maximal 3 times +#define WARNv(f,...) { static int msgn= 3; if(--msgn>=0) \ + fprintf(stderr,"osmconvert Warning: " f "\n",__VA_ARGS__); } + // print a warning message with value(s), do it maximal 3 times +#define PINFO(f) \ + fprintf(stderr,"osmconvert: " f "\n"); // print info message +#define PINFOv(f,...) \ + fprintf(stderr,"osmconvert: " f "\n",__VA_ARGS__); +#define ONAME(i) \ + (i==0? "node": i==1? "way": i==2? "relation": "unknown object") +#define global_fileM 1002 // maximum number of input files + +//------------------------------------------------------------ +// end Module Global global variables for this program +//------------------------------------------------------------ + + + +static inline char* uint32toa(uint32_t v,char* s) { + // convert uint32_t integer into string; + // v: long integer value to convert; + // return: s; + // s[]: digit string; + char* s1,*s2; + char c; + + s1= s; + if(v==0) + *s1++= '0'; + s2= s1; + while(v>0) + { *s2++= "0123456789"[v%10]; v/= 10; } + *s2--= 0; + while(s2>s1) + { c= *s1; *s1= *s2; *s2= c; s1++; s2--; } + return s; + } // end uint32toa() + +static inline char* int64toa(int64_t v,char* s) { + // convert int64_t integer into string; + // v: long integer value to convert; + // return: s; + // s[]: digit string; + char* s1,*s2; + char c; + + s1= s; + if(v<0) + { *s1++= '-'; v= -v; } + else if(v==0) + *s1++= '0'; + s2= s1; + while(v>0) + { *s2++= "0123456789"[v%10]; v/= 10; } + *s2--= 0; + while(s2>s1) + { c= *s1; *s1= *s2; *s2= c; s1++; s2--; } + return s; + } // end int64toa() + +static inline char *stpcpy0(char *dest, const char *src) { + // redefinition of C99's stpcpy() because it's missing in MinGW, + // and declaration in Linux seems to be wrong; + while(*src!=0) + *dest++= *src++; + *dest= 0; + return dest; + } // end stpcpy0() + +static inline char *strmcpy(char *dest, const char *src, size_t maxlen) { + // similar to strcpy(), this procedure copies a character string; + // here, the length is cared about, i.e. the target string will + // be limited in case it is too long; + // src[]: source string which is to be copied; + // maxlen: maximum length of the destination string + // (including terminator null); + // return: + // dest[]: destination string of the copy; this is the + // function's return value too; + char* d; + + if(maxlen==0) +return dest; + d= dest; + while(--maxlen>0 && *src!=0) + *d++= *src++; + *d= 0; + return dest; + } // end strmcpy() +#define strMcpy(d,s) strmcpy((d),(s),sizeof(d)) + +static inline char *stpmcpy(char *dest, const char *src, size_t maxlen) { + // similar to strmcpy(), this procedure copies a character string; + // however, it returns the address of the destination string's + // terminating zero character; + // this makes it easier to concatenate strings; + char* d; + + if(maxlen==0) +return dest; + d= dest; + while(--maxlen>0 && *src!=0) + *d++= *src++; + *d= 0; + return d; + } // end stpmcpy() +#define stpMcpy(d,s) stpmcpy(d,s,sizeof(d)) + +static inline int strzcmp(const char* s1,const char* s2) { + // similar to strcmp(), this procedure compares two character strings; + // here, the number of characters which are to be compared is limited + // to the length of the second string; + // i.e., this procedure can be used to identify a short string s2 + // within a long string s1; + // s1[]: first string; + // s2[]: string to compare with the first string; + // return: + // 0: both strings are identical; the first string may be longer than + // the second; + // -1: the first string is alphabetical smaller than the second; + // 1: the first string is alphabetical greater than the second; + while(*s1==*s2 && *s1!=0) { s1++; s2++; } + if(*s2==0) + return 0; + return *(unsigned char*)s1 < *(unsigned char*)s2? -1: 1; + } // end strzcmp() + +static inline int strzlcmp(const char* s1,const char* s2) { + // similar to strzcmp(), this procedure compares two character strings; + // and accepts the first string to be longer than the second; + // other than strzcmp(), this procedure returns the length of s2[] in + // case both string contents are identical, and returns 0 otherwise; + // s1[]: first string; + // s2[]: string to compare with the first string; + // return: + // >0: both strings are identical, the length of the second string is + // returned; the first string may be longer than the second; + // 0: the string contents are not identical; + const char* s2a; + + s2a= s2; + while(*s1==*s2 && *s1!=0) { s1++; s2++; } + if(*s2==0) + return s2-s2a; + return 0; + } // end strzlcmp() + +static inline int strycmp(const char* s1,const char* s2) { + // similar to strcmp(), this procedure compares two character strings; + // here, both strings are end-aligned; + // not more characters will be compared than are existing in string s2; + // i.e., this procedure can be used to identify a file name extension; + const char* s1e; + int l; + + l= strchr(s2,0)-s2; + s1e= strchr(s1,0); + if(s1e-s1=0x100000000LL) { + v/= 0x100000000LL; + msb+= 32; + } + if(v>=0x10000L) { + v/= 0x10000L; + msb+= 16; + } + if(v>=0x100) { + v/= 0x100; + msb+= 8; + } + if(v>=0x10) { + v/= 0x10; + msb+= 4; + } + if(v>=0x4) { + v/= 0x4; + msb+= 2; + } + if(v>=0x2) { + v/= 0x2; + msb+= 1; + } + if(v!=0) { + msb+= 1; + } + return msb; + } // msbit() + +static inline int64_t cosrk(int32_t lat) { + // this procedure calculates the Cosinus of the given latitude, + // multiplies it with 40000k/(360*10^7)==0.00012345679, + // and takes the reciprocal value of it; + // lat: latitude in 100 nano degrees; + // return: constant k needed to approximate the area of a + // coordinte-defined bbox: + // (lonmax-lonmin)*(latmax-latmin)/k + static const int32_t cosrktab[901]= { + 8100,8100,8100,8100,8100,8100,8100,8100, + 8100,8100,8101,8101,8101,8102,8102,8102, + 8103,8103,8103,8104,8104,8105,8105,8106, + 8107,8107,8108,8109,8109,8110,8111,8111, + 8112,8113,8114,8115,8116,8116,8117,8118, + 8119,8120,8121,8122,8123,8125,8126,8127, + 8128,8129,8130,8132,8133,8134,8136,8137, + 8138,8140,8141,8143,8144,8146,8147,8149, + 8150,8152,8154,8155,8157,8159,8160,8162, + 8164,8166,8168,8169,8171,8173,8175,8177, + 8179,8181,8183,8185,8187,8189,8192,8194, + 8196,8198,8200,8203,8205,8207,8210,8212, + 8215,8217,8219,8222,8224,8227,8230,8232, + 8235,8237,8240,8243,8246,8248,8251,8254, + 8257,8260,8263,8265,8268,8271,8274,8277, + 8280,8284,8287,8290,8293,8296,8299,8303, + 8306,8309,8313,8316,8319,8323,8326,8330, + 8333,8337,8340,8344,8347,8351,8355,8358, + 8362,8366,8370,8374,8377,8381,8385,8389, + 8393,8397,8401,8405,8409,8413,8418,8422, + 8426,8430,8434,8439,8443,8447,8452,8456, + 8461,8465,8470,8474,8479,8483,8488,8493, + 8497,8502,8507,8512,8516,8521,8526,8531, + 8536,8541,8546,8551,8556,8561,8566,8571, + 8577,8582,8587,8592,8598,8603,8608,8614, + 8619,8625,8630,8636,8642,8647,8653,8658, + 8664,8670,8676,8682,8687,8693,8699,8705, + 8711,8717,8723,8729,8736,8742,8748,8754, + 8761,8767,8773,8780,8786,8793,8799,8806, + 8812,8819,8825,8832,8839,8846,8852,8859, + 8866,8873,8880,8887,8894,8901,8908,8915, + 8922,8930,8937,8944,8951,8959,8966,8974, + 8981,8989,8996,9004,9012,9019,9027,9035, + 9043,9050,9058,9066,9074,9082,9090,9098, + 9107,9115,9123,9131,9140,9148,9156,9165, + 9173,9182,9190,9199,9208,9216,9225,9234, + 9243,9252,9261,9270,9279,9288,9297,9306, + 9315,9325,9334,9343,9353,9362,9372,9381, + 9391,9400,9410,9420,9430,9439,9449,9459, + 9469,9479,9489,9499,9510,9520,9530,9540, + 9551,9561,9572,9582,9593,9604,9614,9625, + 9636,9647,9658,9669,9680,9691,9702,9713, + 9724,9736,9747,9758,9770,9781,9793,9805, + 9816,9828,9840,9852,9864,9876,9888,9900, + 9912,9924,9937,9949,9961,9974,9986,9999, + 10012,10024,10037,10050,10063,10076,10089,10102, + 10115,10128,10142,10155,10169,10182,10196,10209, + 10223,10237,10251,10265,10279,10293,10307,10321, + 10335,10350,10364,10378,10393,10408,10422,10437, + 10452,10467,10482,10497,10512,10527,10542,10558, + 10573,10589,10604,10620,10636,10652,10668,10684, + 10700,10716,10732,10748,10765,10781,10798,10815, + 10831,10848,10865,10882,10899,10916,10934,10951, + 10968,10986,11003,11021,11039,11057,11075,11093, + 11111,11129,11148,11166,11185,11203,11222,11241, + 11260,11279,11298,11317,11337,11356,11375,11395, + 11415,11435,11455,11475,11495,11515,11535,11556, + 11576,11597,11618,11639,11660,11681,11702,11724, + 11745,11767,11788,11810,11832,11854,11876,11899, + 11921,11944,11966,11989,12012,12035,12058,12081, + 12105,12128,12152,12176,12200,12224,12248,12272, + 12297,12321,12346,12371,12396,12421,12446,12472, + 12497,12523,12549,12575,12601,12627,12654,12680, + 12707,12734,12761,12788,12815,12843,12871,12898, + 12926,12954,12983,13011,13040,13069,13098,13127, + 13156,13186,13215,13245,13275,13305,13336,13366, + 13397,13428,13459,13490,13522,13553,13585,13617, + 13649,13682,13714,13747,13780,13813,13847,13880, + 13914,13948,13982,14017,14051,14086,14121,14157, + 14192,14228,14264,14300,14337,14373,14410,14447, + 14485,14522,14560,14598,14637,14675,14714,14753, + 14792,14832,14872,14912,14952,14993,15034,15075, + 15116,15158,15200,15242,15285,15328,15371,15414, + 15458,15502,15546,15591,15636,15681,15726,15772, + 15818,15865,15912,15959,16006,16054,16102,16151, + 16200,16249,16298,16348,16398,16449,16500,16551, + 16603,16655,16707,16760,16813,16867,16921,16975, + 17030,17085,17141,17197,17253,17310,17367,17425, + 17483,17542,17601,17660,17720,17780,17841,17903, + 17964,18027,18090,18153,18217,18281,18346,18411, + 18477,18543,18610,18678,18746,18814,18883,18953, + 19023,19094,19166,19238,19310,19384,19458,19532, + 19607,19683,19759,19836,19914,19993,20072,20151, + 20232,20313,20395,20478,20561,20645,20730,20815, + 20902,20989,21077,21166,21255,21346,21437,21529, + 21622,21716,21811,21906,22003,22100,22199,22298, + 22398,22500,22602,22705,22810,22915,23021,23129, + 23237,23347,23457,23569,23682,23796,23912,24028, + 24146,24265,24385,24507,24630,24754,24879,25006, + 25134,25264,25395,25527,25661,25796,25933,26072, + 26212,26353,26496,26641,26788,26936,27086,27238, + 27391,27547,27704,27863,28024,28187,28352,28519, + 28688,28859,29033,29208,29386,29566,29748,29933, + 30120,30310,30502,30696,30893,31093,31295,31501, + 31709,31920,32134,32350,32570,32793,33019,33249, + 33481,33717,33957,34200,34447,34697,34951,35209, + 35471,35737,36007,36282,36560,36843,37131,37423, + 37720,38022,38329,38641,38958,39281,39609,39943, + 40282,40628,40980,41337,41702,42073,42450,42835, + 43227,43626,44033,44447,44870,45301,45740,46188, + 46646,47112,47588,48074,48570,49076,49594,50122, + 50662,51214,51778,52355,52946,53549,54167,54800, + 55447,56111,56790,57487,58200,58932,59683,60453, + 61244,62056,62890,63747,64627,65533,66464,67423, + 68409,69426,70473,71552,72665,73814,75000,76225, + 77490,78799,80153,81554,83006,84510,86071,87690, + 89371,91119,92937,94828,96799,98854,100998,103238, + 105580,108030,110598,113290,116118,119090,122220,125518, + 129000,132681,136578,140712,145105,149781,154769,160101, + 165814,171950,178559,185697,193429,201834,211004,221047, + 232095,244305,257873,273037,290097,309432,331529,357027, + 386774,421931,464119,515683,580138,663010,773507,928203, + 1160248,1546993,2320483,4640960, + 2147483647 }; // cosrk values for 10th degrees from 0 to 90 + + lat/= 1000000; + // transform unit 100 nano degree into unit 10th degree + if(lat<0) lat= -lat; // make it positive + if(lat>900) lat= 900; // set maximum of 90 degree + return cosrktab[lat]; + } // cosrk() +// the table in the previous procedure has been generated by this +// program: +#if 0 // file cosrk.c, run it with: gcc cosrk.c -lm -o cosrk && ./cosrk +#include +#include +#include +int main() { + int i; + printf(" static const int32_t cosrktab[901]= {"); + i= 0; + for(i= 0;i<900;i++) { + if(i%8==0) + printf("\n "); + printf("%"PRIi32",",(int32_t)( + 1/( cos(i/1800.0*3.14159265359) *0.00012345679) + )); + } + printf("\n 2147483647"); + printf(" }; // cosrk values for 10th degrees from 0 to 90\n"); + return 0; } +#endif + + + +//------------------------------------------------------------ +// Module pbf_ protobuf conversions module +//------------------------------------------------------------ + +// this module provides procedures for conversions from +// protobuf formats to regular numbers; +// as usual, all identifiers of a module have the same prefix, +// in this case 'pbf'; an underline will follow in case of a +// global accessible object, two underlines in case of objects +// which are not meant to be accessed from outside this module; +// the sections of private and public definitions are separated +// by a horizontal line: ---- +// many procedures have a parameter 'pp'; here, the address of +// a buffer pointer is expected; this pointer will be incremented +// by the number of bytes the converted protobuf element consumes; + +//------------------------------------------------------------ + +static inline uint32_t pbf_uint32(byte** pp) { + // get the value of an unsigned integer; + // pp: see module header; + byte* p; + uint32_t i; + uint32_t fac; + + p= *pp; + i= *p; + if((*p & 0x80)==0) { // just one byte + (*pp)++; +return i; + } + i&= 0x7f; + fac= 0x80; + while(*++p & 0x80) { // more byte(s) will follow + i+= (*p & 0x7f)*fac; + fac<<= 7; + } + i+= *p++ *fac; + *pp= p; + return i; + } // end pbf_uint32() + +static inline int32_t pbf_sint32(byte** pp) { + // get the value of an unsigned integer; + // pp: see module header; + byte* p; + int32_t i; + int32_t fac; + int sig; + + p= *pp; + i= *p; + if((*p & 0x80)==0) { // just one byte + (*pp)++; + if(i & 1) // negative +return -1-(i>>1); + else +return i>>1; + } + sig= i & 1; + i= (i & 0x7e)>>1; + fac= 0x40; + while(*++p & 0x80) { // more byte(s) will follow + i+= (*p & 0x7f)*fac; + fac<<= 7; + } + i+= *p++ *fac; + *pp= p; + if(sig) // negative +return -1-i; + else +return i; + } // end pbf_sint32() + +static inline uint64_t pbf_uint64(byte** pp) { + // get the value of an unsigned integer; + // pp: see module header; + byte* p; + uint64_t i; + uint64_t fac; + + p= *pp; + i= *p; + if((*p & 0x80)==0) { // just one byte + (*pp)++; +return i; + } + i&= 0x7f; + fac= 0x80; + while(*++p & 0x80) { // more byte(s) will follow + i+= (*p & 0x7f)*fac; + fac<<= 7; + } + i+= *p++ *fac; + *pp= p; + return i; + } // end pbf_uint64() + +static inline int64_t pbf_sint64(byte** pp) { + // get the value of a signed integer; + // pp: see module header; + byte* p; + int64_t i; + int64_t fac; + int sig; + + p= *pp; + i= *p; + if((*p & 0x80)==0) { // just one byte + (*pp)++; + if(i & 1) // negative +return -1-(i>>1); + else +return i>>1; + } + sig= i & 1; + i= (i & 0x7e)>>1; + fac= 0x40; + while(*++p & 0x80) { // more byte(s) will follow + i+= (*p & 0x7f)*fac; + fac<<= 7; + } + i+= *p++ *fac; + *pp= p; + if(sig) // negative +return -1-i; + else +return i; + } // end pbf_sint64() + +static inline bool pbf_jump(byte** pp) { + // jump over a protobuf formatted element - no matter + // which kind of element; + // pp: see module header; + // return: the data do not meet protobuf specifications (error); + byte* p; + int type; + uint32_t u; + + p= *pp; + type= *p & 0x07; + switch(type) { // protobuf type + case 0: // Varint + while(*p & 0x80) p++; p++; // jump over id + while(*p & 0x80) p++; p++; // jump over data + break; + case 1: // fixed 64 bit; + while(*p & 0x80) p++; p++; // jump over id + p+= 4; // jump over data + break; + case 2: // String + while(*p & 0x80) p++; p++; // jump over id + u= pbf_uint32(&p); + p+= u; // jump over string contents + break; + case 5: // fixed 32 bit; + while(*p & 0x80) p++; p++; // jump over id + p+= 2; // jump over data + break; + default: // unknown id + fprintf(stderr,"osmconvert: Format error: 0x%02X.\n",*p); + (*pp)++; +return true; + } // end protobuf type + *pp= p; + return false; + } // end pbf_jump() + +static inline void pbf_intjump(byte** pp) { + // jump over a protobuf formatted integer; + // pp: see module header; + // we do not care about a possibly existing identifier, + // therefore as the start address *pp the address of the + // integer value is expected; + byte* p; + + p= *pp; + while(*p & 0x80) p++; p++; + *pp= p; + } // end pbf_intjump() + +//------------------------------------------------------------ +// end Module pbf_ protobuf conversions module +//------------------------------------------------------------ + + + +//------------------------------------------------------------ +// Module hash_ OSM hash module +//------------------------------------------------------------ + +// this module provides three hash tables with default sizes +// of 320, 60 and 20 MB; +// the procedures hash_seti() and hash_geti() allow bitwise +// access to these tables; +// as usual, all identifiers of a module have the same prefix, +// in this case 'hash'; an underline will follow in case of a +// global accessible object, two underlines in case of objects +// which are not meant to be accessed from outside this module; +// the sections of private and public definitions are separated +// by a horizontal line: ---- + +static bool hash__initialized= false; +#define hash__M 3 +static unsigned char* hash__mem[hash__M]= {NULL,NULL,NULL}; + // start of the hash fields for each object type (node, way, relation); +static uint32_t hash__max[hash__M]= {0,0,0}; + // size of the hash fields for each object type (node, way, relation); +static int hash__error_number= 0; + // 1: object too large + +static void hash__end() { + // clean-up for hash module; + // will be called at program's end; + int o; // object type + + for(o= 0;o4000) x= 4000; \ + hash__max[o]= x*(1024*1024); + D(n,0u) D(w,1u) D(r,2u) + #undef D + // allocate memory for each hash table + for(o= 0;o=1024); + if(hash__mem[o]==NULL) // allocation unsuccessful at all + error= true; // memorize that the program should be aborted + } // end for each hash table + atexit(hash__end); // chain-in the clean-up procedure + if(!error) hash__initialized= true; + return error? 2: warning? 1: 0; + } // end hash_ini() + +static inline void hash_seti(int o,int64_t idi) { + // set a flag for a specific object type and ID; + // o: object type; 0: node; 1: way; 2: relation; + // caution: due to performance reasons the boundaries + // are not checked; + // id: id of the object; + unsigned char* mem; // address of byte in hash table + unsigned int ido; // bit offset to idi; + + if(!hash__initialized) return; // error prevention + idi+= ((int64_t)hash__max[o])<<3; // consider small negative numbers + ido= idi&0x7; // extract bit number (0..7) + idi>>=3; // calculate byte offset + idi%= hash__max[o]; // consider length of hash table + mem= hash__mem[o]; // get start address of hash table + mem+= idi; // calculate address of the byte + *mem|= (1<>=3; // calculate byte offset + idi%= hash__max[o]; // consider length of hash table + mem= hash__mem[o]; // get start address of hash table + mem+= idi; // calculate address of the byte + *mem&= (unsigned char)(~0)^(1<>=3; // calculate byte offset + idi%= hash__max[o]; // consider length of hash table + mem= hash__mem[o]; // get start address of hash table + mem+= idi; // calculate address of the byte + flag= (*mem&(1<x1; + bx= ((border__edge_t*)b)->x1; + if(ax>bx) +return 1; + if(ax==bx) +return 0; + return -1; + } // end border__qsort_edge() + +//------------------------------------------------------------ + +static bool border_active= false; // borders are to be considered; + // this variable must not be written from outside of the module; + +static bool border_box(const char* s) { + // read coordinates of a border box; + // s[]: coordinates as a string; example: "11,49,11.3,50" + // return: success; + double x1f,y1f; // coordinates of southwestern corner + double x2f,y2f; // coordinates of northeastern corner + int r; + + x1f= y1f= x2f= y2f= 200.1; + r= sscanf(s,"%lG,%lG,%lG,%lG",&x1f,&y1f,&x2f,&y2f); + if(r!=4 || x1f<-180.1 || x1f>180.1 || y1f<-90.1 || y1f>90.1 || + x2f<-180.1 || x2f>180.1 || y2f<-90.1 || y2f>90.1) +return false; + border_active=true; + border__bx1= (int32_t)(x1f*10000000L); + // convert floatingpoint to fixpoint + border__by1= (int32_t)(y1f*10000000L); + border__bx2= (int32_t)(x2f*10000000L); + border__by2= (int32_t)(y2f*10000000L); + return true; + } // end border_box() + +static bool border_file(const char* fn) { + // read border polygon file, store the coordinates, and determine + // an enclosing border box to speed-up the calculations; + // fn[]: file name; + // return: success; + static int32_t nil; + + if(!border__ini()) +return false; + nil= border__nil; + + /* get border polygon */ { + border__edge_t* bep; // growing pointer in border__edge[] + border__edge_t* bee; // memory end of border__edge[] + FILE* fi; + char s[80],*sp; + int32_t x0,y0; // coordinate of the first point in a section; + // this is used to close an unclosed polygon; + int32_t x1,y1; // last coordinates + int32_t x,y; + + border__edge[0].x1= nil; + fi= fopen(fn,"rb"); + if(fi==NULL) +return false; + bee= border__edge+(border__edge_M-2); + bep= border__edge; + x0= nil; // (sign that there is no first coordinate at the moment) + x1= nil; // (sign that there is no last coordinate at the moment) + for(;;) { // for every line in border file + s[0]= 0; + sp= fgets(s,sizeof(s),fi); + if(bep>=bee) { + fclose(fi); +return false; + } + if(s[0]!=' ' && s[0]!='\t') { // not inside a section + if(x0!=nil && x1!=nil && (x1!=x0 || y1!=y0)) { + // last polygon was not closed + if(x1==x0) { // the edge would be vertical + // we have to insert an additional edge + x0+= 3; + if(x0>x1) + { bep->x1= x1; bep->y1= y1; bep->x2= x0; bep->y2= y0; } + else + { bep->x1= x0; bep->y1= y0; bep->x2= x1; bep->y2= y1; } + bep->chain= NULL; + if(loglevel>=1) + fprintf(stderr, + "+ %i %"PRIi32",%"PRIi32",%"PRIi32",%"PRIi32"\n", + (int)(bep-border__edge), + bep->x1,bep->y1,bep->x2,bep->y2); + bep++; + x1= x0; y1= y0; + x0-= 3; + } // the edge would be vertical + // close the polygon + if(x0>x1) + { bep->x1= x1; bep->y1= y1; bep->x2= x0; bep->y2= y0; } + else + { bep->x1= x0; bep->y1= y0; bep->x2= x1; bep->y2= y1; } + bep->chain= NULL; + if(loglevel>=1) + fprintf(stderr, + "c %i %"PRIi32",%"PRIi32",%"PRIi32",%"PRIi32"\n", + (int)(bep-border__edge),bep->x1,bep->y1,bep->x2,bep->y2); + bep++; + } // end last polygon was not closed + x0= x1= nil; + } // end not inside a section + else { // inside a section + double xf,yf; + + xf= yf= 200.1; + sscanf(s+1,"%lG %lG",&xf,&yf); + if(xf<-180.1 || xf>180.1 || yf<-90.1 || yf>90.1) x= nil; + else { + x= (int32_t)(xf*10000000+0.5); + y= (int32_t)(yf*10000000+0.5); + } + if(x!=nil) { // data plausible + if(x1!=nil) { // there is a preceding coordinate + if(x==x1) x+= 2; // do not accept exact north-south + // lines, because then we may not be able to determine + // if a point lies inside or outside the polygon; + if(x>x1) + { bep->x1= x1; bep->y1= y1; bep->x2= x; bep->y2= y; } + else + { bep->x1= x; bep->y1= y; bep->x2= x1; bep->y2= y1; } + bep->chain= NULL; + if(loglevel>=1) + fprintf(stderr, + "- %i %"PRIi32",%"PRIi32",%"PRIi32",%"PRIi32"\n", + (int)(bep-border__edge), + bep->x1,bep->y1,bep->x2,bep->y2); + bep++; + } // end there is a preceding coordinate + x1= x; y1= y; + if(x0==nil) + { x0= x; y0= y; } + } // end data plausible + } // end inside a section + if(sp==NULL) // end of border file + break; + } // end for every line in border file + fclose(fi); + bep->x1= nil; // set terminator of edge list + border__edge_n= bep-border__edge; // set number of edges + } // end get border polygon + + // sort edges ascending by x1 value + if(loglevel>=1) + fprintf(stderr,"Border polygons: %i. Now sorting.\n", + border__edge_n); + qsort(border__edge,border__edge_n,sizeof(border__edge_t), + border__qsort_edge); + + /* generate chains for each edge */ { + int32_t x2; + border__chain_t* bcp; // growing pointer in chain storage + border__edge_t* bep; // pointer in border__edge[] + border__edge_t* bep2; // referenced edge + border__chain_t* bcp2; // chain of referenced edge; + + bep= border__edge; + bcp= border__chain; + while(bep->x1!=nil) { // for each edge in list + if(loglevel>=1) + fprintf(stderr, + "> %i %"PRIi32",%"PRIi32",%"PRIi32",%"PRIi32"\n", + (int)(bep-border__edge),bep->x1,bep->y1,bep->x2,bep->y2); + /*x1= bep->x1;*/ x2= bep->x2; + bep2= bep; + while(bep2>border__edge && (bep2-1)->x1==bep2->x1) bep2--; + // we must examine previous edges having same x1 too; + while(bep2->x1!=nil && bep2->x1 <= x2) { + // for each following overlapping edge in list + if(bep2==bep) { // own edge + bep2++; // (needs not to be chained to itself) + continue; + } + if(bcp>=border__chain+border__chain_M) + // no more space in chain storage +return false; + if(loglevel>=2) + fprintf(stderr,"+ add to chain of %i\n", + (int)(bep2-border__edge)); + bcp2= bep2->chain; + if(bcp2==NULL) // no chain yet + bep2->chain= bcp; // add first chain link + else { // edge already has a chain + // go to the chain's end and add new chain link there + + while(bcp2->next!=NULL) bcp2= bcp2->next; + bcp2->next= bcp; + } // end edge already has a chain + bcp->edge= bep; + // add source edge to chain of overlapping edges + bcp->next= NULL; // new chain termination + bcp++; + bep2++; + } // for each following overlapping edge in list + bep++; + } // end for each edge in list + } // end generate chains for each edge + + // test output + if(loglevel>=2) { + border__edge_t* bep,*bep2; // pointers in border__edge[] + border__chain_t* bcp; // pointer in chain storage + + fprintf(stderr,"Chains:\n"); + bep= border__edge; + while(bep->x1!=nil) { // for each edge in list + fprintf(stderr, + "> %i %"PRIi32",%"PRIi32",%"PRIi32",%"PRIi32"\n", + (int)(bep-border__edge),bep->x1,bep->y1,bep->x2,bep->y2); + bcp= bep->chain; + while(bcp!=NULL) { // for each chain link in edge + bep2= bcp->edge; + fprintf(stderr, + " %i %"PRIi32",%"PRIi32",%"PRIi32",%"PRIi32"\n", + (int)(bep2-border__edge), + bep2->x1,bep2->y1,bep2->x2,bep2->y2); + bcp= bcp->next; + } // end for each chain link in edge + bep++; + } // end for each edge in list + } // end test output + + /* determine enclosing border box */ { + border__edge_t* bep; // pointer in border__edge[] + + border__bx1= border__edge[0].x1; + border__bx2= -2000000000L; // (default) + border__by1= 2000000000L; border__by2= -2000000000L; // (default) + bep= border__edge; + while(bep->x1!=nil) { // for each coordinate of the polygon + if(bep->x2>border__bx2) border__bx2= bep->x2; + if(bep->y1y1; + if(bep->y2y2; + if(bep->y1>border__by2) border__by2= bep->y1; + if(bep->y2>border__by2) border__by2= bep->y2; + bep++; + } // end for each coordinate of the polygon + } // end determine enclosing border box + border_active=true; + if(loglevel>=1) + fprintf(stderr,"End of border initialization.\n"); + return true; + } // end border_file() + +static bool border_queryinside(int32_t x,int32_t y) { + // determine if the given coordinate lies inside or outside the + // border polygon(s); + // x,y: coordinates of the given point in 0.0000001 degrees; + // return: point lies inside the border polygon(s); + static int32_t nil; + + nil= border__nil; + + #if MAXLOGLEVEL>=3 + if(loglevel>=3) + fprintf(stderr,"# %li,%li\n",x,y); + #endif + // first, consider border box (if any) + if(border__bx1!=nil) { // there is a border box + if(xborder__bx2 || + yborder__by2) + // point lies outside the border box +return false; + } // end there is a border box + + /* second, consider border polygon (if any) */ { + border__edge_t* bep; // pointer in border__edge[] + border__chain_t* bcp; // pointer in border__chain[] + int cross; // number of the crossings a line from the point + // to the north pole would have ageinst the border lines + // in border__coord[][]; + + if(border__edge==NULL) +return true; + cross= 0; + + /* binary-search the edge with the closest x1 */ { + int i,i1,i2; // iteration indexes + + i1= 0; i2= border__edge_n; + while(i2>i1+1) { + i= (i1+i2)/2; + bep= border__edge+i; +//fprintf(stderr,"s %i %i %i %li\n",i1,i,i2,bep->x1); /// + if(bep->x1 > x) i2= i; + else i1= i; +//fprintf(stderr," %i %i %i\n",i1,i,i2); /// + } + bep= border__edge+i1; + } // end binary-search the edge with the closest x1 + + bcp= NULL; + // (default, because we want to examine the own edge first) + for(;;) { // for own edge and each edge in chain + if(bep->x1 <= x && bep->x2 > x) { // point lies inside x-range + if(bep->y1 > y && bep->y2 > y) { + // line lies completely north of point + cross++; + #if MAXLOGLEVEL>=3 + if(loglevel>=3) + fprintf(stderr,"= %i %li,%li,%li,%li\n", + (int)(bep-border__edge),bep->x1,bep->y1,bep->x2,bep->y2); + #endif + } + else if(bep->y1 > y || bep->y2 > y) { + // one line end lies north of point + if( (int64_t)(y-bep->y1)*(int64_t)(bep->x2-bep->x1) < + (int64_t)(x-bep->x1)*(int64_t)(bep->y2-bep->y1) ) { + // point lies south of the line + cross++; + #if MAXLOGLEVEL>=3 + if(loglevel>=3) + fprintf(stderr,"/ %i %li,%li,%li,%li\n", + (int)(bep-border__edge), + bep->x1,bep->y1,bep->x2,bep->y2); + #endif + } + #if MAXLOGLEVEL>=3 + else if(loglevel>=3) + fprintf(stderr,". %i %li,%li,%li,%li\n", + (int)(bep-border__edge), + bep->x1,bep->y1,bep->x2,bep->y2); + #endif + } // end one line end north of point + #if MAXLOGLEVEL>=3 + else if(loglevel>=3) + fprintf(stderr,"_ %i %li,%li,%li,%li\n", + (int)(bep-border__edge),bep->x1,bep->y1,bep->x2,bep->y2); + #endif + } // end point lies inside x-range + if(bcp==NULL) // chain has not been examined + bcp= bep->chain; // get the first chain link + else + bcp= bcp->next; // get the next chain link + if(bcp==NULL) // no more chain links + break; + bep= bcp->edge; + } // end for own edge and each edge in chain +//if(loglevel>=3) fprintf(stderr,"# %li,%li cross %i\n",x,y,cross); +return (cross&1)!=0; // odd number of crossings + } // end second, consider border polygon (if any) + } // end border_queryinside() + +static void border_querybox(int32_t* x1p,int32_t* y1p, + int32_t* x2p,int32_t* y2p) { + // get the values of a previously defined border box; + // border_box() or border_file() must have been called; + // return values are valid only if border_active==true; + // *x1p,*y1p; // coordinates of southwestern corner; + // *x2p,*y2p; // coordinates of northeastern corner; + int32_t x1,y1,x2,y2; + + if(!border_active) { + *x1p= *y1p= *x2p= *y2p= 0; +return; + } + x1= border__bx1; y1= border__by1; + x2= border__bx2; y2= border__by2; + // round coordinates a bit + #define D(x) { if(x%1000==1) { if(x>0) x--; else x++; } \ + else if((x)%1000==999) { if((x)>0) x++; else x--; } } + D(x1) D(y1) D(x2) D(y2) + #undef D + *x1p= x1; *y1p= y1; *x2p= x2; *y2p= y2; + } // end border_querybox() + +//------------------------------------------------------------ +// end Module border_ OSM border module +//------------------------------------------------------------ + + + +//------------------------------------------------------------ +// Module read_ OSM file read module +//------------------------------------------------------------ + +// this module provides procedures for buffered reading of +// standard input; +// as usual, all identifiers of a module have the same prefix, +// in this case 'read'; an underline will follow in case of a +// global accessible object, two underlines in case of objects +// which are not meant to be accessed from outside this module; +// the sections of private and public definitions are separated +// by a horizontal line: ---- + +#define read_PREFETCH ((32+3)*1024*1024) + // number of bytes which will be available in the buffer after + // every call of read_input(); + // (important for reading .pbf files: + // size must be greater than pb__blockM) +#define read__bufM (read_PREFETCH*5) // length of the buffer; +#define read_GZ 3 // determines which read procedure set will be used; + // ==0: use open(); ==1: use fopen(); + // ==2: use gzopen() (accept gzip compressed input files); + // ==3: use gzopen() with increased gzip buffer; +typedef struct { // members may not be accessed from external + #if read_GZ==0 + int fd; // file descriptor + off_t jumppos; // position to jump to; -1: invalid + #elif read_GZ==1 + FILE* fi; // file stream + off_t jumppos; // position to jump to; -1: invalid + #else + gzFile fi; // gzip file stream + #if __WIN32__ + z_off64_t jumppos; // position to jump to; -1: invalid + #else + z_off_t jumppos; // position to jump to; -1: invalid + #endif + #endif + int64_t counter; + // byte counter to get the read position in input file; + char filename[300]; + bool isstdin; // is standard input + bool eof; // we are at the end of input file + byte* bufp; // pointer in buf[] + byte* bufe; // pointer to the end of valid input in buf[] + uint64_t bufferstart; + // dummy variable which marks the start of the read buffer + // concatenated with this instance of read info structure; + } read_info_t; +static bool read__jumplock= false; // do not change .jumppos anymore; + +//------------------------------------------------------------ + +static read_info_t* read_infop= NULL; + // presently used read info structure, i.e. file handle +#define read__buf ((byte*)&read_infop->bufferstart) + // start address of the file's input buffer +static byte* read_bufp= NULL; // may be incremented by external + // up to the number of read_PREFETCH bytes before read_input() is + // called again; +static byte* read_bufe= NULL; // may not be changed from external + +static int read_open(const char* filename) { + // open an input file; + // filename[]: path and name of input file; + // ==NULL: standard input; + // return: 0: ok; !=0: error; + // read_infop: handle of the file; + // note that you should close every opened file with read_close() + // before the program ends; + + // save status of presently processed input file (if any) + if(read_infop!=NULL) { + read_infop->bufp= read_bufp; + read_infop->bufp= read_bufe; + } + + // get memory space for file information and input buffer + read_infop= (read_info_t*)malloc(sizeof(read_info_t)+read__bufM); + if(read_infop==NULL) { + fprintf(stderr,"osmconvert Error: could not get " + "%i bytes of memory.\n",read__bufM); +return 1; + } + + // initialize read info structure + #if read_GZ==0 + read_infop->fd= 0; // (default) standard input + #else + read_infop->fi= NULL; // (default) file not opened + #endif + if((read_infop->isstdin= filename==NULL)) + strcpy(read_infop->filename,"standard input"); + else + strMcpy(read_infop->filename,filename); + read_infop->eof= false; // we are not at the end of input file + read_infop->bufp= read_infop->bufe= read__buf; // pointer in buf[] + // pointer to the end of valid input in buf[] + read_infop->counter= 0; + read_infop->jumppos= 0; + // store start of file as default jump destination + + // set modul-global variables which are associated with this file + read_bufp= read_infop->bufp; + read_bufe= read_infop->bufe; + + // open the file + if(loglevel>=2) + fprintf(stderr,"Read-opening: %s\n",read_infop->filename); + if(read_infop->isstdin) { // stdin shall be used + #if read_GZ==0 + read_infop->fd= 0; + #elif read_GZ==1 + read_infop->fi= stdin; + #else + read_infop->fi= gzdopen(0,"rb"); + #if read_GZ==3 && ZLIB_VERNUM>=0x1235 + gzbuffer(read_infop->fi,128*1024); + #endif + #endif + } + else if(filename!=NULL) { // a real file shall be opened + #if read_GZ==0 + read_infop->fd= open(filename,O_RDONLY|O_BINARY); + #elif read_GZ==1 + read_infop->fi= fopen(filename,"rb"); + #else + read_infop->fi= gzopen(filename,"rb"); + #if read_GZ==3 && ZLIB_VERNUM>=0x1235 + if(loglevel>=2) + fprintf(stderr,"Read-opening: increasing gzbuffer.\n"); + gzbuffer(read_infop->fi,128*1024); + #endif + #endif + #if read_GZ==0 + if(read_infop->fd<0) { + #else + if(read_infop->fi==NULL) { + #endif + fprintf(stderr, + "osmconvert Error: could not open input file: %.80s\n", + read_infop->filename); + free(read_infop); read_infop= NULL; + read_bufp= read_bufe= NULL; +return 1; + } + } // end a real file shall be opened +return 0; + } // end read_open() + +static void read_close() { + // close an opened file; + // read_infop: handle of the file which is to close; + if(read_infop==NULL) // handle not valid; +return; + if(loglevel>=2) + fprintf(stderr,"Read-closing: %s\n",read_infop->filename); + #if read_GZ==0 + if(read_infop->fd>0) // not standard input + close(read_infop->fd); + #elif read_GZ==1 + if(!read_infop->isstdin) // not standard input + fclose(read_infop->fi); + #else + gzclose(read_infop->fi); + #endif + free(read_infop); read_infop= NULL; + read_bufp= read_bufe= NULL; + } // end read_close() + +static inline bool read_input() { + // read data from standard input file, use an internal buffer; + // make data available at read_bufp; + // read_open() must have been called before calling this procedure; + // return: there are no (more) bytes to read; + // read_bufp: start of next bytes available; + // may be incremented by the caller, up to read_bufe; + // read_bufe: end of bytes in buffer; + // must not be changed by the caller; + // after having called this procedure, the caller may rely on + // having available at least read_PREFETCH bytes at address + // read_bufp - with one exception: if there are not enough bytes + // left to read from standard input, every byte after the end of + // the reminding part of the file in the buffer will be set to + // 0x00 - up to read_bufp+read_PREFETCH; + int l,r; + + if(read_bufp+read_PREFETCH>=read_bufe) { // read buffer is too low + if(!read_infop->eof) { // still bytes in the file + if(read_bufe>read_bufp) { // bytes remaining in buffer + memmove(read__buf,read_bufp,read_bufe-read_bufp); + // move remaining bytes to start of buffer + read_bufe= read__buf+(read_bufe-read_bufp); + // protect the remaining bytes at buffer start + } + else // no remaining bytes in buffer + read_bufe= read__buf; // no bytes remaining to protect + // add read bytes to debug counter + read_bufp= read__buf; + do { // while buffer has not been filled + l= (read__buf+read__bufM)-read_bufe-4; + // number of bytes to read + #if read_GZ==0 + r= read(read_infop->fd,read_bufe,l); + #elif read_GZ==1 + r= read(fileno(read_infop->fi),read_bufe,l); + #else + r= gzread(read_infop->fi,read_bufe,l); + #endif + if(r<=0) { // no more bytes in the file + read_infop->eof= true; + // memorize that there we are at end of file + l= (read__buf+read__bufM)-read_bufe; + // reminding space in buffer + if(l>read_PREFETCH) l= read_PREFETCH; + memset(read_bufe,0,l); // 2011-12-24 + // set reminding space up to prefetch bytes in buffer to 0 + break; + } + read_infop->counter+= r; + read_bufe+= r; // set new mark for end of data + read_bufe[0]= 0; read_bufe[1]= 0; // set 4 null-terminators + read_bufe[2]= 0; read_bufe[3]= 0; + } while(reof && read_bufp>=read_bufe; + } // end read__input() + +static void read_switch(read_info_t* filehandle) { + // switch to another already opened file; + // filehandle: handle of the file which shall be switched to; + + // first, save status of presently processed input file + if(read_infop!=NULL) { + read_infop->bufp= read_bufp; + read_infop->bufe= read_bufe; + } + // switch to new file information + read_infop= filehandle; + read_bufp= read_infop->bufp; + read_bufe= read_infop->bufe; + read_input(); + } // end read_switch() + +static inline int read_rewind() { + // rewind the file, i.e., the file pointer is set + // to the first byte in the file; + // read_infop: handle of the file which is to rewind; + // return: ==0: ok; !=0: rewind error; + bool err; + + #if read_GZ==0 + err= lseek(read_infop->fd,0,SEEK_SET)<0; + #elif read_GZ==1 + err= fseek(read_infop->fi,0,SEEK_SET)<0; + #else + err= gzseek(read_infop->fi,0,SEEK_SET)<0; + #endif + if(err) { + PERRv("could not rewind file: %-80s",read_infop->filename) +return 1; + } + read_infop->counter= 0; + read_bufp= read_bufe; // force refetch + read_infop->eof= false; // force retest for end of file + read_input(); // ensure prefetch +return 0; + } // end read_rewind() + +static inline bool read_setjump() { + // store the current position in the file as a destination + // for a jump which will follow later; + // if global_complexways is false, the call will be ignored; + // the position is not stored anew if it has been locked + // with read_infop->lockpos; + // return: jump position has been stored; + if(!global_complexways) +return false; + if(read__jumplock) +return false; + read_infop->jumppos= read_infop->counter-(read_bufe-read_bufp); + return true; + } // end read_setjump() + +static inline void read_lockjump() { + // prevent a previously stored jump position from being overwritten; + read__jumplock= true; + } // end read_lockjump() + +static int read_jump() { + // jump to a previously stored location it; + // return: 0: jump ok; + // 1: did not actually jump because we already were + // at the desired position; + // <0: error; + #if read_GZ<2 + off_t pos; // present position in the file; + #else + #if __WIN32__ + z_off64_t pos; // position to jump to; -1: invalid + #else + z_off_t pos; // position to jump to; -1: invalid + #endif + #endif + bool err; + + pos= read_infop->counter-(read_bufe-read_bufp); + if(read_infop->jumppos==-1) { + PERRv("no jump destination in file: %.80s",read_infop->filename) +return -1; + } + #if read_GZ==0 + err= lseek(read_infop->fd,read_infop->jumppos,SEEK_SET)<0; + #elif read_GZ==1 + err= fseek(read_infop->fi,read_infop->jumppos,SEEK_SET)<0; + #else + err= gzseek(read_infop->fi,read_infop->jumppos,SEEK_SET)<0; + #endif + if(err) { + PERRv("could not jump in file: %.80s",read_infop->filename) +return -2; + } + if(read_infop->jumppos!=pos) { // this was a real jump + read_infop->counter= read_infop->jumppos; + read_bufp= read_bufe; // force refetch + read_infop->eof= false; // force retest for end of file + read_input(); // ensure prefetch +return 0; + } + // here: did not actually jump because we already were + // at the desired position +return 1; + } // end read_jump() + +//------------------------------------------------------------ +// end Module read_ OSM file read module +//------------------------------------------------------------ + + + +//------------------------------------------------------------ +// Module write_ write module +//------------------------------------------------------------ + +// this module provides a procedure which writes a byte to +// standard output; +// as usual, all identifiers of a module have the same prefix, +// in this case 'write'; an underline will follow in case of a +// global accessible object, two underlines in case of objects +// which are not meant to be accessed from outside this module; +// the sections of private and public definitions are separated +// by a horizontal line: ---- + +static const char* write__filename= NULL; + // last name of the file; ==NULL: standard output; +static const char* write__filename_standard= NULL; + // name of standard output file; ==NULL: standard output; +static const char* write__filename_temp= NULL; + // name of the tempfile; ==NULL: no tempfile; +static char write__buf[UINT64_C(16000000)]; +static char* write__bufe= write__buf+sizeof(write__buf); + // (const) water mark for buffer filled 100% +static char* write__bufp= write__buf; +static int write__fd= 1; // (initially standard output) +static int write__fd_standard= 1; // (initially standard output) +static inline void write_flush(); + +static void write__close() { + // close the last opened file; + if(loglevel>=2) + fprintf(stderr,"Write-closing FD: %i\n",write__fd); + write_flush(); + if(write__fd>1) { // not standard output + close(write__fd); + write__fd= 1; + } + } // end write__close() + +static void write__end() { + // terminate the services of this module; + if(write__fd>1) + write__close(); + if(write__fd_standard>1) { + write__fd= write__fd_standard; + write__close(); + write__fd_standard= 0; + } + if(loglevel<2) + if(write__filename_temp!=NULL) unlink(write__filename_temp); + } // end write__end() + +//------------------------------------------------------------ + +static bool write_testmode= false; // no standard output +static bool write_error= false; // an error has occurred + +static inline void write_flush() { + if(write__bufp>write__buf && !write_testmode) + // at least one byte in buffer AND not test mode + write_error|= + write(write__fd,write__buf,write__bufp-write__buf)<0; + write__bufp= write__buf; + } // end write_flush(); + +static int write_open(const char* filename) { + // open standard output file; + // filename: name of the output file; + // this string must be accessible until program end; + // ==NULL: standard output; + // this procedure must be called before any output is done; + // return: 0: OK; !=0: error; + static bool firstrun= true; + + if(loglevel>=2) + fprintf(stderr,"Write-opening: %s\n", + filename==NULL? "stdout": filename); + if(filename!=NULL) { // not standard output + write__fd= open(filename, + O_WRONLY|O_CREAT|O_TRUNC|O_BINARY,00600); + if(write__fd<1) { + fprintf(stderr, + "osmconvert Error: could not open output file: %.80s\n", + filename); + write__fd= 1; +return 1; + } + write__fd_standard= write__fd; + write__filename_standard= filename; + } + if(firstrun) { + firstrun= false; + atexit(write__end); + } + return 0; + } // end write_open() + +static int write_newfile(const char* filename) { + // change to another (temporary) output file; + // filename: new name of the output file; + // this string must be accessible until program end + // because the name will be needed to delete the file; + // ==NULL: change back to standard output file; + // the previous output file is closed by this procedure, unless + // it is standard output; + // return: 0: OK; !=0: error; + if(loglevel>=2) + fprintf(stderr,"Write-opening: %s\n", + filename==NULL? "stdout": filename); + if(filename==NULL) { // we are to change back to standard output file + if(loglevel>=2) + fprintf(stderr,"Write-reopening: %s\n", + write__filename_standard==NULL? "stdout": + write__filename_standard); + write__close(); // close temporary file + write__filename= write__filename_standard; + write__fd= write__fd_standard; + } + else { // new temporary file shall be opened + if(loglevel>=2) + fprintf(stderr,"Write-opening: %s\n",filename); + write__filename= filename; + unlink(filename); + write__fd= open(filename,O_WRONLY|O_CREAT|O_TRUNC|O_BINARY,00600); + if(write__fd<1) { + fprintf(stderr, + "osmconvert Error: could not open output file: %.80s\n", + filename); + write__fd= 1; +return 2; + } + write__filename_temp= filename; + } + return 0; + } // end write_newfile() + +static inline void write_char(int c) { + // write one byte to stdout, use a buffer; + if(write__bufp>=write__bufe) { // the write buffer is full + if(!write_testmode) + write_error|= + write(write__fd,write__buf,write__bufp-write__buf)<0; + write__bufp= write__buf; + } + *write__bufp++= (char)c; + } // end write_char(); + +static inline void write_mem(const byte* b,int l) { + // write a memory area to stdout, use a buffer; + while(--l>=0) { + if(write__bufp>=write__bufe) { // the write buffer is full + if(!write_testmode) + write_error|= + write(write__fd,write__buf,write__bufp-write__buf)<0; + write__bufp= write__buf; + } + *write__bufp++= (char)(*b++); + } + } // end write_mem(); + +static inline void write_str(const char* s) { + // write a string to stdout, use a buffer; + while(*s!=0) { + if(write__bufp>=write__bufe) { // the write buffer is full + if(!write_testmode) + write_error|= + write(write__fd,write__buf,write__bufp-write__buf)<0; + write__bufp= write__buf; + } + *write__bufp++= (char)(*s++); + } + } // end write_str(); + +static inline void write_xmlstr(const char* s) { + // write an XML string to stdout, use a buffer; + // every character which is not allowed within an XML string + // will be replaced by the appropriate decimal sequence; + static byte allowedchar[]= { + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,0, // \"&' + 0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0, // <> + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,1, // {}DEL + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + #if 1 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + #else + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, + 4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0}; + #endif + byte b0,b1,b2,b3; + int i; + uint32_t u; + #define write__char_D(c) { \ + if(write__bufp>=write__bufe) { \ + if(!write_testmode) \ + write_error|= \ + write(write__fd,write__buf,write__bufp-write__buf)<0; \ + write__bufp= write__buf; \ + } \ + *write__bufp++= (char)(c); } + + for(;;) { + b0= *s++; + if(b0==0) + break; + i= allowedchar[b0]; + if(i==0) // this character may be written as is + write__char_D(b0) + else { // use numeric encoding + if(--i<=0) // one byte + u= b0; + else { + b1= *s++; + if(--i<=0 && b1>=128) // two bytes + u= ((b0&0x1f)<<6)+(b1&0x3f); + else { + b2= *s++; + if(--i<=0 && b1>=128 && b2>=128) // three bytes + u= ((b0&0x0f)<<12)+((b1&0x3f)<<6)+(b2&0x3f); + else { + b3= *s++; + if(--i<=0 && b1>=128 && b2>=128 && b3>=128) // four bytes + u= ((b0&0x07)<<18)+((b1&0x3f)<<12)+ + ((b1&0x3f)<<6)+(b2&0x3f); + else + u= (byte)'?'; + } + } + } + write__char_D('&') write__char_D('#') + if(u<100) { + if(u>=10) + write__char_D(u/10+'0') + write__char_D(u%10+'0') + } + else if(u<1000) { + write__char_D(u/100+'0') + write__char_D((u/10)%10+'0') + write__char_D(u%10+'0') + } + else { + char st[30]; + + uint32toa(u,st); + write_str(st); + } + write__char_D(';') + } // use numeric encoding + } + #undef write__char_D + } // end write_xmlstr(); + +static inline void write_xmlmnstr(const char* s) { + // write an XML string to stdout, use a buffer; + // every character which is not allowed within an XML string + // will be replaced by the appropriate mnemonic or decimal sequence; + static const byte allowedchar[]= { + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 0,0,9,0,0,0,9,9,0,0,0,0,0,0,0,0, // \"&' + 0,0,0,0,0,0,0,0,0,0,0,0,9,0,9,0, // <> + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,1, // {}DEL + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + #if 1 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + #else + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, + 4,4,4,4,4,4,4,4,0,0,0,0,0,0,0,0}; + #endif + byte b0,b1,b2,b3; + int i; + uint32_t u; + #define write__char_D(c) { \ + if(write__bufp>=write__bufe) { \ + if(!write_testmode) \ + write_error|= \ + write(write__fd,write__buf,write__bufp-write__buf)<0; \ + write__bufp= write__buf; \ + } \ + *write__bufp++= (char)(c); } + #define D(i) ((byte)(s[i])) + #define DD ((byte)c) + + for(;;) { + b0= *s++; + if(b0==0) + break; + i= allowedchar[b0]; + if(i==0) // this character may be written as is + write__char_D(b0) + else if(i==9) { // there is a mnemonic for this character + write__char_D('&') + switch(b0) { + case '\"': + write__char_D('q') write__char_D('u') write__char_D('o') + write__char_D('t') + break; + case '&': + write__char_D('a') write__char_D('m') write__char_D('p') + break; + case '\'': + write__char_D('a') write__char_D('p') write__char_D('o') + write__char_D('s') + break; + case '<': + write__char_D('l') write__char_D('t') + break; + case '>': + write__char_D('g') write__char_D('t') + break; + default: + write__char_D('?') // (should never reach here) + } + write__char_D(';') + } // there is a mnemonic for this character + else { // use numeric encoding + if(--i<=0) // one byte + u= b0; + else { + b1= *s++; + if(--i<=0 && b1>=128) // two bytes + u= ((b0&0x1f)<<6)+(b1&0x3f); + else { + b2= *s++; + if(--i<=0 && b1>=128 && b2>=128) // three bytes + u= ((b0&0x0f)<<12)+((b1&0x3f)<<6)+(b2&0x3f); + else { + b3= *s++; + if(--i<=0 && b1>=128 && b2>=128 && b3>=128) // four bytes + u= ((b0&0x07)<<18)+((b1&0x3f)<<12)+ + ((b1&0x3f)<<6)+(b2&0x3f); + else + u= (byte)'?'; + } + } + } + write__char_D('&') write__char_D('#') + if(u<100) { + if(u>=10) + write__char_D(u/10+'0') + write__char_D(u%10+'0') + } + else if(u<1000) { + write__char_D(u/100+'0') + write__char_D((u/10)%10+'0') + write__char_D(u%10+'0') + } + else { + char st[30]; + + uint32toa(u,st); + write_str(st); + } + write__char_D(';') + } // use numeric encoding + } + #undef DD + #undef D + #undef write__char_D + } // end write_xmlmnstr(); + +static inline void write_uint32(uint32_t v) { + // write an unsigned 32 bit integer number to standard output; + char s[20],*s1,*s2,c; + + s1= s; + if(v==0) + *s1++= '0'; + s2= s1; + while(v>0) + { *s2++= (v%10)+'0'; v/= 10; } + *s2--= 0; + while(s2>s1) + { c= *s1; *s1= *s2; *s2= c; s1++; s2--; } + write_str(s); + } // end write_uint32() + +#if 0 // currently unused +static inline void write_sint32(int32_t v) { + // write a signed 32 bit integer number to standard output; + char s[20],*s1,*s2,c; + + s1= s; + if(v<0) + { *s1++= '-'; v= -v; } + else if(v==0) + *s1++= '0'; + s2= s1; + while(v>0) + { *s2++= (v%10)+'0'; v/= 10; } + *s2--= 0; + while(s2>s1) + { c= *s1; *s1= *s2; *s2= c; s1++; s2--; } + write_str(s); + } // end write_sint32() +#endif + +static inline void write_uint64(uint64_t v) { + // write an unsigned 64 bit integer number to standard output; + char s[30],*s1,*s2,c; + + s1= s; + if(v==0) + *s1++= '0'; + s2= s1; + while(v>0) + { *s2++= (v%10)+'0'; v/= 10; } + *s2--= 0; + while(s2>s1) + { c= *s1; *s1= *s2; *s2= c; s1++; s2--; } + write_str(s); + } // end write_uint64() + +static inline void write_createsint64(int64_t v,char* sp) { + // create a signed 64 bit integer number; + // return: + // sp[30]: value v as decimal integer string; + static char *s1,*s2,c; + + s1= sp; + if(v<0) + { *s1++= '-'; v= -v; } + else if(v==0) + *s1++= '0'; + s2= s1; + while(v>0) + { *s2++= (v%10)+'0'; v/= 10; } + *s2--= 0; + while(s2>s1) + { c= *s1; *s1= *s2; *s2= c; s1++; s2--; } + } // end write_sint64() + +static inline void write_sint64(int64_t v) { + // write a signed 64 bit integer number to standard output; + static char s[30],*s1,*s2,c; + + s1= s; + if(v<0) + { *s1++= '-'; v= -v; } + else if(v==0) + *s1++= '0'; + s2= s1; + while(v>0) + { *s2++= (v%10)+'0'; v/= 10; } + *s2--= 0; + while(s2>s1) + { c= *s1; *s1= *s2; *s2= c; s1++; s2--; } + write_str(s); + } // end write_sint64() + +static inline char* write_createsfix7o(int32_t v,char* s) { + // convert a signed 7 decimals fixpoint value into a string; + // keep trailing zeros; + // v: fixpoint value + // return: pointer do string terminator; + // s[12]: destination string; + char* s1,*s2,*sterm,c; + int i; + + s1= s; + if(v<0) + { *s1++= '-'; v= -v; } + s2= s1; + i= 7; + while(--i>=0) + { *s2++= (v%10)+'0'; v/= 10; } + *s2++= '.'; + do + { *s2++= (v%10)+'0'; v/= 10; } + while(v>0); + sterm= s2; + *s2--= 0; + while(s2>s1) + { c= *s1; *s1= *s2; *s2= c; s1++; s2--; } + return sterm; + } // end write_createsfix7o() + +static inline void write_sfix7(int32_t v) { + // write a signed 7 decimals fixpoint value to standard output; + char s[20],*s1,*s2,c; + int i; + + s1= s; + if(v<0) + { *s1++= '-'; v= -v; } + s2= s1; + i= 7; + while((v%10)==0 && i>1) // trailing zeros + { v/= 10; i--; } + while(--i>=0) + { *s2++= (v%10)+'0'; v/= 10; } + *s2++= '.'; + do + { *s2++= (v%10)+'0'; v/= 10; } + while(v>0); + *s2--= 0; + while(s2>s1) + { c= *s1; *s1= *s2; *s2= c; s1++; s2--; } + write_str(s); + } // end write_sfix7() + +static inline void write_sfix7o(int32_t v) { + // write a signed 7 decimals fixpoint value to standard output; + // keep trailing zeros; + char s[20],*s1,*s2,c; + int i; + + s1= s; + if(v<0) + { *s1++= '-'; v= -v; } + s2= s1; + i= 7; + while(--i>=0) + { *s2++= (v%10)+'0'; v/= 10; } + *s2++= '.'; + do + { *s2++= (v%10)+'0'; v/= 10; } + while(v>0); + *s2--= 0; + while(s2>s1) + { c= *s1; *s1= *s2; *s2= c; s1++; s2--; } + write_str(s); + } // end write_sfix7o() + +static inline void write_sfix6o(int32_t v) { + // write a signed 6 decimals fixpoint value to standard output; + // keep trailing zeros; + char s[20],*s1,*s2,c; + int i; + + s1= s; + if(v<0) + { *s1++= '-'; v= -v; } + s2= s1; + i= 6; + while(--i>=0) + { *s2++= (v%10)+'0'; v/= 10; } + *s2++= '.'; + do + { *s2++= (v%10)+'0'; v/= 10; } + while(v>0); + *s2--= 0; + while(s2>s1) + { c= *s1; *s1= *s2; *s2= c; s1++; s2--; } + write_str(s); + } // end write_sfix6o() + +#if 0 // currently unused +static inline void write_sfix9(int64_t v) { + // write a signed 9 decimals fixpoint value to standard output; + char s[20],*s1,*s2,c; + int i; + + s1= s; + if(v<0) + { *s1++= '-'; v= -v; } + s2= s1; + i= 9; + while(--i>=0) + { *s2++= (v%10)+'0'; v/= 10; } + *s2++= '.'; + do + { *s2++= (v%10)+'0'; v/= 10; } + while(v>0); + *s2--= 0; + while(s2>s1) + { c= *s1; *s1= *s2; *s2= c; s1++; s2--; } + write_str(s); + } // end write_sfix9() +#endif + +static inline void write_createtimestamp(uint64_t v,char* sp) { + // write a timestamp in OSM format, e.g.: "2010-09-30T19:23:30Z", + // into a string; + // v: value of the timestamp; + // sp[21]: destination string; + time_t vtime; + struct tm tm; + int i; + + vtime= v; + #if __WIN32__ + memcpy(&tm,gmtime(&vtime),sizeof(tm)); + #else + gmtime_r(&vtime,&tm); + #endif + i= tm.tm_year+1900; + sp+= 3; *sp--= i%10+'0'; + i/=10; *sp--= i%10+'0'; + i/=10; *sp--= i%10+'0'; + i/=10; *sp= i%10+'0'; + sp+= 4; *sp++= '-'; + i= tm.tm_mon+1; + *sp++= i/10+'0'; *sp++= i%10+'0'; *sp++= '-'; + i= tm.tm_mday; + *sp++= i/10+'0'; *sp++= i%10+'0'; *sp++= 'T'; + i= tm.tm_hour; + *sp++= i/10+'0'; *sp++= i%10+'0'; *sp++= ':'; + i= tm.tm_min; + *sp++= i/10+'0'; *sp++= i%10+'0'; *sp++= ':'; + i= tm.tm_sec%60; + *sp++= i/10+'0'; *sp++= i%10+'0'; *sp++= 'Z'; *sp= 0; + } // end write_createtimestamp() + +static inline void write_timestamp(uint64_t v) { + // write a timestamp in OSM format, e.g.: "2010-09-30T19:23:30Z" + char s[30]; + + write_createtimestamp(v,s); + write_str(s); + } // end write_timestamp() + +//------------------------------------------------------------ +// end Module write_ write module +//------------------------------------------------------------ + + + +//------------------------------------------------------------ +// Module csv_ csv write module +//------------------------------------------------------------ + +// this module provides procedures for generating csv output; +// as usual, all identifiers of a module have the same prefix, +// in this case 'csv'; an underline will follow in case of a +// global accessible object, two underlines in case of objects +// which are not meant to be accessed from outside this module; +// the sections of private and public definitions are separated +// by a horizontal line: ---- + +#define csv__keyM 200 // max number of keys and vals +#define csv__keyMM 256 // max number of characters +1 in key or val +static char* csv__key= NULL; // [csv__keyM][csv__keyMM] +static int csv__keyn= 0; // number of keys +static char* csv__val= NULL; // [csv__keyM][csv__keyMM] +static int csv__valn= 0; // number of vals +// some booleans which tell us if certain keys are in column list; +// this is for program acceleration +static bool csv_key_otype= false, csv_key_oname= false, + csv_key_id= false, csv_key_lon= false, csv_key_lat= false, + csv_key_version=false, csv_key_timestamp=false, + csv_key_changeset=false, csv_key_uid= false, csv_key_user= false; +static char csv__sep0= '\t'; // first character of global_csvseparator; +static char csv__rep0= ' '; // replacement character for separator char + +static void csv__end() { + // clean-up csv processing; + + if(csv__key!=NULL) + { free(csv__key); csv__key= NULL; } + if(csv__val!=NULL) + { free(csv__val); csv__val= NULL; } + } // end csv__end() + +//------------------------------------------------------------ + +static int csv_ini(const char* columns) { + // initialize this module; + // must be called before any other procedure is called; + // may be called more than once; only the first call will + // initialize this module, every other call will solely + // overwrite the columns information if !=NULL; + // columns[]: space-separated list of keys who are to be + // used as column identifiers; + // ==NULL: if list has already been given, do not + // change it; if not, set list to default; + // return: 0: everything went ok; + // !=0: an error occurred; + static bool firstrun= true; + + if(firstrun) { + firstrun= false; + + csv__key= (char*)malloc(csv__keyM*csv__keyMM); + csv__val= (char*)malloc(csv__keyM*csv__keyMM); + if(csv__key==NULL || csv__val==NULL) +return 1; + atexit(csv__end); + } + if(columns==NULL) { // default columns shall be set + if(csv__keyn==0) { // until now no column has been set + // set default columns + strcpy(&csv__key[0*csv__keyMM],"@oname"); + csv_key_oname= true; + strcpy(&csv__key[1*csv__keyMM],"@id"); + csv_key_id= true; + strcpy(&csv__key[2*csv__keyMM],"name"); + csv__keyn= 3; + } // until now no column has been set + } // default columns shall be set + else { // new columns shall be set + for(;;) { // for each column name + int len; + char* tp; + + len= strcspn(columns," "); + if(len==0) + break; + if(csv__keyn>=csv__keyM) { + WARN("too many csv columns") + break; + } + len++; + if(len>csv__keyMM) len= csv__keyMM; // limit key length + tp= &csv__key[csv__keyn*csv__keyMM]; + strmcpy(tp,columns,len); + csv__keyn++; + if(strcmp(tp,"@otype")==0) csv_key_otype= true; + else if(strcmp(tp,"@oname")==0) csv_key_oname= true; + else if(strcmp(tp,"@id")==0) csv_key_id= true; + else if(strcmp(tp,"@lon")==0) csv_key_lon= true; + else if(strcmp(tp,"@lat")==0) csv_key_lat= true; + else if(strcmp(tp,"@version")==0) csv_key_version= true; + else if(strcmp(tp,"@timestamp")==0) csv_key_timestamp= true; + else if(strcmp(tp,"@changeset")==0) csv_key_changeset= true; + else if(strcmp(tp,"@uid")==0) csv_key_uid= true; + else if(strcmp(tp,"@user")==0) csv_key_user= true; + columns+= len-1; + if(columns[0]==' ') columns++; + } // for each column name + } // new columns shall be set + // care about separator chars + if(global_csvseparator[0]==0 || global_csvseparator[1]!=0) { + csv__sep0= 0; + csv__rep0= 0; + } + else { + csv__sep0= global_csvseparator[0]; + if(csv__sep0==' ') + csv__rep0= '_'; + else + csv__rep0= ' '; + } + return 0; + } // end csv_ini() + +static void csv_add(const char* key,const char* val) { + // test if the key's value shall be printed and do so if yes; + int keyn; + const char* kp; + + keyn= csv__keyn; + kp= csv__key; + while(keyn>0) { // for all keys in column list + if(strcmp(key,kp)==0) { // key is in column list + strmcpy(csv__val+(kp-csv__key),val,csv__keyMM); + // store value + csv__valn++; + break; + } // key is in column list + kp+= csv__keyMM; // take next key in list + keyn--; + } // for all keys in column list + } // end csv_add() + +static void csv_write() { + // write a csv line - if csv data had been stored + char* vp,*tp; + int keyn; + + if(csv__valn==0) +return; + vp= csv__val; + keyn= csv__keyn; + while(keyn>0) { // for all keys in column list + if(*vp!=0) { // there is a value for this key + tp= vp; + do { + if(*tp==csv__sep0 || *tp==NL[0] || *tp==NL[1]) + // character identical with separator or line feed + write_char(csv__rep0); // replace it by replacement char + else + write_char(*tp); + tp++; + } while(*tp!=0); + *vp= 0; // delete list entry + } + vp+= csv__keyMM; // take next val in list + keyn--; + if(keyn>0) // at least one column will follow + write_str(global_csvseparator); + } // for all keys in column list + write_str(NL); + csv__valn= 0; + } // end csv_write() + +static void csv_headline() { + // write a headline to csv output file + char* kp; + int keyn; + + if(!global_csvheadline) // headline shall not be written +return; + kp= csv__key; + keyn= csv__keyn; + while(keyn>0) { // for all keys in column list + csv_add(kp,kp); + kp+= csv__keyMM; // take next key in list + keyn--; + } // for all keys in column list + csv_write(); + } // end csv_headline() + +//------------------------------------------------------------ +// end Module csv_ csv write module +//------------------------------------------------------------ + + + +//------------------------------------------------------------ +// Module pb_ pbf read module +//------------------------------------------------------------ + +// this module provides procedures which read osm .pbf objects; +// it uses procedures from modules read_ and pbf_; +// as usual, all identifiers of a module have the same prefix, +// in this case 'pb'; an underline will follow in case of a +// global accessible object, two underlines in case of objects +// which are not meant to be accessed from outside this module; +// the sections of private and public definitions are separated +// by a horizontal line: ---- + +static int pb__decompress(byte* ibuf,uint isiz,byte* obuf,uint osizm, + uint* osizp) { + // decompress a block of data; + // return: 0: decompression was successful; + // !=0: error number from zlib; + // *osizp: size of uncompressed data; + z_stream strm; + int r,i; + + // initialization + strm.zalloc= Z_NULL; + strm.zfree= Z_NULL; + strm.opaque= Z_NULL; + strm.next_in= Z_NULL; + strm.total_in= 0; + strm.avail_out= 0; + strm.next_out= Z_NULL; + strm.total_out= 0; + strm.msg= NULL; + r= inflateInit(&strm); + if(r!=Z_OK) +return r; + // read data + strm.next_in = ibuf; + strm.avail_in= isiz; + // decompress + strm.next_out= obuf; + strm.avail_out= osizm; + r= inflate(&strm,Z_FINISH); + if(r!=Z_OK && r!=Z_STREAM_END) { + inflateEnd(&strm); + *osizp= 0; +return r; + } + // clean-up + inflateEnd(&strm); + obuf+= *osizp= osizm-(i= strm.avail_out); + // add some zero bytes + if(i>4) i= 4; + while(--i>=0) *obuf++= 0; + return 0; + } // end pb__decompress() + +static inline int64_t pb__strtimetosint64(const char* s) { + // read a timestamp in OSM format, e.g.: "2010-09-30T19:23:30Z", + // and convert it to a signed 64-bit integer; + // return: time as a number (seconds since 1970); + // ==0: syntax error; + if((s[0]!='1' && s[0]!='2') || + !isdig(s[1]) || !isdig(s[2]) || !isdig(s[3]) || + s[4]!='-' || !isdig(s[5]) || !isdig(s[6]) || + s[7]!='-' || !isdig(s[8]) || !isdig(s[9]) || + s[10]!='T' || !isdig(s[11]) || !isdig(s[12]) || + s[13]!=':' || !isdig(s[14]) || !isdig(s[15]) || + s[16]!=':' || !isdig(s[17]) || !isdig(s[18]) || + s[19]!='Z') // wrong syntax +return 0; + /* regular timestamp */ { + struct tm tm; + + tm.tm_isdst= 0; + tm.tm_year= + (s[0]-'0')*1000+(s[1]-'0')*100+(s[2]-'0')*10+(s[3]-'0')-1900; + tm.tm_mon= (s[5]-'0')*10+s[6]-'0'-1; + tm.tm_mday= (s[8]-'0')*10+s[9]-'0'; + tm.tm_hour= (s[11]-'0')*10+s[12]-'0'; + tm.tm_min= (s[14]-'0')*10+s[15]-'0'; + tm.tm_sec= (s[17]-'0')*10+s[18]-'0'; + #if __WIN32__ + // use replcement for timegm() because Windows does not know it +return mktime(&tm)-timezone; + #else +return timegm(&tm); + #endif + } // regular timestamp + } // end pb__strtimetosint64() + +// for string primitive group table +#define pb__strM (4*1024*1024) + // maximum number of strings within each block +static char* pb__str[pb__strM]; // string table +static char** pb__stre= pb__str; // end of data in str[] +static char** pb__stree= pb__str+pb__strM; // end of str[] +static int pb__strm= 0; +// for tags of densnodes (start and end address) +static byte* pb__nodetags= NULL,*pb__nodetagse= NULL; // node tag pairs +// for noderefs and tags of ways (start and end address each) +static byte* pb__waynode= NULL,*pb__waynodee= NULL; +static byte* pb__waykey= NULL,*pb__waykeye= NULL; +static byte* pb__wayval= NULL,*pb__wayvale= NULL; +// for refs and tags of relations (start and end address each) +static byte* pb__relrefrole= NULL,*pb__relrefrolee= NULL; +static byte* pb__relrefid= NULL,*pb__relrefide= NULL; +static byte* pb__relreftype= NULL,*pb__relreftypee= NULL; +static byte* pb__relkey= NULL,*pb__relkeye= NULL; +static byte* pb__relval= NULL,*pb__relvale= NULL; + +//------------------------------------------------------------ + +static bool pb_bbvalid= false; + // the following bbox coordinates are valid; +static int32_t pb_bbx1,pb_bby1,pb_bbx2,pb_bby2; + // bbox coordinates (base 10^-7); +static uint64_t pb_filetimestamp= 0; +static int pb_type= -9; // type of the object which has been read; + // 0: node; 1: way; 2: relation; 8: header; + // -1: end of file; <= -10: error; +static int64_t pb_id= 0; // id of read object +static int32_t pb_lon= 0,pb_lat= 0; // coordinates of read node +static int32_t pb_hisver= 0; +static int64_t pb_histime= 0; +static int64_t pb_hiscset= 0; +static uint32_t pb_hisuid= 0; +static char* pb_hisuser= ""; +static int32_t pb_hisvis= -1; // (default for 'unknown') + +static void pb_ini() { + // initialize this module; + // must be called as first procedure of this module; + } // end pb_ini() + +static int pb_input(bool reset) { + // read next pbf object and make it available via other + // procedures of this mudule; + // pb_ini() must have been called before calling this procedure; + // reset: just reset al buffers, do nothing else; + // this is if the file has been rewound; + // return: >=0: OK; -1: end of file; <=-10: error; + // pb_type: type of the object which has been read; + // in dependence of object's type the following information + // will be available: + // pb_bbvalid: the following bbox coordinates are valid; + // pb_bbx1,pb_bby1,pb_bbx2,pb_bby2: bbox coordinates (base 10^-7); + // pb_filetimestamp: timestamp of the file; 0: no file timestamp; + // pb_id: id of this object; + // pb_lon: latitude in 100 nanodegree; + // pb_lat: latitude in 100 nanodegree; + // pb_hisver: version; + // pb_histime: time (seconds since 1970) + // pb_hiscset: changeset + // pb_hisuid: uid; ==0: no user information available; + // pb_hisuser: user name + // subsequent to calling this procedure, the caller may call + // the following procedures - depending on pb_type(): + // pb_noderef(), pb_ref(), pb_keyval() + // the caller may omit these subsequent calls for ways and relations, + // but he must not temporarily omit them for nodes; + // if he omits such a subsequent call for one node, he must not + // call pb_keyval() for any other of the following nodes because + // this would result in wrong key/val data; + #define END(r) {pb_type= (r); goto end;} + // jump to procedure's end and provide a return code + #define ENDE(r,f) { PERR(f) END(r) } + // print error message, then jump to end + #define ENDEv(r,f,...) { PERRv(f,__VA_ARGS__) END(r) } + // print error message with value(s), then jump to end + int blocktype= -1; + // -1: expected; 0: unknown; 1: Header; 2: Data; + #define pb__blockM (32*1024*1024) // maximum block size + static byte zbuf[pb__blockM+1000]; + static byte* zbufp= zbuf,*zbufe= zbuf; + static byte* groupp= zbuf,*groupe= zbuf; + // memory area for primitive groups + // start and end of different arrays, all used for dense nodes: + static byte* nodeid= NULL,*nodeide= NULL; // node ids + static byte* nodever= NULL,*nodevere= NULL; // versions + static byte* nodetime= NULL,*nodetimee= NULL; // times + static byte* nodecset= NULL,*nodecsete= NULL; // change sets + static byte* nodeuid= NULL,*nodeuide= NULL; // user ids + static byte* nodeuser= NULL,*nodeusere= NULL; // user names + static byte* nodevis= NULL,*nodevise= NULL; // visible + static byte* nodelat= NULL,*nodelate= NULL; // node latitudes + static byte* nodelon= NULL,*nodelone= NULL; // node longitudes + static uint32_t hisuser= 0; // string index of user name (delta coded) + static bool waycomplete= false,relcomplete= false; + + if(reset) { + zbufp= zbuf,zbufe= zbuf; + groupp= zbuf,groupe= zbuf; + nodeid= NULL,nodeide= NULL; + nodever= NULL,nodevere= NULL; + nodetime= NULL,nodetimee= NULL; + nodecset= NULL,nodecsete= NULL; + nodeuid= NULL,nodeuide= NULL; + nodeuser= NULL,nodeusere= NULL; + nodevis= NULL,nodevise= NULL; + nodelat= NULL,nodelate= NULL; + nodelon= NULL,nodelone= NULL; + hisuser= 0; + waycomplete= false,relcomplete= false; + pb_type= 99; +return 0; + } + for(;;) { // until we have a new object + mainloop: + if(nodeid=nodevere || nodetime>=nodetimee || + nodecset>=nodecsete || nodeuid>=nodeuide || + nodeuser>=nodeusere) // no author information available + pb_hisver= 0; + else { // author information available + pb_hisver= pbf_uint32(&nodever); + pb_histime+= pbf_sint64(&nodetime); + pb_hiscset+= pbf_sint64(&nodecset); + pb_hisuid+= pbf_sint32(&nodeuid); + hisuser+= pbf_sint32(&nodeuser); + if(hisuser=%i", + pb_id,hisuser,pb__strm) + hisuser= 0; pb_hisuser= ""; + } + pb_hisvis= -1; + if(nodevis!=NULL && nodevis0) { + // author information available + if(hisuser=%i", + pb_id,hisuser,pb__strm) + hisuser= 0; pb_hisuser= ""; + } + } // end author information available + END(1) + } // ways left + if(relcomplete) { // relations left + // provide a relation + relcomplete= false; + // (already got id and author integers) + if(pb_hisver!=0 && hisuser>0) { + // author information available + if(hisuser=%i", + pb_id,hisuser,pb__strm) + hisuser= 0; pb_hisuser= ""; + } + } // end author information available + END(2) + } // relations left + if(grouppgroupe) + ENDEv(-202,"dense nodes too large: %u",l) + groupp= bp+l; + /* get all node data lists */ { + // decode dense node part of primitive group of Data block + byte* dne; // end of dense node memory area + uint l; + byte* bhise; // end of author section in buf[] + + dne= groupp; + while(bpdne) + ENDEv(-301,"node id table too large: %u",l) + nodeid= bp; + nodeide= (bp+= l); + break; + case 0x2a: // S 5, author - with subelements + bp++; + l= pbf_uint32(&bp); + if(bp+l>dne) + ENDEv(-302,"node author section too large: %u",l) + if(global_dropversion) { + // version number is not required + bp+= l; // jump over this section and ignore it + break; + } + bhise= bp+l; + nodevis= NULL; + while(bpbhise) + ENDEv(-303,"node version table too large: %u",l) + nodever= bp; + nodevere= (bp+= l); + break; + case 0x12: // S 2, times + bp++; + l= pbf_uint32(&bp); + if(bp+l>bhise) + ENDEv(-304,"node time table too large: %u",l) + nodetime= bp; + nodetimee= (bp+= l); + break; + case 0x1a: // S 3, change sets + bp++; + l= pbf_uint32(&bp); + if(bp+l>bhise) + ENDEv(-305, + "node change set table too large: %u",l) + nodecset= bp; + nodecsete= (bp+= l); + break; + case 0x22: // S 4, user ids + bp++; + l= pbf_uint32(&bp); + if(bp+l>bhise) + ENDEv(-306,"node user id table too large: %u",l) + nodeuid= bp; + nodeuide= (bp+= l); + break; + case 0x2a: // S 5, user names + bp++; + l= pbf_uint32(&bp); + if(bp+l>bhise) + ENDEv(-307,"node user name table too large: %u",l); + nodeuser= bp; + nodeusere= (bp+= l); + break; + case 0x32: // S 6, visible + bp++; + l= pbf_uint32(&bp); + if(bp+l>bhise) + ENDEv(-308,"node version table too large: %u",l) + nodevis= bp; + nodevise= (bp+= l); + break; + default: + WARNv("node author element type unknown: " + "0x%02X 0x%02X.",bp[0],bp[1]) + if(pbf_jump(&bp)) + END(-308) + } // end first byte of element + } // end for each author subelement + if(bp>bhise) + ENDE(-309,"node author format length.") + bp= bhise; + break; // end author - with subelements + case 0x42: // S 8, latitudes + bp++; + l= pbf_uint32(&bp); + if(bp+l>dne) + ENDEv(-310,"node latitude table too large: %u",l) + nodelat= bp; + nodelate= (bp+= l); + break; + case 0x4a: // S 9, longitudes + bp++; + l= pbf_uint32(&bp); + if(bp+l>dne) + ENDEv(-311,"node longitude table too large: %u",l) + nodelon= bp; + nodelone= (bp+= l); + break; + case 0x52: // S 10, tag pairs + bp++; + l= pbf_uint32(&bp); + if(bp+l>dne) + ENDEv(-312,"node tag pair table too large: %u",l) + pb__nodetags= bp; + pb__nodetagse= (bp+= l); + break; + default: + WARNv("dense node element type unknown: " + "0x%02X 0x%02X.",bp[0],bp[1]) + if(pbf_jump(&bp)) + END(-313) + } // end first byte of element + if(bp>dne) + ENDE(-314,"dense node format length.") + } // end for every element in this loop + // reset (delta-coded) variables + pb_id= 0; + pb_lat= pb_lon= 0; + pb_histime= 0; + pb_hiscset= 0; + pb_hisuid= 0; + hisuser= 0; + pb_hisuser= ""; + bp= groupp; + if(nodeidgroupe) + ENDEv(-204,"ways too large: %u",l) + groupp= bp+l; + /* get way data */ { + byte* bpe; // end of ways memory area + uint l; + byte* bhise; // end of author section in zbuf[] + int complete; + // flags which determine if the dataset is complete + int hiscomplete; + // flags which determine if the author is complete + + bpe= groupp; + complete= hiscomplete= 0; + while(bpbpe) + ENDEv(-401,"way key table too large: %u",l) + pb__waykey= bp; + pb__waykeye= (bp+= l); + complete|= 2; + break; + case 0x1a: // S 3, vals + bp++; + l= pbf_uint32(&bp); + /* deal with strange S 3 element at data set end */ { + if(complete & (4|16)) { + // already have vals or node refs + WARNv("format 0x1a found: %02X",complete) + break; // ignore this element + } + } + if(bp+l>bpe) + ENDEv(-403,"way val table too large: %u",l) + pb__wayval= bp; + pb__wayvale= (bp+= l); + complete|= 4; + break; + case 0x22: // S 4, author - with subelements + bp++; + l= pbf_uint32(&bp); + if(bp+l>bpe) + ENDEv(-404,"way author section too large: %u",l) + if(global_dropversion) { + // version number is not required + bp+= l; // jump over this section and ignore it + break; + } + bhise= bp+l; + pb_hisvis= -1; + while(bpbhise) + ENDE(-411,"way author format length.") + bp= bhise; + complete|= 8; + break; // end author - with subelements + case 0x42: // S 8, node refs + bp++; + l= pbf_uint32(&bp); + if(bp+l>bpe) + ENDEv(-412,"way noderef table too large: %u",l) + pb__waynode= bp; + pb__waynodee= (bp+= l); + complete|= 16; + break; + default: + WARNv("way element type unknown: " + "0x%02X 0x%02X 0x%02X 0x%02X + %i.", + bp[0],bp[1],bp[2],bp[3],complete) + if(pbf_jump(&bp)) + END(-421) + } // end first byte of element + if(bp>bpe) + ENDE(-429,"way format length.") + } // for every element in this primitive group + bp= groupp; + if((hiscomplete&7)!=7) // author information not available + pb_hisver= 0; + else if((hiscomplete&24)!=24) // no user information + pb_hisuid= 0; + #if 1 // 2014-06-16 + if((complete & 1)==1) { // minimum contents available + // (at least id) + #else + if((complete & 17)==17) { // minimum contents available + // (at least id and node refs) + #endif + waycomplete= true; + goto mainloop; + } + } // get way data + break; + //// relations + case 0x22: // S 4, rels + bp++; + l= pbf_uint32(&bp); + if(bp+l>groupe) + ENDEv(-206,"rels too large: %u",l) + groupp= bp+l; + /* get relation data */ { + byte* bpe; // end of ways memory area + uint l; + byte* bhise; // end of author section in zbuf[] + int complete; + // flags which determine if the dataset is complete + int hiscomplete; // flags which determine + // if the author information is complete + + bpe= groupp; + complete= hiscomplete= 0; + while(bpbpe) + ENDEv(-501,"rel key table too large: %u",l) + pb__relkey= bp; + pb__relkeye= (bp+= l); + complete|= 2; + break; + case 0x1a: // S 3, vals + bp++; + l= pbf_uint32(&bp); + if(bp+l>bpe) + ENDEv(-502,"rel val table too large: %u",l) + pb__relval= bp; + pb__relvale= (bp+= l); + complete|= 4; + break; + case 0x22: // S 4, author - with subelements + bp++; + l= pbf_uint32(&bp); + if(bp+l>bpe) + ENDEv(-503,"rel author section too large: %u",l) + if(global_dropversion) { + // version number is not required + bp+= l; // jump over this section and ignore it + break; + } + bhise= bp+l; + pb_hisvis= -1; + while(bpbhise) + ENDE(-510,"rel author format length.") + bp= bhise; + complete|= 8; + break; // end author - with subelements + case 0x42: // S 8, refroles + bp++; + l= pbf_uint32(&bp); + if(bp+l>bpe) + ENDEv(-511,"rel role table too large: %u",l) + pb__relrefrole= bp; + pb__relrefrolee= (bp+= l); + complete|= 16; + break; + case 0x4a: // S 9, refids + bp++; + l= pbf_uint32(&bp); + if(bp+l>bpe) + ENDEv(-512,"rel id table too large: %u",l) + pb__relrefid= bp; + pb__relrefide= (bp+= l); + complete|= 32; + break; + case 0x52: // S 10, reftypes + bp++; + l= pbf_uint32(&bp); + if(bp+l>bpe) + ENDEv(-513,"rel type table too large: %u",l) + pb__relreftype= bp; + pb__relreftypee= (bp+= l); + complete|= 64; + break; + default: + WARNv("rel element type unknown: " + "0x%02X 0x%02X 0x%02X 0x%02X + %i.", + bp[0],bp[1],bp[2],bp[3],complete) + if(pbf_jump(&bp)) + END(-514) + } // end first byte of element + if(bp>bpe) + ENDE(-519,"rel format length.") + } // for every element in this primitive group + bp= groupp; + if((hiscomplete&7)!=7) // author information not available + pb_hisver= 0; + else if((hiscomplete&24)!=24) // no user information + pb_hisuid= 0; + #if 1 + if((complete & 1)==1) { // minimum contents available (id) + #else + if((complete & 113)==113 || + (complete & 7)==7) { // minimum contents available + // have at least id and refs (1|16|32|64) OR + // have at least id and keyval (1|2|4) + #endif + relcomplete= true; + goto mainloop; + } + } // get way data + break; + default: + WARNv("primitive group element type unknown: " + "0x%02X 0x%02X.",bp[0],bp[1]) + if(pbf_jump(&bp)) + END(-209) + } // end first byte of primitive group element + } // end for each element in primitive group + } // data in primitive group left + if(zbufp sfix7 + int bboxflags; + + osmschema= false; + densenodes= false; + bboxflags= 0; + bp= zbufp; + while(bp=100) // bbox group too large + ENDEv(-41,"bbox group too large: %u",l) + bboxe= bp+l; + while(bp0) coord+= 99; + pb_bbx2= coord/100; + bboxflags|= 0x02; + break; + case 0x18: // V 3, maxlat + bp++; + coord= pbf_sint64(&bp); + if(coord>0) coord+= 99; + pb_bby2= coord/100; + bboxflags|= 0x04; + break; + case 0x20: // V 4, minlat + bp++; + coord= pbf_sint64(&bp); + if(coord<0) coord-= 99; + pb_bby1= coord/100; + bboxflags|= 0x08; + break; + default: + WARNv("bbox element type unknown: " + "0x%02X 0x%02X.",bp[0],bp[1]) + if(pbf_jump(&bp)) + END(-42) + } // end first byte of element + if(bp>bboxe) + ENDE(-43,"bbox format length.") + } // end for every element in bbox + bp= bboxe; + break; + case 0x22: // S 4, required features + bp++; + l= pbf_uint32(&bp); + if(memcmp(bp-1,"\x0e""OsmSchema-V0.6",15)==0) + osmschema= true; + else if(memcmp(bp-1,"\x0a""DenseNodes",11)==0) + densenodes= true; + else if(memcmp(bp-1,"\x15""HistoricalInformation",21)==0) + ; + else // unsupported feature + ENDEv(-44,"unsupported feature: %.*s",l>50? 50: l,bp) + bp+= l; + break; + case 0x2a: // 0x01 S 5, optional features + bp++; + l= pbf_uint32(&bp); + if(memcmp(bp-1,"\x1e""timestamp=",11)==0) { + // file timestamp available + pb_filetimestamp= pb__strtimetosint64((char*)bp+10); + } // file timestamp available + bp+= l; + break; + case 0x82: // 0x01 S 16, writing program + if(bp[1]!=0x01) goto h_unknown; + bp+= 2; + l= pbf_uint32(&bp); + bp+= l; // (ignore this element) + break; + case 0x8a: // 0x01 S 17, source + if(bp[1]!=0x01) goto h_unknown; + bp+= 2; + l= pbf_uint32(&bp); + bp+= l; // (ignore this element) + break; + case 0x80: // 0x02 V 32, osmosis_replication_timestamp + if(bp[1]!=0x02) goto h_unknown; + bp+= 2; + pb_filetimestamp= pbf_uint64(&bp); + break; + case 0x88: // 0x02 V 33, osmosis_replication_sequence_number + if(bp[1]!=0x02) goto h_unknown; + bp+= 2; + pbf_uint64(&bp); // (ignore this element) + break; + case 0x92: // 0x02 S 34, osmosis_replication_base_url + if(bp[1]!=0x02) goto h_unknown; + bp+= 2; + l= pbf_uint32(&bp); + bp+= l; // (ignore this element) + break; + default: + h_unknown: + WARNv("header block element type unknown: " + "0x%02X 0x%02X.",bp[0],bp[1]) + if(pbf_jump(&bp)) + END(-45) + } // end first byte of element + if(bp>zbufe) + ENDE(-46,"header block format length.") + } // end for every element in this loop + if(!osmschema) + ENDE(-47,"expected feature: OsmSchema-V0.6") + if(!densenodes) + ENDE(-48,"expected feature: DenseNodes") + zbufp= bp; + pb_bbvalid= bboxflags==0x0f; + END(8) + } // header block + // here: data block + // provide primitive groups + /* process data block */ { + byte* bp; + uint l; + static byte* bstre; // end of string table in zbuf[] + + bp= zbufp; + pb__stre= pb__str; + while(bpzbufe) + ENDEv(-101,"string table too large: %u",l) + bstre= bp+l; + while(bpbstre) // string too large + ENDEv(-102,"string too large: %u",l) + if(pb__stre>=pb__stree) + ENDEv(-103,"too many strings: %i",pb__strM) + *pb__stre++= (char*)bp; + bp+= l; + } // end S 1, string + else { // element type unknown + byte* p; + + WARNv("string table element type unknown: " + "0x%02X 0x%02X.",bp[0],bp[1]) + p= bp; + if(pbf_jump(&bp)) + END(-104) + *p= 0; // set null terminator for previous string + } // end element type unknown + } // end for each element in string table + pb__strm= pb__stre-pb__str; + bp= bstre; + break; + case 0x12: // S 2, primitive group + *bp++= 0; // set null terminator for previous string + l= pbf_uint32(&bp); + if(bp+l>zbufe) + ENDEv(-111,"primitive group too large: %u",l) + groupp= bp; + groupe= bp+l; + zbufp= groupe; + /**/goto mainloop; // we got a new primitive group + case 0x88: // 0x01 V 17, nanodegrees + if(bp[1]!=0x01) goto d_unknown; + bp+= 2; + l= pbf_uint32(&bp); + if(l!=global_pbfgranularity) { + if(l>100) + ENDEv(-120,"please specify: " + "--pbf-granularity=%u",l) + else if(l==100) + ENDE(-120,"please do not specify " + "--pbf-granularity") + else + ENDEv(-121,"granularity %u must be >=100.",l) + } + break; + case 0x90: // 0x01 V 18, millisec + if(bp[1]!=0x01) goto d_unknown; + bp+= 2; + l= pbf_uint32(&bp); + if(l!=1000) + ENDEv(-122,"node milliseconds must be 1000: %u",l) + break; + case 0x98: // 0x01 V 19, latitude offset + if(bp[1]!=0x01) goto d_unknown; + bp+= 2; + if(pbf_sint64(&bp)!=0) + ENDE(-123,"cannot process latitude offsets.") + break; + case 0xa0: // 0x01 V 20, longitude offset + if(bp[1]!=0x01) goto d_unknown; + bp+= 2; + if(pbf_sint64(&bp)!=0) + ENDE(-124,"cannot process longitude offsets.") + break; + d_unknown: + default: + /* block */ { + byte* p; + WARNv("data block element type unknown: " + "0x%02X 0x%02X.",bp[0],bp[1]) + p= bp; + if(pbf_jump(&bp)) + END(-125) + *p= 0; // set null terminator for previous string + } // end block + } // end first byte of element + if(bp>zbufe) + ENDE(-129,"data block format length.") + } // end for every element in this loop + } // process data block + } // zbuf data left +//// provide new zbuf data + /* get new zbuf data */ { + int datasize; // -1: expected; + int rawsize; // -1: expected; + int zdata; // -1: expected; + // 1: encountered section with compressed data + uint l; + byte* p; + int r; + + // initialization + blocktype= datasize= rawsize= zdata= -1; + read_setjump(); + + // care for new input data + if(read_bufp>read_bufe) + ENDE(-11,"main format length.") + read_input(); // get at least maximum block size + if(read_bufp>=read_bufe) // at end of input file + END(-1) + if(read_bufp[0]!=0) // unknown id at outermost level + ENDEv(-12,"main-element type unknown: " + "0x%02X 0x%02X.",read_bufp[0],read_bufp[1]) + if(read_bufp[1]!=0 || read_bufp[2]!=0 || + read_bufp[3]<11 || read_bufp[3]>17) + ENDEv(-13,"format blob header %i.", + read_bufp[1]*65536+read_bufp[2]*256+read_bufp[3]) + read_bufp+= 4; + + // read new block header + for(;;) { // read new block + if(blocktype<0) { // block type expected + if(read_bufp[0]!=0x0a) // not id S 1 + ENDEv(-21,"block type expected at: 0x%02X.",*read_bufp) + read_bufp++; + if(memcmp(read_bufp,"\x09OSMHeader",10)==0) { + blocktype= 1; + read_bufp+= 10; + continue; + } + if(memcmp(read_bufp,"\x07OSMData",8)==0) { + blocktype= 2; + read_bufp+= 8; + continue; + } + blocktype= 0; + l= pbf_uint32(&read_bufp); + if(read_bufp+l>read_bufe) // string too long + ENDEv(-22,"block type too long: %.40s",read_bufp) + WARNv("block type unknown: %.40s",read_bufp) + read_bufp+= l; + continue; + } // end block type expected + if(datasize<0) { // data size expected + if(read_bufp[0]!=0x18) // not id V 3 + ENDEv(-23,"block data size " + "expected at: 0x%02X.",*read_bufp) + read_bufp++; + datasize= pbf_uint32(&read_bufp); + } // end data size expected + if(blocktype==0) { // block type unknown + read_bufp+= datasize; // jump over this block + continue; + } // end block type unknown + if(rawsize<0) { // raw size expected + if(read_bufp[0]!=0x10) // not id V 2 + ENDEv(-24,"block raw size " + "expected at: 0x%02X.",*read_bufp) + p= read_bufp; + read_bufp++; + rawsize= pbf_uint32(&read_bufp); + datasize-= read_bufp-p; + } // end raw size expected + if(zdata<0) { // compressed data expected + if(read_bufp[0]!=0x1a) // not id S 3 + ENDEv(-25,"compressed data " + "expected at: 0x%02X.",*read_bufp) + p= read_bufp; + read_bufp++; + l= pbf_uint32(&read_bufp); + datasize-= read_bufp-p; + if(datasize<0 || datasize>pb__blockM || + read_bufp+datasize>read_bufe) { + PERRv("block data size too large: %i",datasize) + fprintf(stderr,"Pointers: %p %p %p\n", + read__buf,read_bufp,read_bufe); + END(-26) + } + if(l!=datasize) + ENDEv(-31,"compressed length: %i->%u.",datasize,l) + // uncompress + r= pb__decompress(read_bufp,l,zbuf,sizeof(zbuf),&l); + if(r!=0) + ENDEv(-32,"decompression failed: %i.",r) + if(l!=rawsize) + ENDEv(-33,"uncompressed length: %i->%u.",rawsize,l) + zdata= 1; + zbufp= zbuf; zbufe= zbuf+rawsize; + pb__stre= pb__str; + read_bufp+= datasize; + break; + } // end compressed data expected + if(read_bufp[0]==0) // possibly a new block start + break; + } // end read new block + if(zbufp=pb__strm || val>=pb__strm) { + PERRv("node key string index overflow: %u,%u>=%u", + key,val,pb__strm) +return 0; + } + if(++n<=keyvalmax) { // still space in output list + *keyp++= pb__str[key]; + *valp++= pb__str[val]; + } // still space in output list + key= pbf_uint32(&pb__nodetags); + } while(key!=0); // end for every key/val pair + } // end there are key/val pairs for this node + } // node + else if(pb_type==1) { // way + while(pb__waykey=pb__strm || val>=pb__strm) { + PERRv("way key string index overflow: %u,%u>=%i", + key,val,pb__strm) +return 0; + } + if(++n<=keyvalmax) { // still space in output list + *keyp++= pb__str[key]; + *valp++= pb__str[val]; + } // still space in output list + } // end there are still key/val pairs for this way + } // way + else if(pb_type==2) { // relation + while(pb__relkey=pb__strm || val>=pb__strm) { + PERRv("rel key string index overflow: %u,%u>=%i", + key,val,pb__strm) +return 0; + } + if(++n<=keyvalmax) { // still space in output list + *keyp++= pb__str[key]; + *valp++= pb__str[val]; + } // still space in output list + } // end there are still refs for this relation + } // relation + if(n>keyvalmax) { + WARNv("too many key/val pairs for %s: %i>%i", + ONAME(pb_id),n,keyvalmax) + n= keyvalmax; + } + return n; + } // end pb_keyval() + +static int pb_noderef(int64_t* refidp,int refmax) { + // read node references of an osm .pbf way object; + // refidp: start addresses of lists in which the node reference's + // ids will be stored in; + // refmax: maximum number of node references which can be stored + // in the lists; + // return: number of node references which have been read; + // this procedure should be called after OSM way data have + // been provided by pb_input(); + // repetitive calls are not allowed because they would result + // in wrong noderef data; + int64_t noderef; + int n; + + n= 0; + noderef= 0; + while(pb__waynoderefmax) { + WARNv("too many noderefs for %s: %i>%i",ONAME(pb_id),n,refmax) + n= refmax; + } + return n; + } // end pb_noderef() + +static int pb_ref(int64_t* refidp, + byte* reftypep,char** refrolep,int refmax) { + // read references of an osm .pbf object; + // refidp: start addresses of lists in which the reference's + // ids will be stored in; + // reftypep: same for their types; + // refrolep: same for their roles; + // refmax: maximum number of references which can be stored + // in the lists; + // return: number of references which have been read; + // this procedure should be called after OSM relation data have + // been provided by pb_input(); + // repetitive calls are not allowed because they would result + // in wrong ref data; + int64_t refid; + int n; + + n= 0; + refid= 0; + while(pb__relrefid=pb__strm) { + PERRv("rel refrole string index overflow: %u>=%u", + refrole,pb__strm) +return 0; + } + if(++n<=refmax) { // still space in output list + *refidp++= refid; + *reftypep++= reftype; + *refrolep++= pb__str[refrole]; + } // still space in output list + } // end there are still refs for this relation + if(n>refmax) { + WARNv("too many relrefs for %s: %i>%i",ONAME(pb_id),n,refmax) + n= refmax; + } + return n; + } // end pb_ref() + +//------------------------------------------------------------ +// end Module pb_ pbf read module +//------------------------------------------------------------ + + + +//------------------------------------------------------------ +// Module pstw_ pbf string write module +//------------------------------------------------------------ + +// this module provides procedures for collecting c-formatted +// strings while eliminating string doublets; +// this is needed to create Blobs for writing data in .pbf format; +// as usual, all identifiers of a module have the same prefix, +// in this case 'pstw'; an underline will follow in case of a +// global accessible object, two underlines in case of objects +// which are not meant to be accessed from outside this module; +// the sections of private and public definitions are separated +// by a horizontal line: ---- + +// string processing +// we need a string table to collect every string of a Blob; +// the data entities do not contain stings, they just refer to +// the strings in the string table; hence string doublets need +// not to be stored physically; + +// how this is done +// +// there is a string memory; the pointer pstw__mem poits to the start +// of this memory area; into this area every string is written, each +// starting with 0x0a and the string length in pbf unsigned Varint +// format; +// +// there is a string table which contains pointers to the start of each +// string in the string memory area; +// +// there is a hash table which accelerates access to the string table; + +#define kilobytes *1000 // unit "kilo" +#define Kibibytes *1024 // unit "Kibi" +#define Megabytes *1000000 // unit "Mega" +#define Mibibytes *1048576 // unit "Mibi" +#define pstw__memM (30 Megabytes) + // maximum number of bytes in the string memory +#define pstw__tabM (1500000) + // maximum number of strings in the table +#define pstw__hashtabM 25000009 // (preferably a prime number) + // --> 150001, 1500007, 5000011, 10000019, 15000017, + // 20000003, 25000009, 30000049, 40000003, 50000017 +static char* pstw__mem= NULL; // pointer to the string memory +static char* pstw__meme= NULL, *pstw__memee= NULL; // pointers to + // the logical end and to the physical end of string memory +typedef struct pstw__tab_struct { + int index; // index of this string table element; + int len; // length of the string contents + char* mem0; // pointer to the string's header in string memory area, + // i.e., the byte 0x0a and the string's length in Varint format; + char* mem; // pointer to the string contents in string memory area + int frequency; // number of occurrences of this string + int hash; + // hash value of this element, used as a backlink to the hash table; + struct pstw__tab_struct* next; + // for chaining of string table rows which match + // the same hash value; the last element will point to NULL; + } pstw__tab_t; +static pstw__tab_t pstw__tab[pstw__tabM]; // string table +static int pstw__tabn= 0; // number of entries in string table +static pstw__tab_t* pstw__hashtab[pstw__hashtabM]; + // hash table; elements point to matching strings in pstw__tab[]; + // []==NULL: no matching element to this certain hash value; + +static inline uint32_t pstw__hash(const char* str,int* hash) { + // get hash value of a string; + // str[]: string from whose contents the hash is to be retrieved; + // return: length of the string; + // *hash: hash value in the range 0..(pstw__hashtabM-1); + uint32_t c,h; + const char* s; + + s= str; + h= 0; + for(;;) { + if((c= *s++)==0) break; h+= c; + if((c= *s++)==0) break; h+= c<<8; + if((c= *s++)==0) break; h+= c<<16; + if((c= *s++)==0) break; h+= c<<24; + if((c= *s++)==0) break; h+= c<<4; + if((c= *s++)==0) break; h+= c<<12; + if((c= *s++)==0) break; h+= c<<20; + } + *hash= h % pstw__hashtabM; + return (uint32_t)(s-str-1); + } // end pstw__hash() + +static inline pstw__tab_t* pstw__getref( + pstw__tab_t* tabp,const char* s) { + // get the string table reference of a string; + // tabp: presumed index in string table (we got it from hash table); + // must be >=0 and mem; + len= tabp->len; + sp= s; + while(*sp!=0 && len>0 && *sp==*tp) { len--; sp++; tp++; } + if(*sp==0 && len==0) // string identical to string in table + break; + tabp= tabp->next; + } while(tabp!=NULL); + return tabp; + } // end pstw__getref() + +static void pstw__end() { + // clean-up string processing; + if(pstw__mem!=NULL) { + free(pstw__mem); + pstw__mem= pstw__meme= pstw__memee= NULL; + } + } // end pstw__end() + +//------------------------------------------------------------ + +static int pstw_ini() { + // initialize this module; + // must be called before any other procedure is called; + // return: 0: everything went ok; + // !=0: an error occurred; + static bool firstrun= true; + + if(firstrun) { + firstrun= false; + pstw__mem= (char*)malloc(pstw__memM); + if(pstw__mem==NULL) +return 1; + atexit(pstw__end); + pstw__memee= pstw__mem+pstw__memM; + pstw__meme= pstw__mem; + } + return 0; + } // end pstw_ini() + +static inline void pstw_reset() { + // clear string table and string hash table; + // must be called before the first string is stored; + memset(pstw__hashtab,0,sizeof(pstw__hashtab)); + pstw__meme= pstw__mem; + + // write string information of zero-string into string table + pstw__tab->index= 0; + pstw__tab->len= 0; + pstw__tab->frequency= 0; + pstw__tab->next= NULL; + pstw__tab->hash= 0; + + // write zero-string into string information memory area + pstw__tab->mem0= pstw__meme; + *pstw__meme++= 0x0a; // write string header into string memory + *pstw__meme++= 0; // write string length + pstw__tab->mem= pstw__meme; + + pstw__tabn= 1; // start with index 1 + } // end pstw_reset() + +static inline int pstw_store(const char* s) { + // store a string into string memory and return the string's index; + // if an identical string has already been stored, omit writing, + // just return the index of the stored string; + // s[]: string to write; + // return: index of the string in string memory; + // <0: string could not be written (e.g. not enough memory); + uint32_t sl; // length of the string + int h; // hash value + pstw__tab_t* tabp; + + sl= pstw__hash(s,&h); + tabp= pstw__hashtab[h]; + if(tabp!=NULL) // string presumably stored already + tabp= pstw__getref(tabp,s); // get the right one + // (if there are more than one with the same hash value) + if(tabp!=NULL) { // we found the right string in the table + tabp->frequency++; // mark that the string has (another) duplicate +return tabp->index; + } + // here: there is no matching string in the table + + // check for string table overflow + if(pstw__tabn>=pstw__tabM) { // no entry left in string table + PERR("PBF write: string table overflow.") +return -1; + } + if(sl+10>(pstw__memee-pstw__meme)) { + // not enough memory left in string memory area + PERR("PBF write: string memory overflow.") +return -2; + } + + // write string information into string table + tabp= pstw__tab+pstw__tabn; + tabp->index= pstw__tabn++; + tabp->len= sl; + tabp->frequency= 1; + + // update hash table references accordingly + tabp->next= pstw__hashtab[h]; + pstw__hashtab[h]= tabp; // link the new element to hash table + tabp->hash= h; // back-link to hash table element + + // write string into string information memory area + tabp->mem0= pstw__meme; + *pstw__meme++= 0x0a; // write string header into string memory + /* write the string length into string memory */ { + uint32_t v,frac; + + v= sl; + frac= v&0x7f; + while(frac!=v) { + *pstw__meme++= frac|0x80; + v>>= 7; + frac= v&0x7f; + } + *pstw__meme++= frac; + } // write the string length into string memory + tabp->mem= pstw__meme; + strcpy(pstw__meme,s); // write string into string memory + pstw__meme+= sl; + return tabp->index; + } // end pstw_store() + +#if 1 +static inline void pstw_write(byte** bufpp) { + // write the string table in PBF format; + // *bufpp: start address where to write the string table; + // return: + // *bufpp: address of the end of the written string table; + size_t size; + + if(pstw__tabn==0) // not a single string in memory +return; + size= pstw__meme-pstw__mem; + memcpy(*bufpp,pstw__mem,size); + *bufpp+= size; + } // end pstw_write() + +#else +// remark: +// in the present program structure the bare sorting of the +// string table will lead to false output because string indexes +// in data fields are not updated accordingly; +// there would be an easy way to accomplish this for dense nodes, +// but I don't know if it's worth the effort in the first place; + +static int pstw__qsort_write(const void* a,const void* b) { + // string occurrences comparison for qsort() in pstw_write() + int ax,bx; + + ax= ((pstw__tab_t**)a)->frequency; + bx= ((pstw__tab_t**)b)->frequency; + if(ax>bx) +return 1; + if(ax==bx) +return 0; + return -1; + } // end pstw__qsort_write() + +static inline int pstw_write(byte** bufpp) { + // write the string table in PBF format; + // *bufpp: start address where to write the string table; + // return: number of bytes written; + // *bufpp: address of the end of the written string table; + // not used at present: + // before the string table is written, it has to be ordered by + // the number of occurrences of the strings; the most frequently + // used strings must be written first; + pstw__tab_t* tabp,*taborder[pstw__tabM],**taborderp; + int i; + byte* bufp; + int l; + + if(pstw__tabn==0) // not a single string in memory +return; + + // sort the string table, using an index list + taborderp= taborder; + tabp= pstw__tab; + for(i= 0; imem-tabp->mem0)+tabp->len; + memcpy(bufp,tabp->mem0,l); + bufp+= l; + } // for every string in string table + l= bufp-*bufpp; + *bufpp= bufp; + return l; + } // end pstw_write() +#endif + +static inline int pstw_queryspace() { + // query how much memory space is presently used by the strings; + // this is useful before calling pstw_write(); + return (int)(pstw__meme-pstw__mem); + } // end pstw_queryspace() + +//------------------------------------------------------------ +// end Module pstw_ pbf string write module +//------------------------------------------------------------ + + + +//------------------------------------------------------------ +// Module pw_ PBF write module +//------------------------------------------------------------ + +// this module provides procedures which write .pbf objects; +// it uses procedures from module write_; +// as usual, all identifiers of a module have the same prefix, +// in this case 'pw'; an underline will follow in case of a +// global accessible object, two underlines in case of objects +// which are not meant to be accessed from outside this module; +// the sections of private and public definitions are separated +// by a horizontal line: ---- + +static int pw__compress(byte* ibuf,uint isiz,byte* obuf,uint osizm, + uint* osizp) { + // compress a block of data; + // return: 0: compression was successful; + // !=0: error number from zlib; + // *osizp: size of compressed data; + z_stream strm; + int r,i; + + // initialization + strm.zalloc= Z_NULL; + strm.zfree= Z_NULL; + strm.opaque= Z_NULL; + strm.next_in= Z_NULL; + strm.total_in= 0; + strm.avail_out= 0; + strm.next_out= Z_NULL; + strm.total_out= 0; + strm.msg= NULL; + r= deflateInit(&strm,Z_DEFAULT_COMPRESSION); + if(r!=Z_OK) +return r; + + // read data + strm.next_in = ibuf; + strm.avail_in= isiz; + + // compress + strm.next_out= obuf; + strm.avail_out= osizm; + r= deflate(&strm,Z_FINISH); + if(/*r!=Z_OK &&*/ r!=Z_STREAM_END) { + deflateEnd(&strm); + *osizp= 0; + if(r==0) r= 1000; +return r; + } + + // clean-up + deflateEnd(&strm); + obuf+= *osizp= osizm-(i= strm.avail_out); + + // add some zero bytes + if(i>4) i= 4; + while(--i>=0) *obuf++= 0; + return 0; + } // end pw__compress() + +// format description: BlobHeader must be less than 64 kilobytes; +// uncompressed length of a Blob must be less than 32 megabytes; + +#define pw__compress_bufM (UINT64_C(35) Megabytes) +static byte* pw__compress_buf= NULL; // buffer for compressed objects + +#define pw__bufM (UINT64_C(186) Megabytes) +static byte* pw__buf= NULL; // buffer for objects in .pbf format +static byte* pw__bufe= NULL; // logical end of the buffer +static byte* pw__bufee= NULL; // physical end of the buffer + +typedef struct pw__obj_struct { // type of a pbf hierarchy object + //struct pw__obj_struct parent; // parent object; ==NULL: none; + byte* buf; // start address of pbf buffer for this hierarchy object; + // this is where the header starts too; + int headerlen; // usually .bufl-.buf; + byte* bufl; // start address of object's length + byte* bufc; // start address of object's contents + byte* bufe; // write pointer in the pbf buffer + byte* bufee; // end address of pbf buffer for this hierarchy object + } pw__obj_t; + +#define pw__objM 20 +static pw__obj_t pw__obj[pw__objM]; +static pw__obj_t* pw__obje= pw__obj; + // logical end of the object hierarchy array +static pw__obj_t *pw__objee= pw__obj+pw__objM; + // physical end of the object hierarchy array +static pw__obj_t* pw__objp= NULL; // currently active hierarchy object + +static inline pw__obj_t* pw__obj_open(const char* header) { + // open a new hierarchy level + // header[20]: header which is to be written prior to the + // contents length; zero-terminated; + pw__obj_t* op; + + if(pw__obje==pw__obj) { // first hierarchy object + pw__bufe= pw__buf; + //pw__obje->parent= NULL; + pw__obje->buf= pw__bufe; + } + else { // not the first hierarchy object + if(pw__obje>=pw__objee) { // no space left in hierarchy array + PERR("PBF write: hierarchy overflow.") +return pw__objp; + } + op= pw__obje-1; + if(op->bufee==pw__bufee) { // object is not a limited one + pw__obje->buf= op->bufe; + } + else // object is a limited one + pw__obje->buf= op->bufee; + if(pw__obje->buf+50>pw__bufee) { // no space left PBF object buffer + PERR("PBF write: object buffer overflow.") +return pw__objp; + } + } // not the first hierarchy object + pw__objp= pw__obje++; + // write PBF object's header and pointers + pw__objp->bufl= (byte*)stpmcpy((char*)pw__objp->buf,header,20); + pw__objp->headerlen= (int)(pw__objp->bufl-pw__objp->buf); + pw__objp->bufc= pw__objp->bufl+10; + pw__objp->bufe= pw__objp->bufc; + pw__objp->bufee= pw__bufee; + return pw__objp; + } // pw__obj_open() + +static inline void pw__obj_limit(int size) { + // limit the maximum size of an PBF hierarchy object; + // this is necessary if two or more PBF objects shall be written + // simultaneously, e.g. when writing dense nodes; + + if(size>pw__objp->bufee-pw__objp->bufc-50) { + PERRv("PBF write: object buffer limit too large: %i>%i.", + size,(int)(pw__objp->bufee-pw__objp->bufc-50)) +return; + } + pw__objp->bufee= pw__objp->bufc+size; + } // pw__obj_limit() + +static inline void pw__obj_limit_parent(pw__obj_t* parent) { + // limit the size of a PBF hierarchy parent object to the + // sum of the maximum sizes of its children; + // parent: must point to the parent object; + // pw__objp: must point to the last child of the parent; + parent->bufee= pw__objp->bufee; + } // pw__obj_limit_parent() + +static inline void pw__obj_compress() { + // compress the contents of the current PBF hierarchy object; + // pw__objp: pointer to current object; + int r; + unsigned int osiz; // size of the compressed contents + + r= pw__compress(pw__objp->bufc,pw__objp->bufe-pw__objp->bufc, + pw__compress_buf,pw__compress_bufM,&osiz); + if(r!=0) { // an error has occurred + PERRv("PBF write: compression error %i.",r) +return; + } + if(osiz>pw__objp->bufee-pw__objp->bufc) { + PERRv("PBF write: compressed contents too large: %i>%i.", + osiz,(int)(pw__objp->bufee-pw__objp->bufc)) +return; + } + memcpy(pw__objp->bufc,pw__compress_buf,osiz); + pw__objp->bufe= pw__objp->bufc+osiz; + } // pw__obj_compress() + +static inline void pw__obj_add_id(uint8_t pbfid) { + // append a one-byte PBF id to PBF write buffer; + // pbfid: PBF id; + // pw__objp->bufe: write buffer position (will be + // incremented by this procedure); + if(pw__objp->bufe>=pw__objp->bufee) { + PERR("PBF write: id memory overflow.") +return; + } + *pw__objp->bufe++= pbfid; + } // pw__obj_add_id() + +static inline void pw__obj_add_id2(uint16_t pbfid) { + // append a two-byte PBF id to PBF write buffer; + // pbfid: PBF id, high byte is stored first; + // pw__objp->bufe: write buffer position (will be + // incremented by 2 by this procedure); + if(pw__objp->bufe+2>pw__objp->bufee) { + PERR("PBF write: id2 memory overflow.") +return; + } + *pw__objp->bufe++= (byte)(pbfid>>8); + *pw__objp->bufe++= (byte)(pbfid&0xff); + } // pw__obj_add_id2() + +static inline void pw__obj_add_uint32(uint32_t v) { + // append a numeric value to PBF write buffer; + // pw__objp->bufe: write buffer position + // (will be updated by this procedure); + uint32_t frac; + + if(pw__objp->bufe+10>pw__objp->bufee) { + PERR("PBF write: uint32 memory overflow.") +return; + } + frac= v&0x7f; + while(frac!=v) { + *pw__objp->bufe++= frac|0x80; + v>>= 7; + frac= v&0x7f; + } + *pw__objp->bufe++= frac; + } // pw__obj_add_uint32() + +static inline void pw__obj_add_sint32(int32_t v) { + // append a numeric value to PBF write buffer; + // pw__objp->bufe: write buffer position + // (will be updated by this procedure); + uint32_t u; + uint32_t frac; + + if(pw__objp->bufe+10>pw__objp->bufee) { + PERR("PBF write: sint32 memory overflow.") +return; + } + if(v<0) { + u= -v; + u= (u<<1)-1; + } + else + u= v<<1; + frac= u&0x7f; + while(frac!=u) { + *pw__objp->bufe++= frac|0x80; + u>>= 7; + frac= u&0x7f; + } + *pw__objp->bufe++= frac; + } // pw__obj_add_sint32() + +static inline void pw__obj_add_uint64(uint64_t v) { + // append a numeric value to PBF write buffer; + // pw__objp->bufe: write buffer position + // (will be updated by this procedure); + uint32_t frac; + + if(pw__objp->bufe+10>pw__objp->bufee) { + PERR("PBF write: uint64 memory overflow.") +return; + } + frac= v&0x7f; + while(frac!=v) { + *pw__objp->bufe++= frac|0x80; + v>>= 7; + frac= v&0x7f; + } + *pw__objp->bufe++= frac; + } // pw__obj_add_uint64() + +static inline void pw__obj_add_sint64(int64_t v) { + // append a numeric value to PBF write buffer; + // pw__objp->bufe: write buffer position + // (will be updated by this procedure); + uint64_t u; + uint32_t frac; + + if(pw__objp->bufe+10>pw__objp->bufee) { + PERR("PBF write: sint64 memory overflow.") +return; + } + if(v<0) { + u= -v; + u= (u<<1)-1; + } + else + u= v<<1; + frac= u&0x7f; + while(frac!=u) { + *pw__objp->bufe++= frac|0x80; + u>>= 7; + frac= u&0x7f; + } + *pw__objp->bufe++= frac; + } // pw__obj_add_sint64() + +#if 0 // not used at present +static inline void pw__obj_add_mem(byte* s,uint32_t sl) { + // append data to PBF write buffer; + // s[]: data which are to append; + // ls: length of the data; + // pw__objp->bufe: write buffer position + // (will be updated by this procedure); + + if(pw__objp->bufe+sl>pw__objp->bufee) { + PERR("PBF write: mem memory overflow.") +return; + } + memcpy(pw__objp->bufe,s,sl); + pw__objp->bufe+= sl; + } // pw__obj_add_mem() +#endif + +static inline void pw__obj_add_str(const char* s) { + // append a PBF string to PBF write buffer; + // pw__objp->bufe: write buffer position + // (will be updated by this procedure); + uint32_t sl; // length of the string + + sl= strlen(s); + if(pw__objp->bufe+10+sl>pw__objp->bufee) { + PERR("PBF write: string memory overflow.") +return; + } + /* write the string length into PBF write buffer */ { + uint32_t v,frac; + + v= sl; + frac= v&0x7f; + while(frac!=v) { + *pw__objp->bufe++= frac|0x80; + v>>= 7; + frac= v&0x7f; + } + *pw__objp->bufe++= frac; + } // write the string length into PBF write buffer + memcpy(pw__objp->bufe,s,sl); + pw__objp->bufe+= sl; + } // pw__obj_add_str() + +static inline void pw__obj_close() { + // close an object which had been opened with pw__obj_open(); + // pw__objp: pointer to the object which is to close; + // return: + // pw__objp: points to the last opened object; + pw__obj_t* op; + int i; + byte* bp; + uint32_t len; + uint32_t v,frac; + + if(pw__objp==pw__obj) { // this is the anchor object + // write the object's data to standard output + write_mem(pw__objp->buf,pw__objp->headerlen); // write header + write_mem(pw__objp->bufc,(int)(pw__objp->bufe-pw__objp->bufc)); + // write contents + // delete hierarchy object + pw__objp= NULL; + pw__obje= pw__obj; +return; + } + + // determine the parent object + op= pw__objp; + for(;;) { // search for the parent object + if(op<=pw__obj) { // there is no parent object + PERR("PBF write: no parent object.") +return; + } + op--; + if(op->buf!=NULL) // found our parent object + break; + } + + // write PBF object's header into parent object + bp= pw__objp->buf; + i= pw__objp->headerlen; + while(--i>=0) + *op->bufe++= *bp++; + + // write PBF object's length into parent object + len= v= pw__objp->bufe-pw__objp->bufc; + frac= v&0x7f; + while(frac!=v) { + *op->bufe++= frac|0x80; + v>>= 7; + frac= v&0x7f; + } + *op->bufe++= frac; + + // write PBF object's contents into parent object + memmove(op->bufe,pw__objp->bufc,len); + op->bufe+= len; + + // mark this object as deleted + pw__objp->buf= NULL; + + // free the unused space in object hierarchy array + while(pw__obje>pw__obj && pw__obje[-1].buf==NULL) pw__obje--; + pw__objp= pw__obje-1; + } // pw__obj_close() + +static inline void pw__obj_dispose() { + // dispose an object which had been opened with pw__obj_open(); + // pw__objp: pointer to the object which is to close; + // return: + // pw__objp: points to the last opened object; + if(pw__objp==pw__obj) { // this is the anchor object + // delete hierarchy object + pw__objp= NULL; + pw__obje= pw__obj; +return; + } + + // mark this object as deleted + pw__objp->buf= NULL; + + // free the unused space in object hierarchy array + while(pw__obje>pw__obj && pw__obje[-1].buf==NULL) pw__obje--; + pw__objp= pw__obje-1; + } // pw__obj_dispose() + +static pw__obj_t* pw__st= NULL,*pw__dn_id= NULL,*pw__dn_his, + *pw__dn_hisver= NULL,*pw__dn_histime= NULL,*pw__dn_hiscset= NULL, + *pw__dn_hisuid= NULL,*pw__dn_hisuser= NULL, + *pw__dn_lat= NULL,*pw__dn_lon= NULL,*pw__dn_keysvals= NULL; + +// some variables for delta coding +static int64_t pw__dc_id= 0; +static int32_t pw__dc_lon= 0,pw__dc_lat= 0; +static int64_t pw__dc_histime= 0; +static int64_t pw__dc_hiscset= 0; +static uint32_t pw__dc_hisuid= 0; +static uint32_t pw__dc_hisuser= 0; +static int64_t pw__dc_noderef= 0; +static int64_t pw__dc_ref= 0; + +static void pw__data(int otype) { + // prepare or complete an 'OSMData fileblock'; + // should be called prior to writing each OSM object; + // otype: type of the OSM object which is going to be written; + // 0: node; 1: way; 2: relation; -1: none; + static int otype_old= -1; + static const int max_object_size= (250 kilobytes); + // assumed maximum size of one OSM object + #define pw__data_spaceM (31 Megabytes) + // maximum size of one 'fileblock' + static int used_space= pw__data_spaceM; + // presently used memory space in present 'OSMData fileblock', + // not including the strings + int string_space; // memory space used by strings + int remaining_space; + // remaining memory space in present 'OSMData fileblock' + int i; + + // determine remaining space in current 'OSMData fileblock'; + // the remaining space is usually guessed in a pessimistic manner; + // if this estimate shows too less space, then a more exact + // calculation is made; + // this strategy has been chosen for performance reasons; + used_space+= 64000; // increase used-space variable by the assumed + // maximum size of one OSM object, not including the strings + string_space= pstw_queryspace(); + remaining_space= pw__data_spaceM-used_space-string_space; + if(remaining_spacebufe-pw__dn_id->buf)+ + (pw__dn_lat->bufe-pw__dn_lat->buf)+ + (pw__dn_lon->bufe-pw__dn_lon->buf)+ + (pw__dn_keysvals->bufe-pw__dn_keysvals->buf)); + if(!global_dropversion) { + used_space+= (int)(pw__dn_hisver->bufe-pw__dn_hisver->buf); + if(!global_dropauthor) { + used_space+= (int)((pw__dn_histime->bufe-pw__dn_histime->buf)+ + (pw__dn_hiscset->bufe-pw__dn_hiscset->buf)+ + (pw__dn_hisuid->bufe-pw__dn_hisuid->buf)+ + (pw__dn_hisuser->bufe-pw__dn_hisuser->buf)); + } + } + } + else if(otype_old>0) // way or relation + used_space= (int)(pw__objp->bufe-pw__objp->buf); + remaining_space= pw__data_spaceM-used_space-string_space; + } // might be too less space + + // conclude or start an 'OSMData fileblock' + if(otype!=otype_old || remaining_space=0) { // there has been object processing + // complete current 'OSMData fileblock' + used_space= pw__data_spaceM; // force new calculation next time + i= pstw_queryspace(); + if(i>pw__st->bufee-pw__st->bufe) + PERR("PBF write: string table memory overflow.") + else + pstw_write(&pw__st->bufe); + pw__objp= pw__st; pw__obj_close(); // 'stringtable' + switch(otype_old) { // select by OSM object type + case 0: // node + pw__objp= pw__dn_id; pw__obj_close(); + if(!global_dropversion) { // version number is to be written + pw__objp= pw__dn_hisver; pw__obj_close(); + if(!global_dropauthor) { // author information is to be written + pw__objp= pw__dn_histime; pw__obj_close(); + pw__objp= pw__dn_hiscset; pw__obj_close(); + pw__objp= pw__dn_hisuid; pw__obj_close(); + pw__objp= pw__dn_hisuser; pw__obj_close(); + } // author information is to be written + pw__objp= pw__dn_his; pw__obj_close(); + } // version number is to be written + pw__objp= pw__dn_lat; pw__obj_close(); + pw__objp= pw__dn_lon; pw__obj_close(); + pw__objp= pw__dn_keysvals; pw__obj_close(); + pw__obj_close(); // 'dense' + break; + case 1: // way + break; + case 2: // relation + break; + } // select by OSM object type + pw__obj_close(); // 'primitivegroup' + /* write 'raw_size' into hierarchy object's header */ { + uint32_t v,frac; + byte* bp; + + v= pw__objp->bufe-pw__objp->bufc; + bp= pw__objp->buf+1; + frac= v&0x7f; + while(frac!=v) { + *bp++= frac|0x80; + v>>= 7; + frac= v&0x7f; + } + *bp++= frac; + *bp++= 0x1a; + pw__objp->headerlen= bp-pw__objp->buf; + } + pw__obj_compress(); + pw__obj_close(); // 'zlib_data' + pw__obj_close(); // 'datasize' + /* write 'length of BlobHeader message' into object's header */ { + byte* bp; + + bp= pw__objp->bufc+pw__objp->bufc[1]+3; + while((*bp & 0x80)!=0) bp++; + bp++; + pw__objp->buf[0]= pw__objp->buf[1]= pw__objp->buf[2]= 0; + pw__objp->buf[3]= bp-pw__objp->bufc; + } + pw__obj_close(); // 'Blobheader' + otype_old= -1; + } // there has been object processing + + // prepare new 'OSMData fileblock' if necessary + if(otype!=otype_old) { + pw__obj_open("----"); + // open anchor hierarchy object for 'OSMData fileblock' + // (every fileblock starts with four zero-bytes; + // the fourth zero-byte will be overwritten later + // by the length of the BlobHeader;) + pw__obj_add_id(0x0a); // S 1 'type' + pw__obj_add_str("OSMData"); + pw__obj_open("\x18"); // V 3 'datasize' + pw__obj_open("\x10----------\x1a"); // S 3 'zlib_data' + // in the header: V 2 'raw_size' + pw__st= pw__obj_open("\x0a"); // S 1 'stringtable' + pw__obj_limit(30 Megabytes); + pstw_reset(); + pw__obj_open("\x12"); // S 2 'primitivegroup' + switch(otype) { // select by OSM object type + case 0: // node + pw__obj_open("\x12"); // S 2 'dense' + pw__dn_id= pw__obj_open("\x0a"); // S 1 'id' + pw__obj_limit(10 Megabytes); + if(!global_dropversion) { // version number is to be written + pw__dn_his= pw__obj_open("\x2a"); // S 5 'his' + pw__dn_hisver= pw__obj_open("\x0a"); // S 1 'his.ver' + pw__obj_limit(10 Megabytes); + if(!global_dropauthor) { // author information is to be written + pw__dn_histime= pw__obj_open("\x12"); // S 2 'his.time' + pw__obj_limit(10 Megabytes); + pw__dn_hiscset= pw__obj_open("\x1a"); // S 3 'his.cset' + pw__obj_limit(10 Megabytes); + pw__dn_hisuid= pw__obj_open("\x22"); // S 4 'his.uid' + pw__obj_limit(8 Megabytes); + pw__dn_hisuser= pw__obj_open("\x2a"); // S 5 'his.user' + pw__obj_limit(6 Megabytes); + } // author information is to be written + pw__obj_limit_parent(pw__dn_his); + } // version number is to be written + pw__dn_lat= pw__obj_open("\x42"); // S 8 'lat' + pw__obj_limit(30 Megabytes); + pw__dn_lon= pw__obj_open("\x4a"); // S 9 'lon' + pw__obj_limit(30 Megabytes); + pw__dn_keysvals= pw__obj_open("\x52"); // S 10 'tags' + pw__obj_limit(40 Megabytes); + // reset variables for delta coding + pw__dc_id= 0; + pw__dc_lat= pw__dc_lon= 0; + pw__dc_histime= 0; + pw__dc_hiscset= 0; + pw__dc_hisuid= 0; + pw__dc_hisuser= 0; + break; + case 1: // way + break; + case 2: // relation + break; + } // select by OSM object type + otype_old= otype; + } // prepare new 'OSMData fileblock' if necessary + } // 'OSMData fileblock' must be concluded or started + } // pw__data() + +static void pw__end() { + // clean-up this module; + if(pw__obje!=pw__obj) + PERR("PBF write: object hierarchy still open.") + if(pw__buf!=NULL) { + free(pw__buf); + pw__buf= pw__bufe= pw__bufee= NULL; + } + pw__obje= pw__obj; + pw__objp= NULL; + if(pw__compress_buf!=NULL) { + free(pw__compress_buf); + pw__compress_buf= NULL; + } + } // end pw__end() + +//------------------------------------------------------------ + +static inline int pw_ini() { + // initialize this module; + // must be called before any other procedure is called; + // return: 0: everything went ok; + // !=0: an error occurred; + static bool firstrun= true; + int r; + + if(firstrun) { + firstrun= false; + atexit(pw__end); + pw__buf= (byte*)malloc(pw__bufM); + pw__bufe= pw__buf; + pw__bufee= pw__buf+pw__bufM; + pw__compress_buf= (byte*)malloc(pw__compress_bufM); + r= pstw_ini(); + if(pw__buf==NULL || pw__compress_buf==NULL || r!=0) { + PERR("PBF write: not enough memory.") +return 1; + } + } + return 0; + } // end pw_ini() + +static void pw_header(bool bboxvalid, + int32_t x1,int32_t y1,int32_t x2,int32_t y2,int64_t timestamp) { + // start writing PBF objects, i.e., write the 'OSMHeader fileblock'; + // bboxvalid: the following bbox coordinates are valid; + // x1,y1,x2,y2: bbox coordinates (base 10^-7); + // timestamp: file timestamp; ==0: no timestamp given; + pw__obj_open("----"); + // open anchor hierarchy object for 'OSMHeader fileblock' + // (every fileblock starts with four zero-bytes; + // the fourth zero-byte will be overwritten later + // by the length of the BlobHeader;) + pw__obj_add_id(0x0a); // S 1 'type' + pw__obj_add_str("OSMHeader"); + pw__obj_open("\x18"); // V 3 'datasize' + pw__obj_open("\x10----------\x1a"); // S 3 'zlib_data' + // in the header: V 2 'raw_size' + if(bboxvalid) { + pw__obj_open("\x0a"); // S 1 'bbox' + pw__obj_add_id(0x08); // V 1 'minlon' + pw__obj_add_sint64((int64_t)x1*100); + pw__obj_add_id(0x10); // V 2 'maxlon' + pw__obj_add_sint64((int64_t)x2*100); + pw__obj_add_id(0x18); // V 3 'maxlat' + pw__obj_add_sint64((int64_t)y2*100); + pw__obj_add_id(0x20); // V 4 'minlat' + pw__obj_add_sint64((int64_t)y1*100); + pw__obj_close(); + } + pw__obj_add_id(0x22); // S 4 'required_features' + pw__obj_add_str("OsmSchema-V0.6"); + pw__obj_add_id(0x22); // S 4 'required_features' + pw__obj_add_str("DenseNodes"); + pw__obj_add_id(0x2a); // S 5 'optional_features' + pw__obj_add_str("Sort.Type_then_ID"); + if(timestamp!=0) { // file timestamp given + char s[40],*sp; + + sp= stpcpy0(s,"timestamp="); + write_createtimestamp(timestamp,sp); + pw__obj_add_id(0x2a); // S 5 'optional_features' + pw__obj_add_str(s); + } // file timestamp given + pw__obj_add_id2(0x8201); // S 16 'writingprogram' + pw__obj_add_str("osmconvert "VERSION); + pw__obj_add_id2(0x8a01); // S 17 'source' + pw__obj_add_str("http://www.openstreetmap.org/api/0.6"); + if(timestamp!=0) { // file timestamp given + pw__obj_add_id2(0x8002); // V 32 osmosis_replication_timestamp + pw__obj_add_uint64(timestamp); + } // file timestamp given + /* write 'raw_size' into hierarchy object's header */ { + uint32_t v,frac; + byte* bp; + + v= pw__objp->bufe-pw__objp->bufc; + bp= pw__objp->buf+1; + frac= v&0x7f; + while(frac!=v) { + *bp++= frac|0x80; + v>>= 7; + frac= v&0x7f; + } + *bp++= frac; + *bp++= 0x1a; + pw__objp->headerlen= bp-pw__objp->buf; + } + pw__obj_compress(); + pw__obj_close(); // 'zlib_data' + pw__obj_close(); // 'datasize' + /* write 'length of BlobHeader message' into object's header */ { + byte* bp; + + bp= pw__objp->bufc+pw__objp->bufc[1]+3; + while((*bp & 0x80)!=0) bp++; + bp++; + pw__objp->buf[0]= pw__objp->buf[1]= pw__objp->buf[2]= 0; + pw__objp->buf[3]= bp-pw__objp->bufc; + } + pw__obj_close(); // 'Blobheader' + } // end pw_header() + +static inline void pw_foot() { + // end writing a PBF file; + pw__data(-1); + } // end pw_foot() + +static inline void pw_flush() { + // end writing a PBF dataset; + pw__data(-1); + } // end pw_flush() + +static inline void pw_node(int64_t id, + int32_t hisver,int64_t histime,int64_t hiscset, + uint32_t hisuid,const char* hisuser,int32_t lon,int32_t lat) { + // start writing a PBF dense node dataset; + // id: id of this object; + // hisver: version; 0: no author information is to be written + // histime: time (seconds since 1970) + // hiscset: changeset + // hisuid: uid + // hisuser: user name + // lon: latitude in 100 nanodegree; + // lat: latitude in 100 nanodegree; + int stid; // string id + + pw__data(0); + pw__objp= pw__dn_id; pw__obj_add_sint64(id-pw__dc_id); + pw__dc_id= id; + if(!global_dropversion) { // version number is to be written + if(hisver==0) hisver= 1; + pw__objp= pw__dn_hisver; pw__obj_add_uint32(hisver); + if(!global_dropauthor) { // author information is to be written + if(histime==0) { histime= 1; hiscset= 1; hisuser= 0; } + pw__objp= pw__dn_histime; + pw__obj_add_sint64(histime-pw__dc_histime); + pw__dc_histime= histime; + pw__objp= pw__dn_hiscset; + pw__obj_add_sint64(hiscset-pw__dc_hiscset); + pw__dc_hiscset= hiscset; + pw__objp= pw__dn_hisuid; + pw__obj_add_sint32(hisuid-pw__dc_hisuid); + pw__dc_hisuid= hisuid; + pw__objp= pw__dn_hisuser; + if(hisuid==0) hisuser= ""; + stid= pstw_store(hisuser); + pw__obj_add_sint32(stid-pw__dc_hisuser); + pw__dc_hisuser= stid; + } // author information is to be written + } // version number is to be written + pw__objp= pw__dn_lat; pw__obj_add_sint64(lat-pw__dc_lat); + pw__dc_lat= lat; + pw__objp= pw__dn_lon; + pw__obj_add_sint64((int64_t)lon-pw__dc_lon); + pw__dc_lon= lon; + } // end pw_node() + +static inline void pw_node_keyval(const char* key,const char* val) { + // write node object's keyval; + int stid; // string id + + pw__objp= pw__dn_keysvals; + stid= pstw_store(key); + pw__obj_add_uint32(stid); + stid= pstw_store(val); + pw__obj_add_uint32(stid); + } // end pw_node_keyval() + +static inline void pw_node_close() { + // close writing node object; + pw__objp= pw__dn_keysvals; + pw__obj_add_uint32(0); + } // end pw_node_close() + +static pw__obj_t* pw__wayrel_keys= NULL,*pw__wayrel_vals= NULL, + *pw__wayrel_his= NULL,*pw__way_noderefs= NULL, + *pw__rel_roles= NULL,*pw__rel_refids= NULL,*pw__rel_types= NULL; + +static inline void pw_way(int64_t id, + int32_t hisver,int64_t histime,int64_t hiscset, + uint32_t hisuid,const char* hisuser) { + // start writing a PBF way dataset; + // id: id of this object; + // hisver: version; 0: no author information is to be written; + // histime: time (seconds since 1970) + // hiscset: changeset + // hisuid: uid + // hisuser: user name; + int stid; // string id + + pw__data(1); + pw__obj_open("\x1a"); // S 3 'ways' + pw__obj_add_id(0x08); // V 1 'id' + pw__obj_add_uint64(id); + pw__wayrel_keys= pw__obj_open("\x12"); // S 2 'keys' + pw__obj_limit(20 Megabytes); + pw__wayrel_vals= pw__obj_open("\x1a"); // S 3 'vals' + pw__obj_limit(20 Megabytes); + pw__wayrel_his= pw__obj_open("\x22"); // S 4 'his' + pw__obj_limit(2000); + pw__way_noderefs= pw__obj_open("\x42"); // S 8 'noderefs' + pw__obj_limit(30 Megabytes); + if(!global_dropversion) { // version number is to be written + pw__objp= pw__wayrel_his; + if(hisver==0) hisver= 1; + pw__obj_add_id(0x08); // V 1 'hisver' + pw__obj_add_uint32(hisver); + if(!global_dropauthor) { // author information is to be written + if(histime==0) { + histime= 1; hiscset= 1; hisuser= 0; } + pw__obj_add_id(0x10); // V 2 'histime' + pw__obj_add_uint64(histime); + pw__obj_add_id(0x18); // V 3 'hiscset' + pw__obj_add_uint64(hiscset); + pw__obj_add_id(0x20); // V 4 'hisuid' + pw__obj_add_uint32(hisuid); + pw__obj_add_id(0x28); // V 5 'hisuser' + if(hisuid==0) hisuser= ""; + stid= pstw_store(hisuser); + pw__obj_add_uint32(stid); + } // author information is to be written + } // version number is to be written + pw__dc_noderef= 0; + } // end pw_way() + +static inline void pw_wayrel_keyval(const char* key,const char* val) { + // write a ways or a relations object's keyval; + int stid; // string id + + pw__objp= pw__wayrel_keys; + stid= pstw_store(key); + pw__obj_add_uint32(stid); + pw__objp= pw__wayrel_vals; + stid= pstw_store(val); + pw__obj_add_uint32(stid); + } // end pw_wayrel_keyval() + +static inline void pw_way_ref(int64_t noderef) { + // write a ways object's noderefs; + pw__objp= pw__way_noderefs; + pw__obj_add_sint64(noderef-pw__dc_noderef); + pw__dc_noderef= noderef; + } // end pw_way_ref() + +static inline void pw_way_close() { + // close writing way object; + pw__objp= pw__wayrel_keys; + if(pw__objp->bufe==pw__objp->bufc) // object is empty + pw__obj_dispose(); + else + pw__obj_close(); + pw__objp= pw__wayrel_vals; + if(pw__objp->bufe==pw__objp->bufc) // object is empty + pw__obj_dispose(); + else + pw__obj_close(); + pw__objp= pw__wayrel_his; + if(pw__objp->bufe==pw__objp->bufc) // object is empty + pw__obj_dispose(); + else + pw__obj_close(); + pw__objp= pw__way_noderefs; + if(pw__objp->bufe==pw__objp->bufc) // object is empty + pw__obj_dispose(); + else + pw__obj_close(); + pw__obj_close(); + } // end pw_way_close() + +static inline void pw_relation(int64_t id, + int32_t hisver,int64_t histime,int64_t hiscset, + uint32_t hisuid,const char* hisuser) { + // start writing a PBF way dataset; + // id: id of this object; + // hisver: version; 0: no author information is to be written; + // histime: time (seconds since 1970) + // hiscset: changeset + // hisuid: uid + // hisuser: user name; + int stid; // string id + + pw__data(2); + pw__obj_open("\x22"); // S 4 'relations' + pw__obj_add_id(0x08); // V 1 'id' + pw__obj_add_uint64(id); + pw__wayrel_keys= pw__obj_open("\x12"); // S 2 'keys' + pw__obj_limit(20 Megabytes); + pw__wayrel_vals= pw__obj_open("\x1a"); // S 3 'vals' + pw__obj_limit(20 Megabytes); + pw__wayrel_his= pw__obj_open("\x22"); // S 4 'his' + pw__obj_limit(2000); + pw__rel_roles= pw__obj_open("\x42"); // S 8 'role' + pw__obj_limit(20 Megabytes); + pw__rel_refids= pw__obj_open("\x4a"); // S 9 'refid' + pw__obj_limit(20 Megabytes); + pw__rel_types= pw__obj_open("\x52"); // S 10 'type' + pw__obj_limit(20 Megabytes); + if(!global_dropversion) { // version number is to be written + pw__objp= pw__wayrel_his; + if(hisver==0) hisver= 1; + pw__obj_add_id(0x08); // V 1 'hisver' + pw__obj_add_uint32(hisver); + if(!global_dropauthor) { // author information is to be written + if(histime==0) { + histime= 1; hiscset= 1; hisuser= 0; } + pw__obj_add_id(0x10); // V 2 'histime' + pw__obj_add_uint64(histime); + pw__obj_add_id(0x18); // V 3 'hiscset' + pw__obj_add_uint64(hiscset); + pw__obj_add_id(0x20); // V 4 'hisuid' + pw__obj_add_uint32(hisuid); + pw__obj_add_id(0x28); // V 5 'hisuser' + if(hisuid==0) hisuser= ""; + stid= pstw_store(hisuser); + pw__obj_add_uint32(stid); + } // author information is to be written + } // version number is to be written + pw__dc_ref= 0; + } // end pw_relation() + +static inline void pw_relation_ref(int64_t refid,int reftype, + const char* refrole) { + // write a relations object's refs + int stid; // string id + + pw__objp= pw__rel_roles; + stid= pstw_store(refrole); + pw__obj_add_uint32(stid); + pw__objp= pw__rel_refids; + pw__obj_add_sint64(refid-pw__dc_ref); + pw__dc_ref= refid; + pw__objp= pw__rel_types; + pw__obj_add_uint32(reftype); + } // end pw_relation_ref() + +static inline void pw_relation_close() { + // close writing relation object; + pw__objp= pw__wayrel_keys; + if(pw__objp->bufe==pw__objp->bufc) // object is empty + pw__obj_dispose(); + else + pw__obj_close(); + pw__objp= pw__wayrel_vals; + if(pw__objp->bufe==pw__objp->bufc) // object is empty + pw__obj_dispose(); + else + pw__obj_close(); + pw__objp= pw__wayrel_his; + if(pw__objp->bufe==pw__objp->bufc) // object is empty + pw__obj_dispose(); + else + pw__obj_close(); + pw__objp= pw__rel_roles; + if(pw__objp->bufe==pw__objp->bufc) // object is empty + pw__obj_dispose(); + else + pw__obj_close(); + pw__objp= pw__rel_refids; + if(pw__objp->bufe==pw__objp->bufc) // object is empty + pw__obj_dispose(); + else + pw__obj_close(); + pw__objp= pw__rel_types; + if(pw__objp->bufe==pw__objp->bufc) // object is empty + pw__obj_dispose(); + else + pw__obj_close(); + pw__obj_close(); + } // end pw_relation_close() + +//------------------------------------------------------------ +// end Module pw_ PBF write module +//------------------------------------------------------------ + + + +//------------------------------------------------------------ +// Module posi_ OSM position module +//------------------------------------------------------------ + +// this module provides a geocoordinate table for to store +// the coordinates of all OSM objects; +// the procedures posi_set() (resp. posi_setbbox()) and +// posi_get() allow access to this tables; +// as usual, all identifiers of a module have the same prefix, +// in this case 'posi'; an underline will follow for a global +// accessible identifier, two underlines if the identifier +// is not meant to be accessed from outside this module; +// the sections of private and public definitions are separated +// by a horizontal line: ---- + +struct posi__mem_struct { // element of position array + int64_t id; + int32_t data[]; + } __attribute__((__packed__)); + // (do not change this structure; the search algorithm expects + // the size of this structure to be 16 or 32 bytes) + // data[] stands for either + // int32_t x,y; + // or + // int32_t x,y,x1,y1,x2,y2; // (including bbox) + // remarks to .x: + // if you get posi_nil as value for x, you may assume that + // the object has been stored, but its geoposition is unknown; + // remarks to .id: + // the caller of posi_set() and posi_get() has to care about adding + // global_otypeoffset10 to the id if the object is a way and + // global_otypeoffset20 to the id if the object is a relation; +typedef struct posi__mem_struct posi__mem_t; +static posi__mem_t* posi__mem= NULL; // start address of position array +static posi__mem_t* posi__meme= NULL; // logical end address +static posi__mem_t* posi__memee= NULL; // physical end address + +static void posi__end() { + // clean-up for posi module; + // will be called at program's end; + if(posi__mem==NULL) + PERR("not enough memory. Reduce --max-objects=") + else { // was initialized + if(posi__meme>=posi__memee) // not enough space in position array + PERR("not enough space. Increase --max-objects=") + else { + int64_t siz; + + siz= (char*)posi__memee-(char*)posi__mem; + siz= siz/4*3; + if((char*)posi__meme-(char*)posi__mem>siz) + // low space in position array + WARN("low space. Try to increase --max-objects=") + } + free(posi__mem); + posi__mem= NULL; + } + } // end posi__end() + +//------------------------------------------------------------ + +static size_t posi__mem_size= 0; // size of structure +static size_t posi__mem_increment= 0; + // how many increments to ++ when allocating +static size_t posi__mem_mask= 0; + // bitmask to start at base of structure + +static int posi_ini() { + // initialize the posi module; + // return: 0: OK; 1: not enough memory; + int64_t siz; + + global_otypeoffset05= global_otypeoffset10/2; + global_otypeoffset15= global_otypeoffset10+global_otypeoffset05; + global_otypeoffset20= global_otypeoffset10*2; + if(global_otypeoffsetstep!=0) + global_otypeoffsetstep= global_otypeoffset10; + if(posi__mem!=NULL) // already initialized +return 0; + atexit(posi__end); // chain-in the clean-up procedure + // allocate memory for the positions array + if (global_calccoords<0) { + posi__mem_size = 32; + posi__mem_mask = ~0x1f; + posi__mem_increment = 4; + } + else { + posi__mem_size = 16; + posi__mem_mask = ~0x0f; + posi__mem_increment = 2; + } + siz= posi__mem_size*global_maxobjects; + posi__mem= (posi__mem_t*)malloc(siz); + if(posi__mem==NULL) // not enough memory +return 1; + posi__meme= posi__mem; + posi__memee= (posi__mem_t*)((char*)posi__mem+siz); + return 0; + } // end posi_ini() + +static inline void posi_set(int64_t id,int32_t x,int32_t y) { + // set geoposition for a specific object ID; + // id: id of the object; + // x,y: geocoordinates in 10^-7 degrees; + if(posi__meme>=posi__memee) // not enough space in position array + exit(70001); + posi__meme->id= id; + posi__meme->data[0]= x; + posi__meme->data[1]= y; + if (global_calccoords<0) { + posi__meme->data[2]= x; // x_min + posi__meme->data[3]= y; // y_min + posi__meme->data[4]= x; // x_max + posi__meme->data[5]= y; // y_max + } + posi__meme+= posi__mem_increment; + } // end posi_set() + +static inline void posi_setbbox(int64_t id,int32_t x,int32_t y, + int32_t xmin,int32_t ymin,int32_t xmax,int32_t ymax) { + // same as posi_set(), however provide a bbox as well; + // important: the caller must ensure that this module has been + // initialized with global_calccoords= -1; + if(posi__meme>=posi__memee) // not enough space in position array + exit(70001); + posi__meme->id= id; + posi__meme->data[0]= x; + posi__meme->data[1]= y; + posi__meme->data[2]= xmin; + posi__meme->data[3]= ymin; + posi__meme->data[4]= xmax; + posi__meme->data[5]= ymax; + posi__meme+= posi__mem_increment; + } // end posi_setbbox() + +static const int32_t posi_nil= 2000000000L; +static int32_t* posi_xy= NULL; // position of latest read coordinates; + // posi_xy[0]: x; posi_xy[1]: y; + // posi_xy==NULL: no geoposition available for this id; + +static inline void posi_get(int64_t id) { + // get the previously stored geopositions of an object; + // id: id of the object; + // return: posi_xy[0]: x; posi_xy[1]: y; + // the caller may change the values for x and y; + // posi_xy==NULL: no geoposition available for this id; + // if this module has been initialized with global_calccoords + // set to -1, the bbox addresses will be returned too: + // posi_xy[2]: xmin; posi_xy[3]: ymin; + // posi_xy[4]: xmax; posi_xy[5]: ymax; + char* min,*max,*middle; + int64_t middle_id; + + min= (char*)posi__mem; + max= (char*)posi__meme; + while(max>min) { // binary search + middle= (((max-min-posi__mem_size)/2)&(posi__mem_mask))+min; + middle_id= *(int64_t*)middle; + if(middle_id==id) { // we found the right object + posi_xy= (int32_t*)(middle+8); +return; + } + if(middle_id>id) + max= middle; + else + min= middle+posi__mem_size; + } // binary search + // here: did not find the geoposition of the object in question + posi_xy= NULL; + } // end posi_geti(); + +//------------------------------------------------------------ +// end Module posi_ OSM position module +//------------------------------------------------------------ + + + +//------------------------------------------------------------ +// Module posr_ object ref temporary module +//------------------------------------------------------------ + +// this module provides procedures to use a temporary file for +// storing relations' references when --all-to-nodes is used; +// as usual, all identifiers of a module have the same prefix, +// in this case 'posr'; an underline will follow for a global +// accessible identifier, two underlines if the identifier +// is not meant to be accessed from outside this module; +// the sections of private and public definitions are separated +// by a horizontal line: ---- + +static char posr__filename[400]= ""; +static int posr__fd= -1; // file descriptor for temporary file +#define posr__bufM 400000 +static int64_t posr__buf[posr__bufM], + *posr__bufp,*posr__bufe,*posr__bufee; + // buffer - used for write, and later for read; +static bool posr__writemode; // buffer is used for writing + +static inline void posr__flush() { + if(!posr__writemode || posr__bufp==posr__buf) +return; + UR(write(posr__fd,posr__buf,(char*)posr__bufp-(char*)posr__buf)) + posr__bufp= posr__buf; + } // end posr__flush() + +static inline void posr__write(int64_t i) { + // write an int64 to tempfile, use a buffer; +//DPv(posr__write %lli,i) + if(posr__bufp>=posr__bufee) posr__flush(); + *posr__bufp++= i; + } // end posr__write() + +static void posr__end() { + // clean-up for temporary file access; + // will be called automatically at program end; + if(posr__fd>2) { + close(posr__fd); + posr__fd= -1; + } + if(loglevel<2) unlink(posr__filename); + } // end posr__end() + +//------------------------------------------------------------ + +static int posr_ini(const char* filename) { + // open a temporary file with the given name for random r/w access; + // return: ==0: ok; !=0: error; + strcpy(stpmcpy(posr__filename,filename,sizeof(posr__filename)-2),".2"); + if(posr__fd>=0) // file already open +return 0; // ignore this call + unlink(posr__filename); + posr__fd= open(posr__filename,O_RDWR|O_CREAT|O_TRUNC|O_BINARY,00600); + if(posr__fd<0) { + PERRv("could not open temporary file: %.80s",posr__filename) +return 1; + } + atexit(posr__end); + posr__bufee= posr__buf+posr__bufM; + posr__bufp= posr__bufe= posr__buf; + posr__writemode= true; + return 0; + } // end posr_ini() + +static inline void posr_rel(int64_t relid,bool is_area) { + // store the id of a relation in tempfile; + // relid: id of this relation; + // is_area: this relation describes an area; + // otherwise: it describes a way; + posr__write(0); + posr__write(relid); + posr__write(is_area); + } // end posr_rel() + +static inline void posr_ref(int64_t refid) { + // store the id of a reference in tempfile; + posr__write(refid); + } // end posr_ref() + +static int posr_rewind() { + // rewind the file pointer; + // return: ==0: ok; !=0: error; + if(posr__writemode) { + posr__flush(); posr__writemode= false; } + if(lseek(posr__fd,0,SEEK_SET)<0) { + PERRv("osmconvert Error: could not rewind temporary file %.80s", + posr__filename) +return 1; + } + posr__bufp= posr__bufe= posr__buf; + return 0; + } // end posr_rewind() + +static inline int posr_read(int64_t* ip) { + // read one integer; meaning of the values of these integers: + // every value is an interrelation reference id, with one exception: + // integers which follow a 0-integer directly are relation ids; + // return: ==0: ok; !=0: eof; + int r,r2; + + if(posr__bufp>=posr__bufe) { + r= read(posr__fd,posr__buf,sizeof(posr__buf)); + if(r<=0) +return 1; + posr__bufe= (int64_t*)((char*)posr__buf+r); + if((r%8)!=0) { // odd number of bytes + r2= read(posr__fd,posr__bufe,8-(r%8)); + // request the missing bytes + if(r2<=0) // did not get the missing bytes + posr__bufe= (int64_t*)((char*)posr__bufe-(r%8)); + else + posr__bufe= (int64_t*)((char*)posr__bufe+r2); + } + posr__bufp= posr__buf; + } + *ip= *posr__bufp++; + return 0; + } // end posr_read() + +static void posr_processing(int* maxrewindp,int32_t** refxy) { + // process temporary relation reference file; + // the file must already have been written; this procedure + // processes the interrelation references of this file and updates + // the georeference table of module posi_ accordingly; + // maxrewind: maximum number of rewinds; + // refxy: memory space provided by the caller; + // this is a temporarily used space for the coordinates + // of the relations' members; + // return: + // maxrewind: <0: maximum number of rewinds was not sufficient; + int changed; + // number of relations whose flag has been changed, i.e., + // the recursive processing will continue; + // if none of the relations' flags has been changed, + // this procedure will end; + int h; // counter for interrelational hierarchies + int64_t relid; // relation id; + int64_t refid; // interrelation reference id; + bool jump_over; // jump over the presently processed relation + int32_t* xy_rel; // geocoordinates of the processed relation; + int32_t x_min,x_max,y_min,y_max; + int32_t x_middle,y_middle,xy_distance,new_distance; + int n; // number of referenced objects with coordinates + int64_t temp64; + bool is_area; // the relation describes an area + int32_t** refxyp; // pointer in refxy array + int r; + + h= 0; n=0; + jump_over= true; + relid= 0; + while(*maxrewindp>=0) { // for every recursion + changed= 0; + if(posr_rewind()) // could not rewind + break; + for(;;) { // for every reference + for(;;) { // get next id + r= posr_read(&refid); + if((r || refid==0) && n>0) { // (EOF OR new relation) AND + // there have been coordinates for this relation + x_middle= x_max/2+x_min/2; + y_middle= (y_max+y_min)/2; + // store the coordinates for this relation +//DPv(is_area %i refxy %i,is_area,refxyp==refxy) + if(global_calccoords<0) { + xy_rel[2]= x_min; + xy_rel[3]= y_min; + xy_rel[4]= x_max; + xy_rel[5]= y_max; + } + if(is_area || refxyp==refxy) { + // take the center as position for this relation + xy_rel[0]= x_middle; + xy_rel[1]= y_middle; + } + else { // not an area + int32_t x,y; + + // get the member position which is the nearest + // to the center + posi_xy= *--refxyp; + x= posi_xy[0]; + y= posi_xy[1]; + xy_distance= abs(x-x_middle)+abs(y-y_middle); + while(refxyp>refxy) { + refxyp--; + new_distance= abs(posi_xy[0]-x_middle)+ + abs(posi_xy[1]-y_middle); + if(new_distanceglobal_otypeoffset15) { // refers to a relation + n= 0; // ignore previously collected coordinates + jump_over= true; // no yet able to determine the position + } + continue; // go on and examine next reference of this relation + } + *refxyp++= posi_xy; // store coordinate for reprocessing later + if(n==0) { // first coordinate + if(global_calccoords<0) { + x_min = posi_xy[2]; + y_min = posi_xy[3]; + x_max = posi_xy[4]; + y_max = posi_xy[5]; + } + else { + // just store it as min and max + x_min= x_max= posi_xy[0]; + y_min= y_max= posi_xy[1]; + } + } + else if(global_calccoords<0) { + // adjust extrema + if(posi_xy[2]x_max && posi_xy[4]-x_max<900000000) + x_max= posi_xy[4]; + if(posi_xy[3]y_max) + y_max= posi_xy[5]; + } + else { // additional coordinate + // adjust extrema + if(posi_xy[0]x_max && posi_xy[0]-x_max<900000000) + x_max= posi_xy[0]; + if(posi_xy[1]y_max) + y_max= posi_xy[1]; + } + n++; + } // end for every reference + rewind: + if(loglevel>0) fprintf(stderr, + "Interrelational hierarchy %i: %i dependencies.\n",++h,changed); + if(changed==0) // no changes have been made in last recursion + break; // end the processing + (*maxrewindp)--; + } // end for every recursion + } // end posr_processing() + +//------------------------------------------------------------ +// end Module posr_ object ref temporary module +//------------------------------------------------------------ + + + +//------------------------------------------------------------ +// Module rr_ relref temporary module +//------------------------------------------------------------ + +// this module provides procedures to use a temporary file for +// storing relation's references; +// as usual, all identifiers of a module have the same prefix, +// in this case 'rr'; an underline will follow in case of a +// global accessible object, two underlines in case of objects +// which are not meant to be accessed from outside this module; +// the sections of private and public definitions are separated +// by a horizontal line: ---- + +static char rr__filename[400]= ""; +static int rr__fd= -1; // file descriptor for temporary file +#define rr__bufM 400000 +static int64_t rr__buf[rr__bufM],*rr__bufp,*rr__bufe,*rr__bufee; + // buffer - used for write, and later for read; +static bool rr__writemode; // buffer is used for writing + +static inline void rr__flush() { + if(!rr__writemode || rr__bufp==rr__buf) +return; + UR(write(rr__fd,rr__buf,(char*)rr__bufp-(char*)rr__buf)) + rr__bufp= rr__buf; + } // end rr__flush() + +static inline void rr__write(int64_t i) { + // write an int to tempfile, use a buffer; + if(rr__bufp>=rr__bufee) rr__flush(); + *rr__bufp++= i; + } // end rr__write() + +static void rr__end() { + // clean-up for temporary file access; + // will be called automatically at program end; + if(rr__fd>2) { + close(rr__fd); + rr__fd= -1; + } + if(loglevel<2) unlink(rr__filename); + } // end rr__end() + +//------------------------------------------------------------ + +static int rr_ini(const char* filename) { + // open a temporary file with the given name for random r/w access; + // return: ==0: ok; !=0: error; + strcpy(stpmcpy(rr__filename,filename,sizeof(rr__filename)-2),".0"); + if(rr__fd>=0) // file already open +return 0; // ignore this call + unlink(rr__filename); + rr__fd= open(rr__filename,O_RDWR|O_CREAT|O_TRUNC|O_BINARY,00600); + if(rr__fd<0) { + fprintf(stderr, + "osmconvert Error: could not open temporary file: %.80s\n", + rr__filename); +return 1; + } + atexit(rr__end); + rr__bufee= rr__buf+rr__bufM; + rr__bufp= rr__bufe= rr__buf; + rr__writemode= true; + return 0; + } // end rr_ini() + +static inline void rr_rel(int64_t relid) { + // store the id of a relation in tempfile; + rr__write(0); + rr__write(relid); + } // end rr_rel() + +static inline void rr_ref(int64_t refid) { + // store the id of an interrelation reference in tempfile; + rr__write(refid); + } // end rr_ref() + +static int rr_rewind() { + // rewind the file pointer; + // return: ==0: ok; !=0: error; + if(rr__writemode) { + rr__flush(); rr__writemode= false; } + if(lseek(rr__fd,0,SEEK_SET)<0) { + fprintf(stderr,"osmconvert Error: could not rewind temporary file" + " %.80s\n",rr__filename); +return 1; + } + rr__bufp= rr__bufe= rr__buf; + return 0; + } // end rr_rewind() + +static inline int rr_read(int64_t* ip) { + // read one integer; meaning of the values of these integers: + // every value is an interrelation reference id, with one exception: + // integers which follow a 0-integer directly are relation ids; + // note that we take 64-bit-integers although the number of relations + // will never exceed 2^31; the reason is that Free OSM ("FOSM") uses + // IDs > 2^16 for new data which adhere the cc-by-sa license; + // return: ==0: ok; !=0: eof; + int r,r2; + + if(rr__bufp>=rr__bufe) { + r= read(rr__fd,rr__buf,sizeof(rr__buf)); + if(r<=0) +return 1; + rr__bufe= (int64_t*)((char*)rr__buf+r); + if((r%8)!=0) { // odd number of bytes + r2= read(rr__fd,rr__bufe,8-(r%8)); // request the missing bytes + if(r2<=0) // did not get the missing bytes + rr__bufe= (int64_t*)((char*)rr__bufe-(r%8)); + else + rr__bufe= (int64_t*)((char*)rr__bufe+r2); + } + rr__bufp= rr__buf; + } + *ip= *rr__bufp++; + return 0; + } // end rr_read() + +//------------------------------------------------------------ +// end Module rr_ relref temporary module +//------------------------------------------------------------ + + + +//------------------------------------------------------------ +// Module cwn_ complete way ref temporary module +//------------------------------------------------------------ + +// this module provides procedures to use a temporary file for +// storing a list of nodes which have to be marked as 'inside'; +// this is used if option --complete-ways is invoked; +// as usual, all identifiers of a module have the same prefix, +// in this case 'posi'; an underline will follow for a global +// accessible identifier, two underlines if the identifier +// is not meant to be accessed from outside this module; +// the sections of private and public definitions are separated +// by a horizontal line: ---- + +static char cwn__filename[400]= ""; +static int cwn__fd= -1; // file descriptor for temporary file +#define cwn__bufM 400000 +static int64_t cwn__buf[cwn__bufM], + *cwn__bufp,*cwn__bufe,*cwn__bufee; + // buffer - used for write, and later for read; +static bool cwn__writemode; // buffer is used for writing + +static inline void cwn__flush() { + if(!cwn__writemode || cwn__bufp==cwn__buf) +return; + UR(write(cwn__fd,cwn__buf,(char*)cwn__bufp-(char*)cwn__buf)) + cwn__bufp= cwn__buf; + } // end cwn__flush() + +static inline void cwn__write(int64_t i) { + // write an int64 to tempfile, use a buffer; + if(cwn__bufp>=cwn__bufee) cwn__flush(); + *cwn__bufp++= i; + } // end cwn__write() + +static void cwn__end() { + // clean-up for temporary file access; + // will be called automatically at program end; + if(cwn__fd>2) { + close(cwn__fd); + cwn__fd= -1; + } + if(loglevel<2) unlink(cwn__filename); + } // end cwn__end() + +//------------------------------------------------------------ + +static int cwn_ini(const char* filename) { + // open a temporary file with the given name for random r/w access; + // return: ==0: ok; !=0: error; + strcpy(stpmcpy(cwn__filename,filename,sizeof(cwn__filename)-2),".3"); + if(cwn__fd>=0) // file already open +return 0; // ignore this call + unlink(cwn__filename); + cwn__fd= open(cwn__filename,O_RDWR|O_CREAT|O_TRUNC|O_BINARY,00600); + if(cwn__fd<0) { + PERRv("could not open temporary file: %.80s",cwn__filename) +return 1; + } + atexit(cwn__end); + cwn__bufee= cwn__buf+cwn__bufM; + cwn__bufp= cwn__bufe= cwn__buf; + cwn__writemode= true; + return 0; + } // end cwn_ini() + +static inline void cwn_ref(int64_t refid) { + // store the id of a referenced node in tempfile; + cwn__write(refid); + } // end cwn_ref() + +static int cwn_rewind() { + // rewind the file pointer; + // return: ==0: ok; !=0: error; + if(cwn__writemode) { + cwn__flush(); cwn__writemode= false; } + if(lseek(cwn__fd,0,SEEK_SET)<0) { + PERRv("osmconvert Error: could not rewind temporary file %.80s", + cwn__filename) +return 1; + } + cwn__bufp= cwn__bufe= cwn__buf; + return 0; + } // end cwn_rewind() + +static inline int cwn_read(int64_t* ip) { + // read the id of next referenced node; + // return: ==0: ok; !=0: eof; + int r,r2; + + if(cwn__bufp>=cwn__bufe) { + r= read(cwn__fd,cwn__buf,sizeof(cwn__buf)); + if(r<=0) +return 1; + cwn__bufe= (int64_t*)((char*)cwn__buf+r); + if((r%8)!=0) { // odd number of bytes + r2= read(cwn__fd,cwn__bufe,8-(r%8)); + // request the missing bytes + if(r2<=0) // did not get the missing bytes + cwn__bufe= (int64_t*)((char*)cwn__bufe-(r%8)); + else + cwn__bufe= (int64_t*)((char*)cwn__bufe+r2); + } + cwn__bufp= cwn__buf; + } + *ip= *cwn__bufp++; + return 0; + } // end cwn_read() + +static void cwn_processing() { + // process temporary node reference file; + // the file must already have been written; this procedure + // sets the a flag in hash table (module hash_) for each node + // which is referred to by an entry in the temporary file; + int64_t id; // node id; + + if(cwn_rewind()) // could not rewind +return; + for(;;) { // get next id + if(cwn_read(&id)) + break; + hash_seti(0,id); + } + } // end cwn_processing() + +//------------------------------------------------------------ +// end Module cwn_ complete way ref temporary module +//------------------------------------------------------------ + + + +//------------------------------------------------------------ +// Module cww_ complex way ref temporary module +//------------------------------------------------------------ + +// this module provides procedures to use a temporary file for +// storing a list of ways which have to be marked as 'inside'; +// this is used if option --complex-ways is invoked; +// as usual, all identifiers of a module have the same prefix, +// in this case 'posi'; an underline will follow for a global +// accessible identifier, two underlines if the identifier +// is not meant to be accessed from outside this module; +// the sections of private and public definitions are separated +// by a horizontal line: ---- + +static char cww__filename[400]= ""; +static int cww__fd= -1; // file descriptor for temporary file +#define cww__bufM 400000 +static int64_t cww__buf[cww__bufM], + *cww__bufp,*cww__bufe,*cww__bufee; + // buffer - used for write, and later for read; +static bool cww__writemode; // buffer is used for writing + +static inline void cww__flush() { + if(!cww__writemode || cww__bufp==cww__buf) +return; + UR(write(cww__fd,cww__buf,(char*)cww__bufp-(char*)cww__buf)) + cww__bufp= cww__buf; + } // end cww__flush() + +static inline void cww__write(int64_t i) { + // write an int64 to tempfile, use a buffer; + if(cww__bufp>=cww__bufee) cww__flush(); + *cww__bufp++= i; + } // end cww__write() + +static void cww__end() { + // clean-up for temporary file access; + // will be called automatically at program end; + if(cww__fd>2) { + close(cww__fd); + cww__fd= -1; + } + if(loglevel<2) unlink(cww__filename); + } // end cww__end() + +//------------------------------------------------------------ + +static int cww_ini(const char* filename) { + // open a temporary file with the given name for random r/w access; + // return: ==0: ok; !=0: error; + strcpy(stpmcpy(cww__filename,filename,sizeof(cww__filename)-2),".5"); + if(cww__fd>=0) // file already open +return 0; // ignore this call + unlink(cww__filename); + cww__fd= open(cww__filename,O_RDWR|O_CREAT|O_TRUNC|O_BINARY,00600); + if(cww__fd<0) { + PERRv("could not open temporary file: %.80s",cww__filename) +return 1; + } + atexit(cww__end); + cww__bufee= cww__buf+cww__bufM; + cww__bufp= cww__bufe= cww__buf; + cww__writemode= true; + return 0; + } // end cww_ini() + +static inline void cww_ref(int64_t refid) { + // store the id of a referenced way in tempfile; + cww__write(refid); + } // end cww_ref() + +static int cww_rewind() { + // rewind the file pointer; + // return: ==0: ok; !=0: error; + if(cww__writemode) { + cww__flush(); cww__writemode= false; } + if(lseek(cww__fd,0,SEEK_SET)<0) { + PERRv("osmconvert Error: could not rewind temporary file %.80s", + cww__filename) +return 1; + } + cww__bufp= cww__bufe= cww__buf; + return 0; + } // end cww_rewind() + +static inline int cww_read(int64_t* ip) { + // read the id of next referenced node; + // return: ==0: ok; !=0: eof; + int r,r2; + + if(cww__bufp>=cww__bufe) { + r= read(cww__fd,cww__buf,sizeof(cww__buf)); + if(r<=0) +return 1; + cww__bufe= (int64_t*)((char*)cww__buf+r); + if((r%8)!=0) { // odd number of bytes + r2= read(cww__fd,cww__bufe,8-(r%8)); + // request the missing bytes + if(r2<=0) // did not get the missing bytes + cww__bufe= (int64_t*)((char*)cww__bufe-(r%8)); + else + cww__bufe= (int64_t*)((char*)cww__bufe+r2); + } + cww__bufp= cww__buf; + } + *ip= *cww__bufp++; + return 0; + } // end cww_read() + +static void cww_processing_set() { + // process temporary way reference file; + // the file must already have been written; this procedure + // sets the a flag in hash table (module hash_) for each way + // which is referred to by an entry in the temporary file; + int64_t id; // way id; + + if(cww__filename[0]==0) // not initialized +return; + if(cww_rewind()) // could not rewind +return; + for(;;) { // get next id + if(cww_read(&id)) + break; + hash_seti(1,id); + } + } // end cww_processing_set() + +static void cww_processing_clear() { + // process temporary way reference file; + // the file must already have been written; this procedure + // clears the a flag in hash table (module hash_) for each way + // which is referred to by an entry in the temporary file; + int64_t id; // way id; + + if(cww__filename[0]==0) // not initialized +return; + if(cww_rewind()) // could not rewind +return; + for(;;) { // get next id + if(cww_read(&id)) + break; + hash_cleari(1,id); + } + } // end cww_processing_clear() + +//------------------------------------------------------------ +// end Module cww_ complex way ref temporary module +//------------------------------------------------------------ + + + +//------------------------------------------------------------ +// Module o5_ o5m conversion module +//------------------------------------------------------------ + +// this module provides procedures which convert data to +// o5m format; +// as usual, all identifiers of a module have the same prefix, +// in this case 'o5'; an underline will follow in case of a +// global accessible object, two underlines in case of objects +// which are not meant to be accessed from outside this module; +// the sections of private and public definitions are separated +// by a horizontal line: ---- + +static inline void stw_reset(); + +#define o5__bufM UINT64_C(5000000) +static byte* o5__buf= NULL; // buffer for one object in .o5m format +static byte* o5__bufe= NULL; + // (const) water mark for buffer filled nearly 100% +static byte* o5__bufp= NULL; +static byte* o5__bufr0= NULL,*o5__bufr1= NULL; + // start end end mark of a reference area in o5__buf[]; + // ==NULL: no mark set; + +// basis for delta coding +static int64_t o5_id; +static uint32_t o5_lat,o5_lon; +static int64_t o5_cset; +static int64_t o5_time; +static int64_t o5_ref[3]; // for node, way, relation + +static inline void o5__resetvars() { + // reset all delta coding counters; + o5__bufp= o5__buf; + o5__bufr0= o5__bufr1= o5__buf; + o5_id= 0; + o5_lat= o5_lon= 0; + o5_cset= 0; + o5_time= 0; + o5_ref[0]= o5_ref[1]= o5_ref[2]= 0; + stw_reset(); + } // end o5__resetvars() + +static void o5__end() { + // clean-up for o5 module; + // will be called at program's end; + if(o5__buf!=NULL) { + free(o5__buf); o5__buf= NULL; } + } // end o5__end() + +//------------------------------------------------------------ + +static inline void o5_reset() { + // perform and write an o5m Reset; + o5__resetvars(); + write_char(0xff); // write .o5m Reset + } // end o5_reset() + +static int o5_ini() { + // initialize this module; + // must be called before any other procedure is called; + // return: 0: everything went ok; + // !=0: an error occurred; + static bool firstrun= true; + + if(firstrun) { + firstrun= false; + o5__buf= (byte*)malloc(o5__bufM); + if(o5__buf==NULL) +return 1; + atexit(o5__end); + o5__bufe= o5__buf+o5__bufM-400000; + } + o5__resetvars(); + return 0; + } // end o5_ini() + +static inline void o5_byte(byte b) { + // write a single byte; + // writing starts at position o5__bufp; + // o5__bufp: incremented by 1; + *o5__bufp++= b; + } // end o5_byte() + +static inline int o5_str(const char* s) { + // write a zero-terminated string; + // writing starts at position o5__bufp; + // return: bytes written; + // o5__bufp: increased by the number of written bytes; + byte* p0; + byte c; + + p0= o5__bufp; + if(o5__bufp>=o5__bufe) { + static int msgn= 1; + if(--msgn>=0) { + fprintf(stderr,"osmconvert Error: .o5m memory overflow.\n"); +return 0; + } + } + do *o5__bufp++= c= *s++; + while(c!=0); +return o5__bufp-p0; + } // end o5_str() + +static inline int o5_uvar32buf(byte* p,uint32_t v) { + // write an unsigned 32 bit integer as Varint into a buffer; + // writing starts at position p; + // return: bytes written; + byte* p0; + uint32_t frac; + + p0= p; + frac= v&0x7f; + if(frac==v) { // just one byte + *p++= frac; +return 1; + } + do { + *p++= frac|0x80; + v>>= 7; + frac= v&0x7f; + } while(frac!=v); + *p++= frac; +return p-p0; + } // end o5_uvar32buf() + +static inline int o5_uvar32(uint32_t v) { + // write an unsigned 32 bit integer as Varint; + // writing starts at position o5__bufp; + // return: bytes written; + // o5__bufp: increased by the number of written bytes; + byte* p0; + uint32_t frac; + + if(o5__bufp>=o5__bufe) { + static int msgn= 1; + if(--msgn>=0) { + fprintf(stderr,"osmconvert Error: .o5m memory overflow.\n"); +return 0; + } + } + p0= o5__bufp; + frac= v&0x7f; + if(frac==v) { // just one byte + *o5__bufp++= frac; +return 1; + } + do { + *o5__bufp++= frac|0x80; + v>>= 7; + frac= v&0x7f; + } while(frac!=v); + *o5__bufp++= frac; +return o5__bufp-p0; + } // end o5_uvar32() + +static inline int o5_svar32(int32_t v) { + // write a signed 32 bit integer as signed Varint; + // writing starts at position o5__bufp; + // return: bytes written; + // o5__bufp: increased by the number of written bytes; + byte* p0; + uint32_t u; + uint32_t frac; + + if(o5__bufp>=o5__bufe) { + static int msgn= 1; + if(--msgn>=0) { + fprintf(stderr,"osmconvert Error: .o5m memory overflow.\n"); +return 0; + } + } + p0= o5__bufp; + if(v<0) { + u= -v; + u= (u<<1)-1; + } + else + u= v<<1; + frac= u&0x7f; + if(frac==u) { // just one byte + *o5__bufp++= frac; +return 1; + } + do { + *o5__bufp++= frac|0x80; + u>>= 7; + frac= u&0x7f; + } while(frac!=u); + *o5__bufp++= frac; +return o5__bufp-p0; + } // end o5_svar32() + +static inline int o5_svar64(int64_t v) { + // write a signed 64 bit integer as signed Varint; + // writing starts at position o5__bufp; + // return: bytes written; + // o5__bufp: increased by the number of written bytes; + byte* p0; + uint64_t u; + uint32_t frac; + + if(o5__bufp>=o5__bufe) { + static int msgn= 1; + if(--msgn>=0) { + fprintf(stderr,"osmconvert Error: .o5m memory overflow.\n"); +return 0; + } + } + p0= o5__bufp; + if(v<0) { + u= -v; + u= (u<<1)-1; + } + else + u= v<<1; + frac= u&0x7f; + if(frac==u) { // just one byte + *o5__bufp++= frac; +return 1; + } + do { + *o5__bufp++= frac|0x80; + u>>= 7; + frac= u&0x7f; + } while(frac!=u); + *o5__bufp++= frac; +return o5__bufp-p0; + } // end o5_svar64() + +static inline void o5_markref(int pos) { + // mark reference area; + // pos: ==0: start; ==1: end; + // 0 is accepted only once per dataset; only the first + // request is valid; + // 1 may be repeated, the last one counts; + if(pos==0) { + if(o5__bufr0==o5__buf) o5__bufr0= o5__bufp; + } + else + o5__bufr1= o5__bufp; + } // end o5_markref() + +static inline void o5_type(int type) { + // mark object type we are going to process now; + // should be called every time a new object is started to be + // written into o5_buf[]; + // type: object type; 0: node; 1: way; 2: relation; + // if object type has changed, a 0xff byte ("reset") + // will be written; + static int oldtype= -1; + + // process changes of object type + if(type!=oldtype) { // object type has changed + oldtype= type; + o5_reset(); + } + oldtype= type; + } // end o5_type() + +static void o5_write() { + // write o5__buf[] contents to standard output; + // include object length information after byte 0 and include + // ref area length information right before o5__bufr0 (if !=NULL); + // if buffer is empty, this procedure does nothing; + byte lens[30],reflens[30]; // lengths as pbf numbers + int len; // object length + int reflen; // reference area length + int reflenslen; // length of pbf number of reflen + + // get some length information + len= o5__bufp-o5__buf; + if(len<=0) goto o5_write_end; + reflen= 0; // (default) + if(o5__bufr1o5__buf) { + // reference area contains at least 1 byte + reflen= o5__bufr1-o5__bufr0; + reflenslen= o5_uvar32buf(reflens,reflen); + len+= reflenslen; + } // end reference area contains at least 1 byte + + // write header + if(--len>=0) { + write_char(o5__buf[0]); + write_mem(lens,o5_uvar32buf(lens,len)); + } + + // write body + if(o5__bufr0<=o5__buf) // no reference area + write_mem(o5__buf+1,o5__bufp-(o5__buf+1)); + else { // valid reference area + write_mem(o5__buf+1,o5__bufr0-(o5__buf+1)); + write_mem(reflens,reflenslen); + write_mem(o5__bufr0,o5__bufp-o5__bufr0); + } // end valid reference area + + // reset buffer pointer + o5_write_end: + o5__bufp= o5__buf; // set original buffer pointer to buffer start + o5__bufr0= o5__bufr1= o5__buf; // clear reference area marks + } // end o5_write() + +//------------------------------------------------------------ +// end Module o5_ o5m conversion module +//------------------------------------------------------------ + + + +//------------------------------------------------------------ +// Module stw_ string write module +//------------------------------------------------------------ + +// this module provides procedures for conversions from +// c formatted strings into referenced string data stream objects +// - and writing it to buffered standard output; +// as usual, all identifiers of a module have the same prefix, +// in this case 'stw'; an underline will follow in case of a +// global accessible object, two underlines in case of objects +// which are not meant to be accessed from outside this module; +// the sections of private and public definitions are separated +// by a horizontal line: ---- + +#define stw__tabM 15000 +#define stw__tabstrM 250 // must be < row size of stw__rab[] +#define stw__hashtabM 150001 // (preferably a prime number) +static char stw__tab[stw__tabM][256]; + // string table; see o5m documentation; + // row length must be at least stw__tabstrM+2; + // each row contains a double string; each of the two strings + // is terminated by a zero byte, the lengths must not exceed + // stw__tabstrM bytes in total; +static int stw__tabi= 0; + // index of last entered element in string table +static int stw__hashtab[stw__hashtabM]; + // has table; elements point to matching strings in stw__tab[]; + // -1: no matching element; +static int stw__tabprev[stw__tabM],stw__tabnext[stw__tabM]; + // for to chaining of string table rows which match + // the same hash value; matching rows are chained in a loop; + // if there is only one row matching, it will point to itself; +static int stw__tabhash[stw__tabM]; + // has value of this element as a link back to the hash table; + // a -1 element indicates that the string table entry is not used; + +static inline int stw__hash(const char* s1,const char* s2) { + // get hash value of a string pair; + // s2: ==NULL: single string; this is treated as s2==""; + // return: hash value in the range 0..(stw__hashtabM-1); + // -1: the strings are longer than stw__tabstrM characters in total; + uint32_t h; + uint32_t c; + int len; + + #if 0 // not used because strings would not be transparent anymore + if(*s1==(char)0xff) // string is marked as 'do-not-store'; +return -1; + #endif + len= stw__tabstrM; + h= 0; + for(;;) { + if((c= *s1++)==0 || --len<0) break; h+= c; + if((c= *s1++)==0 || --len<0) break; h+= c<<8; + if((c= *s1++)==0 || --len<0) break; h+= c<<16; + if((c= *s1++)==0 || --len<0) break; h+= c<<24; + } + if(s2!=NULL) for(;;) { + if((c= *s2++)==0 || --len<0) break; h+= c; + if((c= *s2++)==0 || --len<0) break; h+= c<<8; + if((c= *s2++)==0 || --len<0) break; h+= c<<16; + if((c= *s2++)==0 || --len<0) break; h+= c<<24; + } + if(len<0) +return -1; + h%= stw__hashtabM; + return h; + } // end stw__hash() + +static inline int stw__getref(int stri,const char* s1,const char* s2) { + // get the string reference of a string pair; + // the strings must not have more than 250 characters in total + // (252 including terminators), there is no check in this procedure; + // stri: presumed index in string table (we got it from hash table); + // must be >=0 and =0) stw__tabhash[i]= -1; + i= stw__hashtabM; + while(--i>=0) stw__hashtab[i]= -1; + } // end stw_reset() + +static void stw_write(const char* s1,const char* s2) { + // write a string (pair), e.g. key/val, to o5m buffer; + // if available, write a string reference instead of writing the + // string pair directly; + // no reference is used if the strings are longer than + // 250 characters in total (252 including terminators); + // s2: ==NULL: it's not a string pair but a single string; + int h; // hash value + int ref; + + /* try to find a matching string (pair) in string table */ { + int i; // index in stw__tab[] + + ref= -1; // ref invalid (default) + h= stw__hash(s1,s2); + if(h>=0) { // string (pair) short enough for the string table + i= stw__hashtab[h]; + if(i>=0) // string (pair) presumably stored already + ref= stw__getref(i,s1,s2); + } // end string (pair) short enough for the string table + if(ref>=0) { // we found the string (pair) in the table + o5_uvar32(ref); // write just the reference +return; + } // end we found the string (pair) in the table + else { // we did not find the string (pair) in the table + // write string data + o5_byte(0); o5_str(s1); + if(s2!=NULL) o5_str(s2); // string pair, not a single string + if(h<0) // string (pair) too long, + // cannot be stored in string table +return; + } // end we did not find the string (pair) in the table + } // end try to find a matching string (pair) in string table + // here: there is no matching string (pair) in the table + + /* free new element - if still being used */ { + int h0; // hash value of old element + + h0= stw__tabhash[stw__tabi]; + if(h0>=0) { // new element in string table is still being used + // delete old element + if(stw__tabnext[stw__tabi]==stw__tabi) + // self-chain, i.e., only this element + stw__hashtab[h0]= -1; // invalidate link in hash table + else { // one or more other elements in chain + stw__hashtab[h0]= stw__tabnext[stw__tabi]; // just to ensure + // that hash entry does not point to deleted element + // now unchain deleted element + stw__tabprev[stw__tabnext[stw__tabi]]= stw__tabprev[stw__tabi]; + stw__tabnext[stw__tabprev[stw__tabi]]= stw__tabnext[stw__tabi]; + } // end one or more other elements in chain + } // end next element in string table is still being used + } // end free new element - if still being used + + /* enter new string table element data */ { + char* sp; + int i; + + sp= stpcpy0(stw__tab[stw__tabi],s1)+1; + // write first string into string table + if(s2==NULL) // single string + *sp= 0; // second string must be written as empty string + // into string table + else + stpcpy0(sp,s2); // write second string into string table + i= stw__hashtab[h]; + if(i<0) // no reference in hash table until now + stw__tabprev[stw__tabi]= stw__tabnext[stw__tabi]= stw__tabi; + // self-link the new element; + else { // there is already a reference in hash table + // in-chain the new element + stw__tabnext[stw__tabi]= i; + stw__tabprev[stw__tabi]= stw__tabprev[i]; + stw__tabnext[stw__tabprev[stw__tabi]]= stw__tabi; + stw__tabprev[i]= stw__tabi; + } + stw__hashtab[h]= stw__tabi; // link the new element to hash table + stw__tabhash[stw__tabi]= h; // backlink to hash table element + // new element now in use; set index to oldest element + if(++stw__tabi>=stw__tabM) { // index overflow + stw__tabi= 0; // restart index + if(loglevel>=2) { + static int rs= 0; + fprintf(stderr, + "osmconvert: String table index restart %i\n",++rs); + } + } // end index overflow + } // end enter new string table element data + } // end stw_write() + +//------------------------------------------------------------ +// end Module stw_ string write module +//------------------------------------------------------------ + + + +//------------------------------------------------------------ +// Module str_ string read module +//------------------------------------------------------------ + +// this module provides procedures for conversions from +// strings which have been stored in data stream objects to +// c-formatted strings; +// as usual, all identifiers of a module have the same prefix, +// in this case 'str'; one underline will follow in case of a +// global accessible object, two underlines in case of objects +// which are not meant to be accessed from outside this module; +// the sections of private and public definitions are separated +// by a horizontal line: ---- + +#define str__tabM (15000+4000) + // +4000 because it might happen that an object has a lot of + // key/val pairs or refroles which are not stored already; +#define str__tabstrM 250 // must be < row size of str__rab[] +typedef struct str__info_struct { + // members of this structure must not be accessed + // from outside this module; + char tab[str__tabM][256]; + // string table; see o5m documentation; + // row length must be at least str__tabstrM+2; + // each row contains a double string; each of the two strings + // is terminated by a zero byte, the logical lengths must not + // exceed str__tabstrM bytes in total; + // the first str__tabM lines of this array are used as + // input buffer for strings; + int tabi; // index of last entered element in string table; + int tabn; // number of valid strings in string table; + struct str__info_struct* prev; // address of previous unit; + } str_info_t; +str_info_t* str__infop= NULL; + +static void str__end() { + // clean-up this module; + str_info_t* p; + + while(str__infop!=NULL) { + p= str__infop->prev; + free(str__infop); + str__infop= p; + } + } // end str__end() + +//------------------------------------------------------------ + +static str_info_t* str_open() { + // open an new string client unit; + // this will allow us to process multiple o5m input files; + // return: handle of the new unit; + // ==NULL: error; + // you do not need to care about closing the unit(s); + static bool firstrun= true; + str_info_t* prev; + + prev= str__infop; + str__infop= (str_info_t*)malloc(sizeof(str_info_t)); + if(str__infop==NULL) { + PERR("could not get memory for string buffer.") +return NULL; + } + str__infop->tabi= 0; + str__infop->tabn= 0; + str__infop->prev= prev; + if(firstrun) { + firstrun= false; + atexit(str__end); + } + return str__infop; + } // end str_open() + +static inline void str_switch(str_info_t* sh) { + // switch to another string unit + // sh: string unit handle; + str__infop= sh; + } // end str_switch() + +static inline void str_reset() { + // clear string table; + // must be called before any other procedure of this module + // and may be called every time the string processing shall + // be restarted; + if(str__infop!=NULL) + str__infop->tabi= str__infop->tabn= 0; + } // end str_reset() + +static void str_read(byte** pp,char** s1p,char** s2p) { + // read an o5m formatted string (pair), e.g. key/val, from + // standard input buffer; + // if got a string reference, resolve it, using an internal + // string table; + // no reference is used if the strings are longer than + // 250 characters in total (252 including terminators); + // pp: address of a buffer pointer; + // this pointer will be incremented by the number of bytes + // the converted protobuf element consumes; + // s2p: ==NULL: read not a string pair but a single string; + // return: + // *s1p,*s2p: pointers to the strings which have been read; + char* p; + int len1,len2; + int ref; + bool donotstore; // string has 'do not store flag' 2012-10-01 + + p= (char*)*pp; + if(*p==0) { // string (pair) given directly + p++; + donotstore= false; + #if 0 // not used because strings would not be transparent anymore + if(*p==(char)0xff) { // string has 'do-not-store' flag + donotstore= true; + p++; + } // string has 'do-not-store' flag + #endif + *s1p= p; + len1= strlen(p); + p+= len1+1; + if(s2p==NULL) { // single string + if(!donotstore && len1<=str__tabstrM) { + // single string short enough for string table + stpcpy0(str__infop->tab[str__infop->tabi],*s1p)[1]= 0; + // add a second terminator, just in case someone will try + // to read this single string as a string pair later; + if(++str__infop->tabi>=str__tabM) str__infop->tabi= 0; + if(str__infop->tabntabn++; + } // end single string short enough for string table + } // end single string + else { // string pair + *s2p= p; + len2= strlen(p); + p+= len2+1; + if(!donotstore && len1+len2<=str__tabstrM) { + // string pair short enough for string table + memcpy(str__infop->tab[str__infop->tabi],*s1p,len1+len2+2); + if(++str__infop->tabi>=str__tabM) str__infop->tabi= 0; + if(str__infop->tabntabn++; + } // end string pair short enough for string table + } // end string pair + *pp= (byte*)p; + } // end string (pair) given directly + else { // string (pair) given by reference + ref= pbf_uint32(pp); + if(ref>str__infop->tabn) { // string reference invalid + WARNv("invalid .o5m string reference: %i->%i", + str__infop->tabn,ref) + *s1p= "(invalid)"; + if(s2p!=NULL) // caller wants a string pair + *s2p= "(invalid)"; + } // end string reference invalid + else { // string reference valid + ref= str__infop->tabi-ref; + if(ref<0) ref+= str__tabM; + *s1p= str__infop->tab[ref]; + if(s2p!=NULL) // caller wants a string pair + *s2p= strchr(str__infop->tab[ref],0)+1; + } // end string reference valid + } // end string (pair) given by reference + } // end str_read() + +//------------------------------------------------------------ +// end Module str_ string read module +//------------------------------------------------------------ + + + +//------------------------------------------------------------ +// Module wo_ write osm module +//------------------------------------------------------------ + +// this module provides procedures which write osm objects; +// it uses procedures from module o5_; +// as usual, all identifiers of a module have the same prefix, +// in this case 'wo'; an underline will follow in case of a +// global accessible object, two underlines in case of objects +// which are not meant to be accessed from outside this module; +// the sections of private and public definitions are separated +// by a horizontal line: ---- + +static int wo__format= 0; // output format; + // 0: o5m; 11: native XML; 12: pbf2osm; 13: Osmosis; 14: Osmium; + // 21: csv; -1: PBF; +static bool wo__logaction= false; // write action for change files, + // e.g. "", "", etc. +static char* wo__xmlclosetag= NULL; // close tag for XML output; +static bool wo__xmlshorttag= false; + // write the short tag ("/>") instead of the long tag; +#define wo__CLOSE { /* close the newest written object; */ \ + if(wo__xmlclosetag!=NULL) { if(wo__xmlshorttag) write_str("\"/>"NL); \ + else write_str(wo__xmlclosetag); \ + wo__xmlclosetag= NULL; wo__xmlshorttag= false; } } +#define wo__CONTINUE { /* continue an XML object */ \ + if(wo__xmlshorttag) { write_str("\">"NL); wo__xmlshorttag= false; \ + /* from now on: long close tag necessary; */ } } +static int wo__lastaction= 0; // last action tag which has been set; + // 0: no action tag; 1: "create"; 2: "modify"; 3: "delete"; + // this is used only in .osc files; + +static inline void wo__author(int32_t hisver,int64_t histime, + int64_t hiscset,uint32_t hisuid,const char* hisuser) { + // write osm object author; + // must not be called if writing PBF format; + // hisver: version; 0: no author is to be written + // (necessary if o5m format); + // histime: time (seconds since 1970) + // hiscset: changeset + // hisuid: uid + // hisuser: user name + // global_fakeauthor: the author contents will be faked that way + // that the author data will be as short as + // possible; + // global fakeversion: same as global_fakeauthor, but for .osm + // format: just the version will be written; + // note that when writing o5m format, this procedure needs to be + // called even if there is no author information to be written; + // PBF and csv: this procedure is not called; + if(global_fakeauthor|global_fakeversion) { + hisver= 1; histime= 1; hiscset= 1; hisuid= 0; hisuser= ""; + } + if(wo__format==0) { // o5m + if(hisver==0 || global_dropversion) // no version number + o5_byte(0x00); + else { // version number available + o5_uvar32(hisver); + if(global_dropauthor) histime= 0; + o5_svar64(histime-o5_time); o5_time= histime; + if(histime!=0) { + // author information available + o5_svar64(hiscset-o5_cset); o5_cset= hiscset; + if(hisuid==0 || hisuser==NULL || hisuser[0]==0) + // user identification not available + stw_write("",""); + else { // user identification available + byte uidbuf[30]; + + uidbuf[o5_uvar32buf(uidbuf,hisuid)]= 0; + stw_write((const char*)uidbuf,hisuser); + } // end user identification available + } // end author information available + } // end version number available +return; + } // end o5m + // here: XML format + if(global_fakeversion) { + write_str("\" version=\"1"); +return; + } + if(hisver==0 || global_dropversion) // no version number +return; + switch(wo__format) { // depending on output format + case 11: // native XML + write_str("\" version=\""); write_uint32(hisver); + if(histime!=0 && !global_dropauthor) { + write_str("\" timestamp=\""); write_timestamp(histime); + write_str("\" changeset=\""); write_uint64(hiscset); + if(hisuid!=0 && hisuser[0]!=0) { // user information available + write_str("\" uid=\""); write_uint32(hisuid); + write_str("\" user=\""); write_xmlstr(hisuser); + } + } + break; + case 12: // pbf2osm XML + write_str("\" version=\""); write_uint32(hisver); + if(histime!=0 && !global_dropauthor) { + write_str("\" changeset=\""); write_uint64(hiscset); + if(hisuid!=0 && hisuser[0]!=0) { // user information available + write_str("\" user=\""); write_xmlstr(hisuser); + write_str("\" uid=\""); write_uint32(hisuid); + } + write_str("\" timestamp=\""); write_timestamp(histime); + } + break; + case 13: // Osmosis XML + write_str("\" version=\""); write_uint32(hisver); + if(histime!=0 && !global_dropauthor) { + write_str("\" timestamp=\""); write_timestamp(histime); + if(hisuid!=0 && hisuser[0]!=0) { // user information available + write_str("\" uid=\""); write_uint32(hisuid); + write_str("\" user=\""); write_xmlmnstr(hisuser); + } + write_str("\" changeset=\""); write_uint64(hiscset); + } + break; + case 14: // Osmium XML + write_str("\" version=\""); write_uint32(hisver); + if(histime!=0 && !global_dropauthor) { + write_str("\" changeset=\""); write_uint64(hiscset); + write_str("\" timestamp=\""); write_timestamp(histime); + if(hisuid!=0 && hisuser[0]!=0) { // user information available + write_str("\" uid=\""); write_uint32(hisuid); + write_str("\" user=\""); write_xmlstr(hisuser); + } + } + break; + } // end depending on output format + if(global_outosh) { + if(wo__lastaction==3) + write_str("\" visible=\"false"); + else + write_str("\" visible=\"true"); + } + } // end wo__author() + +static inline void wo__action(int action) { + // set one of these action tags: "create", "modify", "delete"; + // write tags only if 'global_outosc' is true; + // must only be called if writing XML format; + // action: 0: no action tag; 1: "create"; 2: "modify"; 3: "delete"; + // caution: there is no check for validity of this parameter; + static const char* starttag[]= + {"",""NL,""NL,""NL}; + static const char* endtag[]= + {"",""NL,""NL,""NL}; + + if(global_outosc && action!=wo__lastaction) { // there was a change + write_str(endtag[wo__lastaction]); // end last action + write_str(starttag[action]); // start new action + } + wo__lastaction= action; + } // end wo__action() + +//------------------------------------------------------------ + +static void wo_start(int format,bool bboxvalid, + int32_t x1,int32_t y1,int32_t x2,int32_t y2,int64_t timestamp) { + // start writing osm objects; + // format: 0: o5m; 11: native XML; + // 12: pbf2osm; 13: Osmosis; 14: Osmium; 21:csv; -1: PBF; + // bboxvalid: the following bbox coordinates are valid; + // x1,y1,x2,y2: bbox coordinates (base 10^-7); + // timestamp: file timestamp; ==0: no timestamp given; + if(format<-1 || (format >0 && format<11) || + (format >14 && format<21) || format>21) format= 0; + wo__format= format; + wo__logaction= global_outosc || global_outosh; + if(wo__format==0) { // o5m + static const byte o5mfileheader[]= {0xff,0xe0,0x04,'o','5','m','2'}; + static const byte o5cfileheader[]= {0xff,0xe0,0x04,'o','5','c','2'}; + + if(global_outo5c) + write_mem(o5cfileheader,sizeof(o5cfileheader)); + else + write_mem(o5mfileheader,sizeof(o5mfileheader)); + if(timestamp!=0) { // timestamp has been supplied + o5_byte(0xdc); // timestamp + o5_svar64(timestamp); + o5_write(); // write this object + } + if(border_active) // borders are to be applied + border_querybox(&x1,&y1,&x2,&y2); + if(border_active || bboxvalid) { + // borders are to be applied OR bbox has been supplied + o5_byte(0xdb); // border box + o5_svar32(x1); o5_svar32(y1); + o5_svar32(x2); o5_svar32(y2); + o5_write(); // write this object + } +return; + } // end o5m + if(wo__format<0) { // PBF + if(border_active) // borders are to be applied + border_querybox(&x1,&y1,&x2,&y2); + bboxvalid= bboxvalid || border_active; + pw_ini(); + pw_header(bboxvalid,x1,y1,x2,y2,timestamp); +return; + } + if(wo__format==21) { // csv + csv_headline(); +return; + } + // here: XML + if(wo__format!=14) + write_str(""NL); + else // Osmium XML + write_str(""NL); + if(global_outosc) + write_str(""NL); + if(wo__format!=12) { // bbox may be written + if(border_active) // borders are to be applied + border_querybox(&x1,&y1,&x2,&y2); + if(border_active || bboxvalid) { // borders are to be applied OR + // bbox has been supplied + if(wo__format==13) { // Osmosis + // + write_str(" "NL); + } // Osmosis + else { // not Osmosis + // + write_str("\t"NL); + } // not Osmosis + } + } // end bbox may be written + } // end wo_start() + +static void wo_end() { + // end writing osm objects; + switch(wo__format) { // depending on output format + case 0: // o5m + o5_write(); // write last object - if any + write_char(0xfe); // write o5m eof indicator + break; + case 11: // native XML + case 12: // pbf2osm XML + case 13: // Osmosis XML + case 14: // Osmium XML + wo__CLOSE + wo__action(0); + write_str(global_outosc? ""NL: ""NL); + if(wo__format>=12) + write_str(""NL); + break; + case 21: // csv + csv_write(); + // (just in case the last object has not been terminated) + break; + case -1: // PBF + pw_foot(); + break; + } // end depending on output format + } // end wo_end() + +static inline void wo_flush() { + // write temporarily stored object information; + if(wo__format==0) // o5m + o5_write(); // write last object - if any + else if(wo__format<0) // PBF format + pw_flush(); + else if(wo__format==21) // csv + csv_write(); + else // any XML output format + wo__CLOSE + write_flush(); + } // end wo_flush() + +static int wo_format(int format) { + // get or change output format; + // format: -9: return the currently used format, do not change it; + if(format==-9) // do not change the format +return wo__format; + wo_flush(); + if(format<-1 || (format >0 && format<11) || + (format >14 && format<21) || format>21) format= 0; + wo__format= format; + wo__logaction= global_outosc || global_outosh; + return wo__format; + } // end wo_format() + +static inline void wo_reset() { + // in case of o5m format, write a Reset; + // note that this is done automatically at every change of + // object type; this procedure offers to write additional Resets + // at every time you want; + if(wo__format==0) + o5_reset(); + } // end wo_reset() + +static inline void wo_node(int64_t id, + int32_t hisver,int64_t histime,int64_t hiscset, + uint32_t hisuid,const char* hisuser,int32_t lon,int32_t lat) { + // write osm node body; + // id: id of this object; + // hisver: version; 0: no author information is to be written + // (necessary if o5m format); + // histime: time (seconds since 1970) + // hiscset: changeset + // hisuid: uid + // hisuser: user name + // lon: latitude in 100 nanodegree; + // lat: latitude in 100 nanodegree; + if(wo__format==0) { // o5m + o5_write(); // write last object - if any + o5_type(0); + o5_byte(0x10); // data set id for node + o5_svar64(id-o5_id); o5_id= id; + wo__author(hisver,histime,hiscset,hisuid,hisuser); + o5_svar32(lon-o5_lon); o5_lon= lon; + o5_svar32(lat-o5_lat); o5_lat= lat; +return; + } // end o5m + if(wo__format<0) { // PBF + pw_node(id,hisver,histime,hiscset,hisuid,hisuser,lon,lat); +return; + } + if(wo__format==21) { // csv + char s[25]; + + if(csv_key_otype) + csv_add("@otype","0"); + if(csv_key_oname) + csv_add("@oname",ONAME(0)); + if(csv_key_id) { + int64toa(id,s); + csv_add("@id",s); + } + if(csv_key_version) { + uint32toa(hisver,s); + csv_add("@version",s); + } + if(csv_key_timestamp) { + write_createtimestamp(histime,s); + csv_add("@timestamp",s); + } + if(csv_key_changeset) { + int64toa(hiscset,s); + csv_add("@changeset",s); + } + if(csv_key_uid) { + uint32toa(hisuid,s); + csv_add("@uid",s); + } + if(csv_key_user) + csv_add("@user",hisuser); + if(csv_key_lon) { + write_createsfix7o(lon,s); + csv_add("@lon",s); + } + if(csv_key_lat) { + write_createsfix7o(lat,s); + csv_add("@lat",s); + } +return; + } + // here: XML format + wo__CLOSE + if(wo__logaction) + wo__action(hisver==1? 1: 2); + switch(wo__format) { // depending on output format + case 11: // native XML + write_str("\t=0) lon= (lon+5)/10; + else lon= (lon-5)/10; + if(lat>=0) lat= (lat+5)/10; + else lat= (lat-5)/10; + write_str("\" lon=\""); write_sfix6o(lon); + write_str("\" lat=\""); write_sfix6o(lat); + wo__xmlclosetag= " "NL; // preset close tag + break; + } // end depending on output format + wo__xmlshorttag= true; // (default) + } // end wo_node() + +static inline void wo_node_close() { + // complete writing an OSM node; + if(wo__format<0) + pw_node_close(); + else if(wo__format==21) + csv_write(); + } // end wo_node_close() + +static inline void wo_way(int64_t id, + int32_t hisver,int64_t histime,int64_t hiscset, + uint32_t hisuid,const char* hisuser) { + // write osm way body; + // id: id of this object; + // hisver: version; 0: no author information is to be written + // (necessary if o5m format); + // histime: time (seconds since 1970) + // hiscset: changeset + // hisuid: uid + // hisuser: user name + if(wo__format==0) { // o5m + o5_write(); // write last object - if any + o5_type(1); + o5_byte(0x11); // data set id for way + o5_svar64(id-o5_id); o5_id= id; + wo__author(hisver,histime,hiscset,hisuid,hisuser); + o5_markref(0); +return; + } // end o5m + if(wo__format<0) { // PBF + pw_way(id,hisver,histime,hiscset,hisuid,hisuser); +return; + } + if(wo__format==21) { // csv + char s[25]; + + if(csv_key_otype) + csv_add("@otype","1"); + if(csv_key_oname) + csv_add("@oname",ONAME(1)); + if(csv_key_id) { + int64toa(id,s); + csv_add("@id",s); + } + if(csv_key_version) { + uint32toa(hisver,s); + csv_add("@version",s); + } + if(csv_key_timestamp) { + write_createtimestamp(histime,s); + csv_add("@timestamp",s); + } + if(csv_key_changeset) { + int64toa(hiscset,s); + csv_add("@changeset",s); + } + if(csv_key_uid) { + uint32toa(hisuid,s); + csv_add("@uid",s); + } + if(csv_key_user) + csv_add("@user",hisuser); +return; + } + // here: XML format + wo__CLOSE + if(wo__logaction) + wo__action(hisver==1? 1: 2); + switch(wo__format) { // depending on output format + case 11: // native XML + write_str("\t2 || wo__format<0) +return; + if(wo__format==0) { // o5m (.o5c) + o5_write(); // write last object - if any + o5_type(otype); + o5_byte(0x10+otype); // data set id + o5_svar64(id-o5_id); o5_id= id; + wo__author(hisver,histime,hiscset,hisuid,hisuser); + } // end o5m (.o5c) + else { // .osm (.osc) + wo__CLOSE + if(wo__logaction) + wo__action(3); + if(wo__format>=13) write_str(" <"); else write_str("\t<"); + write_str(ONAME(otype)); + write_str(" id=\""); write_sint64(id); + if(global_fakelonlat) + write_str("\" lat=\"0\" lon=\"0"); + wo__author(hisver,histime,hiscset,hisuid,hisuser); + wo__xmlclosetag= "\"/>"NL; // preset close tag + wo__xmlshorttag= false; // (default) + wo__CLOSE // write close tag + } // end .osm (.osc) + } // end wo_delete() + +static inline void wo_noderef(int64_t noderef) { + // write osm object node reference; + if(wo__format==0) { // o5m + o5_svar64(noderef-o5_ref[0]); o5_ref[0]= noderef; + o5_markref(1); +return; + } // end o5m + if(wo__format<0) { // PBF + pw_way_ref(noderef); +return; + } + if(wo__format==21) // csv +return; + // here: XML format + wo__CONTINUE + switch(wo__format) { // depending on output format + case 11: // native XML + case 12: // pbf2osm XML + write_str("\t\t"NL); + break; + case 13: // Osmosis XML + case 14: // Osmium XML + write_str(" "NL); + break; + } // end depending on output format + } // end wo_noderef() + +static inline void wo_ref(int64_t refid,int reftype, + const char* refrole) { + // write osm object reference; + if(wo__format==0) { // o5m + char o5typerole[4000]; + + o5_svar64(refid-o5_ref[reftype]); o5_ref[reftype]= refid; + o5typerole[0]= reftype+'0'; + strmcpy(o5typerole+1,refrole,sizeof(o5typerole)-1); + stw_write(o5typerole,NULL); + o5_markref(1); +return; + } // end o5m + if(wo__format<0) { // PBF + pw_relation_ref(refid,reftype,refrole); +return; + } + if(wo__format==21) // csv +return; + // here: XML format + wo__CONTINUE + switch(wo__format) { // depending on output format + case 11: // native XML + case 12: // pbf2osm XML + if(reftype==0) + write_str("\t\t"NL); + break; + case 13: // Osmosis XML + case 14: // Osmium XML + if(reftype==0) + write_str(" "NL); + break; + } // end depending on output format + } // end wo_ref() + +static inline void wo_node_keyval(const char* key,const char* val) { + // write an OSM node object's keyval; + if(wo__format==0) { // o5m + #if 0 // not used because strings would not be transparent anymore + if(key[1]=='B' && strcmp(key,"bBox")==0 && strchr(val,',')!=0) + // value is assumed to be dynamic, hence it should not be + // stored in string list; + // mark string pair as 'do-not-store'; + key= "\xff""bBox"; // 2012-10-14 + #endif + stw_write(key,val); +return; + } // end o5m + if(wo__format<0) { // PBF + pw_node_keyval(key,val); +return; + } + if(wo__format==21) { // csv + csv_add(key,val); +return; + } + // here: XML format + wo__CONTINUE + switch(wo__format) { // depending on output format + case 11: // native XML + write_str("\t\t"NL); + break; + case 12: // pbf2osm XML + write_str("\t\t"NL); + break; + case 13: // Osmosis XML + case 14: // Osmium XML + write_str(" "NL); + break; + } // end depending on output format + } // end wo_node_keyval() + +static inline void wo_wayrel_keyval(const char* key,const char* val) { + // write an OSM way or relation object's keyval; + if(wo__format==0) { // o5m + stw_write(key,val); +return; + } // end o5m + if(wo__format<0) { // PBF + pw_wayrel_keyval(key,val); +return; + } + if(wo__format==21) { // csv + csv_add(key,val); +return; + } + // here: XML format + wo__CONTINUE + switch(wo__format) { // depending on output format + case 11: // native XML + write_str("\t\t"NL); + break; + case 12: // pbf2osm XML + write_str("\t\t"NL); + break; + case 13: // Osmosis XML + case 14: // Osmium XML + write_str(" "NL); + break; + } // end depending on output format + } // end wo_wayrel_keyval() + + +static inline void wo_addbboxtags(bool fornode, + int32_t x_min, int32_t y_min,int32_t x_max, int32_t y_max) { + // adds tags for bbox and box area if requested by + // global_addbbox, global_addbboxarea resp. global_addbboxweight; + // fornode: add the tag(s) to a node, not to way/rel; + char s[84],*sp; + int64_t area; + + if(global_addbbox) { // add bbox tags + sp= s; + sp= write_createsfix7o(x_min,sp); + *sp++= ','; + sp= write_createsfix7o(y_min,sp); + *sp++= ','; + sp= write_createsfix7o(x_max,sp); + *sp++= ','; + sp= write_createsfix7o(y_max,sp); + if(fornode) + wo_node_keyval("bBox",s); + else + wo_wayrel_keyval("bBox",s); + } // add bbox tags + if(global_addbboxarea|global_addbboxweight) { + // add bbox area tags OR add bbox weight tags + area= (int64_t)(x_max-x_min)*(int64_t)(y_max-y_min)/ + cosrk((y_min+y_max)/2); + if(global_addbboxarea) { // add bbox area tags + write_createsint64(area,s); + if(fornode) + wo_node_keyval("bBoxArea",s); + else + wo_wayrel_keyval("bBoxArea",s); + } // add bbox area tags + if(global_addbboxweight) { // add bbox weight tags + write_createsint64(msbit(area),s); + if(fornode) + wo_node_keyval("bBoxWeight",s); + else + wo_wayrel_keyval("bBoxWeight",s); + } // add bbox weight tags + } // add bbox area tags OR add bbox weight tags + } // end wo_addbboxtags() + +//------------------------------------------------------------ +// end Module wo_ write osm module +//------------------------------------------------------------ + + + +//------------------------------------------------------------ +// Module oo_ osm to osm module +//------------------------------------------------------------ + +// this module provides procedures which read osm objects, +// process them and write them as osm objects, using module wo_; +// that goes for .osm format as well as for .o5m format; +// as usual, all identifiers of a module have the same prefix, +// in this case 'oo'; an underline will follow in case of a +// global accessible object, two underlines in case of objects +// which are not meant to be accessed from outside this module; +// the sections of private and public definitions are separated +// by a horizontal line: ---- + +static void oo__rrprocessing(int* maxrewindp) { + // process temporary relation reference file; + // the file must have been written; this procedure processes + // the interrelation references of this file and updates + // the hash table of module hash_ accordingly; + // maxrewind: maximum number of rewinds; + // return: + // maxrewind: <0: maximum number of rewinds was not sufficient; + int changed; + // number of relations whose flag has been changed, i.e., + // the recursive processing will continue; + // if none of the relations' flags has been changed, + // this procedure will end; + int h; + int64_t relid; // relation id; + int64_t refid; // interrelation reference id; + bool flag; + + h= 0; + relid= 0; flag= false; + while(*maxrewindp>=0) { // for every recursion + changed= 0; + if(rr_rewind()) // could not rewind + break; + for(;;) { // for every reference + for(;;) { // get next id + if(rr_read(&refid)) + goto rewind; // if at file end, rewind + if(refid!=0) + break; + // here: a relation id will follow + rr_read(&relid); // get the relation id + flag= hash_geti(2,relid); // get the related flag + } // end get next id + if(flag) // flag already set + continue; // go on until next relation + if(!hash_geti(2,refid)) // flag of reference is not set + continue; // go on and examine next reference of this relation + hash_seti(2,relid); // set flag of this relation + flag= true; + changed++; // memorize that we changed a flag + } // end for every reference + rewind: + if(loglevel>0) fprintf(stderr, + "Interrelational hierarchy %i: %i dependencies.\n",++h,changed); + if(changed==0) // no changes have been made in last recursion + break; // end the processing + (*maxrewindp)--; + } // end for every recursion + } // end oo__rrprocessing() + +static byte oo__whitespace[]= { + 0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0, // HT LF VT FF CR + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // SPC + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; +#define oo__ws(c) (oo__whitespace[(byte)(c)]) +static byte oo__whitespacenul[]= { + 1,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0, // NUL HT LF VT FF CR + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, // SPC / + 0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0, // < + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; +#define oo__wsnul(c) (oo__whitespacenul[(byte)(c)]) +static byte oo__letter[]= { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1, + 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; +#define oo__le(c) (oo__letter[(byte)(c)]) + +static const uint8_t* oo__hexnumber= (uint8_t*) + // convert a hex character to a number + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x00\x00\x00\x00\x00\x00" + "\x00\x0a\x0b\x0c\x0d\x0e\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x0a\x0b\x0c\x0d\x0e\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; + +static inline uint32_t oo__strtouint32(const char* s) { + // read a number and convert it to an unsigned 32-bit integer; + // return: number; + int32_t i; + byte b; + + i= 0; + for(;;) { + b= (byte)(*s++ -'0'); + if(b>=10) + break; + i= i*10+b; + } + return i; + } // end oo__strtouint32() + +#if 0 // presently unused +static inline int32_t oo__strtosint32(const char* s) { + // read a number and convert it to a signed 64-bit integer; + // return: number; + int sign; + int32_t i; + byte b; + + if(*s=='-') { s++; sign= -1; } else sign= 1; + i= 0; + for(;;) { + b= (byte)(*s++ -'0'); + if(b>=10) + break; + i= i*10+b; + } + return i*sign; + } // end oo__strtosint32() +#endif + +static inline int64_t oo__strtosint64(const char* s) { + // read a number and convert it to a signed 64-bit integer; + // return: number; + int sign; + int64_t i; + byte b; + + if(*s=='-') { s++; sign= -1; } else sign= 1; + i= 0; + for(;;) { + b= (byte)(*s++ -'0'); + if(b>=10) + break; + i= i*10+b; + } + return i*sign; + } // end oo__strtosint64() + +static const int32_t oo__nildeg= 2000000000L; + +static inline int32_t oo__strtodeg(char* s) { + // read a number which represents a degree value and + // convert it to a fixpoint number; + // s[]: string with the number between -180 and 180, + // e.g. "-179.99", "11", ".222"; + // return: number in 10 millionth degrees; + // =='oo__nildeg': syntax error; + static const long di[]= {10000000L,10000000L,1000000L,100000L, + 10000L,1000L,100L,10L,1L}; + static const long* dig= di+1; + int sign; + int d; // position of decimal digit; + long k; + char c; + + if(*s=='-') { s++; sign= -1; } else sign= 1; + if(!isdig(*s) && *s!='.') +return border__nil; + k= 0; + d= -1; + do { // for every digit + c= *s++; + if(c=='.') { d= 0; continue; } // start fractional part + else if(!isdig(c) || c==0) + break; + k= k*10+c-'0'; + if(d>=0) d++; + } while(d<7); // end for every digit + k*= dig[d]*sign; + return k; + } // end oo__strtodeg() + +static inline int64_t oo__strtimetosint64(const char* s) { + // read a timestamp in OSM format, e.g.: "2010-09-30T19:23:30Z", + // and convert it to a signed 64-bit integer; + // also allowed: relative time to NOW, e.g.: "NOW-86400", + // which means '24 hours ago'; + // return: time as a number (seconds since 1970); + // ==0: syntax error; + if(s[0]=='N') { // presumably a relative time to 'now' + if(s[1]!='O' || s[2]!='W' || (s[3]!='+' && s[3]!='-') || + !isdig(s[4])) // wrong syntax +return 0; + s+= 3; // jump over "NOW" + if(*s=='+') s++; // jump over '+', if any +return time(NULL)+oo__strtosint64(s); + } // presumably a relative time to 'now' + if((s[0]!='1' && s[0]!='2') || + !isdig(s[1]) || !isdig(s[2]) || !isdig(s[3]) || + s[4]!='-' || !isdig(s[5]) || !isdig(s[6]) || + s[7]!='-' || !isdig(s[8]) || !isdig(s[9]) || + s[10]!='T' || !isdig(s[11]) || !isdig(s[12]) || + s[13]!=':' || !isdig(s[14]) || !isdig(s[15]) || + s[16]!=':' || !isdig(s[17]) || !isdig(s[18]) || + s[19]!='Z') // wrong syntax +return 0; + /* regular timestamp */ { + struct tm tm; + + tm.tm_isdst= 0; + tm.tm_year= + (s[0]-'0')*1000+(s[1]-'0')*100+(s[2]-'0')*10+(s[3]-'0')-1900; + tm.tm_mon= (s[5]-'0')*10+s[6]-'0'-1; + tm.tm_mday= (s[8]-'0')*10+s[9]-'0'; + tm.tm_hour= (s[11]-'0')*10+s[12]-'0'; + tm.tm_min= (s[14]-'0')*10+s[15]-'0'; + tm.tm_sec= (s[17]-'0')*10+s[18]-'0'; + #if __WIN32__ + // use replcement for timegm() because Windows does not know it + #if 0 + if(original_timezone==&original_timezone_none) { + original_timezone= getenv("TZ"); + putenv("TZ="); + tzset(); + } + #endif + //DPv(timezone %lli,timezone) +return mktime(&tm)-timezone; + #else +return timegm(&tm); + #endif + } // regular timestamp + } // end oo__strtimetosint64() + +static inline void oo__xmltostr(char* s) { + // read an xml string and convert is into a regular UTF-8 string, + // for example: "Mayer's" -> "Mayer's"; + char* t; // pointer in overlapping target string + char c; + uint32_t u; + + for(;;) { // for all characters, until first '&' or string end; + c= *s; + if(c==0) // no character to convert +return; + if(c=='&') + break; + s++; + } + t= s; + for(;;) { // for all characters after the first '&' + c= *s++; + if(c==0) // at the end of string + break; + if(c!='&') { + *t++= c; + continue; + } + c= *s; + if(c=='#') { // numeric value + c= *++s; + if(c=='x') { // hex value + s++; + u= 0; + for(;;) { + c= *s++; + if(c==';' || c==0) + break; + u= (u<<4)+oo__hexnumber[(byte)c]; + } + } // end hex value + else { // decimal value + u= 0; + for(;;) { + c= *s++; + if(c==';' || c==0) + break; + u= u*10+c-'0'; + } + } // end decimal value + if(u<128) // 1 byte sufficient + *t++= (char)u; + else if(u<2048) { // 2 bytes sufficient + *t++= (u>>6)|0xc0; *t++= (u&0x3f)|0x80; } + else if(u<65536) { // 3 bytes sufficient + *t++= (u>>12)|0xe0; *t++= ((u>>6)&0x3f)|0x80; + *t++= (u&0x3f)|0x80; } + else { // 4 bytes necessary + *t++= ((u>>18)&0x07)|0xf0; *t++= ((u>>12)&0x3f)|0x80; + *t++= ((u>>6)&0x3f)|0x80; *t++= (u&0x3f)|0x80; } + } // end numeric value + else if(strzcmp(s,"quot;")==0) { + s+= 5; *t++= '\"'; } + else if(strzcmp(s,"apos;")==0) { + s+= 5; *t++= '\''; } + else if(strzcmp(s,"amp;")==0) { + s+= 4; *t++= '&'; } + else if(strzcmp(s,"lt;")==0) { + s+= 3; *t++= '<'; } + else if(strzcmp(s,"gt;")==0) { + s+= 3; *t++= '>'; } + else { // unknown escape code + *t++= '&'; + } + } // end for all characters after the first '&' + *t= 0; // terminate target string + //fprintf(stderr,"Z %s\n",s0);sleep(1);//,, + } // end oo__xmltostr() + +static bool oo__xmlheadtag; // currently, we are inside an xml start tag, + // maybe a short tag, e.g. or + // (the second example is a so-called short tag) +static char* oo__xmlkey,*oo__xmlval; // return values of oo__xmltag + +static inline bool oo__xmltag() { + // read the next xml key/val and return them both; + // due to performance reasons, global and module global variables + // are used; + // read_bufp: address at which the reading begins; + // oo__xmlheadtag: see above; + // return: no more xml keys/vals to read inside the outer xml tag; + // oo__xmlkey,oo__xmlval: newest xml key/val which have been read; + // "","": encountered the end of an + // enclosed xml tag; + char xmldelim; + char c; + + for(;;) { // until break + while(!oo__wsnul(*read_bufp)) read_bufp++; + // find next whitespace or null character or '/' + while(oo__ws(*read_bufp)) read_bufp++; + // find first character after the whitespace(s) + c= *read_bufp; + if(c==0) { + oo__xmlkey= oo__xmlval= ""; +return true; + } + else if(c=='/') { + oo__xmlkey= oo__xmlval= ""; + c= *++read_bufp; + read_bufp++; + if(c=='>') { // short tag ends here + if(oo__xmlheadtag) { + // this ending short tag is the object's tag + oo__xmlheadtag= false; +return true; + } +return false; + } // end short tag ands here + continue; + } + else if(c=='<') { + oo__xmlheadtag= false; + if(*++read_bufp=='/' && ( + (c= *++read_bufp)=='n' || c=='w' || c=='r') ) { + // this has been long tag which is ending now + while(!oo__wsnul(*read_bufp)) read_bufp++; + // find next whitespace + oo__xmlkey= oo__xmlval= ""; +return true; + } + continue; + } + oo__xmlkey= (char*)read_bufp; + while(oo__le(*read_bufp)) read_bufp++; + if(*read_bufp!='=') { + oo__xmlkey= ""; + continue; + } + *read_bufp++= 0; + if(*read_bufp!='\"' && *read_bufp!='\'') + continue; + xmldelim= (char)*read_bufp; + oo__xmlval= (char*)(++read_bufp); + for(;;) { + c= *read_bufp; + if(c==xmldelim) + break; + if(c==0) { + oo__xmlkey= oo__xmlval= ""; +return true; + } + read_bufp++; + } + *read_bufp++= 0; + break; + } // end until break + oo__xmltostr(oo__xmlkey); + oo__xmltostr(oo__xmlval); + return false; + } // end oo__xmltag() + +static int oo__error= 0; // error number which will be returned when + // oo_main() terminates normal; +typedef struct { + read_info_t* ri; // file handle for input files + read_info_t* riph; // file handle for input files; + // this is a copy of .ri because it may be necessary to reaccess + // a file which has already been logically closed; + // used by the procedures oo__rewind() and oo__closeall(); + int format; // input file format; + // ==-9: unknown; ==0: o5m; ==10: xml; ==-1: pbf; + str_info_t* str; // string unit handle (if o5m format) + uint64_t tyid; // type/id of last read osm object of this file + uint32_t hisver; // OSM object version; needed for creating diff file + const char* filename; + bool endoffile; + int deleteobject; // replacement for .osc tag + // 0: not to delete; 1: delete this object; 2: delete from now on; + int deleteobjectjump; // same as before but as save value for jumps + // 0: not to delete; 1: delete this object; 2: delete from now on; + bool subtract; // this file is to be subtracted, i.e., the + // meaning of 'deleteobject' will be treated inversely; + int64_t o5id; // for o5m delta coding + int32_t o5lon,o5lat; // for o5m delta coding + int64_t o5histime; // for o5m delta coding + int64_t o5hiscset; // for o5m delta coding + int64_t o5rid[3]; // for o5m delta coding + } oo__if_t; +static oo__if_t oo__if[global_fileM]; +static oo__if_t* oo__ifp= oo__if; // currently used element in oo__if[] +#define oo__ifI (oo__ifp-oo__if) // index +static oo__if_t* oo__ife= oo__if; // logical end of elements in oo__if[] +static oo__if_t* oo__ifee= oo__if+global_fileM; + // physical end of oo_if[] +static int oo_ifn= 0; // number of currently open files + +static bool oo__bbvalid= false; + // the following bbox coordinates are valid; +static int32_t oo__bbx1= 0,oo__bby1= 0,oo__bbx2= 0,oo__bby2= 0; + // bbox coordinates (base 10^-7) of the first input file; +static int64_t oo__timestamp= 0; + // file timestamp of the last input file which has a timestamp; + // ==0: no file timestamp given; +static bool oo__alreadyhavepbfobject= false; + +static void oo__mergebbox(int32_t bbx1,int32_t bby1, + int32_t bbx2,int32_t bby2) { + // merge new bbox coordinates to existing ones; + // if there are no bbox coordinates at present, + // just store the new coordinates; + // bbx1 .. bby2: border box coordinates to merge; + // return: + // oo__bbvalid: following border box information is valid; + // oo__bbx1 .. oo__bby2: border box coordinates; + + if(!oo__bbvalid) { // not yet any bbox stored + // just store the new coordinates as bbox + oo__bbx1= bbx1; + oo__bby1= bby1; + oo__bbx2= bbx2; + oo__bby2= bby2; + oo__bbvalid= true; + } // not yet any bbox stored + else { // there is already a bbox + // merge the new coordinates with the existing bbox + if(bbx1oo__bbx2) oo__bbx2= bbx2; + if(bby2>oo__bby2) oo__bby2= bby2; + } // there is already a bbox + } // oo__mergebbox() + +static void oo__findbb() { + // find timestamp and border box in input file; + // return: + // oo__bbvalid: following border box information is valid; + // oo__bbx1 .. oo__bby2: border box coordinates; + // read_bufp will not be changed; + byte* bufp,*bufe,*bufe1; + int32_t bbx1= 0,bby1= 0,bbx2= 0,bby2= 0; + // bbox coordinates (base 10^-7) + + bbx1= bby1= bbx2= bby2= 0; + read_input(); + bufp= read_bufp; bufe= read_bufe; + if(oo__ifp->format==0) { // o5m + byte b; // latest byte which has been read + int l; + + while(bufp=0x10 && b<=0x12)) // regular dataset id +return; + if(b>=0xf0) { // single byte dataset + bufp++; + continue; + } // end single byte dataset + // here: non-object multibyte dataset + if(b==0xdc) { // timestamp + bufp++; + l= pbf_uint32(&bufp); + bufe1= bufp+l; if(bufe1>=bufe) bufe1= bufe; + if(bufp=bufe) bufe1= bufe; + if(bufpformat>0) { // osm xml + char* sp; + char c1,c2,c3; // next available characters + + while(bufp + int l; + char c; + + sp++; // jump over '<' + if(strzcmp(sp,"osmAugmentedDiff")==0) + global_mergeversions= true; + for(;;) { // jump over "osm ", "osmChange ", "osmAugmentedDiff" + c= *sp; + if(oo__wsnul(c)) + break; + sp++; + } + for(;;) { // for every word in 'osm' + c= *sp; + if(c=='/' || c=='<' || c=='>' || c==0) + break; + if(oo__ws(c)) { + sp++; + continue; + } + if((l= strzlcmp(sp,"timestamp="))>0 && + (sp[10]=='\"' || sp[10]=='\'') && isdig(sp[11])) { + sp+= l+1; + oo__timestamp= oo__strtimetosint64(sp); + } + for(;;) { // find next whitespace or '<' + c= *sp; + if(oo__wsnul(c)) + break; + sp++; + } + } // end for every word in 'osm' + bufp++; + continue; + } // " + // + uint32_t bboxcomplete; // flags for bbx1 .. bby2 + int l; + char c; + + bboxcomplete= 0; + sp++; // jump over '<' + for(;;) { // jump over "bounds ", resp. "bound " + c= *sp; + if(oo__wsnul(c)) + break; + sp++; + } + for(;;) { // for every word in 'bounds' + c= *sp; + if(c=='/' || c=='<' || c=='>' || c==0) + break; + if(oo__ws(c) || c==',') { + sp++; + continue; + } + if((l= strzlcmp(sp,"box=\""))>0 || + (l= strzlcmp(sp,"box=\'"))>0) { + sp+= l; + c= *sp; + } + if((l= strzlcmp(sp,"minlat=\""))>0 || + (l= strzlcmp(sp,"minlat=\'"))>0 || + ((isdig(c) || c=='-' || c=='.') && (bboxcomplete&2)==0)) { + sp+= l; + bby1= oo__strtodeg(sp); + if(bby1!=oo__nildeg) bboxcomplete|= 2; + } + else if((l= strzlcmp(sp,"minlon=\""))>0 || + (l= strzlcmp(sp,"minlon=\'"))>0 || + ((isdig(c) || c=='-' || c=='.') && (bboxcomplete&1)==0)) { + sp+= l; + bbx1= oo__strtodeg(sp); + if(bbx1!=oo__nildeg) bboxcomplete|= 1; + } + else if((l= strzlcmp(sp,"maxlat=\""))>0 || + (l= strzlcmp(sp,"maxlat=\'"))>0 || + ((isdig(c) || c=='-' || c=='.') && (bboxcomplete&8)==0)) { + sp+= l; + bby2= oo__strtodeg(sp); + if(bby2!=oo__nildeg) bboxcomplete|= 8; + } + else if((l= strzlcmp(sp,"maxlon=\""))>0 || + (l= strzlcmp(sp,"maxlon=\'"))>0 || + ((isdig(c) || c=='-' || c=='.') && (bboxcomplete&4)==0)) { + sp+= l; + bbx2= oo__strtodeg(sp); + if(bbx2!=oo__nildeg) bboxcomplete|= 4; + } + for(;;) { // find next blank or comma + c= *sp; + if(oo__wsnul(c) || c==',') + break; + sp++; + } + } // end for every word in 'bounds' + if(bboxcomplete==15) + oo__mergebbox(bbx1,bby1,bbx2,bby2); + bufp++; + continue; + } // bounds + else { + bufp++; + continue; + } + } // end for all bytes of the file + } // end osm xml + else if(oo__ifp->format==-1) { // pbf + //pb_input(); + if(pb_type==8) { // pbf header + if(pb_bbvalid) + oo__mergebbox(pb_bbx1,pb_bby1,pb_bbx2,pb_bby2); + if(pb_filetimestamp!=0) + oo__timestamp= pb_filetimestamp; + } // end pbf header + else + oo__alreadyhavepbfobject= true; + } // end pbf + } // end oo__findbb() + +static inline int oo__gettyid() { + // get tyid of the next object in the currently processed input file; + // tyid is a combination of object type and id: we take the id and + // add UINT64_C(0x0800000000000000) for nodes, + // UINT64_C(0x1800000000000000) for ways, and + // UINT64_C(0x2800000000000000) for relations; + // if global_diff is set, besides tyid the hisver is retrieved too; + // oo__ifp: handle of the currently processed input file; + // return: ==0: ok; !=0: could not get tyid because starting object + // is not an osm object; + // oo__ifp->tyid: tyid of the starting osm object; + // if there is not an osm object starting at + // read_bufp, *iyidp remains unchanged; + // oo__ifp->hisver: only if global_diff; version of next object; + static const uint64_t idoffset[]= {UINT64_C(0x0800000000000000), + UINT64_C(0x1800000000000000),UINT64_C(0x2800000000000000), + 0,0,0,0,0,0,0,0,0,0,0,0,0,UINT64_C(0x0800000000000000), + UINT64_C(0x1800000000000000),UINT64_C(0x2800000000000000)}; + int format; + + format= oo__ifp->format; + if(format==0) { // o5m + int64_t o5id; + byte* p,b; + int l; + + o5id= oo__ifp->o5id; + p= read_bufp; + while(p=0x10 && b<=0x12) { // osm object is starting here + oo__ifp->tyid= idoffset[b]; + pbf_intjump(&p); // jump over length information + oo__ifp->tyid+= o5id+pbf_sint64(&p); + if(global_diff) + oo__ifp->hisver= pbf_uint32(&p); +return 0; + } + if(b>=0xf0) { // single byte + if(b==0xff) // this is an o5m Reset object + o5id= 0; + continue; + } + // here: unknown o5m object + l= pbf_uint32(&p); // get length of this object + p+= l; // jump over this object; + } +return 1; + } + else if(format>0) { // 10: osm xml + char* s; + uint64_t r; + + s= (char*)read_bufp; + for(;;) { // for every byte in XML object + while(*s!='<' && *s!=0) s++; + if(*s==0) + break; + s++; + if(*s=='n' && s[1]=='o') r= UINT64_C(0x0800000000000000); + else if(*s=='w'&& s[1]=='a') r= UINT64_C(0x1800000000000000); + else if(*s=='r'&& s[1]=='e') r= UINT64_C(0x2800000000000000); + else + continue; + do { + s++; + if(*s==0) + break; + } while(*s!=' '); + while(*s==' ') s++; + if(s[0]=='i' && s[1]=='d' && s[2]=='=' && + (s[3]=='\"' || s[3]=='\'')) { // found id + oo__ifp->tyid= r+oo__strtosint64(s+4); + if(!global_diff) +return 0; + oo__ifp->hisver= 0; + for(;;) { + if(*s=='>' || *s==0) +return 0; + if(s[0]==' ' && s[1]=='v' && s[2]=='e' && s[3]=='r' && + s[4]=='s' && s[5]=='i' && s[6]=='o' && s[7]=='n' && + s[8]=='=' && (s[9]=='\"' || s[9]=='\'') && isdig(s[10])) { + // found version + oo__ifp->hisver= oo__strtouint32(s+10); +return 0; + } + s++; + } +return 0; + } // end found id + } // end for every byte in XML object +return 1; + } + else if(format==-1) { // pbf + while(pb_type>2) { // not an OSM object + pb_input(false); + oo__alreadyhavepbfobject= true; + } + if((pb_type & 3)!=pb_type) // still not an osm object +return 1; + oo__ifp->tyid= idoffset[pb_type]+pb_id; + oo__ifp->hisver= pb_hisver; +return 0; + } +return 2; // (unknown format) + } // end oo__gettyid() + +static inline int oo__getformat() { + // determine the formats of all opened files of unknown format + // and store these determined formats; + // do some intitialization for the format, of necessary; + // oo__if[].format: !=-9: do nothing for this file; + // return: 0: ok; !=0: error; + // 5: too many pbf files; + // this is, because the module pbf (see above) + // does not have multi-client capabilities; + // oo__if[].format: input file format; ==0: o5m; ==10: xml; ==-1: pbf; + static int pbffilen= 0; // number of pbf input files; + oo__if_t* ifptemp; + byte* bufp; + #define bufsp ((char*)bufp) // for signed char + + ifptemp= oo__ifp; + oo__ifp= oo__if; + while(oo__ifpri!=NULL && oo__ifp->format==-9) { + // format not yet determined + read_switch(oo__ifp->ri); + if(read_bufp[0]==0xef && read_bufp[1]==0xbb && + read_bufp[2]==0xbf && read_bufp[3]=='<') + // UTF-8 BOM detected + read_bufp+= 3; // jump over BOM + if(read_bufp>=read_bufe) { // file empty + PERRv("file empty: %.80s",oo__ifp->filename) +return 2; + } + bufp= read_bufp; + if(bufp[0]==0 && bufp[1]==0 && bufp[2]==0 && + bufp[3]>8 && bufp[3]<20) { // presumably .pbf format + if(++pbffilen>1) { // pbf + PERR("more than one .pbf input file is not allowed."); +return 5; + } + oo__ifp->format= -1; + pb_ini(); + pb_input(false); + oo__alreadyhavepbfobject= true; + } + else if(strzcmp(bufsp,"format= 10; + } + else if(bufp[0]==0xff && bufp[1]==0xe0 && ( + strzcmp(bufsp+2,"\x04""o5m2")==0 || + strzcmp(bufsp+2,"\x04""o5c2")==0 )) { + // presumably .o5m format + oo__ifp->format= 0; + oo__ifp->str= str_open(); + // call some initialization of string read module + } + else if((bufp[0]==0xff && bufp[1]>=0x10 && bufp[1]<=0x12) || + (bufp[0]==0xff && bufp[1]==0xff && + bufp[2]>=0x10 && bufp[2]<=0x12) || + (bufp[0]==0xff && read_bufe==read_bufp+1)) { + // presumably shortened .o5m format + if(loglevel>=2) + fprintf(stderr,"osmconvert: Not a standard .o5m file header " + "%.80s\n",oo__ifp->filename); + oo__ifp->format= 0; + oo__ifp->str= str_open(); + // call some initialization of string read module + } + else { // unknown file format + PERRv("unknown file format: %.80s",oo__ifp->filename) +return 3; + } + oo__findbb(); + oo__ifp->tyid= 0; + oo__ifp->hisver= 0; + oo__gettyid(); + // initialize tyid of the currently used input file + } // format not yet determined + oo__ifp++; + } // for all input files + oo__ifp= ifptemp; + if(loglevel>0 && oo__timestamp!=0) { + char s[30]; // timestamp as string + + write_createtimestamp(oo__timestamp,s); + fprintf(stderr,"osmconvert: File timestamp: %s\n",s); + } + if(global_timestamp!=0) // user wants a new file timestamp + oo__timestamp= global_timestamp; + return 0; + #undef bufsp + } // end oo__getformat() + +static uint64_t oo__tyidold= 0; // tyid of the last written object; + +static inline void oo__switch() { + // determine the input file with the lowest tyid + // and switch to it + oo__if_t* ifp,*ifpmin; + uint64_t tyidmin,tyidold,tyid; + + // update tyid of the currently used input file and check sequence + if(oo__ifp!=NULL) { // handle of current input file is valid + tyidold= oo__ifp->tyid; + if(oo__gettyid()==0) { // new tyid is valid +//DPv(got %llx %s,oo__ifp->tyid,oo__ifp->filename) + if(oo__ifp->tyid>60; + id= ((int64_t)(tyidold & UINT64_C(0xfffffffffffffff)))- + INT64_C(0x800000000000000); + WARNv("wrong order at %s %"PRIi64" in file %s", + ONAME(ty),id,oo__ifp->filename) + ty= oo__ifp->tyid>>60; + id= ((int64_t)(oo__ifp->tyid & UINT64_C(0xfffffffffffffff)))- + INT64_C(0x800000000000000); + WARNv("next object is %s %"PRIi64,ONAME(ty),id) + } // wrong sequence + } // new tyid is valid + } // end handle of current input file is valid + + // find file with smallest tyid + tyidmin= UINT64_C(0xffffffffffffffff); + ifpmin= oo__ifp; + // default; therefore we do not switch in cases we do not + // find a minimum + ifp= oo__ife; + while(ifp>oo__if) { + ifp--; + if(ifp->ri!=NULL) { // file handle is valid +//DPv(have %llx %s,ifp->tyid,ifp->filename) + tyid= ifp->tyid; + if(tyidri); + str_switch(oo__ifp->str); + } +//DPv(chose %llx %s,oo__ifp->tyid,oo__ifp->filename) + } // end oo__switch() + +static int oo_sequencetype= -1; + // type of last object which has been processed; + // -1: no object yet; 0: node; 1: way; 2: relation; +static int64_t oo_sequenceid= INT64_C(-0x7fffffffffffffff); + // id of last object which has been processed; + +static void oo__reset(oo__if_t* ifp) { + // perform a reset of output procedures and variables; + // this is mandatory if reading .o5m or .pbf and jumping + // within the input file; + ifp->o5id= 0; + ifp->o5lat= ifp->o5lon= 0; + ifp->o5hiscset= 0; + ifp->o5histime= 0; + ifp->o5rid[0]= ifp->o5rid[1]= ifp->o5rid[2]= 0; + str_reset(); + if(ifp->format==-1) + pb_input(true); + } // oo__reset() + +static int oo__rewindall() { + // rewind all input files; + // return: 0: ok; !=0: error; + oo__if_t* ifp,*ifp_sav; + + ifp_sav= oo__ifp; // save original info pointer + ifp= oo__if; + while(ifpriph!=NULL) { + if(ifp->ri==NULL && ifp->riph!=NULL) { + // file has been logically closed + // logically reopen it + ifp->ri= ifp->riph; + oo_ifn++; + } + read_switch(ifp->ri); + if(read_rewind()) +return 1; + ifp->tyid= 1; + ifp->endoffile= false; + ifp->deleteobject= 0; + oo__reset(ifp); + } + ifp++; + } + oo__ifp= ifp_sav; // restore original info pointer + if(oo__ifp!=NULL && oo__ifp->ri!=NULL) { + read_switch(oo__ifp->ri); + str_switch(oo__ifp->str); + } + else + oo__switch(); + oo__tyidold= 0; + oo_sequencetype= -1; + oo_sequenceid= INT64_C(-0x7fffffffffffffff); + return 0; + } // end oo__rewindall() + +static int oo__jumpall() { + // jump in all input files to the previously stored position; + // return: 0: ok; !=0: error; + oo__if_t* ifp,*ifp_sav; + int r; + + ifp_sav= oo__ifp; // save original info pointer + ifp= oo__if; + while(ifpriph!=NULL) { // file is still physically open + if(ifp->ri==NULL && ifp->riph!=NULL) { + // file has been logically closed + // logically reopen it + ifp->ri= ifp->riph; + oo_ifn++; + } + read_switch(ifp->ri); + r= read_jump(); + if(r<0) // jump error +return 1; + if(r==0) { // this was a real jump + ifp->tyid= 1; + ifp->endoffile= false; + ifp->deleteobject= ifp->deleteobjectjump; + oo__reset(ifp); + } + } // file is still physically open + ifp++; + } // for all files + oo__ifp= ifp_sav; // restore original info pointer + if(oo__ifp!=NULL && oo__ifp->ri!=NULL) { + read_switch(oo__ifp->ri); + str_switch(oo__ifp->str); + } + else { + oo__switch(); + if(oo__ifp==NULL) { // no file chosen + oo_ifn= 0; + ifp= oo__if; + while(ifpri= NULL; // mark file as able to be logically reopened + ifp++; + } + } + } + oo__tyidold= 0; + oo_sequencetype= -1; + oo_sequenceid= INT64_C(-0x7fffffffffffffff); + return 0; + } // end oo__jumpall() + +static void oo__close() { + // logically close an input file; + // oo__ifp: handle of currently active input file; + // if this file has already been closed, nothing happens; + // after calling this procedure, the handle of active input file + // will be invalid; you may call oo__switch() to select the + // next file in the sequence; + if(oo__ifp!=NULL && oo__ifp->ri!=NULL) { + if(!oo__ifp->endoffile && oo_ifn>0) // missing logical end of file + fprintf(stderr,"osmconvert Warning: " + "unexpected end of input file: %.80s\n",oo__ifp->filename); + read_switch(oo__ifp->ri); + //read_close(); + oo__ifp->ri= NULL; + oo__ifp->tyid= UINT64_C(0xffffffffffffffff); + // (to prevent this file being selected as next file + // in the sequence) + oo_ifn--; + } + oo__ifp= NULL; + } // end oo__close() + +static void oo__closeall() { + // close all input files; + // after calling this procedure, the handle of active input file + // will be invalid; + oo_ifn= 0; // mark end of program; + // this is used to suppress warning messages in oo__close() + while(oo__ife>oo__if) { + oo__ifp= --oo__ife; + oo__ifp->endoffile= true; // suppress warnings (see oo__close()) + if(oo__ifp->riph!=NULL) { + read_switch(oo__ifp->riph); + read_close(); + } + oo__ifp->ri= oo__ifp->riph= NULL; + oo__ifp->tyid= UINT64_C(0xffffffffffffffff); + } + } // end oo__closeall() + +static void* oo__malloc_p[50]; + // pointers for controlled memory allocation +static int oo__malloc_n= 0; + // number of elements used in oo__malloc_p[] + +static void* oo__malloc(size_t size) { + // same as malloc(), but the allocated memory will be + // automatically freed at program end; + void* mp; + + mp= malloc(size); + if(mp==NULL) { + PERRv("cannot allocate %"PRIi64" bytes of memory.",(int64_t)size); + exit(1); + } + oo__malloc_p[oo__malloc_n++]= mp; + return mp; + } // oo__malloc() + +static void oo__end() { + // clean-up this module; + oo__closeall(); + while(oo__malloc_n>0) + free(oo__malloc_p[--oo__malloc_n]); + } // end oo__end() + + + +//------------------------------------------------------------ + +static bool oo_open(const char* filename) { + // open an input file; + // filename[]: path and name of input file; + // ==NULL: standard input; + // return: 0: ok; 1: no appropriate input file; + // 2: maximum number of input files exceeded; + // the handle for the current input file oo__ifp is set + // to the opened file; + // after having opened all input files, call oo__getformat(); + // you do not need to care about closing the file; + static bool firstrun= true; + + if(oo__ife>=oo__ifee) { + fprintf(stderr,"osmconvert Error: too many input files.\n"); + fprintf(stderr,"osmconvert Error: too many input files: %d>%d\n", + (int)(oo__ife-oo__if),global_fileM); +return 2; + } + if(read_open(filename)!=0) +return 1; + oo__ife->ri= oo__ife->riph= read_infop; + oo__ife->str= NULL; + oo__ife->format= -9; // 'not yet determined' + oo__ife->tyid= 0; + if(filename==NULL) + oo__ife->filename= "standard input"; + else + oo__ife->filename= filename; + oo__ife->endoffile= false; + oo__ife->deleteobject= 0; + oo__ife->subtract= global_subtract; + oo__ifp= oo__ife++; + oo_ifn++; + if(firstrun) { + firstrun= false; + atexit(oo__end); + } + return 0; + } // end oo_open() + +int dependencystage; + // stage of the processing of interobject dependencies: + // interrelation dependencies, --complete-ways or --complex-ways; + // processing in stages allows us to reprocess parts of the data; + // abbrevation "ht" means hash table (module hash_); + // + // 0: no recursive processing at all; + // + // option --complex-ways: + // 11: no output; + // for each node which is inside the borders, + // set flag in ht; + // store start of ways in read_setjump(); + // for each way which has a member with flag in ht, + // set the way's flag in ht; + // for each relation with a member with flag in ht, + // store the relation's flag and write the ids + // of member ways which have no flag in ht + // (use cww_); + // 11->12: at all files' end: + // let all files jump to start of ways, + // use read_jump(); + // set flags for ways, use cww_processing_set(); + // 12: no output; + // for each way with a member with a flag in ht, + // set the way's flag in ht and write the ids + // of all the way's members (use cwn_); + // 12->22: as soon as first relation shall be written: + // rewind all files; + // set flags for nodes, use cwn_processing(); + // + // option --complete-ways: + // 21: no output; + // for each node inside the borders, + // set flag in ht; + // for each way with a member with a flag in ht, + // set the way's flag in ht and write the ids + // of all the way's members (use cwn_); + // 21->22: as soon as first relation shall be written: + // rewind all files; + // set flags for nodes, use cwn_processing(); + // 22: write each node which has a flag in ht to output; + // write each way which has a flag in ht to output; + // 22->32: as soon as first relation shall be written: + // clear flags for ways, use cww_processing_clear(); + // switch output to temporary file; + // + // regular procedure: + // 31: for each node inside the borders, + // set flag in ht; + // for each way with a member with a flag in ht, + // set the way's flag in ht; + // 31->32: as soon as first relation shall be written: + // switch output to temporary .o5m file; + // 32: for each relation with a member with a flag + // in ht, set the relation's flag in ht; + // for each relation, + // write its id and its members' ids + // into a temporary file (use rr_); + // if option --all-to-nodes is set, then + // for each relation, write its members' + // geopositions into a temporary file (use posr_); + // 32->33: at all files' end: + // process all interrelation references (use rr_); + // if option --all-to-nodes is set, then + // process position array (use posr_); + // switch input to the temporary .o5m file; + // switch output to regular output file; + // 33: write each relation which has a flag in ht + // to output; use temporary .o5m file as input; + // 33->99: at all files' end: end this procedure; + // + // out-of-date: + // 1: (this stage is applied only with --complete-ways option) + // read nodes and ways, do not write anything; change to next + // stage as soon as the first relation has been encountered; + // now: 21; + // 1->2: at this moment, rewind all input files; + // now: 21->22; + // 2: write nodes and ways, change to next stage as soon as + // the first relation has been encountered; + // now: 22 or 31; + // 2->3: at this moment, change the regular output file to a + // tempfile, and switch output format to .o5m; + // now: 22->32 or 31->32; + // 3: write interrelation references into a second to tempfile, + // use modules rr_ or posr_ for this purpose; + // now: 32; + // 3->4: at this moment, change output back to standard output, + // and change input to the start of the tempfile; + // in addition to this, process temporarily stored + // interrelation data; + // now: 32->33; + // 4: write only relations, use tempfile as input; + // now: 33; +static void oo__dependencystage(int ds) { + // change the dependencystage; + if(loglevel>=2) + PINFOv("changing dependencystage from %i to %i.",dependencystage,ds) + dependencystage= ds; + } // oo__dependencystage() + +static int oo_main() { + // start reading osm objects; + // return: ==0: ok; !=0: error; + // this procedure must only be called once; + // before calling this procedure you must open an input file + // using oo_open(); + int wformat; // output format; + // 0: o5m; 11,12,13,14: some different XML formats; + // 21: csv; -1: PBF; + bool hashactive; + // must be set to true if border_active OR global_dropbrokenrefs; + static char o5mtempfile[400]; // must be static because + // this file will be deleted by an at-exit procedure; + #define oo__maxrewindINI 12 + int maxrewind; // maximum number of relation-relation dependencies + int maxrewind_posr; // same as before, but for --all-to-nodes + bool writeheader; // header must be written + int otype; // type of currently processed object; + // 0: node; 1: way; 2: relation; + int64_t id; + int32_t lon,lat; + uint32_t hisver; + int64_t histime; + int64_t hiscset; + uint32_t hisuid; + char* hisuser; + int64_t* refid; // ids of referenced object + int64_t* refidee; // end address of array + int64_t* refide,*refidp; // pointer in array + int32_t** refxy; // coordinates of referenced object + int32_t** refxyp; // pointer in array + byte* reftype; // types of referenced objects + byte* reftypee,*reftypep; // pointer in array + char** refrole; // roles of referenced objects + char** refrolee,**refrolep; // pointer in array + #define oo__keyvalM 8000 // changed from 4000 to 8000 + // because there are old ways with this many key/val pairs + // in full istory planet due to malicious Tiger import + char* key[oo__keyvalM],*val[oo__keyvalM]; + char** keyee; // end address of first array + char** keye,**keyp; // pointer in array + char** vale,**valp; // pointer in array + byte* bufp; // pointer in read buffer + #define bufsp ((char*)bufp) // for signed char + byte* bufe; // pointer in read buffer, end of object + char c; // latest character which has been read + byte b; // latest byte which has been read + int l; + byte* bp; + char* sp; + struct { + int64_t nodes,ways,relations; // number of objects + int64_t node_id_min,node_id_max; + int64_t way_id_min,way_id_max; + int64_t relation_id_min,relation_id_max; + int64_t timestamp_min,timestamp_max; + int32_t lon_min,lon_max; + int32_t lat_min,lat_max; + int32_t keyval_pairs_max; + int keyval_pairs_otype; + int64_t keyval_pairs_oid; + int32_t noderefs_max; + int64_t noderefs_oid; + int32_t relrefs_max; + int64_t relrefs_oid; + } statistics; + bool diffcompare; // the next object shall be compared + // with the object which has just been read; + bool diffdifference; + // there was a difference in object comparison + + // procedure initialization + atexit(oo__end); + memset(&statistics,0,sizeof(statistics)); + oo__bbvalid= false; + hashactive= border_active || global_dropbrokenrefs; + dependencystage= 0; // 0: no recursive processing at all; + maxrewind= maxrewind_posr= oo__maxrewindINI; + writeheader= true; + if(global_outo5m) wformat= 0; + else if(global_outpbf) wformat= -1; + else if(global_emulatepbf2osm) wformat= 12; + else if(global_emulateosmosis) wformat= 13; + else if(global_emulateosmium) wformat= 14; + else if(global_outcsv) wformat= 21; + else wformat= 11; + refid= (int64_t*)oo__malloc(sizeof(int64_t)*global_maxrefs); + refidee= refid+global_maxrefs; + refxy= (int32_t**)oo__malloc(sizeof(int32_t*)*global_maxrefs); + reftype= (byte*)oo__malloc(global_maxrefs); + refrole= (char**)oo__malloc(sizeof(char*)*global_maxrefs); + keyee= key+oo__keyvalM; + diffcompare= false; + diffdifference= false; + + // get input file format and care about tempfile name + if(oo__getformat()) +return 5; + if((hashactive && !global_droprelations) || + global_calccoords!=0) { + // (borders to apply AND relations are required) OR + // user wants ways and relations to be converted to nodes + // initiate recursive processing; + if(global_complexways) { + oo__dependencystage(11); + // 11: no output; + // for each node which is inside the borders, + // set flag in ht; + // store start of ways in read_setjump(); + // for each way which has a member with flag in ht, + // set the way's flag in ht; + // for each relation with a member with flag in ht, + // store the relation's flag and write the ids + // of member ways which have no flag in ht + // (use cww_); + if(cwn_ini(global_tempfilename)) +return 28; + if(cww_ini(global_tempfilename)) +return 28; + } + else if(global_completeways) { + oo__dependencystage(21); + // 21: no output; + // for each node inside the borders, + // set flag in ht; + // for each way with a member with a flag in ht, + // set the way's flag in ht and write the ids + // of all the way's members (use cwn_); + if(cwn_ini(global_tempfilename)) +return 28; + } + else + oo__dependencystage(31); + // 31: for each node inside the borders, + // set flag in ht; + // for each way with a member with a flag in ht, + // set the way's flag in ht; + strcpy(stpmcpy(o5mtempfile,global_tempfilename, + sizeof(o5mtempfile)-2),".1"); + } + else { + oo__dependencystage(0); // no recursive processing + global_completeways= false; + global_complexways= false; + } + + // print file timestamp and nothing else if requested + if(global_outtimestamp) { + if(oo__timestamp!=0) // file timestamp is valid + write_timestamp(oo__timestamp); + else + write_str("(invalid timestamp)"); + write_str(NL); +return 0; // nothing else to do here + } + + // process the file + for(;;) { // read all input files + + if(oo_ifn>0) { // at least one input file open + + // get next object - if .pbf + //read_input(); (must not be here because of diffcompare) + if(oo__ifp->format==-1) { + if(!oo__alreadyhavepbfobject) + pb_input(false); + while(pb_type>2) // unknown pbf object + pb_input(false); // get next object + } + + // merging - if more than one file + if((oo_ifn>1 || oo__tyidold>0) && dependencystage!=33) + // input file switch necessary; + // not: + // 33: write each relation which has a flag in ht + // to output; + oo__switch(); + else if(global_mergeversions) + oo__gettyid(); + else + oo__ifp->tyid= 1; + if(diffcompare && oo__ifp!=oo__if) { + // comparison must be made with the first file but presently + // the second file is active + // switch to the first file + oo__ifp= oo__if; + read_switch(oo__ifp->ri); + str_switch(oo__ifp->str); + } + + // get next data + read_input(); + + } // at least one input file open + + // care about end of input file + if(oo_ifn==0 || (read_bufp>=read_bufe && oo__ifp->format>=0) || + (oo__ifp->format==-1 && pb_type<0)) { // at end of input file + if(oo_ifn>0) { + if(oo__ifp->format==-1 && pb_type<0) { + if(pb_type<-1) // error +return 1000-pb_type; + oo__ifp->endoffile= true; + } + oo__close(); + } + if(oo_ifn>0) // still input files + oo__switch(); + else { // at end of all input files + // care about recursive processing + if(dependencystage==11) { + // 11: no output; + // for each node which is inside the borders, + // set flag in ht; + // store start of ways in read_setjump(); + // for each way which has a member with flag in ht, + // set the way's flag in ht; + // for each relation with a member with flag in ht, + // store the relation's flag and write the ids + // of member ways which have no flag in ht + // (use cww_); + // 11->12: at all files' end: + // let all files jump to start of ways, + // use read_jump(); + // set flags for ways, use cww_processing_set(); + if(oo__jumpall()) +return 28; + cww_processing_set(); + oo__dependencystage(12); + // 12: no output; + // for each way with a member with a flag in ht, + // set the way's flag in ht and write the ids + // of all the way's members (use cwn_); + continue; // do not write this object + } + if(dependencystage==21 || dependencystage==12) { + // 12: no output; + // for each way with a member with a flag in ht, + // set the way's flag in ht and write the ids + // of all the way's members (use cwn_); + // 21: no output; + // for each node inside the borders, + // set flag in ht; + // for each way with a member with a flag in ht, + // set the way's flag in ht and write the ids + // of all the way's members (use cwn_); + // 12->22: as soon as first relation shall be written: + // rewind all files; + // set flags for nodes, use cwn_processing(); + // 21->22: as soon as first relation shall be written: + // rewind all files; + // set flags for nodes, use cwn_processing(); + if(oo__rewindall()) +return 28; + cwn_processing(); + oo__dependencystage(22); + // 22: write each node which has a flag in ht to output; + // write each way which has a flag in ht to output; + continue; // do not write this object + } + if(dependencystage!=32) { + // not: + // 32: for each relation with a member with a flag + // in ht, set the relation's flag in ht; + // for each relation, + // write its id and its members' ids + // into a temporary file (use rr_); + // if option --all-to-nodes is set, then + // for each relation, write its members' + // geopositions into a temporary file + // (use posr_); + if(dependencystage==33) { + // 33: write each relation which has a flag in ht + // to output; use temporary .o5m file as input; + if(oo__ifp!=NULL) + oo__ifp->endoffile= true; + // this is because the file we have read + // has been created as temporary file by the program + // and does not contain an eof object; + if(maxrewind_posr0) fprintf(stderr, + "Relation hierarchies: %i of maximal %i.\n", + oo__maxrewindINI-maxrewind,oo__maxrewindINI); + if(maxrewind<0) + fprintf(stderr, + "osmconvert Warning: relation dependencies too complex\n" + " (more than %i hierarchy levels).\n" + " A few relations might have been excluded\n" + " although lying within the borders.\n", + oo__maxrewindINI); + } + break; + } // end dependencystage!=32 + // here: dependencystage==32 + // 32: for each relation with a member with a flag + // in ht, set the relation's flag in ht; + // for each relation, + // write its id and its members' ids + // into a temporary file (use rr_); + // if option --all-to-nodes is set, then + // for each relation, write its members' + // geopositions into a temporary file (use posr_); + // 32->33: at all files' end: + // process all interrelation references (use rr_); + // if option --all-to-nodes is set, then + // process position array (use posr_); + // switch input to the temporary .o5m file; + // switch output to regular output file; + if(!global_outnone) { + wo_flush(); + wo_reset(); + wo_end(); + wo_flush(); + } + if(write_newfile(NULL)) +return 21; + if(!global_outnone) { + wo_format(wformat); + wo_reset(); + } + if(hashactive) + oo__rrprocessing(&maxrewind); + if(global_calccoords!=0) + posr_processing(&maxrewind_posr,refxy); + oo__dependencystage(33); // enter next stage + oo__tyidold= 0; // allow the next object to be written + if(oo_open(o5mtempfile)) +return 22; + if(oo__getformat()) +return 23; + read_input(); + continue; + } // at end of all input files + } // at end of input file + + // care about unexpected contents at file end + if(dependencystage<=31) + // 31: for each node inside the borders, + // set flag in ht; + // for each way with a member with a flag in ht, + // set the way's flag in ht; + if(oo__ifp->endoffile) { // after logical end of file + WARNv("osmconvert Warning: unexpected contents " + "after logical end of file: %.80s",oo__ifp->filename) + break; + } + + readobject: + bufp= read_bufp; + b= *bufp; c= (char)b; + + // care about header and unknown objects + if(oo__ifp->format<0) { // -1, pbf + if(pb_type<0 || pb_type>2) // not a regular dataset id + continue; + otype= pb_type; + oo__alreadyhavepbfobject= false; + } // end pbf + else if(oo__ifp->format==0) { // o5m + if(b<0x10 || b>0x12) { // not a regular dataset id + if(b>=0xf0) { // single byte dataset + if(b==0xff) { // file start, resp. o5m reset + if(read_setjump()) + oo__ifp->deleteobjectjump= oo__ifp->deleteobject; + oo__reset(oo__ifp); + } + else if(b==0xfe) + oo__ifp->endoffile= true; + else if(write_testmode) + WARNv("unknown .o5m short dataset id: 0x%02x",b) + read_bufp++; + continue; + } // end single byte dataset + else { // unknown multibyte dataset + if(b!=0xdb && b!=0xdc && b!=0xe0) + // not border box AND not header + WARNv("unknown .o5m dataset id: 0x%02x",b) + read_bufp++; + l= pbf_uint32(&read_bufp); // length of this dataset + read_bufp+= l; // jump over this dataset + continue; + } // end unknown multibyte dataset + } // end not a regular dataset id + otype= b&3; + } // end o5m + else { // xml + while(c!=0 && c!='<') c= (char)*++bufp; + if(c==0) { + read_bufp= read_bufe; + continue; + } + c= bufsp[1]; + if(c=='n' && bufsp[2]=='o' && bufsp[3]=='d') // node + otype= 0; + else if(c=='w' && bufsp[2]=='a') // way + otype= 1; + else if(c=='r' && bufsp[2]=='e') // relation + otype= 2; + else if(c=='c' || (c=='m' && bufsp[2]=='o') || c=='d' || + c=='i' || c=='k' || c=='e' ) { + // create, modify or delete, + // insert, keep or erase + if(c=='d' || c=='e') + oo__ifp->deleteobject= 2; + read_bufp= bufp+1; + continue; + } // end create, modify or delete, + // resp. insert, keep or erase + else if(c=='/') { // xml end object + if(bufsp[2]=='d' || bufsp[2]=='e') // end of delete or erase + oo__ifp->deleteobject= 0; + else if(strzcmp(bufsp+2,"osm>")==0) { // end of file + oo__ifp->endoffile= true; + read_bufp= bufp+6; + while(oo__ws(*read_bufp)) read_bufp++; + continue; + } // end end of file + else if(strzcmp(bufsp+2,"osmChange>")==0) { // end of file + oo__ifp->endoffile= true; + read_bufp= bufp+6+6; + while(oo__ws(*read_bufp)) read_bufp++; + continue; + } // end end of file + else if(strzcmp(bufsp+2,"osmAugmentedDiff>")==0) { + // end of file + oo__ifp->endoffile= true; + read_bufp= bufp+6+13; + while(oo__ws(*read_bufp)) read_bufp++; + continue; + } // end end of file + goto unknownxmlobject; + } // end xml end object + else { // unknown xml object + unknownxmlobject: + bufp++; + for(;;) { // find end of xml object + c= *bufsp; + if(c=='>' || c==0) + break; + bufp++; + } + read_bufp= bufp; + continue; + } // end unknown XML object + // here: regular OSM XML object + if(read_setjump()) + oo__ifp->deleteobjectjump= oo__ifp->deleteobject; + read_bufp= bufp; + } // end xml + + // write header + if(writeheader) { + writeheader= false; + if(!global_outnone) + wo_start(wformat,oo__bbvalid, + oo__bbx1,oo__bby1,oo__bbx2,oo__bby2,oo__timestamp); + } + + // object initialization + if(!diffcompare) { // regularly read the object + hisver= 0; + histime= 0; + hiscset= 0; + hisuid= 0; + hisuser= ""; + refide= refid; + reftypee= reftype; + refrolee= refrole; + keye= key; + vale= val; + } // regularly read the object + if(oo__ifp->deleteobject==1) oo__ifp->deleteobject= 0; + + + // read one osm object + if(oo__ifp->format<0) { // pbf + // read id + id= pb_id; + // read coordinates (for nodes only) + if(otype==0) { // node + lon= pb_lon; lat= pb_lat; + } // node + // read author + hisver= pb_hisver; + if(hisver!=0) { // author information available + histime= pb_histime; + if(histime!=0) { + hiscset= pb_hiscset; + hisuid= pb_hisuid; + hisuser= pb_hisuser; + } + } // end author information available + oo__ifp->deleteobject= pb_hisvis==0? 1: 0; + // read noderefs (for ways only) + if(otype==1) // way + refide= refid+pb_noderef(refid,global_maxrefs); + // read refs (for relations only) + else if(otype==2) { // relation + l= pb_ref(refid,reftype,refrole,global_maxrefs); + refide= refid+l; + reftypee= reftype+l; + refrolee= refrole+l; + } // end relation + // read node key/val pairs + l= pb_keyval(key,val,oo__keyvalM); + keye= key+l; vale= val+l; + } // end pbf + else if(oo__ifp->format==0) { // o5m + bufp++; + l= pbf_uint32(&bufp); + read_bufp= bufe= bufp+l; + if(diffcompare) { // just compare, do not store the object + uint32_t hisverc; + int64_t histimec; + char* hisuserc; + int64_t* refidc; // pointer for object contents comparison + byte* reftypec; // pointer for object contents comparison + char** refrolec; // pointer for object contents comparison + char** keyc,**valc; // pointer for object contents comparison + + // initialize comparison variables + hisverc= 0; + histimec= 0; + hisuserc= ""; + refidc= refid; + reftypec= reftype; + refrolec= refrole; + keyc= key; + valc= val; + + // compare object id + if(id!=(oo__ifp->o5id+= pbf_sint64(&bufp))) + diffdifference= true; + + // compare author + hisverc= pbf_uint32(&bufp); + if(hisverc!=hisver) + diffdifference= true; + if(hisverc!=0) { // author information available + histimec= oo__ifp->o5histime+= pbf_sint64(&bufp); + if(histimec!=0) { + if(histimec!=histime) + diffdifference= true; + if(hiscset!=(oo__ifp->o5hiscset+= pbf_sint32(&bufp))) + diffdifference= true; + str_read(&bufp,&sp,&hisuserc); + if(strcmp(hisuserc,hisuser)!=0) + diffdifference= true; + if(hisuid!=pbf_uint64((byte**)&sp)) + diffdifference= true; + } + } // end author information available + + if(bufp>=bufe) { + // just the id and author, i.e. this is a delete request + oo__ifp->deleteobject= 1; + diffdifference= true; + } + else { // not a delete request + oo__ifp->deleteobject= 0; + + // compare coordinates (for nodes only) + if(otype==0) { // node + // read node body + if(lon!=(oo__ifp->o5lon+= pbf_sint32(&bufp))) + diffdifference= true; + if(lat!=(oo__ifp->o5lat+= pbf_sint32(&bufp))) + diffdifference= true; + } // end node + + // compare noderefs (for ways only) + if(otype==1) { // way + l= pbf_uint32(&bufp); + bp= bufp+l; + if(bp>bufe) bp= bufe; // (format error) + while(bufpo5rid[0]+= pbf_sint64(&bufp))) + diffdifference= true; + refidc++; + } + } // end way + + // compare refs (for relations only) + else if(otype==2) { // relation + int64_t ri; // temporary, refid + int rt; // temporary, reftype + char* rr; // temporary, refrole + + l= pbf_uint32(&bufp); + bp= bufp+l; + if(bp>bufe) bp= bufe; // (format error) + while(bufpo5rid[rt]+= ri)) + diffdifference= true; + if(refrolec>=refrolee || strcmp(*refrolec,rr)!=0) + diffdifference= true; + reftypec++; + refidc++; + refrolec++; + } + } // end relation + + // compare node key/val pairs + while(bufp=keye || strcmp(k,*keyc)!=0 || strcmp(v,*valc)!=0) + diffdifference= true; + keyc++; valc++; + } + } // end not a delete request + + // compare indexes + if(keyc!=keye || (otype>0 && refidc!=refide)) + diffdifference= true; + + } // just compare, do not store the object + else { // regularly read the object + // read object id + id= oo__ifp->o5id+= pbf_sint64(&bufp); + // read author + hisver= pbf_uint32(&bufp); + if(hisver!=0) { // author information available + histime= oo__ifp->o5histime+= pbf_sint64(&bufp); + if(histime!=0) { + hiscset= oo__ifp->o5hiscset+= pbf_sint32(&bufp); + str_read(&bufp,&sp,&hisuser); + hisuid= pbf_uint64((byte**)&sp); + } + } // end author information available + if(bufp>=bufe) + // just the id and author, i.e. this is a delete request + oo__ifp->deleteobject= 1; + else { // not a delete request + oo__ifp->deleteobject= 0; + // read coordinates (for nodes only) + if(otype==0) { // node + // read node body + lon= oo__ifp->o5lon+= pbf_sint32(&bufp); + lat= oo__ifp->o5lat+= pbf_sint32(&bufp); + } // end node + // read noderefs (for ways only) + if(otype==1) { // way + l= pbf_uint32(&bufp); + bp= bufp+l; + if(bp>bufe) bp= bufe; // (format error) + while(bufpo5rid[0]+= pbf_sint64(&bufp); + } // end way + // read refs (for relations only) + else if(otype==2) { // relation + int64_t ri; // temporary, refid + int rt; // temporary, reftype + char* rr; // temporary, refrole + + l= pbf_uint32(&bufp); + bp= bufp+l; + if(bp>bufe) bp= bufe; // (format error) + while(bufpo5rid[rt]+= ri; + *refrolee++= rr; + } + } // end relation + // read node key/val pairs + keye= key; vale= val; + while(bufpdeleteobject==0) + oo__ifp->deleteobject= 1; + } // end visible + else if(oo__xmlkey[0]=='a' && oo__xmlkey[1]=='c') { // action + if(oo__xmlval[0]=='d' && oo__xmlval[1]=='e') + if(oo__ifp->deleteobject==0) + oo__ifp->deleteobject= 1; + } // end action + else if(!global_dropversion) { // version not to drop + if(oo__xmlkey[0]=='v' && oo__xmlkey[1]=='e') // hisver + hisver= oo__strtouint32(oo__xmlval); + if(!global_dropauthor) { // author not to drop + if(oo__xmlkey[0]=='t') // histime + histime= oo__strtimetosint64(oo__xmlval); + else if(oo__xmlkey[0]=='c') // hiscset + hiscset= oo__strtosint64(oo__xmlval); + else if(oo__xmlkey[0]=='u' && oo__xmlkey[1]=='i') // hisuid + hisuid= oo__strtouint32(oo__xmlval); + else if(oo__xmlkey[0]=='u' && oo__xmlkey[1]=='s') //hisuser + hisuser= oo__xmlval; + } // end author not to drop + } // end version not to drop + } // end still in object header + else { // in object body + if(oo__xmlkey[0]==0) { // xml tag completed + if(rcomplete>=3) { // at least refid and reftype + *refide++= ri; + *reftypee++= rt; + if(rcomplete<4) // refrole is missing + rr= ""; // assume an empty string as refrole + *refrolee++= rr; + } // end at least refid and reftype + rcomplete= 0; + if(v!=NULL && k!=NULL) { // key/val available + *keye++= k; *vale++= v; + k= v= NULL; + } // end key/val available + } // end xml tag completed + else { // inside xml tag + if(otype!=0 && refidetyid; + if(oo__gettyid()==0) { + if(oo__ifp->tyid==tyidold) // next object has same type and id + goto readobject; // dispose this object and take the next + } + oo__ifp->tyid= tyidold; + } + + // care about possible array overflows + if(refide>=refidee) + PERRv("%s %"PRIi64" has too many refs.",ONAME(otype),id) + if(keye>=keyee) + PERRv("%s %"PRIi64" has too many key/val pairs.", + ONAME(otype),id) + + // care about diffs and sequence + if(global_diffcontents) { // diff contents is to be considered + // care about identical contents if calculating a diff + if(oo__ifp!=oo__if && oo__ifp->tyid==oo__if->tyid) { + // second file and there is a similar object in the first file + // and version numbers are different + diffcompare= true; // compare with next object, do not read + diffdifference= false; // (default) + continue; // no check the first file + } + } // diff contents is to be considered + else { // no diff contents is to be considered + // stop processing if object is to ignore because of duplicates + // in same or other file(s) + if(oo__ifp->tyid<=oo__tyidold) + continue; + oo__tyidold= 0; + if(oo_ifn>1) + oo__tyidold= oo__ifp->tyid; + // stop processing if in wrong stage for nodes or ways + if(dependencystage>=32 && otype<=1) + // 32: for each relation with a member with a flag + // in ht, set the relation's flag in ht; + // for each relation, + // write its id and its members' ids + // into a temporary file (use rr_); + // if option --all-to-nodes is set, then + // for each relation, write its members' + // geopositions into a temporary file (use posr_); + // 33: write each relation which has a flag in ht + // to output; use temporary .o5m file as input; + continue; // ignore this object + // check sequence, if necessary + if(oo_ifn==1 && dependencystage!=33) { + // not: + // 33: write each relation which has a flag in ht + // to output; use temporary .o5m file as input; + if(otype<=oo_sequencetype && + (otype1 && id<=oo_sequenceid))) { + oo__error= 92; + WARNv("wrong sequence at %s %"PRIi64, + ONAME(oo_sequencetype),oo_sequenceid) + WARNv("next object is %s %"PRIi64,ONAME(otype),id) + } + } // dependencystage>=32 + } // no diff contents is to be considered + oo_sequencetype= otype; oo_sequenceid= id; + + // care about calculating a diff file + if(global_diff) { // diff + if(oo__ifp==oo__if) { // first file has been chosen + if(diffcompare) { + diffcompare= false; + if(!diffdifference) + continue; // there has not been a change in object's contents + oo__ifp->deleteobject= 0; + } + else { + //if(global_diffcontents && oo_ifn<2) continue; + oo__ifp->deleteobject= 1; // add "delete request"; + } + } + else { // second file has been chosen + if(oo__ifp->tyid==oo__if->tyid && + oo__ifp->hisver==oo__if->hisver) + // there is a similar object in the first file + continue; // ignore this object + } // end second file has been chosen + } // end diff + + // care about dependency stages + if(dependencystage==11) { + // 11: no output; + // for each node which is inside the borders, + // set flag in ht; + // store start of ways in read_setjump(); + // for each way which has a member with flag in ht, + // set the way's flag in ht; + // for each relation with a member with flag in ht, + // store the relation's flag and write the ids + // of member ways which have no flag in ht + // (use cww_); + if(otype>=1) // way or relation + read_lockjump(); + if((oo__ifp->deleteobject==0) ^ oo__ifp->subtract) { + // object is not to delete + if(otype==0) { // node + if(!border_active || border_queryinside(lon,lat)) + // no border to be applied OR node lies inside + hash_seti(0,id); // mark this node id as 'inside' + } // node + else if(otype==1) { // way + refidp= refid; + while(refidpdeleteobject==0) ^ oo__ifp->subtract) { + // object is not to delete + if(otype==1 && hash_geti(1,id)) { + // way AND is marked in ht + // store ids of all referenced nodes of this way + refidp= refid; + while(refidpdeleteobject==0) ^ oo__ifp->subtract) { + // object is not to delete + if(otype==0) { // node + if(!border_active || border_queryinside(lon,lat)) + // no border to be applied OR node lies inside + hash_seti(0,id); // mark this node id as 'inside' + } // node + else if(otype==1) { // way + refidp= refid; + while(refidpendoffile= true; // suppress warnings + oo__close(); // the next stage will be entered as soon as + // all files have been closed; + // 21->22: as soon as first relation shall be written: + // rewind all files; + // set flags for nodes, use cwn_processing(); + } // relation + } // object is not to delete +continue; // do not write this object + } // dependencystage 21 + else if(otype==2) { // relation + if(!global_droprelations && + (dependencystage==31 || dependencystage==22)) { + // not relations to drop AND + // 22: write each node which has a flag in ht to output; + // write each way which has a flag in ht to output; + // 31: for each node inside the borders, + // set flag in ht; + // for each way with a member with a flag in ht, + // set the way's flag in ht; + // 22->32: as soon as first relation shall be written: + // clear flags for ways, use cww_processing_clear(); + // switch output to temporary file; + // 31->32: as soon as first relation shall be written: + // switch output to temporary .o5m file; + wo_flush(); + if(write_newfile(o5mtempfile)) +return 24; + wo_format(0); + if(hashactive) + if(rr_ini(global_tempfilename)) +return 25; + if(dependencystage==22) + cww_processing_clear(); + if(global_calccoords!=0) + if(posr_ini(global_tempfilename)) +return 26; + oo__dependencystage(32); + // 32: for each relation with a member with a flag + // in ht, set the relation's flag in ht; + // for each relation, + // write its id and its members' ids + // into a temporary file (use rr_); + // if option --all-to-nodes is set, then + // for each relation, write its members' + // geopositions into a temporary file (use posr_); + } // dependencystage was 31 + } // relation + else { // node or way + } // node or way + // end care about dependency stages + + // process object deletion + if((oo__ifp->deleteobject!=0) ^ oo__ifp->subtract) { + // object is to delete + if((otype==0 && !global_dropnodes) || + (otype==1 && !global_dropways) || + (otype==2 && !global_droprelations)) + // section is not to drop anyway + if(global_outo5c || global_outosc || global_outosh) + // write o5c, osc or osh file + wo_delete(otype,id,hisver,histime,hiscset,hisuid,hisuser); + // write delete request + continue; // end processing for this object + } // end object is to delete + + // care about object statistics + if(global_statistics && + dependencystage!=32) { + // not: + // 32: for each relation with a member with a flag + // in ht, set the relation's flag in ht; + // for each relation, + // write its id and its members' ids + // into a temporary file (use rr_); + // if option --all-to-nodes is set, then + // for each relation, write its members' + // geopositions into a temporary file (use posr_); + + if(otype==0) { // node + if(statistics.nodes==0) { // this is the first node + statistics.lon_min= statistics.lon_max= lon; + statistics.lat_min= statistics.lat_max= lat; + } + statistics.nodes++; + if(statistics.node_id_min==0 || idstatistics.node_id_max) + statistics.node_id_max= id; + if(lonstatistics.lon_max) + statistics.lon_max= lon; + if(latstatistics.lat_max) + statistics.lat_max= lat; + } + else if(otype==1) { // way + statistics.ways++; + if(statistics.way_id_min==0 || idstatistics.way_id_max) + statistics.way_id_max= id; + if(refide-refid>statistics.noderefs_max) { + statistics.noderefs_oid= id; + statistics.noderefs_max= refide-refid; + } + } + else if(otype==2) { // relation + statistics.relations++; + if(statistics.relation_id_min==0 || + idstatistics.relation_id_max) + statistics.relation_id_max= id; + if(refide-refid>statistics.relrefs_max) { + statistics.relrefs_oid= id; + statistics.relrefs_max= refide-refid; + } + } + if(histime!=0) { // timestamp valid + if(statistics.timestamp_min==0 || + histimestatistics.timestamp_max) + statistics.timestamp_max= histime; + } + if(keye-key>statistics.keyval_pairs_max) { + statistics.keyval_pairs_otype= otype; + statistics.keyval_pairs_oid= id; + statistics.keyval_pairs_max= keye-key; + } + } // object statistics + + // abort writing if user does not want any standard output + if(global_outnone) + continue; + + // write the object + if(otype==0) { // write node + bool inside; // node lies inside borders, if appl. + + if(!border_active) // no borders shall be applied + inside= true; + else if(dependencystage==22) + // 22: write each node which has a flag in ht to output; + // write each way which has a flag in ht to output; + inside= hash_geti(0,id); + else { + inside= border_queryinside(lon,lat); // node lies inside + if(inside) + hash_seti(0,id); // mark this node id as 'inside' + } + if(inside) { // node lies inside + if(global_calccoords!=0) { + // check id range + if(id>=global_otypeoffset05 || id<=-global_otypeoffset05) + WARNv("node id %"PRIi64 + " out of range. Increase --object-type-offset",id) + posi_set(id,lon,lat); // store position + } + if(!global_dropnodes) { // not to drop + wo_node(id, + hisver,histime,hiscset,hisuid,hisuser,lon,lat); + keyp= key; valp= val; + while(keyp=global_otypeoffset05 || id<=-global_otypeoffset05) + WARNv("way id %"PRIi64 + " out of range. Increase --object-type-offset",id) + + // determine the center of the way's bbox + n= 0; + refidp= refid; refxyp= refxy; + while(refidp + x_max && posi_xy[0]-x_max<900000000) + x_max= posi_xy[0]; + if(posi_xy[1]y_max) + y_max= posi_xy[1]; + } + n++; + } // coordinate is valid + } // referenced node lies inside the borders + refidp++; refxyp++; + } // end for every referenced node + + // determine if the way is an area + is_area= refide!=refid && refide[-1]==refid[0]; + // first node is the same as the last one + + // determine the valid center of the way + x_middle= x_max/2+x_min/2; + y_middle= (y_max+y_min)/2; + if(is_area) { + lon= x_middle; + lat= y_middle; + } + else { // the way is not an area + // determine the node which has the smallest distance + // to the center of the bbox + n= 0; + refidp= refid; refxyp= refxy; + while(refidp0) + posi_set(id+global_otypeoffset10,lon,lat); + else + posi_setbbox(id+global_otypeoffset10,lon,lat, + x_min,y_min,x_max,y_max); + if(global_alltonodes) { // convert all objects to nodes + // write a node as a replacement for the way + if(n>0) { // there is at least one coordinate available + int64_t id_new; + + if(global_otypeoffsetstep!=0) + id_new= global_otypeoffsetstep++; + else + id_new= id+global_otypeoffset10; + wo_node(id_new, + hisver,histime,hiscset,hisuid,hisuser,lon,lat); + if(global_add) + wo_addbboxtags(true,x_min,y_min,x_max,y_max); + keyp= key; valp= val; + while(keyp=global_otypeoffset05 || id<=-global_otypeoffset05) + WARNv("relation id %"PRIi64 + " out of range. Increase --object-type-offset",id) + posi_get(id+global_otypeoffset20); // get coordinates + if(posi_xy!=NULL && posi_xy[0]!=posi_nil) { + // stored coordinates are valid + int64_t id_new; + + if(global_otypeoffsetstep!=0) + id_new= global_otypeoffsetstep++; + else + id_new= id+global_otypeoffset20; + // write a node as a replacement for the relation + wo_node(id_new, + hisver,histime,hiscset,hisuid,hisuser, + posi_xy[0],posi_xy[1]); + if(global_add) + wo_addbboxtags(true, + posi_xy[2],posi_xy[3],posi_xy[4],posi_xy[5]); + keyp= key; valp= val; + while(keyp0) { // at least one node + char coord[20]; + + write_createsfix7o(statistics.lon_min,coord); + fprintf(fi,"lon min: %s\n",coord); + write_createsfix7o(statistics.lon_max,coord); + fprintf(fi,"lon max: %s\n",coord); + write_createsfix7o(statistics.lat_min,coord); + fprintf(fi,"lat min: %s\n",coord); + write_createsfix7o(statistics.lat_max,coord); + fprintf(fi,"lat max: %s\n",coord); + } + fprintf(fi,"nodes: %"PRIi64"\n",statistics.nodes); + fprintf(fi,"ways: %"PRIi64"\n",statistics.ways); + fprintf(fi,"relations: %"PRIi64"\n",statistics.relations); + if(statistics.node_id_min!=0) + fprintf(fi,"node id min: %"PRIi64"\n",statistics.node_id_min); + if(statistics.node_id_max!=0) + fprintf(fi,"node id max: %"PRIi64"\n",statistics.node_id_max); + if(statistics.way_id_min!=0) + fprintf(fi,"way id min: %"PRIi64"\n",statistics.way_id_min); + if(statistics.way_id_max!=0) + fprintf(fi,"way id max: %"PRIi64"\n",statistics.way_id_max); + if(statistics.relation_id_min!=0) + fprintf(fi,"relation id min: %"PRIi64"\n", + statistics.relation_id_min); + if(statistics.relation_id_max!=0) + fprintf(fi,"relation id max: %"PRIi64"\n", + statistics.relation_id_max); + if(statistics.keyval_pairs_max!=0) { + fprintf(fi,"keyval pairs max: %"PRIi32"\n", + statistics.keyval_pairs_max); + fprintf(fi,"keyval pairs max object: %s %"PRIi64"\n", + ONAME(statistics.keyval_pairs_otype), + statistics.keyval_pairs_oid); + } + if(statistics.noderefs_max!=0) { + fprintf(fi,"noderefs max: %"PRIi32"\n", + statistics.noderefs_max); + fprintf(fi,"noderefs max object: way %"PRIi64"\n", + statistics.noderefs_oid); + } + if(statistics.relrefs_max!=0) { + fprintf(fi,"relrefs max: %"PRIi32"\n", + statistics.relrefs_max); + fprintf(fi,"relrefs max object: relation %"PRIi64"\n", + statistics.relrefs_oid); + } + } // print statistics + return oo__error; + } // end oo_main() + +//------------------------------------------------------------ +// end Module oo_ osm to osm module +//------------------------------------------------------------ + + + +static void assistant_end(); + +static bool assistant(int* argcp,char*** argvp) { + // interactively guide the user through basic functions; + // argcp==NULL AND argvp==NULL: to confirm that the calculation + // has been terminated correctly; + // argcp==NULL AND argvp!=NULL: + // display 'bye message', do nothing else (third call); + // usually, this procedure must be called twice: first, before + // parsing the command line arguments, and second, after + // the regular processing has been done without any error; + // the third call will be done by atexit(); + // return: user wants to terminate the program; + #define langM 2 + static int lang= 0; + static const char* talk_lang1[langM]= { "", "de_" }; + static const char* talk_lang2[langM]= { "", "German_" }; + static const char* talk_section[langM]= { + "-----------------------------------------------------------------\n" + }; + static const char* talk_intro[langM]= { + "\n" + "osmconvert "VERSION"\n" + "\n" + "Converts .osm, .o5m, .pbf, .osc, .osh files, applies changes\n" + "of .osc, .o5c, .osh files and sets limiting borders.\n" + "Use command line option -h to get a parameter overview,\n" + "or --help to get detailed help.\n" + "\n" + "If you are familiar with the command line, press .\n" + "\n" + "If you do not know how to operate the command line, please\n" + "enter \"a\" (press key E and hit ).\n" + , + "\n" + "osmconvert "VERSION"\n" + "\n" + "Konvertiert .osm-, .o5m-, .pbf-, .osc- und .osh-Dateien,\n" + "spielt Updates von .osc-, .o5c- und .osh-Dateien ein und\n" + "setzt geografische Grenzen.\n" + "Die Kommandozeilenoption -h zeigt eine Parameteruebersicht,\n" + "--help bringt eine detaillierte Hilfe (in Englisch).\n" + "\n" + "Wenn Sie mit der Kommandozeile vertraut sind, druecken Sie\n" + "bitte .\n" + "\n" + "Falls Sie sich mit der Kommandozeile nicht auskennen, druecken\n" + "Sie bitte \"a\" (Taste A und dann die Eingabetaste).\n" + }; + static const char* talk_hello[langM]= { + "Hi, I am osmconBert - just call me Bert.\n" + "I will guide you through the basic functions of osmconvert.\n" + "\n" + "At first, please ensure to have the \"osmconvert\" file\n" + "(resp. \"osmconvert.exe\" file if Windows) located in the\n" + "same directory in which all your OSM data is stored.\n" + "\n" + "You may exit this program whenever you like. Just hold\n" + "the key and press the key C.\n" + "\n" + , + "Hallo, ich bin osmconBert - nennen Sie mich einfach Bert.\n" + "Ich werde Sie durch die Standardfunktionen von osmconvert leiten.\n" + "\n" + "Bitte stellen Sie zuerst sicher, dass sich die Programmdatei\n" + "\"osmconvert\" (bzw. \"osmconvert.exe\" im Fall von Windows) im\n" + "gleichen Verzeichnis befindet wie Ihre OSM-Dateien.\n" + "\n" + "Sie koennen das Programm jederzeit beenden. Halten Sie dazu die\n" + "-Taste gedrueckt und druecken die Taste C.\n" + "\n" + }; + static const char* talk_input_file[langM]= { + "Please please tell me the name of the file you want to process:\n" + , + "Bitte nennen Sie mir den Namen der Datei, die verarbeitet werden soll:\n" + }; + static const char* talk_not_found[langM]= { + "Sorry, I cannot find a file with this name in the current directory.\n" + "\n" + , + "Sorry, ich kann diese Datei im aktuellen Verzeichnis nicht finden.\n" + "\n" + }; + static const char* talk_input_file_suffix[langM]= { + "Sorry, the file must have \".osm\", \".o5m\" or \".pbf\" as suffix.\n" + "\n" + , + "Sorry, die Datei muss \".osm\", \".o5m\" oder \".pbf\" als Endung haben.\n" + "\n" + }; + static const char* talk_thanks[langM]= { + "Thanks!\n" + , + "Danke!\n" + }; + static const char* talk_function[langM]= { + "What may I do with this file?\n" + "\n" + " 1 convert it to a different file format\n" + " 2 use an OSM Changefile to update this file\n" + " 3 use a border box to limit the geographical region\n" + " 4 use a border polygon file to limit the geographical region\n" + " 5 minimize file size by deleting author information\n" + " 6 display statistics of the file\n" + "To options 3 or 4 you may also choose:\n" + " a keep ways complete, even if they cross the border\n" + " b keep ways and areas complete, even if they cross the border\n" + "\n" + "Please enter the number of one or more functions you choose:\n" + , + "Was soll ich mit dieser Datei tun?\n" + "\n" + " 1 in ein anderes Format umwandeln\n" + " 2 sie per OSM-Change-Datei aktualisieren\n" + " 3 per Laengen- und Breitengrad einen Bereich ausschneiden\n" + " 4 mit einer Polygon-Datei einen Bereich ausschneiden\n" + " 5 Autorinformationen loeschen,damit Dateigroesse minimieren\n" + " 6 statistische Daten zu dieser Datei anzeigen\n" + "Zu den Optionen 3 oder 4 koennen zusaetzlich gewaehlt werden:\n" + " a grenzueberschreitende Wege als Ganzes behalten\n" + " b grenzueberschreitende Wege und Flaechen als Ganzes behalten\n" + "\n" + "Bitte waehlen Sie die Nummer(n) von einer oder mehreren Funktionen:\n" + }; + static const char* talk_all_right[langM]= { + "All right.\n" + , + "Geht in Ordnung.\n" + }; + static const char* talk_cannot_understand[langM]= { + "Sorry, I could not understand.\n" + "\n" + , + "Sorry, das habe ich nicht verstanden.\n" + "\n" + }; + static const char* talk_two_borders[langM]= { + "Please do not choose both, border box and border polygon.\n" + "\n" + , + "Bitte nicht beide Arten des Ausschneidens gleichzeitig waehlen.\n" + "\n" + }; + static const char* talk_changefile[langM]= { + "Please tell me the name of the OSM Changefile:\n" + , + "Bitte nennen Sie mir den Namen der OSM-Change-Datei:\n" + }; + static const char* talk_changefile_suffix[langM]= { + "Sorry, the Changefile must have \".osc\" or \".o5c\" as suffix.\n" + "\n" + , + "Sorry, die Change-Datei muss \".osc\" oder \".o5c\" als Endung haben.\n" + "\n" + }; + static const char* talk_polygon_file[langM]= { + "Please tell me the name of the polygon file:\n" + , + "Bitte nennen Sie mir den Namen der Polygon-Datei:\n" + }; + static const char* talk_polygon_file_suffix[langM]= { + "Sorry, the polygon file must have \".poly\" as suffix.\n" + "\n" + , + "Sorry, die Polygon-Datei muss \".poly\" als Endung haben.\n" + "\n" + }; + static const char* talk_coordinates[langM]= { + "We need the coordinates of the border box.\n" + "The unit is degree, just enter each number, e.g.: -35.75\n" + , + "Wir brauchen die Bereichs-Koordinaten in Grad,\n" + "aber jeweils ohne Einheitenbezeichnung, also z.B.: 7,75\n" + }; + static const char* talk_minlon[langM]= { + "Please tell me the minimum longitude:\n" + , + "Bitte nennen Sie mir den Minimum-Laengengrad:\n" + }; + static const char* talk_maxlon[langM]= { + "Please tell me the maximum longitude:\n" + , + "Bitte nennen Sie mir den Maximum-Laengengrad:\n" + }; + static const char* talk_minlat[langM]= { + "Please tell me the minimum latitude:\n" + , + "Bitte nennen Sie mir den Minimum-Breitengrad:\n" + }; + static const char* talk_maxlat[langM]= { + "Please tell me the maximum latitude:\n" + , + "Bitte nennen Sie mir den Maximum-Breitengrad:\n" + }; + static const char* talk_output_format[langM]= { + "Please choose the output file format:\n" + "\n" + "1 .osm (standard XML format - results in very large files)\n" + "2 .o5m (binary format - allows fast)\n" + "3 .pbf (standard binary format - results in small files)\n" + "\n" + "Enter 1, 2 or 3:\n" + , + "Bitte waehlen Sie das Format der Ausgabe-Datei:\n" + "\n" + "1 .osm (Standard-XML-Format - ergibt sehr grosse Dateien)\n" + "2 .o5m (binaeres Format - recht schnell)\n" + "3 .pbf (binaeres Standard-Format - ergibt kleine Dateien)\n" + "\n" + "1, 2 oder 3 eingeben:\n" + }; + static const char* talk_working[langM]= { + "Now, please hang on - I am working for you.\n" + "If the input file is very large, this will take several minutes.\n" + "\n" + "If you want to get acquainted with the much more powerful\n" + "command line, this would have been your command:\n" + "\n" + , + "Einen Moment bitte - ich arbeite fuer Sie.\n" + "Falls die Eingabe-Datei sehr gross ist, dauert das einige Minuten.\n" + "\n" + "Fall Sie sich mit der viel leistungsfaehigeren Kommandozeilen-\n" + "eingabe vertraut machen wollen, das waere Ihr Kommando gewesen:\n" + "\n" + }; + static const char* talk_finished[langM]= { + "Finished! Calculation time: " + , + "Fertig! Berechnungsdauer: " + }; + static const char* talk_finished_file[langM]= { + "I just completed your new file with this name:\n" + , + "Soeben habe ich Ihre neue Datei mit diesem Namen fertiggestellt:\n" + }; + static const char* talk_error[langM]= { + "I am sorry, an error has occurred (see above).\n" + , + "Es tut mir Leid, es ist ein Fehler aufgetreten (siehe oben).\n" + }; + static const char* talk_bye[langM]= { + "\n" + "Thanks for visiting me. Bye!\n" + "Yours, Bert\n" + "(To close this window, please press .)\n" + , + "\n" + "Danke fuer Ihren Besuch. Tschues!\n" + "Schoene Gruesse - Bert\n" + "(Zum Schließen dieses Fensters bitte die Eingabetaste druecken.)\n" + }; + #define DD(s) fprintf(stderr,"%s",(s[lang])); // display text + #define DI(s) s[0]= 0; UR(fgets(s,sizeof(s),stdin)) \ + if(strchr(s,'\r')!=NULL) *strchr(s,'\r')= 0; \ + if(strchr(s,'\n')!=NULL) *strchr(s,'\n')= 0; // get user's response + bool + function_convert= false, + function_update= false, + function_border_box= false, + function_border_polygon= false, + function_drop_author= false, + function_statistics= false; + int function_cut_mode= 0; + // 0: normal; 1: complete ways; 2: complex ways; + static bool function_only_statistics= false; + static time_t start_time; + bool verbose; + char s[500]; // temporary string for several purposes + char* sp; + static char input_file[500]; + bool file_type_osm,file_type_osc,file_type_o5m,file_type_o5c, + file_type_pbf; + static char changefile[500]; + char polygon_file[500]; + char minlon[30],maxlon[30],minlat[30],maxlat[30]; + static char output_file[550]= ""; // the first three characters + // are reserved for the commandline option "-o=" + int i; + + // display 'bye message' - if requested + if(argcp==NULL) { + static bool no_error= false; + + if(argvp==NULL) + no_error= true; + else { + if(output_file[0]!=0) { + DD(talk_section) + if(no_error) { + DD(talk_finished) + fprintf(stderr,"%"PRIi64"s.\n", + (int64_t)(time(NULL)-start_time)); + DD(talk_finished_file) + fprintf(stderr," %s",output_file+3); + } + else + DD(talk_error) + DD(talk_bye) + DI(s) + } + else if(function_only_statistics) { + DD(talk_section) + if(no_error) { + DD(talk_finished) + fprintf(stderr,"%"PRIi64"s.\n", + (int64_t)(time(NULL)-start_time)); + } + else + DD(talk_error) + DD(talk_bye) + DI(s) + } + } +return false; + } + + // initialization + atexit(assistant_end); + for(i= 1; i0) + if(syslang!=NULL && + (strzcmp(syslang,talk_lang1[lang])==0 || + strzcmp(syslang,talk_lang2[lang])==0)) break; + setlocale(LC_ALL,"C"); // switch back to C standard + } + + // introduction + DD(talk_intro) + DI(s) + sp= s; + while(*sp==' ') sp++; // dispose of leading spaces + if((*sp!='a' && *sp!='A') || sp[1]!=0) +return true; + verbose= isupper(*(unsigned char*)sp); + + // choose input file + DD(talk_section) + DD(talk_hello) + for(;;) { + DD(talk_input_file) + DI(input_file) + file_type_osm= strycmp(input_file,".osm")==0; + file_type_osc= strycmp(input_file,".osc")==0; + file_type_o5m= strycmp(input_file,".o5m")==0; + file_type_o5c= strycmp(input_file,".o5c")==0; + file_type_pbf= strycmp(input_file,".pbf")==0; + if(!file_type_osm && !file_type_osc && !file_type_o5m && + !file_type_o5c && !file_type_pbf) { + DD(talk_input_file_suffix) + continue; + } + if(input_file[strcspn(input_file,"\"\', :;|&\\")]!=0 || + !file_exists(input_file)) { + DD(talk_not_found) + continue; + } + break; + } + DD(talk_thanks) + + // choose function + DD(talk_section) + for(;;) { + function_convert= function_update= function_border_polygon= + function_border_box= function_statistics= false; + DD(talk_function) + DI(s) + i= 0; // here: number of selected functions + sp= s; + while(*sp!=0) { + if(*sp=='1') + function_convert= true; + else if(*sp=='2') + function_update= true; + else if(*sp=='3') + function_border_box= true; + else if(*sp=='4') + function_border_polygon= true; + else if(*sp=='5') + function_drop_author= true; + else if(*sp=='6') + function_statistics= true; + else if(*sp=='a' || *sp=='A') { + if(function_cut_mode==0) + function_cut_mode= 1; + } + else if(*sp=='b' || *sp=='B') + function_cut_mode= 2; + else if(*sp==' ' || *sp==',' || *sp==';') { + sp++; + continue; + } + else { // syntax error + i= 0; // ignore previous input + break; + } + i++; sp++; + } + if(function_border_box && function_border_polygon) { + DD(talk_two_borders) + continue; + } + if(i==0) { // no function has been chosen OR syntax error + DD(talk_cannot_understand) + continue; + } + if(function_cut_mode!=0 && + !function_border_box && !function_border_polygon) + function_border_box= true; + break; + } + function_only_statistics= function_statistics && + !function_convert && !function_update && + !function_border_polygon && !function_border_box; + DD(talk_all_right) + + // choose OSM Changefile + if(function_update) { + DD(talk_section) + for(;;) { + DD(talk_changefile) + DI(changefile) + if(strycmp(changefile,".osc")!=0 && + strycmp(changefile,".o5c")!=0) { + DD(talk_changefile_suffix) + continue; + } + if(changefile[strcspn(changefile,"\"\' ,:;|&\\")]!=0 || + !file_exists(changefile)) { + DD(talk_not_found) + continue; + } + break; + } + DD(talk_thanks) + } + + // choose polygon file + if(function_border_polygon) { + DD(talk_section) + for(;;) { + DD(talk_polygon_file) + DI(polygon_file) + if(strycmp(polygon_file,".poly")!=0) { + DD(talk_polygon_file_suffix) + continue; + } + if(polygon_file[strcspn(polygon_file,"\"\' ,:;|&\\")]!=0 || + !file_exists(polygon_file)) { + DD(talk_not_found) + continue; + } + break; + } + DD(talk_thanks) + } + + // choose coordinates + if(function_border_box) { + DD(talk_section) + for(;;) { + #define D(s) DI(s) \ + while(strchr(s,',')!=NULL) *strchr(s,',')= '.'; \ + if(s[0]==0 || s[strspn(s,"0123456789.-")]!=0) { \ + DD(talk_cannot_understand) continue; } + DD(talk_coordinates) + DD(talk_minlon) + D(minlon) + DD(talk_minlat) + D(minlat) + DD(talk_maxlon) + D(maxlon) + DD(talk_maxlat) + D(maxlat) + #undef D + break; + } + DD(talk_thanks) + } + + // choose file type + if(function_convert) { + file_type_osm= file_type_osc= file_type_o5m= + file_type_o5c= file_type_pbf= false; + DD(talk_section) + for(;;) { + DD(talk_output_format) + DI(s) + sp= s; while(*sp==' ') sp++; // ignore spaces + if(*sp=='1') + file_type_osm= true; + else if(*sp=='2') + file_type_o5m= true; + else if(*sp=='3') + file_type_pbf= true; + else { + DD(talk_cannot_understand) + continue; + } + break; + } + DD(talk_thanks) + } + + // assemble output file name + DD(talk_section) + if(!function_only_statistics) { + if(file_type_osm) strcpy(s,".osm"); + if(file_type_osc) strcpy(s,".osc"); + if(file_type_o5m) strcpy(s,".o5m"); + if(file_type_o5c) strcpy(s,".o5c"); + if(file_type_pbf) strcpy(s,".pbf"); + sp= stpcpy0(output_file,"-o="); + strcpy(sp,input_file); + sp= strrchr(sp,'.'); + if(sp==NULL) sp= strchr(output_file,0); + i= 1; + do + sprintf(sp,"_%02i%s",i,s); + while(++i<9999 && file_exists(output_file+3)); + } + + /* create new commandline arguments */ { + int argc; + static char* argv[10]; + static char border[550]; + + argc= 0; + argv[argc++]= (*argvp)[0]; // save program name + if(verbose) + argv[argc++]= "-v"; // activate verbose mode + argv[argc++]= input_file; + if(function_update) + argv[argc++]= changefile; + if(function_border_polygon) { + sp= stpcpy0(border,"-B="); + strcpy(sp,polygon_file); + argv[argc++]= border; + } + else if(function_border_box) { + sprintf(border,"-b=%s,%s,%s,%s",minlon,minlat,maxlon,maxlat); + argv[argc++]= border; + } + if(function_drop_author) + argv[argc++]= "--drop-author"; + if(function_cut_mode==1) + argv[argc++]= "--complete-ways"; + if(function_cut_mode==2) + argv[argc++]= "--complex-ways"; + if(function_only_statistics) + argv[argc++]= "--out-statistics"; + else if(function_statistics) + argv[argc++]= "--statistics"; + if(output_file[0]!=0) { + if(file_type_osm) argv[argc++]= "--out-osm"; + else if(file_type_osc) argv[argc++]= "--out-osc"; + else if(file_type_o5m) argv[argc++]= "--out-o5m"; + else if(file_type_o5c) argv[argc++]= "--out-o5c"; + else if(file_type_pbf) argv[argc++]= "--out-pbf"; + argv[argc++]= output_file; + } + // return commandline variables + *argcp= argc; + *argvp= argv; + + // display the virtual command line + DD(talk_working) + fprintf(stderr,"osmconvert"); + i= 0; + while(++i0) { + // for every parameter in command line + if(parafile!=NULL) do { + // there are parameters waiting in a parameter file + ap= aa; + for(;;) { + aamax= main__aaM-1-(ap-aa); + if(fgets(ap,aamax,parafile)==NULL) { + if(ap>aa) { + if(ap>aa && ap[-1]==' ') + *--ap= 0; // cut one trailing space + break; + } + goto parafileend; + } + if(strzcmp(ap,"// ")==0) + continue; + if(ap>aa && (*ap=='\r' || *ap=='\n' || *ap==0)) { + // end of this parameter + while(ap>aa && (ap[-1]=='\r' || ap[-1]=='\n')) *--ap= 0; + // eliminate trailing NL + if(ap>aa && ap[-1]==' ') + *--ap= 0; // cut one trailing space + break; + } + ap= strchr(ap,0); // find end of string + while(ap>aa && (ap[-1]=='\r' || ap[-1]=='\n')) + *--ap= 0; // cut newline chars + *ap++= ' '; *ap= 0; // add a space + } + a= aa; + while(*a!=0 && strchr(" \t\r\n",*a)!=NULL) a++; + if(*a!=0) + break; + parafileend: + fclose(parafile); parafile= NULL; + free(aa); aa= NULL; + } while(false); + if(parafile==NULL) { + if(--argc<=0) + break; + argv++; // switch to next parameter; as the first one is just + // the program name, we must do this previous reading the + // first 'real' parameter; + a= argv[0]; + } + if((l= strzlcmp(a,"--parameter-file="))>0 && a[l]!=0) { + // parameter file + parafile= fopen(a+l,"r"); + if(parafile==NULL) { + PERRv("Cannot open parameter file: %.80s",a+l) + perror("osmconvert"); +return 1; + } + aa= (char*)malloc(main__aaM); + if(aa==NULL) { + PERR("Cannot get memory for parameter file.") + fclose(parafile); parafile= NULL; +return 1; + } + aa[0]= 0; + continue; // take next parameter + } + if(loglevel>0) // verbose mode + fprintf(stderr,"osmconvert Parameter: %.2000s\n",a); + if(strcmp(a,"-h")==0) { // user wants parameter overview + fprintf(stdout,"%s",shorthelptext); // print brief help text + // (took "%s", to prevent oversensitive compiler reactions) +return 0; + } + if(strcmp(a,"-help")==0 || strcmp(a,"--help")==0) { + // user wants help text + fprintf(stdout,"%s",helptext); // print help text + // (took "%s", to prevent oversensitive compiler reactions) +return 0; + } + if(strzcmp(a,"--diff-c")==0) { + // user wants a diff file to be calculated + global_diffcontents= true; + global_diff= true; + continue; // take next parameter + } + if(strcmp(a,"--diff")==0) { + // user wants a diff file to be calculated + global_diff= true; + continue; // take next parameter + } + if(strcmp(a,"--subtract")==0) { + // user wants to subtract any following input file + global_subtract= true; + continue; // take next parameter + } + if(strzcmp(a,"--drop-his")==0) { + // (deprecated) + PINFO("Option --drop-history is deprecated. Using --drop-author."); + global_dropauthor= true; + continue; // take next parameter + } + if(strzcmp(a,"--drop-aut")==0) { + // user does not want author information in standard output + global_dropauthor= true; + continue; // take next parameter + } + if(strzcmp(a,"--drop-ver")==0) { + // user does not want version number in standard output + global_dropauthor= true; + global_dropversion= true; + continue; // take next parameter + } + if(strzcmp(a,"--fake-his")==0) { + // (deprecated) + PINFO("Option --fake-history is deprecated. Using --fake-author."); + global_fakeauthor= true; + continue; // take next parameter + } + if(strzcmp(a,"--fake-aut")==0) { + // user wants faked author information + global_fakeauthor= true; + continue; // take next parameter + } + if(strzcmp(a,"--fake-ver")==0) { + // user wants just a faked version number as meta data + global_fakeversion= true; + continue; // take next parameter + } + if(strzcmp(a,"--fake-lonlat")==0) { + // user wants just faked longitude and latitude + // in case of delete actions (.osc files); + global_fakelonlat= true; + continue; // take next parameter + } + if(strzcmp(a,"--drop-bro")==0) { + // user does not want broken references in standard output + global_dropbrokenrefs= true; + continue; // take next parameter + } + if(strzcmp(a,"--drop-nod")==0) { + // user does not want nodes section in standard output + global_dropnodes= true; + continue; // take next parameter + } + if(strzcmp(a,"--drop-way")==0) { + // user does not want ways section in standard output + global_dropways= true; + continue; // take next parameter + } + if(strzcmp(a,"--drop-rel")==0) { + // user does not want relations section in standard output + global_droprelations= true; + continue; // take next parameter + } + if(strzcmp(a,"--merge-ver")==0) { + // user wants duplicate versions in input files to be merged + global_mergeversions= true; + continue; // take next parameter + } + if((l= strzlcmp(a,"--csv="))>0 && a[l]!=0) { + // user-defined columns for csv format + csv_ini(a+l); + global_outcsv= true; + continue; // take next parameter + } + if(strcmp(a,"--csv-headline")==0) { + // write headline to csv output + global_csvheadline= true; + global_outcsv= true; + continue; // take next parameter + } + if((l= strzlcmp(a,"--csv-separator="))>0 && a[l]!=0) { + // user-defined separator for csv format + strMcpy(global_csvseparator,a+l); + global_outcsv= true; + continue; // take next parameter + } + if(strcmp(a,"--in-josm")==0) { + // deprecated; + // this option is still accepted for compatibility reasons; + continue; // take next parameter + } + if(strcmp(a,"--out-o5m")==0 || + strcmp(a,"-5")==0) { + // user wants output in o5m format + global_outo5m= true; + continue; // take next parameter + } + if(strcmp(a,"--out-o5c")==0 || + strcmp(a,"-5c")==0) { + // user wants output in o5m format + global_outo5m= global_outo5c= true; + continue; // take next parameter + } + if(strcmp(a,"--out-osm")==0) { + // user wants output in osm format + global_outosm= true; + continue; // take next parameter + } + if(strcmp(a,"--out-osc")==0) { + // user wants output in osc format + global_outosc= true; + continue; // take next parameter + } + if(strcmp(a,"--out-osh")==0) { + // user wants output in osc format + global_outosh= true; + continue; // take next parameter + } + if(strcmp(a,"--out-none")==0) { + // user does not want any standard output + global_outnone= true; + continue; // take next parameter + } + if(strcmp(a,"--out-pbf")==0) { + // user wants output in PBF format + global_outpbf= true; + continue; // take next parameter + } + if(strcmp(a,"--out-csv")==0) { + // user wants output in CSV format + global_outcsv= true; + continue; // take next parameter + } + if((l= strzlcmp(a,"--pbf-granularity="))>0 && a[l]!=0) { + // specify lon/lat granularity for .pbf input files + global_pbfgranularity= oo__strtouint32(a+l); + global_pbfgranularity100= global_pbfgranularity/100; + global_pbfgranularity= global_pbfgranularity100*100; + if(global_pbfgranularity==1) global_pbfgranularity= 0; + continue; // take next parameter + } + if(strzcmp(a,"--emulate-pbf2")==0) { + // emulate pbf2osm compatible output + global_emulatepbf2osm= true; + continue; // take next parameter + } + if(strzcmp(a,"--emulate-osmo")==0) { + // emulate Osmosis compatible output + global_emulateosmosis= true; + continue; // take next parameter + } + if(strzcmp(a,"--emulate-osmi")==0) { + // emulate Osmium compatible output + global_emulateosmium= true; + continue; // take next parameter + } + if((l= strzlcmp(a,"--timestamp="))>0 && a[l]!=0) { + // user-defined file timestamp + global_timestamp= oo__strtimetosint64(a+l); + continue; // take next parameter + } + if(strcmp(a,"--out-timestamp")==0) { + // user wants output in osc format + global_outtimestamp= true; + continue; // take next parameter + } + if(strcmp(a,"--statistics")==0) { + // print statistics (usually to stderr) + global_statistics= true; + continue; // take next parameter + } + if(strcmp(a,"--out-statistics")==0) { // print statistics to stdout + global_outstatistics= true; + global_statistics= true; + global_outnone= true; + continue; // take next parameter + } + if(strcmp(a,"--complete-ways")==0) { + // do not clip ways when applying borders + global_completeways= true; + continue; // take next parameter + } + if(strcmp(a,"--complex-ways")==0) { + // do not clip multipolygons when applying borders + global_complexways= true; + continue; // take next parameter + } + if(strcmp(a,"--all-to-nodes")==0) { + // convert ways and relations to nodes + if(global_calccoords==0) global_calccoords= 1; + global_alltonodes= true; + continue; // take next parameter + } + if(strcmp(a,"--all-to-nodes-bbox")==0) { + // convert ways and relations to nodes, + // and compute a bounding box + PINFO("Option --all-to-nodes-bbox is deprecated. " + "Using --all-to-nodes and --add-bbox-tags."); + global_calccoords= -1; + global_alltonodes= true; + global_addbbox= true; + global_add= true; + continue; // take next parameter + } + if(strcmp(a,"--add-bbox-tags")==0) { + // compute a bounding box and add it as tag + global_calccoords= -1; + global_addbbox= true; + global_add= true; + continue; // take next parameter + } + if(strcmp(a,"--add-bboxarea-tags")==0) { + // compute a bounding box and add its area as tag + global_calccoords= -1; + global_addbboxarea= true; + global_add= true; + continue; // take next parameter + } + if(strcmp(a,"--add-bboxweight-tags")==0) { + // compute a bounding box and add its weight as tag + global_calccoords= -1; + global_addbboxweight= true; + global_add= true; + continue; // take next parameter + } + if((l= strzlcmp(a,"--max-objects="))>0 && a[l]!=0) { + // define maximum number of objects for --all-to-nodes + global_maxobjects= oo__strtosint64(a+l); + if(global_maxobjects<4) global_maxobjects= 4; + continue; // take next parameter + } + if((l= strzlcmp(a,"--max-refs="))>0 && a[l]!=0) { + // define maximum number of references + global_maxrefs= oo__strtosint64(a+l); + if(global_maxrefs<1) global_maxrefs= 1; + continue; // take next parameter + } + if((l= strzlcmp(a,"--object-type-offset="))>0 && a[l]!=0) { + // define id offset for ways and relations for --all-to-nodes + global_otypeoffset10= oo__strtosint64(a+l); + if(global_otypeoffset10<10) global_otypeoffset10= 10; + if(strstr(a+l,"+1")!=NULL) + global_otypeoffsetstep= true; + continue; // take next parameter + } + if(strzcmp(a,"-t=")==0 && a[3]!=0) { + // user-defined prefix for names of temorary files + strmcpy(global_tempfilename,a+3,sizeof(global_tempfilename)-30); + continue; // take next parameter + } + if(strzcmp(a,"-o=")==0 && a[3]!=0) { + // reroute standard output to a file + strMcpy(outputfilename,a+3); + continue; // take next parameter + } + if((strcmp(a,"-v")==0 || strcmp(a,"--verbose")==0 || + strzcmp(a,"-v=")==0 || strzcmp(a,"--verbose=")==0) && + loglevel==0) { // test mode - if not given already + char* sp; + + sp= strchr(a,'='); + if(sp!=NULL) loglevel= sp[1]-'0'; else loglevel= 1; + if(loglevel<1) loglevel= 1; + if(loglevel>MAXLOGLEVEL) loglevel= MAXLOGLEVEL; + if(a[1]=='-') { // must be "--verbose" and not "-v" + if(loglevel==1) + fprintf(stderr,"osmconvert: Verbose mode.\n"); + else + fprintf(stderr,"osmconvert: Verbose mode %i.\n",loglevel); + } + continue; // take next parameter + } + if(strcmp(a,"-t")==0) { + // test mode + write_testmode= true; + fprintf(stderr,"osmconvert: Entering test mode.\n"); + continue; // take next parameter + } + if(((l= strzlcmp(a,"--hash-memory="))>0 || + (l= strzlcmp(a,"-h="))>0) && isdig(a[l])) { + // "-h=...": user wants a specific hash size; + const char* p; + + p= a+l; // jump over "-h=" + h_n= h_w= h_r= 0; + // read the up to three values for hash tables' size; + // format examples: "-h=200-20-10", "-h=1200" + while(isdig(*p)) { h_n= h_n*10+*p-'0'; p++; } + if(*p!=0) { p++; while(isdig(*p)) { h_w= h_w*10+*p-'0'; p++; } } + if(*p!=0) { p++; while(isdig(*p)) { h_r= h_r*10+*p-'0'; p++; } } + continue; // take next parameter + } + if(strzcmp(a,"-b=")==0) { + // border consideration by a bounding box + if(!border_box(a+3)) { + fprintf(stderr,"osmconvert Error: use border format: " + " -b=\"x1,y1,x2,y2\"\n"); +return 3; + } // end border consideration by a bounding box + continue; // take next parameter + } + if(strzcmp(a,"-B=")==0) { + // border consideration by polygon file + if(!border_file(a+3)) { + fprintf(stderr, + "osmconvert Error: no polygon file or too large: %s\n",a); +return 4; + } // end border consideration by polygon file + continue; // take next parameter + } + if(strcmp(a,"-")==0) { // use standard input + usesstdin= true; + if(oo_open(NULL)) // file cannot be read +return 2; + continue; // take next parameter + } + if(a[0]=='-') { + PERRv("unrecognized option: %.80s",a) +return 1; + } + // here: parameter must be a file name + if(strcmp(a,"/dev/stdin")==0) + usesstdin= true; + if(oo_open(a)) // file cannot be read +return 2; + } // end for every parameter in command line + + // process parameters + global_subtract= false; + if(usesstdin && global_completeways) { + PERR("cannot apply --complete-ways when reading standard input.") +return 2; + } + if(usesstdin && global_complexways) { + PERR("cannot apply --complex-ways when reading standard input.") +return 2; + } + if(global_completeways || global_complexways) { + uint32_t zlibflags; + zlibflags= zlibCompileFlags(); + if(loglevel>=2) { + PINFOv("zlib "ZLIB_VERSION" flags: %08"PRIx32"",zlibflags) + } + //if((zlibflags&0xc0) <= 0x40) + //WARN("you are using the 32 bit zlib. Hence file size max. 2 GB.") + } + if(oo_ifn==0) { // no input files given + PERR("use \"-\" to read from standard input or try: osmconvert -h") +return 0; // end the program, because without having input files + // we do not know what to do; + } + if(outputfilename[0]!=0 && !global_outo5m && + !global_outo5c && !global_outosm && !global_outosc && + !global_outosh && !global_outpbf && !global_outcsv && + !global_outnone && !global_outstatistics) { + // have output file name AND output format not defined + // try to determine the output format by evaluating + // the file name extension + if(strycmp(outputfilename,".o5m")==0) global_outo5m= true; + else if(strycmp(outputfilename,".o5c")==0) + global_outo5m= global_outo5c= true; + else if(strycmp(outputfilename,".osm")==0) global_outosm= true; + else if(strycmp(outputfilename,".osc")==0) global_outosc= true; + else if(strycmp(outputfilename,".osh")==0) global_outosh= true; + else if(strycmp(outputfilename,".pbf")==0) global_outpbf= true; + else if(strycmp(outputfilename,".csv")==0) global_outcsv= true; + } + if(write_open(outputfilename[0]!=0? outputfilename: NULL)!=0) +return 3; + if(border_active || global_dropbrokenrefs) { // user wants borders + int r; + + if(global_diff) { + PERR( + "-b=, -B=, --drop-brokenrefs must not be combined with --diff"); +return 6; + } + if(h_n==0) h_n= 1000; // use standard value if not set otherwise + if(h_w==0 && h_r==0) { + // user chose simple form for hash memory value + // take the one given value as reference and determine the + // three values using these factors: 90%, 9%, 1% + h_w= h_n/10; h_r= h_n/100; + h_n-= h_w; h_w-= h_r; } + r= hash_ini(h_n,h_w,h_r); // initialize hash table + if(r==1) + fprintf(stderr,"osmconvert: Hash size had to be reduced.\n"); + else if(r==2) + fprintf(stderr,"osmconvert: Not enough memory for hash.\n"); + } // end user wants borders + if(global_outo5m || border_active || global_dropbrokenrefs || + global_calccoords!=0) { + // .o5m format is needed as output + if(o5_ini()!=0) { + fprintf(stderr,"osmconvert: Not enough memory for .o5m buffer.\n"); +return 5; + } + } // end user wants borders + if(global_diff) { + if(oo_ifn!=2) { + PERR("Option --diff requires exactly two input files."); +return 7; + } + if(!global_outosc && !global_outosh && !global_outo5c) + global_outosc= true; + } // end diff + sprintf(strchr(global_tempfilename,0),".%"PRIi64,(int64_t)getpid()); + if(loglevel>=2) + fprintf(stderr,"Tempfiles: %s.*\n",global_tempfilename); + if(global_calccoords!=0) + posi_ini(); + if(global_outcsv) + csv_ini(NULL); + + // do the work + r= oo_main(); + if(loglevel>=2) { // verbose + if(read_bufp!=NULL && read_bufp0) { // verbose mode + if(oo_sequenceid!=INT64_C(-0x7fffffffffffffff)) + fprintf(stderr,"osmconvert: Last processed: %s %"PRIu64".\n", + ONAME(oo_sequencetype),oo_sequenceid); + if(r!=0) + fprintf(stderr,"osmconvert Exit: %i\n",r); + } // verbose mode + assistant(NULL,NULL); + return r; + } // end main() +