Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

from django.contrib.gis.db import models 

from django.conf import settings 

from django.contrib.gis.measure import A, D 

from madrona.unit_converter.models import length_in_display_units, area_in_display_units 

from madrona.common.utils import KmlWrap, ComputeLookAt 

from django.contrib.gis.geos import Point, Polygon, LinearRing 

 

class StudyRegionManager(models.GeoManager): 

    """Returns the currently active study region. The active study region is  

    determined by the active attribute. 

    """ 

    def current(self): 

        return self.get(active=True) 

        #or should the above line now be changed to: 

        #return self.current() 

 

class StudyRegion(models.Model): 

    """Model used for representing study regions 

 

        ======================  ============================================== 

        Attribute               Description 

        ======================  ============================================== 

 

        ``name``                Name of the Study Region 

 

        ``active``              Whether the study region is treated as the 

                                current authoritative copy. This should not be 

                                set using the admin interface 

 

        ``creation_date``       Automatically assigned 

 

        ``modification_date``   Automatically assigned 

 

        ``geometry``            PolygonField representing the study region boundary 

 

        ``date_modified``       When the Study Region geometry was last updated. 

 

        ``lookAt_Lat``          Latitude of the default look-at point 

 

        ``lookAt_Lon``          Longitude of the default look-at point 

 

        ``lookAt_Range``        Range camera sits from the default look-at point 

 

        ``lookAt_Tilt``         Angle offset from vertical for default camera pos 

 

        ``lookAt_Heading``      Angle offset from North for default camera pos 

 

        ======================  ============================================== 

""" 

    name = models.CharField(verbose_name="Study Region Name", max_length=255) 

 

    active = models.BooleanField(default=False, help_text='This options will usually not be set using the admin interface, but rather by using the management commands relating to study region changes.') 

 

    creation_date = models.DateTimeField(auto_now_add=True) 

    modification_date = models.DateTimeField(auto_now=True) 

 

    geometry = models.MultiPolygonField(srid=settings.GEOMETRY_DB_SRID, null=True, blank=True, verbose_name="Study region boundary") 

 

    date_modified = models.DateTimeField(auto_now=True, verbose_name="Date Modified") 

 

    lookAt_Lat = models.FloatField(default=0, null=True, blank=True) 

    lookAt_Lon = models.FloatField(default=0, null=True, blank=True) 

    lookAt_Range = models.FloatField(default=80000, help_text='Distance from lookAt point in meters') 

    lookAt_Tilt = models.FloatField(default=0, help_text='Degrees from vertical (0=directly above)') 

    lookAt_Heading = models.FloatField(default=0, help_text='View direction in degrees (0=look North)') 

 

    objects = StudyRegionManager() 

 

    class Meta: 

        db_table = u'mm_study_region' 

 

    def __unicode__(self): 

        return u'%s' % self.name 

 

    def save(self, *args, **kwargs): 

        super(StudyRegion, self).save(*args, **kwargs) 

        if self.active and StudyRegion.objects.filter(active=True).count() > 1: 

            # Ensure that any previously active study region is deactivated 

            # There can be only one! 

            StudyRegion.objects.filter(active=True).exclude(pk=self.pk).update(active=False) 

 

    @models.permalink 

    def get_absolute_url(self): 

        return ('madrona.studyregion.views.show', [self.pk]) 

 

    @property 

    def area_sq_mi(self): 

        """ 

        WARNING:  This method assumes that the native units of the geometry are meters.   

        Returns the area of the study region in sq_mi 

        """ 

        return area_in_display_units(self.geometry.area) 

 

    def kml(self, style_domain): 

        """ 

        Get the kml of the entire study region 

        """ 

        bUseLod = False 

 

        if not bUseLod: 

            transform_geom = self.geometry.simplify(settings.KML_SIMPLIFY_TOLERANCE, preserve_topology=True) 

            transform_geom.transform(4326) 

 

            shape_kml = transform_geom.kml 

 

            # remove Polygon, outerBoundaryIs, innerBoundaryIs tags 

            shape_kml = shape_kml.replace('<Polygon>', '') 

            shape_kml = shape_kml.replace('</Polygon>', '') 

            shape_kml = shape_kml.replace('<outerBoundaryIs>', '') 

            shape_kml = shape_kml.replace('</outerBoundaryIs>', '') 

            shape_kml = shape_kml.replace('<innerBoundaryIs>', '') 

            shape_kml = shape_kml.replace('</innerBoundaryIs>', '') 

 

            return '<Document><name>%s</name>' % (self.name,) + self.lookAtKml() + '<Placemark><name>Study Region Boundaries</name>%s<styleUrl>http://%s/media/studyregion/styles.kml#StudyRegionStyle</styleUrl>%s</Placemark></Document>' % (self.lookAtKml(), style_domain, shape_kml,) 

 

        else: 

            # use the kml_chunk LOD system, 

            trans_geom = self.geometry.clone() # cloning here to avoid stepping into subfunctions with a mutated self.geometry 

            trans_geom.transform(4326) 

 

            w = trans_geom.extent[0] 

            s = trans_geom.extent[1] 

            e = trans_geom.extent[2] 

            n = trans_geom.extent[3] 

 

            return '<Document><name>%s</name>' % (self.name,) + self.lookAtKml() + self.kml_chunk(n,s,e,w) + '</Document>' 

 

    # NOTE: not currently used, LOD system overhead not justified by performance 

    def kml_chunk(self, n, s, e, w): 

        """ 

        Get the kml of a lat/lon bounded part of the study region,  

        with geometry simplified in proportion to the visible % of the region 

        """ 

 

        bounds = Polygon(LinearRing([Point(w, n), Point(e, n), Point(e, s), Point(w, s), Point(w, n)])) 

        bounds.set_srid(4326) 

        center_lat = bounds.centroid.y # in 4326 because it is used only for setting up the subregion calls 

        center_lon = bounds.centroid.x # in 4326 because it is used only for setting up the subregion calls 

        bounds.transform(settings.GEOMETRY_DB_SRID) 

 

        # all longitudinal width calcs should be done in GEOMETRY_DB_SRID - 4326 can fail across the date line 

        zoom_width = (Point(bounds.extent[0], bounds.centroid.y)).distance(Point(bounds.extent[2], bounds.centroid.y)) 

 

        full_shape_width = (Point(self.geometry.extent[0], self.geometry.centroid.y)).distance(Point(self.geometry.extent[2], self.geometry.centroid.y)) 

 

        # The following simplify values can be tuned to your preference 

        # minimum geometry simplify value (highest detail) = 50 (arbitrary, based on observation) 

        # maximum geometry simplify value = 200 (arbitrary, based on observation) 

        # value set by pecentage of study region width requested in this chunk 

        min_simplify_val = 50.0 

        max_simplify_val = 200.0 

        simplify_factor = max(min_simplify_val, min(max_simplify_val, max_simplify_val * zoom_width / full_shape_width)) 

 

        transform_geom = self.geometry.simplify(simplify_factor, preserve_topology=True) 

        transform_geom = transform_geom.intersection(bounds) 

        transform_geom.transform(4326) 

 

        # Debugging info 

        #print zoom_width 

        #print full_shape_width 

        #print simplify_factor 

        #print transform_geom.num_coords 

        # End debugging info 

 

        # only add sub-regions if this is not our highest detail level 

        bLastLodLevel = simplify_factor < max_simplify_val # change this last value to build varying levels of LOD 

        max_lod_pixels = 500 

        min_lod_pixels = 250 

 

        # make sure the most detailed lod stays active no matter how close user zooms 

        if bLastLodLevel: 

            max_lod_pixels = -1 

 

        retval = '<Region><LatLonAltBox><north>%f</north><south>%f</south><east>%f</east><west>%f</west></LatLonAltBox><Lod><minLodPixels>%f</minLodPixels><maxLodPixels>%f</maxLodPixels><minFadeExtent>0</minFadeExtent><maxFadeExtent>0</maxFadeExtent></Lod></Region>' % (n, s, e, w, min_lod_pixels, max_lod_pixels) + '<Placemark> <name>Study Region Boundaries</name><Style> <LineStyle> <color>ff00ffff</color> <width>2</width> </LineStyle> <PolyStyle> <color>8000ffff</color> </PolyStyle></Style>%s</Placemark>' % (transform_geom.kml,) 

 

        # conditionally add sub-regions 

        if not bLastLodLevel: 

            subregions = '<Folder><name>Study Region LODs</name>' + '<Folder><name>SE</name>' + self.kml_chunk(center_lat, s, e, center_lon) + '</Folder>' 

 

            subregions = subregions + '<Folder><name>NE</name>' + self.kml_chunk(n, center_lat, e, center_lon) + '</Folder>' 

 

            subregions = subregions + '<Folder><name>SW</name>' + self.kml_chunk(center_lat, s, center_lon, w) + '</Folder>' 

 

            subregions = subregions + '<Folder><name>NW</name>' + self.kml_chunk(n, center_lat, center_lon, w) + '</Folder>' 

 

            retval = retval + subregions + '</Folder>' 

 

        return retval 

 

    def lookAtKml(self): 

        """ 

        Get the kml for the region's lookat values saved in the DB, 

        or compute them if they are set to 0 

        """ 

 

        if self.lookAt_Lat == 0.0 and self.lookAt_Lon == 0.0: 

            self.cacheLookAt() 

 

        retval = '<LookAt><latitude>%f</latitude><longitude>%f</longitude><range>%f</range><tilt>%f</tilt><heading>%f</heading><altitudeMode>clampToGround</altitudeMode></LookAt>' % (self.lookAt_Lat, self.lookAt_Lon, self.lookAt_Range, self.lookAt_Tilt, self.lookAt_Heading) 

        return retval 

 

    def cacheLookAt(self): 

        """ 

        Compute and store the camera perspective that puts the whole study region in view 

        """ 

 

        lookAtParams = ComputeLookAt(self.geometry) 

 

        self.lookAt_Range = lookAtParams['range'] 

        self.lookAt_Lat = lookAtParams['latitude'] 

        self.lookAt_Lon = lookAtParams['longitude'] 

        self.lookAt_Tilt = lookAtParams['tilt'] 

        self.lookAt_Heading = lookAtParams['heading'] 

 

        self.save() 

 

#    def updated(self): 

 

        # expire reports and report caches 

 

        # reclip MPA's