Zum Hauptinhalt springen

style.json

Die style.json beinhaltet Visualisierungsvorschriften zum steuern der Darstellung von Vektor-Features. Sie ist damit für alle Arten von Vektorlayern relevant, wie WFS, GeoJson und Sensor.

Was geschieht beim Starten der Kartenviewer API

Beim Starten wird die konfigurierte style.json eingelesen und eine Liste aller dort definierten styles in einer internen Datenstruktur angelegt. Die Layer fragen bei Bedarf diese Liste ab und erhalten die definierten Stylingvorschriften.

Hinweis: Jeder Eintrag der Liste kann zu Testzwecken aus der console für eine spezifische styleId abgefragt werden:

Backbone.Radio.request("StyleList", "returnModelById", "styleId")

Erst zu dem Zeitpunkt, wenn ein Layer visualisiert werden soll, werden aus der internen Datenstruktur openlayer Styles abgeleitet und den Features zugeordnet.

Hinweis: Malformed style.json Dateien werden vollständig abgewiesen und führen zur Ausgabe einer entsprechenden Fehlermeldung. Wir empfehlen eine syntaktische Prüfung z.B. über freie Online-Validatoren, wie jsonlint.

Konfiguration des Styling-Moduls

Der Pfad zur verwendenden style.json wird in der config.js definiert und ist dort dokumentiert. Bitte beachten Sie folgende Parameter:

  • useVectorStyleBeta: Zum steuern der Version.
  • styleConf: Zum definieren des Pfades zur Datei.

Hinweis: Fehlerhafte Pfadangaben werden über eine entsprechende Fehlermeldung mitgeteilt.

Layerverknüpfung

In der config.json erfolgt im Abschnitt Themenconfig.Layer.Vector die Definition der Layer im Portal. Dort wird für jeden Vektorlayer auch eine styleId zwingend verlangt. Diese styleId stellt die Verbindung zur style.json dar und wird in dieser ebenfalls über das Attribut styleId verknüpft Siehe hier.

Hinweis: Eine fehlerhafte Verknüpfung führt zu keinem Laufzeitfehler. Es wird eine Meldung in der console ausgegeben, der Adminsitratoren auf die Fehlkonfiguration hinweist. Der Portalnutzer bekommt hiervon nur insofern etwas mit, als dass das Styling dem openlayers default entspricht.

Aufbau

Nachfolgend wird der syntaktische und schematische Aufbau und die Funktionsweise der style.json erklärt.

Hinweis: Die grundsätzliche Syntax von Json-Dateien ist z.B. hier erklärt und gilt auch für die style.json.

Die style.json enthält grundsätzlich nur ein Array von Objekten. Das Array umschließt dabei Styledefinitionen. Jede Styledefinition kann mit einem Layer verknüpft werden.

[
{}, // Styledefinition 1
{} // Styledefinition 2
]

Zur Verknüpfung einer Styledefinition aus dem Array mit einem Layer bedarf es zwingend eines Attributs styleId. Näheres hierzu unter Layerverknüpfung.

{
"styleId": "1711"
}

Parallel zum Attribut styleId wird zwingend ein Attribut rules erwartet. Das rules-Attribut ist dabei wieder ein Array und umfasst alle Regeln eines Layers.

{
"styleId": "1711",
"rules": []
}

Jede Regel im Array rules ist ein Objekt und besteht zwingend aus dem Attribut style. Das style-Attribut nimmt die Abbildungsvorschriften entgegen, die bei dieser Regel angewendet werden sollen. Näheres hierzu unter Abbildungsvorschriften.

"rules": [
{
"style": {}
}
]

Parallel zum Attribut style kann optional ein Attribut conditions eingefügt werden. Das conditions-Attribut nimmt die Bedingungen entgegen, die ein Feature erfüllen muss, damit die Regel angewendet wird. Näheres hierzu unter Bedingungen.

"rules": [
{
"conditions": {},
"style": {}
}
]

DIE ZUORDNUNG EINER REGEL ZU EINEM FEATURE ERFOLGT DAMIT ÜBER EINE OR-VERKNÜPFUNG, INDEM DAS ARRAY VON OBEN NACH UNTEN (VON INDEX = 0 BIS INDEX = MAX) DURCHLAUFEN WIRD, WOBEI INNERHALB JEDER REGEL EINE AND-VERKNÜPFUNG VON PROPERTIES UND SEQUENCE (IN DEN CONDITIONS) BESTEHT.

