(from v2.6.0)
1. Introduction
ldproxy is a software tool to share spatial data according to the W3C/OGC Spatial Data on the Web Best Practices and the draft OGC Web Feature Service standard. ldproxy 1.3.0 supports two data sources: OGC Web Feature Services 2.0 and PostgreSQL/PostGIS databases (PostgreSQL versions 9.5, 9.6, 10.5; PostGIS version 2.4). The ldproxy configuration for a dataset is internally stored in a set of JSON documents.
This ShapeChange target generates ldproxy configurations for data according to one or more application schemas. Currently only PostgreSQL/PostGIS databases are supported at the backend.
2. Description
2.1. Overview
The configuration depends on two aspects:
-
how the feature types of the application schema(s) are mapped to the database;
-
how you want the data to appear to users of the API published by ldproxy.
The configuration can be influenced using conversion rules and parameters that are described below.
The following configuration directories and JSON files are created. The JSON files are underlined. The directories and files in red are not created by ShapeChange and have to be created or edited by the administrator as described below.
-
config-store
-
entities
-
services
-
xyz [the serviceId, see below]
-
overrides
-
xyz [the same serviceId]
-
-
-
codelists
-
codelist1 [the name of the code list classifier]
-
codelist2
-
…
-
-
-
settings
-
ldproxy-target-geojson
-
overrides
-
GeoJsonConfig
-
-
-
ldproxy-target-gml
-
overrides
-
GmlConfig
-
-
-
xtraplatform-server
-
CoreServerConfig
-
-
xtraplatform-auth-external
-
overrides
-
ExternalAuthConfig
-
-
-
-
2.2. The service override configuration file
The additional service override (xyz - see the example below) is a copy of the file with the same name created by ShapeChange. Unchanged JSON members can be removed. The service override at least has to contain the database connection info (JSON path "$.featureProvider.connectionInfo.*") and the coordinate reference system information (JSON path "$.featureProvider.nativeCrs"). For example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"id": "xyz",
"featureProvider": {
"connectionInfo": {
"host": "172.17.0.1",
"database": "postgres",
"user": "postgres",
"password": "eHl6Cg=="
},
"nativeCrs": {
"code": 25832
},
"providerType": "PGIS"
}
}
The value of "$.featureProvider.connectionInfo.host" has to be the hostname that is reachable from the ldproxy docker container (see below). In a local test environment where the database is on the same machine, this would typically be "172.17.0.1" (except on macOS, where it is "host.docker.internal").
The value of "$.featureProvider.connectionInfo.password" is the base64 encoded password.
The value of "$.featureProvider.nativeCrs.code" is the integer EPSG code of the coordinate reference system of the geometries in the database. 25832 from the example above is ETRS89 / UTM zone 32N.
In addition, the spatial extents for the feature types in the dataset should be set. The coordinate reference system is WGS 84 longitude/latitude. For feature types with temporal properties the temporal extent should be set, too.
The default values set by ShapeChange are the whole Earth as the spatial extent (-180° to 180° longitude, -90° to 90° latitude) and unlimited temporal extent (indicated by null values). To change this, set the values of "$.featureTypes.\{featureTypeId}.extent.spatial.*" and "$.featureTypes.\{featureTypeId}.extent.temporal.*"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
{
"id": "xyz",
"featureProvider": { ...
},
"featureTypes": {
"afeaturetype": {
"id": "afeaturetype",
"label": "A Feature Type",
"description": "A description of the feature type",
"extent": {
"spatial": {
"xmin": 6.1173598760,
"ymin": 48.9662745077,
"xmax": 8.5084754437,
"ymax": 50.9404435711
},
"temporal": {
"start": "2010-01-01T00:00:00Z",
"end": null
}
}
},
"anotherfeaturetype": {
"id": "anotherfeaturetype",
"label": "Another Feature Type",
"description": "A description of the feature type",
"extent": {
"spatial": {
"xmin": 6.1173598760,
"ymin": 48.9662745077,
"xmax": 8.5084754437,
"ymax": 50.9404435711
},
"temporal": {
"start": "2010-01-01T00:00:00Z",
"end": null
}
}
},
...
}
}
2.3. The security configuration file (optional)
If the ldproxy deployment includes services with access control (see the parameter "secured"), the configuration file ExternalAuthConfig has to be provided, too.
See the ldproxy documentation for details.
Example:
1
2
3
4
5
{
"jwtSigningKey" : "eW91ci0yNTYtYml0LXNlY3JldA==",
"externalDynamicAuthorizationEndpoint": "http://127.0.0.1:10010/authorize",
"postProcessingEndpoint": "http://127.0.0.1:10010/postprocess"
}
The configuration has to contain either a "$.userInfoEndpoint" or a "$.jwtSigningKey" member for token validation.
If dynamic authorization using JSON XACML is used (JSON path"$.externalDynamicAuthorizationEndpoint"), ldproxy will provide the following attributes in the POST request to the endpoint:
-
"urn:oasis:names:tc:xacml:1.0:subject:subject-id" is the username, if it can be derived from the token.
-
"urn:oasis:names:tc:xacml:1.0:resource:resource-id" is the access path of the API for this request.
-
"urn:oasis:names:tc:xacml:1.0:action:action-id" is always "POST".
-
"payload" is the base64-encoded JSON payload.
The JSON Web Token is provided in the Authorization header, too.
If the response value is "Deny", ldproxy will reject the request. If the value is "Permit", the request will be processed.
In secured services, ldproxy also supports a preprocessing of the payload of a POST/PUT request to insert or update a feature. If the request has been permitted, the post-processing endpoint (JSON path "$.postProcessingEndpoint") is invoked. The post-processing endpoint has to return an updated feature that will then be inserted/updated in the dataset.
Request:
1
2
3
4
{
"id": "abc",
...
}
Response:
1
2
3
4
5
6
7
8
9
{
"id": "abc",
...
"status": 0,
"createdBy": "johndoe",
"created": "2018-06-22T11:28:34Z",
"changedBy": "johndoe",
"changed": "2018-06-22T11:28:34Z"
}
2.4. The ldproxy deployment
To deploy an instance of ldproxy with the configuration, a docker container with a mounted volume has to be created, for example with the docker command line tool:
1
docker run --name ldproxy -d -p 7080:7080 -v /home/user/ldproxy_data:/ldproxy/data iide/ldproxy:v1.3.0
In this example "/home/user/ldproxy_data" has to be the parent directory of the "config-store" directory.
The published services are then available at http://localhost:7080/rest/services.
To change the external URL of a service, see the ldproxy documentation. Public services will typically hide the ldproxy manager. In this case, the value of the external URL (JSON path "$.externalUrl" in CoreServerConfig) from "/rest/services" to the new value.
2.5. Deployment issues
If you encounter issues with the deployment, inspect the log files to determine the cause of the issue. See the ldproxy documentation.
3. Configuration
3.1. Class
The class for the target implementation is de.interactive_instruments.ShapeChange.Target.Ldproxy.Config
3.2. Conversion Rules
3.2.1. rule-ldp-prop-all-datatype-relations-as-n-to-m-relations
This rule states that all properties with a value that is a data type are represented in the database using an intermediate table. The name of the table is determined by parameter templateNtoMTable.
Parameter(s):
-
templateNtoMTable
3.2.2. rule-ldp-prop-all-featuretype-relations-as-n-to-m-relations
This rule states that all properties with a value that is a feature type are represented in the database using an intermediate table. The name of the table is determined by parameter templateNtoMTable.
Parameter(s):
-
templateNtoMTable
3.2.3. rule-ldp-prop-all-codelist-values-as-strings
This rule states that all properties with a code list value are represented in the database using a string field.
Currently, no other mapping is supported.
Parameter(s): none
3.2.4. rule-ldp-prop-multiple-single-values-as-1-to-n-relations
This rule states that all properties with a value that is a simple value and a maximum multiplicity greater than 1 are represented in the database using a separate table. The name of the table is determined by parameter template1toNTable.
Parameter(s):
-
template1toNTable
3.2.5. rule-ldp-prop-separate-geometry-table
This rule states that all geometries are stored in a single geometry table (and not directly in the feature tables).
Parameter(s): none
3.2.6. rule-ldp-all-names-in-lowercase
This rule derives all table and field names as lower case from the model.
Currently, no other mapping is supported.
Parameter(s): none
3.2.7. rule-ldp-all-names-max-length
This rule limits table and field names to n characters where n is set using parameter maxLength.
Parameter(s):
-
maxLength
3.2.8. rule-ldp-cls-non-abstract-feature-types-as-collection
This rule maps all non-abstract feature types to a collection.
Parameter(s): none
3.2.9. rule-ldp-cls-table-per-feature-type
This rule states that all feature type classifiers are mapped to a separate table. I.e., for classes with superclasses, each of them will have a separate table. Each feature will have a row in each of those tables, all with the same identifier.
Note that this rule is a requirement, if the schema includes feature associations that involve non-abstract feature types.
Parameter(s): none
3.2.10. rule-ldp-cls-id-field
Create an id field for each feature or data type table
Parameter(s): none
3.2.11. rule-ldp-cls-generate-codelist
Try to process also the code list values and add a code list file to the configuration.
Parameter(s): none
3.2.12. rule-ldp-cls-oneo-metadata
Add OSIRIS-Neo metadata fields in feature tables. This assumes the following fields in each feature table:
-
erstelltvon character varying(255)
-
erstelltam timestamp without time zone
-
geaendertvon character varying(255)
-
geaendertam timestamp without time zone
Parameter(s): none
3.3. Parameters
3.3.1. outputDirectory
Required / Optional: optional
Type: String
Default Value: the current directory
Explanation: The directory where the configuration files will be created.
Applies to Rule(s): none – default behaviour
3.3.2. serviceId
Required / Optional: optional
Type: String (no whitespace etc.)
Default Value: name of the XML namespace prefix of a selected schema; or "fixme", if none is specified
Explanation: The id of this service. The id is used as the last step in the base URI of the service.
Applies to Rule(s): none – default behaviour
3.3.3. serviceLabel
Required / Optional: optional
Type: String
Default Value: Alias or name of a selected schema; or "Some Dataset", if none is specified
Explanation: Human readable label of this service.
Applies to Rule(s): none – default behaviour
3.3.4. serviceDescription
Required / Optional: optional
Type: String
Default Value: Documentation or the definition of a selected schema; or an empty string, if none is specified
Explanation: Description of this service.
Applies to Rule(s): none – default behaviour
3.3.5. serviceVersion
Required / Optional: optional
Type: String
Default Value: "1.0.0"
Explanation:Version of this service API.
Applies to Rule(s): none – default behaviour
3.3.6. secured
Required / Optional: optional
Type: Boolean ("true" or "false")
Default Value: false (not secured)
Explanation: If "true", the service is marked as secured.
Applies to Rule(s): none – default behaviour
3.3.7. primaryKeyField
Required / Optional: optional
Type:String
Default Value: "id"
Explanation: Name of the field in tables used for the primary key
Applies to Rule(s):
-
rule-ldp-cls-id-field
3.3.8. foreignKeySuffix
Required / Optional: optional
Type: String
Default Value: "_id"
Explanation: Suffix of the foreign key field in tables
Applies to Rule(s):
-
rule-ldp-cls-id-field
3.3.9. fieldNameMaxLength
Required / Optional: optional
Type: Integer
Default Value: 60
Explanation:Maximum length of field names
Applies to Rule(s):
-
rule-ldp-all-names-max-length
3.3.10. geometryTableName
Required / Optional: optional
Type: String
Default Value: "geom", if there is a separate table for all geometries
Explanation: By default, it is assumed that geometry fields are part of the feature tables. However, if all geometries are in a separate table, specify the name of that table.
Applies to Rule(s):
-
rule-ldp-prop-separate-geometry-table
3.3.11. geometryFieldName
Required / Optional: optional
Type: String
Default Value: "geom", if there is a separate table for all geometries
Explanation: By default, it is assumed that geometry fields are part of the feature tables. However, if all geometries are in a separate table, specify the field name of the geometry in that table.
Applies to Rule(s):
-
rule-ldp-prop-separate-geometry-table
3.3.12. templateNtoMTable
Required / Optional: optional
Type: String
Default Value: "{{class}}_2_{{property}}"
Explanation: By default, all intermediate tables capable of representing properties that could be n-to-m relations are called "{{class}}_2_{{property}}" where "class" is the name of the table representing objects that have the property and "property" would be the standard field name of the property according to the encoding rule.
That table will include foreign keys to the identifier of the "class" table and the table that represents the objects / data structures that are the values of the property.
Applies to Rule(s):
-
rule-ldp-prop-all-featuretype-relations-as-n-to-m-relations
-
rule-ldp-prop-all-datatype-relations-as-n-to-m-relations
3.3.13. template1toNTable
Required / Optional: optional
Type: String
Default Value: "{{class}}_{{property}}"
Explanation: By default, all dependent tables capable of representing properties that could be 1-to-n relations are called "{{class}}_{{property}}" where "class" is the name of the table representing objects that have the property and "property" would be the standard field name of the property according to the encoding rule.
That table will include a foreign key to the identifier of the "class" table.
Applies to Rule(s):
-
rule-ldp-prop-multiple-single-values-as-1-to-n-relations
3.3.14. rootFeatureTable
Required / Optional: optional
Type: String
Default Value: "root"
Explanation: By default, the collection to use as the target for a feature reference is determined by collection the feature is associated with. However, for associations to an abstract (root) feature type, this is unclear.
In that case, rule rule-ldp-cls-table-per-feature-type is required and the name of a table that includes a row for each feature in the dataset must be provided.
That table must include a field that has the collection name of the feature as its value. See parameter rootCollectionField.
For example, if the table name is "root" and the field name is "collection" then this parameter will be "root" and rootCollectionField will be "collection". These are also the default values.
Applies to Rule(s):
-
rule-ldp-cls-table-per-feature-type
3.3.15. rootCollectionField
Required / Optional: optional
Type: String
Default Value: "collection"
Explanation: By default, the collection to use as the target for a feature reference is determined by collection the feature is associated with. However, for associations to an abstract (root) feature type, this is unclear.
In that case, rule rule-ldp-cls-table-per-feature-type is required and the name of a table that includes a row for each feature in the dataset must be provided. See parameter rootFeatureTable. That table must include a field that has the collection name of the feature as its value. For example, if the table name is "root" and the field name is "collection" then this parameter will be "collection" and parameter rootFeatureTable will be "root". These are also the default values.
Applies to Rule(s):
-
rule-ldp-cls-table-per-feature-type
3.3.16. filterableFields
Required / Optional: optional
Type: String
Default Value: none
Explanation: By default, only the geometry fields are set as "filterable". To mark other fields that are represented by a field (string, number or date) as filterable, specify them in a comma-separated list where each value uses the template "{{tablename}}.{{fieldname}}" where "tablename" is the name of the table that includes the filterable property (note that for nested data structures this is not the feature table) and "fieldname" is the name of the field. Example: "table1.field1,table2.field2".
Applies to Rule(s): none - default behaviour
3.3.17. htmlLabels
Required / Optional: optional
Type: String
Default Value: primary key fields
Explanation: By default, the field that is the primary key is used as the label of a feature in HTML. To set the label for a feature type to another field, specify them in a comma-separated list where each value uses the template "{{tablename}}.{{fieldlabel}}" where "tablename" is the name of the feature table that includes the property and "fieldlabel" is the label of the field. Example: "table1.Field 1,table2.Field 2". Use "*" as the table name to select a field for all feature types. Example: "*.Full Name".
Applies to Rule(s): none - default behaviour
3.3.18. featureTypes
Required / Optional: optional
Type: String
Default Value: all feature types
Explanation: By default, the configuration is generated for all feature types. To reduce the configuration to a subset of feature types, specify them in a comma-separated list.
Applies to Rule(s): none - default behaviour
3.3.19. taggedValueForCodeListUrl
Required / Optional: optional
Type: String
Default Value: 'codeList'
Explanation: By default, code lists are accessed using the standard 'codeList' tagged value. To support other UML profiles, another tagged value may be specified that is not normalized to 'codeList'.
Applies to Rule(s):
-
rule-ldp-cls-generate-codeList
3.3.20. enablePropertiesReportable
Required / Optional: optional
Type: String
Default Value: all properties
Explanation: By default, all properties are included in the output. To support publishing only a subset, only the properties are enabled in the configuration with a tagged value 'reportable' and one of the values in this parameter. Specify the values in a comma-separated list, e.g. 'true,internal'.
Applies to Rule(s): none - default behaviour
3.3.21. trigger_onDelete
Required / Optional: optional
Type: String
Default Value: no trigger on delete
Explanation: If a feature is deleted using the HTTP DELETE method, additional SQL statements can be executed in the database. Example: "DELETE FROM othertable WHERE objectid={{id}}" where "{{id}}" is the value of the "id" column in the table of the deleted feature. Specify the values in a semicolon-separated list.
Applies to Rule(s): none - default behaviour
3.4. MapEntries
MapEntry elements are used in ldproxy configurations to map types (UML classifiers) in the application schema to ldproxy. The name of the type is provided as usual in the XML attribute "type". A map entry can be restricted to certain encoding rules in the XML attribute "rule"; "*" matches all encoding rules.
The XML attribute "targetType" specifies the type to use for the UML classifier in the applicable encoding rules. ldproxy supports:
-
STRING
-
DATE
-
NUMBER
-
BOOLEAN
-
GEOMETRY
The XML attribute "param" can be used to provide additional information in a semicolon separated list. Each value in the list is a key value pair separated by a colon:
-
Values with key "category" are used to identify spatial / temporal properties (the bbox / time parameter apply to this property). GEOMETRY values should have a value "SPATIAL" and DATE values should have a value "TEMPORAL".
-
Values with key "format" are used to specify a format for JSON values according to the use of "format" for strings in JSON Schema.
-
Values with key "htmlformat" are used to specify a format for rendering a value in HTML, typically used for date/time values.
-
Values with key "jsongeometry" are used for GEOMETRY types to declare the GeoJSON geometry type. Valid values are:
-
POINT
-
MULTI_POINT
-
LINE_STRING
-
MULTI_LINE_STRING
-
POLYGON
-
MULTI_POLYGON
-
GEOMETRY_COLLECTION
-
GENERIC
-
-
Values with key "htmlgeometry" are used for GEOMETRY types to declare the schema.org geometry type. Valid values are:
-
POINT
-
LINE_STRING
-
POLYGON
-
GENERIC
-
Typical standard map entries are provided in https://shapechange.net/resources/config/StandardLdproxyMapEntries.xml.
4. Configuration Example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<Target class="de.interactive_instruments.ShapeChange.Target.Ldproxy.Config"
mode="enabled" inputs="INPUT">
<targetParameter name="outputDirectory" value="results/api"/>
<targetParameter name="sortedOutput" value="true"/>
<targetParameter name="serviceId" value="xyz"/>
<targetParameter name="serviceLabel" value="Dataset XYZ"/>
<targetParameter name="serviceDescription" value="This API ..."/>
<targetParameter name="serviceVersion" value="0.0.1"/>
<targetParameter name="secured" value="true"/>
<targetParameter name="primaryKeyField" value="id"/>
<targetParameter name="foreignKeySuffix" value="_id"/>
<targetParameter name="fieldNameMaxLength" value="60"/>
<targetParameter name="geometryTableName" value="geom"/>
<targetParameter name="geometryFieldName" value="geom"/>
<targetParameter name="templateNtoMTable" value="{{class}}_2_{{property}}"/>
<targetParameter name="template1toNTable" value="{{class}}_{{property}}"/>
<targetParameter name="rootFeatureTable" value="root"/>
<targetParameter name="rootCollectionField" value="collection"/>
<targetParameter name="featureTypes" value="building, parcel"/>
<targetParameter name="filterableFields" value="root.identifier, root.timestamp, building.name, parcel.municipality"/>
<targetParameter name="htmlLabels" value=".identifier"/>
<targetParameter name="trigger_onDelete" value="DELETE FROM othertable WHERE objectid={{id}}"/>
<targetParameter name="defaultEncodingRule" value="ldp"/>
<rules>
<EncodingRule name="ldp">
<rule name="rule-ldp-cls-non-abstract-feature-types-as-collection"/>
<rule name="rule-ldp-cls-table-per-feature-type"/>
<rule name="rule-ldp-cls-id-field"/>
<rule name="rule-ldp-cls-generate-codelist"/>
<rule name="rule-ldp-all-names-in-lowercase"/>
<rule name="rule-ldp-all-names-max-length"/>
<rule name="rule-ldp-prop-all-datatype-relations-as-n-to-m-relations"/>
<rule name="rule-ldp-prop-all-featuretype-relations-as-n-to-m-relations"/>
<rule name="rule-ldp-prop-multiple-single-values-as-1-to-n-relations"/>
<rule name="rule-ldp-prop-separate-geometry-table"/>
<rule name="rule-ldp-prop-all-codelist-values-as-strings"/>
<rule name="rule-ldp-cls-oneo-metadata"/>
</EncodingRule>
</rules>
<xi:include href="https://shapechange.net/resources/config/StandardLdproxyMapEntries.xml"/>
</Target>