directives.rst 60 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489
  1. .. _template-directives:
  2. Template Directives
  3. ===================
  4. This part of the documentation is an introduction explaining the different template directives.
  5. Examples will be provided for both Simple Features and Complex Features.
  6. The syntax of the directives varies slightly between XML based templates and JSON based templates.
  7. The examples will be provided mainly for GeoJSON and GML. However the syntax defined for GeoJSON output, unless otherwise specified, is valid for JSON-LD templates
  8. Template directive summary
  9. --------------------------
  10. The following constitutes a summary of all the template directives and it is meant to be used for quick reference. Each directive is explained in detail in the sections below.
  11. JSON based templates
  12. ^^^^^^^^^^^^^^^^^^^^
  13. The following are the directives available in JSON based templates.
  14. .. list-table::
  15. :widths: 30 10 60
  16. * - **Usage**
  17. - **Syntax**
  18. - **Description**
  19. * - property interpolation
  20. - ${property}
  21. - specify it as an attribute value (:code:`"json_attribute":"${property}"`)
  22. * - cql evaluation
  23. - $${cql}
  24. - specify it as an element value (:code:`"json_attribute":"$${cql}"`)
  25. * - setting the evaluation context for child attributes.
  26. - ${source}.
  27. - specify it as the first nested object in arrays (:code:`{"$source":"property"}`) or as an attribute in objects (:code:`"$source":"property"`)
  28. * - filter the array, object, attribute
  29. - $filter
  30. - specify it inside the first nested object in arrays (:code:`{"$filter":"condition"}`) or as an attribute in objects (:code:`"$filter":"condition"`) or in an attribute next to the attribute value separated by a :code:`,` (:code:`"attribute":"$filter{condition}, ${property}"`)
  31. * - defines options to customize the output outside of a feature scope
  32. - $options
  33. - specify it at the top of the JSON template as a JSON object (GeoJSON options: :code:`"$options":{"flat_output":true, "separator":"."}`; JSON-LD options: :code:`"$options":{"@context": "the context json", "encode_as_string": true, "@type":"schema:SpecialAnnouncement", "collection_name":"customCollectionName"}`).
  34. * - allows including a template into another
  35. - $include, $includeFlat
  36. - specify the :code:`$include` option as an attribute value (:code:`"attribute":"$include{subProperty.json}"`) and the :code:`$includeFlat` as an attribute name with the included template path as a value (:code:`"$includeFlat":"included.json"`)
  37. * - allows a template to extend another template
  38. - $merge
  39. - specify the :code:`$merge` directive as an attribute name containing the path to the extended template (:code: `"$merged":"base_template.json"`).
  40. * - allows null values to be encoded. default is not encoded.
  41. - ${property}! or $${expression}!
  42. - ! at the end of a property interpolation or cql directive (:code:`"attribute":"${property}!"` or :code:`"attribute":"$${expression}!"`).
  43. XML based templates
  44. ^^^^^^^^^^^^^^^^^^^^
  45. The following are the directives available in XML based templates.
  46. .. list-table::
  47. :widths: 30 10 60
  48. * - **Usage**
  49. - **Syntax**
  50. - **Description**
  51. * - property interpolation
  52. - ${property}
  53. - specify it either as an element value (:code:`<element>${property}</element>`) or as an xml attribute value (:code:`<element attribute:"${property}"/>`)
  54. * - cql evaluation
  55. - $${cql}
  56. - specify them either as an element value (:code:`<element>$${cql}</element>`) or as an xml attribute value (:code:`<element attribute:"$${cql}"/>`)
  57. * - setting the evaluation context for property interpolation and cql evaluation in child elements.
  58. - gft:source
  59. - specify it as an xml attribute (:code:`<element gft:source:"property">`)
  60. * - filter the element to which is applied based on the defined condition
  61. - gft:filter
  62. - specify it as an XML attribute on the element to be filtered (:code:`<element gft:filter:"condition">`)
  63. * - marks the beginning of an XML template.
  64. - gft:Template
  65. - It has to be the root element of an XML template (:code:`<gft:Template> Template content</gft:Template>`)
  66. * - defines options to customize the output outside of a feature scope
  67. - gft:Options
  68. - specify it as an element at the beginning of the xml document after the :code:`<gft:Template>` one (:code:`<gft:Options></gft:Options>`). GML options: :code:`<gtf:Namespaces>`,:code:`<gtf:SchemaLocation>`. HTML options: :code:`<script>`, :code: `<script type="application/ld+json"/>`, :code:`<style>`, :code: `<link>`.
  69. * - allows including a template into another
  70. - $include, gft:includeFlat
  71. - specify the :code:`$include` option as an element value (:code:`<element>$include{included.xml}</element>`) and the :code:`gft:includeFlat` as an element having the included template as text content (:code:`<gft:includeFlat>included.xml</gft:includeFlat>`)
  72. * - allows null values to be encoded. default is not encoded.
  73. - ${property}!
  74. - specify it either as an element value (:code:`<element>${property}!</element>`) or as an xml attribute value (:code:`<element attribute:"${property}!"/>`)
  75. A step by step introduction to features-templating syntax
  76. ---------------------------------------------------------
  77. This introduction is meant to illustrate the different directives that can be used in a template.
  78. For clarity the documentation will start with a ``Simple Feature`` example and then progress through a ``Complex Feature`` example. However all the directives that will be shown are available for both Simple and Complex Features. ``GeoJSON`` and ``GML`` examples will be used mostly. For ``JSON-LD`` output format the rules to define a template are the same as the ``GeoJSON`` template with two exceptions:
  79. * A ``@context`` needs to be specified (see the ``options`` section below).
  80. * The standard mandates that attributes' values are all strings.
  81. ${property} and $${cql} directive (Simple Feature example)
  82. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  83. GeoJSON
  84. """""""
  85. Assume that we want to change the default geojson output of the :code:`topp:states` layer. A single feature in the default output is like the following:
  86. .. code-block:: json
  87. {
  88. "type": "Feature",
  89. "id": "states.1",
  90. "geometry": {},
  91. "geometry_name": "the_geom",
  92. "properties": {
  93. "STATE_NAME": "Illinois",
  94. "STATE_FIPS": "17",
  95. "SUB_REGION": "E N Cen",
  96. "STATE_ABBR": "IL",
  97. "LAND_KM": 143986.61,
  98. "WATER_KM": 1993.335,
  99. "PERSONS": 11430602,
  100. "FAMILIES": 2924880,
  101. "HOUSHOLD": 4202240,
  102. "MALE": 5552233,
  103. "FEMALE": 5878369,
  104. "WORKERS": 4199206,
  105. "DRVALONE": 3741715,
  106. "CARPOOL": 652603,
  107. "PUBTRANS": 538071,
  108. "EMPLOYED": 5417967,
  109. "UNEMPLOY": 385040,
  110. "SERVICE": 1360159,
  111. "MANUAL": 828906,
  112. "P_MALE": 0.486,
  113. "P_FEMALE": 0.514,
  114. "SAMP_POP": 1747776
  115. }
  116. }
  117. In particular we want to include in the final output only certain properties (e.g. the geometry, the state name, the code, values about population, male, female and workers). We want also to change some attribute names and to have them lower cased. Finally we want to have a string field having a wkt representation of the geometry. The desired output is like the following:
  118. .. code-block:: json
  119. {
  120. "type":"Feature",
  121. "id":"states.1",
  122. "geometry":{
  123. "type":"MultiPolygon",
  124. "coordinates":"[....]"
  125. },
  126. "properties":{
  127. "name":"Illinois",
  128. "region":"E N Cen",
  129. "code":"IL",
  130. "population_data":{
  131. "population":114306027,
  132. "males":5552233.0,
  133. "females":5878369.0,
  134. "active_population":4199206.0
  135. },
  136. "wkt_geom":"MULTIPOLYGON (((37.51099000000001 -88.071564, [...])))"
  137. }
  138. }
  139. A template like this will allows us to produce the above output:
  140. .. code-block:: json
  141. {
  142. "type": "Feature",
  143. "id": "${@id}",
  144. "geometry": "${the_geom}",
  145. "properties": {
  146. "name": "${STATE_NAME}",
  147. "region": "${SUB_REGION}",
  148. "code": "${STATE_ABBR}",
  149. "population_data":{
  150. "population": "${PERSONS}",
  151. "males": "${MALE}",
  152. "females": "${FEMALE}",
  153. "active_population": "${WORKERS}"
  154. },
  155. "wkt_geom":"$${toWKT(the_geom)}"
  156. }
  157. }
  158. As it is possible to see the new output has the attribute names defined in the template. Moreover the :code:`population` related attributes have been placed inside a nested json object. Finally a wkt_geom attribute with the WKT geometry representation has been added.
  159. GML
  160. """
  161. The same template mechanism can be applied to a GML output format. This is an example GML template, again for the :code:`topp:states` layer
  162. .. code-block:: xml
  163. <gft:Template>
  164. <gft:Options>
  165. <gft:Namespaces xmlns:topp="http://www.openplans.org/topp"/>
  166. <gft:SchemaLocation xsi:schemaLocation="http://www.opengis.net/wfs/2.0 http://brgm-dev.geo-solutions.it/geoserver/schemas/wfs/2.0/wfs.xsd http://www.opengis.net/gml/3.2 http://schemas.opengis.net/gml/3.2.1/gml.xsd"/>
  167. </gft:Options>
  168. <topp:states gml:id="${@id}">
  169. <topp:name code="${STATE_ABBR}">${STATE_NAME}</topp:name>
  170. <topp:region>${SUB_REGION}</topp:region>
  171. <topp:population>${PERSONS}</topp:population>
  172. <topp:males>${MALE}</topp:males>
  173. <topp:females>${FEMALE}</topp:females>
  174. <topp:active_population>${WORKERS}</topp:active_population>
  175. <topp:wkt_geom>$${toWKT(the_geom)}</topp:wkt_geom>
  176. </topp:states>
  177. </gft:Template>
  178. And this is how a feature will appear:
  179. .. code-block:: xml
  180. <topp:states gml:id="states.10">
  181. <topp:name code="MO">Missouri</topp:name>
  182. <topp:region>W N Cen</topp:region>
  183. <topp:population>5117073.0</topp:population>
  184. <topp:males>2464315.0</topp:males>
  185. <topp:females>2652758.0</topp:females>
  186. <topp:active_population>1861192.0</topp:active_population>
  187. <topp:wkt_geom>MULTIPOLYGON (([....])))</topp:wkt_geom>
  188. </topp:states>
  189. As it is possible to see the geometry is being encoded only as a wkt, moreover the STATE_ATTR value is now present as an xml attribute of the element :code:`topp:states`. Finally elements that were not defined in the template did not show up.
  190. Looking at these examples it is possible to see additional directives that can customize the output:
  191. * Property interpolation can be invoked using the directive :code:`${property_name}`.
  192. * In case complex operation are needed a CQL expression can be used thought a :code:`$${cql}` syntax (all CQL functions are supported).
  193. * Simple text values are reproduced in the final output as they are.
  194. * Finally the GML template needs the actual template content to be wrapped into a :code:`gft:Template` element. The :code:`gft` doesn't needs to be bound to a namespace. It is used just as marker of a features-templating related element and will not be present in the final output.
  195. * There is also another element, the :code:`gft:Options`, with two more elements inside. It will be explained in a later dedicated section.
  196. Source and filter (Complex Feature example)
  197. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  198. GeoJSON
  199. """""""
  200. Let's assume now that an AppSchema layer has been configured and customization of the complex features output is needed.
  201. The Meteo Stations use case will be used as an example. For a description of the use case check the documentation at :ref:`community_smart_data_loader`.
  202. This is the domain model of the use case:
  203. .. figure:: images/meteos-stations-er-diagram.png
  204. The default GeoJSON output format produces features like the following:
  205. .. code-block:: json
  206. {
  207. "type":"Feature",
  208. "id":"MeteoStationsFeature.7",
  209. "geometry":{
  210. },
  211. "properties":{
  212. "@featureType":"MeteoStations",
  213. "id":7,
  214. "code":"BOL",
  215. "common_name":"Bologna",
  216. "meteoObservations":[
  217. {
  218. "id":3,
  219. "time":"2016-12-19T11:28:31Z",
  220. "value":35,
  221. "meteoParameters":[
  222. {
  223. "id":1,
  224. "param_name":"temperature",
  225. "param_unit":"C"
  226. }
  227. ]
  228. },
  229. {
  230. "id":4,
  231. "time":"2016-12-19T11:28:55Z",
  232. "value":25,
  233. "meteoParameters":[
  234. {
  235. "id":1,
  236. "param_name":"temperature",
  237. "param_unit":"C"
  238. }
  239. ]
  240. },
  241. {
  242. "id":5,
  243. "time":"2016-12-19T11:29:24Z",
  244. "value":80,
  245. "meteoParameters":[
  246. {
  247. "id":2,
  248. "param_name":"wind speed",
  249. "param_unit":"Km/h"
  250. }
  251. ]
  252. },
  253. {
  254. "id":6,
  255. "time":"2016-12-19T11:30:26Z",
  256. "value":1019,
  257. "meteoParameters":[
  258. {
  259. "id":3,
  260. "param_name":"pressure",
  261. "param_unit":"hPa"
  262. }
  263. ]
  264. },
  265. {
  266. "id":7,
  267. "time":"2016-12-19T11:30:51Z",
  268. "value":1015,
  269. "meteoParameters":[
  270. {
  271. "id":3,
  272. "param_name":"pressure",
  273. "param_unit":"hPa"
  274. }
  275. ]
  276. }
  277. ]
  278. }
  279. }
  280. The above JSON has a data structure where:
  281. * Station object has a nested array of Observations.
  282. * Each Observation has a an array of parameter that describe the type of Observation.
  283. Now let's assume that a different output needs to be produced where instead of having a generic array of observation nested into the root object, arrays are provided separately for each type of parameter e.g. Temperatures, Pressures and Winds_speed observations. In other words instead of having the Observation type defined inside a nested Parameter object that information should be provided directly in the attribute name.
  284. The desired output looks like the following:
  285. .. code-block:: json
  286. {
  287. "type":"FeatureCollection",
  288. "features":[
  289. {
  290. "Identifier":"MeteoStationsFeature.7",
  291. "geometry":{
  292. "type":"Point",
  293. "coordinates":[
  294. 44.5,
  295. 11.34
  296. ]
  297. },
  298. "properties":{
  299. "Name":"Bologna",
  300. "Code":"STATION-BOL",
  301. "Location":"POINT (44.5 11.34)",
  302. "Temperatures":[
  303. {
  304. "Timestamp":"2016-12-19T11:28:31.000+00:00",
  305. "Value":35.0
  306. },
  307. {
  308. "Timestamp":"2016-12-19T11:28:55.000+00:00",
  309. "Value":25.0
  310. }
  311. ],
  312. "Pressures":[
  313. {
  314. "Timestamp":"2016-12-19T11:30:26.000+00:00",
  315. "Value":1019.0
  316. },
  317. {
  318. "Timestamp":"2016-12-19T11:30:51.000+00:00",
  319. "Value":1015.0
  320. }
  321. ],
  322. "Winds_speed":[
  323. {
  324. "Timestamp":"2016-12-19T11:29:24.000+00:00",
  325. "Value":80.0
  326. }
  327. ]
  328. }
  329. }
  330. ],
  331. "totalFeatures":3,
  332. "numberMatched":3,
  333. "numberReturned":1,
  334. "timeStamp":"2021-07-13T14:00:19.457Z",
  335. "crs":{
  336. "type":"name",
  337. "properties":{
  338. "name":"urn:ogc:def:crs:EPSG::4326"
  339. }
  340. }
  341. }
  342. A template like this will allow to produce such an output:
  343. .. code-block:: json
  344. {
  345. "$source":"st:MeteoStationsFeature",
  346. "Identifier":"${@id}",
  347. "geometry":"${st:position}",
  348. "properties":{
  349. "Name":"${st:common_name}",
  350. "Code":"$${strConcat('STATION-', xpath('st:code'))}",
  351. "Location":"$${toWKT(xpath('st:position'))}",
  352. "Temperatures":[
  353. {
  354. "$source":"st:meteoObservations/st:MeteoObservationsFeature",
  355. "$filter":"xpath('st:meteoParameters/st:MeteoParametersFeature/st:param_name') = 'temperature'"
  356. },
  357. {
  358. "Timestamp": "${st:time}",
  359. "Value": "${st:value}"
  360. }
  361. ],
  362. "Pressures":[
  363. {
  364. "$source":"st:meteoObservations/st:MeteoObservationsFeature",
  365. "$filter":"xpath('st:meteoParameters/st:MeteoParametersFeature/st:param_name') = 'pressure'"
  366. },
  367. {
  368. "Timestamp": "${st:time}",
  369. "Value": "${st:value}"
  370. }
  371. ],
  372. "Winds_speed":[
  373. {
  374. "$source":"st:meteoObservations/st:MeteoObservationsFeature",
  375. "$filter":"xpath('st:meteoParameters/st:MeteoParametersFeature/st:param_name') = 'wind speed'"
  376. },
  377. {
  378. "Timestamp": "${st:time}",
  379. "Value": "${st:value}"
  380. }
  381. ]
  382. }
  383. }
  384. In addition to the :code:`${property}` and :code:`$${cql}` directives seen before, there are two more:
  385. * In the example above the :code:`xpath('xpath')` function is used to reference property. When dealing with Complex Features it must be used when referencing properties inside a :code:`$filter` or a :code:`$${cql}` directive.
  386. * :code:`$source` which is meant to provide the context against which evaluated nested element properties and xpaths. In this case the :code:`"$source":"st:meteoObservations/st:MeteoObservationsFeature"` provides the context for the nested attributes angainst which the directives will be evaluated. When defining a :code:`$source` for a JSON array it should be provided in a JSONObject separated from the JSON Object mapping the nested feature attributes as in the example above. When defining the :code:`$source` for a JSONObject it can be simply added as an object attribute (see below examples).
  387. * When using :code:`${property}` directive or an :code:`xpath('xpath')` function it is possible to reference a property bounded to an upper :code:`$source` using a ``../`` notation eg. ``${../previousContextValue}``.
  388. * :code:`$filter` provides the possibility to filter the value that will be included in the element to which is applied, in this case a json array. For instance the filter :code:`$filter":"xpath('st:meteoParameters/st:MeteoParametersFeature/st:param_name') = 'wind speed'` in the :code:`Winds_speed` array allows filtering the element that will be included in this array according to the :code:`param_name value`.
  389. One note aboute the Source. It is strictly needed only when referencing a nested feature. This means that in the GeoJSON template example the :code:`"$source":"st:MeteoStationsFeature"` could have been omitted. This not apply for nested elements definition where the :code:`"$source":"st:meteoObservations/st:MeteoObservationsFeature"` is mandatory.
  390. Follows a list of JSON template bits showing :code:`filters` definition in context different from a JSON array, as well as :code:`$source` definition for a JSONObject.
  391. * Object (encode the JSON object only if the st:value is greater than 75.3).
  392. .. code-block:: json
  393. {
  394. "Observation":
  395. {
  396. "$source":"st:MeteoObservationsFeature",
  397. "$filter":"st:value > 75.3 ",
  398. "Timestamp":"${st:time}",
  399. "Value":"${st:value}"
  400. }
  401. }
  402. * Attribute (encode the Timestamp attribute only if the st:value is greater than 75.3).
  403. .. code-block:: json
  404. {
  405. "Observation":
  406. {
  407. "$source":"st:MeteoObservationsFeature",
  408. "Timestamp":"$filter{st:value > 75.3}, ${st:time}",
  409. "Value":"${st:value}"
  410. }
  411. }
  412. * Static attribute (encode the Static_value attribute only if the st:value is greater than 75.3).
  413. .. code-block:: json
  414. {
  415. "Observation":
  416. {
  417. "$source":"st:MeteoObservationsFeature",
  418. "Timestamp":"${st:time}",
  419. "Static_value":"$filter{st:value > 75.3}, this Observation has a value > 75.3",
  420. "Value":"${st:value}"
  421. }
  422. }
  423. As it is possible to see from the previous example in the array and object cases the filter syntax expected a :code:`"$filter"` key followed by an attribute with the filter to evaluate. In the attribute case, instead, the filter is being specified inside the value as :code:`"$filter{...}"`, followed by the CQL expression, or by the static content, with a comma separating the two.
  424. GML
  425. """
  426. :code:`filter` and :code:`source` are available as well in GML templates. Assuming that the desired output is the corresponding GML equivalent of the GeoJSON output above e.g.:
  427. .. code-block:: xml
  428. <?xml version="1.0" encoding="UTF-8"?>
  429. <wfs:FeatureCollection xmlns:st="http://www.stations.org/1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:wfs="http://www.opengis.net/wfs/2.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:gml="http://www.opengis.net/gml/3.2" numberMatched="3" numberReturned="0" timeStamp="2021-07-13T15:09:28.620Z">
  430. <wfs:member>
  431. <st:MeteoStations gml:id="MeteoStationsFeature.7">
  432. <st:code>Station_BOL</st:code>
  433. <st:name>Bologna</st:name>
  434. <st:geometry>
  435. <gml:Point srsName="urn:ogc:def:crs:EPSG::4326" srsDimension="2" gml:id="smdl-stations.1.geom">
  436. <gml:pos>11.34 44.5</gml:pos>
  437. </gml:Point>
  438. </st:geometry>
  439. <st:temperature>
  440. <st:temperature>
  441. <st:Temperature>
  442. <st:time>2016-12-19T11:28:31.000Z</st:time>
  443. <st:value>35.0</st:value>
  444. </st:Temperature>
  445. </st:temperature>
  446. <st:temperature>
  447. <st:Temperature>
  448. <st:time>2016-12-19T11:28:55.000Z</st:time>
  449. <st:value>25.0</st:value>
  450. </st:Temperature>
  451. </st:temperature>
  452. </st:temperature>
  453. <st:pressure>
  454. <st:pressure>
  455. <st:Pressure>
  456. <st:time>2016-12-19T11:30:26.000Z</st:time>
  457. <st:value>1019.0</st:value>
  458. </st:Pressure>
  459. </st:pressure>
  460. <st:pressure>
  461. <st:Pressure>
  462. <st:time>2016-12-19T11:30:51.000Z</st:time>
  463. <st:value>1015.0</st:value>
  464. </st:Pressure>
  465. </st:pressure>
  466. </st:pressure>
  467. <st:wind_speed>
  468. <st:wind_speed>
  469. <st:Wind_speed>
  470. <st:time>2016-12-19T11:29:24.000Z</st:time>
  471. <st:value>80.0</st:value>
  472. </st:Wind_speed>
  473. </st:wind_speed>
  474. </st:wind_speed>
  475. </st:MeteoStations>
  476. </wfs:member>
  477. </wfs:FeatureCollection>
  478. The following GML template will produce the above output:
  479. .. code-block:: xml
  480. <gft:Template>
  481. <gft:Options>
  482. <gft:Namespaces xmlns:st="http://www.stations.org/1.0"/>
  483. </gft:Options>
  484. <st:MeteoStations gml:id="${@id}">
  485. <st:code>$${strConcat('Station_',st:code)}</st:code>
  486. <st:name>${st:common_name}</st:name>
  487. <st:geometry>${st:position}</st:geometry>
  488. <st:temperature gft:isCollection="true" gft:source="st:meteoObservations/st:MeteoObservationsFeature" gft:filter="xpath('st:meteoParameters/st:MeteoParametersFeature/st:param_name') = 'temperature'">
  489. <st:Temperature>
  490. <st:time>${st:time}</st:time>
  491. <st:value>${st:value}</st:value>
  492. </st:Temperature>
  493. </st:temperature>
  494. <st:pressure gft:isCollection="true" gft:source="st:meteoObservations/st:MeteoObservationsFeature" gft:filter="xpath('st:meteoParameters/st:MeteoParametersFeature/st:param_name') = 'pressure'">
  495. <st:Pressure>
  496. <st:time>${st:time}</st:time>
  497. <st:value>${st:value}</st:value>
  498. </st:Pressure>
  499. </st:pressure>
  500. <st:wind_speed gft:isCollection="true" gft:source="st:meteoObservations/st:MeteoObservationsFeature" gft:filter="xpath('st:meteoParameters/st:MeteoParametersFeature/st:param_name') = 'wind speed'">
  501. <st:Wind_speed>
  502. <st:time>${st:time}</st:time>
  503. <st:value>${st:value}</st:value>
  504. </st:Wind_speed>
  505. </st:wind_speed>
  506. </st:MeteoStations>
  507. </gft:Template>
  508. In the GML case :code:`filter` and :code:`source` directives are defined in a slightly different manner from the JSON usecase.
  509. * The filter needs to be defined as an attribute :code:`gft:filter` in the element that is meant to be filtered.
  510. * The source needs to be defined as an attribute :code:`gft:source` in the element that will set the source for its child elements.
  511. * The attribute :code:`gft:isCollection="true"` defines a directive meant to be used in GML templates to mark collection elements: this directive is needed since XML doesn't have the array concept and the template mechanism needs to be informed if an element should be repeated because it represent a collection element.
  512. As for the GeoJSON case the source is not needed for the top level feature. In this case we indeed omitted it for the st:MeteoStations element. Instead, as stated above, it is mandatory for nested elements like :code:`Temperature`, :code:`Pressure` and :code:`Winds_speed`. All of them show indeed a :code:`gft:source="st:meteoObservations/st:MeteoObservationsFeature"`.
  513. More on XPath Function
  514. """""""""""""""""""""""
  515. The :code:`xpath('xpath')` function is meant to provide the possibility to reference a Feature's properties no matter how nested, in a template, providing also the possibility to reference the previous context value through :code:`../`.
  516. Check the following template from the GeoJSON Stations use case.
  517. .. code-block:: json
  518. {
  519. "$source":"st:MeteoStationsFeature",
  520. "properties":{
  521. "Code":"$${strConcat('STATION-', xpath('st:code'))}",
  522. "Location":"$${toWKT(xpath('st:position'))}",
  523. "Temperatures":[
  524. {
  525. "$source":"st:meteoObservations/st:MeteoObservationsFeature",
  526. "$filter":"xpath('st:meteoParameters/st:MeteoParametersFeature/st:param_name') = 'temperature'"
  527. },
  528. {
  529. "Value": "${st:value}",
  530. "StillCode":"$${strConcat('STATION-', xpath('../st:code'))}"
  531. }
  532. ]
  533. }
  534. In the :code:`Temperatures` array a :code:`StillCode` attribute has been defined that through :code:`../` references not the :code:`"$source":"st:meteoObservations/st:MeteoObservationsFeature"`, but the previous one :code:`"$source":"st:MeteoStationsFeature"`.
  535. The same can be achieved with the property interpolation directive if a cql function evaluation is not needed: :code:`"StillCode":"$${strConcat('STATION-', xpath('../st:code'))}"`.
  536. .. warning:: the :code:`xpath('some xpath)` cql function is meant to be used in the scope of this plugin. For general usage please refer to the :geotools:`property function <library/main/function_list.html#property-propertyname-returns-propertyvalue>`.
  537. Template Options
  538. ^^^^^^^^^^^^^^^^
  539. The directives seen so far allow control of the output in the scope of a Feature element.
  540. The :code:`options` directive, instead, allows customizing the output for part of the output outside the Feature scope or to define general modifications to the overall output. The available options vary according to the output format.
  541. GeoJSON
  542. """""""
  543. In the context of a GeoJSON template two options are available: :code:`flat_output` and :code:`separator`. These options are meant to provide a GeoJSON output encoded following INSPIRE rule for `alternative feature GeoJSON encoding <https://github.com/INSPIRE-MIF/2017.2/blob/master/GeoJSON/ads/simple-addresses.md>`_ (`see also <https://github.com/INSPIRE-MIF/2017.2/blob/master/GeoJSON/efs/simple-environmental-monitoring-facilities.md>`_).
  544. To use the functionality an :code:`"$options"` JSON object can be added on top of a JSON template, like in the following example:
  545. .. code-block:: json
  546. {
  547. "$options":{
  548. "flat_output":true,
  549. "separator": "."
  550. },
  551. "$source":"st:MeteoStationsFeature",
  552. "Identifier":"${@id}",
  553. "geometry":"${st:position}",
  554. "properties":{
  555. "Name":"${st:common_name}",
  556. "Code":"$${strConcat('STATION-', xpath('st:code'))}",
  557. "Location":"$${toWKT(xpath('st:position'))}",
  558. "Temperatures":[
  559. {
  560. "$source":"st:meteoObservations/st:MeteoObservationsFeature",
  561. "$filter":"xpath('st:meteoParameters/st:MeteoParametersFeature/st:param_name') = 'temperature'"
  562. },
  563. {
  564. "Timestamp": "${st:time}",
  565. "Value": "${st:value}"
  566. }
  567. ],
  568. "Pressures":[
  569. {
  570. "$source":"st:meteoObservations/st:MeteoObservationsFeature",
  571. "$filter":"xpath('st:meteoParameters/st:MeteoParametersFeature/st:param_name') = 'pressure'"
  572. },
  573. {
  574. "Timestamp": "${st:time}",
  575. "Value": "${st:value}"
  576. }
  577. ],
  578. "Winds_speed":[
  579. {
  580. "$source":"st:meteoObservations/st:MeteoObservationsFeature",
  581. "$filter":"xpath('st:meteoParameters/st:MeteoParametersFeature/st:param_name') = 'wind speed'"
  582. },
  583. {
  584. "Timestamp": "${st:time}",
  585. "Value": "${st:value}"
  586. }
  587. ]
  588. }
  589. }
  590. The :code:`flat_output` will act in the following way:
  591. * The encoding of nested arrays and objects will be skipped, by encoding only their attributes.
  592. * Object attribute names will be concatenated with the names of their json attributes.
  593. * Arrays' attribute names will be concatenated as well with the one of the json attributes of their inner object. In addition an index value will be added after the array's attribute name for each nested object.
  594. * The :code:`separator` specifies the separator of the attributes' names. Default is :code:`_`.
  595. * The final output will have a flat list of attributes with names produced by the concatenation, like the following.
  596. JSON-LD
  597. """"""""
  598. A JSON-LD template can be defined as a GeoJSON template since it is a JSON based output as well. However it needs to have a :code:`@context` attribute, object or array at the beginning of it in order to conform to the standard. Moreover each JSON Object must have an :code:`@type` defining a type through a vocabulary term.
  599. To accomplish these requirements it is possible to specify several :code:`$options` on the template:
  600. * :code:`@context` providing a full JSON-LD :code:`@context`.
  601. * :code:`@type` providing a type term for the root JSON object in the final output (by default the value is :code:`FeatureCollection`).
  602. * :code:`collection_name` providing an alternative name for the features array in the final output (by default :code:`features` is used). The option is useful in case the user wants to use a features attribute name equals to a specific term defined in a vocabulary.
  603. .. code-block:: json
  604. {
  605. "$options":{
  606. "encode_as_string": true,
  607. "collection_name":"stations",
  608. "@type":"schema:Thing",
  609. "@context":[
  610. "https://opengeospatial.github.io/ELFIE/contexts/elfie-2/elf-index.jsonld",
  611. "https://opengeospatial.github.io/ELFIE/contexts/elfie-2/gwml2.jsonld",
  612. {
  613. "gsp":"http://www.opengis.net/ont/geosparql#",
  614. "sf":"http://www.opengis.net/ont/sf#",
  615. "schema":"https://schema.org/",
  616. "st":"http://www.stations.org/1.0",
  617. "wkt":"gsp:asWKT",
  618. "Feature":"gsp:Feature",
  619. "geometry":"gsp:hasGeometry",
  620. "point":"sf:point",
  621. "features":{
  622. "@container":"@set",
  623. "@id":"schema:hasPart"
  624. }
  625. }
  626. ]
  627. },
  628. "$source":"st:MeteoStationsFeature",
  629. "Identifier":"${@id}",
  630. "Name":"${st:common_name}",
  631. "Code":"$${strConcat('STATION-', xpath('st:code'))}",
  632. "Location":"$${toWKT(st:position)}",
  633. "Temperatures":[
  634. {
  635. "$source":"st:meteoObservations/st:MeteoObservationsFeature",
  636. "$filter":"xpath('st:meteoParameters/st:MeteoParametersFeature/st:param_name') = 'temperature' AND 'yes' = env('showTemperatures','yes')"
  637. },
  638. {
  639. "Timestamp":"${st:time}",
  640. "Value":"${st:value}"
  641. }
  642. ],
  643. "Pressures":[
  644. {
  645. "$source":"st:meteoObservations/st:MeteoObservationsFeature",
  646. "$filter":"xpath('st:meteoParameters/st:MeteoParametersFeature/st:param_name') = 'pressure' AND 'yes' = env('showPressures','yes')"
  647. },
  648. {
  649. "Timestamp":"${st:time}",
  650. "Value":"${st:value}"
  651. }
  652. ],
  653. "Winds speed":[
  654. {
  655. "$source":"st:meteoObservations/st:MeteoObservationsFeature",
  656. "$filter":"xpath('st:meteoParameters/st:MeteoParametersFeature/st:param_name') = 'wind speed' AND 'yes' = env('showWinds','yes')"
  657. },
  658. {
  659. "Timestamp":"${st:time}",
  660. "Value":"${st:value}"
  661. }
  662. ]
  663. }
  664. The :code:`@context` will show up at the beginning of the JSON-LD output:
  665. .. code-block:: json
  666. {
  667. "@context":[
  668. "https://opengeospatial.github.io/ELFIE/contexts/elfie-2/elf-index.jsonld",
  669. "https://opengeospatial.github.io/ELFIE/contexts/elfie-2/gwml2.jsonld",
  670. {
  671. "gsp":"http://www.opengis.net/ont/geosparql#",
  672. "sf":"http://www.opengis.net/ont/sf#",
  673. "schema":"https://schema.org/",
  674. "st":"http://www.stations.org/1.0",
  675. "wkt":"gsp:asWKT",
  676. "Feature":"gsp:Feature",
  677. "geometry":"gsp:hasGeometry",
  678. "point":"sf:point",
  679. "features":{
  680. "@container":"@set",
  681. "@id":"schema:hasPart"
  682. }
  683. }
  684. ],
  685. "type":"FeatureCollection",
  686. "@type":"schema:Thing",
  687. "stations":[
  688. {
  689. "Identifier":"MeteoStationsFeature.7",
  690. "Name":"Bologna",
  691. "Code":"STATION-BOL",
  692. "Location":"POINT (44.5 11.34)",
  693. "Temperatures":[
  694. {
  695. "Timestamp":"2016-12-19T11:28:31.000+00:00",
  696. "Value":"35.0"
  697. },
  698. {
  699. "Timestamp":"2016-12-19T11:28:55.000+00:00",
  700. "Value":"25.0"
  701. }
  702. ],
  703. "Pressures":[
  704. {
  705. "Timestamp":"2016-12-19T11:30:26.000+00:00",
  706. "Value":"1019.0"
  707. },
  708. {
  709. "Timestamp":"2016-12-19T11:30:51.000+00:00",
  710. "Value":"1015.0"
  711. }
  712. ],
  713. "Winds speed":[
  714. {
  715. "Timestamp":"2016-12-19T11:29:24.000+00:00",
  716. "Value":"80.0"
  717. }
  718. ]
  719. }
  720. ]
  721. }
  722. The above template defines, along with the :code:`@context`, also the :code:`option` :code:`encode_as_string`. The option is used to request a JSON-LD output where all the attributes are encoded as text. By default attributes are instead encoded as in :code:`GeoJSON` output format.
  723. When dealing with a GetFeatureInfo request over a LayerGroup asking for a JSON-LD output the plug-in will perform a union of the JSON-LD :code:`@context` (when different) defined in the template of each contained layer. This means that in case of conflicting attributes name the attributes name will override each other according to the processing order of the layers.
  724. The user can prevent this behaviour by taking advantage of the :code:`include` directive, explained below, defining a single :code:`@context` included in the template of each contained layer. In this way all the layer will share the same context definition.
  725. GML
  726. """
  727. GML output has two :code:`options`: Namespaces and SchemaLocation, that define the namespaces and the SchemaLocation attribute that will be included in the FeatureCollection element in the resulting output. These options needs to be specified inside a :code:`gft:Options` element at the beginning of the template right after the :code:`gft:Template` element, e.g.
  728. .. code-block:: xml
  729. <gft:Template>
  730. <gft:Options>
  731. <gft:Namespaces xmlns:st="http://www.stations.org/1.0"/>
  732. <gft:SchemaLocation xsi:schemaLocation="http://www.stations.org/1.0 http://www.stations.org/stations/1.0/xsd/stations.xsd"/>
  733. </gft:Options>
  734. <st:MeteoStations gml:id="${@id}">
  735. <st:code>$${strConcat('Station_',st:code)}</st:code>
  736. <st:name>${st:common_name}</st:name>
  737. <st:geometry>${st:position}</st:geometry>
  738. <st:temperature gft:isCollection="true" gft:source="st:meteoObservations/st:MeteoObservationsFeature" gft:filter="xpath('st:meteoParameters/st:MeteoParametersFeature/st:param_name') = 'temperature'">
  739. <st:Temperature>
  740. <st:time>${st:time}</st:time>
  741. <st:value>${st:value}</st:value>
  742. </st:Temperature>
  743. </st:temperature>
  744. <st:pressure gft:isCollection="true" gft:source="st:meteoObservations/st:MeteoObservationsFeature" gft:filter="xpath('st:meteoParameters/st:MeteoParametersFeature/st:param_name') = 'pressure'">
  745. <st:Pressure>
  746. <st:time>${st:time}</st:time>
  747. <st:value>${st:value}</st:value>
  748. </st:Pressure>
  749. </st:pressure>
  750. <st:wind_speed gft:isCollection="true" gft:source="st:meteoObservations/st:MeteoObservationsFeature" gft:filter="xpath('st:meteoParameters/st:MeteoParametersFeature/st:param_name') = 'wind speed'">
  751. <st:Wind_speed>
  752. <st:time>${st:time}</st:time>
  753. <st:value>${st:value}</st:value>
  754. </st:Wind_speed>
  755. </st:wind_speed>
  756. </st:MeteoStations>
  757. </gft:Template>
  758. HTML
  759. """"
  760. HTML templates can use several :code:`options`:
  761. * :code:`<script>` allows defining whatever javascript is needed, e.g. to create a tree view (as in the example below) or an openlayers map client.
  762. * :code: `<script type="application/ld+json"/>` allows to inject the JSON-LD representation of the features being templated in the `<head>`. In order to have the option working properly a JSON-LD template must be configured for the layer, or GeoServer will return an error message.
  763. * :code:`<style>` allows defining css content.
  764. * :code:`<link>` allows linking to external resources.
  765. The content of :code:`<script>` and :code:`<style>` needs to be provided as :code:`<![CDATA[`.
  766. The following is an example of a HTML template that will output the Stations features as a tree view. Also in this example we are using the same filter on :code:`st:meteoObservations` as in the other template examples.::
  767. <gft:Template>
  768. <gft:Options>
  769. <style>
  770. <![CDATA[ul, #myUL {
  771. list-style-type: none;
  772. }
  773. #myUL {
  774. margin: 0;
  775. padding: 0;
  776. }
  777. .caret {
  778. cursor: pointer;
  779. -webkit-user-select: none; /* Safari 3.1+ */
  780. -moz-user-select: none; /* Firefox 2+ */
  781. -ms-user-select: none; /* IE 10+ */
  782. user-select: none;
  783. }
  784. .caret::before {
  785. content: "\25B6";
  786. color: black;
  787. display: inline-block;
  788. margin-right: 6px;
  789. }
  790. .caret-down::before {
  791. -ms-transform: rotate(90deg); /* IE 9 */
  792. -webkit-transform: rotate(90deg); /* Safari */'
  793. transform: rotate(90deg);
  794. }
  795. .nested {
  796. display: none;
  797. }
  798. .active {
  799. display: block;
  800. }]]></style>
  801. <script><![CDATA[window.onload = function() {
  802. var toggler = document.getElementsByClassName("caret");
  803. for (let item of toggler){
  804. item.addEventListener("click", function() {
  805. this.parentElement.querySelector(".nested").classList.toggle("active");
  806. this.classList.toggle("caret-down");
  807. });
  808. }
  809. }]]></script>
  810. <script type="application/ld+json"/>
  811. </gft:Options>
  812. <ul id="myUL">
  813. <li>
  814. <span class="caret">MeteoStations</span>
  815. <ul class="nested">
  816. <li>
  817. <span class="caret">Code</span>
  818. <ul class="nested">
  819. <li>$${strConcat('Station_',st:code)}</li>
  820. </ul>
  821. </li>
  822. <li>
  823. <span class="caret">Name</span>
  824. <ul class="nested">
  825. <li>${st:common_name}</li>
  826. </ul>
  827. </li>
  828. <li>
  829. <span class="caret">Geometry</span>
  830. <ul class="nested">
  831. <li>${st:position}</li>
  832. </ul>
  833. </li>
  834. <li gft:isCollection="true" gft:source="st:meteoObservations/st:MeteoObservationsFeature" gft:filter="xpath('st:meteoParameters/st:MeteoParametersFeature/st:param_name') = 'temperature'">
  835. <span class="caret">Temperature</span>
  836. <ul class="nested">
  837. <li>
  838. <span class="caret">Time</span>
  839. <ul class="nested">
  840. <li>${st:time}</li>
  841. </ul>
  842. </li>
  843. <li>
  844. <span class="caret">Value</span>
  845. <ul class="nested">
  846. <li>${st:time}</li>
  847. </ul>
  848. </li>
  849. </ul>
  850. </li>
  851. <li gft:isCollection="true" gft:source="st:meteoObservations/st:MeteoObservationsFeature" gft:filter="xpath('st:meteoParameters/st:MeteoParametersFeature/st:param_name') = 'pressure'">
  852. <span class="caret">Pressure</span>
  853. <ul class="nested">
  854. <li>
  855. <span class="caret">Time</span>
  856. <ul class="nested">
  857. <li>${st:time}</li>
  858. </ul>
  859. </li>
  860. <li>
  861. <span class="caret">Value</span>
  862. <ul class="nested">
  863. <li>${st:time}</li>
  864. </ul>
  865. </li>
  866. </ul>
  867. </li>
  868. <li gft:isCollection="true" gft:source="st:meteoObservations/st:MeteoObservationsFeature" gft:filter="xpath('st:meteoParameters/st:MeteoParametersFeature/st:param_name') = 'wind speed'">
  869. <span class="caret">Wind Speed</span>
  870. <ul class="nested">
  871. <li>
  872. <span class="caret">Time</span>
  873. <ul class="nested">
  874. <li>${st:time}</li>
  875. </ul>
  876. </li>
  877. <li>
  878. <span class="caret">Value</span>
  879. <ul class="nested">
  880. <li>${st:time}</li>
  881. </ul>
  882. </li>
  883. </ul>
  884. </li>
  885. </ul>
  886. </li>
  887. </ul>
  888. </gft:Template>
  889. The output of the template will be the following:
  890. .. figure:: images/html-template-result.png
  891. Including other templates
  892. -------------------------
  893. While developing a group of templates, it's possible to notice sections that repeat across
  894. different template instances. Template inclusion allows sharing the common parts, extracting them
  895. in a re-usable building block.
  896. Inclusion can be performed using two directives:
  897. * :code:`include` allows including a separate template as is.
  898. * :code:`includeFlat` allows including a separate template, stripping the top-most container.
  899. As for other directives the syntax varies slightly between JSON based template and XML based ones.
  900. The two directives need to specify a path to the template to be included.
  901. Template names can be plain, as in this example, refer to sub-directories, or be absolute.
  902. Examples of valid template references are:
  903. * ``subProperty.json``
  904. * ``./subProperty.json``
  905. * ``./blocks/aBlock.json``
  906. * ``/templates/test/aBlock.json``
  907. However it's currently not possible to climb up the directory hierarchy using relative references,
  908. so a reference like ``../myParentBlock.json`` will be rejected.
  909. JSON based templates (GeoJSON, JSON-LD)
  910. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  911. In this context the two directives can be defined as:
  912. * :code:`$include`.
  913. * :code:`$includeFlat`.
  914. Regarding the :code:`$includeFlat` option is worth mentioning that in a JSON context:
  915. * If a JSON object is included, then its properties are directly included in-place, which makes sense only within another object.
  916. * If instead a JSON array is included, then its values are directly included in-place, which makes sense only within another array.
  917. The following JSON snippet shows the four possible syntax options for template inclusion:
  918. .. code-block:: json
  919. :linenos:
  920. {
  921. "aProperty": "$include{subProperty.json}",
  922. "$includeFlat": "propsInAnObject.json",
  923. "anArray" : [
  924. "$include{arrayElement.json}",
  925. "$includeFlat{subArray.json}"
  926. ],
  927. "$includeFlat": "${property}"
  928. }
  929. Notes:
  930. 1) The ``subProperty.json`` template (line 2) can be both an object or an array, it will be used as the new value of ``aProperty``
  931. 2) The ``propsInAnObject.json`` template (line 3) is required to be a JSON object, its properties will be
  932. directly included in-place where the ``$includeFlat`` directive is
  933. 3) The ``arrayElement.json`` template (line 5) can be both an object or an array, the value will be replaced
  934. directly as the new element in ``anArray``. This allows creation of a JSON object as the array
  935. element, or the creation of a nested array.
  936. 4) The ``subArray.json`` template (line 6) must be an array itself, the container array will be stripped and
  937. its values directly integrated inside ``anArray``.
  938. In case an includeFlat directive is specified and it's attribute value is a property interpolation directive, if the property name evaluates to a json it gets included flat in the final output e.g
  939. including json:
  940. .. code-block:: json
  941. :linenos:
  942. {
  943. "property":"${property}",
  944. "bProperty":"15",
  945. "cProperty":"30"
  946. }
  947. ${property} value:
  948. .. code-block:: json
  949. :linenos:
  950. {
  951. "aProperty": "10",
  952. "bProperty": "20"
  953. }
  954. result:
  955. .. code-block:: json
  956. :linenos:
  957. {
  958. "aProperty":"10",
  959. "bProperty":"20",
  960. "cProperty":"30"
  961. }
  962. The ``${property}`` directive evaluates to a JSON that will be merged with the including one. In case the including JSON as an attribute with the name equal to one of the attributes in the included JSON, the included will override the property with the same name in the including one.
  963. In case an includeFlat directive is specified inside a JSON Array with a Feature property and the property evaluate to a JSON Array, the container array will be stripped and its values included directly inside the container Array:
  964. .. code-block:: json
  965. :linenos:
  966. [
  967. "value1",
  968. "value2",
  969. "value3",
  970. "$includeFlat{${property}}"
  971. ]
  972. ${property} value:
  973. .. code-block:: json
  974. :linenos:
  975. [
  976. "value4",
  977. "value5"
  978. ]
  979. result:
  980. .. code-block:: json
  981. :linenos:
  982. [
  983. "value1",
  984. "value2",
  985. "value3",
  986. "value4",
  987. "value5"
  988. ]
  989. XML based templates (GML)
  990. ^^^^^^^^^^^^^^^^^^^^^^^^^^
  991. In an XML context the two directives needs to be defined in the following way:
  992. * :code:`<gft:includeFlat>path/to/included.xml</gft:includeFlat>`.
  993. * :code:`<gsml:specification gft:source="gsml:specification">$include{includedTemplate.xml}</gsml:specification>`.
  994. In the first case the included template will replace the :code:`<gft:includeFlat>` element. In the second one the included template will be placed inside the :code:`<gsml:specification>` element.
  995. Extending other templates via merge (JSON based templates only)
  996. ---------------------------------------------------------------
  997. Templates inclusion, described above, allows importing a block into another template, as is.
  998. The ``$merge`` directive instead allows getting an object and use it as a base, that will be
  999. overridden by the properties of the object it is merged into.
  1000. For example, let's assume this is a base JSON template:
  1001. .. code-block:: json
  1002. {
  1003. "a": 10,
  1004. "b": "${attribute1}",
  1005. "c": "${attribute2}",
  1006. "array": [1, 2, 3]
  1007. }
  1008. and this is a template extending it:
  1009. .. code-block:: json
  1010. {
  1011. "$merge": "base.json",
  1012. "a": {
  1013. "a1": 1,
  1014. "a2": 2
  1015. },
  1016. "b": null,
  1017. "d": "${customAttribute}"
  1018. }
  1019. The template actually being processed would look as follows:
  1020. .. code-block:: json
  1021. {
  1022. "a": {
  1023. "a1": 1,
  1024. "a2": 2
  1025. },
  1026. "c": "${attribute2}",
  1027. "array": [1, 2, 3]
  1028. "d": "${customAttribute}"
  1029. }
  1030. The general rules for object merging are:
  1031. * Overridden simple properties are replaced.
  1032. * Properties set to null are removed.
  1033. * Nested objects available in both trees are drilled down, being recursively merged.
  1034. * Arrays are replaced as-is, with no merging. The eventual top level ``features`` array is the only
  1035. exception to this rule.
  1036. * While order of the keys is not important in JSON, the merge is processed so that the base
  1037. property names are included first in the merged result, and the new ones included in the override
  1038. are added after them.
  1039. * If in the overalay JSON template, are present attributes with a property interpolation directive or an expression that in turn returns a JSON, the JSON attribute tree will be merged too with the corresponding one in the base JSON tree.
  1040. The ``$merge`` directive can be used in any object, making it the root for the merge operation.
  1041. This could be used as an alternative to inclusion when local customizations are needed.
  1042. Environment parametrization
  1043. ---------------------------
  1044. A template configuration can also be manipulated on the fly, replacing existing attributes, attributes' names and sources using the :code:`env` parameter.
  1045. To achieve this the attribute name, the attribute, or the source should be replaced by the env function in the following way :code:`$${env('nameOfTheEnvParameter','defaultValue')}`.
  1046. If in the request it is specified an env query parameter :code:`env='nameOfTheEnvParameter':'newValue'`, the default value will be replaced in the final output with the one specified in the request.
  1047. The functionality allows also to manipulate dynamically filters and expression. For example it is possible to change Filter arguments: :code:`"$filter":"xpath('gsml:name') = env('nameOfTheEnvParameter','defaultValue')`.
  1048. Xpaths can be manipulated as well to be totally or partially replaced: :code:`$${xpath(env('xpath','gsml:ControlledConcept/gsml:name')}` or :code:`$${xpath(strConcat('env('gsml:ControlledConcept',xpath','/gsml:name')))}`.
  1049. Dynamic keys
  1050. ------------
  1051. Keys in JSON output can also be fully dependent on feature attributes, for example:
  1052. .. code-block:: json
  1053. {
  1054. "${attributeA}" : "${attributeB}",
  1055. "$${strSubstring(attributeC, 0, 3)}": "$${att1 * att2}"
  1056. }
  1057. Using a key depending on feature attributes has however drawbacks: it won't be possible to use it
  1058. for filtering in WFS and for queriables generation in OGC APIs, as it does not have a stable value.
  1059. JSON based properties
  1060. ---------------------
  1061. Certain databases have native support for JSON fields. For example, PostgreSQL has both a JSON
  1062. and a JSONB type. The JSON templating machinery can recognize these fields and export them
  1063. as JSON blocks, for direct substitution in the output.
  1064. It is also possible to pick a JSON attribute and use the ``jsonPointer`` function to extract either
  1065. a property or a whole JSON subtree from it. See the `JSON Pointer RFC <https://datatracker.ietf.org/doc/html/rfc6901>`_
  1066. for more details about valid expressions.
  1067. Here is an example of using JSON properties:
  1068. .. code-block:: json
  1069. :linenos:
  1070. {
  1071. "assets": "${assets}",
  1072. "links": [
  1073. "$${jsonPointer(others, '/fullLink')}",
  1074. {
  1075. "href": "$${jsonPointer(others, '/otherLink/href')}",
  1076. "rel": "metadata",
  1077. "title": "$${jsonPointer(others, '/otherLink/title')}",
  1078. "type": "text/xml"
  1079. }
  1080. ]
  1081. }
  1082. Some references:
  1083. - ``Line 1`` uses ``assets``, a property that can contain a JSON tree of any shape, which will be
  1084. expanded in place.
  1085. - ``Line 4`` inserts a full JSON object in the array. The object is a sub-tree of the ``others`` property,
  1086. which is a complex JSON document with several extra properties (could be a generic containers for
  1087. properties not fitting the fixed database schema).
  1088. - ``Line 6`` and ``Line 8`` extract from the ``others`` property specific string values.
  1089. Array based properties (JSON based templates only)
  1090. --------------------------------------------------
  1091. Along JSON properties, it's not rare to find support for array based attributes in modern databases.
  1092. E.g. ``varchar[]`` is a attributes containing an array of strings.
  1093. The array properties can be used as-is, and they will be expanded into a JSON array.
  1094. Let's assume the ``keywords`` database column contains a list of strings, then the following template:
  1095. .. code-block:: json
  1096. :linenos:
  1097. {
  1098. "keywords": "${keywords}"
  1099. }
  1100. May expand into:
  1101. .. code-block:: json
  1102. :linenos:
  1103. {
  1104. "keywords": ["features", "templating"]
  1105. }
  1106. It is also possible to use an array as the source of iteration, referencing the current
  1107. array item using the ``${.}`` XPath. For example:
  1108. .. code-block:: json
  1109. :linenos:
  1110. {
  1111. "metadata": [
  1112. {
  1113. "$source": "keywords"
  1114. },
  1115. {
  1116. "type": "keyword",
  1117. "value": "${.}"
  1118. }
  1119. ]
  1120. }
  1121. The above may expand into:
  1122. .. code-block:: json
  1123. :linenos:
  1124. {
  1125. "metadata": [
  1126. {
  1127. "type": "keyword",
  1128. "value": "features"
  1129. },
  1130. {
  1131. "type": "keyword",
  1132. "value": "templating"
  1133. }
  1134. ]
  1135. }
  1136. In case a specific item of an array needs to be retrieved, the ``item`` function can be used,
  1137. for example, the following template extracts the second item in an array (would fail if not
  1138. present):
  1139. .. code-block:: json
  1140. :linenos:
  1141. {
  1142. "second": "$${item(keywords, 1)}"
  1143. }
  1144. There is currently no explicit support for array based columns in GML templates.
  1145. Simplified Property Access
  1146. --------------------------
  1147. The features-templating plug-in provides the possibility to directly reference domain name when dealing with Complex Features and using property interpolation in a template.
  1148. As an example let's use again the meteo stations use case. This is the ER diagram of the Database table involved.
  1149. .. figure:: images/meteos-stations-er-diagram.png
  1150. The following is a GeoJSON template that directly reference table names and column name, instead of referencing the target Xpath in the AppSchema mappings.
  1151. .. code-block:: json
  1152. {
  1153. "$source":"meteo_stations",
  1154. "Identifier":"${id}",
  1155. "Name":"${common_name}",
  1156. "Code":"$${strConcat('STATION-', xpath('code'))}",
  1157. "Location":"$${toWKT(position)}",
  1158. "Temperatures":[
  1159. {
  1160. "$source":"meteo_observations",
  1161. "$filter":"propertyPath('->meteo_parameters.param_name') = 'temperature' AND 'yes' = env('showTemperatures','yes')"
  1162. },
  1163. {
  1164. "Timestamp":"${time}",
  1165. "Value":"${value}"
  1166. }
  1167. ],
  1168. "Pressures":[
  1169. {
  1170. "$source":"meteo_observations",
  1171. "$filter":"propertyPath('->meteo_parameters.param_name') = 'pressure' AND 'yes' = env('showPressures','yes')"
  1172. },
  1173. {
  1174. "Timestamp":"${time}",
  1175. "Value":"${value}"
  1176. }
  1177. ],
  1178. "Winds speed":[
  1179. {
  1180. "$source":"meteo_observations",
  1181. "$filter":"propertyPath('->meteo_parameters.param_name') = 'wind speed' AND 'yes' = env('showWinds','yes')"
  1182. },
  1183. {
  1184. "Timestamp":"${time}",
  1185. "Value":"${value}"
  1186. }
  1187. ]
  1188. }
  1189. As it is possible to see this template has some differences comparing to the one seen above:
  1190. * Property interpolation (``${property}``) and cql evaluation (``$${cql}``) directives are referencing the column name of the attribute that is meant to be included in the final output. The names match the ones of the columns and no namespaces prefix is being used.
  1191. * Inside the $${cql} directive instead of using an ``xpath`` function the ``propertyPath`` function is being use. It must be used when the property references domain names inside a ``$${cql}`` directive. Paths in this case are no more separated by a ``/`` but by a ``.`` dot.
  1192. * The ``$source`` directive references the table names.
  1193. * When a ``column/property`` in a ``table/source`` is referenced from the context of the upper ``table/source``, as in all the filters in the template, the table name needs to be prefixed with a ``->`` symbol, and column name can come next separated by a ``.`` dot. Putting it in another way: the ``->`` signals that the next path part is a table joined to the last source defined.
  1194. .. warning:: the :code:`propertyPath('propertyPath')` cql function is meant to be used only in the scope of this plugin. It is not currently possible to reference domain property outside the context of a template file.
  1195. This functionality is particularly useful when defining templates on top of Smart Data Loader based Complex Features.
  1196. Controlling Attributes With N Cardinality
  1197. ------------------------------------------
  1198. When a property interpolation targets an attribute with multiple cardinality in a Complex Feature, feature templating will output the result as an array. This default behaviour can be controlled and modified with the usage of a set of CQL functions that are available in the plug-in, which allow to control how the list should be encoded in the template.
  1199. * ``aggregate``: takes as arguments an expression (a property name or a function) that returns a list of values and a literal with the aggregation type eg. ``aggregate(my.property.name,'MIN')``. The supported aggregation type are the following:
  1200. - ``MIN`` will return the minimum value from a list of numeric values.
  1201. - ``MAX`` will return the max value from a list of numeric values.
  1202. - ``AVG`` will return the average value from a list of numeric values.
  1203. - ``UNIQUE`` will remove duplicates values from a list of values.
  1204. - ``JOIN`` will concatenate the list of values in a single string. It accepts a parameter to specify the separator that by default is blank space: ``aggregate(my.property.name,'JOIN(,)')`` .
  1205. * ``stream``: takes an undefined number of expressions as parameters and chain them so that each expression evaluate on top of the output of the previous expression: eg. ``stream(aPropertyName,aFunction,anotherPropertyName)`` while evaluate the ``aFunction`` on the output of ``aPropertyName`` evaluation and finally ``anotherPropertyName`` will evaluate on top of the result of ``aFunction``.
  1206. * ``filter``: takes a literal cql filter as a parameter and evaluates it. Every string literal value in the cql filter must be between double quotes escaped: eg. ``filter('aProperty = \"someValue\"')``.
  1207. * ``sort``: sort the list of values in ascending or descending order. It accepts as a parameter the sort order (``ASC``,``DESC``) and optionally a property name to target the property on which the sorting should be executed. If no property name is defined the sorting will be applied on the current node on which the function evaluates: ``sort('DESC',nested.property)``, ``sort('ASC')``.
  1208. The above functions can be combined allowing fine grained control over the encoding of list values in a template. Assuming to write a template for the `meteo stations use case <#source-and-filter-complex-feature-example>`__, these are some example of the usage of the functions (simplified property access is used in the example below):
  1209. - ``aggregate(stream(->meteo_observations,filter('value > 35')),AVG)`` will compute and return the average value of all the Observation nested feature value attribute.
  1210. - ``aggregate(stream(->meteo_observations.->meteo_parameters,sort("ASC",param_name),param_unit),JOIN(,))`` will pick up the ``meteo_parameter`` nested features for each station feature, will sort them in ascending order based on the value of the ``param_name`` and will concatenate the ``param_unit`` values in a single string, comma separated.
  1211. Template Validation
  1212. -------------------
  1213. There are two kind of validation available. The first one is done automatically every time a template is requested for the first time or after modifications occurred. It is done automatically by GeoServer and validates that all the property names being used in the template applies to the Feature Type.
  1214. The second type of validation can be issued from the UI (see the configuration section) in case a JSON-LD or a GML output are request. The GML validation will validate the output against the provided ``SchemaLocation`` values. The ``JSON-LD`` validation is detailed below.
  1215. JSON-LD Validation
  1216. ^^^^^^^^^^^^^^^^^^
  1217. The plugin provides a validation for the json-ld output against the ``@context`` defined in the template. It is possible to require it by specifying a new query parameter in the request: ``validation=true``.
  1218. The validation takes advantage form the json-ld api and performs the following steps:
  1219. * the `expansion algorithm <https://www.w3.org/TR/json-ld11-api/#expansion-algorithm>`_ is executed against the json-ld output, expanding each features' attribute name to IRIs, removing those with no reference in the ``@context`` and the ``@context`` itself;
  1220. * the `compaction algorithm <https://www.w3.org/TR/json-ld11-api/#compaction-algorithm>`_ is then executed on the expansion result, putting back the ``@context`` and shortens to the terms the expanded attribute names as in the original output;
  1221. * finally the result of the compaction process is compared to the original json-ld and if some attributes are missing it means that they were not referenced in the ``@context``. An exception is thrown with a message pointing to the missing attributes.