ES WIRD DIE ERSTE REGEL ZUR ANWENDUNG GEBRACHT, DEREN CONDITIONS VOLLSTÄNDIG AUF DAS FEATURE ZUTREFFEN.

Hinweis: Erfüllt keine Regel die conditions so wird ein leeres Style-Objekt erzeugt und dem Feature zugeordnet. Das Feature wird damit nicht gezeichnet.

Es empfiehlt sich eine Regel ohne conditions als Fallback-Lösung in folgender Form vorzusehen.

"rules": [
{
"conditions": {},
"style": {}
},
{
"style": {}
}
]

Hinweis: Die Reihenfolge der Regeln im Array rules ist maßgeblich. Der Style der ersten passenden Condition wird verwendet, alle weiteren Rules werden ignoriert. Alle Regeln hinter dem Default-Style (dem ersten ohne Condition) werden somit ignoriert. Im Beispiel oben: Ein Vertauschen der beiden dargestellten Regeln würde definieren, dass immer die Fallback-Lösung genutzt wird. Die Regel mit conditions wäre somit obsolet.

Bedingungen

Nachfolgend werden die Inhalte beschrieben, die unter conditions, wie unter Aufbau vorgestellt, gesetzt werden können. Unter conditions können zwei optionale condition types angewendet werden:

  • properties
  • sequence.
"conditions": {
"properties": {},
"sequence": []
}

properties

Das properties-Attribut steuert eine inhaltliche Prüfung jedes Features über Soll-Ist-Vergleiche der Feature-Properties. Auch innerhalb der properties gilt äquivalent zu den conditions eine AND-Verknüpfung, sodass alle key-value-Paare erfüllt sein müssen.

Die properties werden als Objekt definiert. Der Key entspricht einem Attributnamen innerhalb der Feature-Properties. Der Value entspricht dem Referenzwert.

"conditions": {
"properties": {
key: value,
key2: value2
}
}

key

Als key wird der Feature-Attributname angegeben, der innerhalb der Feature-Properties als direktes child-Element existiert.

Hinweis: Existiert der key nicht, so ist die condition nicht erfüllt.

Alternativ kann auf ein beliebig verzweigtes Attribut innerhalb der Feature-Properties verwiesen werden. Näheres hierzu unter Objektpfadverweise.

Hinweis: Objektpfade werden z.B. von Sensorlayern in die Properties übernommen, die mehrere Datastreams aufweisen.

Ein key ist damit immer vom Datentyp String.

value

Als value wird der Referenzwert angegeben, gegen den der key geprüft wird. Values können unterschiedliche Datentypen aufweisen:

DatentypBeschreibung
StringDirekter Vergleich der Textinhalte zwischen Feature-Attribut und Referenzwert.
NumberDirekter Vergleich des nummerischen Wertes zwischen Feature-Attribut und Referenzwert. Ist der Attributwert vom Typ String, so wird versucht diesen in einen nummerischen Wert zu übersetzen.
Array mit zwei ZahlenEin Array mit zwei nummerischen Werten definiert eine Prüfung gegen einen nummerischen Wertebereich. Der erste Wert des Array wird als minValue interpretiert und der zweite Wert als maxValue. Es erfolgt ein Prüfung des Feature-Attributs gegen diesen Wertebereich. Ist der Attributwert vom Typ String, so wird versucht diesen in einen nummerischen Wert zu übersetzen.
Array mit vier ZahlenEin Array mit vier nummerischen Werten definiert eine Prüfung gegen einen relativen, nummerischen Wertebereich. Der erste Wert des Array wird als minValue interpretiert und der zweite Wert als maxValue. Der Attributwert aber wird nicht absolut gegen diesen Wertebereich geprüft, sondern relativ, wobei der dritte Wert des Array relativMin und der vierte Wert des Array relativMax definiert. Der Attributwert wird zunächst in Relation zu relativMin und relativMax gebracht und das Ergebnis gegen den Wertebereich von minValue und maxValue geprüft. Ist der Attributwert vom Typ String, so wird versucht diesen in einen nummerischen Wert zu übersetzen.

