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管线 ...
随机推荐
- 搭建开源java博客并通过域名访问
这个博客系统是王爵在GitHub上开源的,通过简单几步就可以部署成功. 前面几步可以参照如下几个链接: 1.https://www.qcloud.com/community/article/29008 ...
- .NET Core 2.0 Preview2 发布汇总
前言 关于 ASP.NET Core 2.0 的新功能可以查看我的这篇博客. 这篇文章是 Priview2中的一些改进. .NET Core 2.0 - Preview2 Azure 的改进 Dock ...
- webdriver API中文文档
1.1 下载selenium2.0的lib包 http://code.google.com/p/selenium/downloads/list 官方UserGuide:http://seleniu ...
- Tomcat常用配置修改
Tomcat常用配置修改 说明 运行需要设置环境变量 JAVA_HOME 即JDK安装目录 tomcat 默认登录地址 http://localhost:8080 配置tomcat 1.端口设置 打开 ...
- Less 的使用
Less 的使用 开发时直接使用 引用你的样式文件(main.less) (必须在less.min.js) 前引用 引用less.min.js 文件 <link href="resou ...
- HTML基本结构与标签总结整理篇
HTML基本结构与标签总结整理篇 前言:这是笔者的学习总结与整理,如果有错误或疑问的地方,欢迎指正与讨论!另:此文会不定时更新~ 1.了解HTML 学习前端技术,必然涉及三个方面:html(结构).c ...
- [高并发]EntityFramework之高性能扩展
目录 简介 读写分离 指定字段更新 事务 Entity 简介 本EF扩展插件将持续更新:开源,敏捷,高性能.(由于EF Core暂未提供方便的钩子位置,暂无EF Core版本) EntityFrame ...
- Ajax 向后台提交一个 JavaScript 对象数组?
var postArray= new Array(); var temp = new Object(); temp.id='1'; temp.name='test'; postArray.push(t ...
- Django学习(二)---使用模板Templates
学会使用渲染模板的方法来显示html内容. 一.Templates是什么: HTML文件 使用了Django模板语言(Django Tamplate Language DTL) 可以使用第三方模板 二 ...
- [POJ2104/HDU2665]Kth Number-主席树-可持久化线段树
Problem Kth Number Solution 裸的主席树,模板题.但是求k大的时候需要非常注意,很多容易写错的地方.卡了好久.写到最后还给我来个卡空间. 具体做法参见主席树论文<可持久 ...