add constants and move style parser to its own class

This commit is contained in:
Konstantin Käfer
2014-03-12 13:26:24 +01:00
parent 207358b598
commit 277bc1586d
5 changed files with 551 additions and 488 deletions

View File

@@ -89,6 +89,9 @@ module.exports = {
},
},
"sprite": "img/maki-sprite",
"constants": {
"park": "#c8df9f",
},
"structure": [
{ "name": "park", "bucket": "park" },
{ "name": "wood", "bucket": "wood" },
@@ -113,7 +116,7 @@ module.exports = {
},
"park": {
"type": "fill",
"color": "#c8df9f",
"color": "park",
"antialias": true,
"image": "park"
},

View File

@@ -0,0 +1,42 @@
#ifndef LLMR_STYLE_STYLE_PARSER
#define LLMR_STYLE_STYLE_PARSER
#include <rapidjson/document.h>
#include <llmr/style/style.hpp>
namespace llmr {
class StyleParser {
public:
using JSVal = const rapidjson::Value&;
void parseBuckets(JSVal value, std::map<std::string, BucketDescription>& buckets);
void parseLayers(JSVal value, std::vector<LayerDescription>& layers);
void parseConstants(JSVal value);
void parseClasses(JSVal value, std::map<std::string, ClassDescription>& classes);
private:
LayerDescription parseLayer(JSVal value);
BucketDescription parseBucket(JSVal value);
std::pair<std::string, ClassDescription> parseClassDescription(JSVal value);
void parseClass(const std::string& name, JSVal value, ClassDescription& class_desc);
FillClass parseFillClass(JSVal value);
LineClass parseLineClass(JSVal value);
PointClass parsePointClass(JSVal value);
BackgroundClass parseBackgroundClass(JSVal value);
bool parseBoolean(JSVal value);
std::string parseString(JSVal value);
Color parseColor(JSVal value);
Value parseValue(JSVal value);
template <typename T> typename FunctionProperty<T>::fn parseFunctionType(JSVal type);
FunctionProperty<float> parseFloatFunction(JSVal value);
FunctionProperty<bool> parseBoolFunction(JSVal value);
private:
std::map<std::string, const rapidjson::Value *> constants;
};
}
#endif

View File

@@ -3,5 +3,5 @@
using namespace llmr;
const unsigned char resources::style[] = R"JSON({"buckets":{"water":{"source":"streets","layer":"water","type":"fill"},"road_large":{"source":"streets","layer":"road","field":"class","value":["motorway","main"],"type":"line","cap":"round","join":"round"},"road_regular":{"source":"streets","layer":"road","field":"class","value":"street","type":"line","cap":"round","join":"round"},"road_limited":{"source":"streets","layer":"road","field":"class","value":"street_limited","type":"line","cap":"round","join":"round"},"park":{"source":"streets","layer":"landuse","field":"class","value":"park","type":"fill"},"wood":{"source":"streets","layer":"landuse","field":"class","value":"wood","type":"fill"},"school":{"source":"streets","layer":"landuse","field":"class","value":"school","type":"fill"},"cemetery":{"source":"streets","layer":"landuse","field":"class","value":"cemetery","type":"fill"},"industrial":{"source":"streets","layer":"landuse","field":"class","value":"industrial","type":"fill"},"building":{"source":"streets","layer":"building","type":"fill"},"alcohol_poi":{"source":"streets","layer":"poi_label","field":"type","value":["Alcohol"],"type":"point"},"cafe_poi":{"source":"streets","layer":"poi_label","field":"type","value":["Cafe"],"type":"point"},"embassy_poi":{"source":"streets","layer":"poi_label","field":"type","value":["Embassy"],"type":"point"},"park_poi":{"source":"streets","layer":"poi_label","field":"type","value":["Park"],"type":"point"},"restaurant_poi":{"source":"streets","layer":"poi_label","field":"type","value":["Restaurant"],"type":"point"}},"sprite":"img/maki-sprite","structure":[{"name":"park","bucket":"park"},{"name":"wood","bucket":"wood"},{"name":"water","bucket":"water"},{"name":"building","bucket":"building"},{"name":"road_limited","bucket":"road_limited"},{"name":"road_regular","bucket":"road_regular"},{"name":"road_large","bucket":"road_large"},{"name":"alcohol_poi","bucket":"alcohol_poi"},{"name":"cafe_poi","bucket":"cafe_poi"},{"name":"embassy_poi","bucket":"embassy_poi"},{"name":"park_poi","bucket":"park_poi"},{"name":"restaurant_poi","bucket":"restaurant_poi"}],"classes":[{"name":"default","layers":{"background":{"type":"background","color":"#FFFFFF"},"park":{"type":"fill","color":"#c8df9f","antialias":true,"image":"park"},"wood":{"type":"fill","color":"#33AA66","antialias":true,"opacity":0.1},"water":{"type":"fill","color":"#bae0e9","stroke":"#73b6e6","antialias":true,"image":"water_etsy"},"building":{"type":"fill","color":"#000000","antialias":true,"opacity":["linear",13,0,0.1,0,0.1]},"road_limited":{"type":"line","color":"#BBBBBB","width":["stops",{"z":0,"val":1},{"z":30,"val":1}]},"road_regular":{"type":"line","color":"#999999","width":["stops",{"z":0,"val":0.5},{"z":13,"val":0.5},{"z":16,"val":2},{"z":20,"val":32},{"z":30,"val":32}]},"road_large":{"type":"line","color":"#666666","width":["stops",{"z":0,"val":0.5},{"z":11,"val":0.5},{"z":13,"val":1},{"z":16,"val":4},{"z":20,"val":64},{"z":30,"val":64}]},"alcohol_poi":{"type":"point","color":"#cccccc","size":18,"image":"alcohol-shop","opacity":["linear",15,0,1,0,0.75]},"cafe_poi":{"type":"point","color":"#cccccc","size":18,"image":"cafe","opacity":["linear",15,0,1,0,0.75]},"embassy_poi":{"type":"point","color":"#cccccc","size":18,"image":"embassy","opacity":["linear",15,0,1,0,0.75]},"park_poi":{"type":"point","color":"#cccccc","size":18,"image":"park","opacity":["linear",15,0,1,0,0.75]},"restaurant_poi":{"type":"point","color":"#cccccc","size":18,"image":"restaurant","opacity":["linear",15,0,1,0,0.75]}}}]})JSON";
const unsigned char resources::style[] = R"JSON({"buckets":{"water":{"source":"streets","layer":"water","type":"fill"},"road_large":{"source":"streets","layer":"road","field":"class","value":["motorway","main"],"type":"line","cap":"round","join":"round"},"road_regular":{"source":"streets","layer":"road","field":"class","value":"street","type":"line","cap":"round","join":"round"},"road_limited":{"source":"streets","layer":"road","field":"class","value":"street_limited","type":"line","cap":"round","join":"round"},"park":{"source":"streets","layer":"landuse","field":"class","value":"park","type":"fill"},"wood":{"source":"streets","layer":"landuse","field":"class","value":"wood","type":"fill"},"school":{"source":"streets","layer":"landuse","field":"class","value":"school","type":"fill"},"cemetery":{"source":"streets","layer":"landuse","field":"class","value":"cemetery","type":"fill"},"industrial":{"source":"streets","layer":"landuse","field":"class","value":"industrial","type":"fill"},"building":{"source":"streets","layer":"building","type":"fill"},"alcohol_poi":{"source":"streets","layer":"poi_label","field":"type","value":["Alcohol"],"type":"point"},"cafe_poi":{"source":"streets","layer":"poi_label","field":"type","value":["Cafe"],"type":"point"},"embassy_poi":{"source":"streets","layer":"poi_label","field":"type","value":["Embassy"],"type":"point"},"park_poi":{"source":"streets","layer":"poi_label","field":"type","value":["Park"],"type":"point"},"restaurant_poi":{"source":"streets","layer":"poi_label","field":"type","value":["Restaurant"],"type":"point"}},"sprite":"img/maki-sprite","constants":{"park":"#c8df9f"},"structure":[{"name":"park","bucket":"park"},{"name":"wood","bucket":"wood"},{"name":"water","bucket":"water"},{"name":"building","bucket":"building"},{"name":"road_limited","bucket":"road_limited"},{"name":"road_regular","bucket":"road_regular"},{"name":"road_large","bucket":"road_large"},{"name":"alcohol_poi","bucket":"alcohol_poi"},{"name":"cafe_poi","bucket":"cafe_poi"},{"name":"embassy_poi","bucket":"embassy_poi"},{"name":"park_poi","bucket":"park_poi"},{"name":"restaurant_poi","bucket":"restaurant_poi"}],"classes":[{"name":"default","layers":{"background":{"type":"background","color":"#FFFFFF"},"park":{"type":"fill","color":"park","antialias":true,"image":"park"},"wood":{"type":"fill","color":"#33AA66","antialias":true,"opacity":0.1},"water":{"type":"fill","color":"#bae0e9","stroke":"#73b6e6","antialias":true,"image":"water_etsy"},"building":{"type":"fill","color":"#000000","antialias":true,"opacity":["linear",13,0,0.1,0,0.1]},"road_limited":{"type":"line","color":"#BBBBBB","width":["stops",{"z":0,"val":1},{"z":30,"val":1}]},"road_regular":{"type":"line","color":"#999999","width":["stops",{"z":0,"val":0.5},{"z":13,"val":0.5},{"z":16,"val":2},{"z":20,"val":32},{"z":30,"val":32}]},"road_large":{"type":"line","color":"#666666","width":["stops",{"z":0,"val":0.5},{"z":11,"val":0.5},{"z":13,"val":1},{"z":16,"val":4},{"z":20,"val":64},{"z":30,"val":64}]},"alcohol_poi":{"type":"point","color":"#cccccc","size":18,"image":"alcohol-shop","opacity":["linear",15,0,1,0,0.75]},"cafe_poi":{"type":"point","color":"#cccccc","size":18,"image":"cafe","opacity":["linear",15,0,1,0,0.75]},"embassy_poi":{"type":"point","color":"#cccccc","size":18,"image":"embassy","opacity":["linear",15,0,1,0,0.75]},"park_poi":{"type":"point","color":"#cccccc","size":18,"image":"park","opacity":["linear",15,0,1,0,0.75]},"restaurant_poi":{"type":"point","color":"#cccccc","size":18,"image":"restaurant","opacity":["linear",15,0,1,0,0.75]}}}]})JSON";
const unsigned long resources::style_size = sizeof(resources::style);

View File

@@ -1,5 +1,5 @@
#include <llmr/style/style.hpp>
#include <csscolorparser/csscolorparser.hpp>
#include <llmr/style/style_parser.hpp>
#include <rapidjson/document.h>
@@ -80,23 +80,8 @@ void Style::cascade(float z) {
}
}
void loadBucketsJSON(const rapidjson::Value& value, std::map<std::string, BucketDescription>& buckets);
BucketDescription loadBucketJSON(const rapidjson::Value& value);
void loadLayersJSON(const rapidjson::Value& value, std::vector<LayerDescription>& layers);
LayerDescription loadLayerJSON(const rapidjson::Value& value);
void loadClassesJSON(const rapidjson::Value& value, std::map<std::string, ClassDescription>& classes);
std::pair<std::string, ClassDescription> loadClassDescriptionJSON(const rapidjson::Value& value);
void loadClassJSON(const std::string& name, const rapidjson::Value& value, ClassDescription& class_desc);
FillClass loadFillClassJSON(const rapidjson::Value& value);
LineClass loadLineClassJSON(const rapidjson::Value& value);
PointClass loadPointClassJSON(const rapidjson::Value& value);
BackgroundClass loadBackgroundClassJSON(const rapidjson::Value& value);
Value loadValueJSON(const rapidjson::Value& value);
void Style::loadJSON(const uint8_t *const data, size_t bytes) {
rapidjson::Document document;
rapidjson::Document doc;
if (bytes <= 0) {
return;
@@ -106,491 +91,34 @@ void Style::loadJSON(const uint8_t *const data, size_t bytes) {
throw exception("style JSON string is not 0-terminated");
}
document.Parse<0>((const char *const)data);
doc.Parse<0>((const char *const)data);
const rapidjson::Document& document = doc;
buckets.clear();
layers.clear();
classes.clear();
StyleParser parser;
if (document.IsObject()) {
if (document.HasMember("buckets")) {
loadBucketsJSON(document["buckets"], buckets);
parser.parseBuckets(document["buckets"], buckets);
}
if (document.HasMember("structure")) {
loadLayersJSON(document["structure"], layers);
parser.parseLayers(document["structure"], layers);
}
if (document.HasMember("constants")) {
parser.parseConstants(document["constants"]);
}
if (document.HasMember("classes")) {
loadClassesJSON(document["classes"], classes);
parser.parseClasses(document["classes"], classes);
}
} else {
throw Style::exception("stylesheet root must be an object");
}
}
void loadBucketsJSON(const rapidjson::Value& value, std::map<std::string, BucketDescription>& buckets) {
if (value.IsObject()) {
rapidjson::Value::ConstMemberIterator itr = value.MemberBegin();
for (; itr != value.MemberEnd(); ++itr) {
buckets.emplace(itr->name.GetString(), std::forward<BucketDescription>(loadBucketJSON(itr->value)));
}
} else {
throw Style::exception("buckets must be an object");
}
}
BucketDescription loadBucketJSON(const rapidjson::Value& value) {
BucketDescription bucket;
rapidjson::Value::ConstMemberIterator itr = value.MemberBegin();
for (; itr != value.MemberEnd(); ++itr) {
const std::string name(itr->name.GetString(), itr->name.GetStringLength());
const rapidjson::Value& value = itr->value;
if (name == "type") {
if (value.IsString()) {
bucket.type = bucketType({ value.GetString(), value.GetStringLength() });
} else {
throw Style::exception("bucket type must be a string");
}
} else if (name == "source") {
if (value.IsString()) {
bucket.source_name = { value.GetString(), value.GetStringLength() };
} else {
throw Style::exception("source name must be a string");
}
} else if (name == "layer") {
if (value.IsString()) {
bucket.source_layer = { value.GetString(), value.GetStringLength() };
} else {
throw Style::exception("layer name must be a string");
}
} else if (name == "field") {
if (value.IsString()) {
bucket.source_field = { value.GetString(), value.GetStringLength() };
} else {
throw Style::exception("field name must be a string");
}
} else if (name == "value") {
if (value.IsArray()) {
for (rapidjson::SizeType i = 0; i < value.Size(); ++i) {
bucket.source_value.push_back(loadValueJSON(value[i]));
}
} else {
bucket.source_value.push_back(loadValueJSON(value));
}
} else if (name == "cap") {
if (value.IsString()) {
bucket.geometry.cap = capType({ value.GetString(), value.GetStringLength() });
} else {
throw Style::exception("cap type must be a string");
}
} else if (name == "join") {
if (value.IsString()) {
bucket.geometry.join = joinType({ value.GetString(), value.GetStringLength() });
} else {
throw Style::exception("join type must be a string");
}
} else if (name == "font") {
if (value.IsString()) {
bucket.geometry.font = { value.GetString(), value.GetStringLength() };
} else {
throw Style::exception("font stack must be a string");
}
} else if (name == "fontSize") {
if (value.IsNumber()) {
bucket.geometry.font_size = value.GetDouble();
} else {
throw Style::exception("font size must be a number");
}
} else if (name == "miterLimit") {
if (value.IsNumber()) {
bucket.geometry.miter_limit = value.GetDouble();
} else {
throw Style::exception("miter limit must be a number");
}
} else if (name == "roundLimit") {
if (value.IsNumber()) {
bucket.geometry.round_limit = value.GetDouble();
} else {
throw Style::exception("round limit must be a number");
}
}
}
return bucket;
}
void loadLayersJSON(const rapidjson::Value& value, std::vector<LayerDescription>& layers) {
if (value.IsArray()) {
for (rapidjson::SizeType i = 0; i < value.Size(); ++i) {
layers.push_back(loadLayerJSON(value[i]));
}
} else {
throw Style::exception("structure must be an array");
}
}
LayerDescription loadLayerJSON(const rapidjson::Value& value) {
LayerDescription layer;
if (value.IsObject()) {
if (value.HasMember("name")) {
const rapidjson::Value& name = value["name"];
if (name.IsString()) {
layer.name = { name.GetString(), name.GetStringLength() };
} else {
throw Style::exception("structure element name must be a string");
}
} else {
throw Style::exception("structure element must have a name");
}
if (value.HasMember("bucket")) {
const rapidjson::Value& bucket = value["bucket"];
if (bucket.IsString()) {
layer.bucket_name = { bucket.GetString(), bucket.GetStringLength() };
} else {
throw Style::exception("structure element bucket must be a string");
}
} else if (value.HasMember("layers")) {
loadLayersJSON(value["layers"], layer.child_layer);
} else {
throw Style::exception("structure element must have either a bucket name or child layers");
}
} else {
throw Style::exception("structure element must be an object");
}
return layer;
}
void loadClassesJSON(const rapidjson::Value& value, std::map<std::string, ClassDescription>& classes) {
if (value.IsArray()) {
for (rapidjson::SizeType i = 0; i < value.Size(); ++i) {
classes.insert(std::forward<std::pair<std::string, ClassDescription>>(loadClassDescriptionJSON(value[i])));
}
} else {
throw Style::exception("classes must be an array");
}
}
std::pair<std::string, ClassDescription> loadClassDescriptionJSON(const rapidjson::Value& value) {
ClassDescription klass;
std::string klass_name;
if (value.IsObject()) {
if (value.HasMember("name")) {
const rapidjson::Value& name = value["name"];
if (name.IsString()) {
klass_name = { name.GetString(), name.GetStringLength() };
} else {
throw Style::exception("class name must be a string");
}
} else {
throw Style::exception("class must have a name");
}
if (value.HasMember("layers")) {
const rapidjson::Value& layers = value["layers"];
if (layers.IsObject()) {
rapidjson::Value::ConstMemberIterator itr = layers.MemberBegin();
for (; itr != layers.MemberEnd(); ++itr) {
const std::string name {
itr->name.GetString(), itr->name.GetStringLength()
};
loadClassJSON(name, itr->value, klass);
}
} else {
throw Style::exception("class layer styles must be an object");
}
} else {
throw Style::exception("class must have layer styles");
}
}
return { klass_name, std::forward<ClassDescription>(klass) };
}
void loadClassJSON(const std::string& name, const rapidjson::Value& value, ClassDescription& class_desc) {
if (value.IsObject()) {
if (value.HasMember("type")) {
const rapidjson::Value& type = value["type"];
if (type.IsString()) {
std::string type_name = { type.GetString(), type.GetStringLength() };
if (type_name == "fill") {
class_desc.fill.insert({ name, std::forward<FillClass>(loadFillClassJSON(value)) });
} else if (type_name == "line") {
class_desc.line.insert({ name, std::forward<LineClass>(loadLineClassJSON(value)) });
} else if (type_name == "point") {
class_desc.point.insert({ name, std::forward<PointClass>(loadPointClassJSON(value)) });
} else if (type_name == "background") {
class_desc.background = loadBackgroundClassJSON(value);
} else {
throw Style::exception("unkonwn class type name");
}
} else {
throw Style::exception("style class type must be a string");
}
} else {
throw Style::exception("style class must specify a type");
}
} else {
throw Style::exception("style class must be an object");
}
}
bool parseBoolean(const rapidjson::Value& value) {
if (!value.IsBool()) {
throw Style::exception("boolean value must be a boolean");
}
return value.GetBool();
}
std::string parseString(const rapidjson::Value& value) {
if (!value.IsString()) {
throw Style::exception("string value must be a string");
}
return { value.GetString(), value.GetStringLength() };
}
Color parseColor(const rapidjson::Value& value) {
if (!value.IsString()) {
throw Style::exception("color value must be a string");
}
CSSColorParser::Color css_color = CSSColorParser::parse({
value.GetString(),
value.GetStringLength()
});
return {{
(float)css_color.r / 255,
(float)css_color.g / 255,
(float)css_color.b / 255,
css_color.a
}
};
}
template <typename T>
typename FunctionProperty<T>::fn parseFunctionType(const rapidjson::Value& type) {
if (type.IsString()) {
std::string t { type.GetString(), type.GetStringLength() };
if (t == "constant") {
return &functions::constant;
} else if (t == "linear") {
return &functions::linear;
} else if (t == "stops") {
return &functions::stops;
} else {
throw Style::exception("unknown function type");
}
} else {
throw Style::exception("function type must be a string");
}
}
FunctionProperty<float> parseFloatFunction(const rapidjson::Value& value) {
FunctionProperty<float> property;
if (value.IsArray()) {
if (value.Size() < 1) {
throw Style::exception("value function does not have arguments");
}
property.function = parseFunctionType<float>(value[(rapidjson::SizeType)0]);
for (rapidjson::SizeType i = 1; i < value.Size(); ++i) {
const rapidjson::Value& stop = value[i];
if (stop.IsObject()) {
if (!stop.HasMember("z")) {
throw Style::exception("stop must have zoom level specification");
}
const rapidjson::Value& z = stop["z"];
if (!z.IsNumber()) {
throw Style::exception("zoom level in stops must be a number");
}
property.values.push_back(z.GetDouble());
if (!stop.HasMember("val")) {
throw Style::exception("stop must have value specification");
}
const rapidjson::Value& val = stop["val"];
if (!val.IsNumber()) {
throw Style::exception("value in stops must be a number");
}
property.values.push_back(val.GetDouble());
} else if (stop.IsNumber()) {
property.values.push_back(stop.GetDouble());
} else {
throw Style::exception("stop must be a number");
}
}
} else if (value.IsNumber()) {
property.function = &functions::constant;
property.values.push_back(value.GetDouble());
}
return property;
}
FunctionProperty<bool> parseBoolFunction(const rapidjson::Value& value) {
FunctionProperty<bool> property;
if (value.IsArray()) {
if (value.Size() < 1) {
throw Style::exception("value function does not have arguments");
}
property.function = parseFunctionType<bool>(value[(rapidjson::SizeType)0]);
for (rapidjson::SizeType i = 1; i < value.Size(); ++i) {
const rapidjson::Value& stop = value[i];
if (stop.IsObject()) {
if (!stop.HasMember("z")) {
throw Style::exception("stop must have zoom level specification");
}
const rapidjson::Value& z = stop["z"];
if (!z.IsBool()) {
throw Style::exception("zoom level in stops must be a number");
}
property.values.push_back(z.GetBool());
if (!stop.HasMember("val")) {
throw Style::exception("stop must have value specification");
}
const rapidjson::Value& val = stop["val"];
if (!val.IsBool()) {
throw Style::exception("value in stops must be a number");
}
property.values.push_back(val.GetBool());
} else if (stop.IsBool()) {
property.values.push_back(stop.GetBool());
} else {
throw Style::exception("stop must be a number");
}
}
} else if (value.IsBool()) {
property.function = &functions::constant;
property.values.push_back(value.GetBool());
}
return property;
}
FillClass loadFillClassJSON(const rapidjson::Value& value) {
FillClass klass;
if (value.HasMember("hidden")) {
klass.hidden = parseBoolFunction(value["hidden"]);
}
if (value.HasMember("color")) {
klass.fill_color = parseColor(value["color"]);
}
if (value.HasMember("stroke")) {
klass.stroke_color = parseColor(value["stroke"]);
} else {
klass.stroke_color = klass.fill_color;
}
if (value.HasMember("antialias")) {
klass.antialias = parseBoolean(value["antialias"]);
}
if (value.HasMember("image")) {
klass.image = parseString(value["image"]);
}
if (value.HasMember("opacity")) {
klass.opacity = parseFloatFunction(value["opacity"]);
}
return klass;
}
LineClass loadLineClassJSON(const rapidjson::Value& value) {
LineClass klass;
if (value.HasMember("hidden")) {
klass.hidden = parseBoolFunction(value["hidden"]);
}
if (value.HasMember("color")) {
klass.color = parseColor(value["color"]);
}
if (value.HasMember("width")) {
klass.width = parseFloatFunction(value["width"]);
}
if (value.HasMember("opacity")) {
klass.opacity = parseFloatFunction(value["opacity"]);
}
return klass;
}
PointClass loadPointClassJSON(const rapidjson::Value& value) {
PointClass klass;
if (value.HasMember("hidden")) {
klass.hidden = parseBoolFunction(value["hidden"]);
}
if (value.HasMember("color")) {
klass.color = parseColor(value["color"]);
}
if (value.HasMember("opacity")) {
klass.opacity = parseFloatFunction(value["opacity"]);
}
if (value.HasMember("image")) {
klass.image = parseString(value["image"]);
}
if (value.HasMember("size")) {
klass.size = parseFloatFunction(value["size"]);
}
return klass;
}
BackgroundClass loadBackgroundClassJSON(const rapidjson::Value& value) {
BackgroundClass klass;
if (value.HasMember("color")) {
klass.color = parseColor(value["color"]);
}
return klass;
}
Value loadValueJSON(const rapidjson::Value& value) {
switch (value.GetType()) {
case rapidjson::kNullType:
case rapidjson::kFalseType:
return false;
case rapidjson::kTrueType:
return true;
case rapidjson::kStringType:
return std::string { value.GetString(), value.GetStringLength() };
case rapidjson::kNumberType:
if (value.IsUint64()) return value.GetUint64();
if (value.IsInt64()) return value.GetInt64();
return value.GetDouble();
case rapidjson::kObjectType:
case rapidjson::kArrayType:
throw Style::exception("value cannot be an object or array");
return false;
}
}

490
src/style/style_parser.cpp Normal file
View File

@@ -0,0 +1,490 @@
#include <llmr/style/style_parser.hpp>
#include <csscolorparser/csscolorparser.hpp>
using namespace llmr;
void StyleParser::parseBuckets(JSVal value, std::map<std::string, BucketDescription>& buckets) {
if (value.IsObject()) {
rapidjson::Value::ConstMemberIterator itr = value.MemberBegin();
for (; itr != value.MemberEnd(); ++itr) {
buckets.emplace(
std::forward<std::string>({ itr->name.GetString(), itr->name.GetStringLength() }),
std::forward<BucketDescription>(parseBucket(itr->value))
);
}
} else {
throw Style::exception("buckets must be an object");
}
}
BucketDescription StyleParser::parseBucket(JSVal value) {
BucketDescription bucket;
rapidjson::Value::ConstMemberIterator itr = value.MemberBegin();
for (; itr != value.MemberEnd(); ++itr) {
const std::string name(itr->name.GetString(), itr->name.GetStringLength());
JSVal value = itr->value;
if (name == "type") {
if (value.IsString()) {
bucket.type = bucketType({ value.GetString(), value.GetStringLength() });
} else {
throw Style::exception("bucket type must be a string");
}
} else if (name == "source") {
if (value.IsString()) {
bucket.source_name = { value.GetString(), value.GetStringLength() };
} else {
throw Style::exception("source name must be a string");
}
} else if (name == "layer") {
if (value.IsString()) {
bucket.source_layer = { value.GetString(), value.GetStringLength() };
} else {
throw Style::exception("layer name must be a string");
}
} else if (name == "field") {
if (value.IsString()) {
bucket.source_field = { value.GetString(), value.GetStringLength() };
} else {
throw Style::exception("field name must be a string");
}
} else if (name == "value") {
if (value.IsArray()) {
for (rapidjson::SizeType i = 0; i < value.Size(); ++i) {
bucket.source_value.push_back(parseValue(value[i]));
}
} else {
bucket.source_value.push_back(parseValue(value));
}
} else if (name == "cap") {
if (value.IsString()) {
bucket.geometry.cap = capType({ value.GetString(), value.GetStringLength() });
} else {
throw Style::exception("cap type must be a string");
}
} else if (name == "join") {
if (value.IsString()) {
bucket.geometry.join = joinType({ value.GetString(), value.GetStringLength() });
} else {
throw Style::exception("join type must be a string");
}
} else if (name == "font") {
if (value.IsString()) {
bucket.geometry.font = { value.GetString(), value.GetStringLength() };
} else {
throw Style::exception("font stack must be a string");
}
} else if (name == "fontSize") {
if (value.IsNumber()) {
bucket.geometry.font_size = value.GetDouble();
} else {
throw Style::exception("font size must be a number");
}
} else if (name == "miterLimit") {
if (value.IsNumber()) {
bucket.geometry.miter_limit = value.GetDouble();
} else {
throw Style::exception("miter limit must be a number");
}
} else if (name == "roundLimit") {
if (value.IsNumber()) {
bucket.geometry.round_limit = value.GetDouble();
} else {
throw Style::exception("round limit must be a number");
}
}
}
return bucket;
}
void StyleParser::parseLayers(JSVal value, std::vector<LayerDescription>& layers) {
if (value.IsArray()) {
for (rapidjson::SizeType i = 0; i < value.Size(); ++i) {
layers.push_back(parseLayer(value[i]));
}
} else {
throw Style::exception("structure must be an array");
}
}
LayerDescription StyleParser::parseLayer(JSVal value) {
LayerDescription layer;
if (value.IsObject()) {
if (value.HasMember("name")) {
JSVal name = value["name"];
if (name.IsString()) {
layer.name = { name.GetString(), name.GetStringLength() };
} else {
throw Style::exception("structure element name must be a string");
}
} else {
throw Style::exception("structure element must have a name");
}
if (value.HasMember("bucket")) {
JSVal bucket = value["bucket"];
if (bucket.IsString()) {
layer.bucket_name = { bucket.GetString(), bucket.GetStringLength() };
} else {
throw Style::exception("structure element bucket must be a string");
}
} else if (value.HasMember("layers")) {
parseLayers(value["layers"], layer.child_layer);
} else {
throw Style::exception("structure element must have either a bucket name or child layers");
}
} else {
throw Style::exception("structure element must be an object");
}
return layer;
}
void StyleParser::parseConstants(JSVal value) {
if (value.IsObject()) {
rapidjson::Value::ConstMemberIterator itr = value.MemberBegin();
for (; itr != value.MemberEnd(); ++itr) {
constants.emplace(
std::forward<std::string>({ itr->name.GetString(), itr->name.GetStringLength() }),
&itr->value
);
}
} else {
throw Style::exception("constants must be an object");
}
}
void StyleParser::parseClasses(JSVal value, std::map<std::string, ClassDescription>& classes) {
if (value.IsArray()) {
for (rapidjson::SizeType i = 0; i < value.Size(); ++i) {
classes.insert(std::forward<std::pair<std::string, ClassDescription>>(parseClassDescription(value[i])));
}
} else {
throw Style::exception("classes must be an array");
}
}
std::pair<std::string, ClassDescription> StyleParser::parseClassDescription(JSVal value) {
ClassDescription klass;
std::string klass_name;
if (value.IsObject()) {
if (value.HasMember("name")) {
JSVal name = value["name"];
if (name.IsString()) {
klass_name = { name.GetString(), name.GetStringLength() };
} else {
throw Style::exception("class name must be a string");
}
} else {
throw Style::exception("class must have a name");
}
if (value.HasMember("layers")) {
JSVal layers = value["layers"];
if (layers.IsObject()) {
rapidjson::Value::ConstMemberIterator itr = layers.MemberBegin();
for (; itr != layers.MemberEnd(); ++itr) {
const std::string name {
itr->name.GetString(), itr->name.GetStringLength()
};
parseClass(name, itr->value, klass);
}
} else {
throw Style::exception("class layer styles must be an object");
}
} else {
throw Style::exception("class must have layer styles");
}
}
return { klass_name, std::forward<ClassDescription>(klass) };
}
void StyleParser::parseClass(const std::string& name, JSVal value, ClassDescription& class_desc) {
if (value.IsObject()) {
if (value.HasMember("type")) {
JSVal type = value["type"];
if (type.IsString()) {
std::string type_name = { type.GetString(), type.GetStringLength() };
if (type_name == "fill") {
class_desc.fill.insert({ name, std::forward<FillClass>(parseFillClass(value)) });
} else if (type_name == "line") {
class_desc.line.insert({ name, std::forward<LineClass>(parseLineClass(value)) });
} else if (type_name == "point") {
class_desc.point.insert({ name, std::forward<PointClass>(parsePointClass(value)) });
} else if (type_name == "background") {
class_desc.background = parseBackgroundClass(value);
} else {
throw Style::exception("unkonwn class type name");
}
} else {
throw Style::exception("style class type must be a string");
}
} else {
throw Style::exception("style class must specify a type");
}
} else {
throw Style::exception("style class must be an object");
}
}
bool StyleParser::parseBoolean(JSVal value) {
if (!value.IsBool()) {
throw Style::exception("boolean value must be a boolean");
}
return value.GetBool();
}
std::string StyleParser::parseString(JSVal value) {
if (!value.IsString()) {
throw Style::exception("string value must be a string");
}
return { value.GetString(), value.GetStringLength() };
}
Color StyleParser::parseColor(JSVal value) {
if (!value.IsString()) {
throw Style::exception("color value must be a string");
}
const std::string str { value.GetString(), value.GetStringLength() };
auto it = constants.find(str);
if (it != constants.end()) {
return parseColor(*it->second);
} else {
CSSColorParser::Color css_color = CSSColorParser::parse(str);
return {{
(float)css_color.r / 255,
(float)css_color.g / 255,
(float)css_color.b / 255,
css_color.a
}
};
}
}
template <typename T>
typename FunctionProperty<T>::fn StyleParser::parseFunctionType(JSVal type) {
if (type.IsString()) {
std::string t { type.GetString(), type.GetStringLength() };
if (t == "constant") {
return &functions::constant;
} else if (t == "linear") {
return &functions::linear;
} else if (t == "stops") {
return &functions::stops;
} else {
throw Style::exception("unknown function type");
}
} else {
throw Style::exception("function type must be a string");
}
}
FunctionProperty<float> StyleParser::parseFloatFunction(JSVal value) {
FunctionProperty<float> property;
if (value.IsArray()) {
if (value.Size() < 1) {
throw Style::exception("value function does not have arguments");
}
property.function = parseFunctionType<float>(value[(rapidjson::SizeType)0]);
for (rapidjson::SizeType i = 1; i < value.Size(); ++i) {
JSVal stop = value[i];
if (stop.IsObject()) {
if (!stop.HasMember("z")) {
throw Style::exception("stop must have zoom level specification");
}
JSVal z = stop["z"];
if (!z.IsNumber()) {
throw Style::exception("zoom level in stops must be a number");
}
property.values.push_back(z.GetDouble());
if (!stop.HasMember("val")) {
throw Style::exception("stop must have value specification");
}
JSVal val = stop["val"];
if (!val.IsNumber()) {
throw Style::exception("value in stops must be a number");
}
property.values.push_back(val.GetDouble());
} else if (stop.IsNumber()) {
property.values.push_back(stop.GetDouble());
} else {
throw Style::exception("stop must be a number");
}
}
} else if (value.IsNumber()) {
property.function = &functions::constant;
property.values.push_back(value.GetDouble());
}
return property;
}
FunctionProperty<bool> StyleParser::parseBoolFunction(JSVal value) {
FunctionProperty<bool> property;
if (value.IsArray()) {
if (value.Size() < 1) {
throw Style::exception("value function does not have arguments");
}
property.function = parseFunctionType<bool>(value[(rapidjson::SizeType)0]);
for (rapidjson::SizeType i = 1; i < value.Size(); ++i) {
JSVal stop = value[i];
if (stop.IsObject()) {
if (!stop.HasMember("z")) {
throw Style::exception("stop must have zoom level specification");
}
JSVal z = stop["z"];
if (!z.IsBool()) {
throw Style::exception("zoom level in stops must be a number");
}
property.values.push_back(z.GetBool());
if (!stop.HasMember("val")) {
throw Style::exception("stop must have value specification");
}
JSVal val = stop["val"];
if (!val.IsBool()) {
throw Style::exception("value in stops must be a number");
}
property.values.push_back(val.GetBool());
} else if (stop.IsBool()) {
property.values.push_back(stop.GetBool());
} else {
throw Style::exception("stop must be a number");
}
}
} else if (value.IsBool()) {
property.function = &functions::constant;
property.values.push_back(value.GetBool());
}
return property;
}
FillClass StyleParser::parseFillClass(JSVal value) {
FillClass klass;
if (value.HasMember("hidden")) {
klass.hidden = parseBoolFunction(value["hidden"]);
}
if (value.HasMember("color")) {
klass.fill_color = parseColor(value["color"]);
}
if (value.HasMember("stroke")) {
klass.stroke_color = parseColor(value["stroke"]);
} else {
klass.stroke_color = klass.fill_color;
}
if (value.HasMember("antialias")) {
klass.antialias = parseBoolean(value["antialias"]);
}
if (value.HasMember("image")) {
klass.image = parseString(value["image"]);
}
if (value.HasMember("opacity")) {
klass.opacity = parseFloatFunction(value["opacity"]);
}
return klass;
}
LineClass StyleParser::parseLineClass(JSVal value) {
LineClass klass;
if (value.HasMember("hidden")) {
klass.hidden = parseBoolFunction(value["hidden"]);
}
if (value.HasMember("color")) {
klass.color = parseColor(value["color"]);
}
if (value.HasMember("width")) {
klass.width = parseFloatFunction(value["width"]);
}
if (value.HasMember("opacity")) {
klass.opacity = parseFloatFunction(value["opacity"]);
}
return klass;
}
PointClass StyleParser::parsePointClass(JSVal value) {
PointClass klass;
if (value.HasMember("hidden")) {
klass.hidden = parseBoolFunction(value["hidden"]);
}
if (value.HasMember("color")) {
klass.color = parseColor(value["color"]);
}
if (value.HasMember("opacity")) {
klass.opacity = parseFloatFunction(value["opacity"]);
}
if (value.HasMember("image")) {
klass.image = parseString(value["image"]);
}
if (value.HasMember("size")) {
klass.size = parseFloatFunction(value["size"]);
}
return klass;
}
BackgroundClass StyleParser::parseBackgroundClass(JSVal value) {
BackgroundClass klass;
if (value.HasMember("color")) {
klass.color = parseColor(value["color"]);
}
return klass;
}
Value StyleParser::parseValue(JSVal value) {
switch (value.GetType()) {
case rapidjson::kNullType:
case rapidjson::kFalseType:
return false;
case rapidjson::kTrueType:
return true;
case rapidjson::kStringType:
return std::string { value.GetString(), value.GetStringLength() };
case rapidjson::kNumberType:
if (value.IsUint64()) return value.GetUint64();
if (value.IsInt64()) return value.GetInt64();
return value.GetDouble();
case rapidjson::kObjectType:
case rapidjson::kArrayType:
throw Style::exception("value cannot be an object or array");
return false;
}
}