Hinweis: Die Prüfungswert gegen einen relativen, nummerischen Wertebereich erfolgt über $$x=1/(relativMax-relativMin)(x-relativMin)$$.

Hinweis: Jeder Vergleich gegen einen nummerischen Wertebereich erfolgt über $$minValue <= x<maxValue$$.

Alternativ kann auch als value für jeden der oben genannten Datentypen auf ein beliebig verzweigtes Attribut innerhalb der Feature-Properties verwiesen werden. Näheres hierzu unter Objektpfadverweise.

Beispiel

Hier ist eine beispielhafte Konfiguration von Properties. Wir gehen davon aus, dass das Feature enstsprechende Informationen zu name, typ, anzahlBetten, anzahlPersonal, anzahlOperationen, hamburgGesamt.OperationenSollMin und hamburgGesamt.OperationenSollMax bereitstellt.

"conditions": {
"properties": {
"name": "Kinderkrankenhaus Wilhelmsstift",
"typ": 1,
"anzahlBetten": [50, 100],
"anzahlPersonal": [25, 50, 100, 500],
"anzahlOperationen": [0, 50, "@hamburgGesamt.OperationenSollMin", "@hamburgGesamt.OperationenSollMax"]
}
}

sequence

Das sequence-Attribut steuert eine indexielle Prüfung für MultiGeometry-Features. Sie ist daher nur relevant für Features vom Typ:

  • MultiPoint,
  • MultiLinestring,
  • MultiPolygon,
  • GeometryCollection

Hinweis: Für alle anderen einfachen Geometrietypen erfolgt diese Prüfung nicht.

Alle MultiGeometry-Features bestehen aus einfachen Features. Innerhalb eines MultiGeomtry-Features wird über seine Feature iteriert und jedes Feature wird individuell gestylt. Über sequence kann der Indexbereich der Features innerhalb des MultiGeomtry-Features definiert werden, für die diese condition greift. Der Wertebereich wird in einem Array mit zwei nummerischen Werten festgelegt, wobei der erste Wert den unteren Index und der zweite Wert den oberen Index vorgibt. Somit gilt folgender Eintrag für das zweite und dritte Feature innerhalb eines Multigeometry-Feature, das aus mindestens drei Features besteht.

Hinweis: Array sind null-basiert. Das erste Feature innerhalb der Multi-Geometry hat den index 0.

"sequence": [1, 2]

Hinweis: Sequence ist auch für MultiGeometry-Features optional.

Objektpfadverweise

Wie beschrieben, können Objektpfadverweise unter den properties sowohl für key wie auch für value gesetzt werden.

**JEDER STRING MIT EINEM PREFIX @ WIRD ALS EIN OBJEKTPFADVERWEIS ERKANNT. **

Der Verweis über einen Objektpfad ist z.B. dann sinnvoll, wenn unterhalb der Feature-Properties tiefere Objekt-Strukturen liegen, in denen auf einen tiefer liegenden Wert verwiesen werden soll.

Hinweis: Objektpfade werden insbesondere bei Sensorlayern genutzt, da sie tlw. mehrere Datastreams anzapfen.

Ein Objektpfad von "@Datastreams.0.ObservedProperty.name" wertet bspw. folgenden Eintrag aus und gibt "myName" zurück:

"featureProperties": {
"name": "Kinderkrankenhaus Wilhelmsstift",
"Datastreams": [
{
"ObservedProperty": {
"name": "myName"
}
}
]
}

Hinweis: Objektpfade können sowohl Objekte als auch Array beinhalten. Der Eintrag Datastreams.0 gibt an, dass dem ersten Eintrag im Array gefolgt werden soll. Hinweis: Arrays sind null-basiert. Der erste Eintrag im Array wird über Datastreams.0 erreicht. Der zweite entsprechend über Datastreams.1.

Neben diesem Anwendungsszenario machen Objektpfadverweise auch für direkte child-Elemente Sinn. Z.B. können Features mit direkten child Properties, wie diesem:

"featureProperties": {
"name": "Kinderkrankenhaus Wilhelmsstift",
"alternativName": "Wartestube"
}

gegen sich selbst geprüft werden.

"conditions": {
"properties": {
"name": "@alternativName"
}
}

Abbildungsvorschriften

Nachfolgende werden die Inhalte beschrieben, die unter style, wie unter Aufbau vorgestellt, gesetzt werden können.

