Overview

Hawkore's Advanced Apache Ignite Spatial module provides spatial indexing/queries capabilities.

Features:

  • Supports persistence on disk, without lost on-memory computation layer performance, minimizing node startup.
  • Secure implementation for Apache Ignite EncryptionSpi as Spatial index does not store cache value. Please note that spatial coordinates and cache key will not be encrypted for performance reasons, so never store sensitive data on cache key.

Importing maven dependency:

Add repository and the Maven dependency below to your pom.xml file to make sure that the module is included into your application:

<dependency>
    <groupId>com.hawkore.libs.ignite</groupId>
    <artifactId>hk-ignite-geospatial</artifactId>
    <version>2.8.1-hk</version>
</dependency>
<repositories>
    <repository>
        <id>Hawkore Repository</id>
        <url>https://repository.hawkore.com/maven2/</url>
    </repository>
</repositories>

Indexing

The Spatial module is usable only for the objects of org.locationtech.jts type.

To configure indexes and/or queryable​ fields of geometric types, you need to use the same approached that is used for non-spatial types. First, the indexes can be defined with the help of org.apache.ignite.cache.QueryEntity which is convenient for Spring XML based configurations. Second, you can achieve the same outcome by annotating indexes with @QuerySqlField annotations which will be converted QueryEntities internally.

<bean class="org.apache.ignite.configuration.CacheConfiguration">
    <property name="name" value="mycache"/>
    <!-- Configure query entities -->
    <property name="queryEntities">
        <list>
            <bean class="org.apache.ignite.cache.QueryEntity">
                <property name="keyType" value="java.lang.Integer"/>
                <property name="valueType" value="org.apache.ignite.examples.MapPoint"/>

                <property name="fields">
                    <map>
                        <entry key="coords" value="org.locationtech.jts.geom.Geometry"/>
                    </map>
                </property>

                <property name="indexes">
                    <list>
                        <bean class="org.apache.ignite.cache.QueryIndex">
                            <constructor-arg value="coords"/>
                        </bean>
                    </list>
                </property>
            </bean>
        </list>
    </property>
</bean>
/**
 * Map point with indexed coordinates.
 */
private static class MapPoint {
    /** Coordinates. */
    @QuerySqlField(index = true)
    private Geometry coords;

    /**
     * @param coords Coordinates.
     */
    private MapPoint(Geometry coords) {
        this.coords = coords;
    }
}

Searching

Spatial queries' capabilities, as well as available functions and operands, are defined by Simple Features Specification for SQL.

Searches supports the intersection operation (&&) of the specification with the usage of JTS Topology Suite.

SELECT count(*) 
FROM "tweets".tweet USE INDEX(tweet_place_idx)
where
place && 'POLYGON((-80.14247278479951 25.795756477689594, -66.11315588592869 18.47447597127288, -64.82713517019887 32.33019640254669, -80.14247278479951 25.795756477689594))';

You could use full H2Gis spatial functions suite by adding Hawkore's H2Gis plugin for Apache Ignite to you project.

Example: Search for tweets within a distance of 6Km from POINT (-3.703790 40.416775) using H2Gis spatial functions

SELECT ID, BODY, PLACE,
PUBLIC.ST_DISTANCE_SPHERE(place, 'POINT (-3.703790 40.416775)')/1000 as distance_km
FROM "tweets".tweet USE INDEX (tweet_place_idx)
WHERE
place && public.ST_GEOCIRCLE('POINT (-3.703790 40.416775)', 6000) 
and countryCode = 'ES' 
order by distance_km desc
limit 100;

Result:

+------+------------------------------------+------------------------------------------------+-------------------+
|   ID |                               BODY |                                          PLACE |        DISTANCE_KM|
+======+====================================+================================================+===================+
|73191 | big data gives organizations 73191 |  POINT (-3.6442361407780948 40.37040139512792) |  7.212915443266619|
+------+------------------------------------+------------------------------------------------+-------------------+
|63858 | big data gives organizations 63858 |   POINT (-3.643352725561062 40.46116642472653) |   7.10820463443938|
+------+------------------------------------+------------------------------------------------+-------------------+
|13448 | big data gives organizations 13448 |   POINT (-3.636347681677335 40.45222253018613) |   6.93670477740279|
+------+------------------------------------+------------------------------------------------+-------------------+
|96152 | big data gives organizations 96152 |  POINT (-3.6654897719289026 40.36390665055403) |  6.714217715121237|
+------+------------------------------------+------------------------------------------------+-------------------+
|65159 | big data gives organizations 65159 | POINT (-3.7640315124351558 40.446409673786384) |  6.070951684524529|
+------+------------------------------------+------------------------------------------------+-------------------+
| 8378 |  big data gives organizations 8378 |  POINT (-3.772904879942281 40.421784009278184) |  5.877374868190893|
+------+------------------------------------+------------------------------------------------+-------------------+
|37155 | big data gives organizations 37155 |  POINT (-3.6380660941530323 40.42141155416504) |  5.587711868859234|
+------+------------------------------------+------------------------------------------------+-------------------+
|58808 | big data gives organizations 58808 |   POINT (-3.729606286597199 40.37173141817499) |  5.464997075063737|
+------+------------------------------------+------------------------------------------------+-------------------+
|77380 | big data gives organizations 77380 |   POINT (-3.706806844939874 40.36775414863283) |   5.45686194450224|
+------+------------------------------------+------------------------------------------------+-------------------+
|12933 | big data gives organizations 12933 | POINT (-3.6649634836220173 40.451800390063305) |  5.095776716778619|
+------+------------------------------------+------------------------------------------------+-------------------+
|49273 | big data gives organizations 49273 |  POINT (-3.7104281499138283 40.37563441659352) |  4.609040190217024|
+------+------------------------------------+------------------------------------------------+-------------------+
|10044 | big data gives organizations 10044 | POINT (-3.7436340044866285 40.399516380751784) | 3.8812000456430584|
+------+------------------------------------+------------------------------------------------+-------------------+
|56307 | big data gives organizations 56307 |    POINT (-3.66013803142915 40.40659292511337) | 3.8653120435493484|
+------+------------------------------------+------------------------------------------------+-------------------+
|69222 | big data gives organizations 69222 |  POINT (-3.7266549086214713 40.43754064232654) | 3.0128789379494414|
+------+------------------------------------+------------------------------------------------+-------------------+
| 8580 |  big data gives organizations 8580 |   POINT (-3.728757264872743 40.41049598199824) | 2.2261074062240658|
+------+------------------------------------+------------------------------------------------+-------------------+

