base.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675
  1. """!@package grass.temporal
  2. @brief GRASS Python scripting module (temporal GIS functions)
  3. Temporal GIS base classes to be used in other
  4. Python temporal gis packages.
  5. This packages includes all base classes to store basic information like id, name,
  6. mapset creation and modification time as well as sql serialization and de-serialization
  7. and the sql database interface.
  8. Usage:
  9. @code
  10. import grass.temporal as tgis
  11. rbase = tgis.raster_base(ident="soil")
  12. ...
  13. @endcode
  14. (C) 2008-2011 by the GRASS Development Team
  15. This program is free software under the GNU General Public
  16. License (>=v2). Read the file COPYING that comes with GRASS
  17. for details.
  18. @author Soeren Gebbert
  19. """
  20. from core import *
  21. ###############################################################################
  22. class dict_sql_serializer(object):
  23. def __init__(self):
  24. self.D = {}
  25. def serialize(self, type, table, where=None):
  26. """!Convert the internal dictionary into a string of semicolon separated SQL statements
  27. The keys are the column names and the values are the row entries
  28. @type must be SELECT. INSERT, UPDATE
  29. @table The name of the table to select, insert or update
  30. @where The optional where statement
  31. @return the sql string
  32. """
  33. sql = ""
  34. args = []
  35. # Create ordered select statement
  36. if type == "SELECT":
  37. sql += 'SELECT '
  38. count = 0
  39. for key in self.D.keys():
  40. if count == 0:
  41. sql += ' %s ' % key
  42. else:
  43. sql += ' , %s ' % key
  44. count += 1
  45. sql += ' FROM ' + table + ' '
  46. if where:
  47. sql += where
  48. # Create insert statement
  49. if type =="INSERT":
  50. count = 0
  51. sql += 'INSERT INTO ' + table + ' ('
  52. for key in self.D.keys():
  53. if count == 0:
  54. sql += ' %s ' % key
  55. else:
  56. sql += ' ,%s ' % key
  57. count += 1
  58. count = 0
  59. sql += ') VALUES ('
  60. for key in self.D.keys():
  61. if count == 0:
  62. if dbmi.paramstyle == "qmark":
  63. sql += '?'
  64. else:
  65. sql += '%s'
  66. else:
  67. if dbmi.paramstyle == "qmark":
  68. sql += ' ,?'
  69. else:
  70. sql += ' ,%s'
  71. count += 1
  72. args.append(self.D[key])
  73. sql += ') '
  74. if where:
  75. sql += where
  76. # Create update statement for existing entries
  77. if type =="UPDATE":
  78. count = 0
  79. sql += 'UPDATE ' + table + ' SET '
  80. for key in self.D.keys():
  81. # Update only entries which are not None
  82. if self.D[key] != None:
  83. if count == 0:
  84. if dbmi.paramstyle == "qmark":
  85. sql += ' %s = ? ' % key
  86. else:
  87. sql += ' %s ' % key
  88. sql += '= %s '
  89. else:
  90. if dbmi.paramstyle == "qmark":
  91. sql += ' ,%s = ? ' % key
  92. else:
  93. sql += ' ,%s ' % key
  94. sql += '= %s '
  95. count += 1
  96. args.append(self.D[key])
  97. if where:
  98. sql += where
  99. # Create update statement for all entries
  100. if type =="UPDATE ALL":
  101. count = 0
  102. sql += 'UPDATE ' + table + ' SET '
  103. for key in self.D.keys():
  104. if count == 0:
  105. if dbmi.paramstyle == "qmark":
  106. sql += ' %s = ? ' % key
  107. else:
  108. sql += ' %s ' % key
  109. sql += '= %s '
  110. else:
  111. if dbmi.paramstyle == "qmark":
  112. sql += ' ,%s = ? ' % key
  113. else:
  114. sql += ' ,%s ' % key
  115. sql += '= %s '
  116. count += 1
  117. args.append(self.D[key])
  118. if where:
  119. sql += where
  120. return sql, tuple(args)
  121. def deserialize(self, row):
  122. """!Convert the content of the dbmi dictionary like row into the internal dictionary
  123. @param row: The dictionary like row to store in the internal dict
  124. """
  125. self.D = {}
  126. for key in row.keys():
  127. self.D[key] = row[key]
  128. def clear(self):
  129. """!Initialize the internal storage"""
  130. self.D = {}
  131. def print_self(self):
  132. print self.D
  133. def test(self):
  134. t = dict_sql_serializer()
  135. t.D["id"] = "soil@PERMANENT"
  136. t.D["name"] = "soil"
  137. t.D["mapset"] = "PERMANENT"
  138. t.D["creator"] = "soeren"
  139. t.D["creation_time"] = datetime.now()
  140. t.D["modification_time"] = datetime.now()
  141. t.D["revision"] = 1
  142. sql, values = t.serialize(type="SELECT", table="raster_base")
  143. print sql, '\n', values
  144. sql, values = t.serialize(type="INSERT", table="raster_base")
  145. print sql, '\n', values
  146. sql, values = t.serialize(type="UPDATE", table="raster_base")
  147. print sql, '\n', values
  148. ###############################################################################
  149. class sql_database_interface(dict_sql_serializer):
  150. """!This class represents the SQL database interface
  151. Functions to insert, select and update the internal structure of this class
  152. in the temporal database are implemented. The following DBMS are supported:
  153. * sqlite via the sqlite3 standard library
  154. * postgresql via psycopg2
  155. This is the base class for raster, raster3d, vector and space time datasets
  156. data management classes:
  157. * Identification information (base)
  158. * Spatial extent
  159. * Temporal extent
  160. * Metadata
  161. """
  162. def __init__(self, table=None, ident=None):
  163. """!Constructor of this class
  164. @param table: The name of the table
  165. @param ident: The identifier (primary key) of this object in the database table
  166. """
  167. dict_sql_serializer.__init__(self)
  168. self.table = table # Name of the table, set in the subclass
  169. self.ident = ident
  170. def get_table_name(self):
  171. """!Return the name of the table in which the internal data are inserted, updated or selected"""
  172. return self.table
  173. def connect(self):
  174. """!Connect to the DBMI to execute SQL statements
  175. Supported backends are sqlite3 and postgresql
  176. """
  177. init = get_temporal_dbmi_init_string()
  178. #print "Connect to", self.database
  179. if dbmi.__name__ == "sqlite3":
  180. self.connection = dbmi.connect(init, detect_types=dbmi.PARSE_DECLTYPES|dbmi.PARSE_COLNAMES)
  181. self.connection.row_factory = dbmi.Row
  182. self.connection.isolation_level = None
  183. self.cursor = self.connection.cursor()
  184. self.cursor.execute("PRAGMA synchronous = OFF")
  185. self.cursor.execute("PRAGMA journal_mode = MEMORY")
  186. elif dbmi.__name__ == "psycopg2":
  187. self.connection = dbmi.connect(init)
  188. self.connection.set_isolation_level(dbmi.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
  189. self.cursor = self.connection.cursor(cursor_factory=dbmi.extras.DictCursor)
  190. def close(self):
  191. """!Close the DBMI connection"""
  192. #print "Close connection to", self.database
  193. self.connection.commit()
  194. self.cursor.close()
  195. def get_delete_statement(self):
  196. return "DELETE FROM " + self.get_table_name() + " WHERE id = \'" + str(self.ident) + "\'"
  197. def delete(self, dbif=None):
  198. """!Delete the entry of this object from the temporal database"""
  199. sql = self.get_delete_statement()
  200. #print sql
  201. if dbif:
  202. dbif.cursor.execute(sql)
  203. else:
  204. self.connect()
  205. self.cursor.execute(sql)
  206. self.close()
  207. def get_is_in_db_statement(self):
  208. return "SELECT id FROM " + self.get_table_name() + " WHERE id = \'" + str(self.ident) + "\'"
  209. def is_in_db(self, dbif=None):
  210. """!Check if this object is present in the temporal database
  211. @param dbif: The database interface to be used
  212. """
  213. sql = self.get_is_in_db_statement()
  214. #print sql
  215. if dbif:
  216. dbif.cursor.execute(sql)
  217. row = dbif.cursor.fetchone()
  218. else:
  219. self.connect()
  220. self.cursor.execute(sql)
  221. row = self.cursor.fetchone()
  222. self.close()
  223. # Nothing found
  224. if row == None:
  225. return False
  226. return True
  227. def get_select_statement(self):
  228. return self.serialize("SELECT", self.get_table_name(), "WHERE id = \'" + str(self.ident) + "\'")
  229. def select(self, dbif=None):
  230. """!Select the content from the temporal database and store it
  231. in the internal dictionary structure
  232. @param dbif: The database interface to be used
  233. """
  234. sql, args = self.get_select_statement()
  235. #print sql
  236. #print args
  237. if dbif:
  238. if len(args) == 0:
  239. dbif.cursor.execute(sql)
  240. else:
  241. dbif.cursor.execute(sql, args)
  242. row = dbif.cursor.fetchone()
  243. else:
  244. self.connect()
  245. if len(args) == 0:
  246. self.cursor.execute(sql)
  247. else:
  248. self.cursor.execute(sql, args)
  249. row = self.cursor.fetchone()
  250. self.close()
  251. # Nothing found
  252. if row == None:
  253. return False
  254. if len(row) > 0:
  255. self.deserialize(row)
  256. else:
  257. core.fatal(_("Object <%s> not found in the temporal database") % self.get_id())
  258. return True
  259. def get_insert_statement(self):
  260. return self.serialize("INSERT", self.get_table_name())
  261. def insert(self, dbif=None):
  262. """!Serialize the content of this object and store it in the temporal
  263. database using the internal identifier
  264. @param dbif: The database interface to be used
  265. """
  266. sql, args = self.get_insert_statement()
  267. #print sql
  268. #print args
  269. if dbif:
  270. dbif.cursor.execute(sql, args)
  271. else:
  272. self.connect()
  273. self.cursor.execute(sql, args)
  274. self.close()
  275. def get_update_statement(self):
  276. return self.serialize("UPDATE", self.get_table_name(), "WHERE id = \'" + str(self.ident) + "\'")
  277. def update(self, dbif=None):
  278. """!Serialize the content of this object and update it in the temporal
  279. database using the internal identifier
  280. Only object entries which are exists (not None) are updated
  281. @param dbif: The database interface to be used
  282. """
  283. if self.ident == None:
  284. raise IOError("Missing identifer");
  285. sql, args = self.get_update_statement()
  286. #print sql
  287. #print args
  288. if dbif:
  289. dbif.cursor.execute(sql, args)
  290. else:
  291. self.connect()
  292. self.cursor.execute(sql, args)
  293. self.close()
  294. def get_update_all_statement(self):
  295. return self.serialize("UPDATE ALL", self.get_table_name(), "WHERE id = \'" + str(self.ident) + "\'")
  296. def update_all(self, dbif=None):
  297. """!Serialize the content of this object, including None objects, and update it in the temporal
  298. database using the internal identifier
  299. @param dbif: The database interface to be used
  300. """
  301. if self.ident == None:
  302. raise IOError("Missing identifer");
  303. sql, args = self.get_update_all_statement()
  304. #print sql
  305. #print args
  306. if dbif:
  307. dbif.cursor.execute(sql, args)
  308. else:
  309. self.connect()
  310. self.cursor.execute(sql, args)
  311. self.close()
  312. ###############################################################################
  313. class dataset_base(sql_database_interface):
  314. """!This is the base class for all maps and spacetime datasets storing basic identification information"""
  315. def __init__(self, table=None, ident=None, name=None, mapset=None, creator=None, ctime=None,\
  316. mtime=None, ttype=None, revision=1):
  317. sql_database_interface.__init__(self, table, ident)
  318. self.set_id(ident)
  319. if ident != None and name == None and mapset == None:
  320. if ident.find("@") >= 0:
  321. name, mapset = ident.split("@")
  322. if name.find(":") >= 0:
  323. name, layer = ident.split(":")
  324. self.set_name(name)
  325. self.set_mapset(mapset)
  326. self.set_creator(creator)
  327. self.set_ctime(ctime)
  328. self.set_ttype(ttype)
  329. #self.set_mtime(mtime)
  330. #self.set_revision(revision)
  331. def set_id(self, ident):
  332. """!Convenient method to set the unique identifier (primary key)
  333. @param ident: The unique identifier should be a combination of the dataset name, layer name and the mapset name@mapset
  334. """
  335. self.ident = ident
  336. self.D["id"] = ident
  337. def set_name(self, name):
  338. """!Set the name of the dataset
  339. @param name: The name of the dataset
  340. """
  341. self.D["name"] = name
  342. def set_mapset(self, mapset):
  343. """!Set the mapset of the dataset
  344. @param mapsets: The name of the mapset in which this dataset is stored
  345. """
  346. self.D["mapset"] = mapset
  347. def set_layer(self, layer):
  348. """!Convenient method to set the layer of the map (part of primary key)
  349. Layer are currently supported for vector maps
  350. @param layer: The layer of the map
  351. """
  352. self.D["layer"] = layer
  353. def set_creator(self, creator):
  354. """!Set the creator of the dataset
  355. @param creator: The name of the creator
  356. """
  357. self.D["creator"] = creator
  358. def set_ctime(self, ctime=None):
  359. """!Set the creation time of the dataset, if nothing set the current time is used
  360. @param ctime: The current time of type datetime
  361. """
  362. if ctime == None:
  363. self.D["creation_time"] = datetime.now()
  364. else:
  365. self.D["creation_time"] = ctime
  366. def set_ttype(self, ttype):
  367. """!Set the temporal type of the dataset: absolute or relative, if nothing set absolute time will assumed
  368. @param ttype: The temporal type of the dataset "absolute or relative"
  369. """
  370. if ttype == None or (ttype != "absolute" and ttype != "relative"):
  371. self.D["temporal_type"] = "absolute"
  372. else:
  373. self.D["temporal_type"] = ttype
  374. # def set_mtime(self, mtime=None):
  375. # """!Set the modification time of the map, if nothing set the current time is used"""
  376. # if mtime == None:
  377. # self.D["modification_time"] = datetime.now()
  378. # else:
  379. # self.D["modification_time"] = mtime
  380. # def set_revision(self, revision=1):
  381. # """!Set the revision of the map: if nothing set revision 1 will assumed"""
  382. # self.D["revision"] = revision
  383. def get_id(self):
  384. """!Convenient method to get the unique identifier (primary key)
  385. @return None if not found
  386. """
  387. if self.D.has_key("id"):
  388. return self.D["id"]
  389. else:
  390. return None
  391. def get_map_id(self):
  392. """!Convenient method to get the unique map identifier without layer information
  393. @param return the name of the vector map as name@mapset
  394. """
  395. id = self.get_id()
  396. if id.find(":") >= 0:
  397. # Remove the layer identifier from the id
  398. return id.split("@")[0].split(":")[0] + "@" + id.split("@")[1]
  399. else:
  400. return id
  401. def get_layer(self):
  402. """!Convenient method to get the layer of the map (part of primary key)
  403. Layer are currently supported for vector maps
  404. @return None if not found
  405. """
  406. if self.D.has_key("layer"):
  407. return self.D["layer"]
  408. else:
  409. return None
  410. def get_name(self):
  411. """!Get the name of the dataset
  412. @return None if not found"""
  413. if self.D.has_key("name"):
  414. return self.D["name"]
  415. else:
  416. return None
  417. def get_mapset(self):
  418. """!Get the name of mapset of this dataset
  419. @return None if not found"""
  420. if self.D.has_key("mapset"):
  421. return self.D["mapset"]
  422. else:
  423. return None
  424. def get_creator(self):
  425. """!Get the creator of the dataset
  426. @return None if not found"""
  427. if self.D.has_key("creator"):
  428. return self.D["creator"]
  429. else:
  430. return None
  431. def get_ctime(self):
  432. """!Get the creation time of the dataset, datatype is datetime
  433. @return None if not found"""
  434. if self.D.has_key("creation_time"):
  435. return self.D["creation_time"]
  436. else:
  437. return None
  438. def get_ttype(self):
  439. """!Get the temporal type of the map
  440. @return None if not found"""
  441. if self.D.has_key("temporal_type"):
  442. return self.D["temporal_type"]
  443. else:
  444. return None
  445. # def get_mtime(self):
  446. # """!Get the modification time of the map, datatype is datetime
  447. # @return None if not found"""
  448. # if self.D.has_key("modification_time"):
  449. # return self.D["modification_time"]
  450. # else:
  451. # return None
  452. # def get_revision(self):
  453. # """!Get the revision of the map
  454. # @return None if not found"""
  455. # if self.D.has_key("revision"):
  456. # return self.D["revision"]
  457. # else:
  458. # return None
  459. def print_info(self):
  460. """!Print information about this class in human readable style"""
  461. # 0123456789012345678901234567890
  462. print " +-------------------- Basic information -------------------------------------+"
  463. print " | Id: ........................ " + str(self.get_id())
  464. print " | Name: ...................... " + str(self.get_name())
  465. print " | Mapset: .................... " + str(self.get_mapset())
  466. if self.get_layer():
  467. print " | Layer:...................... " + str(self.get_layer())
  468. print " | Creator: ................... " + str(self.get_creator())
  469. print " | Creation time: ............. " + str(self.get_ctime())
  470. # print " | Modification time: ......... " + str(self.get_mtime())
  471. print " | Temporal type: ............. " + str(self.get_ttype())
  472. # print " | Revision in database: ...... " + str(self.get_revision())
  473. def print_shell_info(self):
  474. """!Print information about this class in shell style"""
  475. print "id=" + str(self.get_id())
  476. print "name=" + str(self.get_name())
  477. print "mapset=" + str(self.get_mapset())
  478. if self.get_layer():
  479. print "layer=" + str(self.get_layer())
  480. print "creator=" + str(self.get_creator())
  481. print "creation_time=" + str(self.get_ctime())
  482. # print "modification_time=" + str(self.get_mtime())
  483. print "temporal_type=" + str(self.get_ttype())
  484. # print "revision=" + str(self.get_revision())
  485. ###############################################################################
  486. class raster_base(dataset_base):
  487. def __init__(self, ident=None, name=None, mapset=None, creator=None, creation_time=None,\
  488. modification_time=None, temporal_type=None, revision=1):
  489. dataset_base.__init__(self, "raster_base", ident, name, mapset, creator, creation_time,\
  490. modification_time, temporal_type, revision)
  491. class raster3d_base(dataset_base):
  492. def __init__(self, ident=None, name=None, mapset=None, creator=None, creation_time=None,\
  493. modification_time=None, temporal_type=None, revision=1):
  494. dataset_base.__init__(self, "raster3d_base", ident, name, mapset, creator, creation_time,\
  495. modification_time, temporal_type, revision)
  496. class vector_base(dataset_base):
  497. def __init__(self, ident=None, name=None, mapset=None, layer=None, creator=None, creation_time=None,\
  498. modification_time=None, temporal_type=None, revision=1):
  499. dataset_base.__init__(self, "vector_base", ident, name, mapset, creator, creation_time,\
  500. modification_time, temporal_type, revision)
  501. self.set_id(ident)
  502. if ident != None and name == None and mapset == None:
  503. if ident.find("@") >= 0:
  504. name, mapset = ident.split("@")
  505. if layer == None:
  506. if name.find(":") >= 0:
  507. name, layer = name.split(":")
  508. self.set_name(name)
  509. self.set_mapset(mapset)
  510. # Layer currently only in use by vector maps
  511. self.set_layer(layer)
  512. ###############################################################################
  513. class stds_base(dataset_base):
  514. def __init__(self, table=None, ident=None, name=None, mapset=None, semantic_type=None, creator=None, creation_time=None,\
  515. modification_time=None, temporal_type=None, revision=1):
  516. dataset_base.__init__(self, table, ident, name, mapset, creator, creation_time,\
  517. modification_time, temporal_type, revision)
  518. self.set_semantic_type(semantic_type)
  519. def set_semantic_type(self, semantic_type):
  520. """!Set the semantic type of the space time dataset"""
  521. self.D["semantic_type"] = semantic_type
  522. def get_semantic_type(self):
  523. """!Get the semantic type of the space time dataset
  524. @return None if not found"""
  525. if self.D.has_key("semantic_type"):
  526. return self.D["semantic_type"]
  527. else:
  528. return None
  529. def print_info(self):
  530. """!Print information about this class in human readable style"""
  531. dataset_base.print_info(self)
  532. # 0123456789012345678901234567890
  533. print " | Semantic type:.............. " + str(self.get_semantic_type())
  534. def print_shell_info(self):
  535. """!Print information about this class in shell style"""
  536. dataset_base.print_shell_info(self)
  537. print "semantic_type=" + str(self.get_semantic_type())
  538. ###############################################################################
  539. class strds_base(stds_base):
  540. def __init__(self, ident=None, name=None, mapset=None, semantic_type=None, creator=None, creation_time=None,\
  541. modification_time=None, temporal_type=None, revision=1):
  542. stds_base.__init__(self, "strds_base", ident, name, mapset, semantic_type, creator, creation_time,\
  543. modification_time, temporal_type, revision)
  544. class str3ds_base(stds_base):
  545. def __init__(self, ident=None, name=None, mapset=None, semantic_type=None, creator=None, creation_time=None,\
  546. modification_time=None, temporal_type=None, revision=1):
  547. stds_base.__init__(self, "str3ds_base", ident, name, mapset, semantic_type, creator, creation_time,\
  548. modification_time, temporal_type, revision)
  549. class stvds_base(stds_base):
  550. def __init__(self, ident=None, name=None, mapset=None, semantic_type=None, creator=None, creation_time=None,\
  551. modification_time=None, temporal_type=None, revision=1):
  552. stds_base.__init__(self, "stvds_base", ident, name, mapset, semantic_type, creator, creation_time,\
  553. modification_time, temporal_type, revision)