Das Styling sind vom Geometrietyp des Features abhängig. Alle MultiGeometry-Features bestehen aus einfachen Features. Innerhalb eines MultiGeomtry-Features wird über seine Feature iteriert und jedes Feature wird individuell gestylt. Folgende Geometrietypen können bislang gestylt werden:

Hinweis: Es ist nicht möglich, MultiGeometrien zu stylen, die innerhalb einer GeometryCollection (double nested) definiert sind.

Das Styling erfolgt auf Grundlage des jeweiligen Geometrietyps des Features, indem für jeden Typ default-Abbildungsvorschriften angewandt werden, die über die Einträge in style übersteuert werden können.

Beispiel eines style:

"style": {
"imageName": "krankenhaus.png",
"clusterImageName": "krankenhaus.png"
}

Es ist somit möglich, in einem style gleichzeitig unterschiedliche Geometrietypen (Point, Linestring, Polygon, ...) zu stylen, indem deren Abbildungsvorschriften ergänzt werden.

Darüber hinaus kann für alle genannten Geometriearten eine Textbeschriftung vorgenommen werden. Siehe hierzu Text.

Eine individuelle Legendenbeschriftung kann gesetzt werden. Siehe hierzu Legende.

Point

Die Abbildungsvorschriften für Punkte unterscheiden sich in

  • einfache Punktgeometrien: Siehe nachfolgenden Parameter type.
  • geclusterte Punkthaufen: Wird in der Layerconfiguration (config.json) eine clusterDistance gesetzt, so wird ein ClusterStyle erzeugt. Siehe nachfolgenden Parameter clusterType.
NameVerpflichtendTypDefaultBeschreibung
typeString"circle"Art des Stylings von Punkten gemäß Wertebereich: icon, circle, nominal, interval.
clusterTypeString"circle"Art des Stylings von geclusterten Punkten gemäß Wertebereich: icon, circle.
Nachfolgend werden die möglichen styling Optionen aufgelistet.

Point.Icon

Für weitere Informationen siehe auch die Openlayers Beschreibung.

NameVerpflichtendTypDefaultBeschreibung
imageNameString"blank.png"Name des Images.
imageWidthString1Breite des Images.
imageHeightString1Höhe des Images.
imageScaleString1Skalierung des Bildes.
imageOffsetXFloat0.5Offset des Bildes in X-Richtung.
imageOffsetYFloat0.5Offset des Bildes in Y-Richtung.
imageOffsetXUnitString"fraction"Units in which the anchor x value is specified.
imageOffsetYUnitString"fraction"Units in which the anchor y value is specified.

Point.Circle

Für weitere Informationen siehe auch die Openlayers Beschreibung.

NameVerpflichtendTypDefaultBeschreibung
circleRadiusInteger10Radius des Kreises.
circleStrokeColorInteger [][0, 0, 0, 1]Farbe des Kreisrandes in rgba.
circleStrokeWidthInteger2Breite des Kreisrandes.
circleFillColorInteger[][0, 153, 255, 1]Farbe der Kreisfüllung in rgba.

Point.Nominal

Für jedes Feature wird ein dynamischer Style gesetzt. Dieser Style unterstützt die automatisierte Aktualisierung von Sensor-Features. Für Daten die sich nicht in eine Reihenfolge bringen lassen (z.B. Farben oder Formen).

NameVerpflichtendTypDefaultBeschreibung
scalingShapexStringAngabe der Darstellungsart: CIRCLESEGMENTS.
scalingAttributexStringAttribut das zur Darstellung verwendet werden soll
circleBarScalingFactorFloat1Faktor um den Attributwert zu überhöhen. Notwendig bei sehr großen (positiven oder negativen Werten) und bei Werten nahe 0.
circleBarRadiusFloat6Radius des Punktes.
circleBarLineStrokeFloat5Breite des Balkens.
circleBarCircleFillColorInteger[][0, 0, 0, 1]Füllfarbe des Punktes in rgba.
circleBarCircleStrokeColorInteger[][0, 0, 0, 1]Farbe des Kreisrandes in rgba.
circleBarCircleStrokeWidth1Breite des Kreisrandes
circleBarLineStrokeColorInteger[][0, 0, 0, 1]Farbe des Balkens in rgba.

Point.Interval

