1"""
2Spatial extents classes for map layer and space time datasets
3
4Usage:
5
6.. code-block:: python
7
8    >>> import grass.temporal as tgis
9    >>> tgis.init()
10    >>> extent = tgis.RasterSpatialExtent(
11    ... ident="raster@PERMANENT", north=90, south=90, east=180, west=180,
12    ... top=100, bottom=-20)
13    >>> extent = tgis.Raster3DSpatialExtent(
14    ... ident="raster3d@PERMANENT", north=90, south=90, east=180, west=180,
15    ... top=100, bottom=-20)
16    >>> extent = tgis.VectorSpatialExtent(
17    ... ident="vector@PERMANENT", north=90, south=90, east=180, west=180,
18    ... top=100, bottom=-20)
19    >>> extent = tgis.STRDSSpatialExtent(
20    ... ident="strds@PERMANENT", north=90, south=90, east=180, west=180,
21    ... top=100, bottom=-20)
22    >>> extent = tgis.STR3DSSpatialExtent(
23    ... ident="str3ds@PERMANENT", north=90, south=90, east=180, west=180,
24    ... top=100, bottom=-20)
25    >>> extent = tgis.STVDSSpatialExtent(
26    ... ident="stvds@PERMANENT", north=90, south=90, east=180, west=180,
27    ... top=100, bottom=-20)
28
29(C) 2012-2013 by the GRASS Development Team
30This program is free software under the GNU General Public
31License (>=v2). Read the file COPYING that comes with GRASS
32for details.
33
34:authors: Soeren Gebbert
35"""
36from __future__ import print_function
37from .base import SQLDatabaseInterface
38from .core import init
39from datetime import datetime
40
41
42class SpatialExtent(SQLDatabaseInterface):
43    """This is the spatial extent base class for all maps and space time datasets
44
45        This class implements a three dimensional axis aligned bounding box
46        and functions to compute topological relationships
47
48        Usage:
49
50        .. code-block:: python
51
52            >>> init()
53            >>> extent = SpatialExtent(table="raster_spatial_extent",
54            ... ident="soil@PERMANENT", north=90, south=90, east=180, west=180,
55            ... top=100, bottom=-20)
56            >>> extent.id
57            'soil@PERMANENT'
58            >>> extent.north
59            90.0
60            >>> extent.south
61            90.0
62            >>> extent.east
63            180.0
64            >>> extent.west
65            180.0
66            >>> extent.top
67            100.0
68            >>> extent.bottom
69            -20.0
70            >>> extent.print_info()
71             +-------------------- Spatial extent ----------------------------------------+
72             | North:...................... 90.0
73             | South:...................... 90.0
74             | East:.. .................... 180.0
75             | West:....................... 180.0
76             | Top:........................ 100.0
77             | Bottom:..................... -20.0
78            >>> extent.print_shell_info()
79            north=90.0
80            south=90.0
81            east=180.0
82            west=180.0
83            top=100.0
84            bottom=-20.0
85
86    """
87    def __init__(self, table=None, ident=None, north=None, south=None,
88                 east=None, west=None, top=None, bottom=None, proj="XY"):
89
90        SQLDatabaseInterface.__init__(self, table, ident)
91        self.set_id(ident)
92        self.set_spatial_extent_from_values(north, south, east, west, top,
93                                            bottom)
94        self.set_projection(proj)
95
96    def overlapping_2d(self, extent):
97        """Return True if this (A) and the provided spatial extent (B) overlaps
98        in two dimensional space.
99        Code is lend from wind_overlap.c in lib/gis
100
101        Overlapping includes the spatial relations:
102
103        - contain
104        - in
105        - cover
106        - covered
107        - equivalent
108
109        .. code-block:: python
110
111             >>> A = SpatialExtent(north=80, south=20, east=60, west=10)
112             >>> B = SpatialExtent(north=80, south=20, east=60, west=10)
113             >>> A.overlapping_2d(B)
114             True
115
116        :param extent: The spatial extent to check overlapping with
117        :return: True or False
118        """
119
120        if self.get_projection() != extent.get_projection():
121            self.msgr.error(_("Projections are different. Unable to compute "
122                              "overlapping_2d for spatial extents"))
123            return False
124
125        N = extent.get_north()
126        S = extent.get_south()
127        E = extent.get_east()
128        W = extent.get_west()
129
130        # Adjust the east and west in case of LL projection
131        if self.get_projection() == "LL":
132            while E < self.get_west():
133                E += 360.0
134                W += 360.0
135
136            while W > self.get_east():
137                E -= 360.0
138                W -= 360.0
139
140        if(self.get_north() <= S):
141            return False
142
143        if(self.get_south() >= N):
144            return False
145
146        if self.get_east() <= W:
147            return False
148
149        if self.get_west() >= E:
150            return False
151
152        return True
153
154    def overlapping(self, extent):
155        """Return True if this (A) and the provided spatial
156        extent (B) overlaps in three dimensional space.
157
158        Overlapping includes the spatial relations:
159
160        - contain
161        - in
162        - cover
163        - covered
164        - equivalent
165
166        Usage:
167
168        .. code-block:: python
169
170             >>> A = SpatialExtent(north=80, south=20, east=60, west=10, bottom=-50, top=50)
171             >>> B = SpatialExtent(north=80, south=20, east=60, west=10, bottom=-50, top=50)
172             >>> A.overlapping(B)
173             True
174
175        :param extent: The spatial extent to check overlapping with
176        :return: True or False
177        """
178
179        if not self.overlapping_2d(extent):
180            return False
181
182        T = extent.get_top()
183        B = extent.get_bottom()
184
185        if self.get_top() <= B:
186            return False
187
188        if self.get_bottom() >= T:
189            return False
190
191        return True
192
193    def intersect_2d(self, extent):
194        """Return the two dimensional intersection as spatial_extent
195           object or None in case no intersection was found.
196
197        :param extent: The spatial extent to intersect with
198        :return: The intersection spatial extent
199        """
200
201        if not self.overlapping_2d(extent):
202            return None
203
204        eN = extent.get_north()
205        eS = extent.get_south()
206        eE = extent.get_east()
207        eW = extent.get_west()
208
209        N = self.get_north()
210        S = self.get_south()
211        E = self.get_east()
212        W = self.get_west()
213
214        # Adjust the east and west in case of LL projection
215        if self.get_projection() == "LL":
216            while eE < W:
217                eE += 360.0
218                eW += 360.0
219
220            while eW > E:
221                eE -= 360.0
222                eW -= 360.0
223
224        # Compute the extent
225        nN = N
226        nS = S
227        nE = E
228        nW = W
229
230        if W < eW:
231            nW = eW
232        if E > eE:
233            nE = eE
234        if N > eN:
235            nN = eN
236        if S < eS:
237            nS = eS
238
239        new = SpatialExtent(north=nN, south=nS, east=nE, west=nW,
240                            top=0, bottom=0, proj=self.get_projection())
241        return new
242
243    def intersect(self, extent):
244        """Return the three dimensional intersection as spatial_extent
245        object or None in case no intersection was found.
246
247        Usage:
248
249        .. code-block:: python
250
251            >>> A = SpatialExtent(north=80, south=20, east=60, west=10,
252            ... bottom=-50, top=50)
253            >>> B = SpatialExtent(north=80, south=20, east=60, west=10,
254            ... bottom=-50, top=50)
255            >>> C = A.intersect(B)
256            >>> C.print_info()
257             +-------------------- Spatial extent ----------------------------------------+
258             | North:...................... 80.0
259             | South:...................... 20.0
260             | East:.. .................... 60.0
261             | West:....................... 10.0
262             | Top:........................ 50.0
263             | Bottom:..................... -50.0
264            >>> B = SpatialExtent(north=40, south=30, east=60, west=10,
265            ... bottom=-50, top=50)
266            >>> C = A.intersect(B)
267            >>> C.print_info()
268             +-------------------- Spatial extent ----------------------------------------+
269             | North:...................... 40.0
270             | South:...................... 30.0
271             | East:.. .................... 60.0
272             | West:....................... 10.0
273             | Top:........................ 50.0
274             | Bottom:..................... -50.0
275            >>> B = SpatialExtent(north=40, south=30, east=60, west=30,
276            ... bottom=-50, top=50)
277            >>> C = A.intersect(B)
278            >>> C.print_info()
279             +-------------------- Spatial extent ----------------------------------------+
280             | North:...................... 40.0
281             | South:...................... 30.0
282             | East:.. .................... 60.0
283             | West:....................... 30.0
284             | Top:........................ 50.0
285             | Bottom:..................... -50.0
286            >>> B = SpatialExtent(north=40, south=30, east=60, west=30,
287            ... bottom=-30, top=50)
288            >>> C = A.intersect(B)
289            >>> C.print_info()
290             +-------------------- Spatial extent ----------------------------------------+
291             | North:...................... 40.0
292             | South:...................... 30.0
293             | East:.. .................... 60.0
294             | West:....................... 30.0
295             | Top:........................ 50.0
296             | Bottom:..................... -30.0
297            >>> B = SpatialExtent(north=40, south=30, east=60, west=30,
298            ... bottom=-30, top=30)
299            >>> C = A.intersect(B)
300            >>> C.print_info()
301             +-------------------- Spatial extent ----------------------------------------+
302             | North:...................... 40.0
303             | South:...................... 30.0
304             | East:.. .................... 60.0
305             | West:....................... 30.0
306             | Top:........................ 30.0
307             | Bottom:..................... -30.0
308
309
310         :param extent: The spatial extent to intersect with
311         :return: The intersection spatial extent
312        """
313
314        if not self.overlapping(extent):
315            return None
316
317        new = self.intersect_2d(extent)
318
319        eT = extent.get_top()
320        eB = extent.get_bottom()
321
322        T = self.get_top()
323        B = self.get_bottom()
324
325        nT = T
326        nB = B
327
328        if B < eB:
329            nB = eB
330        if T > eT:
331            nT = eT
332
333        new.set_top(nT)
334        new.set_bottom(nB)
335
336        return new
337
338    def union_2d(self, extent):
339        """Return the two dimensional union as spatial_extent
340           object or None in case the extents does not overlap or meet.
341
342        :param extent: The spatial extent to create a union with
343        :return: The union spatial extent
344        """
345        if not self.overlapping_2d(extent) and not self.meet_2d(extent):
346            return None
347
348        return self.disjoint_union_2d(extent)
349
350    def disjoint_union_2d(self, extent):
351        """Return the two dimensional union as spatial_extent.
352
353        :param extent: The spatial extent to create a union with
354        :return: The union spatial extent
355        """
356        eN = extent.get_north()
357        eS = extent.get_south()
358        eE = extent.get_east()
359        eW = extent.get_west()
360
361        N = self.get_north()
362        S = self.get_south()
363        E = self.get_east()
364        W = self.get_west()
365
366        # Adjust the east and west in case of LL projection
367        if self.get_projection() == "LL":
368            while eE < W:
369                eE += 360.0
370                eW += 360.0
371
372            while eW > E:
373                eE -= 360.0
374                eW -= 360.0
375
376        # Compute the extent
377        nN = N
378        nS = S
379        nE = E
380        nW = W
381
382        if W > eW:
383            nW = eW
384        if E < eE:
385            nE = eE
386        if N < eN:
387            nN = eN
388        if S > eS:
389            nS = eS
390
391        new = SpatialExtent(north=nN, south=nS, east=nE, west=nW,
392                            top=0, bottom=0, proj=self.get_projection())
393        return new
394
395    def union(self, extent):
396        """Return the three dimensional union as spatial_extent
397           object or None in case the extents does not overlap or meet.
398
399        :param extent: The spatial extent to create a union with
400        :return: The union spatial extent
401        """
402        if not self.overlapping(extent) and not self.meet(extent):
403            return None
404
405        return self.disjoint_union(extent)
406
407    def disjoint_union(self, extent):
408        """Return the three dimensional union as spatial_extent .
409
410        Usage:
411
412        .. code-block:: python
413
414            >>> A = SpatialExtent(north=80, south=20, east=60, west=10,
415            ... bottom=-50, top=50)
416            >>> B = SpatialExtent(north=80, south=20, east=60, west=10,
417            ... bottom=-50, top=50)
418            >>> C = A.disjoint_union(B)
419            >>> C.print_info()
420             +-------------------- Spatial extent ----------------------------------------+
421             | North:...................... 80.0
422             | South:...................... 20.0
423             | East:.. .................... 60.0
424             | West:....................... 10.0
425             | Top:........................ 50.0
426             | Bottom:..................... -50.0
427            >>> B = SpatialExtent(north=40, south=30, east=60, west=10,
428            ... bottom=-50, top=50)
429            >>> C = A.disjoint_union(B)
430            >>> C.print_info()
431             +-------------------- Spatial extent ----------------------------------------+
432             | North:...................... 80.0
433             | South:...................... 20.0
434             | East:.. .................... 60.0
435             | West:....................... 10.0
436             | Top:........................ 50.0
437             | Bottom:..................... -50.0
438            >>> B = SpatialExtent(north=40, south=30, east=60, west=30,
439            ... bottom=-50, top=50)
440            >>> C = A.disjoint_union(B)
441            >>> C.print_info()
442             +-------------------- Spatial extent ----------------------------------------+
443             | North:...................... 80.0
444             | South:...................... 20.0
445             | East:.. .................... 60.0
446             | West:....................... 10.0
447             | Top:........................ 50.0
448             | Bottom:..................... -50.0
449            >>> B = SpatialExtent(north=40, south=30, east=60, west=30,
450            ... bottom=-30, top=50)
451            >>> C = A.disjoint_union(B)
452            >>> C.print_info()
453             +-------------------- Spatial extent ----------------------------------------+
454             | North:...................... 80.0
455             | South:...................... 20.0
456             | East:.. .................... 60.0
457             | West:....................... 10.0
458             | Top:........................ 50.0
459             | Bottom:..................... -50.0
460            >>> B = SpatialExtent(north=40, south=30, east=60, west=30,
461            ... bottom=-30, top=30)
462            >>> C = A.disjoint_union(B)
463            >>> C.print_info()
464             +-------------------- Spatial extent ----------------------------------------+
465             | North:...................... 80.0
466             | South:...................... 20.0
467             | East:.. .................... 60.0
468             | West:....................... 10.0
469             | Top:........................ 50.0
470             | Bottom:..................... -50.0
471            >>> A = SpatialExtent(north=80, south=20, east=60, west=10,
472            ... bottom=-50, top=50)
473            >>> B = SpatialExtent(north=90, south=80, east=70, west=20,
474            ... bottom=-30, top=60)
475            >>> C = A.disjoint_union(B)
476            >>> C.print_info()
477             +-------------------- Spatial extent ----------------------------------------+
478             | North:...................... 90.0
479             | South:...................... 20.0
480             | East:.. .................... 70.0
481             | West:....................... 10.0
482             | Top:........................ 60.0
483             | Bottom:..................... -50.0
484
485
486         :param extent: The spatial extent to create a disjoint union with
487         :return: The union spatial extent
488        """
489
490        new = self.disjoint_union_2d(extent)
491
492        eT = extent.get_top()
493        eB = extent.get_bottom()
494
495        T = self.get_top()
496        B = self.get_bottom()
497
498        nT = T
499        nB = B
500
501        if B > eB:
502            nB = eB
503        if T < eT:
504            nT = eT
505
506        new.set_top(nT)
507        new.set_bottom(nB)
508
509        return new
510
511    def is_in_2d(self, extent):
512        """Return True if this extent (A) is located in the provided spatial
513        extent (B) in two dimensions.
514
515        ::
516
517             _____
518            |A _  |
519            | |_| |
520            |_____|B
521
522
523        :param extent: The spatial extent
524        :return: True or False
525        """
526        if self.get_projection() != extent.get_projection():
527            self.msgr.error(_("Projections are different. Unable to compute "
528                              "is_in_2d for spatial extents"))
529            return False
530
531        eN = extent.get_north()
532        eS = extent.get_south()
533        eE = extent.get_east()
534        eW = extent.get_west()
535
536        N = self.get_north()
537        S = self.get_south()
538        E = self.get_east()
539        W = self.get_west()
540
541        # Adjust the east and west in case of LL projection
542        if self.get_projection() == "LL":
543            while eE < W:
544                eE += 360.0
545                eW += 360.0
546
547            while eW > E:
548                eE -= 360.0
549                eW -= 360.0
550
551        if W <= eW:
552            return False
553        if E >= eE:
554            return False
555        if N >= eN:
556            return False
557        if S <= eS:
558            return False
559
560        return True
561
562    def is_in(self, extent):
563        """Return True if this extent (A) is located in the provided spatial
564        extent (B) in three dimensions.
565
566        Usage:
567
568        .. code-block:: python
569
570            >>> A = SpatialExtent(north=79, south=21, east=59, west=11,
571            ... bottom=-49, top=49)
572            >>> B = SpatialExtent(north=80, south=20, east=60, west=10,
573            ... bottom=-50, top=50)
574            >>> A.is_in(B)
575            True
576            >>> B.is_in(A)
577            False
578
579        :param extent: The spatial extent
580        :return: True or False
581        """
582        if not self.is_in_2d(extent):
583            return False
584
585        eT = extent.get_top()
586        eB = extent.get_bottom()
587
588        T = self.get_top()
589        B = self.get_bottom()
590
591        if B <= eB:
592            return False
593        if T >= eT:
594            return False
595
596        return True
597
598    def contain_2d(self, extent):
599        """Return True if this extent (A) contains the provided spatial
600        extent (B) in two dimensions.
601
602        Usage:
603
604        .. code-block:: python
605
606            >>> A = SpatialExtent(north=80, south=20, east=60, west=10)
607            >>> B = SpatialExtent(north=79, south=21, east=59, west=11)
608            >>> A.contain_2d(B)
609            True
610            >>> B.contain_2d(A)
611            False
612
613        :param extent: The spatial extent
614        :return: True or False
615        """
616        return extent.is_in_2d(self)
617
618    def contain(self, extent):
619        """Return True if this extent (A) contains the provided spatial
620        extent (B) in three dimensions.
621
622        Usage:
623
624        .. code-block:: python
625
626            >>> A = SpatialExtent(north=80, south=20, east=60, west=10,
627            ... bottom=-50, top=50)
628            >>> B = SpatialExtent(north=79, south=21, east=59, west=11,
629            ... bottom=-49, top=49)
630            >>> A.contain(B)
631            True
632            >>> B.contain(A)
633            False
634
635        :param extent: The spatial extent
636        :return: True or False
637        """
638        return extent.is_in(self)
639
640    def equivalent_2d(self, extent):
641        """Return True if this extent (A) is equal to the provided spatial
642        extent (B) in two dimensions.
643
644        Usage:
645
646        .. code-block:: python
647
648            >>> A = SpatialExtent(north=80, south=20, east=60, west=10)
649            >>> B = SpatialExtent(north=80, south=20, east=60, west=10)
650            >>> A.equivalent_2d(B)
651            True
652            >>> B.equivalent_2d(A)
653            True
654
655        :param extent: The spatial extent
656        :return: True or False
657        """
658        if self.get_projection() != extent.get_projection():
659            self.msgr.error(_("Projections are different. Unable to compute "
660                              "equivalent_2d for spatial extents"))
661            return False
662
663        eN = extent.get_north()
664        eS = extent.get_south()
665        eE = extent.get_east()
666        eW = extent.get_west()
667
668        N = self.get_north()
669        S = self.get_south()
670        E = self.get_east()
671        W = self.get_west()
672
673        # Adjust the east and west in case of LL projection
674        if self.get_projection() == "LL":
675            while eE < W:
676                eE += 360.0
677                eW += 360.0
678
679            while eW > E:
680                eE -= 360.0
681                eW -= 360.0
682
683        if W != eW:
684            return False
685        if E != eE:
686            return False
687        if N != eN:
688            return False
689        if S != eS:
690            return False
691
692        return True
693
694    def equivalent(self, extent):
695        """Return True if this extent (A) is equal to the provided spatial
696        extent (B) in three dimensions.
697
698        Usage:
699
700        .. code-block:: python
701
702            >>> A = SpatialExtent(north=80, south=20, east=60, west=10,
703            ... bottom=-50, top=50)
704            >>> B = SpatialExtent(north=80, south=20, east=60, west=10,
705            ... bottom=-50, top=50)
706            >>> A.equivalent(B)
707            True
708            >>> B.equivalent(A)
709            True
710
711        :param extent: The spatial extent
712        :return: True or False
713        """
714
715        if not self.equivalent_2d(extent):
716            return False
717
718        eT = extent.get_top()
719        eB = extent.get_bottom()
720
721        T = self.get_top()
722        B = self.get_bottom()
723
724        if B != eB:
725            return False
726        if T != eT:
727            return False
728
729        return True
730
731    def cover_2d(self, extent):
732        """Return True if this extent (A) covers the provided spatial
733        extent (B) in two dimensions.
734
735        ::
736
737             _____    _____    _____    _____
738            |A  __|  |__  A|  |A | B|  |B | A|
739            |  |B |  | B|  |  |  |__|  |__|  |
740            |__|__|  |__|__|  |_____|  |_____|
741
742             _____    _____    _____    _____
743            |A|B| |  |A  __|  |A _  |  |__  A|
744            | |_| |  |  |__|B | |B| | B|__|  |
745            |_____|  |_____|  |_|_|_|  |_____|
746
747             _____    _____    _____    _____
748            |A|B  |  |_____|A |A|B|A|  |_____|A
749            | |   |  |B    |  | | | |  |_____|B
750            |_|___|  |_____|  |_|_|_|  |_____|A
751
752
753        The following cases are excluded:
754
755        - contain
756        - in
757        - equivalent
758
759        :param extent: The spatial extent
760        :return: True or False
761        """
762
763        if self.get_projection() != extent.get_projection():
764            self.msgr.error(_("Projections are different. Unable to compute"
765                              " cover_2d for spatial extents"))
766            return False
767
768        # Exclude equivalent_2d
769        if self.equivalent_2d(extent):
770            return False
771
772        eN = extent.get_north()
773        eS = extent.get_south()
774        eE = extent.get_east()
775        eW = extent.get_west()
776
777        N = self.get_north()
778        S = self.get_south()
779        E = self.get_east()
780        W = self.get_west()
781
782        # Adjust the east and west in case of LL projection
783        if self.get_projection() == "LL":
784            while eE < W:
785                eE += 360.0
786                eW += 360.0
787
788            while eW > E:
789                eE -= 360.0
790                eW -= 360.0
791
792        # Edges of extent located outside of self are not allowed
793        if E <= eW:
794            return False
795        if W >= eE:
796            return False
797        if N <= eS:
798            return False
799        if S >= eN:
800            return False
801
802        # First we check that at least one edge of extent meets an edge of self
803        if W != eW and E != eE and N != eN and S != eS:
804            return False
805
806        # We check that at least one edge of extent is located in self
807        edge_count = 0
808        if W < eW and E > eW:
809            edge_count += 1
810        if E > eE and W < eE:
811            edge_count += 1
812        if N > eN and S < eN:
813            edge_count += 1
814        if S < eS and N > eS:
815            edge_count += 1
816
817        if edge_count == 0:
818            return False
819
820        return True
821
822    def cover(self, extent):
823        """Return True if this extent covers the provided spatial
824        extent in three dimensions.
825
826        The following cases are excluded:
827
828        - contain
829        - in
830        - equivalent
831
832        :param extent: The spatial extent
833        :return: True or False
834        """
835        if self.get_projection() != extent.get_projection():
836            self.msgr.error(_("Projections are different. Unable to compute "
837                              "cover for spatial extents"))
838            return False
839
840        # Exclude equivalent_2d
841        if self.equivalent_2d(extent):
842            return False
843
844        eN = extent.get_north()
845        eS = extent.get_south()
846        eE = extent.get_east()
847        eW = extent.get_west()
848
849        eT = extent.get_top()
850        eB = extent.get_bottom()
851
852        N = self.get_north()
853        S = self.get_south()
854        E = self.get_east()
855        W = self.get_west()
856
857        T = self.get_top()
858        B = self.get_bottom()
859
860        # Adjust the east and west in case of LL projection
861        if self.get_projection() == "LL":
862            while eE < W:
863                eE += 360.0
864                eW += 360.0
865
866            while eW > E:
867                eE -= 360.0
868                eW -= 360.0
869
870        # Edges of extent located outside of self are not allowed
871        if E <= eW:
872            return False
873        if W >= eE:
874            return False
875        if N <= eS:
876            return False
877        if S >= eN:
878            return False
879        if T <= eB:
880            return False
881        if B >= eT:
882            return False
883
884        # First we check that at least one edge of extent meets an edge of self
885        if W != eW and E != eE and N != eN and S != eS and B != eB and T != eT:
886            return False
887
888        # We check that at least one edge of extent is located in self
889        edge_count = 0
890        if W < eW and E > eW:
891            edge_count += 1
892        if E > eE and W < eE:
893            edge_count += 1
894        if N > eN and S < eN:
895            edge_count += 1
896        if S < eS and N > eS:
897            edge_count += 1
898        if N > eN and S < eN:
899            edge_count += 1
900        if S < eS and N > eS:
901            edge_count += 1
902        if T > eT and B < eT:
903            edge_count += 1
904        if B < eB and T > eB:
905            edge_count += 1
906
907        if edge_count == 0:
908            return False
909
910        return True
911
912    def covered_2d(self, extent):
913        """Return True if this extent is covered by the provided spatial
914        extent in two dimensions.
915
916        The following cases are excluded:
917
918        - contain
919        - in
920        - equivalent
921
922        :param extent: The spatial extent
923        :return: True or False
924        """
925
926        return extent.cover_2d(self)
927
928    def covered(self, extent):
929        """Return True if this extent is covered by the provided spatial
930        extent in three dimensions.
931
932        The following cases are excluded:
933
934        - contain
935        - in
936        - equivalent
937
938        :param extent: The spatial extent
939        :return: True or False
940        """
941
942        return extent.cover(self)
943
944    def overlap_2d(self, extent):
945        """Return True if this extent (A) overlaps with the provided spatial
946        extent (B) in two dimensions.
947        Code is lend from wind_overlap.c in lib/gis
948
949        ::
950
951             _____
952            |A  __|__
953            |  |  | B|
954            |__|__|  |
955               |_____|
956
957
958        The following cases are excluded:
959
960        - contain
961        - in
962        - cover
963        - covered
964        - equivalent
965
966        :param extent: The spatial extent
967        :return: True or False
968        """
969
970        if self.contain_2d(extent):
971            return False
972
973        if self.is_in_2d(extent):
974            return False
975
976        if self.cover_2d(extent):
977            return False
978
979        if self.covered_2d(extent):
980            return False
981
982        if self.equivalent_2d(extent):
983            return False
984
985        N = extent.get_north()
986        S = extent.get_south()
987        E = extent.get_east()
988        W = extent.get_west()
989
990        # Adjust the east and west in case of LL projection
991        if self.get_projection() == "LL":
992            while E < self.get_west():
993                E += 360.0
994                W += 360.0
995
996            while W > self.get_east():
997                E -= 360.0
998                W -= 360.0
999
1000        if(self.get_north() <= S):
1001            return False
1002
1003        if(self.get_south() >= N):
1004            return False
1005
1006        if self.get_east() <= W:
1007            return False
1008
1009        if self.get_west() >= E:
1010            return False
1011
1012        return True
1013
1014    def overlap(self, extent):
1015        """Return True if this extent overlaps with the provided spatial
1016        extent in three dimensions.
1017
1018        The following cases are excluded:
1019
1020        - contain
1021        - in
1022        - cover
1023        - covered
1024        - equivalent
1025
1026        :param extent: The spatial extent
1027        :return: True or False
1028        """
1029
1030        if self.is_in(extent):
1031            return False
1032
1033        if self.contain(extent):
1034            return False
1035
1036        if self.cover(extent):
1037            return False
1038
1039        if self.covered(extent):
1040            return False
1041
1042        if self.equivalent(extent):
1043            return False
1044
1045        N = extent.get_north()
1046        S = extent.get_south()
1047        E = extent.get_east()
1048        W = extent.get_west()
1049        T = extent.get_top()
1050        B = extent.get_bottom()
1051
1052        # Adjust the east and west in case of LL projection
1053        if self.get_projection() == "LL":
1054            while E < self.get_west():
1055                E += 360.0
1056                W += 360.0
1057
1058            while W > self.get_east():
1059                E -= 360.0
1060                W -= 360.0
1061
1062        if(self.get_north() <= S):
1063            return False
1064
1065        if(self.get_south() >= N):
1066            return False
1067
1068        if self.get_east() <= W:
1069            return False
1070
1071        if self.get_west() >= E:
1072            return False
1073
1074        if self.get_top() <= B:
1075            return False
1076
1077        if self.get_bottom() >= T:
1078            return False
1079
1080        return True
1081
1082    def meet_2d(self, extent):
1083        """Return True if this extent (A) meets with the provided spatial
1084        extent (B) in two dimensions.
1085
1086        ::
1087
1088              _____ _____
1089             |  A  |  B  |
1090             |_____|     |
1091                   |_____|
1092              _____ _____
1093             |  B  |  A  |
1094             |     |     |
1095             |_____|_____|
1096               ___
1097              | A |
1098              |   |
1099              |___|
1100             |  B  |
1101             |     |
1102             |_____|
1103              _____
1104             |  B  |
1105             |     |
1106             |_____|_
1107               |  A  |
1108               |     |
1109               |_____|
1110
1111
1112        :param extent: The spatial extent
1113        :return: True or False
1114        """
1115
1116        eN = extent.get_north()
1117        eS = extent.get_south()
1118        eE = extent.get_east()
1119        eW = extent.get_west()
1120
1121        N = self.get_north()
1122        S = self.get_south()
1123        E = self.get_east()
1124        W = self.get_west()
1125
1126        # Adjust the east and west in case of LL projection
1127        if self.get_projection() == "LL":
1128            while eE < W:
1129                eE += 360.0
1130                eW += 360.0
1131
1132            while eW > E:
1133                eE -= 360.0
1134                eW -= 360.0
1135
1136        edge = None
1137        edge_count = 0
1138
1139        if E == eW:
1140            edge = "E"
1141            edge_count += 1
1142        if W == eE:
1143            edge = "W"
1144            edge_count += 1
1145        if N == eS:
1146            edge = "N"
1147            edge_count += 1
1148        if S == eN:
1149            edge = "S"
1150            edge_count += 1
1151
1152        # Meet a a single edge only
1153        if edge_count != 1:
1154            return False
1155
1156        # Check boundaries of the faces
1157        if edge == "E" or edge == "W":
1158            if N < eS or S > eN:
1159                return False
1160
1161        if edge == "N" or edge == "S":
1162            if E < eW or W > eE:
1163                return False
1164
1165        return True
1166
1167    def meet(self, extent):
1168        """Return True if this extent meets with the provided spatial
1169        extent in three dimensions.
1170
1171        :param extent: The spatial extent
1172        :return: True or False
1173        """
1174        eN = extent.get_north()
1175        eS = extent.get_south()
1176        eE = extent.get_east()
1177        eW = extent.get_west()
1178
1179        eT = extent.get_top()
1180        eB = extent.get_bottom()
1181
1182        N = self.get_north()
1183        S = self.get_south()
1184        E = self.get_east()
1185        W = self.get_west()
1186
1187        T = self.get_top()
1188        B = self.get_bottom()
1189
1190        # Adjust the east and west in case of LL projection
1191        if self.get_projection() == "LL":
1192            while eE < W:
1193                eE += 360.0
1194                eW += 360.0
1195
1196            while eW > E:
1197                eE -= 360.0
1198                eW -= 360.0
1199
1200        edge = None
1201        edge_count = 0
1202
1203        if E == eW:
1204            edge = "E"
1205            edge_count += 1
1206        if W == eE:
1207            edge = "W"
1208            edge_count += 1
1209        if N == eS:
1210            edge = "N"
1211            edge_count += 1
1212        if S == eN:
1213            edge = "S"
1214            edge_count += 1
1215        if T == eB:
1216            edge = "T"
1217            edge_count += 1
1218        if B == eT:
1219            edge = "B"
1220            edge_count += 1
1221
1222        # Meet a single edge only
1223        if edge_count != 1:
1224            return False
1225
1226        # Check boundaries of the faces
1227        if edge == "E" or edge == "W":
1228            if N < eS or S > eN:
1229                return False
1230            if T < eB or B > eT:
1231                return False
1232
1233        if edge == "N" or edge == "S":
1234            if E < eW or W > eE:
1235                return False
1236            if T < eB or B > eT:
1237                return False
1238
1239        if edge == "T" or edge == "B":
1240            if E < eW or W > eE:
1241                return False
1242            if N < eS or S > eN:
1243                return False
1244
1245        return True
1246
1247    def disjoint_2d(self, extent):
1248        """Return True if this extent (A) is disjoint with the provided spatial
1249        extent (B) in three dimensions.
1250
1251        ::
1252
1253              _____
1254             |  A  |
1255             |_____|
1256             _______
1257            |   B   |
1258            |_______|
1259
1260
1261        :param extent: The spatial extent
1262        :return: True or False
1263        """
1264
1265        if self.is_in_2d(extent):
1266            return False
1267
1268        if self.contain_2d(extent):
1269            return False
1270
1271        if self.cover_2d(extent):
1272            return False
1273
1274        if self.covered_2d(extent):
1275            return False
1276
1277        if self.equivalent_2d(extent):
1278            return False
1279
1280        if self.overlapping_2d(extent):
1281            return False
1282
1283        if self.meet_2d(extent):
1284            return False
1285
1286        return True
1287
1288    def disjoint(self, extent):
1289        """Return True if this extent is disjoint with the provided spatial
1290        extent in three dimensions.
1291
1292        :param extent: The spatial extent
1293        :return: True or False
1294        """
1295
1296        if self.is_in(extent):
1297            return False
1298
1299        if self.contain(extent):
1300            return False
1301
1302        if self.cover(extent):
1303            return False
1304
1305        if self.covered(extent):
1306            return False
1307
1308        if self.equivalent(extent):
1309            return False
1310
1311        if self.overlapping(extent):
1312            return False
1313
1314        if self.meet(extent):
1315            return False
1316
1317        return True
1318
1319    def spatial_relation_2d(self, extent):
1320        """Returns the two dimensional spatial relation between this
1321        extent and the provided spatial extent in two dimensions.
1322
1323        Spatial relations are:
1324
1325        - disjoint
1326        - meet
1327        - overlap
1328        - cover
1329        - covered
1330        - in
1331        - contain
1332        - equivalent
1333
1334        Usage: see self.spatial_relation()
1335        """
1336
1337        if self.equivalent_2d(extent):
1338            return "equivalent"
1339        if self.contain_2d(extent):
1340            return "contain"
1341        if self.is_in_2d(extent):
1342            return "in"
1343        if self.cover_2d(extent):
1344            return "cover"
1345        if self.covered_2d(extent):
1346            return "covered"
1347        if self.overlap_2d(extent):
1348            return "overlap"
1349        if self.meet_2d(extent):
1350            return "meet"
1351        if self.disjoint_2d(extent):
1352            return "disjoint"
1353
1354        return "unknown"
1355
1356    def spatial_relation(self, extent):
1357        """Returns the two dimensional spatial relation between this
1358        extent and the provided spatial extent in three dimensions.
1359
1360        Spatial relations are:
1361
1362        - disjoint
1363        - meet
1364        - overlap
1365        - cover
1366        - covered
1367        - in
1368        - contain
1369        - equivalent
1370
1371
1372        Usage:
1373
1374        .. code-block:: python
1375
1376            >>> A = SpatialExtent(north=80, south=20, east=60, west=10, bottom=-50, top=50)
1377            >>> B = SpatialExtent(north=80, south=20, east=60, west=10, bottom=-50, top=50)
1378            >>> A.spatial_relation(B)
1379            'equivalent'
1380            >>> B.spatial_relation(A)
1381            'equivalent'
1382            >>> B = SpatialExtent(north=70, south=20, east=60, west=10, bottom=-50, top=50)
1383            >>> A.spatial_relation_2d(B)
1384            'cover'
1385            >>> A.spatial_relation(B)
1386            'cover'
1387            >>> B = SpatialExtent(north=70, south=30, east=60, west=10, bottom=-50, top=50)
1388            >>> A.spatial_relation_2d(B)
1389            'cover'
1390            >>> A.spatial_relation(B)
1391            'cover'
1392            >>> B.spatial_relation_2d(A)
1393            'covered'
1394            >>> B.spatial_relation(A)
1395            'covered'
1396            >>> B = SpatialExtent(north=70, south=30, east=50, west=10, bottom=-50, top=50)
1397            >>> A.spatial_relation_2d(B)
1398            'cover'
1399            >>> B.spatial_relation_2d(A)
1400            'covered'
1401            >>> A.spatial_relation(B)
1402            'cover'
1403            >>> B = SpatialExtent(north=70, south=30, east=50, west=20, bottom=-50, top=50)
1404            >>> B.spatial_relation(A)
1405            'covered'
1406            >>> B = SpatialExtent(north=70, south=30, east=50, west=20, bottom=-50, top=50)
1407            >>> A.spatial_relation_2d(B)
1408            'contain'
1409            >>> A.spatial_relation(B)
1410            'cover'
1411            >>> B = SpatialExtent(north=70, south=30, east=50, west=20, bottom=-40, top=50)
1412            >>> A.spatial_relation(B)
1413            'cover'
1414            >>> B = SpatialExtent(north=70, south=30, east=50, west=20, bottom=-40, top=40)
1415            >>> A.spatial_relation(B)
1416            'contain'
1417            >>> B.spatial_relation(A)
1418            'in'
1419            >>> B = SpatialExtent(north=90, south=30, east=50, west=20, bottom=-40, top=40)
1420            >>> A.spatial_relation_2d(B)
1421            'overlap'
1422            >>> A.spatial_relation(B)
1423            'overlap'
1424            >>> B = SpatialExtent(north=90, south=5, east=70, west=5, bottom=-40, top=40)
1425            >>> A.spatial_relation_2d(B)
1426            'in'
1427            >>> A.spatial_relation(B)
1428            'overlap'
1429            >>> B = SpatialExtent(north=90, south=5, east=70, west=5, bottom=-40, top=60)
1430            >>> A.spatial_relation(B)
1431            'overlap'
1432            >>> B = SpatialExtent(north=90, south=5, east=70, west=5, bottom=-60, top=60)
1433            >>> A.spatial_relation(B)
1434            'in'
1435            >>> A = SpatialExtent(north=80, south=60, east=60, west=10, bottom=-50, top=50)
1436            >>> B = SpatialExtent(north=60, south=20, east=60, west=10, bottom=-50, top=50)
1437            >>> A.spatial_relation_2d(B)
1438            'meet'
1439            >>> A.spatial_relation(B)
1440            'meet'
1441            >>> A = SpatialExtent(north=60, south=40, east=60, west=10, bottom=-50, top=50)
1442            >>> B = SpatialExtent(north=80, south=60, east=60, west=10, bottom=-50, top=50)
1443            >>> A.spatial_relation_2d(B)
1444            'meet'
1445            >>> A.spatial_relation(B)
1446            'meet'
1447            >>> A = SpatialExtent(north=80, south=40, east=60, west=40, bottom=-50, top=50)
1448            >>> B = SpatialExtent(north=80, south=40, east=40, west=20, bottom=-50, top=50)
1449            >>> A.spatial_relation_2d(B)
1450            'meet'
1451            >>> A.spatial_relation(B)
1452            'meet'
1453            >>> A = SpatialExtent(north=80, south=40, east=40, west=20, bottom=-50, top=50)
1454            >>> B = SpatialExtent(north=90, south=30, east=60, west=40, bottom=-50, top=50)
1455            >>> A.spatial_relation_2d(B)
1456            'meet'
1457            >>> A.spatial_relation(B)
1458            'meet'
1459            >>> A = SpatialExtent(north=80, south=40, east=40, west=20, bottom=-50, top=50)
1460            >>> B = SpatialExtent(north=70, south=50, east=60, west=40, bottom=-50, top=50)
1461            >>> A.spatial_relation_2d(B)
1462            'meet'
1463            >>> A.spatial_relation(B)
1464            'meet'
1465            >>> A = SpatialExtent(north=80, south=40, east=40, west=20, bottom=-50, top=50)
1466            >>> B = SpatialExtent(north=60, south=20, east=60, west=40, bottom=-50, top=50)
1467            >>> A.spatial_relation_2d(B)
1468            'meet'
1469            >>> A.spatial_relation(B)
1470            'meet'
1471            >>> A = SpatialExtent(north=80, south=40, east=40, west=20, bottom=-50, top=50)
1472            >>> B = SpatialExtent(north=40, south=20, east=60, west=40, bottom=-50, top=50)
1473            >>> A.spatial_relation_2d(B)
1474            'disjoint'
1475            >>> A.spatial_relation(B)
1476            'disjoint'
1477            >>> A = SpatialExtent(north=80, south=40, east=40, west=20, bottom=-50, top=50)
1478            >>> B = SpatialExtent(north=60, south=20, east=60, west=40, bottom=-60, top=60)
1479            >>> A.spatial_relation(B)
1480            'meet'
1481            >>> A = SpatialExtent(north=80, south=40, east=40, west=20, bottom=-50, top=50)
1482            >>> B = SpatialExtent(north=90, south=30, east=60, west=40, bottom=-40, top=40)
1483            >>> A.spatial_relation(B)
1484            'meet'
1485            >>> A = SpatialExtent(north=80, south=40, east=60, west=20, bottom=0, top=50)
1486            >>> B = SpatialExtent(north=80, south=40, east=60, west=20, bottom=-50, top=0)
1487            >>> A.spatial_relation(B)
1488            'meet'
1489            >>> A = SpatialExtent(north=80, south=40, east=60, west=20, bottom=0, top=50)
1490            >>> B = SpatialExtent(north=80, south=50, east=60, west=30, bottom=-50, top=0)
1491            >>> A.spatial_relation(B)
1492            'meet'
1493            >>> A = SpatialExtent(north=80, south=40, east=60, west=20, bottom=0, top=50)
1494            >>> B = SpatialExtent(north=70, south=50, east=50, west=30, bottom=-50, top=0)
1495            >>> A.spatial_relation(B)
1496            'meet'
1497            >>> A = SpatialExtent(north=80, south=40, east=60, west=20, bottom=0, top=50)
1498            >>> B = SpatialExtent(north=90, south=30, east=70, west=10, bottom=-50, top=0)
1499            >>> A.spatial_relation(B)
1500            'meet'
1501            >>> A = SpatialExtent(north=80, south=40, east=60, west=20, bottom=0, top=50)
1502            >>> B = SpatialExtent(north=70, south=30, east=50, west=10, bottom=-50, top=0)
1503            >>> A.spatial_relation(B)
1504            'meet'
1505            >>> A = SpatialExtent(north=80, south=40, east=60, west=20, bottom=-50, top=0)
1506            >>> B = SpatialExtent(north=80, south=40, east=60, west=20, bottom=0, top=50)
1507            >>> A.spatial_relation(B)
1508            'meet'
1509            >>> A = SpatialExtent(north=80, south=40, east=60, west=20, bottom=-50, top=0)
1510            >>> B = SpatialExtent(north=80, south=50, east=60, west=30, bottom=0, top=50)
1511            >>> A.spatial_relation(B)
1512            'meet'
1513            >>> A = SpatialExtent(north=80, south=40, east=60, west=20, bottom=-50, top=0)
1514            >>> B = SpatialExtent(north=70, south=50, east=50, west=30, bottom=0, top=50)
1515            >>> A.spatial_relation(B)
1516            'meet'
1517            >>> A = SpatialExtent(north=80, south=40, east=60, west=20, bottom=-50, top=0)
1518            >>> B = SpatialExtent(north=90, south=30, east=70, west=10, bottom=0, top=50)
1519            >>> A.spatial_relation(B)
1520            'meet'
1521            >>> A = SpatialExtent(north=80, south=40, east=60, west=20, bottom=-50, top=0)
1522            >>> B = SpatialExtent(north=70, south=30, east=50, west=10, bottom=0, top=50)
1523            >>> A.spatial_relation(B)
1524            'meet'
1525            >>> A = SpatialExtent(north=80, south=20, east=60, west=10, bottom=-50, top=50)
1526            >>> B = SpatialExtent(north=90, south=81, east=60, west=10, bottom=-50, top=50)
1527            >>> A.spatial_relation(B)
1528            'disjoint'
1529            >>> A = SpatialExtent(north=80, south=20, east=60, west=10, bottom=-50, top=50)
1530            >>> B = SpatialExtent(north=90, south=80, east=60, west=10, bottom=-50, top=50)
1531            >>> A.spatial_relation(B)
1532            'meet'
1533
1534        """
1535
1536        if self.equivalent(extent):
1537            return "equivalent"
1538        if self.contain(extent):
1539            return "contain"
1540        if self.is_in(extent):
1541            return "in"
1542        if self.cover(extent):
1543            return "cover"
1544        if self.covered(extent):
1545            return "covered"
1546        if self.overlap(extent):
1547            return "overlap"
1548        if self.meet(extent):
1549            return "meet"
1550        if self.disjoint(extent):
1551            return "disjoint"
1552
1553        return "unknown"
1554
1555    def set_spatial_extent_from_values(self, north, south, east, west, top,
1556                                       bottom):
1557        """Set the three dimensional spatial extent
1558
1559           :param north: The northern edge
1560           :param south: The southern edge
1561           :param east: The eastern edge
1562           :param west: The western edge
1563           :param top: The top edge
1564           :param bottom: The bottom edge
1565        """
1566
1567        self.set_north(north)
1568        self.set_south(south)
1569        self.set_east(east)
1570        self.set_west(west)
1571        self.set_top(top)
1572        self.set_bottom(bottom)
1573
1574    def set_spatial_extent(self, spatial_extent):
1575        """Set the three dimensional spatial extent
1576
1577           :param spatial_extent: An object of type SpatialExtent or its
1578                                  subclasses
1579        """
1580
1581        self.set_north(spatial_extent.get_north())
1582        self.set_south(spatial_extent.get_south())
1583        self.set_east(spatial_extent.get_east())
1584        self.set_west(spatial_extent.get_west())
1585        self.set_top(spatial_extent.get_top())
1586        self.set_bottom(spatial_extent.get_bottom())
1587
1588    def set_projection(self, proj):
1589        """Set the projection of the spatial extent it should be XY or LL.
1590           As default the projection is XY
1591        """
1592        if proj is None or (proj != "XY" and proj != "LL"):
1593            self.D["proj"] = "XY"
1594        else:
1595            self.D["proj"] = proj
1596
1597    def set_spatial_extent_from_values_2d(self, north, south, east, west):
1598        """Set the two dimensional spatial extent from values
1599
1600           :param north: The northern edge
1601           :param south: The southern edge
1602           :param east: The eastern edge
1603           :param west: The western edge
1604        """
1605
1606        self.set_north(north)
1607        self.set_south(south)
1608        self.set_east(east)
1609        self.set_west(west)
1610
1611    def set_spatial_extent_2d(self, spatial_extent):
1612        """Set the three dimensional spatial extent
1613
1614           :param spatial_extent: An object of type SpatialExtent or its
1615                                  subclasses
1616        """
1617
1618        self.set_north(spatial_extent.north)
1619        self.set_south(spatial_extent.south)
1620        self.set_east(spatial_extent.east)
1621        self.set_west(spatial_extent.west)
1622
1623    def set_id(self, ident):
1624        """Convenient method to set the unique identifier (primary key)"""
1625        self.ident = ident
1626        self.D["id"] = ident
1627
1628    def set_north(self, north):
1629        """Set the northern edge of the map"""
1630        if north is not None:
1631            self.D["north"] = float(north)
1632        else:
1633            self.D["north"] = None
1634
1635    def set_south(self, south):
1636        """Set the southern edge of the map"""
1637        if south is not None:
1638            self.D["south"] = float(south)
1639        else:
1640            self.D["south"] = None
1641
1642    def set_west(self, west):
1643        """Set the western edge of the map"""
1644        if west is not None:
1645            self.D["west"] = float(west)
1646        else:
1647            self.D["west"] = None
1648
1649    def set_east(self, east):
1650        """Set the eastern edge of the map"""
1651        if east is not None:
1652            self.D["east"] = float(east)
1653        else:
1654            self.D["east"] = None
1655
1656    def set_top(self, top):
1657        """Set the top edge of the map"""
1658        if top is not None:
1659            self.D["top"] = float(top)
1660        else:
1661            self.D["top"] = None
1662
1663    def set_bottom(self, bottom):
1664        """Set the bottom edge of the map"""
1665        if bottom is not None:
1666            self.D["bottom"] = float(bottom)
1667        else:
1668            self.D["bottom"] = None
1669
1670    def get_id(self):
1671        """Convenient method to get the unique identifier (primary key)
1672           :return: None if not found
1673        """
1674        if "id" in self.D:
1675            return self.D["id"]
1676        else:
1677            return None
1678
1679    def get_projection(self):
1680        """Get the projection of the spatial extent"""
1681        return self.D["proj"]
1682
1683    def get_volume(self):
1684        """Compute the volume of the extent, in case z is zero
1685           (top == bottom or top - bottom = 1) the area is returned"""
1686
1687        if self.get_projection() == "LL":
1688            self.msgr.error(_("Volume computation is not supported "
1689                              "for LL projections"))
1690
1691        area = self.get_area()
1692
1693        bbox = self.get_spatial_extent_as_tuple()
1694
1695        z = abs(bbox[4] - bbox[5])
1696
1697        if z == 0:
1698            z = 1.0
1699
1700        return area * z
1701
1702    def get_area(self):
1703        """Compute the area of the extent, extent in z direction is ignored"""
1704
1705        if self.get_projection() == "LL":
1706            self.msgr.error(_("Area computation is not supported "
1707                              "for LL projections"))
1708
1709        bbox = self.get_spatial_extent_as_tuple()
1710
1711        y = abs(bbox[0] - bbox[1])
1712        x = abs(bbox[2] - bbox[3])
1713
1714        return x * y
1715
1716    def get_spatial_extent_as_tuple(self):
1717        """Return a tuple (north, south, east, west, top, bottom)
1718           of the spatial extent"""
1719
1720        return (
1721            self.north, self.south, self.east, self.west,
1722            self.top, self.bottom)
1723
1724    def get_spatial_extent_as_tuple_2d(self):
1725        """Return a tuple (north, south, east, west,) of the 2d spatial extent
1726        """
1727        return (self.north, self.south, self.east, self.west)
1728
1729    def get_north(self):
1730        """Get the northern edge of the map
1731           :return: None if not found"""
1732        if "north" in self.D:
1733            return self.D["north"]
1734        else:
1735            return None
1736
1737    def get_south(self):
1738        """Get the southern edge of the map
1739           :return: None if not found"""
1740        if "south" in self.D:
1741            return self.D["south"]
1742        else:
1743            return None
1744
1745    def get_east(self):
1746        """Get the eastern edge of the map
1747           :return: None if not found"""
1748        if "east" in self.D:
1749            return self.D["east"]
1750        else:
1751            return None
1752
1753    def get_west(self):
1754        """Get the western edge of the map
1755           :return: None if not found"""
1756        if "west" in self.D:
1757            return self.D["west"]
1758        else:
1759            return None
1760
1761    def get_top(self):
1762        """Get the top edge of the map
1763           :return: None if not found"""
1764        if "top" in self.D:
1765            return self.D["top"]
1766        else:
1767            return None
1768
1769    def get_bottom(self):
1770        """Get the bottom edge of the map
1771           :return: None if not found"""
1772        if "bottom" in self.D:
1773            return self.D["bottom"]
1774        else:
1775            return None
1776
1777    id = property(fget=get_id, fset=set_id)
1778    north = property(fget=get_north, fset=set_north)
1779    south = property(fget=get_south, fset=set_south)
1780    east = property(fget=get_east, fset=set_east)
1781    west = property(fget=get_west, fset=set_west)
1782    top = property(fget=get_top, fset=set_top)
1783    bottom = property(fget=get_bottom, fset=set_bottom)
1784
1785    def print_info(self):
1786        """Print information about this class in human readable style"""
1787        #      0123456789012345678901234567890
1788        print(" +-------------------- Spatial extent ----------------------------------------+")
1789        print(" | North:...................... " + str(self.get_north()))
1790        print(" | South:...................... " + str(self.get_south()))
1791        print(" | East:.. .................... " + str(self.get_east()))
1792        print(" | West:....................... " + str(self.get_west()))
1793        print(" | Top:........................ " + str(self.get_top()))
1794        print(" | Bottom:..................... " + str(self.get_bottom()))
1795
1796    def print_shell_info(self):
1797        """Print information about this class in shell style"""
1798        print("north=" + str(self.get_north()))
1799        print("south=" + str(self.get_south()))
1800        print("east=" + str(self.get_east()))
1801        print("west=" + str(self.get_west()))
1802        print("top=" + str(self.get_top()))
1803        print("bottom=" + str(self.get_bottom()))
1804
1805
1806###############################################################################
1807
1808class RasterSpatialExtent(SpatialExtent):
1809    def __init__(self, ident=None, north=None, south=None, east=None,
1810                 west=None, top=None, bottom=None):
1811        SpatialExtent.__init__(self, "raster_spatial_extent",
1812                               ident, north, south, east, west, top, bottom)
1813
1814
1815class Raster3DSpatialExtent(SpatialExtent):
1816    def __init__(self, ident=None, north=None, south=None, east=None,
1817                 west=None, top=None, bottom=None):
1818        SpatialExtent.__init__(self, "raster3d_spatial_extent",
1819                               ident, north, south, east, west, top, bottom)
1820
1821
1822class VectorSpatialExtent(SpatialExtent):
1823    def __init__(self, ident=None, north=None, south=None, east=None,
1824                 west=None, top=None, bottom=None):
1825        SpatialExtent.__init__(self, "vector_spatial_extent",
1826                               ident, north, south, east, west, top, bottom)
1827
1828
1829class STRDSSpatialExtent(SpatialExtent):
1830    def __init__(self, ident=None, north=None, south=None, east=None,
1831                 west=None, top=None, bottom=None):
1832        SpatialExtent.__init__(self, "strds_spatial_extent",
1833                               ident, north, south, east, west, top, bottom)
1834
1835
1836class STR3DSSpatialExtent(SpatialExtent):
1837    def __init__(self, ident=None, north=None, south=None, east=None,
1838                 west=None, top=None, bottom=None):
1839        SpatialExtent.__init__(self, "str3ds_spatial_extent",
1840                               ident, north, south, east, west, top, bottom)
1841
1842
1843class STVDSSpatialExtent(SpatialExtent):
1844    def __init__(self, ident=None, north=None, south=None, east=None,
1845                 west=None, top=None, bottom=None):
1846        SpatialExtent.__init__(self, "stvds_spatial_extent",
1847                               ident, north, south, east, west, top, bottom)
1848
1849###############################################################################
1850
1851if __name__ == "__main__":
1852    import doctest
1853    doctest.testmod()
1854