--- /dev/null
+To build gpx-import:
+
+You will need a C compiler, and the following Debian/Ubuntu packages:
+ zlib1g-dev libbz2-dev libarchive-dev libexpat1-dev libgd2-noxpm-dev
+
+then run
+ make -C src
+
+To run gpx-import, ./settings.sh src/gpx-import
+
+Enjoy,
+
+Daniel.
--- /dev/null
+ OpenStreetMap GPX Import Daemon
+ Copyright (C) 2008 Daniel Silverstone <dsilvers@digital-scurf.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License *ONLY*.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+
+The full terms of the GNU GPL follow:
+
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+\f
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
--- /dev/null
+#!/bin/sh
+
+# This script configures the environment to use the OSM db
+# so that the gpx-import program can find it.
+
+setting () {
+ S_N=GPX_$1
+ shift
+ eval "${S_N}='$*'"
+ export ${S_N}
+}
+
+# General settings
+setting SLEEP_TIME 1
+
+# Paths (can be relative from invocation path if appropriate)
+setting PATH_TRACES /tmp/osm/traces
+setting PATH_IMAGES /tmp/osm/images
+setting PATH_TEMPLATES templates/
+
+# MySQL connection
+setting MYSQL_HOST localhost
+setting MYSQL_USER openstreetmap
+setting MYSQL_DB openstreetmap
+setting MYSQL_PASS openstreetmap
+
+# Optional debug statements
+#setting INTERPOLATE_STDOUT 1
+
+# Run the commandline
+
+exec "$@"
+
--- /dev/null
+# gpx-import/src/Makefile
+#
+# GPX Import tool for OpenStreetMap
+#
+# Copyright 2008 Daniel Silverstone <dsilvers@digital-scurf.org>
+#
+
+all: gpx-import
+
+DB := mysql
+
+OPT := -O0
+CFLAGS := $(OPT) -g -Wall -std=c99 -D_POSIX_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE -include log.h
+
+# Comment this out to get the DEBUG log lines
+CFLAGS += -DNDEBUG
+
+# Get dep files
+CFLAGS += -MD
+
+CFLAGS += $(shell gdlib-config --cflags)
+LDFLAGS += $(shell gdlib-config --libs) -lgd
+
+ifeq ($(DB),mysql)
+CFLAGS += $(shell mysql_config --cflags)
+LDFLAGS += $(shell mysql_config --libs)
+endif
+
+MAINOBJS := main.o gpx.o mercator.o image.o log.o db.o filename.o interpolate.o quadtile.o
+
+ALLOBJS := $(DB).o $(MAINOBJS)
+
+gpx-import: $(ALLOBJS)
+ gcc $(LDFLAGS) -o $@ $^ -lexpat -larchive -lz -lbz2
+
+clean:
+ $(RM) *.o gpx-import *.d
+
+distclean: clean
+ $(RM) *~
+
+-include $(patsubst %.o,%.d,$(ALLOBJS))
--- /dev/null
+/* gpx-import/src/db.h
+ *
+ * Database interface layer
+ *
+ * Copyright 2008 Daniel Silverstone <dsilvers@digital-scurf.org>
+ *
+ * Written for OpenStreetMap
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the License
+ * only.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "db.h"
+
+void
+db_free_job(DBJob *job)
+{
+ if (job == NULL)
+ return;
+
+ if (job->gpx != NULL) {
+ gpx_free(job->gpx);
+ }
+
+ free(job->title);
+ free(job->description);
+ free(job->tags);
+ free(job->email);
+ free(job->error);
+
+ free(job);
+}
+
+void
+db_error(DBJob *job, const char *fmt, ...)
+{
+ va_list va;
+ int sz;
+
+ va_start(va, fmt);
+ sz = vsnprintf(NULL, 0, fmt, va);
+ job->error = malloc(sz + 1);
+ vsnprintf(job->error, sz + 1, fmt, va);
+ va_end(va);
+}
+
--- /dev/null
+/* gpx-import/src/db.h
+ *
+ * Database interface layer
+ *
+ * Copyright 2008 Daniel Silverstone <dsilvers@digital-scurf.org>
+ *
+ * Written for OpenStreetMap
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the License
+ * only.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef GPX_IMPORT_DB_H
+#define GPX_IMPORT_DB_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "gpx.h"
+
+typedef struct {
+ GPX *gpx;
+ int64_t gpx_id;
+ char *title;
+ char *description;
+ char *tags;
+ char *email;
+
+ /* If this is non-NULL then an error has occurred */
+ char *error;
+} DBJob;
+
+/* All these functions are in the database backend files. */
+extern bool db_connect(void);
+extern DBJob *db_find_work(int minage);
+extern bool db_insert_gpx(DBJob *job);
+extern int64_t db_find_invisible(void);
+extern bool db_destroy_trace(int64_t jobnr);
+extern void db_disconnect(void);
+
+/* These are implemented in db.c rather than in any of the backends.
+ */
+extern void db_free_job(DBJob *job);
+extern void db_error(DBJob *job, const char *fmt, ...);
+
+#endif /* GPX_IMPORT_DB_H */
--- /dev/null
+/* gpx-import/src/filename.c
+ *
+ * GPX importer, filename generator
+ *
+ * Copyright Daniel Silverstone <dsilvers@digital-scurf.org>
+ *
+ * Written for the OpenStreetMap project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the License
+ * only.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <stdio.h>
+#include <limits.h>
+#include <stdlib.h>
+
+#include "filename.h"
+
+char *
+make_filename(const char *base, int64_t nr, const char *suffix)
+{
+ static char namebuffer[PATH_MAX];
+ char *real_base = getenv(base);
+
+ if (real_base == NULL) {
+ WARN("Unable to find base from: %s", base);
+ real_base = ".";
+ }
+
+ snprintf(namebuffer, PATH_MAX, "%s/%ld%s", real_base, nr, suffix);
+
+ return namebuffer;
+}
+
--- /dev/null
+/* gpx-import/src/filename.h
+ *
+ * GPX importer, filename generator
+ *
+ * Copyright Daniel Silverstone <dsilvers@digital-scurf.org>
+ *
+ * Written for the OpenStreetMap project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the License
+ * only.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef GPX_FILENAME_H
+#define GPX_FILENAME_H
+
+#include <stdint.h>
+
+/* memory buffer is current shared, copy if you need to retain. */
+extern char *make_filename(const char *base, int64_t nr, const char *suffix);
+
+#endif /* GPX_FILENAME_H */
--- /dev/null
+/* gpx-import/src/gpx.c
+ *
+ * GPX load and memory management
+ *
+ * Copyright Daniel Silverstone <dsilvers@digital-scurf.org>
+ *
+ * Written for the OpenStreetMap project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the License
+ * only.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <sys/types.h>
+#include <expat.h>
+#include <stdlib.h>
+#include <alloca.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <string.h>
+#include <strings.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <time.h>
+
+#include <archive.h>
+#include <archive_entry.h>
+
+#include <zlib.h>
+#include <bzlib.h>
+
+#include "gpx.h"
+
+#define GPX_BUFLEN 4096
+#define ERR_BUFFER_SIZE 1024
+
+#define PARSE_ERROR(ctx,F...) \
+ do { \
+ if (ctx->err != NULL && *(ctx->err) == NULL) \
+ gpx_record_error(ctx, F); \
+ } while (0)
+
+void
+gpx_free(GPX *gpx)
+{
+ GPXTrackPoint *pt = gpx->points, *ptnext;
+ while (pt != NULL) {
+ ptnext = pt->next;
+ free(pt->timestamp);
+ free(pt);
+ pt = ptnext;
+ }
+ free(gpx);
+}
+
+typedef enum {
+ UNKNOWN,
+ TRACKPOINT,
+ ELEVATION,
+ TIMESTAMP,
+} GPXParseState;
+
+static const char *
+gpx_state_name(GPXParseState p)
+{
+ switch (p) {
+ case UNKNOWN:
+ return "UNKNOWN";
+ case TRACKPOINT:
+ return "TRACKPOINT";
+ case ELEVATION:
+ return "ELEVATION";
+ case TIMESTAMP:
+ return "TIMESTAMP";
+ }
+ return "INVALID";
+}
+
+typedef struct {
+ XML_Parser p;
+ GPX *gpx;
+ GPXTrackPoint *point;
+ GPXTrackPoint *lastpoint;
+ uint32_t curseg;
+ char *accumulator;
+ int accumulator_size;
+ GPXParseState state;
+ bool got_lat, got_long, got_ele, got_time;
+ char **err;
+ const char *subfile;
+} GPXParseContext;
+
+static void
+gpx_record_error(GPXParseContext *ctx, char *fmt, ...)
+{
+ va_list va;
+ int sz;
+ char msg[ERR_BUFFER_SIZE];
+
+ va_start(va, fmt);
+ sz = vsnprintf(msg, ERR_BUFFER_SIZE, fmt, va);
+ va_end(va);
+
+ *(ctx->err) = calloc(2, ERR_BUFFER_SIZE);
+ snprintf(*(ctx->err), 2 * ERR_BUFFER_SIZE, "%s%s%s%s\n XML parser at line %ld column %ld",
+ (ctx->subfile == NULL) ? "" : "In file ",
+ (ctx->subfile == NULL) ? "" : ctx->subfile,
+ (ctx->subfile == NULL) ? "" : " inside your upload:\n ",
+ msg,
+ XML_GetCurrentLineNumber(ctx->p),
+ XML_GetCurrentColumnNumber(ctx->p));
+}
+
+static GPXCoord
+gpx_parse_coord(const XML_Char *str)
+{
+ GPXCoord ret = 0;
+ bool is_neg = false;
+ bool is_afterdot = false;
+ int afterdot = 0;
+
+ if (*str == '-') {
+ is_neg = true;
+ str++;
+ }
+ while (*str != '\0' && afterdot < 9) {
+ if (*str == '.')
+ is_afterdot = true;
+ if (*str != '.') {
+ ret *= 10;
+ ret += (*str - '0');
+ if (is_afterdot == true)
+ afterdot++;
+ }
+ str++;
+ }
+
+ while (afterdot < 9) {
+ afterdot++;
+ ret *= 10;
+ }
+
+ return (is_neg ? -ret : ret);
+}
+
+static void
+gpx_clear_accumulator(GPXParseContext *ctx)
+{
+ if (ctx->accumulator != NULL)
+ free(ctx->accumulator);
+ ctx->accumulator = NULL;
+ ctx->accumulator_size = 0;
+}
+
+#define REQUIRE_STATE(S) \
+ if (ctx->state != (S)) { \
+ PARSE_ERROR(ctx, "Expected state %s, got state %s", gpx_state_name(S), gpx_state_name(ctx->state)); \
+ XML_StopParser(ctx->p, 0); \
+ return; \
+ }
+
+static void
+gpx_handle_start_element(void *_ctx, const XML_Char *name, const XML_Char **atts)
+{
+ GPXParseContext *ctx = (GPXParseContext *)(_ctx);
+ if (strcmp(name, "trkpt") == 0) {
+ REQUIRE_STATE(UNKNOWN);
+ ctx->state = TRACKPOINT;
+ ctx->point = calloc(1, sizeof(GPXTrackPoint));
+ ctx->point->segment = ctx->curseg;
+ ctx->got_lat = ctx->got_long = ctx->got_ele = ctx->got_time = false;
+ while (*atts != NULL) {
+ if (strcmp(*atts, "lat") == 0) {
+ atts++;
+ ctx->point->latitude = gpx_parse_coord(*atts++);
+ ctx->got_lat = true;
+ } else if (strcmp(*atts, "lon") == 0) {
+ atts++;
+ ctx->point->longitude = gpx_parse_coord(*atts++);
+ ctx->got_long = true;
+ } else {
+ atts += 2; /* skip tag + value */
+ }
+ }
+ } else if (strcmp(name, "ele") == 0) {
+ REQUIRE_STATE(TRACKPOINT);
+ ctx->state = ELEVATION;
+ gpx_clear_accumulator(ctx);
+ } else if (strcmp(name, "time") == 0) {
+ if (ctx->state == TRACKPOINT) {
+ ctx->state = TIMESTAMP;
+ gpx_clear_accumulator(ctx);
+ }
+ }
+}
+
+#ifndef MIN
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+#ifndef MAX
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+
+static void
+gpx_update_minmax(GPX *gpx, GPXTrackPoint *point)
+{
+ gpx->minlatitude = MIN(gpx->minlatitude, point->latitude);
+ gpx->minlongitude = MIN(gpx->minlongitude, point->longitude);
+ gpx->maxlatitude = MAX(gpx->maxlatitude, point->latitude);
+ gpx->maxlongitude = MAX(gpx->maxlongitude, point->longitude);
+}
+
+static void
+gpx_handle_end_element(void *_ctx, const XML_Char *name)
+{
+ GPXParseContext *ctx = (GPXParseContext *)(_ctx);
+ if (strcmp(name, "trkpt") == 0) {
+ REQUIRE_STATE(TRACKPOINT);
+ /* Remove the || true if elevation is mandatory. */
+ if (ctx->got_time == false)
+ ctx->gpx->missed_time++;
+
+ if ((ctx->got_lat == false) ||
+ (ctx->point->latitude < -90000000000) ||
+ (ctx->point->latitude > 90000000000))
+ ctx->gpx->bad_lat++;
+
+ if ((ctx->got_long == false) ||
+ (ctx->point->longitude < -180000000000) ||
+ (ctx->point->longitude > 180000000000))
+ ctx->gpx->bad_long++;
+
+ if ((ctx->got_lat && ctx->got_long && (ctx->got_ele || true) && ctx->got_time) &&
+ (ctx->point != NULL) && (ctx->point->longitude >= -180000000000) &&
+ (ctx->point->longitude <= 180000000000) &&
+ (ctx->point->latitude >= -90000000000) &&
+ (ctx->point->latitude <= 90000000000)) {
+ gpx_update_minmax(ctx->gpx, ctx->point);
+
+ if (ctx->lastpoint == NULL) {
+ INFO("Attaching first point");
+ ctx->gpx->firstlatitude = ctx->point->latitude;
+ ctx->gpx->firstlongitude = ctx->point->longitude;
+ ctx->lastpoint = ctx->point;
+ ctx->gpx->points = ctx->point;
+ ctx->point = NULL;
+ } else {
+ ctx->lastpoint->next = ctx->point;
+ ctx->lastpoint = ctx->point;
+ ctx->point = NULL;
+ }
+
+ ctx->gpx->goodpoints++;
+
+ } else {
+ ctx->gpx->badpoints++;
+ if (ctx->point->timestamp != NULL)
+ free(ctx->point->timestamp);
+ free(ctx->point);
+ ctx->point = NULL;
+ }
+ ctx->state = UNKNOWN;
+ } else if (strcmp(name, "ele") == 0) {
+ REQUIRE_STATE(ELEVATION);
+ ctx->point->elevation = strtof(ctx->accumulator, NULL);
+ ctx->state = TRACKPOINT;
+ ctx->got_ele = true;
+ } else if (strcmp(name, "time") == 0) {
+ char *pnull = NULL;
+ struct tm ignored;
+ if (ctx->state == UNKNOWN)
+ return;
+ REQUIRE_STATE(TIMESTAMP);
+ pnull = strptime(ctx->accumulator ? ctx->accumulator : "",
+ "%Y-%m-%dT%H:%M:%S",
+ &ignored);
+ if (pnull != NULL && (*pnull == '\0' || *pnull == '.' || *pnull == 'Z')) {
+ ctx->point->timestamp = ctx->accumulator;
+ ctx->accumulator = NULL;
+ ctx->got_time = true;
+ }
+ ctx->state = TRACKPOINT;
+ } else if (strcmp(name, "trkseg") == 0) {
+ REQUIRE_STATE(UNKNOWN);
+ ctx->curseg++;
+ }
+}
+
+void
+gpx_handle_string_data(void *_ctx, const XML_Char *str, int len)
+{
+ GPXParseContext *ctx = (GPXParseContext *)(_ctx);
+
+ if ((ctx->state != ELEVATION) &&
+ (ctx->state != TIMESTAMP))
+ /* We only accumulate during elevation or timestamp */
+ return;
+
+ if (ctx->accumulator == NULL) {
+ ctx->accumulator = malloc(len + 1);
+ memcpy(ctx->accumulator, str, len);
+ ctx->accumulator_size = len + 1;
+ } else {
+ ctx->accumulator = realloc(ctx->accumulator, ctx->accumulator_size + len);
+ memcpy(ctx->accumulator + ctx->accumulator_size - 1, str, len);
+ ctx->accumulator_size += len;
+ }
+
+ ctx->accumulator[ctx->accumulator_size - 1] = '\0';
+}
+
+static bool
+gpx_create_parser(GPXParseContext *ctx)
+{
+ ctx->p = XML_ParserCreate(NULL);
+
+ if (ctx->p == NULL)
+ return false;
+
+ XML_SetElementHandler(ctx->p, gpx_handle_start_element, gpx_handle_end_element);
+ XML_SetDefaultHandler(ctx->p, gpx_handle_string_data);
+ XML_SetUserData(ctx->p, ctx);
+
+ return true;
+}
+
+static bool
+gpx_parse_buffer(GPXParseContext *ctx, const char *buffer, ssize_t buflen)
+{
+ if (XML_Parse(ctx->p, buffer, buflen, XML_FALSE) != XML_STATUS_OK) {
+ if (*(ctx->err) == NULL) {
+ PARSE_ERROR(ctx, "Generic XML parse error");
+ }
+ return false;
+ }
+ return true;
+}
+
+static void
+gpx_free_parser(GPXParseContext *ctx)
+{
+ if (ctx->p != NULL)
+ XML_ParserFree(ctx->p);
+ ctx->p = NULL;
+}
+
+static void
+gpx_abort_context(GPXParseContext *ctx)
+{
+ gpx_free(ctx->gpx);
+ if (ctx->point != NULL) {
+ if (ctx->point->timestamp)
+ free(ctx->point->timestamp);
+ free(ctx->point);
+ }
+ if (ctx->accumulator != NULL)
+ free(ctx->accumulator);
+ gpx_free_parser(ctx);
+}
+
+static bool
+gpx_parse_plain_file(GPXParseContext *ctx, const char *gpxfile)
+{
+ char *buffer = alloca(GPX_BUFLEN);
+ ssize_t bufread;
+ int fd = open(gpxfile, O_RDONLY);
+
+ if (fd == -1) {
+ PARSE_ERROR(ctx, "Error opening file %s: %s", gpxfile, strerror(errno));
+ return false;
+ }
+
+ if (gpx_create_parser(ctx) == false) {
+ PARSE_ERROR(ctx, "Unable to create XML parser");
+ close(fd);
+ return false;
+ }
+
+ while ((bufread = read(fd, buffer, GPX_BUFLEN)) > 0) {
+ if (gpx_parse_buffer(ctx, buffer, bufread) == false) {
+ close(fd);
+ return false;
+ }
+ }
+
+ close(fd);
+
+ gpx_free_parser(ctx);
+
+ return true;
+}
+
+static bool
+gpx_parse_archive(GPXParseContext *ctx, const char *gpxfile)
+{
+ struct archive *arch;
+ struct archive_entry *ent;
+ bool okay = false;
+ int64_t sublen;
+ char *buffer = alloca(GPX_BUFLEN);
+ int ret;
+
+ arch = archive_read_new();
+ if (arch == NULL) {
+ return false;
+ }
+
+ archive_read_support_compression_gzip(arch);
+ archive_read_support_compression_bzip2(arch);
+ archive_read_support_format_all(arch);
+
+ if (archive_read_open_filename(arch, gpxfile, 1) < ARCHIVE_OK) {
+ goto out;
+ }
+
+ while ((ret = archive_read_next_header(arch, &ent)) == ARCHIVE_OK) {
+ ctx->subfile = archive_entry_pathname(ent);
+ sublen = archive_entry_size(ent);
+ if (sublen > 0) {
+ INFO("Considering sub-entry %s in job", ctx->subfile);
+ /* There's data, let's try and parse it */
+ gpx_create_parser(ctx);
+ while (sublen > 0) {
+ if (archive_read_data(arch, buffer, MIN(GPX_BUFLEN, sublen)) < ARCHIVE_OK) {
+ PARSE_ERROR(ctx, "Unable to read data from archive.");
+ goto out;
+ }
+ if (gpx_parse_buffer(ctx, buffer, MIN(GPX_BUFLEN, sublen)) == false) {
+ goto out;
+ }
+ sublen -= MIN(GPX_BUFLEN, sublen);
+ }
+ gpx_free_parser(ctx);
+ }
+ ctx->subfile = NULL;
+ }
+
+
+ if ((ret == ARCHIVE_EOF) && ctx->gpx->goodpoints > 0)
+ okay = true;
+
+ out:
+ archive_read_close(arch);
+ archive_read_finish(arch);
+ return okay;
+}
+
+static char gzip_magic[] = { 0x1f, 0x8b, 0x08 };
+static char bzip2_magic[] = { 0x42, 0x5a, 0x68, 0x39, 0x31 };
+
+static bool
+gpx_try_gzip(GPXParseContext *ctx, const char *gpxfile)
+{
+ int fd = open(gpxfile, O_RDONLY);
+ gzFile *f;
+ char *buffer = alloca(GPX_BUFLEN);
+ ssize_t bufread;
+
+ if (fd == -1) {
+ PARSE_ERROR(ctx, "Unable to open %s (errno=%s)", gpxfile, strerror(errno));
+ return false;
+ }
+
+ if (read(fd, buffer, sizeof(gzip_magic)) != sizeof(gzip_magic)) {
+ PARSE_ERROR(ctx, "Unable to read data from %s (errno=%s)", gpxfile, strerror(errno));
+ close(fd);
+ return false;
+ }
+
+ if (memcmp(buffer, gzip_magic, sizeof(gzip_magic)) != 0) {
+ close(fd);
+ return false;
+ }
+
+ lseek(fd, 0, SEEK_SET);
+
+ f = gzdopen(fd, "rb"); /* Takes over the FD */
+
+ INFO("Detected gzip encoded data");
+
+ if (gpx_create_parser(ctx) == false) {
+ PARSE_ERROR(ctx, "Unable to create XML parser");
+ gzclose(f);
+ return false;
+ }
+
+ while ((bufread = gzread(f, buffer, GPX_BUFLEN)) > 0) {
+ if (gpx_parse_buffer(ctx, buffer, bufread) == false) {
+ gzclose(f);
+ return false;
+ }
+ }
+
+ gzclose(f);
+
+ gpx_free_parser(ctx);
+
+ return true;
+}
+
+static bool
+gpx_try_bzip2(GPXParseContext *ctx, const char *gpxfile)
+{
+ int fd = open(gpxfile, O_RDONLY);
+ BZFILE *f;
+ char *buffer = alloca(GPX_BUFLEN);
+ ssize_t bufread;
+
+ if (fd == -1) {
+ PARSE_ERROR(ctx, "Unable to open %s (errno=%s)", gpxfile, strerror(errno));
+ return false;
+ }
+
+ if (read(fd, buffer, sizeof(bzip2_magic)) != sizeof(bzip2_magic)) {
+ PARSE_ERROR(ctx, "Unable to read data from %s (errno=%s)", gpxfile, strerror(errno));
+ close(fd);
+ return false;
+ }
+
+ if (memcmp(buffer, bzip2_magic, sizeof(bzip2_magic)) != 0) {
+ close(fd);
+ return false;
+ }
+
+ close(fd);
+
+ f = BZ2_bzopen(gpxfile, "rb");
+
+ INFO("Detected bzip2 encoded data");
+
+ if (gpx_create_parser(ctx) == false) {
+ PARSE_ERROR(ctx, "Unable to create XML parser");
+ BZ2_bzclose(f);
+ return false;
+ }
+
+ while ((bufread = BZ2_bzread(f, buffer, GPX_BUFLEN)) > 0) {
+ if (gpx_parse_buffer(ctx, buffer, bufread) == false) {
+ BZ2_bzclose(f);
+ return false;
+ }
+ }
+
+ BZ2_bzclose(f);
+
+ gpx_free_parser(ctx);
+
+ return true;
+}
+
+GPX*
+gpx_parse_file(const char *gpxfile, char **err)
+{
+ GPXParseContext ctx;
+
+ ctx.err = err;
+
+ ctx.gpx = calloc(1, sizeof(GPX));
+ ctx.gpx->minlatitude = 91000000000;
+ ctx.gpx->minlongitude = 181000000000;
+ ctx.gpx->maxlatitude = -91000000000;
+ ctx.gpx->maxlongitude = -181000000000;
+ ctx.point = NULL;
+ ctx.lastpoint = NULL;
+ ctx.curseg = 0;
+ ctx.accumulator = NULL;
+ ctx.state = UNKNOWN;
+ ctx.accumulator_size = 0;
+ ctx.subfile = NULL;
+
+ if (gpx_parse_archive(&ctx, gpxfile) == true) {
+ goto success;
+ } else {
+ if (*(ctx.err) != NULL) {
+ ERROR("Archive failure");
+ goto failure;
+ }
+ }
+
+ if (gpx_try_gzip(&ctx, gpxfile) == true) {
+ goto success;
+ } else {
+ if (*(ctx.err) != NULL) {
+ ERROR("GZip failure");
+ goto failure;
+ }
+ }
+
+ if (gpx_try_bzip2(&ctx, gpxfile) == true) {
+ goto success;
+ } else {
+ if (*(ctx.err) != NULL) {
+ ERROR("BZip2 failure");
+ goto failure;
+ }
+ }
+
+ if (gpx_parse_plain_file(&ctx, gpxfile) == false) {
+ goto failure;
+ }
+
+ if (ctx.gpx->goodpoints == 0) {
+ ERROR("Zero good points, %d bad (%d missed time, %d bad lat, %d bad long)",
+ ctx.gpx->badpoints,
+ ctx.gpx->missed_time,
+ ctx.gpx->bad_lat,
+ ctx.gpx->bad_long);
+ if (ctx.gpx->missed_time > ((ctx.gpx->badpoints * 3) >> 2)) {
+ *(ctx.err) = strdup("Found no good GPX points in the input data. At least 75% of the trackpoints lacked a <time> tag.");
+ } else {
+ *(ctx.err) = strdup("Found no good GPX points in the input data");
+ }
+ goto failure;
+ }
+
+ success:
+ if (ctx.point)
+ free(ctx.point);
+ if (ctx.accumulator != NULL)
+ free(ctx.accumulator);
+
+ return ctx.gpx;
+
+ failure:
+ gpx_abort_context(&ctx);
+ return NULL;
+}
+
+void
+gpx_print(GPX *gpx)
+{
+ GPXTrackPoint *pt;
+ int pointnr;
+
+ printf("minlat=%ld maxlat=%ld minlon=%ld maxlon=%ld\n",
+ gpx->minlatitude, gpx->maxlatitude,
+ gpx->minlongitude, gpx->maxlongitude);
+
+ printf("goodpoints=%d badpoints=%d\n",
+ gpx->goodpoints,
+ gpx->badpoints);
+ return;
+ for (pt = gpx->points, pointnr = 1; pt != NULL; pt = pt->next, pointnr++)
+ printf("%4d: lat=%ld lon=%ld ele=%f time=%s seg=%d\n",
+ pointnr, pt->latitude, pt->longitude, pt->elevation,
+ pt->timestamp, pt->segment);
+
+}
--- /dev/null
+/* gpx-import/src/gpx.h
+ *
+ * GPX structures and management
+ *
+ * Copyright Daniel Silverstone <dsilvers@digital-scurf.org>
+ *
+ * Written for the OpenStreetMap project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the License
+ * only.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef GPX_IMPORT_GPX_H
+#define GPX_IMPORT_GPX_H
+
+#include <stdint.h>
+#include <sys/time.h>
+#include <time.h>
+
+typedef int64_t GPXCoord;
+
+typedef struct _GPXTrackPoint_s {
+ GPXCoord longitude, latitude, altitude;
+ char *timestamp;
+ uint32_t segment;
+ float elevation;
+ struct _GPXTrackPoint_s *next;
+} GPXTrackPoint;
+
+typedef struct {
+ GPXCoord firstlatitude, firstlongitude, minlatitude, minlongitude, maxlatitude, maxlongitude;
+ uint32_t goodpoints;
+ uint32_t badpoints;
+ uint32_t missed_time;
+ uint32_t bad_lat;
+ uint32_t bad_long;
+ GPXTrackPoint *points;
+} GPX;
+
+extern GPX* gpx_parse_file(const char *gpxfile, char **err);
+extern void gpx_free(GPX *gpx);
+extern void gpx_print(GPX *gpx);
+
+#endif
--- /dev/null
+/* gpx-import/src/image.c
+ *
+ * GPX Importer, thumbnail/icon and animation generator
+ *
+ * Copyright Daniel Silverstone <dsilvers@digital-scurf.org>
+ *
+ * Written for the OpenStreetMap project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the License
+ * only.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <gd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+#include "image.h"
+#include "mercator.h"
+
+typedef struct {
+ uint32_t x, y;
+} XYPair;
+
+static XYPair *
+image_generate_coords(GPX *gpx, MercatorProjection *proj)
+{
+ XYPair *ret = calloc(gpx->goodpoints, sizeof(XYPair));
+ GPXTrackPoint *pt;
+ uint32_t n;
+
+ for(n = 0, pt = gpx->points; n < gpx->goodpoints; ++n, pt = pt->next) {
+ mercator_projection_project(proj, pt->latitude, pt->longitude,
+ &(ret[n].x), &(ret[n].y));
+ }
+
+ return ret;
+}
+
+void
+image_generate_animation(GPX *gpx,
+ const char *outfilename,
+ uint32_t width,
+ uint32_t height,
+ uint32_t nframes)
+{
+ gdImagePtr frame[nframes];
+ FILE *out;
+ int black, white, grey;
+ XYPair *coords;
+ MercatorProjection *proj;
+ uint32_t n, oldx, oldy, curx, cury, pt;
+ uint32_t ptsper = gpx->goodpoints / nframes;
+
+
+ proj = mercator_projection_new(gpx->minlatitude,
+ gpx->minlongitude,
+ gpx->maxlatitude,
+ gpx->maxlongitude,
+ width,
+ height);
+
+ coords = image_generate_coords(gpx, proj);
+
+ for (n = 0; n < nframes; ++n) {
+ gdImagePtr f;
+ f = frame[n] = gdImageCreate(width, height);
+ if (n == 0) {
+ black = gdImageColorAllocate(f, 0, 0, 0);
+ white = gdImageColorAllocate(f, 255, 255, 255);
+ grey = gdImageColorAllocate(f, 0xBB, 0xBB, 0xBB);
+ } else {
+ gdImagePaletteCopy(frame[n], frame[0]);
+ }
+ gdImageFilledRectangle(f, 0, 0, width, height, white);
+ }
+
+ oldx = coords[0].x;
+ oldy = coords[0].y;
+
+ for (pt = 1; pt < gpx->goodpoints; ++pt) {
+ curx = coords[pt].x;
+ cury = coords[pt].y;
+
+ for (n = 0; n < nframes; ++n) {
+ if ((pt >= (ptsper * n)) && (pt <= (ptsper * (n+1)))) {
+ gdImageSetThickness(frame[n], 3);
+ gdImageSetAntiAliased(frame[n], black);
+ } else {
+ gdImageSetThickness(frame[n], 1);
+ gdImageSetAntiAliased(frame[n], grey);
+ }
+ gdImageLine(frame[n], oldx, oldy, curx, cury, gdAntiAliased);
+ }
+
+ oldx = curx;
+ oldy = cury;
+ }
+
+ out = fopen(outfilename, "wb");
+ if (out != NULL) {
+ gdImageGifAnimBegin(frame[0], out, 1, 0);
+ for (n = 0; n < nframes; ++n) {
+ gdImageGifAnimAdd(frame[n], out, 0, 0, 0, 50, gdDisposalNone, (n > 0) ? frame[n-1] : NULL);
+ }
+ gdImageGifAnimEnd(out);
+ fclose(out);
+ } else {
+ ERROR("Unable to create %s (errno=%s)", outfilename, strerror(errno));
+ }
+
+ for (n = 0; n < nframes; ++n) {
+ gdImageDestroy(frame[n]);
+ }
+
+ free(coords);
+ mercator_projection_free(proj);
+}
+
+void
+image_generate_icon(GPX *gpx,
+ const char *outfilename,
+ uint32_t width,
+ uint32_t height)
+{
+ gdImagePtr icon;
+ FILE *out;
+ int black, white;
+ XYPair *coords;
+ MercatorProjection *proj;
+ uint32_t n, oldx, oldy, curx, cury;
+
+ proj = mercator_projection_new(gpx->minlatitude,
+ gpx->minlongitude,
+ gpx->maxlatitude,
+ gpx->maxlongitude,
+ width,
+ height);
+
+ icon = gdImageCreate(width, height);
+
+ black = gdImageColorAllocate(icon, 0, 0, 0);
+ white = gdImageColorAllocate(icon, 255, 255, 255);
+
+ gdImageFilledRectangle(icon, 0, 0, width, height, white);
+
+ gdImageSetAntiAliased(icon, black);
+
+ coords = image_generate_coords(gpx, proj);
+
+ oldx = coords[0].x;
+ oldy = coords[0].y;
+
+ for(n = 1; n < gpx->goodpoints; ++n) {
+ curx = coords[n].x;
+ cury = coords[n].y;
+ gdImageLine(icon, oldx, oldy, curx, cury, gdAntiAliased);
+ oldx = curx;
+ oldy = cury;
+ }
+
+ out = fopen(outfilename, "wb");
+
+ if (out != NULL) {
+ gdImageGif(icon, out);
+ fclose(out);
+ } else {
+ ERROR("Unable to create %s (errno=%s)", outfilename, strerror(errno));
+ }
+
+ gdImageDestroy(icon);
+ free(coords);
+ mercator_projection_free(proj);
+}
--- /dev/null
+/* gpx-import/src/image.h
+ *
+ * GPX Importer, thumbnail/icon and animation generator
+ *
+ * Copyright Daniel Silverstone <dsilvers@digital-scurf.org>
+ *
+ * Written for the OpenStreetMap project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the License
+ * only.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef GPX_IMPORT_IMAGE_H
+#define GPX_IMPORT_IMAGE_H
+
+#include "gpx.h"
+
+extern void image_generate_icon(GPX *gpx,
+ const char *outfilename,
+ uint32_t width,
+ uint32_t height);
+
+extern void image_generate_animation(GPX *gpx,
+ const char *outfilename,
+ uint32_t width,
+ uint32_t height,
+ uint32_t nframes);
+
+#endif /* GPX_IMPORT_IMAGE_H */
--- /dev/null
+/* gpx-import/src/interpolate.c
+ *
+ * GPX file importer, email interpolation
+ *
+ * Copyright Daniel Silverstone <dsilvers@digital-scurf.org>
+ *
+ * Written for the OpenStreetMap project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the License
+ * only.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+
+#include <errno.h>
+
+#include "interpolate.h"
+
+static void
+do_interpolate(DBJob *job, FILE *input, FILE *output)
+{
+ int c;
+
+ while ((c = fgetc(input)) != EOF) {
+ if (c != '%') {
+ fputc(c, output);
+ continue;
+ }
+ c = fgetc(input);
+ switch (c) {
+ case -1:
+ case '%':
+ fputc('%', output);
+ break;
+ case 'e':
+ fputs(job->email, output);
+ break;
+ case 'E':
+ fputs(job->error, output);
+ break;
+ case 't':
+ fputs(job->title, output);
+ break;
+ case 'd':
+ fputs(job->description, output);
+ break;
+ case 'g':
+ fprintf(output, "%d", job->gpx->goodpoints);
+ break;
+ case 'p':
+ fprintf(output, "%d", job->gpx->goodpoints + job->gpx->badpoints);
+ break;
+ case 'T':
+ if (strlen(job->tags) > 0) {
+ fputs("and the following tags:\n\n ", output);
+ fputs(job->tags, output);
+ } else {
+ fputs("and no tags.", output);
+ }
+ break;
+ case 'm':
+ if (job->gpx->missed_time > 0) {
+ fprintf(output, "Of the failed points, %d lacked <time>", job->gpx->missed_time);
+ }
+ break;
+ case 'l':
+ if (job->gpx->bad_lat > 0) {
+ fprintf(output, "Of the failed points, %d had bad latitude", job->gpx->bad_lat);
+ }
+ break;
+ case 'L':
+ if (job->gpx->bad_long > 0) {
+ fprintf(output, "Of the failed points, %d had bad longitude", job->gpx->bad_long);
+ }
+ break;
+ default:
+ fputs("\n\n[Unknown % escape: ", output);
+ fputc(c, output);
+ fputs("]\n\n", output);
+ }
+ }
+}
+
+void
+interpolate(DBJob *job, const char *template)
+{
+ FILE *outputfile, *inputfile;
+ char inputpath[PATH_MAX];
+
+ if (getenv("GPX_INTERPOLATE_STDOUT") != NULL) {
+ outputfile = stdout;
+ } else {
+ outputfile = popen("/usr/lib/sendmail -t -r '<>'", "w");
+ if (outputfile == NULL) {
+ ERROR("Unable to open sendmail! (errno=%s)", strerror(errno));
+ return;
+ }
+ }
+
+ snprintf(inputpath, PATH_MAX, "%s/%s", getenv("GPX_PATH_TEMPLATES"), template);
+
+ inputfile = fopen(inputpath, "rb");
+
+ if (inputfile == NULL) {
+ ERROR("Unable to open input file %s (errno=%s)", inputpath, strerror(errno));
+ } else {
+ do_interpolate(job, inputfile, outputfile);
+ fclose(inputfile);
+ }
+
+ if (outputfile != stdout) {
+ if (pclose(outputfile) == -1) {
+ ERROR("Failure while closing sendmail! (errno=%s)", strerror(errno));
+ }
+ }
+}
--- /dev/null
+/* gpx-import/src/interpolate.h
+ *
+ * GPX file importer, email interpolation
+ *
+ * Copyright Daniel Silverstone <dsilvers@digital-scurf.org>
+ *
+ * Written for the OpenStreetMap project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the License
+ * only.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef GPX_INTERPOLATE_H
+#define GPX_INTERPOLATE_H
+
+#include "db.h"
+
+extern void interpolate(DBJob *job, const char *template);
+
+#endif /* GPX_INTERPOLATE_H */
--- /dev/null
+/* gpx-import/src/log.h
+ *
+ * GPX importer, logging primitives
+ *
+ * Copyright Daniel Silverstone <dsilvers@digital-scurf.org>
+ *
+ * Written for the OpenStreetMap project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the License
+ * only.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <time.h>
+#include <string.h>
+
+#include "log.h"
+
+#define LOGBUFLEN 1024
+
+void
+_gpxlog(const char *level, const char *fmt, ...)
+{
+ char buffer[LOGBUFLEN];
+ va_list ap;
+ time_t ttnow;
+ struct tm tmnow;
+
+ time(&ttnow);
+ gmtime_r(&ttnow, &tmnow);
+
+ strftime(buffer, LOGBUFLEN, "[%Y-%m-%dT%H:%M:%SZ] ", &tmnow);
+ strcat(buffer, level);
+ strcat(buffer, ": ");
+ va_start(ap, fmt);
+ vsnprintf(buffer + strlen(buffer), LOGBUFLEN - strlen(buffer) - 1, fmt, ap);
+ va_end(ap);
+ puts(buffer);
+}
--- /dev/null
+/* gpx-import/src/log.h
+ *
+ * GPX importer, logging primitives
+ *
+ * Copyright Daniel Silverstone <dsilvers@digital-scurf.org>
+ *
+ * Written for the OpenStreetMap project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the License
+ * only.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef GPX_IMPORT_LOG_H
+#define GPX_IMPORT_LOG_H
+
+extern void _gpxlog(const char *level, const char *fmt, ...)
+ __attribute__ ((format (printf, 2, 3)));
+
+
+
+#ifdef NDEBUG
+#define DEBUG(X...)
+#else
+#define DEBUG(X...) _gpxlog("DEBUG", X)
+#endif
+
+#define INFO(X...) _gpxlog("INFO", X)
+#define WARN(X...) _gpxlog("WARN", X)
+#define ERROR(X...) _gpxlog("ERROR", X)
+
+
+#endif /* GPX_IMPORT_LOG_H */
--- /dev/null
+/* gpx-import/src/main.c
+ *
+ * GPX file importer
+ *
+ * Copyright Daniel Silverstone <dsilvers@digital-scurf.org>
+ *
+ * Written for the OpenStreetMap project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the License
+ * only.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+
+#include "gpx.h"
+#include "db.h"
+#include "image.h"
+#include "filename.h"
+#include "interpolate.h"
+
+static bool needs_quit = false;
+
+static void
+do_quit(int ignored)
+{
+ (void)ignored;
+
+ if (needs_quit == true) {
+ ERROR("Hard quit!");
+ exit(1);
+ }
+ needs_quit = true;
+}
+
+int
+main(int argc, char **argv)
+{
+ DBJob *job;
+ GPX *g;
+ int64_t gpxnr;
+ int sleep_time = atoi(getenv("GPX_SLEEP_TIME") ? getenv("GPX_SLEEP_TIME") : "0");
+ bool did_work = true;
+ clock_t cstart, cend;
+
+ if (sleep_time == 0) {
+ sleep_time = 30;
+ WARN("Defaulted sleep time to %d seconds\n", sleep_time);
+ }
+
+ INFO("Connecting to DB");
+
+ if (db_connect() == false) {
+ ERROR("Unable to connect to DB");
+ return 1;
+ }
+
+ signal(SIGHUP, do_quit);
+ signal(SIGINT, do_quit);
+
+ do {
+ DEBUG("Looking for work");
+ cstart = clock();
+ job = db_find_work(sleep_time);
+
+ if (job == NULL) {
+ /* Found no work, can we find an invisible trace to delete? */
+ gpxnr = db_find_invisible();
+ if (gpxnr != -1) {
+ INFO("Found invisible trace");
+ did_work = true;
+ db_destroy_trace(gpxnr);
+ }
+ }
+
+ if (job == NULL) {
+ if (did_work == true)
+ INFO("No work to do, sleeping");
+ did_work = false;
+ sleep(sleep_time);
+ continue;
+ }
+ did_work = true;
+
+ if (job->error == NULL) {
+ gpxnr = job->gpx_id;
+ INFO("Found job %ld, reading in...", gpxnr);
+ g = job->gpx = gpx_parse_file(make_filename("GPX_PATH_TRACES", job->gpx_id, ".gpx"), &(job->error));
+
+ if (g != NULL && job->error == NULL) {
+ INFO("GPX contained %d good point(s) and %d bad point(s)", g->goodpoints, g->badpoints);
+ if (g->badpoints > 0) {
+ INFO("%d missed <time>, %d had bad latitude, %d had bad longitude",
+ g->missed_time, g->bad_lat, g->bad_long);
+ }
+ INFO("Creating icon and animation");
+ image_generate_icon(g, make_filename("GPX_PATH_IMAGES", gpxnr, "_icon.gif"), 50, 50);
+ image_generate_animation(g, make_filename("GPX_PATH_IMAGES", gpxnr, ".gif"), 250, 250, 10);
+
+ if (db_insert_gpx(job) == false) {
+ db_error(job, "Issue while inserting job into database");
+ ERROR("Failure inserting into DB");
+ }
+ } else {
+ if (job->error == NULL)
+ job->error = strdup("XML failure while parsing GPX data");
+ ERROR("Failure while parsing GPX");
+ }
+ }
+
+ if (job->error == NULL) {
+ /* Report success */
+ interpolate(job, "import-ok.eml");
+ } else {
+ /* Report failure */
+ interpolate(job, "import-bad.eml");
+
+ /* Destroy this item */
+ db_destroy_trace(job->gpx_id);
+ }
+
+ DEBUG("Cleaning up");
+ db_free_job(job);
+ cend = clock();
+ INFO("Import consumed %g CPU seconds", (double)(cend - cstart) / CLOCKS_PER_SEC);
+ } while (needs_quit == false);
+
+ INFO("Disconnecting from DB");
+
+ db_disconnect();
+
+ DEBUG("Bye");
+
+ return 0;
+}
--- /dev/null
+/* gpx-import/src/mercator.c
+ *
+ * GPX Importer, mercator projector
+ *
+ * Copyright Daniel Silverstone <dsilvers@digital-scurf.org>
+ *
+ * Written for the OpenStreetMap project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the License
+ * only.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <stdlib.h>
+#include <math.h>
+
+#include "mercator.h"
+
+#ifndef MAX
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+
+struct _MercatorProjection {
+ uint32_t height, width;
+ double tx, ty, byty, bxtx;
+};
+
+static inline double
+mercator_double_from_coord(GPXCoord coord)
+{
+ return ((double)coord) / 1000000000.0;
+}
+
+static inline double
+mercator_sheet_x(double longitude)
+{
+ return longitude;
+}
+
+static inline double
+mercator_sheet_y(double latitude)
+{
+ if (latitude < -85.0511)
+ latitude = -85.0511;
+ else if (latitude > 85.0511)
+ latitude = 85.0511;
+
+ return log(tan(M_PI / 4 + (latitude * M_PI / 180 / 2))) / (M_PI / 180);
+}
+
+void
+mercator_projection_free(MercatorProjection *projection)
+{
+ free(projection);
+}
+
+void
+mercator_projection_project(MercatorProjection *projection,
+ GPXCoord _latitude,
+ GPXCoord _longitude,
+ uint32_t *x,
+ uint32_t *y)
+{
+ double latitude = mercator_double_from_coord(_latitude);
+ double longitude = mercator_double_from_coord(_longitude);
+ *x = ((mercator_sheet_x(longitude) - projection->tx) / projection->bxtx) * (double)(projection->width);
+ *y = (double)(projection->height) - (((mercator_sheet_y(latitude) - projection->ty) / projection->byty) * (double)(projection->height));
+}
+
+MercatorProjection *
+mercator_projection_new(GPXCoord _min_latitude,
+ GPXCoord _min_longitude,
+ GPXCoord _max_latitude,
+ GPXCoord _max_longitude,
+ uint32_t width,
+ uint32_t height)
+{
+ MercatorProjection *ret = calloc(1, sizeof(MercatorProjection));
+ double xsize, ysize, xscale, yscale, scale, xpad, ypad, bx, by;
+ double min_latitude, min_longitude, max_latitude, max_longitude;
+
+ ret->height = height;
+ ret->width = width;
+
+ min_latitude = mercator_double_from_coord(_min_latitude);
+ min_longitude = mercator_double_from_coord(_min_longitude);
+ max_latitude = mercator_double_from_coord(_max_latitude);
+ max_longitude = mercator_double_from_coord(_max_longitude);
+
+ xsize = mercator_sheet_x(max_longitude) - mercator_sheet_x(min_longitude);
+ ysize = mercator_sheet_y(max_latitude) - mercator_sheet_y(min_latitude);
+
+ xscale = xsize / width;
+ yscale = ysize / height;
+
+ scale = MAX(xscale, yscale);
+
+ xpad = ((double)width * scale) - xsize;
+ ypad = ((double)height * scale) - ysize;
+
+ ret->tx = mercator_sheet_x(min_longitude) - (xpad / 2);
+ ret->ty = mercator_sheet_y(min_latitude) - (ypad / 2);
+
+ bx = mercator_sheet_x(max_longitude) + (xpad / 2);
+ by = mercator_sheet_y(max_latitude) + (ypad / 2);
+
+ ret->byty = by - ret->ty;
+ ret->bxtx = bx - ret->tx;
+
+ return ret;
+}
--- /dev/null
+/* gpx-import/src/mercator.h
+ *
+ * GPX Importer, mercator projector
+ *
+ * Copyright Daniel Silverstone <dsilvers@digital-scurf.org>
+ *
+ * Written for the OpenStreetMap project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the License
+ * only.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef GPX_IMPORT_MERCATOR_H
+#define GPX_IMPORT_MERCATOR_H
+
+#include "gpx.h"
+
+typedef struct _MercatorProjection MercatorProjection;
+
+extern MercatorProjection *mercator_projection_new(GPXCoord _min_latitude,
+ GPXCoord _min_longitude,
+ GPXCoord _max_latitude,
+ GPXCoord _max_longitude,
+ uint32_t width,
+ uint32_t height);
+
+extern void mercator_projection_project(MercatorProjection *projection,
+ GPXCoord _latitude,
+ GPXCoord _longitude,
+ uint32_t *x,
+ uint32_t *y);
+
+extern void mercator_projection_free(MercatorProjection *projection);
+
+#endif
--- /dev/null
+/* gpx-import/src/mysql.c
+ *
+ * GPS point insertion into MySQL database
+ *
+ * Copyright Daniel Silverstone <dsilvers@digital-scurf.org>
+ *
+ * Written for the OpenStreetMap project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the License
+ * only.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <stdlib.h>
+#include <mysql.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "filename.h"
+#include "db.h"
+#include "quadtile.h"
+
+#define STMT_BUFLEN (1024 * 256)
+
+static MYSQL *handle;
+static char statement_buffer[STMT_BUFLEN];
+static char escape_buffer[STMT_BUFLEN];
+
+#define STMT(V...) \
+ do { \
+ int stmt_len = snprintf(statement_buffer, STMT_BUFLEN, V); \
+ if (mysql_real_query(handle, statement_buffer, stmt_len) != 0) { \
+ return false; \
+ } \
+ } while (0)
+
+#define BLANKOR(S) strdup(((S) ? (S) : ("")))
+
+bool
+db_destroy_trace(int64_t jobnr)
+{
+ INFO("Destroying job %ld", jobnr);
+ STMT("DELETE FROM gpx_file_tags WHERE gpx_id=%ld", jobnr);
+ STMT("DELETE FROM gps_points WHERE gpx_id=%ld", jobnr);
+ STMT("DELETE FROM gpx_files WHERE id=%ld", jobnr);
+ unlink(make_filename("GPX_PATH_TRACES", jobnr, ".gpx"));
+ unlink(make_filename("GPX_PATH_IMAGES", jobnr, "_icon.gif"));
+ unlink(make_filename("GPX_PATH_IMAGES", jobnr, ".gif"));
+ return true;
+}
+
+
+bool
+db_insert_gpx(DBJob *job)
+{
+ MYSQL_RES *res;
+ MYSQL_ROW row;
+ bool do_delete = false;
+ GPXTrackPoint *pt;
+ int64_t gpxnr = job->gpx_id;
+ GPX *gpx = job->gpx;
+
+ STMT("SELECT COUNT(*) FROM gps_points WHERE gpx_id=%ld", gpxnr);
+ res = mysql_store_result(handle);
+ row = mysql_fetch_row(res);
+ if (atoi(row[0]) != 0) {
+ do_delete = true;
+ }
+ mysql_free_result(res);
+
+ if (do_delete == true) {
+ WARN("Old rows detected, deleting");
+ STMT("DELETE FROM gps_points WHERE gpx_id=%ld", gpxnr);
+ }
+
+ INFO("Inserting %d points", gpx->goodpoints);
+
+ /* Iterate the points, inserting them into the DB */
+ for (pt = gpx->points; pt != NULL; pt = pt->next) {
+ mysql_real_escape_string(handle, escape_buffer, pt->timestamp, strlen(pt->timestamp));
+ STMT("INSERT INTO gps_points (gpx_id, trackid, latitude, longitude, timestamp, altitude, tile) " \
+ "VALUES (%ld, %d, %ld, %ld, '%s', %f, %u)",
+ gpxnr, pt->segment, pt->latitude / 100, pt->longitude / 100, escape_buffer, pt->elevation,
+ quadtile_for_coords(pt->latitude, pt->longitude));
+ }
+
+ /* Last up, update the GPX with our lat/long/numpoints etc */
+ STMT("UPDATE gpx_files SET inserted=1, size=%d, latitude=%g, longitude=%g WHERE id=%ld\n",
+ gpx->goodpoints, (double)gpx->firstlatitude / 1000000000.0, (double)gpx->firstlongitude / 1000000000.0, gpxnr);
+
+ return true;
+}
+
+int64_t
+db_find_invisible(void)
+{
+ int64_t ret = -1;
+ MYSQL_RES *res;
+ MYSQL_ROW row;
+
+ STMT("SELECT id FROM gpx_files WHERE visible=0 LIMIT 1");
+
+ res = mysql_store_result(handle);
+ if (res != NULL) {
+ row = mysql_fetch_row(res);
+ if (row != NULL) {
+ ret = strtol(row[0], NULL, 0);
+ }
+ mysql_free_result(res);
+ }
+ return ret;
+}
+
+DBJob *
+db_find_work(int minage)
+{
+ DBJob *ret = NULL;
+ MYSQL_RES *res;
+ MYSQL_ROW row;
+ int64_t user;
+
+ STMT("SELECT id, name, description, user_id FROM gpx_files WHERE visible=1 AND inserted=0 AND timestamp <= now() - %d ORDER BY timestamp ASC LIMIT 1", minage);
+ res = mysql_store_result(handle);
+ if (res != NULL) {
+ row = mysql_fetch_row(res);
+ if (row != NULL) {
+ ret = calloc(1, sizeof(DBJob));
+ ret->gpx_id = strtol(row[0], NULL, 0);
+ ret->title = BLANKOR(row[1]);
+ ret->description = BLANKOR(row[2]);
+ user = strtol(row[3], NULL, 0);
+ }
+ mysql_free_result(res);
+ }
+
+ if (ret != NULL) {
+ /* Attempt to retrieve the email address */
+ STMT("SELECT display_name, email FROM users WHERE id=%ld", user);
+ res = mysql_store_result(handle);
+ if (res != NULL) {
+ row = mysql_fetch_row(res);
+ if (row != NULL) {
+ int tlen = strlen(row[0]) + strlen(row[1]) + 4; /* space '<' '>' NULL */
+ ret->email = malloc(tlen);
+ snprintf(ret->email, tlen, "%s <%s>", row[0], row[1]);
+ } else {
+ db_error(ret, "Unable to find user information for user %ld", user);
+ }
+ mysql_free_result(res);
+ } else {
+ db_error(ret, "Database error while retrieving user information for user %ld", user);
+ }
+ }
+
+ if (ret != NULL && ret->error == NULL) {
+ /* Attempt to retrieve the tags */
+ STMT("SELECT COALESCE(GROUP_CONCAT(tag), '') AS tags FROM gpx_file_tags WHERE gpx_id=%ld", ret->gpx_id);
+ res = mysql_store_result(handle);
+ if (res != NULL) {
+ row = mysql_fetch_row(res);
+ if (row != NULL) {
+ ret->tags = BLANKOR(row[0]);
+ } else {
+ db_error(ret, "Unable to retrieve GPX tags for file %ld\n", ret->gpx_id);
+ }
+ mysql_free_result(res);
+ } else {
+ db_error(ret, "Database error while retrieving GPX tags for file %ld\n", ret->gpx_id);
+ }
+ }
+
+ return ret;
+}
+
+bool
+db_connect(void)
+{
+ char *host, *user, *pass, *db;
+ int port;
+ /* Establish connection to MySQL using environment */
+ mysql_library_init(0, NULL, NULL);
+ handle = mysql_init(NULL);
+ if (handle == NULL)
+ return false;
+
+ host = getenv("GPX_MYSQL_HOST");
+ user = getenv("GPX_MYSQL_USER");
+ pass = getenv("GPX_MYSQL_PASS");
+ db = getenv("GPX_MYSQL_DB");
+ port = (getenv("GPX_MYSQL_PORT") ? atoi(getenv("GPX_MYSQL_PORT")) : 0);
+
+ if (mysql_real_connect(handle, host, user, pass, db, port, NULL, 0) == NULL) {
+ ERROR("Failure connecting to MySQL server: %s", mysql_error(handle));
+ mysql_close(handle);
+ mysql_library_end();
+ return false;
+ }
+
+ return true;
+}
+
+void
+db_disconnect(void)
+{
+ mysql_close(handle);
+ mysql_library_end();
+}
--- /dev/null
+/* gpx-import/src/quadtile.c
+ *
+ * GPX importer, quadtile calculation
+ *
+ * Copyright Daniel Silverstone <dsilvers@digital-scurf.org>
+ *
+ * Written for the OpenStreetMap project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the License
+ * only.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <math.h>
+
+#include "quadtile.h"
+
+/* This code is stolen from sites/rails_port/lib/quad_tile/quad_tile.h */
+
+static inline unsigned int
+xy2tile(unsigned int x, unsigned int y)
+{
+ unsigned int tile = 0;
+ int i;
+
+ for (i = 15; i >= 0; i--)
+ {
+ tile = (tile << 1) | ((x >> i) & 1);
+ tile = (tile << 1) | ((y >> i) & 1);
+ }
+
+ return tile;
+}
+
+static inline unsigned int
+lon2x(double lon)
+{
+ return round((lon + 180.0) * 65535.0 / 360.0);
+}
+
+static inline unsigned int
+lat2y(double lat)
+{
+ return round((lat + 90.0) * 65535.0 / 180.0);
+}
+
+uint32_t
+quadtile_for_coords(GPXCoord latitude, GPXCoord longitude)
+{
+ return xy2tile(lon2x((double)longitude / 1000000000.0),
+ lat2y((double)latitude / 1000000000.0));
+}
--- /dev/null
+/* gpx-import/src/quadtile.h
+ *
+ * GPX importer, quadtile calculation
+ *
+ * Copyright Daniel Silverstone <dsilvers@digital-scurf.org>
+ *
+ * Written for the OpenStreetMap project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the License
+ * only.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef GPX_QUADTILE_H
+#define GPX_QUADTILE_H
+
+#include "gpx.h"
+
+extern uint32_t quadtile_for_coords(GPXCoord latitude, GPXCoord longitude);
+
+#endif /* GPX_QUADTILE_H */
--- /dev/null
+This directory contains templates which are used by the importer to
+send emails etc.
+
+Interpolations are %X where X is:
+
+ e = email address of recipient, formatted for SMTP
+ so Real Name If Available <email@domain>
+
+ E = Any error which was encountered while processing the job
+
+ t = Title of GPX trace
+
+ T = Any tags, preceeded by "and the following tags\n"
+ or "and no tags." if there were no tags in the GPX import
+
+ d = The description of the GPX trace
+
+ g = Number of good points imported from the trace
+
+ p = Number of points in the trace in total.
+
+ m = Number of points which missed a <time> tag
+ l = Number of points with bad latitude
+ L = Number of points with bad longitude
--- /dev/null
+From: webmaster@openstreetmap.org
+To: %e
+Subject: [OpenStreetMap] GPX Import Failure
+Auto-Submitted: auto-generated
+
+Hi,
+
+It looks like your GPX file
+
+ %t
+
+with the description
+
+ %d
+
+%T
+
+failed to import. Here's the error:
+
+ %E
+
+More information about GPX import failures and how to avoid
+them can be found at:
+
+ http://wiki.openstreetmap.org/index.php/GPX_Import_Failures
+
--- /dev/null
+From: webmaster@openstreetmap.org
+To: %e
+Subject: [OpenStreetMap] GPX Import Success
+Auto-Submitted: auto-generated
+
+Hi,
+
+It looks like your GPX file
+
+ %t
+
+with the description
+
+ %d
+
+%T
+
+loaded successfully with %g out of a possible %p points.
+
+%m
+%l
+%L