Für jedes Feature wird ein dynamischer Style gesetzt. Dieser Style unterstützt die automatisierte Aktualisierung von Sensor-Features. Für Zahlendaten die eine natürliche Reihenfolge haben (z.B. in der Einheit Meter, oder Grad Celsius).

NameVerpflichtendTypDefaultBeschreibung
scalingShapexStringAngabe der Darstellungsart: CIRCLE_BAR.
scalingAttributexStringAttribut das zur Darstellung verwendet werden soll. Kann auch als Objektpfadverweis genutzt werden.
scalingValuesObject[]Attributwerte denen eine Farbe zugeordnet ist, z.B. {"charging" : [220, 0, 0, 1]}. Innerhalb des Objektes können beliebig viele Attributwerte angegeben werden.
scalingValueDefaultColorInteger[][0, 0, 0, 1]Standardfarbe für alle Attributwerte die nicht in scalingValues definiert sind.
circleSegmentsRadiusFloat10Radius der Kreissegmente
circleSegmentsStrokeWidthFloat4Breite der Kreissegmente
circleSegmentsGapFloat10Abstand zwischen den Kreissegmenten
circleSegmentsBackgroundColorInteger[][255, 255, 255, 0]Farbe des Kreises

Point.Cluster

Geclusterte Punkte stellen ein Symbol für mehrere Features dar. Siehe auch die Openlayers Beschreibung. Die Darstellung ist abhängig von Anzahl und Lage der Features, der gewählten Zoomstufe und der am Layer definierten clusterDistance. PointCluster können auf zwei Arten dargestellt werden:

Es ist ebenso möglich, geclusterte Punkte mit einem Textattribut zu belegen. Dies ist z.B. häufig gewünscht, um die Anzahl der geclusterten Features darzustellen. Siehe hierzu ClusterText.

Point.Cluster.Icon

Für weitere Informationen siehe auch die Openlayers Beschreibung.

NameVerpflichtendTypDefaultBeschreibung
clusterImageNameString"blank.png"Name des Images als Clusterstyle.
clusterImageWidthInteger1Breite des Images als Clusterstyle.
clusterImageHeightInteger1Höhe des Images als Clusterstyle.
clusterImageScaleInteger1Skalierung des Images als Clusterstyle.
clusterImageOffsetXFloat0.5Offset des Images als Clusterstyle in X-Richtung.
clusterImageOffsetYFloat0.5Offset des Images als Clusterstyle in Y-Richtung.

Point.Cluster.Circle

Für weitere Informationen siehe auch die Openlayers Beschreibung.

NameVerpflichtendTypDefaultBeschreibung
clusterCircleRadiusInteger10Radius des Kreises als Clusterstyle.
clusterCircleFillColorInteger[][0, 153, 255, 1]Füllfarbe des Kreises als Clusterstyle in rgba.
clusterCircleStrokeColorInteger[][0, 0, 0, 1]Randfarbe des Kreises als Clusterstyle in rgba.
clusterCircleStrokeWidthInteger2Randstärke des Kreises als Clusterstyle.

Point.Cluster.Text

Für weitere Informationen siehe auch die Openlayers Beschreibung.

Es gibt zwei Arten clusterTexte darzustellen. Sie werden im Attribut clusterTextType gesetzt:

  • counter : Darstellung der Anzahl der geclusterten Features.
  • text : Darstellung eines festen Textes.
  • none : Mit dem Wert none wird die Darstellung unterdrückt.
NameVerpflichtendTypDefaultBeschreibung
clusterTextTypeString"counter"Beschriftungsart gemäß Wertebereich: counter, none, text.
clusterTextnur bei clusterTextType: textString"undefined"Darzustellender Text.
clusterTextAlignString"center"Ausrichtung des Textes am Feature.
clusterTextFontString"Comic Sans MS"Font des Textes am Feature.
clusterTextScaleInteger2Skalierung des Textes.
clusterTextOffsetXInteger10Offset des Textes in X-Richtung.
clusterTextOffsetYInteger-8Offset des Textes in Y-Richtung.
clusterTextFillColorInteger[][255, 255, 255, 1]Füllfarbe des Textes in rgba.
clusterTextStrokeColorInteger[][0, 0, 0, 0]Randfarbe des Textes in rgba.
clusterTextStrokeWidthInteger3Breite der Textstriche.

