Browse Source

Add preliminary write support for GeoJSON.

tags/gpsbabel_1_5_4
Robert Lipe 3 years ago
parent
commit
ecbf04080c

+ 1
- 1
Makefile.in View File

@@ -81,7 +81,7 @@ ALL_FMTS=$(MINIMAL_FMTS) gtm.o gpsutil.o \
vpl.o teletype.o jogmap.o bushnell.o bushnell_trl.o wintec_tes.o \
subrip.o garmin_xt.o garmin_fit.o lowranceusr4.o \
mtk_locus.o googledir.o mapbar_track.o f90g_track.o mapfactor.o energympro.o \
mynav.o ggv_bin.o globalsat_sport.o
mynav.o ggv_bin.o globalsat_sport.o geojson.o

FMTS=@FMTS@


+ 169
- 0
geojson.cc View File

@@ -0,0 +1,169 @@
/*
Copyright (C) 2016 Robert Lipe, robertlipe+source@gpsbabel.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; 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 USA

*/
#include "defs.h"
#include <QtCore/QJsonDocument>
#include <QtCore/QJsonObject>
#include <QtCore/QJsonArray>
#include "src/core/file.h"

static gbfile* ofd;
static const char MYNAME[] = "geojson";
static char* compact_opt = NULL;
static QJsonObject* track_object = NULL;
static QJsonArray* track_coords = NULL;

static arglist_t geojson_args[] = {
{"compact", &compact_opt, "Compact Output. Default is off.",
NULL, ARGTYPE_BOOL, ARG_NOMINMAX } ,
ARG_TERMINATOR
};

static void
geojson_rd_init(const QString& fname) {
}

QJsonArray* feature_collection = nullptr;

static void
geojson_wr_init(const QString& fname) {
feature_collection = new QJsonArray;
ofd = gbfopen(fname, "w", MYNAME);
}

static void
geojson_waypt_pr(const Waypoint* waypoint) {
QJsonObject object;
object["type"] = "Feature";
QJsonObject geometry;
geometry["type"] = "Point";

QJsonArray coords;
coords.append(waypoint->longitude);
coords.append(waypoint->latitude);
if (waypoint->altitude != unknown_alt && waypoint->altitude != 0) {
coords.append(waypoint->altitude);
}

geometry["type"] = "Point";
geometry["coordinates"] = coords;
object["geometry"] = geometry;

// Build up the properties.
QJsonObject properties;
if (!waypoint->shortname.isEmpty()) {
properties["name"] = waypoint->shortname;
}
if (!waypoint->description.isEmpty()) {
properties["description"] = waypoint->description;
}
UrlLink link = waypoint->GetUrlLink();
if (!link.url_.isEmpty()) {
properties["url"] = link.url_;
}
if (!link.url_link_text_.isEmpty()) {
properties["urlname"] = link.url_link_text_;
}
if (!properties.empty()) {
object["properties"] = properties;
}
feature_collection->append(object);
}

static void
geojson_rd_deinit() {
}

static void
geojson_wr_deinit(void) {
QJsonObject object;
object["type"] = "FeatureCollection";
object["features"] = *feature_collection;

QJsonDocument save(object);
QJsonDocument::JsonFormat style;
style = compact_opt ? QJsonDocument::Compact : QJsonDocument::Indented;
gbfputs(save.toJson(style),ofd);

gbfclose(ofd);
ofd = NULL;
delete feature_collection;
feature_collection = nullptr;
}

static void
geojson_read(void) {
}

static void geojson_track_hdr(const route_head* track) {
track_object = new QJsonObject();

(*track_object)["type"] = "Feature";
track_coords = new QJsonArray();

QJsonObject properties;
if (!track->rte_name.isEmpty()) {
properties["name"] = track->rte_name;
}
(*track_object)["properties"] = properties;
}

static void geojson_track_disp(const Waypoint* trackpoint) {

QJsonArray coords;
coords.append(trackpoint->longitude);
coords.append(trackpoint->latitude);
if (trackpoint->altitude != unknown_alt && trackpoint->altitude != 0) {
coords.append(trackpoint->altitude);
}
(*track_coords).append(coords);
}

static void geojson_track_tlr(const route_head* track) {
QJsonObject geometry;
geometry["type"] = "LineString";
geometry["coordinates"] = *track_coords;
(*track_object)["geometry"] = geometry;
feature_collection->append(*track_object);
delete track_object;
track_object = NULL;
delete track_coords;
track_coords = NULL;
}

static void
geojson_write(void) {
waypt_disp_all(geojson_waypt_pr);
track_disp_all(geojson_track_hdr, geojson_track_tlr, geojson_track_disp);
}

