geotrellis使用(三十)使用geotrellis读取PostGIS空间数据
前言
最近事情很多,各种你想不到的事情——such as singing and dancing——再加上最近又研究docker上瘾,所以geotrellis看上去似乎没有关注,其实我一直在脑中思考着geotrellis。之前看geotrellis源码看到有关geotrellis.slick的相关部分,仅大概浏览了一番,知道是用于读取PostGIS数据库的,未做深入研究,又恰巧前几日有老外在gitter上问了如何读取PostGIS数据库,我当时回答他可以用传统的JDBC方式或者使用geotrellis.slick。JDBC方式我是亲自测试过的,在geotrellis使用(十一)实现空间数据库栅格化以及根据属性字段进行赋值一文中,我详细讲述了如何从PostGIS中读取空间数据并进行栅格化操作;然而我也有极度强迫症,一个事物不知道还着罢了,一旦让我知道我是一定要拿来试试的,尤其在新技术方面,所以这两天就研究了一下,基本调通。现总结如下,以待查用。
一、geotrellis.slick 简介
geotrellis.slick是geotrellis的一个模块,它是对slick的封装。当然如果你要问我什么是geotrellis,请你先从底部的系列链接中看看前面的博客,大致能对其有个了解。
先介绍一下slick,它是一款开源的scala语言数据库处理框架,官网http://slick.lightbend.com/。官网介绍如下:
Slick is a modern database query and access library for Scala. It allows you to work with stored data almost as if you were using Scala collections while at the same time giving you full control over when a database access happens and which data is transferred. You can write your database queries in Scala instead of SQL, thus profiting from the static checking, compile-time safety and compositionality of Scala. Slick features an extensible query compiler which can generate code for different backends.
大概是说Slick使得我们能像处理普通Scala集合那样处理多种数据库,并能对数据库进行控制,相当于一个ORM框架。它支持以下几种数据库:
- SQLServer 2008, 2012, 2014
- Oracle 11g
- DB2 10.5
- MySQL
- PostgreSQL
- SQLite
- Derby/JavaDB
- HSQLDB/HyperSQL
- H2
geotrellis.slick对其进行了封装,支持PostGIS数据库并能够简单的进行空间数据的读写。
二、geotrllis.slick 使用
2.1 引用
话不多说,直接进入干货。首先是对geotrllis.slick的引用,在build.sbt中的libraryDependencies添加如下项:
"org.locationtech.geotrellis" %% "geotrellis-slick" % 1.1.1
2.2 创建数据库连接类
与普通JDBC方式连接基本相同,创建一个连接对象即可。代码如下:
object ConnDatabase {
def newInstance(pghost: String, pgdb: String, pguser: String, pgpass: String) = {
val s = s"jdbc:postgresql://$pghost/$pgdb"
Database.forURL(
s,
driver="org.postgresql.Driver",
user=pguser,
password=pgpass
)
}
}
trait ConnDatabase {
protected var db: Database = null
def connectDb(pghost: String, pgdb: String, pguser: String, pgpass: String) {
db = ConnDatabase.newInstance(pghost, pgdb, pguser, pgpass)
}
}
创建了一个特质(trait)ConnDatabase,其中包含了db对象,此对象即为数据库连接,后续都要基于此对象进行操作。
2.3 创建数据库表与实体类映射
首先要在PostGIS中创建一个数据库(此处假设为test),此数据库要选择空间模板以使该数据库支持空间操作。
在创建映射之前,需要先创建一个类使得程序能够正确识别此类映射并加入相应PostGIS扩展。代码如下:
object driver extends PostgresDriver with PostGisSupport {
override val api = new API with PostGISAssistants with PostGISImplicits
}
此类中的api对象需要被实体类和操作类引用,具体在下面讲述。
我们以城市这个实体为例,假设仅仅关注城市名称以及经纬度坐标,考虑到数据库操作则需要再加一ID项。那么城市实体的定义如下:
import driver.api._
class City(tag: Tag) extends Table[(Int, String, Point)](tag, "cities") {
def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
def name = column[String]("name")
def geom = column[Point]("geom")
def * = (id, name, geom)
}
直观上说这段代码很容易理解,City实体对应与cities表;id字段对应表中id字段,并为主键及自动增长,类型为Int;name对应表中name字段,类型为String;geom对应空间字段geom,类型为Point(空间字段类型可以直接设置为Geometry);def * 表示三个字段的组合。当然此处也可以设置字段可空,只需要将类型使用Option包裹并且上下对应即可,如需要设置geom可空,则整个类修改如下:
class City(tag: Tag) extends Table[(Int, String, Option[Point])](tag, "cities") {
def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
def name = column[String]("name")
def geom = column[Option[Point]]("geom")
def * = (id, name, geom)
}
所以在定义实体类与数据库表映射的时候,首先引入上面driver中定义的api,之后定义实体类继承自Table对象,其泛型即为def *中组合类型,并且二者顺序必须完全一致。这样就定义好了二者映射。
2.4 操作类
上文讲到slick的优势就在于我们可以像使用scala集合那样读取数据库中信息,并能够对数据库进行操作。首先定义一个CityOperate类,在其中完成对City的操作,有点dal或者bll的感觉。
import geotrellis.postgis.model.City
import org.scalatest.concurrent.ScalaFutures
import geotrellis.vector._
object CityOperate extends ConnDatabase with ScalaFutures {
import driver.api._
implicit override val patienceConfig = PatienceConfig(timeout = Span(5, Seconds))
val pguser = "******"
val pgpass = "******"
val pgdb = "test"
val pghost = "127.0.0.1:5432"
connectDb(pghost, pgdb, pguser, pgpass)
val CityTable = TableQuery[City]
}
该类继承自ConnDatabase和ScalaFutures。
其中ConnDatabase是上文我们写好的数据库连接类,主要目的在于得到其中的db对象,所以必须先执行connectDb函数,传入数据库参数。
ScalaFutures主要是获取查询等的Future操作的结果值。
引入上面driver中定义的api,并重写patienceConfig加大超时时间,防止下面的future执行超时。
CityTable很明显是City的映射对象,主要基于此对象对数据库进行操作。
2.4.1 创建表
我们可以无需创建表cities而由slick完成,只需要在上述类中添加如下方法:
def createSchema {
try {
db.run(CityTable.schema.create).futureValue
} catch {
case _: Throwable =>
}
}
该函数实现的功能就是创建cities表。从这段代码大致能看出slick的整个操作模式,其所有操作都要执行db.run函数,传入的是进行的操作,无论是增删改查还是创建、删除表等。此函数的结果需要进行futureValue操作,来获取真正的结果,如果不加此项则不会进行操作。CityTable.schema.create表示进行的是创建schema操作。
可以通过CityTable.schema.create.statements来查看创建表的SQL语句。
2.4.2 删除表
有了创建表操作,删除操作就很容易了,代码如下:
def dropSchema {
try {
db.run(CityTable.schema.drop).futureValue
} catch {
case _: Throwable =>
}
}
很简单,只需要在db.run函数中传入CityTable.schema.drop。
可以通过CityTable.schema.drop.statements来查看创建表的SQL语句。
2.4.3 增
进入数据库操作以及码农的最最最常规操作。增加数据代码如下:
def insertData(data: Array[(String, Point)]) {
db.run(CityTable.map(c => (c.name, c.geom)) ++= data.map { d => (d._1, d._2) })
}
函数接受(String, Point)类型的数组,表示名称和位置。插入操作也很容易,直接像db.run函数传入CityTable.map(c => (c.name, c.geom)) ++= data.map { d => (d._1, d._2) },++=正是一个插入操作的action,前面表示的是要插入的字段名称,后面则是对应的数据,此处表示插入name和geom字段,后面为数据。
当然如果在实体映射中某个字段按照上述方式设置可空,那么在insert以及下面的update操作的时候此字段的类型都要为Option,即有值的地方使用Some包裹,无值的地方设置为None。
可以通过
(CityTable.map(c => (c.name, c.geom)) ++= data.map { d => (d._1, d._2) }).statements
来查看插入的SQL语句,其实到这里大家应该能总结出来规律,只要对传入db.run函数的参数执行statements操作就能查看此操作的SQL语句,以下同,不再赘述。
2.4.4 删
删除数据分为删除全部和有条件删除。
- 删除全部数据:
def deleteAllData {
val q = for { c <- CityTable } yield c
db.run(q.delete).futureValue
}
从这段代码能看出slick对数据操作的基本流程,首先使用for循环生成想要处理的数据的集合,而后使用db.run对此集合执行相应的操作。
上述代码中q表示的是全部数据,db.run传入的也是q.delete,则表中所有数据都会被删除。
- 删除部分数据:
def bboxBuffer(x: Double, y: Double, d: Double) =
Polygon(Line(
(x - d, y - d),
(x - d, y + d),
(x + d, y + d),
(x + d, y - d),
(x - d, y - d)
))
def deleteDataByBufer {
val bbox = bboxBuffer(78.32, 40.30, 0.01)
val q = for(c <- CityTable if c.geom @&& bbox) yield c
db.run(q.delete).futureValue
}
其中bboxBuffer函数表示给定一个点和距离创建其缓冲区。
在deleteDataByBufer函数中,我们先创建了一个bbox缓冲区,该函数的目的是删除所有坐标在给定缓冲区内的城市。可以看出此处q的值在获取的时候稍有变化,加了一个c.geom @&& bbox的条件,@&&是geotrellis写好的空间支持函数,该函数表示前面的空间是否在缓冲区(Polygon)中。将q.delete传入db.run即可实现删除部分数据的目的,当然按照其他条件删除则同理。
2.4.5 改
def updateData(name: String) {
val bbox = bboxBuffer(78.32, 40.30, 0.01)
val q = for (c <- CityTable if c.geom @&& bbox) yield c.name
db.run(q.update(name)).futureValue
}
此函数实现的功能是将词缓冲区内城市名称全部改为传入的name参数。区别只是在于将q.update(name)传入db.run函数。
2.4.6 查
同样查也分为查询全部数据和查询部分数据,其实基本与上述相同。
- 查询全部数据:
def getData = {
val q = for { c <- CityTable } yield (c.name, c.geom)
db.run(q.result).futureValue.toList
}
q获取到的是城市名称和位置信息,则最后查询的结果就是所有城市的名称和位置信息,不包含id。将q.result传入db.run函数即可获取到最终结果。
- 查询部分数据:
def getDataByBuffer = {
val bbox = bboxBuffer(78.32, 40.30, 0.01)
val q = for(c <- CityTable if c.geom @&& bbox) yield c
db.run(q.result).futureValue.toList
}
该函数实现的功能是查询缓冲区内的城市信息,此处q直接获取到的是缓冲区内的城市所有信息,所以将q.result传入db.run后就能获取到缓冲区内的城市的所有信息。
- 对数据进行空间操作:
geotrelis.slick支持将scala的空间操作转换为PostGIS的空间函数,如下:
def getGeomWKTData {
val q = for {
c <- CityTable
} yield c.geom.asEWKT
println(q.result.statements)
db.run(q.result).futureValue.toList
}
上述函数中直接对geom对象进行asEWKT操作,将Point转化为WKT语言,并输出查询结果。执行上面的函数会打印出如下信息:
List(select ST_AsEWKT("geom") from "cities")
表明geotrellis.slick确实将asEWKT操作转换为PostGIS中的ST_AsEWKT函数。
三、总结
本文尝试了geotrliis.slick的相关功能和用法,由于刚接触可能有理解不透彻的地方,欢迎留言指正,不甚感激!
Geotrellis系列文章链接地址http://www.cnblogs.com/shoufengwei/p/5619419.html
geotrellis使用(三十)使用geotrellis读取PostGIS空间数据的更多相关文章
- geotrellis使用(十)缓冲区分析以及多种类型要素栅格化
目录 前言 缓冲区分析 多种类型要素栅格化 总结 参考链接 一.前言 上两篇文章介绍了如何使用Geotrellis进行矢量数据栅格化以及栅格渲染,本文主要介绍栅格化过程中常用到的缓冲区分 ...
- geotrellis使用(十六)使用缓冲区分析的方式解决投影变换中边缘数据值计算的问题
Geotrellis系列文章链接地址http://www.cnblogs.com/shoufengwei/p/5619419.html 目录 前言 问题探索 采样说明 实现方案 总结 一.前言 ...
- 风炫安全web安全学习第三十五节课 文件下载和文件读取漏洞
风炫安全web安全学习第三十五节课 文件下载和文件读取漏洞 0x03 任意文件下载漏洞 一些网站由于业务需求,往往需要提供文件下载功能,但若对用户下载的文件不做限制,则恶意用户就能够下载任意敏感文件, ...
- geotrellis使用(四)geotrellis数据处理部分细节
前面写了几篇博客介绍了Geotrellis的简单使用,具体链接在文后,今天我主要介绍一下Geotrellis在数据处理的过程中需要注意的细节,或者一些简单的经验技巧以供参考. 一.直接操作本地Geot ...
- 程序员编程艺术第三十六~三十七章、搜索智能提示suggestion,附近点搜索
第三十六~三十七章.搜索智能提示suggestion,附近地点搜索 作者:July.致谢:caopengcs.胡果果.时间:二零一三年九月七日. 题记 写博的近三年,整理了太多太多的笔试面试题,如微软 ...
- NeHe OpenGL教程 第三十八课:资源文件
转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...
- NeHe OpenGL教程 第三十五课:播放AVI
转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...
- NeHe OpenGL教程 第三十四课:地形
转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...
- NeHe OpenGL教程 第三十二课:拾取游戏
转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...
随机推荐
- 【Android Developers Training】 100. 使用Intent修改联系人数据
注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...
- 处理浏览器兼容 各个浏览器的标识 hack
Firefox 浏览器 @-moz-document url-prefix() { .selector { property: value; } } 支持所有Gecko内核的浏览器 (包括Firefo ...
- .NetCore+Jexus代理+Redis模拟秒杀商品活动
开篇叙 本篇将和大家分享一下秒杀商品活动架构,采用的架构方案正如标题名称.NetCore+Jexus代理+Redis,由于精力有限所以这里只设计到商品添加,抢购,订单查询,处理队列抢购订单的功能:有不 ...
- Linux系统目录结构介绍
参考博客: http://www.cnblogs.com/chensiqiqi/p/6243549.html 感谢原博主为我学习Linux指明方向!! linux目录:一切从“根”开始,“/”是所有目 ...
- Linux程序设计之shell程序设计
看了<linux程序设计(第4版)>(作者:Neil Matthew ,Richard Stones ,陈建 ,宋健建译).做个笔记,以备后面查看. 首先,清楚几个概念. shell编程属 ...
- SpringMvc多视图配置(jsp、velocity、freemarker) 在src目录views.properties配置
#welcome为modelAndView.setViewName(" welcome " ) ; 中的welcome .(class)固定写法 welcome.(class)=o ...
- 【Mysql基本知识整理】
一.简介 1.什么是数据库? 数据库(Database)是按照数据结构来组织.存储和管理数据的仓库,每个数据库都有一个或多个不同的API用于创建,访问,管理,搜索和复制所保存的数据. 2.关系型数据库 ...
- Jenkins构建Android项目持续集成之单元测试及代码覆盖率
单元测试 在软件开发中一直在推崇TDD(测试驱动开发),但是一直不能被有效的执行或者并不是真正的测试驱动开发(先开发后写单元测试),因为我们懒!而Android开发又是大多应用层面的开发,很多都是和视 ...
- python多线程爬虫设计及实现示例
爬虫的基本步骤分为:获取,解析,存储.假设这里获取和存储为io密集型(访问网络和数据存储),解析为cpu密集型.那么在设计多线程爬虫时主要有两种方案:第一种方案是一个线程完成三个步骤,然后运行多个线程 ...
- Java 并发编程内部分享PPT分享
.NET程序员转向JAVA领域,必备技术首当其冲就是JAVA Concurrency 并发编程. 最近系统性的学习了 Doug Lea <JAVA并发编程实战>一书.这书很有嚼劲,进入JA ...