Why does query return points out of geo-circle filter condition?

As you can see, there are points out of the circle, you may think this is a bug, however it's not. This is default behavior of h2-spatial implementation that uses the envelope of geometry filter condition to make indexed searches.

geo circle

Enforce geometry filter condition

In general, with h2-spatial queries, you will need to use ST_WITHIN, ST_INTERSECTS or ST_DISTANCE_SPHERE (only if you search by radius) as additional filter to ensure that returned points are really within geometry filter condition. This is not necessary on lucene-spatial queries.

Example: Search for tweets within a geo-circle of 6Km from POINT (-3.703790 40.416775)

SELECT ID, BODY, PLACE,
PUBLIC.ST_DISTANCE_SPHERE(place, 'POINT (-3.703790 40.416775)')/1000 as distance_km
FROM "tweets".tweet USE INDEX (tweet_place_idx)
WHERE
place && public.ST_GEOCIRCLE('POINT (-3.703790 40.416775)', 6000) 
and PUBLIC.ST_WITHIN(place, public.ST_GEOCIRCLE('POINT (-3.703790 40.416775)', 6000))
and countryCode = 'ES' 
order by distance_km desc
limit 100;
SELECT ID, BODY, PLACE,
PUBLIC.ST_DISTANCE_SPHERE(place, 'POINT (-3.703790 40.416775)')/1000 as distance_km
FROM "tweets".tweet USE INDEX (tweet_place_idx)
WHERE
place && public.ST_GEOCIRCLE('POINT (-3.703790 40.416775)', 6000) 
and PUBLIC.ST_INTERSECTS(place, public.ST_GEOCIRCLE('POINT (-3.703790 40.416775)', 6000))
and countryCode = 'ES' 
order by distance_km desc
limit 100;
SELECT ID, BODY, PLACE,
PUBLIC.ST_DISTANCE_SPHERE(place, 'POINT (-3.703790 40.416775)')/1000 as distance_km
FROM "tweets".tweet USE INDEX (tweet_place_idx)
WHERE
place && public.ST_GEOCIRCLE('POINT (-3.703790 40.416775)', 6000) 
and PUBLIC.ST_DISTANCE_SPHERE(place, 'POINT (-3.703790 40.416775)') <= 6000
and countryCode = 'ES' 
order by distance_km desc
limit 100;
+------+------------------------------------+------------------------------------------------+-------------------+
|   ID |                               BODY |                                          PLACE |        DISTANCE_KM|
+======+====================================+================================================+===================+
| 8378 |  big data gives organizations 8378 |  POINT (-3.772904879942281 40.421784009278184) |  5.877374868190893|
+------+------------------------------------+------------------------------------------------+-------------------+
|37155 | big data gives organizations 37155 |  POINT (-3.6380660941530323 40.42141155416504) |  5.587711868859234|
+------+------------------------------------+------------------------------------------------+-------------------+
|58808 | big data gives organizations 58808 |   POINT (-3.729606286597199 40.37173141817499) |  5.464997075063737|
+------+------------------------------------+------------------------------------------------+-------------------+
|77380 | big data gives organizations 77380 |   POINT (-3.706806844939874 40.36775414863283) |   5.45686194450224|
+------+------------------------------------+------------------------------------------------+-------------------+
|12933 | big data gives organizations 12933 | POINT (-3.6649634836220173 40.451800390063305) |  5.095776716778619|
+------+------------------------------------+------------------------------------------------+-------------------+
|49273 | big data gives organizations 49273 |  POINT (-3.7104281499138283 40.37563441659352) |  4.609040190217024|
+------+------------------------------------+------------------------------------------------+-------------------+
|10044 | big data gives organizations 10044 | POINT (-3.7436340044866285 40.399516380751784) | 3.8812000456430584|
+------+------------------------------------+------------------------------------------------+-------------------+
|56307 | big data gives organizations 56307 |    POINT (-3.66013803142915 40.40659292511337) | 3.8653120435493484|
+------+------------------------------------+------------------------------------------------+-------------------+
|69222 | big data gives organizations 69222 |  POINT (-3.7266549086214713 40.43754064232654) | 3.0128789379494414|
+------+------------------------------------+------------------------------------------------+-------------------+
| 8580 |  big data gives organizations 8580 |   POINT (-3.728757264872743 40.41049598199824) | 2.2261074062240658|
+------+------------------------------------+------------------------------------------------+-------------------+

Tip

Take a look to Affinity Collocation to improve search performance.