base.py 23 KB

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