Skip to main content

What's New in MarkLogic 11

Optic Geospatial

In MarkLogic 11, geospatial data can now be indexed using Template Driven Extraction and queried using the Optic engine, SPARQL, and SQL. This builds on the the rich geospatial capabilities already built into MarkLogic, and adds flexibility to how geospatial indexes are built and queried. It also increases interoperability by implementing subsets of the OGC (Open Geospatial Consortium) standard for SQL queries and OGC (Open Geospatial Consortium) GeoSPARQL standard for SPARQL queries.

TDE Indexing

The TDE "scalar-type" has been extended to support the following new scalar types: point , box , circle , linestring , polygon , complexPolygon , and a generic region. Using these types, geospatial data can be selected for indexing using standard TDE XPath. 

The TDE value expression must extract and/or construct content into one of the formats currently supported by the MarkLogic geospatial region index. See Geospatial Region Queries and Indexes in the Search Developer's Guide for more details.

The following code excerpt shows a fragment of a TDE that creates a row for each building and selects the building boundary polygon from the "boundary" element or property:

"rows": [      
  {        
    "schemaName": "City",
    "viewName": "Buildings",
    "columns": [
      {
        "name": "name",
        "scalarType": "string",            
        "val": "name"      
      },
      {            
        "name": "boundary", 
        "scalarType": "polygon",
        "val": "cts:polygon(boundary)",
        "coordinateSystem": "wgs84"
       }
     ]
  }        
]

See Template Driven Extraction in the Application Developer's Guide for more details on how to to use TDE.

Optic Query

Using the view from the above template, the Optic API can be used to query for buildings that intersect a circle with a center point and radius:

const op = require('/MarkLogic/optic');
op.fromView('City', 'Buildings')
  .where(
    op.geo.intersects(
      op.col('boundary'), 
      geo.circlePolygon(cts.circle(10, cts.point(20.7, -156.4)), 0.5)
    )
  )
  .result()

The following functions have been added and are available under the existing "geo" namespace:

Function

Returns

op.geo.crosses(region1 as cts.region, region2 as cts.region)

true  if region1  crosses region2 , false  otherwise

op.geo.within(region1 as cts.region, region2 as cts.region)

true  if region1  is within region2 , false  otherwise

op.geo.contains(region1 as cts.region, region2 as cts.region)

true  if region1  contains region2 , false  otherwise

op.geo.overlaps(region1 as cts.region, region2 as cts.region)

true  if region1  overlaps region2 , false  otherwise

op.geo.equals(region1 as cts.region, region2 as cts.region)

true  if region1  equals region2 , false  otherwise

op.geo.disjoint(region1 as cts.region, region2 as cts.region)

true  if region1  is disjoint from region2 , false  otherwise

op.geo.intersects(region1 as cts.region, region2 as cts.region)

true  if region1  intersects region2 , false  otherwise

op.geo.touches(region1 as cts.region, region2 as cts.region)

true  if region1  touches region2 , false  otherwise

op.geo.covers(region1 as cts.region, region2 as cts.region)

true  if region1  covers region2 , false  otherwise

op.geo.coveredBy(region1 as cts.region, region2 as cts.region)

true  if region1  is covered by region2 , false  otherwise

SQL Query

Using the view from the above template, SQL can be used to query for buildings that intersect a circle with a center point and radius:

select * from Buildings where ST_Intersects(
  boundary,
  geo_circle_polygon(cts_circle(10, cts_point(20.7, -156.4)), 0.5)
)

The following functions from the OGC standard for SQL queries are supported in MarkLogic 11:

Function

Example SQL

Returns

ST_WKTToSQL()

select ST_WKTToSQL('POLYGON((0.39 -.10, 0.42 1.4, 0.5 2.0, 0.39 -0.10))');

cts:region()

ST_WKBToSQL()

select ST_WKBToSQL(geo_to_wkb('POINT(0.39 -0.10)'));

cts:region()

ST_AsText()

select ST_AsText(cts_point(0.39, -0.10));

string

ST_AsBinary()

select ST_AsBinary(cts_point(0.39, -0.10));

binary

ST_Crosses()

select * from polygons WHERE ST_Crosses(cts_point(0.39, -0.10), polygon_column)

boolean

ST_Within()

select * from polygons WHERE ST_Within(cts_point(0.39, -0.10), polygon_column) 

boolean

ST_Contains()

select * from polygons WHERE ST_Contains(cts_point(0.39, -0.10), polygon_column)

boolean

ST_Overlaps()

select * from polygons WHERE ST_Overlaps(cts_point(0.39, -0.10), polygon_column) 

boolean

ST_Equals()

