ref: 9fa9d91d1abbe4180632b98db65b872c34765f78
dir: /geojson.c/
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <json.h>
#include "dat.h"
#include "fns.h"
static char *Tcoords = "coordinates";
static char *Ttype = "type";
static char *Tfeature = "Feature";
static char *Tfeatures = "features";
static char *Tgeometry = "geometry";
static char *Tgeometries = "geometries";
static char *Tfeaturecollection = "FeatureCollection";
static char *Tpoint = "Point";
static char *Tmultipoint = "MultiPoint";
static char *Tlinestring = "LineString";
static char *Tmultilinestring = "MultiLineString";
static char *Tpolygon = "Polygon";
static char *Tmultipolygon = "MultiPolygon";
static char *Tgeocollection = "GeometryCollection";
static char *Enumber = "expected number";
static char *Earray = "expected array";
static char *Ecoords = "expected coords";
static char *Eobject = "expected object";
static char *Estring = "expected string";
static int
jisarray(JSON *j)
{
if (!(j && j->t == JSONArray)) {
werrstr(Earray);
return 0;
}
return 1;
}
static int
jisnumber(JSON *j)
{
if (!(j && j->t == JSONNumber)) {
werrstr(Earray);
return 0;
}
return 1;
}
static int
jisobject(JSON *j)
{
if (!(j && j->t == JSONObject)) {
werrstr(Eobject);
return 0;
}
return 1;
}
static int
jisstring(JSON *j)
{
if (!(j && j->t == JSONString)) {
werrstr(Estring);
return 0;
}
return 1;
}
extern Image *mapimage;
extern int tilesize;
static JSON *geojson = nil;
static GBundle jpos;
static int renderjsontype(JSON*);
static int
jsonnum(JSON *j, double *n)
{
if (!jisnumber(j))
return 0;
*n = j->n;
return 1;
}
static int
jsoncoords(JSON *j, double *x, double *y)
{
JSONEl *el;
assert(x && y);
if (!jisarray(j))
return 0;
el = j->first;
if (el && el->val) {
if (!jsonnum(el->val, x))
return 0;
} else {
werrstr(Ecoords);
return 0;
}
el = el->next;
if (el && el->val) {
if (!jsonnum(el->val, y))
return 0;
} else {
werrstr(Ecoords);
return 0;
}
return 1;
}
static int
coordspoint(JSON *j, Point *point)
{
GPos p;
GBundle b;
Point o;
if (!jsoncoords(j, &p.lon, &p.lat))
return 0;
b = getbundle(p, jpos.z, &o);
b.x -= jpos.x;
b.y -= jpos.y;
o.x = b.x * tilesize + o.x;
o.y = b.y * tilesize + o.y;
*point = o;
return 1;
}
static int
topointarray(JSON *j, Point **arr, int *np)
{
JSONEl *el;
int n;
if (!jisarray(j))
return 0;
*np = 0;
for (el = j->first; el; el = el->next)
(*np)++;
if (!*np) {
*arr = nil;
return 1;
}
*arr = mallocz(*np * sizeof(Point), 1);
if (!*arr)
sysfatal("topointarray: %r");
n = 0;
for (el = j->first; el; el = el->next) {
if (!coordspoint(el->val, &(*arr)[n++]))
return 0;
}
return 1;
}
static void
drawline(GPos f, GPos t)
{
GBundle fb, tb;
Point fo, to;
fb = getbundle(f, jpos.z, &fo);
tb = getbundle(t, jpos.z, &to);
fb.x -= jpos.x;
fb.y -= jpos.y;
tb.x -= jpos.x;
tb.y -= jpos.y;
fo.x = fb.x * tilesize + fo.x;
fo.y = fb.y * tilesize + fo.y;
to.x = tb.x * tilesize + to.x;
to.y = tb.y * tilesize + to.y;
if (!ptinrect(fo, mapimage->r))
return;
if (!ptinrect(to, mapimage->r))
return;
debugprint("jsline: %P - %P\n", fo, to);
line(mapimage, fo, to,
Endsquare, Endsquare,
2, display->black, ZP);
}
static int
renderfeaturecollection(JSON *j)
{
JSONEl *el;
int ret;
if (!jisarray(j))
return 0;
ret = 1;
for (el = j->first; el; el = el->next)
ret &= renderjsontype(el->val);
return ret;
}
static int
renderpoint(JSON *j)
{
GPos p;
GBundle b;
Point off;
if (!jisarray(j))
return 0;
if (!jsoncoords(j, &p.lon, &p.lat))
return 0;
debugprint("POINT: %f, %f\n", p.lon, p.lat);
b = getbundle(p, jpos.z, &off);
b.x -= jpos.x;
b.y -= jpos.y;
if (b.x < 0 || b.y < 0)
return 1;
off.x = b.x * tilesize + off.x;
off.y = b.y * tilesize + off.y;
if (!ptinrect(off, mapimage->r))
return 1;
ellipse(mapimage, off, 5, 5, 1, display->black, ZP);
return 1;
}
static int
rendermpoint(JSON *j)
{
JSONEl *el;
if (!jisarray(j))
return 0;
for (el = j->first; el; el = el->next) {
if (!renderpoint(el->val))
return 0;
}
return 1;
}
static int
renderlinestring(JSON *j)
{
JSONEl *el;
JSON *last = nil;
double x1 = 0., y1 = 0., x2 = 0., y2 = 0.;
GPos f, t;
if (!jisarray(j))
return 0;
debugprint("LINESTRING\n");
for (el = j->first; el; el = el->next) {
if (last) {
x1 = x2;
y1 = y2;
}
if (!jsoncoords(el->val, &x2, &y2))
return 0;
if (!last) {
last = el->val;
continue;
}
last = el->val;
f.lon = x1;
f.lat = y1;
t.lon = x2;
t.lat = y2;
drawline(f, t);
}
return 1;
}
static int
rendermlinestring(JSON *j)
{
JSONEl *el;
if (!jisarray(j))
return 0;
for (el = j->first; el; el = el->next) {
if (!renderlinestring(el->val))
return 0;
}
return 1;
}
static int
renderpolygon(JSON *j)
{
JSONEl *el;
Image *img;
Point *pts;
int np;
if (!jisarray(j))
return 0;
img = allocimage(display, mapimage->r, GREY1, 0, DBlack);
if (!img)
sysfatal("%r");
for (el = j->first; el; el = el->next) {
if (!topointarray(el->val, &pts, &np))
return 0;
fillpoly(img, pts, np, 1, cwhite, ZP);
free(pts);
pts = nil;
}
draw(mapimage, img->r, cgreen, img, ZP);
freeimage(img);
return 1;
}
static int
rendermpolygon(JSON *j)
{
JSONEl *el;
if (!jisarray(j))
return 0;
for (el = j->first; el; el = el->next) {
if (!renderpolygon(el->val))
return 0;
}
return 1;
}
static int
rendergeocollection(JSON *j)
{
JSONEl *el;
if (!jisarray(j))
return 0;
for (el = j->first; el; el = el->next) {
if (!renderjsontype(el->val))
return 0;
}
return 1;
}
static int
renderjsontype(JSON *j)
{
JSON *type;
char *s;
type = jsonbyname(j, Ttype);
if (!jisstring(type))
return 0;
s = jsonstr(type);
if (!s) {
werrstr("empty string");
return 0;
}
if (strcmp(s, Tfeaturecollection) == 0)
return renderfeaturecollection(jsonbyname(j, Tfeatures));
if (strcmp(s, Tfeature) == 0)
return renderjsontype(jsonbyname(j, Tgeometry));
if (strcmp(s, Tpoint) == 0)
return renderpoint(jsonbyname(j, Tcoords));
if (strcmp(s, Tmultipoint) == 0)
return rendermpoint(jsonbyname(j, Tcoords));
if (strcmp(s, Tlinestring) == 0)
return renderlinestring(jsonbyname(j, Tcoords));
if (strcmp(s, Tmultilinestring) == 0)
return rendermlinestring(jsonbyname(j, Tcoords));
if (strcmp(s, Tpolygon) == 0)
return renderpolygon(jsonbyname(j, Tcoords));
if (strcmp(s, Tmultipolygon) == 0)
return rendermpolygon(jsonbyname(j, Tcoords));
if (strcmp(s, Tgeocollection) == 0)
return rendergeocollection(jsonbyname(j, Tgeometries));
werrstr("invalid type: %s", s);
return 0;
}
void
rendergeojson(GBundle pos)
{
if (!geojson)
return;
if (geojson->t != JSONObject) {
jsonfree(geojson);
geojson = nil;
return;
}
debugprint("rendering geojson\n");
jpos = pos;
if (!renderjsontype(geojson)) {
debugprint("invalid geojson: %r\n");
jsonfree(geojson);
geojson = nil;
}
}
int
handlegeojson(char *data, int ndata)
{
char *s;
if (geojson) {
jsonfree(geojson);
geojson = nil;
}
s = mallocz(ndata + 1, 1);
if (!s)
sysfatal("%r");
memcpy(s, data, ndata);
geojson = jsonparse(s);
free(s);
if (geojson)
debugprint("got geojson data\n");
return !!geojson;
}
void
cleargeojson()
{
jsonfree(geojson);
geojson = nil;
}