FeatureServer
DEPRECATED - now migrated to a package in the Koop monorepo. Update any npm
usage to @koopjs/featureserver
.
An open source implementation of the GeoServices specification
Usage
This is meant to be used as a plugin to Express
Example server
const express = require('express')
const app = express()
const FeatureServer = require('featureserver')
const cache = require('cache')
const handler = (req, res) => {
cache.get(, (err, data) => {
if (err) res.status(500).json({error: err.message})
else FeatureServer.route(req, res, data)
})
}
const routes = ['/FeatureServer', '/FeatureServer/layers', '/FeatureServer/:layer', '/FeatureServer/:layer/:method']
routes.forEach(route => {
app.route(route)
.get(handler)
.post(handler)
})
Setting defaults at runtime
FeatureServer allows several defaults to be set at runtime via Express's app.locals
method. Specifically, you will need to set:
app.locals.config = {
featureServer: {
}
}
If you are using FeatureServer as part of a Koop instance, the equivalent of Express's app.locals
is koop.server.locals
.
The follow properties can be set at runtime with the noted method:
app.locals.config = {
featureServer: {
currentVersion: 11.01,
fullVersion: '11.0.1',
serviceDescription: 'default service description',
description: 'default layer description'
}
}
API
FeatureServer.route
Pass in an incoming request object
, an outgoing response object
, a geojson
object, and options
and this function will route and return a geoservices compliant response
- Supports: '/FeatureServer', '/FeatureServer/layers', '/FeatureServer/:layer', '/FeatureServer/:layer/:method'
- Note: only
query
, info
, and generateRenderer
are supported methods at this time.
FeatureServer.route(req, res, data, options)
- data is either a geojson object extended with some additional properties or an object with a layers property which an array of extended geojson objects. These properties are optional and can be used to provide more specific metadata or to shortcut the built in filtering mechanism.
e.g.
{
type: 'FeatureCollection'
features: Array,
statistics: Object,
metadata: {
id: number,
name: String,
description: String,
copyrightText: String,
extent: Array,
displayField: String,
geometryType: String
idField: String,
maxRecordCount: Number,
limitExceeded: Boolean,
timeInfo: Object,
transform: Object,
renderer: Object,
defaultVisibility: boolean,
minScale: number,
maxScale: number,
fields: [
{
name: String,
type: String,
alias: String,
}
]
},
capabilities: {
quantization: Boolean
},
filtersApplied: {
all: Boolean
geometry: Boolean,
where: Boolean,
offset: Boolean
limit: Boolean
projection: Boolean
}
count: Number
}
or
{
layers: [
{
type: 'FeatureCollection'
...
},
{
type: 'FeatureCollection'
...
}
]
- options is an object that dictates method actions. See
FeatureServer.query
and FeatureServer.generateRenderer
for more details.
FeatureServer.query
Pass in geojson
and options
(a valid geoservices query object), and the function will perform the query and return a valid geoservices query object. The in addition to input statistics: {}
, following is an example of all query options
that can be passed into the query route: '/FeatureServer/:layer/query'
e.g.
const options = {
where: `1=1`,
objectIds: '1,2,3',
geometry: {
xmin: -110, ymin: 30, xmax: -106, ymax: 50,
spatialReference: { wkid: 4326 },
},
geometryType: 'esriGeometryEnvelope',
spatialRel: 'esriSpatialRelContains',
outFields: '*',
returnGeometry: true,
outSR: 102100,
returnIdsOnly: true,
returnCountOnly: true,
orderByFields: 'Full/Part_COUNT DESC',
groupByFieldsForStatistics: 'Full/Part',
outStatistics: {
statisticType: 'count',
onStatisticField: '<field>',
outStatisticFieldName: 'name'
},
returnDistinctValues: true,
resultOffset: 0,
resultRecordCount: 0,
f: 'pjson'
}
FeatureServer.query(geojson, options)
FeatureServer.serverInfo
Generate version 10.51
Geoservices server info
const server = {
description: String
copyrightText: String
maxRecordCount: Number
hasStaticData: Boolean
hasAttachments: Boolean
layers: [{
type: 'FeatureCollection',
metadata: {
id: number,
name: String,
description: String,
extent: Array,
displayField: String,
idField: String,
geometryType: String,
maxRecordCount: Number,
limitExceeded: Boolean,
timeInfo: Object,
renderer: Object,
defaultVisibility: boolean,
minScale: number,
maxScale: number
}
features: [
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [125.6, 10.1]
},
properties: {
name: 'Dinagat Islands'
}
}]
}
}],
tables: [{
type: 'FeatureCollection',
metadata: {
}
}],
relationships: [{
id: number,
name: String,
}]
}
FeatureServer.serverInfo(server)
FeatureServer.layerInfo
Generate version 10.51
Geoservices information about a single layer
FeatureServer.layerInfo(geojson, options)
Note that the layer info is modified with properties metadata
and capabilites
found at the top-level of the GeoJSON object.
GeoJSON property | Layer info result |
---|
metadata.id | overrides default |
metadata.name | overrides default |
metadata.description | overrides default |
metadata.geometryType | overrides value determined from data |
metadata.extent | overrides value determined from data |
metadata.timeInfo | overrides default |
metadata.maxRecordCount | overrides default (2000) |
metadata.displayField | overrides default (OBJECTID ) |
metadata.objectIdField | overrides default (OBJECTID ) |
metadata.hasStaticData | overrides default (false ) |
metadata.hasAttachments | overrides default (false ) |
metadata.renderer | overrides default |
metadata.defaultVisibility | overrides default |
metadata.minScale | overrides default |
metadata.maxScale | overrides default |
metadata.relationships | overrides default |
capabilities.extract | when set to true , Extract added to capabilites (e.g., capabilities: "Query,Extract" ) |
capabilities.quantization | when set to true , supportsCoordinatesQuantization: true |
metadata.relationships
This defined the server managed relationships for the layer
e.g.
const metadata = {
relationships: [{
id: number,
name: String,
relatedTableId: number,
cardinality: String,
role: String,
keyField: String,
composite: Boolean
}]
}
FeatureServer.layers
Generate version 10.51
Geoservices information about one or many layers
Can pass a single geojson object or an array of geojson objects
FeatureServer.layers(geojson, options)
FeatureServer.generateRenderer
Pass in geojson
and options
, and the function will return a valid generateRenderer object. Two classificationDef
classification types are supported, classBreaksDef and uniqueValueDef.
classBreaksDef is used to classify numeric data based on a number of breaks and a statistical method. Features can also be normalized before being classified. uniqueValueDef is used to classify data based on a unique field(s). If classification breaks are not supplied through in statistics
, they will be generated using classificationDef
options. The output is a generateRenderer object.
classBreaksDef
In addition to class breaks as input statistics: []
, the following is an example of all classBreaksDef options
that can be passed into the generateRenderer route: '/FeatureServer/:layer/generateRenderer'
e.g.
const options = {
*'classificationDef': {
*'type': 'classBreaksDef',
*'classificationField': '<field1>',
*'classificationMethod': 'esriClassifyEqualInterval' | 'esriClassifyNaturalBreaks' | 'esriClassifyQuantile' | 'esriClassifyStandardDeviation',
*'breakCount': 9,
'normalizationType': 'esriNormalizeByField' | 'esriNormalizeByLog' | 'esriNormalizeByPercentOfTotal',
'normalizationField': '<field2>'
'baseSymbol': {
'type': 'esriSMS',
'style': 'esriSMSCircle',
'width': 2
},
'colorRamp': {
'type': 'algorithmic',
'fromColor': [115,76,0,255],
'toColor': [255,25,86,255],
'algorithm': 'esriHSVAlgorithm'
}
},
'where': '<field2> > 39'
}
FeatureServer.generateRender(geojson, options)
*required
Output:
{
type: 'classBreaks',
field: '<field1>',
classificationMethod: 'esriClassifyEqualInterval',
minValue: 0,
classBreakInfos: [
{
classMinValue: 0,
classMaxValue: 5,
label: '0-5',
description: '',
symbol: {
type: 'esriSMS',
style: 'esriSMSCircle',
width: 2,
color: [115, 76, 0]
}
},
{
classMinValue: 6,
classMaxValue: 11,
label: '6-11',
description: '',
symbol: {
type: 'esriSMS',
style: 'esriSMSCircle',
width: 2,
color: [156, 67, 0]
}
},
...
]
}
uniqueValueDef
The following is an example of all uniqueValueDef options
that can be passed into the generateRenderer route: '/FeatureServer/:layer/generateRenderer'
e.g.
const options = {
*'classificationDef': {
*'type': 'uniqueValueDef',
*'uniqueValueFields': ['Genus', '<field2>', '<field3>'],
*'fieldDelimiter': ', '
'baseSymbol': {
'type': 'esriSMS',
'style': 'esriSMSCircle',
'width': 2
},
'colorRamp': {
'type': 'algorithmic',
'fromColor': [115,76,0,255],
'toColor': [255,25,86,255],
'algorithm': 'esriHSVAlgorithm'
}
},
'where': 'latitude > 39'
}
FeatureServer.generateRender(geojson, options)
*required
Output:
{
type: 'uniqueValue',
field1: 'Genus',
field2: '',
field3: '',
fieldDelimiter: ', ',
defaultSymbol: {},
defaultLabel: '',
uniqueValueInfos: [
{
value: 'MAGNOLIA',
count: 5908,
label: 'MAGNOLIA',
description: '',
symbol: {
type: 'esriSMS',
style: 'esriSMSCircle',
width: 2,
color: [115, 76, 0]
}
},
{
value: 'QUERCUS',
count: 12105,
label: 'QUERCUS',
description: '',
symbol: {
type: 'esriSMS',
style: 'esriSMSCircle',
width: 2,
color: [116, 76, 0]
}
},
...
]
FeatureServer.authenticate
Pass in an outgoing response object and an authentication success object and this function will route and return a formatted authentication success response.
FeatureServer.authenticate(res, auth, ssl = false)
auth
is the result of a successful authentication attempt that returns a token and expiration timessl
is a boolean flag indicating if token should always be passed back via HTTPS. Defaults to false
e.g.,
const auth = {
"token":"elS39KU4bMmZQgMXDuswgA14vavIp4mfpiqcWSr0qM6q4dFguTnnHddWqbpK5Mc3HsCN8XghlwawUUYApOOcxKNyg_9WqTofChJXxxD058_rL1HZkM5PDhUOh9YYQn1K",
"expires":1524508236322
}
FeatureServer.authenticate(res, auth)
{
"token":"elS39KU4bMmZQgMXDuswgA14vavIp4mfpiqcWSr0qM6q4dFguTnnHddWqbpK5Mc3HsCN8XghlwawUUYApOOcxKNyg_9WqTofChJXxxD058_rL1HZkM5PDhUOh9YYQn1K",
"expires":1524508236322,
ssl: false
}
FeatureServer.error.authorize
Pass in an outgoing response object and this function will route and return a formattted authorization error.
FeatureServer.error.authorize(res)
{
"error": {
"code": 499,
"message": "Token Required",
"details": []
}
}
FeatureServer.error.authenticate
Pass in an outgoing response object and this function will route and return a formatted authentication error.
FeatureServer.error.authenticate(res)
{
"error": {
"code": 400,
"message": "Unable to generate token.",
"details": ["Invalid username or password."]
}
}
Unreleased
FeatureServer.queryRelatedRecords
Pass in geojson
and options
, and the function will return a valid queryRelatedRecords object. Required attributes within options
are objectIds
and relationshipId
.
The geojson
should be in the special FeatureCollection of FeatureCollections format to show the relationship between requested Features within the layer/table and the referenced relatinoship's features.
e.g.
const geojson = {
"type": "FeatureCollection",
"features": [
{
"type": "FeatureCollection",
"properties": {
"OBJECTID": 37
},
"features": [
{
"type": "Feature",
"geometry": {...},
"properties": {...}
}
]
}
]
}
const options = {
objectIds: "37, 462",
relationshipId: 4,
}
FeatureServer.queryRelatedRecords(geojson, options)
Output:
{
"geometryType": "esriGeometryPolygon",
"spatialReference": {
"wkid": 4267
},
"fields": [
{
"name": "OBJECTID",
"type": "esriFieldTypeOID",
"alias": "OBJECTID"
},
{
"name": "FIELD1",
"type": "esriFieldTypeString",
"alias": "FIELD1",
"length": 25
}
],
"relatedRecordGroups": [
{
"objectId": 37,
"relatedRecords": [
{
"attributes": {
"OBJECTID": 5540,
"FIELD1": "1000147595"
},
"geometry": {...}
}
]
}
]
}