select * from polygons WHERE ST_Equals(cts_point(0.39, -0.10), polygon_column) 

boolean

ST_Disjoint()

select * from polygons WHERE ST_Disjoint(cts_point(0.39, -0.10), polygon_column) 

boolean

ST_Intersects()

select * from polygons WHERE ST_Intersects(cts_point(0.39, -0.10), polygon_column) 

boolean

ST_Touches()

select * from polygons WHERE ST_Touches(cts_point(0.39, -0.10), polygon_column) 

boolean

ST_Covers()

select * from polygons WHERE ST_Covers(cts_point(0.39, -0.10), polygon_column) 

boolean

ST_CoveredBy()

select * from polygons WHERE ST_CoveredBy(cts_point(0.39, -0.10), polygon_column)

boolean

ST_Distance()

select ST_Distance(ST_WKTToSQL('POINT (30 10)'), point_column) from points

double

ST_Centroid()

select ST_Centroid(ST_WKTToSQL('POLYGON((0.39 -.10, 0.42 1.4, 0.5 2.0, 0.39 -0.10))'));

cts:point()

ST_PointOnSurface()

select ST_PointOnSurface(ST_WKTToSQL('POLYGON((0.39 -.10, 0.42 1.4, 0.5 2.0, 0.39 -0.10))'))

cts:point()

SPARQL Query

The following functions from the OGC (Open Geospatial Consortium) GeoSPARQL standard for SPARQL queries are supported in MarkLogic 11. These all use the SPARQL prefix: PREFIX geof: <http://www.opengis.net/def/function/geosparql/>:

Function

Returns

geof:sfCrosses($region1 as cts:region(), $region2 as cts:region(), $options as xs:string*)

true  if $region1  crosses $region2 , false  otherwise

geof:sfWithin($region1 as cts:region(), $region2 as cts:region(), $options as xs:string*)

true  if $region1  is within $region2 , false  otherwise

geof:sfContains($region1 as cts:region(), $region2 as cts:region(), $options as xs:string*)

true  if $region1  contains $region2 , false  otherwise

geof:sfOverlaps($region1 as cts:region(), $region2 as cts:region(), $options as xs:string*)

true  if $region1  overlaps $region2 , false  otherwise

geof:sfEquals($region1 as cts:region(), $region2 as cts:region(), $options as xs:string*)

true  if $region1  equals $region2 , false  otherwise

geof:sfDisjoint($region1 as cts:region(), $region2 as cts:region(), $options as xs:string*)

true  if $region1  is disjoint from $region2 , false  otherwise

geof:sfIntersects($region1 as cts:region(), $region2 as cts:region(), $options as xs:string*)

true  if $region1  intersects $region2 , false  otherwise

geof:sfTouches($region1 as cts:region(), $region2 as cts:region(), $options as xs:string*)

true  if $region1  touches $region2 , false  otherwise

geof:sfCovers($region1 as cts:region(), $region2 as cts:region(), $options as xs:string*)

true  if $region1  covers $region2 , false  otherwise

geof:sfCoveredBy($region1 as cts:region(), $region2 as cts:region(), $options as xs:string*)

true  if $region1  is covered by $region2 , false  otherwise

geof:distance($point1 as cts:point(), $point2 as cts:point(), $options as xs:string*)

The distance, in the units specified in $options (miles by default), between $point1 and $point2

The following functions are new built-in functions supported in MarkLogic 11:

Function

Description

geo.within()

Compares geospatial regions to see if they fulfill the 'within' DE-9IM relation.

geo.contains()

Compares geospatial regions to see if they fulfill the 'contains' DE-9IM relation.

geo.overlaps()

Compares geospatial regions to see if they fulfill the 'overlaps' DE-9IM relation.

geo.equals()

Compares geospatial regions to see if they fulfill the 'equals' DE-9IM relation.

geo.disjoint()

Compares geospatial regions to see if they fulfill the 'disjoint' DE-9IM relation.

geo.intersects()

Compares geospatial regions to see if they fulfill the 'intersects' DE-9IM relation.

geo.touches()

Compares geospatial regions to see if they fulfill the 'touches' DE-9IM relation.

geo.covers()

Compares geospatial regions to see if they fulfill the 'covers' DE-9IM relation.

geo.coveredBy()

Compares geospatial regions to see if they fulfill the 'covered by' DE-9IM relation.

Additional Information

See Query Geospatial Data in Optic for a detailed walkthrough of working with the new Optic Geospatial capability.

See Getting Started with Optic for more detail about working with the Optic API.

See Geospatial Search Applications in the Search Developer's Guide for more detail about working with geospatial data in MarkLogic.