GeoJSON
The GeoJSON Specification (RFC 7946)
1 Included Data
1.1 Top level output
Element |
Type |
type |
"FeatureCollection" |
feature |
feature List of shape, text and image features detailed below |
1.2 Shape Feature
Element |
Type |
Required |
Value |
type |
string |
Required |
"Feature" |
properties |
object |
Required |
Drawing properties |
style |
object |
Required |
Same data as "properties" element in SVG style |
geometry |
object |
Required |
Points to plot |
1.2.1 Properties
Element |
Type |
Required |
Value |
label |
string |
Required |
"" |
strokeColor |
string |
Optional |
Hex string such as "#000000" |
lineOpacity |
number |
Optional |
Number between 0 and 1 inclusive |
fillColor |
string |
Optional |
Hex string such as "#000000" |
fillOpacity |
number |
Optional |
Number between 0 and 1 inclusive |
fillPattern |
string |
Optional |
base64 encoded image |
strokeDasharray |
string |
Optional |
Example: "2, 2" |
lineCap |
number |
Required |
0 1 or 2 |
strokeWidth |
number |
Required |
Pixel width of lines |
strokeWeight |
number |
Required |
Same value as strokeWidth |
1.2.2 Style
Element |
Type |
Required |
Value |
strokeColor |
string |
Optional |
Hex string such as "#000000" |
line-opacity |
number |
Optional |
Number between 0 and 1 inclusive |
fill |
string |
Optional |
Hex string such as "#000000" |
fill-opacity |
number |
Optional |
Number between 0 and 1 inclusive |
fillPattern |
string |
Optional |
base64 encoded image |
stroke-dasharray |
string |
Optional |
Example: "2 2" |
stroke-lineCap |
string |
Required |
"square" "round" or "butt" |
stroke-width |
number |
Required |
Pixel width of lines |
1.2.3 Geometry
Element |
Type |
Required |
Value |
type |
string |
Required |
"Polygon" or "MultiLineString" |
coordinates |
[ [ [longitude,latitude],[longitude,latitude] ], [second line], ...] |
Required |
Any array of polylines where each polyline is an array of coordinate points. |
1.3 Text Feature Element Type Value
Element |
Type |
Value |
type |
string |
"Feature" |
properties |
object |
|
1.3.1 Text Feature Properties
Element |
Type |
Value |
label |
string |
Text Modifier String |
pointRadius |
number |
0 |
fontColor |
string |
Hex string such as"#000000" |
fontSize |
string |
Example: "12 pt" |
fontFamily |
string |
Example: "arial, sans-serif" |
fontWeight |
string |
"bold" or "normal" |
labelAlign |
string |
"left" "center" or "right" |
labelXOffset |
number |
0 |
labelYOffset |
number |
0 |
labelOutlineColor |
string |
Hex string such as "#000000" |
labelOutlineWidth |
number |
Example: 4 |
rotation |
number |
Text rotation in degrees |
angle |
number |
Same value as rotation |
1.3.2 Geometry
Element |
Type |
Value |
type |
string |
"Point" |
coordinates |
[longitude,latitude] |
Number array of format [x, y] |
1.4 Image Feature
Element |
Type |
Value |
type |
string |
"Feature" |
properties |
object |
Number array of format [x, y] |
1.4.1 Image Feature Properties
Element |
Type |
Value |
image |
string |
base64 encoding of image |
rotation |
number |
Text rotation in degrees |
angle |
number |
Same value as rotation |
1.4.2 Image Feature Geometry
Element |
Type |
Value |
type |
string |
"Point" |
coordinates |
[longitude,latitude] |
Number array of format [x, y] |
1.5 Metadata Feature
Element |
Type |
Value |
type |
string |
"Feature" |
geometry |
object |
|
properties |
object |
|
1.5.1 Metadata Properties
Element |
Type |
Value |
id |
string |
User defined input |
name |
string |
User defined input |
description |
string |
User defined input |
symbolID |
string |
20-30 digit code corresponding to a symbol |
wasClipped |
boolean |
False if the rendered symbol fit inside entirely within the bounding box |
1.5.2 Metadata Geometry
Element |
Type |
Value |
type |
string |
"Point" |
coordinates |
empty array |
|
2 Example Output
WebRenderer.RenderSymbol2D("id", "Base Camp", "Control Measure / Command and Control Areas", "110325000012050000000000000000", "49.3,19.8 49.2,19.4 49.6,19.4 49.7,19.8", 1200, 900, "49.0,19.0,50.0,20.0", new HashMap<>(), new HashMap<>(), WebRenderer.OUTPUT_FORMAT_GEOJSON);
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"label": "",
"strokeColor": "#000000",
"lineOpacity": 1,
"lineCap": 2,
"strokeWidth": 3,
"strokeWeight": 3
},
"style": {
"stroke": "#000000",
"line-opacity": 1,
"stroke-linecap": "square",
"stroke-width": 3
},
"geometry": {
"type": "MultiLineString",
"coordinates": [
[
[
49.3,
19.8
],
[
49.2,
19.4
],
[
49.6,
19.4
],
[
49.7,
19.8
],
[
49.3,
19.8
]
]
]
}
},
{
"type": "Feature",
"properties": {
"label": "BC",
"pointRadius": 0,
"fontColor": "#000000",
"fontSize": "12pt",
"fontFamily": "arial, sans-serif",
"fontWeight": "bold",
"labelAlign": "center",
"labelBaseline": "alphabetic",
"labelXOffset": 0,
"labelYOffset": 0,
"labelOutlineColor": "#FFFFFF",
"labelOutlineWidth": 4,
"rotation": 0,
"angle": 0
},
"geometry": {
"type": "Point",
"coordinates": [
49.45,
19.59333333
]
}
},
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": []
},
"properties": {
"id": "id",
"name": "Base Camp",
"description": "Control Measure / Command and Control Areas",
"symbolID": "110325000012050000000000000000",
"wasClipped": "false"
}
}
]
}
3 Plotting GeoJSON symbols in Cesium
Cesium does not include colors, labels, fills and image modifiers from GeoJSON. The following function adds any missing features to each entity.
async function renderGeojson(geo_json_str: string, viewer: Cesium.Viewer) {
const geo_json = JSON.parse(geo_json_str)
if (geo_json.type == "error") {
console.log(geo_json.error);
return;
}
let geoJsonDataSource = await Cesium.GeoJsonDataSource.load(geo_json, {
stroke: Cesium.Color.BLACK,
fill: Cesium.Color.TRANSPARENT,
clampToGround: true
})
for (let entity of geoJsonDataSource.entities.values) {
if (entity.polyline) {
entity.polyline!.width = entity.properties['strokeWidth'].getValue();
if (entity.properties['strokeColor']) {
entity.polyline.material = new Cesium.ColorMaterialProperty(Cesium.Color.fromCssColorString(entity.properties['strokeColor'].getValue()));
}
} else if (entity.polygon) {
if (entity.properties) {
if (entity.properties['fillPattern']) {
entity.polygon.material = new WallPaperMaterialProperty(viewer.scene, entity.properties['fillPattern'].getValue(), entity.polygon.hierarchy.getValue(viewer.clock.currentTime));
}
if (entity.properties['strokeColor']) {
// Polygon outline requires height to be defined. Height = 0 might place polygon below terrain. Height > 0 raises polygon above other entities.
// Adding outline as polyline allows polygon to remain clamped to ground
// https://community.cesium.com/t/polygon-clamp-to-ground-when-terrain-provider-is-used/22798
// Note outline and fill might not be perfectly aligned
// https://community.cesium.com/t/unaligned-polygon-and-polyline/9059
entity.polyline = new Cesium.PolylineGraphics();
entity.polyline.positions = entity.polygon.hierarchy.getValue(viewer.clock.currentTime).positions;
entity.polyline.material = new Cesium.ColorMaterialProperty(Cesium.Color.fromCssColorString(entity.properties['strokeColor'].getValue()));
entity.polyline.width = entity.properties['strokeWidth'].getValue();
entity.polyline.clampToGround = new Cesium.ConstantProperty(true);
}
if (entity.properties['fillColor']) {
entity.polygon.fill = new Cesium.ConstantProperty(true);
if (entity.properties['fillOpacity']) {
entity.polygon.material = new Cesium.ColorMaterialProperty(Cesium.Color.fromCssColorString(entity.properties['fillColor'].getValue()).withAlpha(entity.properties['fillOpacity'].getValue()));
} else {
entity.polygon.material = new Cesium.ColorMaterialProperty(Cesium.Color.fromCssColorString(entity.properties['fillColor'].getValue()));
}
}
}
} else if (entity.billboard) {
if (entity.properties['label']) {
entity.billboard.color = new Cesium.ColorMaterialProperty(Cesium.Color.TRANSPARENT);
entity.billboard.show = new Cesium.ConstantProperty(false);
const fontColor: Cesium.Color = entity.properties['fontColor']
? Cesium.Color.fromCssColorString(entity.properties['fontColor'].getValue())
: Cesium.Color.BLACK;
const outlineColor: Cesium.Color = entity.properties['labelOutlineColor']
? Cesium.Color.fromCssColorString(entity.properties['labelOutlineColor'].getValue())
: null;
const outlineWidth: number = entity.properties['labelOutlineWidth'] ? entity.properties['labelOutlineWidth'].getValue() : 0;
const fontSize: string = entity.properties['fontSize'] ? entity.properties['fontSize'].getValue() : "12pt";
const fontFamily: string = entity.properties['fontFamily'] ? entity.properties['fontFamily'].getValue() : "Helvetica";
const align: string = entity.properties['labelAlign'] ? entity.properties['labelAlign'].getValue() : "left";
var horizontalOrigin: Cesium.HorizontalOrigin = Cesium.HorizontalOrigin.LEFT;
if (align == "center") {
horizontalOrigin = Cesium.HorizontalOrigin.CENTER;
} else if (align == "right") {
horizontalOrigin = Cesium.HorizontalOrigin.RIGHT;
}
entity.label = new Cesium.LabelGraphics({
text: entity.properties['label'].getValue(),
horizontalOrigin: horizontalOrigin,
font: fontSize + ' ' + fontFamily,
fillColor: fontColor,
outlineColor: outlineColor,
outlineWidth: outlineWidth,
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
disableDepthTestDistance: Number.POSITIVE_INFINITY,
});
} else if (entity.properties['image']) {
entity.billboard.image = entity.properties['image'];
entity.billboard.disableDepthTestDistance = new Cesium.ConstantProperty(Number.POSITIVE_INFINITY);
entity.billboard.rotation = new Cesium.ConstantProperty(-entity.properties['rotation'].getValue() / 360 * 2 * Math.PI);
}
}
}
viewer.dataSources.add(geoJsonDataSource);
};