ff_vecs_t geojson_vecs = {
ff_type_file,
{ (ff_cap)(/*ff_cap_read | */ff_cap_write), ff_cap_write, ff_cap_none },
geojson_rd_init,
geojson_wr_init,
geojson_rd_deinit,
geojson_wr_deinit,
geojson_read,
geojson_write,
NULL,
geojson_args,
CET_CHARSET_UTF8, 0 /* CET-REVIEW */
};

+ 149
- 0
reference/geocaching~json.json View File

@@ -0,0 +1,149 @@
{
"features": [
{
"geometry": {
"coordinates": [
-87.134699999999995,
35.972033332999999
],
"type": "Point"
},
"properties": {
"description": "Mountain Bike Heaven by susy1313",
"name": "GCEBB",
"url": "http://www.geocaching.com/seek/cache_details.asp?ID=3771",
"urlname": "Cache Details"
},
"type": "Feature"
},
{
"geometry": {
"coordinates": [
-86.679550000000006,
36.090683333000001
],
"type": "Point"
},
"properties": {
"description": "The Troll by a182pilot & Family",
"name": "GC1A37",
"url": "http://www.geocaching.com/seek/cache_details.asp?ID=6711",
"urlname": "Cache Details"
},
"type": "Feature"
},
{
"geometry": {
"coordinates": [
-86.620116667000005,
35.996266667
],
"type": "Point"
},
"properties": {
"description": "Dive Bomber by JoGPS & family",
"name": "GC1C2B",
"url": "http://www.geocaching.com/seek/cache_details.asp?ID=7211",
"urlname": "Cache Details"
},
"type": "Feature"
},
{
"geometry": {
"coordinates": [
-86.648616666999999,
36.038483333000002
],
"type": "Point"
},
"properties": {
"description": "FOSTER by JoGPS & Family",
"name": "GC25A9",
"url": "http://www.geocaching.com/seek/cache_details.asp?ID=9641",
"urlname": "Cache Details"
},
"type": "Feature"
},
{
"geometry": {
"coordinates": [
-86.741766666999993,
36.112183332999997
],
"type": "Point"
},
"properties": {
"description": "Logan Lighthouse by JoGps & Family",
"name": "GC2723",
"url": "http://www.geocaching.com/seek/cache_details.asp?ID=10019",
"urlname": "Cache Details"
},
"type": "Feature"
},
{
"geometry": {
"coordinates": [
-86.790516667000006,
36.064083332999999
],
"type": "Point"
},
"properties": {
"description": "Ganier Cache by Susy1313",
"name": "GC2B71",
"url": "http://www.geocaching.com/seek/cache_details.asp?ID=11121",
"urlname": "Cache Details"
},
"type": "Feature"
},
{
"geometry": {
"coordinates": [
-86.809733332999997,
36.087766666999997
],
"type": "Point"
},
"properties": {
"description": "Shy's Hill by FireFighterEng33",
"name": "GC309F",
"url": "http://www.geocaching.com/seek/cache_details.asp?ID=12447",
"urlname": "Cache Details"
},
"type": "Feature"
},
{
"geometry": {
"coordinates": [
-86.891999999999996,
36.057499999999997
],
"type": "Point"
},
"properties": {
"description": "GittyUp by JoGPS / Warner Parks",
"name": "GC317A",
"url": "http://www.geocaching.com/seek/cache_details.asp?ID=12666",
"urlname": "Cache Details"
},
"type": "Feature"
},
{
"geometry": {
"coordinates": [
-86.867283333000003,
36.082799999999999
],
"type": "Point"
},
"properties": {
"description": "Inlighting by JoGPS / Warner Parks",
"name": "GC317D",
"url": "http://www.geocaching.com/seek/cache_details.asp?ID=12669",
"urlname": "Cache Details"
},
"type": "Feature"
}
],
"type": "FeatureCollection"
}

+ 211
- 0
reference/track/segmented_tracks~geojson.json View File

