Permalink
Browse files

Got things working against a couple of the Leaflet plugins. Using ver…

…sion 2 of the spec.
  • Loading branch information...
sdlime committed Jan 24, 2017
1 parent 032e5d3 commit 91e54c9a0293e4ab59e8e1916af17c4a921b4e5e
Showing with 77 additions and 113 deletions.
  1. +49 −77 mapmvt.c
  2. +3 −0 mapoutput.c
  3. +11 −0 mapservutil.c
  4. +14 −36 renderers/mvt/vector_tile.proto
View
126 mapmvt.c
@@ -35,6 +35,10 @@
#include "uthash.h"
#include <float.h>
#define MOVETO 1
#define LINETO 2
#define CLOSEPATH 7
typedef struct {
char *value;
unsigned int index;
@@ -45,7 +49,8 @@ typedef struct {
value_lookup *cache;
} value_lookup_table;
#define ZIGZAG(n) (((n) << 1) ^ ((n) >> 31))
#define COMMAND(id, count) (((id) & 0x7) | ((count) << 3))
#define PARAMETER(n) (((n) << 1) ^ ((n) >> 31))
static int mvtTransformShape(shapeObj *shape, rectObj *extent, int layer_type, int mvt_layer_extent) {
double scale_x,scale_y;
@@ -56,27 +61,26 @@ static int mvtTransformShape(shapeObj *shape, rectObj *extent, int layer_type, i
for(i=0,outi=0;i<shape->numlines;i++) {
for(j=0,outj=0;j<shape->line[i].numpoints;j++) {
shape->line[outi].point[outj].x = (int)((shape->line[i].point[j].x - extent->minx)*scale_x);
shape->line[outi].point[outj].y = mvt_layer_extent - (int)((shape->line[i].point[j].y - extent->miny)*scale_y);
if(!outj ||
shape->line[outi].point[outj].x != shape->line[outi].point[outj-1].x ||
shape->line[outi].point[outj].y != shape->line[outi].point[outj-1].y )
if(!outj || shape->line[outi].point[outj].x != shape->line[outi].point[outj-1].x || shape->line[outi].point[outj].y != shape->line[outi].point[outj-1].y)
/* add the point to the shape only if it's the first one or if it's dofferent than the previous one */
outj++;
}
if((layer_type == MS_LAYER_POLYGON && outj>=4) ||
(layer_type == MS_LAYER_LINE && outj>=2) ||
(layer_type == MS_LAYER_POINT && outj >= 1)) {
/* only add the shape if it's not degenerate */
shape->line[outi].numpoints = outj;
outi++;
if((layer_type == MS_LAYER_POLYGON && outj>=4) || (layer_type == MS_LAYER_LINE && outj>=2) || (layer_type == MS_LAYER_POINT && outj >= 1)) {
/* only add the shape if it's not degenerate */
shape->line[outi].numpoints = outj;
outi++;
}
}
/*free points of unused lines*/
/* free points of unused lines */
while(shape->numlines > outi)
free(shape->line[--shape->numlines].point);
msComputeBounds(shape);
return (shape->numlines == 0)?MS_FAILURE:MS_SUCCESS; /*sucess if at least one line*/
return (shape->numlines == 0)?MS_FAILURE:MS_SUCCESS; /* sucess if at least one line */
}
static int mvtClipShape(shapeObj *shape, int layer_type, int buffer, int mvt_layer_extent) {
@@ -95,7 +99,6 @@ static int mvtClipShape(shapeObj *shape, int layer_type, int buffer, int mvt_lay
return MS_FAILURE;
}
static void freeMvtFeature( VectorTile__Tile__Feature *mvt_feature ) {
if(mvt_feature->tags)
free(mvt_feature->tags);
@@ -140,12 +143,10 @@ int mvtWriteShape( layerObj *layer, shapeObj *shape, VectorTile__Tile__Layer *mv
value_lookup *value;
if(mvtTransformShape(shape, unbuffered_bbox, layer->type, mvt_layer->extent) != MS_SUCCESS) {
/* degenerate shape */
return MS_SUCCESS;
return MS_SUCCESS; /* degenerate shape */
}
if(mvtClipShape(shape, layer->type, buffer, mvt_layer->extent) != MS_SUCCESS) {
/* no features left after clipping */
return MS_SUCCESS;
return MS_SUCCESS; /* no features left after clipping */
}
mvt_layer->features[mvt_layer->n_features++] = msSmallMalloc(sizeof(VectorTile__Tile__Feature));
@@ -164,13 +165,14 @@ int mvtWriteShape( layerObj *layer, shapeObj *shape, VectorTile__Tile__Layer *mv
mvt_feature->type = VECTOR_TILE__TILE__GEOM_TYPE__POINT;
mvt_feature->has_type = 1;
/* output values */
for( i = 0, iout = 0; i < item_list->numitems; i++ ) {
gmlItemObj *item = item_list->items + i;
if( !item->visible )
continue;
UT_HASH_FIND_STR(value_lookup_cache->cache,shape->values[i],value);
UT_HASH_FIND_STR(value_lookup_cache->cache, shape->values[i], value);
if(!value) {
VectorTile__Tile__Value *mvt_value;
value = msSmallMalloc(sizeof(value_lookup));
@@ -203,6 +205,8 @@ int mvtWriteShape( layerObj *layer, shapeObj *shape, VectorTile__Tile__Layer *mv
iout++;
}
/* output geom */
mvt_feature->n_geometry = 0;
if(layer->type == MS_LAYER_POINT) {
for(i=0;i<shape->numlines;i++)
@@ -219,48 +223,37 @@ int mvtWriteShape( layerObj *layer, shapeObj *shape, VectorTile__Tile__Layer *mv
mvt_feature->geometry = msSmallMalloc(mvt_feature->n_geometry * sizeof(uint32_t));
if(layer->type == MS_LAYER_POINT) {
int idx = 1,lastx=0,lasty=0;
mvt_feature->geometry[0] = ((mvt_feature->n_geometry - 1) << 3)/*number of geoms*/|1/*moveto*/;
int idx=0, lastx=0, lasty=0;
mvt_feature->geometry[idx++] = COMMAND(MOVETO, mvt_feature->n_geometry-1);
for(i=0;i<shape->numlines;i++) {
for(j=0;j<shape->line[i].numpoints;j++) {
mvt_feature->geometry[idx++] = ZIGZAG(MS_NINT(shape->line[i].point[j].x)-lastx);
mvt_feature->geometry[idx++] = ZIGZAG(MS_NINT(shape->line[i].point[j].y)-lasty);
mvt_feature->geometry[idx++] = PARAMETER(MS_NINT(shape->line[i].point[j].x)-lastx);
mvt_feature->geometry[idx++] = PARAMETER(MS_NINT(shape->line[i].point[j].y)-lasty);
lastx = MS_NINT(shape->line[i].point[j].x);
lasty = MS_NINT(shape->line[i].point[j].y);
}
}
} else {
int idx = 0,lastx=0,lasty=0;
} else { /* MS_LAYER_LINE or MS_LAYER_POLYGON */
int idx=0, lastx=0, lasty=0;
for(i=0;i<shape->numlines;i++) {
for(j=0;j<shape->line[i].numpoints;j++) {
if(j==0) {
mvt_feature->geometry[idx++] = (1 << 3)/*number of movetos*/|1/*moveto*/;
mvt_feature->geometry[idx++] = ZIGZAG(MS_NINT(shape->line[i].point[0].x)-lastx);
mvt_feature->geometry[idx++] = ZIGZAG(MS_NINT(shape->line[i].point[0].y)-lasty);
lastx = MS_NINT(shape->line[i].point[0].x);
lasty = MS_NINT(shape->line[i].point[0].y);
} else {
if(j==1) {
mvt_feature->geometry[idx++] = ((shape->line[i].numpoints-1) << 3)/*number of linetos*/|2/*lineto*/;
mvt_feature->geometry[idx++] = ZIGZAG(MS_NINT(shape->line[i].point[1].x)-lastx);
mvt_feature->geometry[idx++] = ZIGZAG(MS_NINT(shape->line[i].point[1].y)-lasty);
lastx = MS_NINT(shape->line[i].point[1].x);
lasty = MS_NINT(shape->line[i].point[1].y);
} else {
mvt_feature->geometry[idx++] = ZIGZAG(MS_NINT(shape->line[i].point[j].x)-lastx);
mvt_feature->geometry[idx++] = ZIGZAG(MS_NINT(shape->line[i].point[j].y)-lasty);
lastx = MS_NINT(shape->line[i].point[j].x);
lasty = MS_NINT(shape->line[i].point[j].y);
}
mvt_feature->geometry[idx++] = COMMAND(MOVETO, 1);
} else if(j==1) {
mvt_feature->geometry[idx++] = COMMAND(LINETO, shape->line[i].numpoints-1);
}
mvt_feature->geometry[idx++] = PARAMETER(MS_NINT(shape->line[i].point[j].x)-lastx);
mvt_feature->geometry[idx++] = PARAMETER(MS_NINT(shape->line[i].point[j].y)-lasty);
lastx = MS_NINT(shape->line[i].point[j].x);
lasty = MS_NINT(shape->line[i].point[j].y);
}
if(layer->type == MS_LAYER_POLYGON) {
mvt_feature->geometry[idx++] = 7/*closepolygon*/;
mvt_feature->geometry[idx++] = COMMAND(CLOSEPATH, 1);
}
}
}
return MS_SUCCESS;
return MS_SUCCESS;
}
static void freeMvtTile( VectorTile__Tile *mvt_tile ) {
@@ -278,22 +271,9 @@ int msMVTWriteFromQuery( mapObj *map, outputFormatObj *format, int sendheaders )
void *buf;
const char *mvt_buffer = msGetOutputFormatOption(map->outputformat, "EDGE_BUFFER", "10");
int buffer = MS_ABS(atoi(mvt_buffer));
double res;
rectObj bbox;
VectorTile__Tile mvt_tile = VECTOR_TILE__TILE__INIT;
mvt_tile.layers = msSmallCalloc(map->numlayers,sizeof(VectorTile__Tile__Layer*));
bbox = map->query.rect;
res = (bbox.maxx - bbox.minx)/(double)map->width;
bbox.minx += buffer * res;
bbox.maxx -= buffer * res;
res = (bbox.maxy - bbox.miny)/(double)map->height;
bbox.miny += buffer * res;
bbox.maxy -= buffer * res;
map->width -= buffer;
map->height -= buffer;
for( iLayer = 0; iLayer < map->numlayers; iLayer++ ) {
int status=MS_SUCCESS;
int i;
@@ -314,16 +294,13 @@ int msMVTWriteFromQuery( mapObj *map, outputFormatObj *format, int sendheaders )
/* -------------------------------------------------------------------- */
/* Will we need to reproject? */
/* -------------------------------------------------------------------- */
if(layer->transform == MS_TRUE
&& layer->project
&& msProjectionsDiffer(&(layer->projection),
&(layer->map->projection)) )
if(layer->transform == MS_TRUE && layer->project && msProjectionsDiffer(&(layer->projection), &(layer->map->projection)))
reproject = MS_TRUE;
mvt_tile.layers[mvt_tile.n_layers++] = msSmallMalloc(sizeof(VectorTile__Tile__Layer));
mvt_layer = mvt_tile.layers[mvt_tile.n_layers-1];
vector_tile__tile__layer__init(mvt_layer);
mvt_layer->version = 1;
mvt_layer->version = 2;
mvt_layer->name = layer->name;
mvt_buffer = msGetOutputFormatOption(map->outputformat, "EXTENT", "4096");
mvt_layer->extent = MS_ABS(atoi(mvt_buffer));
@@ -340,7 +317,6 @@ int msMVTWriteFromQuery( mapObj *map, outputFormatObj *format, int sendheaders )
for( i = 0; i < layer->numitems; i++ ) {
gmlItemObj *item = item_list->items + i;
if( !item->visible )
continue;
@@ -387,16 +363,14 @@ int msMVTWriteFromQuery( mapObj *map, outputFormatObj *format, int sendheaders )
if(layer->numclasses > 0) {
/*
** Perform classification, and some annotation related magic.
*/
resultshape.classindex =
msShapeGetClass(layer, map, &resultshape, NULL, -1);
** Perform classification, and some annotation related magic.
*/
resultshape.classindex = msShapeGetClass(layer, map, &resultshape, NULL, -1);
if(resultshape.classindex < 0)
goto feature_cleanup; /* no matching CLASS found, skip this feature */
}
/*
** prepare any necessary JOINs here (one-to-one only)
*/
@@ -412,13 +386,11 @@ int msMVTWriteFromQuery( mapObj *map, outputFormatObj *format, int sendheaders )
}
if( reproject ) {
status =
msProjectShape(&layer->projection, &layer->map->projection,
&resultshape);
status = msProjectShape(&layer->projection, &layer->map->projection, &resultshape);
}
if( status == MS_SUCCESS )
status = mvtWriteShape( layer, &resultshape, mvt_layer, item_list, &value_lookup_cache, &bbox, buffer );
status = mvtWriteShape( layer, &resultshape, mvt_layer, item_list, &value_lookup_cache, &map->query.rect, buffer );
if(status != MS_SUCCESS) {
retcode = status;
@@ -441,13 +413,13 @@ int msMVTWriteFromQuery( mapObj *map, outputFormatObj *format, int sendheaders )
goto cleanup;
}
len = vector_tile__tile__get_packed_size( &mvt_tile); // This is the calculated packing length
buf = msSmallMalloc (len); // Allocate memory
vector_tile__tile__pack( &mvt_tile, buf);
len = vector_tile__tile__get_packed_size(&mvt_tile); // This is the calculated packing length
buf = msSmallMalloc(len); // Allocate memory
vector_tile__tile__pack(&mvt_tile, buf);
if( sendheaders ) {
msIO_fprintf( stdout,
"Content-Length: %d\r\n"
"Content-Type: application/x-protobuf\r\n\r\n",
"Content-Length: %d\r\n"
"Content-Type: application/x-protobuf\r\n\r\n",
len);
}
msIO_fwrite(buf,len,1,stdout);
View
@@ -113,6 +113,9 @@ struct defaultOutputFormatEntry defaultoutputformats[] = {
#ifdef USE_KML
{"kml","KML","application/vnd.google-earth.kml+xml"},
{"kmz","KMZ","application/vnd.google-earth.kmz"},
#endif
#ifdef USE_PBF
{"mvt","MVT","application/x-protobuf"},
#endif
{"json","UTFGrid","application/json"},
{NULL,NULL,NULL}
View
@@ -1515,6 +1515,17 @@ int msCGIDispatchImageRequest(mapservObj *mapserv)
break;
case TILE:
msTileSetExtent(mapserv);
if(!strcmp(MS_IMAGE_MIME_TYPE(mapserv->map->outputformat), "application/x-protobuf")) {
mapserv->map->query.type = MS_QUERY_BY_RECT;
mapserv->map->query.mode = MS_QUERY_MULTIPLE;
mapserv->map->query.rect = mapserv->map->extent;
// if((status = msExecuteQuery(mapserv->map)) != MS_SUCCESS) return MS_FAILURE;
msExecuteQuery(mapserv->map); // ignore error?
if((status = msMVTWriteFromQuery(mapserv->map, mapserv->map->outputformat, mapserv->sendheaders)) != MS_SUCCESS) return MS_FAILURE;
return MS_SUCCESS;
}
img = msTileDraw(mapserv);
break;
case LEGEND:
@@ -1,10 +1,10 @@
// Protocol Version 1
package vector_tile;
option optimize_for = LITE_RUNTIME;
message Tile {
// GeomType is described in section 4.3.4 of the specification
enum GeomType {
UNKNOWN = 0;
POINT = 1;
@@ -13,8 +13,9 @@ message Tile {
}
// Variant type encoding
// The use of values is described in section 4.1 of the specification
message Value {
// Exactly one of these values may be present in a valid message
// Exactly one of these values must be present in a valid message
optional string string_value = 1;
optional float float_value = 2;
optional double double_value = 3;
@@ -26,50 +27,26 @@ message Tile {
extensions 8 to max;
}
// Features are described in section 4.2 of the specification
message Feature {
optional uint64 id = 1 [ default = 0 ];
// Tags of this feature are encoded as repeated pairs of
// integers. Even indexed values (n, beginning with 0) are
// themselves indexes into the layer's keys list. Odd indexed
// values (n+1) are indexes into the layer's values list.
// The first (n=0) tag of a feature, therefore, has a key of
// layer.keys[feature.tags[0]] and a value of
// layer.values[feature.tags[1]].
// integers.
// A detailed description of tags is located in sections
// 4.2 and 4.4 of the specification
repeated uint32 tags = 2 [ packed = true ];
// The type of geometry stored in this feature.
optional GeomType type = 3 [ default = UNKNOWN ];
// Contains a stream of commands and parameters (vertices). The
// repeat count is shifted to the left by 3 bits. This means
// that the command has 3 bits (0-7). The repeat count
// indicates how often this command is to be repeated. Defined
// commands are:
// - MoveTo: 1 (2 parameters follow)
// - LineTo: 2 (2 parameters follow)
// - ClosePath: 7 (no parameters follow)
//
// Commands are encoded as uint32 varints. Vertex parameters
// are encoded as deltas to the previous position and, as they
// may be negative, are further "zigzag" encoded as unsigned
// 32-bit ints:
//
// n = (n << 1) ^ (n >> 31)
//
// Ex.: MoveTo(3, 6), LineTo(8, 12), LineTo(20, 34), ClosePath
// Encoded as: [ 9 6 12 18 10 12 24 44 15 ]
// | | `> [00001 111] command type 7 (ClosePath), length 1
// | | ===== relative LineTo(+12, +22) == LineTo(20, 34)
// | | ===== relative LineTo(+5, +6) == LineTo(8, 12)
// | `> [00010 010] = command type 2 (LineTo), length 2
// | ==== relative MoveTo(+3, +6)
// `> [00001 001] = command type 1 (MoveTo), length 1
//
// The original position is (0,0).
// Contains a stream of commands and parameters (vertices).
// A detailed description on geometry encoding is located in
// section 4.3 of the specification.
repeated uint32 geometry = 4 [ packed = true ];
}
// Layers are described in section 4.1 of the specification
message Layer {
// Any compliant implementation must first read the version
// number encoded in this message and choose the correct
@@ -88,7 +65,8 @@ message Tile {
// Dictionary encoding for values
repeated Value values = 4;
// The bounding box in this tile spans from 0..4095 units
// Although this is an "optional" field it is required by the specification.
// See https://github.com/mapbox/vector-tile-spec/issues/47
optional uint32 extent = 5 [ default = 4096 ];
extensions 16 to max;

0 comments on commit 91e54c9

Please sign in to comment.