Hinweis: Eine Cluster-Beschriftung ist gegenüber einer allgemeinen Beschriftung höher priorisiert.

Linestring

Für weitere Informationen siehe auch die Openlayers Stroke Beschreibung.

NameVerpflichtendTypDefaultBeschreibung
lineStrokeColorInteger[][255, 0, 0, 1]Farbe der Linie in rgba.
lineStrokeWidthInteger5Breite der Linie.
lineStrokeCapString"round"Line cap style
lineStrokeJoinString"round"Line join style
lineStrokeDashInteger[]nullStyle der Linie mit dash
lineStrokeDashOffsetInteger0Line dash offset
lineStrokeMiterLimitInteger10Miter limit

Polygon

Für weitere Informationen siehe auch die Openlayers Fill Beschreibung und diese Openlayers Stroke Beschreibung.

NameVerpflichtendTypDefaultBeschreibung
polygonStrokeColorInteger[][0, 0, 0, 1]Farbe der Linie in rgba.
polygonStrokeWidthInteger1Breite der Linie.
polygonStrokeCapString"round"Line cap style
polygonStrokeJoinString"round"Line join style
polygonStrokeDashInteger[]nullStyle der Linie mit dash
polygonStrokeDashOffsetInteger0Line dash offset
polygonStrokeMiterLimitInteger10Miter limit
polygonFillColorInteger[][10, 200, 100, 0.5]Füllfarbe in rgba.

Text

Für weitere Informationen siehe auch die Openlayers Beschreibung.

Über das Attribut labelfield kann im style gesteuert werden, ob eine Featurebeschriftung vorgenommen werden soll. Es wird der Text ausgegeben und gestylt, der in den Feature-Properties unter dem genannten labelField gefunden wird.

Hinweis: Fehlt das Attribut labelfield wird keine Beschriftung vorgenommen.

NameVerpflichtendTypDefaultBeschreibung
labelFieldjaString"undefined"Attribut des Features, nach dessen Wert das Label angezeigt werden soll. Kann auch als Objektpfadverweis genutzt werden.
textAlignString"center"Ausrichtung des Textes am Feature.
textFontString"Comic Sans MS"Font des Textes am Feature.
textScaleInteger2Skalierung des Textes.
textOffsetXInteger10Offset des Textes in X-Richtung.
textOffsetYInteger-8Offset des Textes in Y-Richtung.
textFillColorInteger[][69, 96, 166, 1]Füllfarbe des Textes in rgba.
textStrokeColorInteger[][240, 240, 240, 1]Randfarbe des Textes in rgba.
textStrokeWidthInteger3Breite der Textstriche.
textSuffixneinString'""'Suffix das hinter den Text gehängt wird.

Hinweis: Eine Cluster-Beschriftung ist gegenüber dieser Beschriftung höher priorisiert.

Legende

Die textliche Beschreibung in der Legende kann gesteuert werden. Dies geschieht über den Parameter legendValue.

"style": {
"legendValue": "mein Text"
}

Hinweis: Das Attribut legendValue muss pro Layer und Geometrietyp unique sein, da die Legende ansonsten unvollständig ist.

Beispiel

Nachfolgend eine Beispielkonfiguration für einen Sensorlayer:

[
{
"styleId": "1711",
"rules": [
{
"conditions": {
"properties": {
"@Datastreams.1.Observations.0.result": [1, 3]
}
},
"style": {
"type": "circle",
"circleFillColor": [255, 0, 0, 1],
"clusterType": "circle"
}
},
{
"conditions": {
"properties": {
"@Datastreams.1.Observations.0.result": [3, 8]
}
},
"style": {
"type": "circle",
"circleFillColor": [255, 255, 102, 1],
"clusterType": "circle"
}
},
{
"conditions": {
"properties": {
"@Datastreams.1.Observations.0.result": [8, 50]
}
},
"style": {
"type": "circle",
"circleFillColor": [132, 222, 2, 1],
"clusterType": "circle"
}
},
{
"conditions": {
"properties": {
"@Datastreams.1.Observations.0.result": "no data"
}
},
"style": {
"type": "circle",
"circleFillColor": [200, 200, 1, 1],
"clusterType": "circle"
}
},
{
"style": {
"type": "circle",
"circleFillColor": [211, 211, 211, 1],
"clusterType": "circle"
}
}
]
}
]