spatial_extent.py 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366
  1. """!@package grass.temporal
  2. @brief GRASS Python scripting module (temporal GIS functions)
  3. Temporal GIS related spatial extent functions to be used in Python scripts and tgis packages.
  4. Usage:
  5. >>> import grass.temporal as tgis
  6. >>> extent = tgis.RasterSpatialExtent(
  7. ... ident="raster@PERMANENT", north=90, south=90, east=180, west=180,
  8. ... top=100, bottom=-20)
  9. >>> extent = tgis.Raster3DSpatialExtent(
  10. ... ident="raster3d@PERMANENT", north=90, south=90, east=180, west=180,
  11. ... top=100, bottom=-20)
  12. >>> extent = tgis.VectorSpatialExtent(
  13. ... ident="vector@PERMANENT", north=90, south=90, east=180, west=180,
  14. ... top=100, bottom=-20)
  15. >>> extent = tgis.STRDSSpatialExtent(
  16. ... ident="strds@PERMANENT", north=90, south=90, east=180, west=180,
  17. ... top=100, bottom=-20)
  18. >>> extent = tgis.STR3DSSpatialExtent(
  19. ... ident="str3ds@PERMANENT", north=90, south=90, east=180, west=180,
  20. ... top=100, bottom=-20)
  21. >>> extent = tgis.STVDSSpatialExtent(
  22. ... ident="stvds@PERMANENT", north=90, south=90, east=180, west=180,
  23. ... top=100, bottom=-20)
  24. (C) 2008-2011 by the GRASS Development Team
  25. This program is free software under the GNU General Public
  26. License (>=v2). Read the file COPYING that comes with GRASS
  27. for details.
  28. @author Soeren Gebbert
  29. """
  30. from base import *
  31. class SpatialExtent(SQLDatabaseInterface):
  32. """!This is the spatial extent base class for all maps and space time datasets
  33. This class implements a three dimensional axis aligned bounding box
  34. and functions to compute topological relationships
  35. >>> import grass.temporal as tgis
  36. >>> extent = tgis.SpatialExtent(table="raster_spatial_extent",
  37. ... ident="soil@PERMANENT", north=90, south=90, east=180, west=180,
  38. ... top=100, bottom=-20)
  39. >>> extent.id
  40. 'soil@PERMANENT'
  41. >>> extent.north
  42. 90.0
  43. >>> extent.south
  44. 90.0
  45. >>> extent.east
  46. 180.0
  47. >>> extent.west
  48. 180.0
  49. >>> extent.top
  50. 100.0
  51. >>> extent.bottom
  52. -20.0
  53. >>> extent.print_info()
  54. +-------------------- Spatial extent ----------------------------------------+
  55. | North:...................... 90.0
  56. | South:...................... 90.0
  57. | East:.. .................... 180.0
  58. | West:....................... 180.0
  59. | Top:........................ 100.0
  60. | Bottom:..................... -20.0
  61. >>> extent.print_shell_info()
  62. north=90.0
  63. south=90.0
  64. east=180.0
  65. west=180.0
  66. top=100.0
  67. bottom=-20.0
  68. """
  69. def __init__(self, table=None, ident=None, north=None, south=None,
  70. east=None, west=None, top=None, bottom=None, proj="XY"):
  71. SQLDatabaseInterface.__init__(self, table, ident)
  72. self.set_id(ident)
  73. self.set_spatial_extent(north, south, east, west, top, bottom)
  74. self.set_projection(proj)
  75. def overlapping_2d(self, extent):
  76. """!Return True if the two dimensional extents overlap.
  77. Code is lend from wind_overlap.c in lib/gis
  78. Overlapping includes the spatial relations:
  79. * contain
  80. * in
  81. * cover
  82. * covered
  83. * equivalent
  84. """
  85. if self.get_projection() != extent.get_projection():
  86. core.error(_("Projections are different. Unable to compute overlapping_2d for spatial extents"))
  87. return False
  88. N = extent.get_north()
  89. S = extent.get_south()
  90. E = extent.get_east()
  91. W = extent.get_west()
  92. # Adjust the east and west in case of LL projection
  93. if self.get_projection() == "LL":
  94. while E < self.get_west():
  95. E += 360.0
  96. W += 360.0
  97. while W > self.get_east():
  98. E -= 360.0
  99. W -= 360.0
  100. if(self.get_north() <= S):
  101. return False
  102. if(self.get_south() >= N):
  103. return False
  104. if self.get_east() <= W:
  105. return False
  106. if self.get_west() >= E:
  107. return False
  108. return True
  109. def overlapping(self, extent):
  110. """!Return True if the three dimensional extents overlap
  111. Overlapping includes the spatial relations:
  112. * contain
  113. * in
  114. * cover
  115. * covered
  116. * equivalent
  117. Usage:
  118. >>> import grass.temporal as tgis
  119. >>> A = tgis.SpatialExtent(north=80, south=20, east=60, west=10, bottom=-50, top=50)
  120. >>> B = tgis.SpatialExtent(north=80, south=20, east=60, west=10, bottom=-50, top=50)
  121. >>> A.overlapping(B)
  122. True
  123. """
  124. if not self.overlapping_2d(extent):
  125. return False
  126. T = extent.get_top()
  127. B = extent.get_bottom()
  128. if self.get_top() <= B:
  129. return False
  130. if self.get_bottom() >= T:
  131. return False
  132. return True
  133. def intersect_2d(self, extent):
  134. """!Return the two dimensional intersection as spatial_extent object or None
  135. in case no intersection was found.
  136. """
  137. if not self.overlapping_2d(extent):
  138. return None
  139. eN = extent.get_north()
  140. eS = extent.get_south()
  141. eE = extent.get_east()
  142. eW = extent.get_west()
  143. N = self.get_north()
  144. S = self.get_south()
  145. E = self.get_east()
  146. W = self.get_west()
  147. # Adjust the east and west in case of LL projection
  148. if self.get_projection() == "LL":
  149. while eE < W:
  150. eE += 360.0
  151. eW += 360.0
  152. while eW > E:
  153. eE -= 360.0
  154. eW -= 360.0
  155. # Compute the extent
  156. nN = N
  157. nS = S
  158. nE = E
  159. nW = W
  160. if W < eW:
  161. nW = eW
  162. if E > eE:
  163. nE = eE
  164. if N > eN:
  165. nN = eN
  166. if S < eS:
  167. nS = eS
  168. new = SpatialExtent(north=nN, south=nS, east=nE, west=nW,
  169. top=0, bottom=0, proj=self.get_projection())
  170. return new
  171. def intersect(self, extent):
  172. """!Return the three dimensional intersection as spatial_extent object or None
  173. in case no intersection was found.
  174. Usage:
  175. >>> import grass.temporal as tgis
  176. >>> A = tgis.SpatialExtent(north=80, south=20, east=60, west=10, bottom=-50, top=50)
  177. >>> B = tgis.SpatialExtent(north=80, south=20, east=60, west=10, bottom=-50, top=50)
  178. >>> C = A.intersect(B)
  179. >>> C.print_info()
  180. +-------------------- Spatial extent ----------------------------------------+
  181. | North:...................... 80.0
  182. | South:...................... 20.0
  183. | East:.. .................... 60.0
  184. | West:....................... 10.0
  185. | Top:........................ 50.0
  186. | Bottom:..................... -50.0
  187. >>> B = tgis.SpatialExtent(north=40, south=30, east=60, west=10, bottom=-50, top=50)
  188. >>> C = A.intersect(B)
  189. >>> C.print_info()
  190. +-------------------- Spatial extent ----------------------------------------+
  191. | North:...................... 40.0
  192. | South:...................... 30.0
  193. | East:.. .................... 60.0
  194. | West:....................... 10.0
  195. | Top:........................ 50.0
  196. | Bottom:..................... -50.0
  197. >>> B = tgis.SpatialExtent(north=40, south=30, east=60, west=30, bottom=-50, top=50)
  198. >>> C = A.intersect(B)
  199. >>> C.print_info()
  200. +-------------------- Spatial extent ----------------------------------------+
  201. | North:...................... 40.0
  202. | South:...................... 30.0
  203. | East:.. .................... 60.0
  204. | West:....................... 30.0
  205. | Top:........................ 50.0
  206. | Bottom:..................... -50.0
  207. >>> B = tgis.SpatialExtent(north=40, south=30, east=60, west=30, bottom=-30, top=50)
  208. >>> C = A.intersect(B)
  209. >>> C.print_info()
  210. +-------------------- Spatial extent ----------------------------------------+
  211. | North:...................... 40.0
  212. | South:...................... 30.0
  213. | East:.. .................... 60.0
  214. | West:....................... 30.0
  215. | Top:........................ 50.0
  216. | Bottom:..................... -30.0
  217. >>> B = tgis.SpatialExtent(north=40, south=30, east=60, west=30, bottom=-30, top=30)
  218. >>> C = A.intersect(B)
  219. >>> C.print_info()
  220. +-------------------- Spatial extent ----------------------------------------+
  221. | North:...................... 40.0
  222. | South:...................... 30.0
  223. | East:.. .................... 60.0
  224. | West:....................... 30.0
  225. | Top:........................ 30.0
  226. | Bottom:..................... -30.0
  227. """
  228. if not self.overlapping(extent):
  229. return None
  230. new = self.intersect_2d(extent)
  231. eT = extent.get_top()
  232. eB = extent.get_bottom()
  233. T = self.get_top()
  234. B = self.get_bottom()
  235. nT = T
  236. nB = B
  237. if B < eB:
  238. nB = eB
  239. if T > eT:
  240. nT = eT
  241. new.set_top(nT)
  242. new.set_bottom(nB)
  243. return new
  244. def is_in_2d(self, extent):
  245. """Check two dimensional if the self is located in extent
  246. _____
  247. |A _ |
  248. | |_| |
  249. |_____|B
  250. """
  251. if self.get_projection() != extent.get_projection():
  252. core.error(_("Projections are different. Unable to compute is_in_2d for spatial extents"))
  253. return False
  254. eN = extent.get_north()
  255. eS = extent.get_south()
  256. eE = extent.get_east()
  257. eW = extent.get_west()
  258. N = self.get_north()
  259. S = self.get_south()
  260. E = self.get_east()
  261. W = self.get_west()
  262. # Adjust the east and west in case of LL projection
  263. if self.get_projection() == "LL":
  264. while eE < W:
  265. eE += 360.0
  266. eW += 360.0
  267. while eW > E:
  268. eE -= 360.0
  269. eW -= 360.0
  270. if W <= eW:
  271. return False
  272. if E >= eE:
  273. return False
  274. if N >= eN:
  275. return False
  276. if S <= eS:
  277. return False
  278. return True
  279. def is_in(self, extent):
  280. """Check three dimensional if the self is located in extent
  281. Usage:
  282. >>> import grass.temporal as tgis
  283. >>> A = tgis.SpatialExtent(north=79, south=21, east=59, west=11, bottom=-49, top=49)
  284. >>> B = tgis.SpatialExtent(north=80, south=20, east=60, west=10, bottom=-50, top=50)
  285. >>> A.is_in(B)
  286. True
  287. >>> B.is_in(A)
  288. False
  289. """
  290. if not self.is_in_2d(extent):
  291. return False
  292. eT = extent.get_top()
  293. eB = extent.get_bottom()
  294. T = self.get_top()
  295. B = self.get_bottom()
  296. if B <= eB:
  297. return False
  298. if T >= eT:
  299. return False
  300. return True
  301. def contain_2d(self, extent):
  302. """Check two dimensional if self contains extent """
  303. return extent.is_in_2d(self)
  304. def contain(self, extent):
  305. """Check three dimensional if self contains extent """
  306. return extent.is_in(self)
  307. def equivalent_2d(self, extent):
  308. """Check two dimensional if self is equivalent to extent """
  309. if self.get_projection() != extent.get_projection():
  310. core.error(_("Projections are different. Unable to compute equivalent_2d for spatial extents"))
  311. return False
  312. eN = extent.get_north()
  313. eS = extent.get_south()
  314. eE = extent.get_east()
  315. eW = extent.get_west()
  316. N = self.get_north()
  317. S = self.get_south()
  318. E = self.get_east()
  319. W = self.get_west()
  320. # Adjust the east and west in case of LL projection
  321. if self.get_projection() == "LL":
  322. while eE < W:
  323. eE += 360.0
  324. eW += 360.0
  325. while eW > E:
  326. eE -= 360.0
  327. eW -= 360.0
  328. if W != eW:
  329. return False
  330. if E != eE:
  331. return False
  332. if N != eN:
  333. return False
  334. if S != eS:
  335. return False
  336. return True
  337. def equivalent(self, extent):
  338. """Check three dimensional if self is equivalent to extent """
  339. if not self.equivalent_2d(extent):
  340. return False
  341. eT = extent.get_top()
  342. eB = extent.get_bottom()
  343. T = self.get_top()
  344. B = self.get_bottom()
  345. if B != eB:
  346. return False
  347. if T != eT:
  348. return False
  349. return True
  350. def cover_2d(self, extent):
  351. """Return True if two dimensional self covers extent
  352. _____ _____ _____ _____
  353. |A __| |__ A| |A | B| |B | A|
  354. | |B | | B| | | |__| |__| |
  355. |__|__| |__|__| |_____| |_____|
  356. _____ _____ _____ _____
  357. |A|B| | |A __| |A _ | |__ A|
  358. | |_| | | |__|B | |B| | B|__| |
  359. |_____| |_____| |_|_|_| |_____|
  360. _____ _____ _____ _____
  361. |A|B | |_____|A |A|B|A| |_____|A
  362. | | | |B | | | | | |_____|B
  363. |_|___| |_____| |_|_|_| |_____|A
  364. The following cases are excluded:
  365. * contain
  366. * in
  367. * equivalent
  368. """
  369. if self.get_projection() != extent.get_projection():
  370. core.error(_("Projections are different. Unable to compute cover_2d for spatial extents"))
  371. return False
  372. # Exclude equivalent_2d
  373. if self.equivalent_2d(extent):
  374. return False
  375. eN = extent.get_north()
  376. eS = extent.get_south()
  377. eE = extent.get_east()
  378. eW = extent.get_west()
  379. N = self.get_north()
  380. S = self.get_south()
  381. E = self.get_east()
  382. W = self.get_west()
  383. # Adjust the east and west in case of LL projection
  384. if self.get_projection() == "LL":
  385. while eE < W:
  386. eE += 360.0
  387. eW += 360.0
  388. while eW > E:
  389. eE -= 360.0
  390. eW -= 360.0
  391. # Edges of extent located outside of self are not allowed
  392. if E < eW:
  393. return False
  394. if W > eE:
  395. return False
  396. if N < eS:
  397. return False
  398. if S > eN:
  399. return False
  400. # First we check that at least one edge of extent meets an edge of self
  401. if W != eW and E != eE and N != eN and S != eS:
  402. return False
  403. # We check that at least one edge of extent is located in self
  404. edge_count = 0
  405. if W < eW and E > eW:
  406. edge_count += 1
  407. if E > eE and W < eE:
  408. edge_count += 1
  409. if N > eN and S < eN:
  410. edge_count += 1
  411. if S < eS and N > eS:
  412. edge_count += 1
  413. if edge_count == 0:
  414. return False
  415. return True
  416. def cover(self, extent):
  417. """Return True if three dimensional self covers extent
  418. The following cases are excluded:
  419. * contain
  420. * in
  421. * equivalent
  422. """
  423. if self.get_projection() != extent.get_projection():
  424. core.error(_("Projections are different. Unable to compute cover for spatial extents"))
  425. return False
  426. # Exclude equivalent_2d
  427. if self.equivalent_2d(extent):
  428. return False
  429. eN = extent.get_north()
  430. eS = extent.get_south()
  431. eE = extent.get_east()
  432. eW = extent.get_west()
  433. eT = extent.get_top()
  434. eB = extent.get_bottom()
  435. N = self.get_north()
  436. S = self.get_south()
  437. E = self.get_east()
  438. W = self.get_west()
  439. T = self.get_top()
  440. B = self.get_bottom()
  441. # Adjust the east and west in case of LL projection
  442. if self.get_projection() == "LL":
  443. while eE < W:
  444. eE += 360.0
  445. eW += 360.0
  446. while eW > E:
  447. eE -= 360.0
  448. eW -= 360.0
  449. # Edges of extent located outside of self are not allowed
  450. if E <= eW:
  451. return False
  452. if W >= eE:
  453. return False
  454. if N <= eS:
  455. return False
  456. if S >= eN:
  457. return False
  458. if T <= eB:
  459. return False
  460. if B >= eT:
  461. return False
  462. # First we check that at least one edge of extent meets an edge of self
  463. if W != eW and E != eE and N != eN and S != eS and B != eB and T != eT:
  464. return False
  465. # We check that at least one edge of extent is located in self
  466. edge_count = 0
  467. if W < eW and E > eW:
  468. edge_count += 1
  469. if E > eE and W < eE:
  470. edge_count += 1
  471. if N > eN and S < eN:
  472. edge_count += 1
  473. if S < eS and N > eS:
  474. edge_count += 1
  475. if N > eN and S < eN:
  476. edge_count += 1
  477. if S < eS and N > eS:
  478. edge_count += 1
  479. if T > eT and B < eT:
  480. edge_count += 1
  481. if B < eB and T > eB:
  482. edge_count += 1
  483. if edge_count == 0:
  484. return False
  485. return True
  486. def covered_2d(self, extent):
  487. """Check two dimensional if self is covered by extent """
  488. return extent.cover_2d(self)
  489. def covered(self, extent):
  490. """Check three dimensional if self is covered by extent """
  491. return extent.cover(self)
  492. def overlap_2d(self, extent):
  493. """Return True if the two dimensional extents overlap. Code is lend from wind_overlap.c in lib/gis
  494. _____
  495. |A __|__
  496. | | | B|
  497. |__|__| |
  498. |_____|
  499. The following cases are excluded:
  500. * contain
  501. * in
  502. * cover
  503. * covered
  504. * equivalent
  505. """
  506. if self.contain_2d(extent):
  507. return False
  508. if self.is_in_2d(extent):
  509. return False
  510. if self.cover_2d(extent):
  511. return False
  512. if self.covered_2d(extent):
  513. return False
  514. if self.equivalent_2d(extent):
  515. return False
  516. N = extent.get_north()
  517. S = extent.get_south()
  518. E = extent.get_east()
  519. W = extent.get_west()
  520. # Adjust the east and west in case of LL projection
  521. if self.get_projection() == "LL":
  522. while E < self.get_west():
  523. E += 360.0
  524. W += 360.0
  525. while W > self.get_east():
  526. E -= 360.0
  527. W -= 360.0
  528. if(self.get_north() <= S):
  529. return False
  530. if(self.get_south() >= N):
  531. return False
  532. if self.get_east() <= W:
  533. return False
  534. if self.get_west() >= E:
  535. return False
  536. return True
  537. def overlap(self, extent):
  538. """Return True if the three dimensional extents overlap
  539. The following cases are excluded:
  540. * contain
  541. * in
  542. * cover
  543. * covered
  544. * equivalent
  545. """
  546. if self.is_in(extent):
  547. return False
  548. if self.contain(extent):
  549. return False
  550. if self.cover(extent):
  551. return False
  552. if self.covered(extent):
  553. return False
  554. if self.equivalent(extent):
  555. return False
  556. N = extent.get_north()
  557. S = extent.get_south()
  558. E = extent.get_east()
  559. W = extent.get_west()
  560. T = extent.get_top()
  561. B = extent.get_bottom()
  562. # Adjust the east and west in case of LL projection
  563. if self.get_projection() == "LL":
  564. while E < self.get_west():
  565. E += 360.0
  566. W += 360.0
  567. while W > self.get_east():
  568. E -= 360.0
  569. W -= 360.0
  570. if(self.get_north() <= S):
  571. return False
  572. if(self.get_south() >= N):
  573. return False
  574. if self.get_east() <= W:
  575. return False
  576. if self.get_west() >= E:
  577. return False
  578. if self.get_top() <= B:
  579. return False
  580. if self.get_bottom() >= T:
  581. return False
  582. return True
  583. def meet_2d(self, extent):
  584. """ Check if self and extent meet each other in two dimensions
  585. _____ _____ _____ _____
  586. | A | B | | B | A |
  587. |_____| | | | |
  588. |_____| |_____|_____|
  589. ___
  590. | A |
  591. | |
  592. |___| _____
  593. | B | | B |
  594. | | | |
  595. |_____| |_____|_
  596. | A |
  597. | |
  598. |_____|
  599. """
  600. eN = extent.get_north()
  601. eS = extent.get_south()
  602. eE = extent.get_east()
  603. eW = extent.get_west()
  604. eT = extent.get_top()
  605. eB = extent.get_bottom()
  606. N = self.get_north()
  607. S = self.get_south()
  608. E = self.get_east()
  609. W = self.get_west()
  610. # Adjust the east and west in case of LL projection
  611. if self.get_projection() == "LL":
  612. while eE < W:
  613. eE += 360.0
  614. eW += 360.0
  615. while eW > E:
  616. eE -= 360.0
  617. eW -= 360.0
  618. edge = None
  619. edge_count = 0
  620. if E == eW:
  621. edge = "E"
  622. edge_count += 1
  623. if W == eE:
  624. edge = "W"
  625. edge_count += 1
  626. if N == eS:
  627. edge = "N"
  628. edge_count += 1
  629. if S == eN:
  630. edge = "S"
  631. edge_count += 1
  632. # Meet a a single edge only
  633. if edge_count != 1:
  634. return False
  635. # Check boundaries of the faces
  636. if edge == "E" or edge == "W":
  637. if N < eS or S > eN:
  638. return False
  639. if edge == "N" or edge == "S":
  640. if E < eW or W > eE:
  641. return False
  642. return True
  643. def meet(self, extent):
  644. """ Check if self and extent meet each other in three dimensions"""
  645. eN = extent.get_north()
  646. eS = extent.get_south()
  647. eE = extent.get_east()
  648. eW = extent.get_west()
  649. eT = extent.get_top()
  650. eB = extent.get_bottom()
  651. N = self.get_north()
  652. S = self.get_south()
  653. E = self.get_east()
  654. W = self.get_west()
  655. T = self.get_top()
  656. B = self.get_bottom()
  657. # Adjust the east and west in case of LL projection
  658. if self.get_projection() == "LL":
  659. while eE < W:
  660. eE += 360.0
  661. eW += 360.0
  662. while eW > E:
  663. eE -= 360.0
  664. eW -= 360.0
  665. edge = None
  666. edge_count = 0
  667. if E == eW:
  668. edge = "E"
  669. edge_count += 1
  670. if W == eE:
  671. edge = "W"
  672. edge_count += 1
  673. if N == eS:
  674. edge = "N"
  675. edge_count += 1
  676. if S == eN:
  677. edge = "S"
  678. edge_count += 1
  679. if T == eB:
  680. edge = "T"
  681. edge_count += 1
  682. if B == eT:
  683. edge = "B"
  684. edge_count += 1
  685. # Meet a single edge only
  686. if edge_count != 1:
  687. return False
  688. # Check boundaries of the faces
  689. if edge == "E" or edge == "W":
  690. if N < eS or S > eN:
  691. return False
  692. if T < eB or B > eT:
  693. return False
  694. if edge == "N" or edge == "S":
  695. if E < eW or W > eE:
  696. return False
  697. if T < eB or B > eT:
  698. return False
  699. if edge == "T" or edge == "B":
  700. if E < eW or W > eE:
  701. return False
  702. if N < eS or S > eN:
  703. return False
  704. return True
  705. def disjoint_2d(self, extent):
  706. """Return True if the two dimensional extents are disjoint
  707. """
  708. if self.overlapping_2d(extent) or self.meet_2d(extent):
  709. return False
  710. return True
  711. def disjoint(self, extent):
  712. """Return True if the three dimensional extents are disjoint
  713. """
  714. if self.overlapping(extent) or self.meet(extent):
  715. return False
  716. return True
  717. def spatial_relation_2d(self, extent):
  718. """Returns the two dimensional spatial relation between self and extent
  719. Spatial relations are:
  720. * disjoint
  721. * meet
  722. * overlap
  723. * cover
  724. * covered
  725. * in
  726. * contain
  727. * equivalent
  728. Usage: see self.spatial_relation()
  729. """
  730. if self.equivalent_2d(extent):
  731. return "equivalent"
  732. if self.contain_2d(extent):
  733. return "contain"
  734. if self.is_in_2d(extent):
  735. return "in"
  736. if self.cover_2d(extent):
  737. return "cover"
  738. if self.covered_2d(extent):
  739. return "covered"
  740. if self.overlap_2d(extent):
  741. return "overlap"
  742. if self.meet_2d(extent):
  743. return "meet"
  744. if self.disjoint_2d(extent):
  745. return "disjoint"
  746. return "unknown"
  747. def spatial_relation(self, extent):
  748. """Returns the three dimensional spatial relation between self and extent
  749. Spatial relations are:
  750. * disjoint
  751. * meet
  752. * overlap
  753. * cover
  754. * covered
  755. * in
  756. * contain
  757. * equivalent
  758. Usage:
  759. >>> import grass.temporal as tgis
  760. >>> A = tgis.SpatialExtent(north=80, south=20, east=60, west=10, bottom=-50, top=50)
  761. >>> B = tgis.SpatialExtent(north=80, south=20, east=60, west=10, bottom=-50, top=50)
  762. >>> A.spatial_relation(B)
  763. 'equivalent'
  764. >>> B.spatial_relation(A)
  765. 'equivalent'
  766. >>> B = tgis.SpatialExtent(north=70, south=20, east=60, west=10, bottom=-50, top=50)
  767. >>> A.spatial_relation_2d(B)
  768. 'cover'
  769. >>> A.spatial_relation(B)
  770. 'cover'
  771. >>> B = tgis.SpatialExtent(north=70, south=30, east=60, west=10, bottom=-50, top=50)
  772. >>> A.spatial_relation_2d(B)
  773. 'cover'
  774. >>> A.spatial_relation(B)
  775. 'cover'
  776. >>> B.spatial_relation_2d(A)
  777. 'covered'
  778. >>> B.spatial_relation(A)
  779. 'covered'
  780. >>> B = tgis.SpatialExtent(north=70, south=30, east=50, west=10, bottom=-50, top=50)
  781. >>> A.spatial_relation_2d(B)
  782. 'cover'
  783. >>> B.spatial_relation_2d(A)
  784. 'covered'
  785. >>> A.spatial_relation(B)
  786. 'cover'
  787. >>> B = tgis.SpatialExtent(north=70, south=30, east=50, west=20, bottom=-50, top=50)
  788. >>> B.spatial_relation(A)
  789. 'covered'
  790. >>> B = tgis.SpatialExtent(north=70, south=30, east=50, west=20, bottom=-50, top=50)
  791. >>> A.spatial_relation_2d(B)
  792. 'contain'
  793. >>> A.spatial_relation(B)
  794. 'cover'
  795. >>> B = tgis.SpatialExtent(north=70, south=30, east=50, west=20, bottom=-40, top=50)
  796. >>> A.spatial_relation(B)
  797. 'cover'
  798. >>> B = tgis.SpatialExtent(north=70, south=30, east=50, west=20, bottom=-40, top=40)
  799. >>> A.spatial_relation(B)
  800. 'contain'
  801. >>> B.spatial_relation(A)
  802. 'in'
  803. >>> B = tgis.SpatialExtent(north=90, south=30, east=50, west=20, bottom=-40, top=40)
  804. >>> A.spatial_relation_2d(B)
  805. 'overlap'
  806. >>> A.spatial_relation(B)
  807. 'overlap'
  808. >>> B = tgis.SpatialExtent(north=90, south=5, east=70, west=5, bottom=-40, top=40)
  809. >>> A.spatial_relation_2d(B)
  810. 'in'
  811. >>> A.spatial_relation(B)
  812. 'overlap'
  813. >>> B = tgis.SpatialExtent(north=90, south=5, east=70, west=5, bottom=-40, top=60)
  814. >>> A.spatial_relation(B)
  815. 'overlap'
  816. >>> B = tgis.SpatialExtent(north=90, south=5, east=70, west=5, bottom=-60, top=60)
  817. >>> A.spatial_relation(B)
  818. 'in'
  819. >>> A = tgis.SpatialExtent(north=80, south=60, east=60, west=10, bottom=-50, top=50)
  820. >>> B = tgis.SpatialExtent(north=60, south=20, east=60, west=10, bottom=-50, top=50)
  821. >>> A.spatial_relation_2d(B)
  822. 'meet'
  823. >>> A.spatial_relation(B)
  824. 'meet'
  825. >>> A = tgis.SpatialExtent(north=60, south=40, east=60, west=10, bottom=-50, top=50)
  826. >>> B = tgis.SpatialExtent(north=80, south=60, east=60, west=10, bottom=-50, top=50)
  827. >>> A.spatial_relation_2d(B)
  828. 'meet'
  829. >>> A.spatial_relation(B)
  830. 'meet'
  831. >>> A = tgis.SpatialExtent(north=80, south=40, east=60, west=40, bottom=-50, top=50)
  832. >>> B = tgis.SpatialExtent(north=80, south=40, east=40, west=20, bottom=-50, top=50)
  833. >>> A.spatial_relation_2d(B)
  834. 'meet'
  835. >>> A.spatial_relation(B)
  836. 'meet'
  837. >>> A = tgis.SpatialExtent(north=80, south=40, east=40, west=20, bottom=-50, top=50)
  838. >>> B = tgis.SpatialExtent(north=90, south=30, east=60, west=40, bottom=-50, top=50)
  839. >>> A.spatial_relation_2d(B)
  840. 'meet'
  841. >>> A.spatial_relation(B)
  842. 'meet'
  843. >>> A = tgis.SpatialExtent(north=80, south=40, east=40, west=20, bottom=-50, top=50)
  844. >>> B = tgis.SpatialExtent(north=70, south=50, east=60, west=40, bottom=-50, top=50)
  845. >>> A.spatial_relation_2d(B)
  846. 'meet'
  847. >>> A.spatial_relation(B)
  848. 'meet'
  849. >>> A = tgis.SpatialExtent(north=80, south=40, east=40, west=20, bottom=-50, top=50)
  850. >>> B = tgis.SpatialExtent(north=60, south=20, east=60, west=40, bottom=-50, top=50)
  851. >>> A.spatial_relation_2d(B)
  852. 'meet'
  853. >>> A.spatial_relation(B)
  854. 'meet'
  855. >>> A = tgis.SpatialExtent(north=80, south=40, east=40, west=20, bottom=-50, top=50)
  856. >>> B = tgis.SpatialExtent(north=40, south=20, east=60, west=40, bottom=-50, top=50)
  857. >>> A.spatial_relation_2d(B)
  858. 'disjoint'
  859. >>> A.spatial_relation(B)
  860. 'disjoint'
  861. >>> A = tgis.SpatialExtent(north=80, south=40, east=40, west=20, bottom=-50, top=50)
  862. >>> B = tgis.SpatialExtent(north=60, south=20, east=60, west=40, bottom=-60, top=60)
  863. >>> A.spatial_relation(B)
  864. 'meet'
  865. >>> A = tgis.SpatialExtent(north=80, south=40, east=40, west=20, bottom=-50, top=50)
  866. >>> B = tgis.SpatialExtent(north=90, south=30, east=60, west=40, bottom=-40, top=40)
  867. >>> A.spatial_relation(B)
  868. 'meet'
  869. >>> A = tgis.SpatialExtent(north=80, south=40, east=60, west=20, bottom=0, top=50)
  870. >>> B = tgis.SpatialExtent(north=80, south=40, east=60, west=20, bottom=-50, top=0)
  871. >>> A.spatial_relation(B)
  872. 'meet'
  873. >>> A = tgis.SpatialExtent(north=80, south=40, east=60, west=20, bottom=0, top=50)
  874. >>> B = tgis.SpatialExtent(north=80, south=50, east=60, west=30, bottom=-50, top=0)
  875. >>> A.spatial_relation(B)
  876. 'meet'
  877. >>> A = tgis.SpatialExtent(north=80, south=40, east=60, west=20, bottom=0, top=50)
  878. >>> B = tgis.SpatialExtent(north=70, south=50, east=50, west=30, bottom=-50, top=0)
  879. >>> A.spatial_relation(B)
  880. 'meet'
  881. >>> A = tgis.SpatialExtent(north=80, south=40, east=60, west=20, bottom=0, top=50)
  882. >>> B = tgis.SpatialExtent(north=90, south=30, east=70, west=10, bottom=-50, top=0)
  883. >>> A.spatial_relation(B)
  884. 'meet'
  885. >>> A = tgis.SpatialExtent(north=80, south=40, east=60, west=20, bottom=0, top=50)
  886. >>> B = tgis.SpatialExtent(north=70, south=30, east=50, west=10, bottom=-50, top=0)
  887. >>> A.spatial_relation(B)
  888. 'meet'
  889. >>> A = tgis.SpatialExtent(north=80, south=40, east=60, west=20, bottom=-50, top=0)
  890. >>> B = tgis.SpatialExtent(north=80, south=40, east=60, west=20, bottom=0, top=50)
  891. >>> A.spatial_relation(B)
  892. 'meet'
  893. >>> A = tgis.SpatialExtent(north=80, south=40, east=60, west=20, bottom=-50, top=0)
  894. >>> B = tgis.SpatialExtent(north=80, south=50, east=60, west=30, bottom=0, top=50)
  895. >>> A.spatial_relation(B)
  896. 'meet'
  897. >>> A = tgis.SpatialExtent(north=80, south=40, east=60, west=20, bottom=-50, top=0)
  898. >>> B = tgis.SpatialExtent(north=70, south=50, east=50, west=30, bottom=0, top=50)
  899. >>> A.spatial_relation(B)
  900. 'meet'
  901. >>> A = tgis.SpatialExtent(north=80, south=40, east=60, west=20, bottom=-50, top=0)
  902. >>> B = tgis.SpatialExtent(north=90, south=30, east=70, west=10, bottom=0, top=50)
  903. >>> A.spatial_relation(B)
  904. 'meet'
  905. >>> A = tgis.SpatialExtent(north=80, south=40, east=60, west=20, bottom=-50, top=0)
  906. >>> B = tgis.SpatialExtent(north=70, south=30, east=50, west=10, bottom=0, top=50)
  907. >>> A.spatial_relation(B)
  908. 'meet'
  909. """
  910. if self.equivalent(extent):
  911. return "equivalent"
  912. if self.contain(extent):
  913. return "contain"
  914. if self.is_in(extent):
  915. return "in"
  916. if self.cover(extent):
  917. return "cover"
  918. if self.covered(extent):
  919. return "covered"
  920. if self.overlap(extent):
  921. return "overlap"
  922. if self.meet(extent):
  923. return "meet"
  924. if self.disjoint(extent):
  925. return "disjoint"
  926. return "unknown"
  927. def set_spatial_extent(self, north, south, east, west, top, bottom):
  928. """Set the spatial extent"""
  929. self.set_north(north)
  930. self.set_south(south)
  931. self.set_east(east)
  932. self.set_west(west)
  933. self.set_top(top)
  934. self.set_bottom(bottom)
  935. def set_projection(self, proj):
  936. """Set the projection of the spatial extent it should be XY or LL.
  937. As default the projection is XY
  938. """
  939. if proj is None or (proj != "XY" and proj != "LL"):
  940. self.D["proj"] = "XY"
  941. else:
  942. self.D["proj"] = proj
  943. def set_spatial_extent_2d(self, north, south, east, west):
  944. self.set_north(north)
  945. self.set_south(south)
  946. self.set_east(east)
  947. self.set_west(west)
  948. def set_id(self, ident):
  949. """Convenient method to set the unique identifier (primary key)"""
  950. self.ident = ident
  951. self.D["id"] = ident
  952. def set_north(self, north):
  953. """Set the northern edge of the map"""
  954. if north is not None:
  955. self.D["north"] = float(north)
  956. else:
  957. self.D["north"] = None
  958. def set_south(self, south):
  959. """Set the southern edge of the map"""
  960. if south is not None:
  961. self.D["south"] = float(south)
  962. else:
  963. self.D["south"] = None
  964. def set_west(self, west):
  965. """Set the western edge of the map"""
  966. if west is not None:
  967. self.D["west"] = float(west)
  968. else:
  969. self.D["west"] = None
  970. def set_east(self, east):
  971. """Set the eastern edge of the map"""
  972. if east is not None:
  973. self.D["east"] = float(east)
  974. else:
  975. self.D["east"] = None
  976. def set_top(self, top):
  977. """Set the top edge of the map"""
  978. if top is not None:
  979. self.D["top"] = float(top)
  980. else:
  981. self.D["top"] = None
  982. def set_bottom(self, bottom):
  983. """Set the bottom edge of the map"""
  984. if bottom is not None:
  985. self.D["bottom"] = float(bottom)
  986. else:
  987. self.D["bottom"] = None
  988. def get_id(self):
  989. """Convenient method to get the unique identifier (primary key)
  990. @return None if not found
  991. """
  992. if "id" in self.D:
  993. return self.D["id"]
  994. else:
  995. return None
  996. def get_projection(self):
  997. """Get the projection of the spatial extent"""
  998. return self.D["proj"]
  999. def get_volume(self):
  1000. """Compute the volume of the extent, in case z is zero
  1001. (top == bottom or top - bottom = 1) the area is returned"""
  1002. if self.get_projection() == "LL":
  1003. core.error(_("Volume computation is not supported for LL projections"))
  1004. area = self.get_area()
  1005. bbox = self.get_spatial_extent()
  1006. z = abs(bbox[4] - bbox[5])
  1007. if z == 0:
  1008. z = 1.0
  1009. return area * z
  1010. def get_area(self):
  1011. """Compute the area of the extent, extent in z direction is ignored"""
  1012. if self.get_projection() == "LL":
  1013. core.error(_("Area computation is not supported for LL projections"))
  1014. bbox = self.get_spatial_extent()
  1015. y = abs(bbox[0] - bbox[1])
  1016. x = abs(bbox[2] - bbox[3])
  1017. return x * y
  1018. def get_spatial_extent(self):
  1019. """Return a tuple (north, south, east, west, top, bottom) of the spatial extent"""
  1020. return (
  1021. self.get_north(), self.get_south, self.get_east(), self.get_west(),
  1022. self.get_top(), self.get_bottom())
  1023. def get_spatial_extent_2d(self):
  1024. """Return a tuple (north, south, east, west,) of the 2d spatial extent"""
  1025. return (self.get_north(), self.get_south, self.get_east(), self.get_west())
  1026. def get_north(self):
  1027. """Get the northern edge of the map
  1028. @return None if not found"""
  1029. if "north" in self.D:
  1030. return self.D["north"]
  1031. else:
  1032. return None
  1033. def get_south(self):
  1034. """Get the southern edge of the map
  1035. @return None if not found"""
  1036. if "south" in self.D:
  1037. return self.D["south"]
  1038. else:
  1039. return None
  1040. def get_east(self):
  1041. """Get the eastern edge of the map
  1042. @return None if not found"""
  1043. if "east" in self.D:
  1044. return self.D["east"]
  1045. else:
  1046. return None
  1047. def get_west(self):
  1048. """Get the western edge of the map
  1049. @return None if not found"""
  1050. if "west" in self.D:
  1051. return self.D["west"]
  1052. else:
  1053. return None
  1054. def get_top(self):
  1055. """Get the top edge of the map
  1056. @return None if not found"""
  1057. if "top" in self.D:
  1058. return self.D["top"]
  1059. else:
  1060. return None
  1061. def get_bottom(self):
  1062. """Get the bottom edge of the map
  1063. @return None if not found"""
  1064. if "bottom" in self.D:
  1065. return self.D["bottom"]
  1066. else:
  1067. return None
  1068. id = property(fget=get_id, fset=set_id)
  1069. north = property(fget=get_north, fset=set_north)
  1070. south = property(fget=get_south, fset=set_south)
  1071. east = property(fget=get_east, fset=set_east)
  1072. west = property(fget=get_west, fset=set_west)
  1073. top = property(fget=get_top, fset=set_top)
  1074. bottom= property(fget=get_bottom, fset=set_bottom)
  1075. def print_info(self):
  1076. """Print information about this class in human readable style"""
  1077. # 0123456789012345678901234567890
  1078. print " +-------------------- Spatial extent ----------------------------------------+"
  1079. print " | North:...................... " + str(self.get_north())
  1080. print " | South:...................... " + str(self.get_south())
  1081. print " | East:.. .................... " + str(self.get_east())
  1082. print " | West:....................... " + str(self.get_west())
  1083. print " | Top:........................ " + str(self.get_top())
  1084. print " | Bottom:..................... " + str(self.get_bottom())
  1085. def print_shell_info(self):
  1086. """Print information about this class in shell style"""
  1087. print "north=" + str(self.get_north())
  1088. print "south=" + str(self.get_south())
  1089. print "east=" + str(self.get_east())
  1090. print "west=" + str(self.get_west())
  1091. print "top=" + str(self.get_top())
  1092. print "bottom=" + str(self.get_bottom())
  1093. ###############################################################################
  1094. class RasterSpatialExtent(SpatialExtent):
  1095. def __init__(self, ident=None, north=None, south=None, east=None, west=None, top=None, bottom=None):
  1096. SpatialExtent.__init__(self, "raster_spatial_extent",
  1097. ident, north, south, east, west, top, bottom)
  1098. class Raster3DSpatialExtent(SpatialExtent):
  1099. def __init__(self, ident=None, north=None, south=None, east=None, west=None, top=None, bottom=None):
  1100. SpatialExtent.__init__(self, "raster3d_spatial_extent",
  1101. ident, north, south, east, west, top, bottom)
  1102. class VectorSpatialExtent(SpatialExtent):
  1103. def __init__(self, ident=None, north=None, south=None, east=None, west=None, top=None, bottom=None):
  1104. SpatialExtent.__init__(self, "vector_spatial_extent",
  1105. ident, north, south, east, west, top, bottom)
  1106. class STRDSSpatialExtent(SpatialExtent):
  1107. def __init__(self, ident=None, north=None, south=None, east=None, west=None, top=None, bottom=None):
  1108. SpatialExtent.__init__(self, "strds_spatial_extent",
  1109. ident, north, south, east, west, top, bottom)
  1110. class STR3DSSpatialExtent(SpatialExtent):
  1111. def __init__(self, ident=None, north=None, south=None, east=None, west=None, top=None, bottom=None):
  1112. SpatialExtent.__init__(self, "str3ds_spatial_extent",
  1113. ident, north, south, east, west, top, bottom)
  1114. class STVDSSpatialExtent(SpatialExtent):
  1115. def __init__(self, ident=None, north=None, south=None, east=None, west=None, top=None, bottom=None):
  1116. SpatialExtent.__init__(self, "stvds_spatial_extent",
  1117. ident, north, south, east, west, top, bottom)
  1118. ###############################################################################
  1119. if __name__ == "__main__":
  1120. import doctest
  1121. doctest.testmod()