shithub: map

ref: 7b6082bf29c9239d93f33c55103668f7046189ca
dir: /geojson.c/

View raw version
#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;
}