spatial_extent.py 41 KB

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