GeoMesa 是开源的基于分布式计算系统的面向海量时空数据查询与分析的工具包,GeoMesa的定位是一个基于分布式数据库的用于海量空间数据处理的数据引擎(SDE),或者叫数据库中间件,目的在于使用户可以在分布式NoSql数据库中存储和管理海量空间数据。GeoMesa作为空间大数据处理套件,本身不存储数据,数据存储依赖底层的分布式数据库,如HBase,Accumulo等。

HBase Ganos是阿里云推出的一款包含管理「空间几何数据」、「时空轨迹」、「专题栅格」、「遥感影像」的时空大数据引擎系统。系统兼容开源GeoMesa、GeoServer等生态,内置了高效的时空索引算法、空间拓扑几何算法、遥感影像处理算法等,结合阿里云HBase强大的分布式存储能力以及Spark分析平台能力,可广泛应用于空间/时空/遥感大数据存储、查询、分析与数据挖掘场景

实验过程中遇到问题:

错误1:Hbase出现ERROR: Can't get master address from ZooKeeper; znode data == null解决办法 

出现此问题可能是zookeeper不稳定造成的,采用的是虚拟机,经常挂起的状态,使用hbase的list命令出现下面错误,这个可能是hbase的稳定性造成的;

解决办法:重启Hbase,或者重启集群

错误2:geomesa导入报错:Class.org.locationtech.geomesa.hbase.coprocessor.GeoMesaCoprocessor cannot be loaded Set hbase.table.sanity.checks to false at conf or table descriptor if you want to bypass sanity checks

解决办法:将geomesa-hbase-distributed-runtime.jar放在了hbase/lib路径下重启hbase

错误3:重启命令

  • stop-all.sh   start-all.sh   启动hadoop
  • stop-hbase.sh   start-hbase.sh 启动和关闭hbase
  • zkServer.sh restart 重启zookeeper start stop

第一次接触时空大数据,需要理解一下名词的含义。

名词含义

时空关系是指两个时空几何对象之间的时间和空间的相对位置。典型的时空关系包括:相交、相离、覆盖、包含等。 在现实场景中“地理围栏判断”是指一个面状要素表示的地理围栏与目标对象(点、线、面)之间的关系。如果目标对象在地理围栏之内,则称之为包含;在面状要素之外,则称之为相离。

OGC全称是开放地理空间信息联盟(Open Geospatial Consortium),是一个非盈利的国际标准组织,它制定了数据模型和相关操作的一系列标准,GIS厂商按照这个标准进行开发可保证空间数据的互操作。

GeoTools是一个遵循OGC标准,用于处理地理空间数据的工具包,实现了OGC标准的数据模型和接口,很多地理工具都基于GeoTools开发

Geometry用来表示一个空间对象,例如空间点对象、空间线对象、空间面对象。Geometry只包含空间对象的位置信息,并不包含其附带的属性信息。 GeoTools提供了工具来帮助构建Geometry,因此在HBase Ganos开发过程中,可以较为方便的使用。

SimpleFeature包含Geometry以及其他属性信息。通常所说的一个轨迹点就是一个SimpleFeature,包含了该轨迹点的空间位置、时间信息以及其他属性信息,其中时间信息也是作为属性信息的一部分。

CQL全称为Common Query Language,是OGC为方便地理服务的查询而定义的查询语言。ECQL全称Extended Common Query Language,是CQL的扩展版,比CQL更强大。 一般来说,ECQL更多的是定义filter,类似于SQL语言的where子句,通过文本描述的方式来筛选出目标对象。

WKT全称Well-known text,是OGC定义的一种用文本来描述空间对象的格式。例如点就可以写成POINT(0,0),这在查询语句中经常使用,CQL&ECQL中也是用WKT来表示空间对象

WKB全称Well-known Binary,是OGC定义的一种通过序列化字节来描述几何对象的格式。与WKT相比,其优点在于数据较小,适宜传输。GeoTools提供了工具可用于WKB与WKT之间的转换。

写入数据分为3步骤

创建索引表

创建时空对象

写入时空对象

具体代码实例(阿里云)

