1 |
# Copyright (c) 2001, 2002, 2003, 2004 by Intevation GmbH |
# Copyright (c) 2001, 2002, 2003, 2004, 2005 by Intevation GmbH |
2 |
# Authors: |
# Authors: |
3 |
# Bernhard Herzog <[email protected]> |
# Bernhard Herzog <[email protected]> |
4 |
# Jonathan Coles <[email protected]> |
# Jonathan Coles <[email protected]> |
12 |
import os |
import os |
13 |
import warnings |
import warnings |
14 |
|
|
15 |
|
from wxproj import point_in_polygon_shape, shape_centroid |
16 |
|
|
17 |
from Thuban import _ |
from Thuban import _ |
18 |
|
|
19 |
from messages import LAYER_PROJECTION_CHANGED, LAYER_VISIBILITY_CHANGED, \ |
from messages import LAYER_PROJECTION_CHANGED, LAYER_VISIBILITY_CHANGED, \ |
24 |
from color import Transparent, Black |
from color import Transparent, Black |
25 |
from base import TitledObject, Modifiable |
from base import TitledObject, Modifiable |
26 |
from data import SHAPETYPE_POLYGON, SHAPETYPE_ARC, SHAPETYPE_POINT |
from data import SHAPETYPE_POLYGON, SHAPETYPE_ARC, SHAPETYPE_POINT |
27 |
|
from label import ALIGN_CENTER, ALIGN_TOP, ALIGN_BOTTOM, \ |
28 |
|
ALIGN_LEFT, ALIGN_RIGHT |
29 |
|
|
30 |
import resource |
import resource |
31 |
|
|
241 |
# Ensure that region lies within the layer's bounding box |
# Ensure that region lies within the layer's bounding box |
242 |
# Otherwise projection of the region would lead to incorrect |
# Otherwise projection of the region would lead to incorrect |
243 |
# values. |
# values. |
244 |
clipbbox = self.ClipBoundingBox(bbox) |
clipbbox = self.__mangle_bounding_box(bbox) |
245 |
bbox = self.projection.ForwardBBox(clipbbox) |
bbox = self.projection.ForwardBBox(clipbbox) |
246 |
return self.store.ShapesInRegion(bbox) |
return self.store.ShapesInRegion(bbox) |
247 |
|
|
318 |
|
|
319 |
return (_("Layer '%s'") % self.Title(), items) |
return (_("Layer '%s'") % self.Title(), items) |
320 |
|
|
321 |
def ClipBoundingBox(self, bbox): |
def __mangle_bounding_box(self, bbox): |
322 |
""" Clip bbox to layer's bounding box. |
# FIXME: This method doesn't make much sense. |
323 |
|
# See RT #2845 which effectively says: |
324 |
Returns that part of bbox that lies within the layers bounding box. |
# |
325 |
If bbox is completely outside of the layers bounding box, bbox is |
# If this method, which was originally called ClipBoundingBox, |
326 |
returned. It is assumed that bbox has sensible values, i.e. bminx |
# is supposed to do clipping it shouldn't return the parameter |
327 |
< bmaxx and bminy < bmaxy. |
# unchanged when it lies completely outside of the bounding box. |
328 |
""" |
# It would be better to return None and return an empty list in |
329 |
|
# ShapesInRegion (the only caller) in that case. |
330 |
|
# |
331 |
|
# This method was introduced to fix a bug that IIRC had |
332 |
|
# something todo with projections and bounding boxes containing |
333 |
|
# NaN or INF when the parameter to ShapesInRegion covered the |
334 |
|
# entire earth or something similarly large). |
335 |
bminx, bminy, bmaxx, bmaxy = bbox |
bminx, bminy, bmaxx, bmaxy = bbox |
336 |
lminx, lminy, lmaxx, lmaxy = self.LatLongBoundingBox() |
lminx, lminy, lmaxx, lmaxy = self.LatLongBoundingBox() |
337 |
if bminx > lmaxx or bmaxx < lminx: |
if bminx > lmaxx or bmaxx < lminx: |
344 |
else: |
else: |
345 |
bottom = max(lminy, bminy) |
bottom = max(lminy, bminy) |
346 |
top = min(lmaxy, bmaxy) |
top = min(lmaxy, bmaxy) |
347 |
|
|
348 |
return (left, bottom, right, top) |
return (left, bottom, right, top) |
349 |
|
|
350 |
|
def GetLabelPosFromShape(self, cmap, shape_index): |
351 |
|
''' |
352 |
|
Return the label position parameters (x, y, halign, valign) from the |
353 |
|
shape object |
354 |
|
''' |
355 |
|
proj = cmap.projection |
356 |
|
if proj is not None: |
357 |
|
map_proj = proj |
358 |
|
else: |
359 |
|
map_proj = None |
360 |
|
proj = self.projection |
361 |
|
if proj is not None: |
362 |
|
layer_proj = proj |
363 |
|
else: |
364 |
|
layer_proj = None |
365 |
|
|
366 |
|
shapetype = self.ShapeType() |
367 |
|
if shapetype == SHAPETYPE_POLYGON: |
368 |
|
shapefile = self.ShapeStore().Shapefile().cobject() |
369 |
|
x, y = shape_centroid(shapefile, shape_index, |
370 |
|
map_proj, layer_proj, 1, 1, 0, 0) |
371 |
|
if map_proj is not None: |
372 |
|
x, y = map_proj.Inverse(x, y) |
373 |
|
else: |
374 |
|
shape = self.Shape(shape_index) |
375 |
|
if shapetype == SHAPETYPE_POINT: |
376 |
|
x, y = shape.Points()[0][0] |
377 |
|
else: |
378 |
|
# assume SHAPETYPE_ARC |
379 |
|
points = shape.Points()[0] |
380 |
|
x, y = points[len(points) / 2] |
381 |
|
if layer_proj is not None: |
382 |
|
x, y = layer_proj.Inverse(x, y) |
383 |
|
if shapetype == SHAPETYPE_POINT: |
384 |
|
halign = ALIGN_LEFT |
385 |
|
valign = ALIGN_CENTER |
386 |
|
elif shapetype == SHAPETYPE_POLYGON: |
387 |
|
halign = ALIGN_CENTER |
388 |
|
valign = ALIGN_CENTER |
389 |
|
elif shapetype == SHAPETYPE_ARC: |
390 |
|
halign = ALIGN_LEFT |
391 |
|
valign = ALIGN_CENTER |
392 |
|
|
393 |
|
return (x, y, halign, valign) |
394 |
|
|
395 |
|
|
396 |
|
|
397 |
if resource.has_gdal_support(): |
if resource.has_gdal_support(): |
398 |
import gdal |
import gdal |
400 |
|
|
401 |
class RasterLayer(BaseLayer): |
class RasterLayer(BaseLayer): |
402 |
|
|
403 |
def __init__(self, title, filename, projection = None, visible = True): |
MASK_NONE = 0 |
404 |
|
MASK_BIT = 1 |
405 |
|
MASK_ALPHA = 2 |
406 |
|
|
407 |
|
def __init__(self, title, filename, projection = None, |
408 |
|
visible = True, opacity = 1, masktype = MASK_BIT): |
409 |
"""Initialize the Raster Layer. |
"""Initialize the Raster Layer. |
410 |
|
|
411 |
title -- title for the layer. |
title -- title for the layer. |
428 |
|
|
429 |
self.bbox = -1 |
self.bbox = -1 |
430 |
|
|
431 |
self.use_mask = False |
self.mask_type = masktype |
432 |
|
self.opacity = opacity |
433 |
|
|
434 |
self.image_info = None |
self.image_info = None |
435 |
|
|
520 |
def GetImageFilename(self): |
def GetImageFilename(self): |
521 |
return self.filename |
return self.filename |
522 |
|
|
523 |
def UseMask(self): |
def MaskType(self): |
524 |
"""Return True if the mask should be used when rendering the layer.""" |
"""Return True if the mask should be used when rendering the layer.""" |
525 |
return self.use_mask |
return self.mask_type |
526 |
|
|
527 |
|
def SetMaskType(self, type): |
528 |
|
"""Set the type of mask to use. |
529 |
|
|
530 |
def SetUseMask(self, use): |
type can be one of MASK_NONE, MASK_BIT, MASK_ALPHA |
|
"""Set whether to use a mask when render the image. |
|
531 |
|
|
532 |
If the state changes, a LAYER_CHANGED message is sent. |
If the state changes, a LAYER_CHANGED message is sent. |
533 |
""" |
""" |
534 |
if use != self.use_mask: |
if type not in (self.MASK_NONE, self.MASK_BIT, self.MASK_ALPHA): |
535 |
self.use_mask = use |
raise ValueError("type is invalid") |
536 |
|
|
537 |
|
if type != self.mask_type: |
538 |
|
self.mask_type = type |
539 |
|
self.changed(LAYER_CHANGED, self) |
540 |
|
|
541 |
|
def Opacity(self): |
542 |
|
"""Return the level of opacity used in alpha blending. |
543 |
|
""" |
544 |
|
return self.opacity |
545 |
|
|
546 |
|
def SetOpacity(self, op): |
547 |
|
"""Set the level of alpha opacity. |
548 |
|
|
549 |
|
0 <= op <= 1. |
550 |
|
|
551 |
|
The layer is fully opaque when op = 1. |
552 |
|
""" |
553 |
|
if not (0 <= op <= 1): |
554 |
|
raise ValueError("op out of range") |
555 |
|
|
556 |
|
if op != self.opacity: |
557 |
|
self.opacity = op |
558 |
self.changed(LAYER_CHANGED, self) |
self.changed(LAYER_CHANGED, self) |
559 |
|
|
560 |
def ImageInfo(self): |
def ImageInfo(self): |