Package madrona :: Package studyregion :: Module models
[hide private]

Source Code for Module madrona.studyregion.models

  1  from django.contrib.gis.db import models 
  2  from django.conf import settings 
  3  from django.contrib.gis.measure import A, D 
  4  from madrona.unit_converter.models import length_in_display_units, area_in_display_units 
  5  from madrona.common.utils import KmlWrap, ComputeLookAt 
  6  from django.contrib.gis.geos import Point, Polygon, LinearRing 
7 8 -class StudyRegionManager(models.GeoManager):
9 """Returns the currently active study region. The active study region is 10 determined by the active attribute. 11 """
12 - def current(self):
13 return self.get(active=True)
14 #or should the above line now be changed to:
15 #return self.current() 16 17 -class StudyRegion(models.Model):
18 """Model used for representing study regions 19 20 ====================== ============================================== 21 Attribute Description 22 ====================== ============================================== 23 24 ``name`` Name of the Study Region 25 26 ``active`` Whether the study region is treated as the 27 current authoritative copy. This should not be 28 set using the admin interface 29 30 ``creation_date`` Automatically assigned 31 32 ``modification_date`` Automatically assigned 33 34 ``geometry`` PolygonField representing the study region boundary 35 36 ``date_modified`` When the Study Region geometry was last updated. 37 38 ``lookAt_Lat`` Latitude of the default look-at point 39 40 ``lookAt_Lon`` Longitude of the default look-at point 41 42 ``lookAt_Range`` Range camera sits from the default look-at point 43 44 ``lookAt_Tilt`` Angle offset from vertical for default camera pos 45 46 ``lookAt_Heading`` Angle offset from North for default camera pos 47 48 ====================== ============================================== 49 """ 50 name = models.CharField(verbose_name="Study Region Name", max_length=255) 51 52 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.') 53 54 creation_date = models.DateTimeField(auto_now_add=True) 55 modification_date = models.DateTimeField(auto_now=True) 56 57 geometry = models.MultiPolygonField(srid=settings.GEOMETRY_DB_SRID, null=True, blank=True, verbose_name="Study region boundary") 58 59 date_modified = models.DateTimeField(auto_now=True, verbose_name="Date Modified") 60 61 lookAt_Lat = models.FloatField(default=0, null=True, blank=True) 62 lookAt_Lon = models.FloatField(default=0, null=True, blank=True) 63 lookAt_Range = models.FloatField(default=80000, help_text='Distance from lookAt point in meters') 64 lookAt_Tilt = models.FloatField(default=0, help_text='Degrees from vertical (0=directly above)') 65 lookAt_Heading = models.FloatField(default=0, help_text='View direction in degrees (0=look North)') 66 67 objects = StudyRegionManager() 68
69 - class Meta:
70 db_table = u'mm_study_region'
71
72 - def __unicode__(self):
73 return u'%s' % self.name
74
75 - def save(self, *args, **kwargs):
76 super(StudyRegion, self).save(*args, **kwargs) 77 if self.active and StudyRegion.objects.filter(active=True).count() > 1: 78 # Ensure that any previously active study region is deactivated 79 # There can be only one! 80 StudyRegion.objects.filter(active=True).exclude(pk=self.pk).update(active=False)
81 82 @models.permalink
83 - def get_absolute_url(self):
84 return ('madrona.studyregion.views.show', [self.pk])
85 86 @property
87 - def area_sq_mi(self):
88 """ 89 WARNING: This method assumes that the native units of the geometry are meters. 90 Returns the area of the study region in sq_mi 91 """ 92 return area_in_display_units(self.geometry.area)
93
94 - def kml(self, style_domain):
95 """ 96 Get the kml of the entire study region 97 """ 98 bUseLod = False 99 100 if not bUseLod: 101 transform_geom = self.geometry.simplify(settings.KML_SIMPLIFY_TOLERANCE, preserve_topology=True) 102 transform_geom.transform(4326) 103 104 shape_kml = transform_geom.kml 105 106 # remove Polygon, outerBoundaryIs, innerBoundaryIs tags 107 shape_kml = shape_kml.replace('<Polygon>', '') 108 shape_kml = shape_kml.replace('</Polygon>', '') 109 shape_kml = shape_kml.replace('<outerBoundaryIs>', '') 110 shape_kml = shape_kml.replace('</outerBoundaryIs>', '') 111 shape_kml = shape_kml.replace('<innerBoundaryIs>', '') 112 shape_kml = shape_kml.replace('</innerBoundaryIs>', '') 113 114 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,) 115 116 else: 117 # use the kml_chunk LOD system, 118 trans_geom = self.geometry.clone() # cloning here to avoid stepping into subfunctions with a mutated self.geometry 119 trans_geom.transform(4326) 120 121 w = trans_geom.extent[0] 122 s = trans_geom.extent[1] 123 e = trans_geom.extent[2] 124 n = trans_geom.extent[3] 125 126 return '<Document><name>%s</name>' % (self.name,) + self.lookAtKml() + self.kml_chunk(n,s,e,w) + '</Document>'
127 128 # NOTE: not currently used, LOD system overhead not justified by performance
129 - def kml_chunk(self, n, s, e, w):
130 """ 131 Get the kml of a lat/lon bounded part of the study region, 132 with geometry simplified in proportion to the visible % of the region 133 """ 134 135 bounds = Polygon(LinearRing([Point(w, n), Point(e, n), Point(e, s), Point(w, s), Point(w, n)])) 136 bounds.set_srid(4326) 137 center_lat = bounds.centroid.y # in 4326 because it is used only for setting up the subregion calls 138 center_lon = bounds.centroid.x # in 4326 because it is used only for setting up the subregion calls 139 bounds.transform(settings.GEOMETRY_DB_SRID) 140 141 # all longitudinal width calcs should be done in GEOMETRY_DB_SRID - 4326 can fail across the date line 142 zoom_width = (Point(bounds.extent[0], bounds.centroid.y)).distance(Point(bounds.extent[2], bounds.centroid.y)) 143 144 full_shape_width = (Point(self.geometry.extent[0], self.geometry.centroid.y)).distance(Point(self.geometry.extent[2], self.geometry.centroid.y)) 145 146 # The following simplify values can be tuned to your preference 147 # minimum geometry simplify value (highest detail) = 50 (arbitrary, based on observation) 148 # maximum geometry simplify value = 200 (arbitrary, based on observation) 149 # value set by pecentage of study region width requested in this chunk 150 min_simplify_val = 50.0 151 max_simplify_val = 200.0 152 simplify_factor = max(min_simplify_val, min(max_simplify_val, max_simplify_val * zoom_width / full_shape_width)) 153 154 transform_geom = self.geometry.simplify(simplify_factor, preserve_topology=True) 155 transform_geom = transform_geom.intersection(bounds) 156 transform_geom.transform(4326) 157 158 # Debugging info 159 #print zoom_width 160 #print full_shape_width 161 #print simplify_factor 162 #print transform_geom.num_coords 163 # End debugging info 164 165 # only add sub-regions if this is not our highest detail level 166 bLastLodLevel = simplify_factor < max_simplify_val # change this last value to build varying levels of LOD 167 max_lod_pixels = 500 168 min_lod_pixels = 250 169 170 # make sure the most detailed lod stays active no matter how close user zooms 171 if bLastLodLevel: 172 max_lod_pixels = -1 173 174 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,) 175 176 # conditionally add sub-regions 177 if not bLastLodLevel: 178 subregions = '<Folder><name>Study Region LODs</name>' + '<Folder><name>SE</name>' + self.kml_chunk(center_lat, s, e, center_lon) + '</Folder>' 179 180 subregions = subregions + '<Folder><name>NE</name>' + self.kml_chunk(n, center_lat, e, center_lon) + '</Folder>' 181 182 subregions = subregions + '<Folder><name>SW</name>' + self.kml_chunk(center_lat, s, center_lon, w) + '</Folder>' 183 184 subregions = subregions + '<Folder><name>NW</name>' + self.kml_chunk(n, center_lat, center_lon, w) + '</Folder>' 185 186 retval = retval + subregions + '</Folder>' 187 188 return retval
189
190 - def lookAtKml(self):
191 """ 192 Get the kml for the region's lookat values saved in the DB, 193 or compute them if they are set to 0 194 """ 195 196 if self.lookAt_Lat == 0.0 and self.lookAt_Lon == 0.0: 197 self.cacheLookAt() 198 199 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) 200 return retval
201
202 - def cacheLookAt(self):
203 """ 204 Compute and store the camera perspective that puts the whole study region in view 205 """ 206 207 lookAtParams = ComputeLookAt(self.geometry) 208 209 self.lookAt_Range = lookAtParams['range'] 210 self.lookAt_Lat = lookAtParams['latitude'] 211 self.lookAt_Lon = lookAtParams['longitude'] 212 self.lookAt_Tilt = lookAtParams['tilt'] 213 self.lookAt_Heading = lookAtParams['heading'] 214 215 self.save()
216 217 # def updated(self): 218 219 # expire reports and report caches 220 221 # reclip MPA's 222