@@ -0,0 +1,211 @@
{
"features": [
{
"geometry": {
"coordinates": [
[
-86.844139609999999,
35.826145640999997
],
[
-86.843587434,
35.824858661
],
[
-86.843868790000002,
35.825508290000002
],
[
-86.843399026,
35.825659280000004
],
[
-86.843647121000004,
35.826200632000003
],
[
-86.843137330000005,
35.825023495000003
],
[
-86.842846089999995,
35.825764722999999
],
[
-86.842592054999997,
35.825212972999999
],
[
-86.842083658999996,
35.825409925000002
],
[
-86.842527868000005,
35.826530337999998
],
[
-86.841916886999996,
35.826227992
],
[
-86.841882385999995,
35.826752995
],
[
-86.841275397999993,
35.825879004999997
],
[
-86.840954045999993,
35.826506488
],
[
-86.840764704999998,
35.826053416000001
],
[
-86.840254282000004,
35.826187447999999
],
[
-86.840499359999995,
35.826664997000002
],
[
-86.840954045999993,
35.826506488
],
[
-86.839682814,
35.826441473000003
],
[
-86.840327092999999,
35.827349034999997
],
[
-86.839510243000007,
35.827133899000003
],
[
-86.839451225000005,
35.827576071000003
],
[
-86.838781982,
35.826743954000001
]
],
"type": "LineString"
},
"properties": {
"name": "No Times"
},
"type": "Feature"
},
{
"geometry": {
"coordinates": [
[
-86.844139609999999,
35.836145641000002
],
[
-86.843587434,
35.834858660999998
],
[
-86.843868790000002,
35.83550829
],
[
-86.843399026,
35.835659280000002
],
[
-86.843647121000004,
35.836200632000001
],
[
-86.843137330000005,
35.835023495000002
],
[
-86.842846089999995,
35.835764722999997
],
[
-86.842592054999997,
35.835212972999997
],
[
-86.842083658999996,
35.835409925
],
[
-86.842527868000005,
35.836530338000003
],
[
-86.841916886999996,
35.836227991999998
],
[
-86.841882385999995,
35.836752994999998
],
[
-86.841275397999993,
35.835879005000002
],
[
-86.840954045999993,
35.836506487999998
],
[
-86.840764704999998,
35.836053415999999
],
[
-86.840254282000004,
35.836187447999997
],
[
-86.840499359999995,
35.836664997
],
[
-86.840954045999993,
35.836506487999998
],
[
-86.839682814,
35.836441473000001
],
[
-86.840327092999999,
35.837349035000003
],
[
-86.839510243000007,
35.837133899000001
],
[
-86.839451225000005,
35.837576071000001
],
[
-86.838781982,
35.836743953999999
]
],
"type": "LineString"
},
"properties": {
"name": "With Times"
},
"type": "Feature"
}
],
"type": "FeatureCollection"
}

+ 10
- 0
testo.d/geojson.test View File

@@ -0,0 +1,10 @@
#
# As this format is initially write-only, we're comparing our own "golden"
# output to what the newly built writer creates.
#

gpsbabel -i gpx -f ${REFERENCE}/geocaching.gpx -o geojson -F ${TMPDIR}/geo.json
compare ${REFERENCE}/geocaching~json.json ${TMPDIR}/geo.json

gpsbabel -i gpx -f ${REFERENCE}/track/segmented_tracks.gpx -o geojson -F ${TMPDIR}/track.json
compare ${REFERENCE}/track/segmented_tracks~geojson.json ${TMPDIR}/track.json

+ 8
- 0
vecs.cc View File

@@ -54,6 +54,7 @@ extern ff_vecs_t gcdb_vecs;
extern ff_vecs_t gdb_vecs;
extern ff_vecs_t geoniche_vecs;
extern ff_vecs_t geo_vecs;
extern ff_vecs_t geojson_vecs;
extern ff_vecs_t globalsat_sport_vecs;
extern ff_vecs_t glogbook_vecs;
extern ff_vecs_t google_dir_vecs;
@@ -1077,6 +1078,13 @@ vecs_t vec_list[] = {
"trc",
NULL,
},
{
&geojson_vecs,
"geojson",
"GeoJson",
"json",
NULL,
},
{
&ggv_bin_vecs,
"ggv_bin",

+ 31
- 0
xmldoc/formats/geojson.xml View File

@@ -0,0 +1,31 @@
<para>
This module supports a subset of the <ulink url="http://geojson.org/">GeoJSON</ulink> format.
</para>
<para>
GeoJSON is a poor fit for GPSBabel's internal data structures as GPSBabel
was designed more around common GPS features (waypoints, tracks, routes)
than about GIS-style concepts like MultiPolygons or Geometry Collections.
In reality, for all but the most simple uses (such as converting a format
that GPSBabel supports well to something like Leaflet, you should not expect
high fidelity transfers through this format.
</para>
<para>
Initially, only write support for waypoints and tracks is available.
Waypoints are converted to a FeatureCollection of Points.
The properties for name and description are written, where available.
Tracks are converted to a LineString.
</para>
<para>
Potential future work includes the ability to read Point Features and/or
LineStrings as these would map into our concept of waypoints and
routes/tracks.
The potentially nested/recursive nature of GeoJSON in general would be
an awkward implementation.
</para>
<para>
Initial development was free-handed by looking at the <ulink url="https://tools.ietf.org/html/rfc7946 ">GeoJSON RFC</ulink>. Corner cases were handled by
using <ulink url="http://www.gdal.org/ogr2ogr.html">GDAL's ogr2ogr</ulink>
to convert GPX to JSON and compare the output. The results were then
<ulink url="http://geojsonlint.com/">JSON validated</ulink> and viewed on
<ulink url="http://geojson.io/">JSON web viewer</ulink>.
</para>

+ 1
- 0
xmldoc/formats/options/geojson-compact.xml View File

@@ -0,0 +1 @@


Loading…
Cancel
Save