package com.aliyun.tst;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.Point;
import org.geotools.data.DataStore;
import org.geotools.data.DataStoreFinder;
import org.geotools.data.Query;
import org.geotools.data.Transaction;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.data.simple.SimpleFeatureWriter;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.factory.Hints;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.filter.text.ecql.ECQL;
import org.geotools.geometry.jts.JTSFactoryFinder;
import org.locationtech.geomesa.utils.geotools.SimpleFeatureTypes;
import org.opengis.feature.Feature;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.filter.FilterFactory2;
import org.opengis.filter.sort.SortBy;
import org.opengis.filter.sort.SortOrder;
import java.io.BufferedReader;
import java.io.FileReader;
import java.text.SimpleDateFormat;
import java.util.*;
public class Demo {
    public static final String ZK = "localhost"; //Zookeeper地址
    public static void main(String args[]){
        try{
            DataStore ds=null;
            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd' 'HH:mm:ss");
            //配置连接参数
            Map<String, String> params= new HashMap<>();
            params.put("hbase.zookeepers",ZK);
            params.put("hbase.catalog","test_catalog");
            //初始化DataStore
            ds= DataStoreFinder.getDataStore(params);
            //创建SimpleFeatureType定义表结构
            String sft_name="point";
            SimpleFeatureType sft=
                    SimpleFeatureTypes.createType(sft_name, "name:String,dtg:Date,*geom:Point:srid=4326");
            //指定压缩方式gz
            sft.getUserData().put("geomesa.table.compression.enabled", "true");
            sft.getUserData().put("geomesa.table.compression.type", "gz");
            //创建数据表
            ds.createSchema(sft);
            /*
             * GeometryFactory 用来创建空间对象
             */
            GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory();
            SimpleFeatureBuilder builder = new SimpleFeatureBuilder(sft);
            //构造空间数据(点)
            Point point1 = geometryFactory.createPoint(new Coordinate(120.301,35.086));
            Point point2 = geometryFactory.createPoint(new Coordinate(120.301,35.076));
            Point point3 = geometryFactory.createPoint(new Coordinate(120.301,35.066));
            //构造点SimpleFeature
            List<SimpleFeature> features=new ArrayList<>();
            features.add(builder.buildFeature("1", new Object[]{"point1",new Date(),point1}));
            features.add(builder.buildFeature("2", new Object[]{"point2",new Date(),point2}));
            features.add(builder.buildFeature("3", new Object[]{"point3",new Date(),point3}));
            //要素入库
            SimpleFeatureWriter writer=(SimpleFeatureWriter)ds.getFeatureWriterAppend(sft_name, Transaction.AUTO_COMMIT);
            for(SimpleFeature feature:features){
                SimpleFeature toWrite=writer.next();
                toWrite.setAttributes(feature.getAttributes());
                toWrite.getUserData().putAll(feature.getUserData());
                writer.write();
            }
            writer.close();
            //构造时空查询条件
            long t1=format.parse("2019-01-19 11:45:00").getTime();
            long t2=format.parse("2019-02-21 12:15:00").getTime();
            String sortField="dtg";//排序字段,这里设为时间
            FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2();
            SortBy[] sort = new SortBy[]{ff.sort(sortField, SortOrder.DESCENDING)};
            //构造Query对象用于查询 测试空间要素是否与由其最小和最大X和Y值指定的边界框相交。
            Query query = new Query(sft_name, ECQL.toFilter( "bbox(geom,120,20,130,40) AND dtg >= "+t1+" AND dtg <= "+t2));
            query.setSortBy(sort);
            SimpleFeatureCollection result=ds.getFeatureSource(sft_name).getFeatures(query);
            SimpleFeatureIterator iterator=result.features();
            //输出查询结果
            long sum = 0;
            while (iterator.hasNext()) {
                System.out.println(iterator.next());
                sum++;
            }
            System.out.println("查询总数:" + sum);
        }
        catch (Exception e){
            e.printStackTrace();
        }
    }
}

 先理解一下,又是一个全新的知识,后续在一点点跟进这个时空数据的处理。