| 342 | | class Point(GEOSGeometry): |
|---|
| 343 | | |
|---|
| 344 | | def __init__(self, x, y=None, z=None): |
|---|
| 345 | | """The Point object may be initialized with either a tuple, or individual |
|---|
| 346 | | parameters. For example: |
|---|
| 347 | | >>> p = Point((5, 23)) # 2D point, passed in as a tuple |
|---|
| 348 | | >>> p = Point(5, 23, 8) # 3D point, passed in with individual parameters |
|---|
| 349 | | """ |
|---|
| 350 | | |
|---|
| 351 | | if isinstance(x, (TupleType, ListType)): |
|---|
| 352 | | # Here a tuple or list was passed in under the ``x`` parameter. |
|---|
| 353 | | ndim = len(x) |
|---|
| 354 | | if ndim < 2 or ndim > 3: |
|---|
| 355 | | raise TypeError, 'Invalid sequence parameter: %s' % str(x) |
|---|
| 356 | | coords = x |
|---|
| 357 | | elif isinstance(x, (IntType, FloatType)) and isinstance(y, (IntType, FloatType)): |
|---|
| 358 | | # Here X, Y, and (optionally) Z were passed in individually as parameters. |
|---|
| 359 | | if isinstance(z, (IntType, FloatType)): |
|---|
| 360 | | ndim = 3 |
|---|
| 361 | | coords = [x, y, z] |
|---|
| 362 | | else: |
|---|
| 363 | | ndim = 2 |
|---|
| 364 | | coords = [x, y] |
|---|
| 365 | | else: |
|---|
| 366 | | raise TypeError, 'Invalid parameters given for Point initialization.' |
|---|
| 367 | | |
|---|
| 368 | | # Creating the coordinate sequence |
|---|
| 369 | | cs = create_cs(c_uint(1), c_uint(ndim)) |
|---|
| 370 | | |
|---|
| 371 | | # Setting the X |
|---|
| 372 | | status = lgeos.GEOSCoordSeq_setX(cs, c_uint(0), c_double(coords[0])) |
|---|
| 373 | | if not status: raise GEOSException, 'Could not set X during Point initialization.' |
|---|
| 374 | | |
|---|
| 375 | | # Setting the Y |
|---|
| 376 | | status = lgeos.GEOSCoordSeq_setY(cs, c_uint(0), c_double(coords[1])) |
|---|
| 377 | | if not status: raise GEOSException, 'Could not set Y during Point initialization.' |
|---|
| 378 | | |
|---|
| 379 | | # Setting the Z |
|---|
| 380 | | if ndim == 3: |
|---|
| 381 | | status = lgeos.GEOSCoordSeq_setZ(cs, c_uint(0), c_double(coords[2])) |
|---|
| 382 | | |
|---|
| 383 | | # Initializing from the geometry, and getting a Python object |
|---|
| 384 | | super(Point, self).__init__(lgeos.GEOSGeom_createPoint(cs)) |
|---|
| 385 | | |
|---|
| 386 | | def _getOrdinate(self, dim, idx): |
|---|
| 387 | | "The coordinate sequence getOrdinate() wrapper." |
|---|
| 388 | | self._cache_cs() |
|---|
| 389 | | return self._cs.getOrdinate(dim, idx) |
|---|
| 390 | | |
|---|
| 391 | | def _setOrdinate(self, dim, idx, value): |
|---|
| 392 | | "The coordinate sequence setOrdinate() wrapper." |
|---|
| 393 | | self._cache_cs() |
|---|
| 394 | | self._cs.setOrdinate(dim, idx, value) |
|---|
| 395 | | |
|---|
| 396 | | def get_x(self): |
|---|
| 397 | | "Returns the X component of the Point." |
|---|
| 398 | | return self._getOrdinate(0, 0) |
|---|
| 399 | | |
|---|
| 400 | | def set_x(self, value): |
|---|
| 401 | | "Sets the X component of the Point." |
|---|
| 402 | | self._setOrdinate(0, 0, value) |
|---|
| 403 | | |
|---|
| 404 | | def get_y(self): |
|---|
| 405 | | "Returns the Y component of the Point." |
|---|
| 406 | | return self._getOrdinate(1, 0) |
|---|
| 407 | | |
|---|
| 408 | | def set_y(self, value): |
|---|
| 409 | | "Sets the Y component of the Point." |
|---|
| 410 | | self._setOrdinate(1, 0, value) |
|---|
| 411 | | |
|---|
| 412 | | def get_z(self): |
|---|
| 413 | | "Returns the Z component of the Point." |
|---|
| 414 | | if self.hasz: |
|---|
| 415 | | return self._getOrdinate(2, 0) |
|---|
| 416 | | else: |
|---|
| 417 | | return None |
|---|
| 418 | | |
|---|
| 419 | | def set_z(self, value): |
|---|
| 420 | | "Sets the Z component of the Point." |
|---|
| 421 | | if self.hasz: |
|---|
| 422 | | self._setOrdinate(2, 0, value) |
|---|
| 423 | | else: |
|---|
| 424 | | raise GEOSException, 'Cannot set Z on 2D Point.' |
|---|
| 425 | | |
|---|
| 426 | | # X, Y, Z properties |
|---|
| 427 | | x = property(get_x, set_x) |
|---|
| 428 | | y = property(get_y, set_y) |
|---|
| 429 | | z = property(get_z, set_z) |
|---|
| 430 | | |
|---|
| 431 | | @property |
|---|
| 432 | | def tuple(self): |
|---|
| 433 | | "Returns a tuple of the point." |
|---|
| 434 | | self._cache_cs() |
|---|
| 435 | | return self._cs.tuple |
|---|
| 436 | | |
|---|
| 437 | | class LineString(GEOSGeometry): |
|---|
| 438 | | |
|---|
| 439 | | #### Python 'magic' routines #### |
|---|
| 440 | | def __init__(self, coords, ring=False): |
|---|
| 441 | | """Initializes on the given sequence, may take lists, tuples, or NumPy arrays |
|---|
| 442 | | of X,Y pairs.""" |
|---|
| 443 | | |
|---|
| 444 | | if isinstance(coords, (TupleType, ListType)): |
|---|
| 445 | | ncoords = len(coords) |
|---|
| 446 | | first = True |
|---|
| 447 | | for coord in coords: |
|---|
| 448 | | if not isinstance(coord, (TupleType, ListType)): |
|---|
| 449 | | raise TypeError, 'each coordinate should be a sequence (list or tuple)' |
|---|
| 450 | | if first: |
|---|
| 451 | | ndim = len(coord) |
|---|
| 452 | | self._checkdim(ndim) |
|---|
| 453 | | first = False |
|---|
| 454 | | else: |
|---|
| 455 | | if len(coord) != ndim: raise TypeError, 'Dimension mismatch.' |
|---|
| 456 | | numpy_coords = False |
|---|
| 457 | | elif HAS_NUMPY and isinstance(coords, ndarray): |
|---|
| 458 | | shape = coords.shape |
|---|
| 459 | | if len(shape) != 2: raise TypeError, 'Too many dimensions.' |
|---|
| 460 | | self._checkdim(shape[1]) |
|---|
| 461 | | ncoords = shape[0] |
|---|
| 462 | | ndim = shape[1] |
|---|
| 463 | | numpy_coords = True |
|---|
| 464 | | else: |
|---|
| 465 | | raise TypeError, 'Invalid initialization input for LineStrings.' |
|---|
| 466 | | |
|---|
| 467 | | # Creating the coordinate sequence |
|---|
| 468 | | cs = GEOSCoordSeq(GEOSPointer(create_cs(c_uint(ncoords), c_uint(ndim)))) |
|---|
| 469 | | |
|---|
| 470 | | # Setting each point in the coordinate sequence |
|---|
| 471 | | for i in xrange(ncoords): |
|---|
| 472 | | if numpy_coords: cs[i] = coords[i,:] |
|---|
| 473 | | else: cs[i] = coords[i] |
|---|
| 474 | | |
|---|
| 475 | | # Getting the initialization function |
|---|
| 476 | | if ring: |
|---|
| 477 | | func = lgeos.GEOSGeom_createLinearRing |
|---|
| 478 | | else: |
|---|
| 479 | | func = lgeos.GEOSGeom_createLineString |
|---|
| 480 | | |
|---|
| 481 | | # Calling the base geometry initialization with the returned pointer from the function. |
|---|
| 482 | | super(LineString, self).__init__(func(cs._ptr())) |
|---|
| 483 | | |
|---|
| 484 | | def __getitem__(self, index): |
|---|
| 485 | | "Gets the point at the specified index." |
|---|
| 486 | | self._cache_cs() |
|---|
| 487 | | return self._cs[index] |
|---|
| 488 | | |
|---|
| 489 | | def __setitem__(self, index, value): |
|---|
| 490 | | "Sets the point at the specified index, e.g., line_str[0] = (1, 2)." |
|---|
| 491 | | self._cache_cs() |
|---|
| 492 | | self._cs[index] = value |
|---|
| 493 | | |
|---|
| 494 | | def __iter__(self): |
|---|
| 495 | | "Allows iteration over this LineString." |
|---|
| 496 | | for i in xrange(self.__len__()): |
|---|
| 497 | | yield self.__getitem__(index) |
|---|
| 498 | | |
|---|
| 499 | | def __len__(self): |
|---|
| 500 | | "Returns the number of points in this LineString." |
|---|
| 501 | | self._cache_cs() |
|---|
| 502 | | return len(self._cs) |
|---|
| 503 | | |
|---|
| 504 | | def _checkdim(self, dim): |
|---|
| 505 | | if dim not in (2, 3): raise TypeError, 'Dimension mismatch.' |
|---|
| 506 | | |
|---|
| 507 | | #### Sequence Properties #### |
|---|
| 508 | | @property |
|---|
| 509 | | def tuple(self): |
|---|
| 510 | | "Returns a tuple version of the geometry from the coordinate sequence." |
|---|
| 511 | | self._cache_cs() |
|---|
| 512 | | return self._cs.tuple |
|---|
| 513 | | |
|---|
| 514 | | def _listarr(self, func): |
|---|
| 515 | | """Internal routine that returns a sequence (list) corresponding with |
|---|
| 516 | | the given function. Will return a numpy array if possible.""" |
|---|
| 517 | | lst = [func(i) for i in xrange(self.__len__())] # constructing the list, using the function |
|---|
| 518 | | if HAS_NUMPY: return array(lst) # ARRRR! |
|---|
| 519 | | else: return lst |
|---|
| 520 | | |
|---|
| 521 | | @property |
|---|
| 522 | | def array(self): |
|---|
| 523 | | "Returns a numpy array for the LineString." |
|---|
| 524 | | self._cache_cs() |
|---|
| 525 | | return self._listarr(self._cs.__getitem__) |
|---|
| 526 | | |
|---|
| 527 | | @property |
|---|
| 528 | | def x(self): |
|---|
| 529 | | "Returns a list or numpy array of the X variable." |
|---|
| 530 | | self._cache_cs() |
|---|
| 531 | | return self._listarr(self._cs.getX) |
|---|
| 532 | | |
|---|
| 533 | | @property |
|---|
| 534 | | def y(self): |
|---|
| 535 | | "Returns a list or numpy array of the Y variable." |
|---|
| 536 | | self._cache_cs() |
|---|
| 537 | | return self._listarr(self._cs.getY) |
|---|
| 538 | | |
|---|
| 539 | | @property |
|---|
| 540 | | def z(self): |
|---|
| 541 | | "Returns a list or numpy array of the Z variable." |
|---|
| 542 | | self._cache_cs() |
|---|
| 543 | | if not self.hasz: return None |
|---|
| 544 | | else: return self._listarr(self._cs.getZ) |
|---|
| 545 | | |
|---|
| 546 | | # LinearRings are LineStrings used within Polygons. |
|---|
| 547 | | class LinearRing(LineString): |
|---|
| 548 | | def __init__(self, coords): |
|---|
| 549 | | "Overriding the initialization function to set the ring keyword." |
|---|
| 550 | | super(LinearRing, self).__init__(coords, ring=True) |
|---|
| 551 | | |
|---|
| 552 | | class Polygon(GEOSGeometry): |
|---|
| 553 | | |
|---|
| 554 | | def __del__(self): |
|---|
| 555 | | "Override the GEOSGeometry delete routine to safely take care of any spawned rings." |
|---|
| 556 | | # Nullifying the pointers to internal rings, preventing any attempted future access |
|---|
| 557 | | for k in self._rings: self._rings[k].nullify() |
|---|
| 558 | | super(Polygon, self).__del__() # Calling the parent __del__() method. |
|---|
| 559 | | |
|---|
| 560 | | def __getitem__(self, index): |
|---|
| 561 | | """Returns the ring at the specified index. The first index, 0, will always |
|---|
| 562 | | return the exterior ring. Indices > 0 will return the interior ring.""" |
|---|
| 563 | | if index < 0 or index > self.num_interior_rings: |
|---|
| 564 | | raise GEOSGeometryIndexError, 'invalid GEOS Geometry index: %s' % str(index) |
|---|
| 565 | | else: |
|---|
| 566 | | if index == 0: |
|---|
| 567 | | return self.exterior_ring |
|---|
| 568 | | else: |
|---|
| 569 | | # Getting the interior ring, have to subtract 1 from the index. |
|---|
| 570 | | return self.get_interior_ring(index-1) |
|---|
| 571 | | |
|---|
| 572 | | def __iter__(self): |
|---|
| 573 | | "Iterates over each ring in the polygon." |
|---|
| 574 | | for i in xrange(self.__len__()): |
|---|
| 575 | | yield self.__getitem__(i) |
|---|
| 576 | | |
|---|
| 577 | | def __len__(self): |
|---|
| 578 | | "Returns the number of rings in this Polygon." |
|---|
| 579 | | return self.num_interior_rings + 1 |
|---|
| 580 | | |
|---|
| 581 | | def get_interior_ring(self, ring_i): |
|---|
| 582 | | """Gets the interior ring at the specified index, |
|---|
| 583 | | 0 is for the first interior ring, not the exterior ring.""" |
|---|
| 584 | | |
|---|
| 585 | | # Making sure the ring index is within range |
|---|
| 586 | | if ring_i < 0 or ring_i >= self.num_interior_rings: |
|---|
| 587 | | raise IndexError, 'ring index out of range' |
|---|
| 588 | | |
|---|
| 589 | | # Placing the ring in internal rings dictionary. |
|---|
| 590 | | idx = ring_i+1 # the index for the polygon is +1 because of the exterior ring |
|---|
| 591 | | if not idx in self._rings: |
|---|
| 592 | | self._rings[idx] = GEOSPointer(lgeos.GEOSGetInteriorRingN(self._ptr(), c_int(ring_i))) |
|---|
| 593 | | |
|---|
| 594 | | # Returning the ring at the given index. |
|---|
| 595 | | return GEOSGeometry(self._rings[idx], child=True) |
|---|
| 596 | | |
|---|
| 597 | | #### Polygon Properties #### |
|---|
| 598 | | @property |
|---|
| 599 | | def num_interior_rings(self): |
|---|
| 600 | | "Returns the number of interior rings." |
|---|
| 601 | | |
|---|
| 602 | | # Getting the number of rings |
|---|
| 603 | | n = lgeos.GEOSGetNumInteriorRings(self._ptr()) |
|---|
| 604 | | |
|---|
| 605 | | # -1 indicates an exception occurred |
|---|
| 606 | | if n == -1: raise GEOSException, 'Error getting the number of interior rings.' |
|---|
| 607 | | else: return n |
|---|
| 608 | | |
|---|
| 609 | | @property |
|---|
| 610 | | def exterior_ring(self): |
|---|
| 611 | | "Gets the exterior ring of the Polygon." |
|---|
| 612 | | # Returns exterior ring |
|---|
| 613 | | self._rings[0] = GEOSPointer(lgeos.GEOSGetExteriorRing((self._ptr()))) |
|---|
| 614 | | return GEOSGeometry(self._rings[0], child=True) |
|---|
| 615 | | |
|---|
| 616 | | @property |
|---|
| 617 | | def shell(self): |
|---|
| 618 | | "Gets the shell (exterior ring) of the Polygon." |
|---|
| 619 | | return self.exterior_ring |
|---|
| 620 | | |
|---|
| 621 | | @property |
|---|
| 622 | | def tuple(self): |
|---|
| 623 | | "Gets the tuple for each ring in this Polygon." |
|---|
| 624 | | return tuple(self.__getitem__(i).tuple for i in xrange(self.__len__())) |
|---|
| 625 | | |
|---|
| 626 | | class GeometryCollection(GEOSGeometry): |
|---|
| 627 | | |
|---|
| 628 | | def __del__(self): |
|---|
| 629 | | "Override the GEOSGeometry delete routine to safely take care of any spawned geometries." |
|---|
| 630 | | # Nullifying the pointers to internal geometries, preventing any attempted future access |
|---|
| 631 | | for k in self._geoms: self._geoms[k].nullify() |
|---|
| 632 | | super(GeometryCollection, self).__del__() # Calling the parent __del__() method. |
|---|
| 633 | | |
|---|
| 634 | | def __getitem__(self, index): |
|---|
| 635 | | "For indexing on the multiple geometries." |
|---|
| 636 | | self._checkindex(index) |
|---|
| 637 | | |
|---|
| 638 | | # Setting an entry in the _geoms dictionary for the requested geometry. |
|---|
| 639 | | if not index in self._geoms: |
|---|
| 640 | | self._geoms[index] = GEOSPointer(lgeos.GEOSGetGeometryN(self._ptr(), c_int(index))) |
|---|
| 641 | | |
|---|
| 642 | | # Cloning the GEOS Geometry first, before returning it. |
|---|
| 643 | | return GEOSGeometry(self._geoms[index], child=True) |
|---|
| 644 | | |
|---|
| 645 | | def __iter__(self): |
|---|
| 646 | | "For iteration on the multiple geometries." |
|---|
| 647 | | for i in xrange(self.__len__()): |
|---|
| 648 | | yield self.__getitem__(i) |
|---|
| 649 | | |
|---|
| 650 | | def __len__(self): |
|---|
| 651 | | "Returns the number of geometries in this collection." |
|---|
| 652 | | return self.num_geom |
|---|
| 653 | | |
|---|
| 654 | | def _checkindex(self, index): |
|---|
| 655 | | "Checks the given geometry index." |
|---|
| 656 | | if index < 0 or index >= self.num_geom: |
|---|
| 657 | | raise GEOSGeometryIndexError, 'invalid GEOS Geometry index: %s' % str(index) |
|---|
| 658 | | |
|---|
| 659 | | # MultiPoint, MultiLineString, and MultiPolygon class definitions. |
|---|
| 660 | | class MultiPoint(GeometryCollection): pass |
|---|
| 661 | | class MultiLineString(GeometryCollection): pass |
|---|
| 662 | | class MultiPolygon(GeometryCollection): pass |
|---|
| 663 | | |
|---|