PageRenderTime 46ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/src/main/groovy/geoscript/workspace/Sqlite.groovy

http://github.com/jericks/geoscript-groovy
Groovy | 467 lines | 376 code | 64 blank | 27 comment | 35 complexity | c1d3325a883f2fb61af03e20ad90a952 MD5 | raw file
  1. package geoscript.workspace
  2. import org.locationtech.jts.geom.Envelope
  3. import org.locationtech.jts.geom.Geometry
  4. import org.locationtech.jts.geom.GeometryCollection
  5. import org.locationtech.jts.geom.GeometryFactory
  6. import org.locationtech.jts.geom.LineString
  7. import org.locationtech.jts.geom.MultiLineString
  8. import org.locationtech.jts.geom.MultiPoint
  9. import org.locationtech.jts.geom.MultiPolygon
  10. import org.locationtech.jts.geom.Point
  11. import org.locationtech.jts.geom.Polygon
  12. import org.locationtech.jts.io.WKBReader
  13. import org.locationtech.jts.io.WKBWriter
  14. import org.locationtech.jts.io.WKTReader
  15. import org.locationtech.jts.io.WKTWriter
  16. import geoscript.proj.Projection
  17. import groovy.sql.Sql
  18. import org.apache.commons.dbcp.BasicDataSource
  19. import org.geotools.data.DataAccessFactory
  20. import org.geotools.data.DataStore
  21. import org.geotools.util.factory.Hints
  22. import org.geotools.geometry.jts.Geometries
  23. import org.geotools.jdbc.JDBCDataStore
  24. import org.geotools.jdbc.JDBCDataStoreFactory
  25. import org.geotools.jdbc.PreparedStatementSQLDialect
  26. import org.geotools.jdbc.SQLDialect
  27. import org.geotools.referencing.CRS
  28. import org.opengis.feature.simple.SimpleFeatureType
  29. import org.opengis.feature.type.GeometryDescriptor
  30. import org.opengis.referencing.FactoryException
  31. import org.opengis.referencing.crs.CoordinateReferenceSystem
  32. import java.sql.Connection
  33. import java.sql.PreparedStatement
  34. import java.sql.ResultSet
  35. import java.sql.SQLException
  36. import java.sql.Types
  37. /**
  38. * A Sqlite Workspace based on the GDAL Sqlite format.
  39. * @author Jared Erickson
  40. */
  41. class Sqlite extends Database {
  42. /**
  43. * Create a new Sqlite Workspace form a File
  44. * @param file The File
  45. */
  46. Sqlite(File file) {
  47. super(createDataStore(file, "WKB"))
  48. }
  49. /**
  50. * Create a new Sqlite Workspace form a File and a Geometry Format (WKB or WKT)
  51. * @param file The File
  52. * @param geometryFormat The geometry encoding format (WKB or WKT)
  53. */
  54. Sqlite(File file, String format) {
  55. super(createDataStore(file, format))
  56. }
  57. /**
  58. * Create a new Sqlite Workspace from a GeoTools JDBCDataStore
  59. * @param ds The GeoTools JDBCDataStore
  60. */
  61. Sqlite(JDBCDataStore ds) {
  62. super(ds)
  63. }
  64. /**
  65. * Get the format
  66. * @return The workspace format name
  67. */
  68. @Override
  69. String getFormat() {
  70. return "Sqlite"
  71. }
  72. /**
  73. * Create a new Sqlite Workspace with a name and directory
  74. */
  75. private static DataStore createDataStore(File file, String geometryFormat) {
  76. Map params = new java.util.HashMap()
  77. params.put("database", file.absolutePath)
  78. params.put("dbtype", "sqlite")
  79. SqliteDataStoreFactory factory = new SqliteDataStoreFactory(GeometryFormat.valueOf(geometryFormat))
  80. factory.createDataStore(params)
  81. }
  82. /**
  83. * The Sqlite WorkspaceFactory
  84. */
  85. static class Factory extends WorkspaceFactory<Sqlite> {
  86. @Override
  87. Map getParametersFromString(String str) {
  88. Map params = [:]
  89. if (!str.contains("=") && str.endsWith(".sqlite")) {
  90. params.put("dbtype", "sqlite")
  91. params.put("database", new File(str).absolutePath)
  92. } else {
  93. params = super.getParametersFromString(str)
  94. }
  95. params
  96. }
  97. @Override
  98. Sqlite create(String type, Map params) {
  99. if (type.equalsIgnoreCase('sqlite')) {
  100. params['dbtype'] = 'sqlite'
  101. if (params.containsKey('file')) {
  102. params['database'] = params['file']
  103. }
  104. super.create(params)
  105. } else {
  106. null
  107. }
  108. }
  109. @Override
  110. Sqlite create(DataStore dataStore) {
  111. Sqlite sqlite = null
  112. if (dataStore instanceof org.geotools.jdbc.JDBCDataStore) {
  113. def jdbcds = dataStore as org.geotools.jdbc.JDBCDataStore
  114. if (jdbcds.dataStoreFactory instanceof SqliteDataStoreFactory) {
  115. sqlite = new Sqlite(dataStore)
  116. }
  117. }
  118. sqlite
  119. }
  120. }
  121. final static class SqliteDataStoreFactory extends JDBCDataStoreFactory {
  122. static final DataAccessFactory.Param DBTYPE = new DataAccessFactory.Param("dbtype", String.class, "Type", true, "sqlite")
  123. static final DataAccessFactory.Param DATABASE = new DataAccessFactory.Param("database", File.class, "Database", true)
  124. private final GeometryFormat geometryFormat
  125. SqliteDataStoreFactory() {
  126. this(GeometryFormat.WKB)
  127. }
  128. SqliteDataStoreFactory(GeometryFormat geometryFormat) {
  129. this.geometryFormat = geometryFormat
  130. }
  131. @Override
  132. protected String getDatabaseID() {
  133. "sqlite"
  134. }
  135. @Override
  136. protected String getDriverClassName() {
  137. "org.sqlite.JDBC"
  138. }
  139. @Override
  140. protected SQLDialect createSQLDialect(JDBCDataStore jdbcDataStore) {
  141. new SqliteDialect(jdbcDataStore, geometryFormat)
  142. }
  143. @Override
  144. protected String getValidationQuery() {
  145. "SELECT 1"
  146. }
  147. @Override
  148. String getDescription() {
  149. "SQLite"
  150. }
  151. @Override
  152. protected String getJDBCUrl(Map params) throws IOException {
  153. File db = (File) DATABASE.lookUp(params)
  154. if (db.getPath().startsWith("file:")) {
  155. db = new File(db.getPath().substring(5))
  156. }
  157. "jdbc:sqlite:${db}"
  158. }
  159. @Override
  160. protected void setupParameters(Map parameters) {
  161. super.setupParameters(parameters);
  162. parameters.remove(HOST.key)
  163. parameters.remove(PORT.key)
  164. parameters.remove(SCHEMA.key)
  165. parameters.remove(PASSWD.key)
  166. parameters.remove(USER.key)
  167. parameters.put(DATABASE.key, DATABASE)
  168. parameters.put(DBTYPE.key, DBTYPE)
  169. }
  170. @Override
  171. BasicDataSource createDataSource(Map params) throws IOException {
  172. BasicDataSource dataSource = new BasicDataSource()
  173. dataSource.setDriverClassName(getDriverClassName())
  174. dataSource.setUrl(getJDBCUrl(params))
  175. dataSource.setAccessToUnderlyingConnectionAllowed(true)
  176. dataSource
  177. }
  178. @Override
  179. protected JDBCDataStore createDataStoreInternal(JDBCDataStore dataStore, Map params) throws IOException {
  180. dataStore.setDatabaseSchema(null)
  181. dataStore
  182. }
  183. }
  184. final static class SqliteDialect extends PreparedStatementSQLDialect {
  185. private final GeometryFormat geometryFormat
  186. SqliteDialect(JDBCDataStore dataStore, GeometryFormat geometryFormat) {
  187. super(dataStore)
  188. this.geometryFormat = geometryFormat
  189. }
  190. protected void createTables(Connection connection) {
  191. Sql sql = new Sql(connection)
  192. sql.execute 'create table if not exists geometry_columns (f_table_name VARCHAR, f_geometry_column VARCHAR, geometry_type INT, coord_dimension INT, srid INT, geometry_format VARCHAR)'
  193. sql.execute 'create table if not exists spatial_ref_sys (srid INTEGER, auth_name TEXT, auth_srid TEXT, srtext TEST)'
  194. }
  195. protected void addGeometryColumn(Connection connection, String table, String geometryColumn, GeometryType geometryType, int coordinateDimension, int srid, GeometryFormat geometryFormat) {
  196. Sql sql = new Sql(connection)
  197. sql.execute 'INSERT INTO geometry_columns (f_table_name, f_geometry_column, geometry_type, coord_dimension, srid, geometry_format) VALUES (?,?,?,?,?,?)', [
  198. table, geometryColumn, geometryType.code, coordinateDimension, srid, geometryFormat.toString()
  199. ]
  200. }
  201. protected void addSpatialReferenceSystem(Connection connection, int srid, String authName, int authSrid, String srText) {
  202. Sql sql = new Sql(connection)
  203. List srsRows = sql.rows("select srid from spatial_ref_sys WHERE srid = ?", [srid])
  204. if (srsRows.size() == 0) {
  205. sql.execute 'INSERT INTO spatial_ref_sys (srid, auth_name, auth_srid, srtext) VALUES (?,?,?,?)', [
  206. srid, authName, authSrid, srText
  207. ]
  208. }
  209. }
  210. protected void deleteGeometryColumn(Connection connection, String table) {
  211. Sql sql = new Sql(connection)
  212. sql.execute 'DELETE FROM geometry_columns WHERE f_table_name = ?', [table]
  213. }
  214. protected GeometryFormat getGeometryFormat(Connection connection, String table) {
  215. Sql sql = new Sql(connection)
  216. List rows = sql.rows("select geometry_format from geometry_columns where f_table_name = ?", [table])
  217. if (!rows.isEmpty()) {
  218. GeometryFormat.valueOf(rows[0].get("geometry_format").toString().toUpperCase())
  219. } else {
  220. null
  221. }
  222. }
  223. protected GeometryType getGeometryType(Connection connection, String table, String geometryColumn) {
  224. Sql sql = new Sql(connection)
  225. List rows = sql.rows("select geometry_type from geometry_columns where f_table_name = ? and f_geometry_column = ?", [table, geometryColumn])
  226. if (!rows.isEmpty()) {
  227. GeometryType.get(rows[0].get("geometry_type") as int)
  228. } else {
  229. null
  230. }
  231. }
  232. protected Geometry readGeometry(byte[] bytes, GeometryFactory geometryFactory, GeometryFormat geometryFormat) {
  233. if (geometryFormat == GeometryFormat.WKB) {
  234. WKBReader reader = new WKBReader(geometryFactory)
  235. reader.read(bytes)
  236. } else /*(geometryFormat == GeometryFormat.WKT)*/ {
  237. WKTReader reader = new WKTReader(geometryFactory)
  238. reader.read(new String(bytes))
  239. }
  240. }
  241. protected byte[] writeGeometry(Geometry geometry, GeometryFormat geometryFormat) {
  242. if (geometryFormat == GeometryFormat.WKB) {
  243. WKBWriter writer = new WKBWriter()
  244. writer.write(geometry)
  245. } else /*(geometryFormat == GeometryFormat.WKT)*/ {
  246. WKTWriter writer = new WKTWriter()
  247. writer.write(geometry).bytes
  248. }
  249. }
  250. protected int getSrid(CoordinateReferenceSystem crs) {
  251. int srid = -1
  252. if (crs != null) {
  253. try {
  254. srid = CRS.lookupEpsgCode(crs, true)
  255. } catch (FactoryException e) {
  256. e.printStackTrace()
  257. }
  258. }
  259. srid
  260. }
  261. @Override
  262. void initializeConnection(Connection cx) throws SQLException {
  263. createTables(cx)
  264. }
  265. @Override
  266. boolean includeTable(String schemaName, String tableName, Connection cx) throws SQLException {
  267. Sql sql = new Sql(cx)
  268. List results = sql.rows("select * from geometry_columns where f_table_name = ?", [tableName])
  269. !results.isEmpty()
  270. }
  271. @Override
  272. void encodePrimaryKey(String column, StringBuffer sql) {
  273. super.encodePrimaryKey(column, sql)
  274. sql.append(" AUTOINCREMENT")
  275. }
  276. @Override
  277. void encodeGeometryEnvelope(String tableName, String geometryColumn, StringBuffer sql) {
  278. encodeColumnName(null, geometryColumn, sql);
  279. }
  280. @Override
  281. Envelope decodeGeometryEnvelope(ResultSet rs, int column, Connection cx) throws SQLException, IOException {
  282. GeometryFormat geometryFormat = getGeometryFormat(cx, rs.metaData.getTableName(1))
  283. Geometry g = readGeometry(rs.getBytes(column), new GeometryFactory(), geometryFormat)
  284. g != null ? g.getEnvelopeInternal() : null
  285. }
  286. @Override
  287. Geometry decodeGeometryValue(GeometryDescriptor descriptor, ResultSet rs, String column, GeometryFactory factory, Connection cx, Hints hints) throws IOException, SQLException {
  288. GeometryFormat geometryFormat = getGeometryFormat(cx, rs.metaData.getTableName(1))
  289. return readGeometry(rs.getBytes(column), factory, geometryFormat)
  290. }
  291. @Override
  292. void setGeometryValue(Geometry g, int dimension, int srid, Class binding, PreparedStatement ps, int column) throws SQLException {
  293. if (g == null || g.isEmpty()) {
  294. ps.setNull(column, Types.BLOB)
  295. }
  296. else {
  297. g.setSRID(srid)
  298. try {
  299. byte[] bytes = writeGeometry(g, geometryFormat)
  300. ps.setBytes(column, bytes)
  301. } catch (IOException e) {
  302. throw new RuntimeException(e)
  303. }
  304. }
  305. }
  306. @Override
  307. String getGeometryTypeName(Integer type) {
  308. Geometries.getForSQLType(type).getName()
  309. }
  310. @Override
  311. void registerSqlTypeNameToClassMappings( Map<String, Class<?>> mappings) {
  312. super.registerSqlTypeNameToClassMappings(mappings)
  313. mappings.put("DOUBLE", Double)
  314. mappings.put("BOOLEAN", Boolean)
  315. mappings.put("DATE", java.sql.Date)
  316. mappings.put("TIMESTAMP", java.sql.Timestamp)
  317. mappings.put("TIME", java.sql.Time)
  318. }
  319. @Override
  320. void registerClassToSqlMappings(Map<Class<?>, Integer> mappings) {
  321. super.registerClassToSqlMappings(mappings)
  322. for (Geometries g : Geometries.values()) {
  323. mappings.put(g.getBinding(), g.getSQLType())
  324. }
  325. mappings.put(Long, Types.INTEGER)
  326. mappings.put(Double, Types.REAL)
  327. mappings.put(Boolean, Types.INTEGER)
  328. }
  329. @Override
  330. void registerSqlTypeToSqlTypeNameOverrides(Map<Integer, String> overrides) {
  331. super.registerSqlTypeToSqlTypeNameOverrides(overrides)
  332. overrides.put(Types.BOOLEAN, "BOOLEAN")
  333. overrides.put(Types.SMALLINT, "SMALLINT")
  334. overrides.put(Types.BIGINT, "BIGINT")
  335. overrides.put(Types.DOUBLE, "DOUBLE")
  336. overrides.put(Types.NUMERIC, "NUMERIC")
  337. overrides.put(Types.DATE, "DATE")
  338. overrides.put(Types.TIME, "TIME")
  339. overrides.put(Types.TIMESTAMP, "TIMESTAMP")
  340. }
  341. @Override
  342. Class<?> getMapping(ResultSet columns, Connection cx) throws SQLException {
  343. String tbl = columns.getString("TABLE_NAME")
  344. String col = columns.getString("COLUMN_NAME")
  345. GeometryType geometryType = getGeometryType(cx, tbl, col)
  346. geometryType ? geometryType.type : null
  347. }
  348. @Override
  349. void postCreateTable(String schemaName, SimpleFeatureType featureType, Connection cx) throws SQLException, IOException {
  350. String table = featureType.typeName
  351. String geometryColumn = featureType.geometryDescriptor.localName
  352. GeometryType geometryType = GeometryType.get(featureType.geometryDescriptor.type.binding)
  353. int coordinateDimension = 2
  354. CoordinateReferenceSystem crs = featureType.coordinateReferenceSystem
  355. Projection projection
  356. if (crs) {
  357. projection = new Projection(featureType.coordinateReferenceSystem)
  358. }
  359. int srid = projection?.epsg ?: -1
  360. addGeometryColumn(cx, table, geometryColumn, geometryType, coordinateDimension, srid, this.geometryFormat)
  361. if (srid > -1) {
  362. String authName = "EPSG"
  363. int authSrid = projection.epsg
  364. String srText = projection.wkt
  365. addSpatialReferenceSystem(cx, srid, authName, authSrid, srText)
  366. }
  367. }
  368. @Override
  369. void postDropTable(String schemaName, SimpleFeatureType featureType, Connection cx) throws SQLException {
  370. super.postDropTable(schemaName, featureType, cx)
  371. deleteGeometryColumn(cx, featureType.typeName)
  372. }
  373. }
  374. private static enum GeometryType {
  375. GEOMETRY(0, Geometry),
  376. POINT(1, Point),
  377. LINESTRING(2, LineString),
  378. POLYGON(3, Polygon),
  379. MULTIPOINT(4, MultiPoint),
  380. MULTILINESTRING(5, MultiLineString),
  381. MULTIPOLYGON(6, MultiPolygon),
  382. GEOMETRYCOLLECTION(7, GeometryCollection)
  383. final int code
  384. final Class type
  385. GeometryType(int code, Class type) {
  386. this.code = code
  387. this.type = type
  388. }
  389. static GeometryType get(int code) {
  390. values().find { GeometryType geometryType ->
  391. geometryType.code == code
  392. }
  393. }
  394. static GeometryType get(Class type) {
  395. values().find { GeometryType geometryType ->
  396. geometryType.type == type
  397. }
  398. }
  399. }
  400. private static enum GeometryFormat {
  401. WKT, WKB
  402. }
  403. }