17.12
This commit is contained in:
@@ -0,0 +1 @@
|
||||
default_app_config = 'django.contrib.gis.apps.GISConfig'
|
||||
@@ -0,0 +1,12 @@
|
||||
from django.contrib.admin import (
|
||||
HORIZONTAL, VERTICAL, AdminSite, ModelAdmin, StackedInline, TabularInline,
|
||||
autodiscover, register, site,
|
||||
)
|
||||
from django.contrib.gis.admin.options import GeoModelAdmin, OSMGeoAdmin
|
||||
from django.contrib.gis.admin.widgets import OpenLayersWidget
|
||||
|
||||
__all__ = [
|
||||
'HORIZONTAL', 'VERTICAL', 'AdminSite', 'ModelAdmin', 'StackedInline',
|
||||
'TabularInline', 'autodiscover', 'register', 'site',
|
||||
'GeoModelAdmin', 'OSMGeoAdmin', 'OpenLayersWidget',
|
||||
]
|
||||
@@ -0,0 +1,19 @@
|
||||
class WKTAdapter:
|
||||
"""
|
||||
An adaptor for Geometries sent to the MySQL and Oracle database backends.
|
||||
"""
|
||||
def __init__(self, geom):
|
||||
self.wkt = geom.wkt
|
||||
self.srid = geom.srid
|
||||
|
||||
def __eq__(self, other):
|
||||
return (
|
||||
isinstance(other, WKTAdapter) and
|
||||
self.wkt == other.wkt and self.srid == other.srid
|
||||
)
|
||||
|
||||
def __hash__(self):
|
||||
return hash((self.wkt, self.srid))
|
||||
|
||||
def __str__(self):
|
||||
return self.wkt
|
||||
@@ -0,0 +1,136 @@
|
||||
from django.contrib.gis import gdal
|
||||
|
||||
|
||||
class SpatialRefSysMixin:
|
||||
"""
|
||||
The SpatialRefSysMixin is a class used by the database-dependent
|
||||
SpatialRefSys objects to reduce redundant code.
|
||||
"""
|
||||
@property
|
||||
def srs(self):
|
||||
"""
|
||||
Return a GDAL SpatialReference object.
|
||||
"""
|
||||
# TODO: Is caching really necessary here? Is complexity worth it?
|
||||
if hasattr(self, '_srs'):
|
||||
# Returning a clone of the cached SpatialReference object.
|
||||
return self._srs.clone()
|
||||
else:
|
||||
# Attempting to cache a SpatialReference object.
|
||||
|
||||
# Trying to get from WKT first.
|
||||
try:
|
||||
self._srs = gdal.SpatialReference(self.wkt)
|
||||
return self.srs
|
||||
except Exception as e:
|
||||
msg = e
|
||||
|
||||
try:
|
||||
self._srs = gdal.SpatialReference(self.proj4text)
|
||||
return self.srs
|
||||
except Exception as e:
|
||||
msg = e
|
||||
|
||||
raise Exception('Could not get OSR SpatialReference from WKT: %s\nError:\n%s' % (self.wkt, msg))
|
||||
|
||||
@property
|
||||
def ellipsoid(self):
|
||||
"""
|
||||
Return a tuple of the ellipsoid parameters:
|
||||
(semimajor axis, semiminor axis, and inverse flattening).
|
||||
"""
|
||||
return self.srs.ellipsoid
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"Return the projection name."
|
||||
return self.srs.name
|
||||
|
||||
@property
|
||||
def spheroid(self):
|
||||
"Return the spheroid name for this spatial reference."
|
||||
return self.srs['spheroid']
|
||||
|
||||
@property
|
||||
def datum(self):
|
||||
"Return the datum for this spatial reference."
|
||||
return self.srs['datum']
|
||||
|
||||
@property
|
||||
def projected(self):
|
||||
"Is this Spatial Reference projected?"
|
||||
return self.srs.projected
|
||||
|
||||
@property
|
||||
def local(self):
|
||||
"Is this Spatial Reference local?"
|
||||
return self.srs.local
|
||||
|
||||
@property
|
||||
def geographic(self):
|
||||
"Is this Spatial Reference geographic?"
|
||||
return self.srs.geographic
|
||||
|
||||
@property
|
||||
def linear_name(self):
|
||||
"Return the linear units name."
|
||||
return self.srs.linear_name
|
||||
|
||||
@property
|
||||
def linear_units(self):
|
||||
"Return the linear units."
|
||||
return self.srs.linear_units
|
||||
|
||||
@property
|
||||
def angular_name(self):
|
||||
"Return the name of the angular units."
|
||||
return self.srs.angular_name
|
||||
|
||||
@property
|
||||
def angular_units(self):
|
||||
"Return the angular units."
|
||||
return self.srs.angular_units
|
||||
|
||||
@property
|
||||
def units(self):
|
||||
"Return a tuple of the units and the name."
|
||||
if self.projected or self.local:
|
||||
return (self.linear_units, self.linear_name)
|
||||
elif self.geographic:
|
||||
return (self.angular_units, self.angular_name)
|
||||
else:
|
||||
return (None, None)
|
||||
|
||||
@classmethod
|
||||
def get_units(cls, wkt):
|
||||
"""
|
||||
Return a tuple of (unit_value, unit_name) for the given WKT without
|
||||
using any of the database fields.
|
||||
"""
|
||||
return gdal.SpatialReference(wkt).units
|
||||
|
||||
@classmethod
|
||||
def get_spheroid(cls, wkt, string=True):
|
||||
"""
|
||||
Class method used by GeometryField on initialization to
|
||||
retrieve the `SPHEROID[..]` parameters from the given WKT.
|
||||
"""
|
||||
srs = gdal.SpatialReference(wkt)
|
||||
sphere_params = srs.ellipsoid
|
||||
sphere_name = srs['spheroid']
|
||||
|
||||
if not string:
|
||||
return sphere_name, sphere_params
|
||||
else:
|
||||
# `string` parameter used to place in format acceptable by PostGIS
|
||||
if len(sphere_params) == 3:
|
||||
radius, flattening = sphere_params[0], sphere_params[2]
|
||||
else:
|
||||
radius, flattening = sphere_params
|
||||
return 'SPHEROID["%s",%s,%s]' % (sphere_name, radius, flattening)
|
||||
|
||||
def __str__(self):
|
||||
"""
|
||||
Return the string representation, a 'pretty' OGC WKT.
|
||||
"""
|
||||
return str(self.srs)
|
||||
@@ -0,0 +1,16 @@
|
||||
from django.db.backends.mysql.base import (
|
||||
DatabaseWrapper as MySQLDatabaseWrapper,
|
||||
)
|
||||
|
||||
from .features import DatabaseFeatures
|
||||
from .introspection import MySQLIntrospection
|
||||
from .operations import MySQLOperations
|
||||
from .schema import MySQLGISSchemaEditor
|
||||
|
||||
|
||||
class DatabaseWrapper(MySQLDatabaseWrapper):
|
||||
SchemaEditorClass = MySQLGISSchemaEditor
|
||||
# Classes instantiated in __init__().
|
||||
features_class = DatabaseFeatures
|
||||
introspection_class = MySQLIntrospection
|
||||
ops_class = MySQLOperations
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
from MySQLdb.constants import FIELD_TYPE
|
||||
|
||||
from django.contrib.gis.gdal import OGRGeomType
|
||||
from django.db.backends.mysql.introspection import DatabaseIntrospection
|
||||
|
||||
|
||||
class MySQLIntrospection(DatabaseIntrospection):
|
||||
# Updating the data_types_reverse dictionary with the appropriate
|
||||
# type for Geometry fields.
|
||||
data_types_reverse = DatabaseIntrospection.data_types_reverse.copy()
|
||||
data_types_reverse[FIELD_TYPE.GEOMETRY] = 'GeometryField'
|
||||
|
||||
def get_geometry_type(self, table_name, geo_col):
|
||||
with self.connection.cursor() as cursor:
|
||||
# In order to get the specific geometry type of the field,
|
||||
# we introspect on the table definition using `DESCRIBE`.
|
||||
cursor.execute('DESCRIBE %s' %
|
||||
self.connection.ops.quote_name(table_name))
|
||||
# Increment over description info until we get to the geometry
|
||||
# column.
|
||||
for column, typ, null, key, default, extra in cursor.fetchall():
|
||||
if column == geo_col:
|
||||
# Using OGRGeomType to convert from OGC name to Django field.
|
||||
# MySQL does not support 3D or SRIDs, so the field params
|
||||
# are empty.
|
||||
field_type = OGRGeomType(typ).django
|
||||
field_params = {}
|
||||
break
|
||||
return field_type, field_params
|
||||
|
||||
def supports_spatial_index(self, cursor, table_name):
|
||||
# Supported with MyISAM/Aria, or InnoDB on MySQL 5.7.5+/MariaDB 10.2.2+
|
||||
storage_engine = self.get_storage_engine(cursor, table_name)
|
||||
if storage_engine == 'InnoDB':
|
||||
return self.connection.mysql_version >= (
|
||||
(10, 2, 2) if self.connection.mysql_is_mariadb else (5, 7, 5)
|
||||
)
|
||||
return storage_engine in ('MyISAM', 'Aria')
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
import cx_Oracle
|
||||
|
||||
from django.db.backends.oracle.introspection import DatabaseIntrospection
|
||||
|
||||
|
||||
class OracleIntrospection(DatabaseIntrospection):
|
||||
# Associating any OBJECTVAR instances with GeometryField. Of course,
|
||||
# this won't work right on Oracle objects that aren't MDSYS.SDO_GEOMETRY,
|
||||
# but it is the only object type supported within Django anyways.
|
||||
data_types_reverse = DatabaseIntrospection.data_types_reverse.copy()
|
||||
data_types_reverse[cx_Oracle.OBJECT] = 'GeometryField'
|
||||
|
||||
def get_geometry_type(self, table_name, geo_col):
|
||||
with self.connection.cursor() as cursor:
|
||||
# Querying USER_SDO_GEOM_METADATA to get the SRID and dimension information.
|
||||
try:
|
||||
cursor.execute(
|
||||
'SELECT "DIMINFO", "SRID" FROM "USER_SDO_GEOM_METADATA" '
|
||||
'WHERE "TABLE_NAME"=%s AND "COLUMN_NAME"=%s',
|
||||
(table_name.upper(), geo_col.upper())
|
||||
)
|
||||
row = cursor.fetchone()
|
||||
except Exception as exc:
|
||||
raise Exception(
|
||||
'Could not find entry in USER_SDO_GEOM_METADATA '
|
||||
'corresponding to "%s"."%s"' % (table_name, geo_col)
|
||||
) from exc
|
||||
|
||||
# TODO: Research way to find a more specific geometry field type for
|
||||
# the column's contents.
|
||||
field_type = 'GeometryField'
|
||||
|
||||
# Getting the field parameters.
|
||||
field_params = {}
|
||||
dim, srid = row
|
||||
if srid != 4326:
|
||||
field_params['srid'] = srid
|
||||
# Size of object array (SDO_DIM_ARRAY) is number of dimensions.
|
||||
dim = dim.size()
|
||||
if dim != 2:
|
||||
field_params['dim'] = dim
|
||||
return field_type, field_params
|
||||
@@ -0,0 +1,26 @@
|
||||
from django.db.backends.base.base import NO_DB_ALIAS
|
||||
from django.db.backends.postgresql.base import (
|
||||
DatabaseWrapper as Psycopg2DatabaseWrapper,
|
||||
)
|
||||
|
||||
from .features import DatabaseFeatures
|
||||
from .introspection import PostGISIntrospection
|
||||
from .operations import PostGISOperations
|
||||
from .schema import PostGISSchemaEditor
|
||||
|
||||
|
||||
class DatabaseWrapper(Psycopg2DatabaseWrapper):
|
||||
SchemaEditorClass = PostGISSchemaEditor
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
if kwargs.get('alias', '') != NO_DB_ALIAS:
|
||||
self.features = DatabaseFeatures(self)
|
||||
self.ops = PostGISOperations(self)
|
||||
self.introspection = PostGISIntrospection(self)
|
||||
|
||||
def prepare_database(self):
|
||||
super().prepare_database()
|
||||
# Check that postgis extension is installed.
|
||||
with self.cursor() as cursor:
|
||||
cursor.execute("CREATE EXTENSION IF NOT EXISTS postgis")
|
||||
@@ -0,0 +1,141 @@
|
||||
import struct
|
||||
|
||||
from django.forms import ValidationError
|
||||
|
||||
from .const import (
|
||||
GDAL_TO_POSTGIS, GDAL_TO_STRUCT, POSTGIS_HEADER_STRUCTURE, POSTGIS_TO_GDAL,
|
||||
STRUCT_SIZE,
|
||||
)
|
||||
|
||||
|
||||
def pack(structure, data):
|
||||
"""
|
||||
Pack data into hex string with little endian format.
|
||||
"""
|
||||
return struct.pack('<' + structure, *data)
|
||||
|
||||
|
||||
def unpack(structure, data):
|
||||
"""
|
||||
Unpack little endian hexlified binary string into a list.
|
||||
"""
|
||||
return struct.unpack('<' + structure, bytes.fromhex(data))
|
||||
|
||||
|
||||
def chunk(data, index):
|
||||
"""
|
||||
Split a string into two parts at the input index.
|
||||
"""
|
||||
return data[:index], data[index:]
|
||||
|
||||
|
||||
def from_pgraster(data):
|
||||
"""
|
||||
Convert a PostGIS HEX String into a dictionary.
|
||||
"""
|
||||
if data is None:
|
||||
return
|
||||
|
||||
# Split raster header from data
|
||||
header, data = chunk(data, 122)
|
||||
header = unpack(POSTGIS_HEADER_STRUCTURE, header)
|
||||
|
||||
# Parse band data
|
||||
bands = []
|
||||
pixeltypes = []
|
||||
while data:
|
||||
# Get pixel type for this band
|
||||
pixeltype, data = chunk(data, 2)
|
||||
pixeltype = unpack('B', pixeltype)[0]
|
||||
|
||||
# Subtract nodata byte from band nodata value if it exists
|
||||
has_nodata = pixeltype >= 64
|
||||
if has_nodata:
|
||||
pixeltype -= 64
|
||||
|
||||
# Convert datatype from PostGIS to GDAL & get pack type and size
|
||||
pixeltype = POSTGIS_TO_GDAL[pixeltype]
|
||||
pack_type = GDAL_TO_STRUCT[pixeltype]
|
||||
pack_size = 2 * STRUCT_SIZE[pack_type]
|
||||
|
||||
# Parse band nodata value. The nodata value is part of the
|
||||
# PGRaster string even if the nodata flag is True, so it always
|
||||
# has to be chunked off the data string.
|
||||
nodata, data = chunk(data, pack_size)
|
||||
nodata = unpack(pack_type, nodata)[0]
|
||||
|
||||
# Chunk and unpack band data (pack size times nr of pixels)
|
||||
band, data = chunk(data, pack_size * header[10] * header[11])
|
||||
band_result = {'data': bytes.fromhex(band)}
|
||||
|
||||
# If the nodata flag is True, set the nodata value.
|
||||
if has_nodata:
|
||||
band_result['nodata_value'] = nodata
|
||||
|
||||
# Append band data to band list
|
||||
bands.append(band_result)
|
||||
|
||||
# Store pixeltype of this band in pixeltypes array
|
||||
pixeltypes.append(pixeltype)
|
||||
|
||||
# Check that all bands have the same pixeltype.
|
||||
# This is required by GDAL. PostGIS rasters could have different pixeltypes
|
||||
# for bands of the same raster.
|
||||
if len(set(pixeltypes)) != 1:
|
||||
raise ValidationError("Band pixeltypes are not all equal.")
|
||||
|
||||
return {
|
||||
'srid': int(header[9]),
|
||||
'width': header[10], 'height': header[11],
|
||||
'datatype': pixeltypes[0],
|
||||
'origin': (header[5], header[6]),
|
||||
'scale': (header[3], header[4]),
|
||||
'skew': (header[7], header[8]),
|
||||
'bands': bands,
|
||||
}
|
||||
|
||||
|
||||
def to_pgraster(rast):
|
||||
"""
|
||||
Convert a GDALRaster into PostGIS Raster format.
|
||||
"""
|
||||
# Prepare the raster header data as a tuple. The first two numbers are
|
||||
# the endianness and the PostGIS Raster Version, both are fixed by
|
||||
# PostGIS at the moment.
|
||||
rasterheader = (
|
||||
1, 0, len(rast.bands), rast.scale.x, rast.scale.y,
|
||||
rast.origin.x, rast.origin.y, rast.skew.x, rast.skew.y,
|
||||
rast.srs.srid, rast.width, rast.height,
|
||||
)
|
||||
|
||||
# Pack raster header.
|
||||
result = pack(POSTGIS_HEADER_STRUCTURE, rasterheader)
|
||||
|
||||
for band in rast.bands:
|
||||
# The PostGIS raster band header has exactly two elements, a 8BUI byte
|
||||
# and the nodata value.
|
||||
#
|
||||
# The 8BUI stores both the PostGIS pixel data type and a nodata flag.
|
||||
# It is composed as the datatype integer plus 64 as a flag for existing
|
||||
# nodata values:
|
||||
# 8BUI_VALUE = PG_PIXEL_TYPE (0-11) + FLAG (0 or 64)
|
||||
#
|
||||
# For example, if the byte value is 71, then the datatype is
|
||||
# 71-64 = 7 (32BSI) and the nodata value is True.
|
||||
structure = 'B' + GDAL_TO_STRUCT[band.datatype()]
|
||||
|
||||
# Get band pixel type in PostGIS notation
|
||||
pixeltype = GDAL_TO_POSTGIS[band.datatype()]
|
||||
|
||||
# Set the nodata flag
|
||||
if band.nodata_value is not None:
|
||||
pixeltype += 64
|
||||
|
||||
# Pack band header
|
||||
bandheader = pack(structure, (pixeltype, band.nodata_value or 0))
|
||||
|
||||
# Add packed header and band data to result
|
||||
result += bandheader + band.data(as_memoryview=True)
|
||||
|
||||
# Convert raster to hex string before passing it to the DB.
|
||||
return result.hex()
|
||||
@@ -0,0 +1,74 @@
|
||||
from ctypes.util import find_library
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.db.backends.sqlite3.base import (
|
||||
DatabaseWrapper as SQLiteDatabaseWrapper,
|
||||
)
|
||||
|
||||
from .client import SpatiaLiteClient
|
||||
from .features import DatabaseFeatures
|
||||
from .introspection import SpatiaLiteIntrospection
|
||||
from .operations import SpatiaLiteOperations
|
||||
from .schema import SpatialiteSchemaEditor
|
||||
|
||||
|
||||
class DatabaseWrapper(SQLiteDatabaseWrapper):
|
||||
SchemaEditorClass = SpatialiteSchemaEditor
|
||||
# Classes instantiated in __init__().
|
||||
client_class = SpatiaLiteClient
|
||||
features_class = DatabaseFeatures
|
||||
introspection_class = SpatiaLiteIntrospection
|
||||
ops_class = SpatiaLiteOperations
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
# Trying to find the location of the SpatiaLite library.
|
||||
# Here we are figuring out the path to the SpatiaLite library
|
||||
# (`libspatialite`). If it's not in the system library path (e.g., it
|
||||
# cannot be found by `ctypes.util.find_library`), then it may be set
|
||||
# manually in the settings via the `SPATIALITE_LIBRARY_PATH` setting.
|
||||
self.lib_spatialite_paths = [name for name in [
|
||||
getattr(settings, 'SPATIALITE_LIBRARY_PATH', None),
|
||||
'mod_spatialite.so',
|
||||
'mod_spatialite',
|
||||
find_library('spatialite'),
|
||||
] if name is not None]
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def get_new_connection(self, conn_params):
|
||||
conn = super().get_new_connection(conn_params)
|
||||
# Enabling extension loading on the SQLite connection.
|
||||
try:
|
||||
conn.enable_load_extension(True)
|
||||
except AttributeError:
|
||||
raise ImproperlyConfigured(
|
||||
'SpatiaLite requires SQLite to be configured to allow '
|
||||
'extension loading.'
|
||||
)
|
||||
# Load the SpatiaLite library extension on the connection.
|
||||
for path in self.lib_spatialite_paths:
|
||||
try:
|
||||
conn.load_extension(path)
|
||||
except Exception:
|
||||
if getattr(settings, 'SPATIALITE_LIBRARY_PATH', None):
|
||||
raise ImproperlyConfigured(
|
||||
'Unable to load the SpatiaLite library extension '
|
||||
'as specified in your SPATIALITE_LIBRARY_PATH setting.'
|
||||
)
|
||||
continue
|
||||
else:
|
||||
break
|
||||
else:
|
||||
raise ImproperlyConfigured(
|
||||
'Unable to load the SpatiaLite library extension. '
|
||||
'Library names tried: %s' % ', '.join(self.lib_spatialite_paths)
|
||||
)
|
||||
return conn
|
||||
|
||||
def prepare_database(self):
|
||||
super().prepare_database()
|
||||
# Check if spatial metadata have been initialized in the database
|
||||
with self.cursor() as cursor:
|
||||
cursor.execute("PRAGMA table_info(geometry_columns);")
|
||||
if cursor.fetchall() == []:
|
||||
cursor.execute("SELECT InitSpatialMetaData(1)")
|
||||
@@ -0,0 +1,5 @@
|
||||
from django.db.backends.sqlite3.client import DatabaseClient
|
||||
|
||||
|
||||
class SpatiaLiteClient(DatabaseClient):
|
||||
executable_name = 'spatialite'
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
from django.contrib.gis.db.backends.base.features import BaseSpatialFeatures
|
||||
from django.db.backends.sqlite3.features import (
|
||||
DatabaseFeatures as SQLiteDatabaseFeatures,
|
||||
)
|
||||
from django.utils.functional import cached_property
|
||||
|
||||
|
||||
class DatabaseFeatures(BaseSpatialFeatures, SQLiteDatabaseFeatures):
|
||||
supports_3d_storage = True
|
||||
|
||||
@cached_property
|
||||
def supports_area_geodetic(self):
|
||||
return bool(self.connection.ops.lwgeom_version())
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
from django.contrib.gis.gdal import OGRGeomType
|
||||
from django.db.backends.sqlite3.introspection import (
|
||||
DatabaseIntrospection, FlexibleFieldLookupDict,
|
||||
)
|
||||
|
||||
|
||||
class GeoFlexibleFieldLookupDict(FlexibleFieldLookupDict):
|
||||
"""
|
||||
Sublcass that includes updates the `base_data_types_reverse` dict
|
||||
for geometry field types.
|
||||
"""
|
||||
base_data_types_reverse = {
|
||||
**FlexibleFieldLookupDict.base_data_types_reverse,
|
||||
'point': 'GeometryField',
|
||||
'linestring': 'GeometryField',
|
||||
'polygon': 'GeometryField',
|
||||
'multipoint': 'GeometryField',
|
||||
'multilinestring': 'GeometryField',
|
||||
'multipolygon': 'GeometryField',
|
||||
'geometrycollection': 'GeometryField',
|
||||
}
|
||||
|
||||
|
||||
class SpatiaLiteIntrospection(DatabaseIntrospection):
|
||||
data_types_reverse = GeoFlexibleFieldLookupDict()
|
||||
|
||||
def get_geometry_type(self, table_name, geo_col):
|
||||
with self.connection.cursor() as cursor:
|
||||
# Querying the `geometry_columns` table to get additional metadata.
|
||||
cursor.execute('SELECT coord_dimension, srid, geometry_type '
|
||||
'FROM geometry_columns '
|
||||
'WHERE f_table_name=%s AND f_geometry_column=%s',
|
||||
(table_name, geo_col))
|
||||
row = cursor.fetchone()
|
||||
if not row:
|
||||
raise Exception('Could not find a geometry column for "%s"."%s"' %
|
||||
(table_name, geo_col))
|
||||
|
||||
# OGRGeomType does not require GDAL and makes it easy to convert
|
||||
# from OGC geom type name to Django field.
|
||||
ogr_type = row[2]
|
||||
if isinstance(ogr_type, int) and ogr_type > 1000:
|
||||
# SpatiaLite uses SFSQL 1.2 offsets 1000 (Z), 2000 (M), and
|
||||
# 3000 (ZM) to indicate the presence of higher dimensional
|
||||
# coordinates (M not yet supported by Django).
|
||||
ogr_type = ogr_type % 1000 + OGRGeomType.wkb25bit
|
||||
field_type = OGRGeomType(ogr_type).django
|
||||
|
||||
# Getting any GeometryField keyword arguments that are not the default.
|
||||
dim = row[0]
|
||||
srid = row[1]
|
||||
field_params = {}
|
||||
if srid != 4326:
|
||||
field_params['srid'] = srid
|
||||
if (isinstance(dim, str) and 'Z' in dim) or dim == 3:
|
||||
field_params['dim'] = 3
|
||||
return field_type, field_params
|
||||
|
||||
def get_constraints(self, cursor, table_name):
|
||||
constraints = super().get_constraints(cursor, table_name)
|
||||
cursor.execute('SELECT f_geometry_column '
|
||||
'FROM geometry_columns '
|
||||
'WHERE f_table_name=%s AND spatial_index_enabled=1', (table_name,))
|
||||
for row in cursor.fetchall():
|
||||
constraints['%s__spatial__index' % row[0]] = {
|
||||
"columns": [row[0]],
|
||||
"primary_key": False,
|
||||
"unique": False,
|
||||
"foreign_key": None,
|
||||
"check": False,
|
||||
"index": True,
|
||||
}
|
||||
return constraints
|
||||
@@ -0,0 +1,381 @@
|
||||
from collections import defaultdict, namedtuple
|
||||
|
||||
from django.contrib.gis import forms, gdal
|
||||
from django.contrib.gis.db.models.proxy import SpatialProxy
|
||||
from django.contrib.gis.gdal.error import GDALException
|
||||
from django.contrib.gis.geos import (
|
||||
GeometryCollection, GEOSException, GEOSGeometry, LineString,
|
||||
MultiLineString, MultiPoint, MultiPolygon, Point, Polygon,
|
||||
)
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.db.models.fields import Field
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
# Local cache of the spatial_ref_sys table, which holds SRID data for each
|
||||
# spatial database alias. This cache exists so that the database isn't queried
|
||||
# for SRID info each time a distance query is constructed.
|
||||
_srid_cache = defaultdict(dict)
|
||||
|
||||
|
||||
SRIDCacheEntry = namedtuple('SRIDCacheEntry', ['units', 'units_name', 'spheroid', 'geodetic'])
|
||||
|
||||
|
||||
def get_srid_info(srid, connection):
|
||||
"""
|
||||
Return the units, unit name, and spheroid WKT associated with the
|
||||
given SRID from the `spatial_ref_sys` (or equivalent) spatial database
|
||||
table for the given database connection. These results are cached.
|
||||
"""
|
||||
from django.contrib.gis.gdal import SpatialReference
|
||||
global _srid_cache
|
||||
|
||||
try:
|
||||
# The SpatialRefSys model for the spatial backend.
|
||||
SpatialRefSys = connection.ops.spatial_ref_sys()
|
||||
except NotImplementedError:
|
||||
SpatialRefSys = None
|
||||
|
||||
alias, get_srs = (
|
||||
(connection.alias, lambda srid: SpatialRefSys.objects.using(connection.alias).get(srid=srid).srs)
|
||||
if SpatialRefSys else
|
||||
(None, SpatialReference)
|
||||
)
|
||||
if srid not in _srid_cache[alias]:
|
||||
srs = get_srs(srid)
|
||||
units, units_name = srs.units
|
||||
_srid_cache[alias][srid] = SRIDCacheEntry(
|
||||
units=units,
|
||||
units_name=units_name,
|
||||
spheroid='SPHEROID["%s",%s,%s]' % (srs['spheroid'], srs.semi_major, srs.inverse_flattening),
|
||||
geodetic=srs.geographic,
|
||||
)
|
||||
|
||||
return _srid_cache[alias][srid]
|
||||
|
||||
|
||||
class BaseSpatialField(Field):
|
||||
"""
|
||||
The Base GIS Field.
|
||||
|
||||
It's used as a base class for GeometryField and RasterField. Defines
|
||||
properties that are common to all GIS fields such as the characteristics
|
||||
of the spatial reference system of the field.
|
||||
"""
|
||||
description = _("The base GIS field.")
|
||||
empty_strings_allowed = False
|
||||
|
||||
def __init__(self, verbose_name=None, srid=4326, spatial_index=True, **kwargs):
|
||||
"""
|
||||
The initialization function for base spatial fields. Takes the following
|
||||
as keyword arguments:
|
||||
|
||||
srid:
|
||||
The spatial reference system identifier, an OGC standard.
|
||||
Defaults to 4326 (WGS84).
|
||||
|
||||
spatial_index:
|
||||
Indicates whether to create a spatial index. Defaults to True.
|
||||
Set this instead of 'db_index' for geographic fields since index
|
||||
creation is different for geometry columns.
|
||||
"""
|
||||
|
||||
# Setting the index flag with the value of the `spatial_index` keyword.
|
||||
self.spatial_index = spatial_index
|
||||
|
||||
# Setting the SRID and getting the units. Unit information must be
|
||||
# easily available in the field instance for distance queries.
|
||||
self.srid = srid
|
||||
|
||||
# Setting the verbose_name keyword argument with the positional
|
||||
# first parameter, so this works like normal fields.
|
||||
kwargs['verbose_name'] = verbose_name
|
||||
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def deconstruct(self):
|
||||
name, path, args, kwargs = super().deconstruct()
|
||||
# Always include SRID for less fragility; include spatial index if it's
|
||||
# not the default value.
|
||||
kwargs['srid'] = self.srid
|
||||
if self.spatial_index is not True:
|
||||
kwargs['spatial_index'] = self.spatial_index
|
||||
return name, path, args, kwargs
|
||||
|
||||
def db_type(self, connection):
|
||||
return connection.ops.geo_db_type(self)
|
||||
|
||||
def spheroid(self, connection):
|
||||
return get_srid_info(self.srid, connection).spheroid
|
||||
|
||||
def units(self, connection):
|
||||
return get_srid_info(self.srid, connection).units
|
||||
|
||||
def units_name(self, connection):
|
||||
return get_srid_info(self.srid, connection).units_name
|
||||
|
||||
def geodetic(self, connection):
|
||||
"""
|
||||
Return true if this field's SRID corresponds with a coordinate
|
||||
system that uses non-projected units (e.g., latitude/longitude).
|
||||
"""
|
||||
return get_srid_info(self.srid, connection).geodetic
|
||||
|
||||
def get_placeholder(self, value, compiler, connection):
|
||||
"""
|
||||
Return the placeholder for the spatial column for the
|
||||
given value.
|
||||
"""
|
||||
return connection.ops.get_geom_placeholder(self, value, compiler)
|
||||
|
||||
def get_srid(self, obj):
|
||||
"""
|
||||
Return the default SRID for the given geometry or raster, taking into
|
||||
account the SRID set for the field. For example, if the input geometry
|
||||
or raster doesn't have an SRID, then the SRID of the field will be
|
||||
returned.
|
||||
"""
|
||||
srid = obj.srid # SRID of given geometry.
|
||||
if srid is None or self.srid == -1 or (srid == -1 and self.srid != -1):
|
||||
return self.srid
|
||||
else:
|
||||
return srid
|
||||
|
||||
def get_db_prep_value(self, value, connection, *args, **kwargs):
|
||||
if value is None:
|
||||
return None
|
||||
return connection.ops.Adapter(
|
||||
super().get_db_prep_value(value, connection, *args, **kwargs),
|
||||
**({'geography': True} if self.geography and connection.ops.geography else {})
|
||||
)
|
||||
|
||||
def get_raster_prep_value(self, value, is_candidate):
|
||||
"""
|
||||
Return a GDALRaster if conversion is successful, otherwise return None.
|
||||
"""
|
||||
if isinstance(value, gdal.GDALRaster):
|
||||
return value
|
||||
elif is_candidate:
|
||||
try:
|
||||
return gdal.GDALRaster(value)
|
||||
except GDALException:
|
||||
pass
|
||||
elif isinstance(value, dict):
|
||||
try:
|
||||
return gdal.GDALRaster(value)
|
||||
except GDALException:
|
||||
raise ValueError("Couldn't create spatial object from lookup value '%s'." % value)
|
||||
|
||||
def get_prep_value(self, value):
|
||||
obj = super().get_prep_value(value)
|
||||
if obj is None:
|
||||
return None
|
||||
# When the input is not a geometry or raster, attempt to construct one
|
||||
# from the given string input.
|
||||
if isinstance(obj, GEOSGeometry):
|
||||
pass
|
||||
else:
|
||||
# Check if input is a candidate for conversion to raster or geometry.
|
||||
is_candidate = isinstance(obj, (bytes, str)) or hasattr(obj, '__geo_interface__')
|
||||
# Try to convert the input to raster.
|
||||
raster = self.get_raster_prep_value(obj, is_candidate)
|
||||
|
||||
if raster:
|
||||
obj = raster
|
||||
elif is_candidate:
|
||||
try:
|
||||
obj = GEOSGeometry(obj)
|
||||
except (GEOSException, GDALException):
|
||||
raise ValueError("Couldn't create spatial object from lookup value '%s'." % obj)
|
||||
else:
|
||||
raise ValueError('Cannot use object with type %s for a spatial lookup parameter.' % type(obj).__name__)
|
||||
|
||||
# Assigning the SRID value.
|
||||
obj.srid = self.get_srid(obj)
|
||||
return obj
|
||||
|
||||
|
||||
class GeometryField(BaseSpatialField):
|
||||
"""
|
||||
The base Geometry field -- maps to the OpenGIS Specification Geometry type.
|
||||
"""
|
||||
description = _("The base Geometry field -- maps to the OpenGIS Specification Geometry type.")
|
||||
form_class = forms.GeometryField
|
||||
# The OpenGIS Geometry name.
|
||||
geom_type = 'GEOMETRY'
|
||||
geom_class = None
|
||||
|
||||
def __init__(self, verbose_name=None, dim=2, geography=False, *, extent=(-180.0, -90.0, 180.0, 90.0),
|
||||
tolerance=0.05, **kwargs):
|
||||
"""
|
||||
The initialization function for geometry fields. In addition to the
|
||||
parameters from BaseSpatialField, it takes the following as keyword
|
||||
arguments:
|
||||
|
||||
dim:
|
||||
The number of dimensions for this geometry. Defaults to 2.
|
||||
|
||||
extent:
|
||||
Customize the extent, in a 4-tuple of WGS 84 coordinates, for the
|
||||
geometry field entry in the `USER_SDO_GEOM_METADATA` table. Defaults
|
||||
to (-180.0, -90.0, 180.0, 90.0).
|
||||
|
||||
tolerance:
|
||||
Define the tolerance, in meters, to use for the geometry field
|
||||
entry in the `USER_SDO_GEOM_METADATA` table. Defaults to 0.05.
|
||||
"""
|
||||
# Setting the dimension of the geometry field.
|
||||
self.dim = dim
|
||||
|
||||
# Is this a geography rather than a geometry column?
|
||||
self.geography = geography
|
||||
|
||||
# Oracle-specific private attributes for creating the entry in
|
||||
# `USER_SDO_GEOM_METADATA`
|
||||
self._extent = extent
|
||||
self._tolerance = tolerance
|
||||
|
||||
super().__init__(verbose_name=verbose_name, **kwargs)
|
||||
|
||||
def deconstruct(self):
|
||||
name, path, args, kwargs = super().deconstruct()
|
||||
# Include kwargs if they're not the default values.
|
||||
if self.dim != 2:
|
||||
kwargs['dim'] = self.dim
|
||||
if self.geography is not False:
|
||||
kwargs['geography'] = self.geography
|
||||
if self._extent != (-180.0, -90.0, 180.0, 90.0):
|
||||
kwargs['extent'] = self._extent
|
||||
if self._tolerance != 0.05:
|
||||
kwargs['tolerance'] = self._tolerance
|
||||
return name, path, args, kwargs
|
||||
|
||||
def contribute_to_class(self, cls, name, **kwargs):
|
||||
super().contribute_to_class(cls, name, **kwargs)
|
||||
|
||||
# Setup for lazy-instantiated Geometry object.
|
||||
setattr(cls, self.attname, SpatialProxy(self.geom_class or GEOSGeometry, self, load_func=GEOSGeometry))
|
||||
|
||||
def formfield(self, **kwargs):
|
||||
defaults = {
|
||||
'form_class': self.form_class,
|
||||
'geom_type': self.geom_type,
|
||||
'srid': self.srid,
|
||||
**kwargs,
|
||||
}
|
||||
if self.dim > 2 and not getattr(defaults['form_class'].widget, 'supports_3d', False):
|
||||
defaults.setdefault('widget', forms.Textarea)
|
||||
return super().formfield(**defaults)
|
||||
|
||||
def select_format(self, compiler, sql, params):
|
||||
"""
|
||||
Return the selection format string, depending on the requirements
|
||||
of the spatial backend. For example, Oracle and MySQL require custom
|
||||
selection formats in order to retrieve geometries in OGC WKB.
|
||||
"""
|
||||
return compiler.connection.ops.select % sql, params
|
||||
|
||||
|
||||
# The OpenGIS Geometry Type Fields
|
||||
class PointField(GeometryField):
|
||||
geom_type = 'POINT'
|
||||
geom_class = Point
|
||||
form_class = forms.PointField
|
||||
description = _("Point")
|
||||
|
||||
|
||||
class LineStringField(GeometryField):
|
||||
geom_type = 'LINESTRING'
|
||||
geom_class = LineString
|
||||
form_class = forms.LineStringField
|
||||
description = _("Line string")
|
||||
|
||||
|
||||
class PolygonField(GeometryField):
|
||||
geom_type = 'POLYGON'
|
||||
geom_class = Polygon
|
||||
form_class = forms.PolygonField
|
||||
description = _("Polygon")
|
||||
|
||||
|
||||
class MultiPointField(GeometryField):
|
||||
geom_type = 'MULTIPOINT'
|
||||
geom_class = MultiPoint
|
||||
form_class = forms.MultiPointField
|
||||
description = _("Multi-point")
|
||||
|
||||
|
||||
class MultiLineStringField(GeometryField):
|
||||
geom_type = 'MULTILINESTRING'
|
||||
geom_class = MultiLineString
|
||||
form_class = forms.MultiLineStringField
|
||||
description = _("Multi-line string")
|
||||
|
||||
|
||||
class MultiPolygonField(GeometryField):
|
||||
geom_type = 'MULTIPOLYGON'
|
||||
geom_class = MultiPolygon
|
||||
form_class = forms.MultiPolygonField
|
||||
description = _("Multi polygon")
|
||||
|
||||
|
||||
class GeometryCollectionField(GeometryField):
|
||||
geom_type = 'GEOMETRYCOLLECTION'
|
||||
geom_class = GeometryCollection
|
||||
form_class = forms.GeometryCollectionField
|
||||
description = _("Geometry collection")
|
||||
|
||||
|
||||
class ExtentField(Field):
|
||||
"Used as a return value from an extent aggregate"
|
||||
|
||||
description = _("Extent Aggregate Field")
|
||||
|
||||
def get_internal_type(self):
|
||||
return "ExtentField"
|
||||
|
||||
def select_format(self, compiler, sql, params):
|
||||
select = compiler.connection.ops.select_extent
|
||||
return select % sql if select else sql, params
|
||||
|
||||
|
||||
class RasterField(BaseSpatialField):
|
||||
"""
|
||||
Raster field for GeoDjango -- evaluates into GDALRaster objects.
|
||||
"""
|
||||
|
||||
description = _("Raster Field")
|
||||
geom_type = 'RASTER'
|
||||
geography = False
|
||||
|
||||
def _check_connection(self, connection):
|
||||
# Make sure raster fields are used only on backends with raster support.
|
||||
if not connection.features.gis_enabled or not connection.features.supports_raster:
|
||||
raise ImproperlyConfigured('Raster fields require backends with raster support.')
|
||||
|
||||
def db_type(self, connection):
|
||||
self._check_connection(connection)
|
||||
return super().db_type(connection)
|
||||
|
||||
def from_db_value(self, value, expression, connection):
|
||||
return connection.ops.parse_raster(value)
|
||||
|
||||
def contribute_to_class(self, cls, name, **kwargs):
|
||||
super().contribute_to_class(cls, name, **kwargs)
|
||||
# Setup for lazy-instantiated Raster object. For large querysets, the
|
||||
# instantiation of all GDALRasters can potentially be expensive. This
|
||||
# delays the instantiation of the objects to the moment of evaluation
|
||||
# of the raster attribute.
|
||||
setattr(cls, self.attname, SpatialProxy(gdal.GDALRaster, self))
|
||||
|
||||
def get_transform(self, name):
|
||||
from django.contrib.gis.db.models.lookups import RasterBandTransform
|
||||
try:
|
||||
band_index = int(name)
|
||||
return type(
|
||||
'SpecificRasterBandTransform',
|
||||
(RasterBandTransform,),
|
||||
{'band_index': band_index}
|
||||
)
|
||||
except ValueError:
|
||||
pass
|
||||
return super().get_transform(name)
|
||||
@@ -0,0 +1,80 @@
|
||||
"""
|
||||
The SpatialProxy object allows for lazy-geometries and lazy-rasters. The proxy
|
||||
uses Python descriptors for instantiating and setting Geometry or Raster
|
||||
objects corresponding to geographic model fields.
|
||||
|
||||
Thanks to Robert Coup for providing this functionality (see #4322).
|
||||
"""
|
||||
from django.db.models.query_utils import DeferredAttribute
|
||||
|
||||
|
||||
class SpatialProxy(DeferredAttribute):
|
||||
def __init__(self, klass, field, load_func=None):
|
||||
"""
|
||||
Initialize on the given Geometry or Raster class (not an instance)
|
||||
and the corresponding field.
|
||||
"""
|
||||
self._field = field
|
||||
self._klass = klass
|
||||
self._load_func = load_func or klass
|
||||
super().__init__(field.attname)
|
||||
|
||||
def __get__(self, instance, cls=None):
|
||||
"""
|
||||
Retrieve the geometry or raster, initializing it using the
|
||||
corresponding class specified during initialization and the value of
|
||||
the field. Currently, GEOS or OGR geometries as well as GDALRasters are
|
||||
supported.
|
||||
"""
|
||||
if instance is None:
|
||||
# Accessed on a class, not an instance
|
||||
return self
|
||||
|
||||
# Getting the value of the field.
|
||||
try:
|
||||
geo_value = instance.__dict__[self._field.attname]
|
||||
except KeyError:
|
||||
geo_value = super().__get__(instance, cls)
|
||||
|
||||
if isinstance(geo_value, self._klass):
|
||||
geo_obj = geo_value
|
||||
elif (geo_value is None) or (geo_value == ''):
|
||||
geo_obj = None
|
||||
else:
|
||||
# Otherwise, a geometry or raster object is built using the field's
|
||||
# contents, and the model's corresponding attribute is set.
|
||||
geo_obj = self._load_func(geo_value)
|
||||
setattr(instance, self._field.attname, geo_obj)
|
||||
return geo_obj
|
||||
|
||||
def __set__(self, instance, value):
|
||||
"""
|
||||
Retrieve the proxied geometry or raster with the corresponding class
|
||||
specified during initialization.
|
||||
|
||||
To set geometries, use values of None, HEXEWKB, or WKT.
|
||||
To set rasters, use JSON or dict values.
|
||||
"""
|
||||
# The geographic type of the field.
|
||||
gtype = self._field.geom_type
|
||||
|
||||
if gtype == 'RASTER' and (value is None or isinstance(value, (str, dict, self._klass))):
|
||||
# For raster fields, assure input is None or a string, dict, or
|
||||
# raster instance.
|
||||
pass
|
||||
elif isinstance(value, self._klass):
|
||||
# The geometry type must match that of the field -- unless the
|
||||
# general GeometryField is used.
|
||||
if value.srid is None:
|
||||
# Assigning the field SRID if the geometry has no SRID.
|
||||
value.srid = self._field.srid
|
||||
elif value is None or isinstance(value, (str, memoryview)):
|
||||
# Set geometries with None, WKT, HEX, or WKB
|
||||
pass
|
||||
else:
|
||||
raise TypeError('Cannot set %s SpatialProxy (%s) with value of type: %s' % (
|
||||
instance.__class__.__name__, gtype, type(value)))
|
||||
|
||||
# Setting the objects dictionary with the value, and returning.
|
||||
instance.__dict__[self._field.attname] = value
|
||||
return value
|
||||
@@ -0,0 +1,7 @@
|
||||
from django.contrib.gis.db.models.sql.conversion import (
|
||||
AreaField, DistanceField,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
'AreaField', 'DistanceField',
|
||||
]
|
||||
@@ -0,0 +1,69 @@
|
||||
"""
|
||||
This module holds simple classes to convert geospatial values from the
|
||||
database.
|
||||
"""
|
||||
from decimal import Decimal
|
||||
|
||||
from django.contrib.gis.measure import Area, Distance
|
||||
from django.db import models
|
||||
|
||||
|
||||
class AreaField(models.FloatField):
|
||||
"Wrapper for Area values."
|
||||
def __init__(self, geo_field):
|
||||
super().__init__()
|
||||
self.geo_field = geo_field
|
||||
|
||||
def get_prep_value(self, value):
|
||||
if not isinstance(value, Area):
|
||||
raise ValueError('AreaField only accepts Area measurement objects.')
|
||||
return value
|
||||
|
||||
def get_db_prep_value(self, value, connection, prepared=False):
|
||||
if value is None:
|
||||
return
|
||||
area_att = connection.ops.get_area_att_for_field(self.geo_field)
|
||||
return getattr(value, area_att) if area_att else value
|
||||
|
||||
def from_db_value(self, value, expression, connection):
|
||||
if value is None:
|
||||
return
|
||||
# If the database returns a Decimal, convert it to a float as expected
|
||||
# by the Python geometric objects.
|
||||
if isinstance(value, Decimal):
|
||||
value = float(value)
|
||||
# If the units are known, convert value into area measure.
|
||||
area_att = connection.ops.get_area_att_for_field(self.geo_field)
|
||||
return Area(**{area_att: value}) if area_att else value
|
||||
|
||||
def get_internal_type(self):
|
||||
return 'AreaField'
|
||||
|
||||
|
||||
class DistanceField(models.FloatField):
|
||||
"Wrapper for Distance values."
|
||||
def __init__(self, geo_field):
|
||||
super().__init__()
|
||||
self.geo_field = geo_field
|
||||
|
||||
def get_prep_value(self, value):
|
||||
if isinstance(value, Distance):
|
||||
return value
|
||||
return super().get_prep_value(value)
|
||||
|
||||
def get_db_prep_value(self, value, connection, prepared=False):
|
||||
if not isinstance(value, Distance):
|
||||
return value
|
||||
distance_att = connection.ops.get_distance_att_for_field(self.geo_field)
|
||||
if not distance_att:
|
||||
raise ValueError('Distance measure is supplied, but units are unknown for result.')
|
||||
return getattr(value, distance_att)
|
||||
|
||||
def from_db_value(self, value, expression, connection):
|
||||
if value is None:
|
||||
return
|
||||
distance_att = connection.ops.get_distance_att_for_field(self.geo_field)
|
||||
return Distance(**{distance_att: value}) if distance_att else value
|
||||
|
||||
def get_internal_type(self):
|
||||
return 'DistanceField'
|
||||
@@ -0,0 +1,132 @@
|
||||
from django import forms
|
||||
from django.contrib.gis.gdal import GDALException
|
||||
from django.contrib.gis.geos import GEOSException, GEOSGeometry
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from .widgets import OpenLayersWidget
|
||||
|
||||
|
||||
class GeometryField(forms.Field):
|
||||
"""
|
||||
This is the basic form field for a Geometry. Any textual input that is
|
||||
accepted by GEOSGeometry is accepted by this form. By default,
|
||||
this includes WKT, HEXEWKB, WKB (in a buffer), and GeoJSON.
|
||||
"""
|
||||
widget = OpenLayersWidget
|
||||
geom_type = 'GEOMETRY'
|
||||
|
||||
default_error_messages = {
|
||||
'required': _('No geometry value provided.'),
|
||||
'invalid_geom': _('Invalid geometry value.'),
|
||||
'invalid_geom_type': _('Invalid geometry type.'),
|
||||
'transform_error': _('An error occurred when transforming the geometry '
|
||||
'to the SRID of the geometry form field.'),
|
||||
}
|
||||
|
||||
def __init__(self, *, srid=None, geom_type=None, **kwargs):
|
||||
self.srid = srid
|
||||
if geom_type is not None:
|
||||
self.geom_type = geom_type
|
||||
super().__init__(**kwargs)
|
||||
self.widget.attrs['geom_type'] = self.geom_type
|
||||
|
||||
def to_python(self, value):
|
||||
"""Transform the value to a Geometry object."""
|
||||
if value in self.empty_values:
|
||||
return None
|
||||
|
||||
if not isinstance(value, GEOSGeometry):
|
||||
if hasattr(self.widget, 'deserialize'):
|
||||
try:
|
||||
value = self.widget.deserialize(value)
|
||||
except GDALException:
|
||||
value = None
|
||||
else:
|
||||
try:
|
||||
value = GEOSGeometry(value)
|
||||
except (GEOSException, ValueError, TypeError):
|
||||
value = None
|
||||
if value is None:
|
||||
raise forms.ValidationError(self.error_messages['invalid_geom'], code='invalid_geom')
|
||||
|
||||
# Try to set the srid
|
||||
if not value.srid:
|
||||
try:
|
||||
value.srid = self.widget.map_srid
|
||||
except AttributeError:
|
||||
if self.srid:
|
||||
value.srid = self.srid
|
||||
return value
|
||||
|
||||
def clean(self, value):
|
||||
"""
|
||||
Validate that the input value can be converted to a Geometry object
|
||||
and return it. Raise a ValidationError if the value cannot be
|
||||
instantiated as a Geometry.
|
||||
"""
|
||||
geom = super().clean(value)
|
||||
if geom is None:
|
||||
return geom
|
||||
|
||||
# Ensuring that the geometry is of the correct type (indicated
|
||||
# using the OGC string label).
|
||||
if str(geom.geom_type).upper() != self.geom_type and not self.geom_type == 'GEOMETRY':
|
||||
raise forms.ValidationError(self.error_messages['invalid_geom_type'], code='invalid_geom_type')
|
||||
|
||||
# Transforming the geometry if the SRID was set.
|
||||
if self.srid and self.srid != -1 and self.srid != geom.srid:
|
||||
try:
|
||||
geom.transform(self.srid)
|
||||
except GEOSException:
|
||||
raise forms.ValidationError(
|
||||
self.error_messages['transform_error'], code='transform_error')
|
||||
|
||||
return geom
|
||||
|
||||
def has_changed(self, initial, data):
|
||||
""" Compare geographic value of data with its initial value. """
|
||||
|
||||
try:
|
||||
data = self.to_python(data)
|
||||
initial = self.to_python(initial)
|
||||
except forms.ValidationError:
|
||||
return True
|
||||
|
||||
# Only do a geographic comparison if both values are available
|
||||
if initial and data:
|
||||
data.transform(initial.srid)
|
||||
# If the initial value was not added by the browser, the geometry
|
||||
# provided may be slightly different, the first time it is saved.
|
||||
# The comparison is done with a very low tolerance.
|
||||
return not initial.equals_exact(data, tolerance=0.000001)
|
||||
else:
|
||||
# Check for change of state of existence
|
||||
return bool(initial) != bool(data)
|
||||
|
||||
|
||||
class GeometryCollectionField(GeometryField):
|
||||
geom_type = 'GEOMETRYCOLLECTION'
|
||||
|
||||
|
||||
class PointField(GeometryField):
|
||||
geom_type = 'POINT'
|
||||
|
||||
|
||||
class MultiPointField(GeometryField):
|
||||
geom_type = 'MULTIPOINT'
|
||||
|
||||
|
||||
class LineStringField(GeometryField):
|
||||
geom_type = 'LINESTRING'
|
||||
|
||||
|
||||
class MultiLineStringField(GeometryField):
|
||||
geom_type = 'MULTILINESTRING'
|
||||
|
||||
|
||||
class PolygonField(GeometryField):
|
||||
geom_type = 'POLYGON'
|
||||
|
||||
|
||||
class MultiPolygonField(GeometryField):
|
||||
geom_type = 'MULTIPOLYGON'
|
||||
@@ -0,0 +1,117 @@
|
||||
import logging
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.gis import gdal
|
||||
from django.contrib.gis.geometry import json_regex
|
||||
from django.contrib.gis.geos import GEOSException, GEOSGeometry
|
||||
from django.forms.widgets import Widget
|
||||
from django.utils import translation
|
||||
|
||||
logger = logging.getLogger('django.contrib.gis')
|
||||
|
||||
|
||||
class BaseGeometryWidget(Widget):
|
||||
"""
|
||||
The base class for rich geometry widgets.
|
||||
Render a map using the WKT of the geometry.
|
||||
"""
|
||||
geom_type = 'GEOMETRY'
|
||||
map_srid = 4326
|
||||
map_width = 600
|
||||
map_height = 400
|
||||
display_raw = False
|
||||
|
||||
supports_3d = False
|
||||
template_name = '' # set on subclasses
|
||||
|
||||
def __init__(self, attrs=None):
|
||||
self.attrs = {}
|
||||
for key in ('geom_type', 'map_srid', 'map_width', 'map_height', 'display_raw'):
|
||||
self.attrs[key] = getattr(self, key)
|
||||
if attrs:
|
||||
self.attrs.update(attrs)
|
||||
|
||||
def serialize(self, value):
|
||||
return value.wkt if value else ''
|
||||
|
||||
def deserialize(self, value):
|
||||
try:
|
||||
return GEOSGeometry(value)
|
||||
except (GEOSException, ValueError, TypeError) as err:
|
||||
logger.error("Error creating geometry from value '%s' (%s)", value, err)
|
||||
return None
|
||||
|
||||
def get_context(self, name, value, attrs):
|
||||
context = super().get_context(name, value, attrs)
|
||||
# If a string reaches here (via a validation error on another
|
||||
# field) then just reconstruct the Geometry.
|
||||
if value and isinstance(value, str):
|
||||
value = self.deserialize(value)
|
||||
|
||||
if value:
|
||||
# Check that srid of value and map match
|
||||
if value.srid and value.srid != self.map_srid:
|
||||
try:
|
||||
ogr = value.ogr
|
||||
ogr.transform(self.map_srid)
|
||||
value = ogr
|
||||
except gdal.GDALException as err:
|
||||
logger.error(
|
||||
"Error transforming geometry from srid '%s' to srid '%s' (%s)",
|
||||
value.srid, self.map_srid, err
|
||||
)
|
||||
|
||||
context.update(self.build_attrs(self.attrs, {
|
||||
'name': name,
|
||||
'module': 'geodjango_%s' % name.replace('-', '_'), # JS-safe
|
||||
'serialized': self.serialize(value),
|
||||
'geom_type': gdal.OGRGeomType(self.attrs['geom_type']),
|
||||
'STATIC_URL': settings.STATIC_URL,
|
||||
'LANGUAGE_BIDI': translation.get_language_bidi(),
|
||||
**(attrs or {}),
|
||||
}))
|
||||
return context
|
||||
|
||||
|
||||
class OpenLayersWidget(BaseGeometryWidget):
|
||||
template_name = 'gis/openlayers.html'
|
||||
map_srid = 3857
|
||||
|
||||
class Media:
|
||||
css = {
|
||||
'all': (
|
||||
'https://cdnjs.cloudflare.com/ajax/libs/ol3/4.6.5/ol.css',
|
||||
'gis/css/ol3.css',
|
||||
)
|
||||
}
|
||||
js = (
|
||||
'https://cdnjs.cloudflare.com/ajax/libs/ol3/4.6.5/ol.js',
|
||||
'gis/js/OLMapWidget.js',
|
||||
)
|
||||
|
||||
def serialize(self, value):
|
||||
return value.json if value else ''
|
||||
|
||||
def deserialize(self, value):
|
||||
geom = super().deserialize(value)
|
||||
# GeoJSON assumes WGS84 (4326). Use the map's SRID instead.
|
||||
if geom and json_regex.match(value) and self.map_srid != 4326:
|
||||
geom.srid = self.map_srid
|
||||
return geom
|
||||
|
||||
|
||||
class OSMWidget(OpenLayersWidget):
|
||||
"""
|
||||
An OpenLayers/OpenStreetMap-based widget.
|
||||
"""
|
||||
template_name = 'gis/openlayers-osm.html'
|
||||
default_lon = 5
|
||||
default_lat = 47
|
||||
default_zoom = 12
|
||||
|
||||
def __init__(self, attrs=None):
|
||||
super().__init__()
|
||||
for key in ('default_lon', 'default_lat', 'default_zoom'):
|
||||
self.attrs[key] = getattr(self, key)
|
||||
if attrs:
|
||||
self.attrs.update(attrs)
|
||||
@@ -0,0 +1,178 @@
|
||||
"""
|
||||
The GDAL/OGR library uses an Envelope structure to hold the bounding
|
||||
box information for a geometry. The envelope (bounding box) contains
|
||||
two pairs of coordinates, one for the lower left coordinate and one
|
||||
for the upper right coordinate:
|
||||
|
||||
+----------o Upper right; (max_x, max_y)
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
Lower left (min_x, min_y) o----------+
|
||||
"""
|
||||
from ctypes import Structure, c_double
|
||||
|
||||
from django.contrib.gis.gdal.error import GDALException
|
||||
|
||||
|
||||
# The OGR definition of an Envelope is a C structure containing four doubles.
|
||||
# See the 'ogr_core.h' source file for more information:
|
||||
# https://www.gdal.org/ogr__core_8h_source.html
|
||||
class OGREnvelope(Structure):
|
||||
"Represent the OGREnvelope C Structure."
|
||||
_fields_ = [("MinX", c_double),
|
||||
("MaxX", c_double),
|
||||
("MinY", c_double),
|
||||
("MaxY", c_double),
|
||||
]
|
||||
|
||||
|
||||
class Envelope:
|
||||
"""
|
||||
The Envelope object is a C structure that contains the minimum and
|
||||
maximum X, Y coordinates for a rectangle bounding box. The naming
|
||||
of the variables is compatible with the OGR Envelope structure.
|
||||
"""
|
||||
|
||||
def __init__(self, *args):
|
||||
"""
|
||||
The initialization function may take an OGREnvelope structure, 4-element
|
||||
tuple or list, or 4 individual arguments.
|
||||
"""
|
||||
|
||||
if len(args) == 1:
|
||||
if isinstance(args[0], OGREnvelope):
|
||||
# OGREnvelope (a ctypes Structure) was passed in.
|
||||
self._envelope = args[0]
|
||||
elif isinstance(args[0], (tuple, list)):
|
||||
# A tuple was passed in.
|
||||
if len(args[0]) != 4:
|
||||
raise GDALException('Incorrect number of tuple elements (%d).' % len(args[0]))
|
||||
else:
|
||||
self._from_sequence(args[0])
|
||||
else:
|
||||
raise TypeError('Incorrect type of argument: %s' % type(args[0]))
|
||||
elif len(args) == 4:
|
||||
# Individual parameters passed in.
|
||||
# Thanks to ww for the help
|
||||
self._from_sequence([float(a) for a in args])
|
||||
else:
|
||||
raise GDALException('Incorrect number (%d) of arguments.' % len(args))
|
||||
|
||||
# Checking the x,y coordinates
|
||||
if self.min_x > self.max_x:
|
||||
raise GDALException('Envelope minimum X > maximum X.')
|
||||
if self.min_y > self.max_y:
|
||||
raise GDALException('Envelope minimum Y > maximum Y.')
|
||||
|
||||
def __eq__(self, other):
|
||||
"""
|
||||
Return True if the envelopes are equivalent; can compare against
|
||||
other Envelopes and 4-tuples.
|
||||
"""
|
||||
if isinstance(other, Envelope):
|
||||
return (self.min_x == other.min_x) and (self.min_y == other.min_y) and \
|
||||
(self.max_x == other.max_x) and (self.max_y == other.max_y)
|
||||
elif isinstance(other, tuple) and len(other) == 4:
|
||||
return (self.min_x == other[0]) and (self.min_y == other[1]) and \
|
||||
(self.max_x == other[2]) and (self.max_y == other[3])
|
||||
else:
|
||||
raise GDALException('Equivalence testing only works with other Envelopes.')
|
||||
|
||||
def __str__(self):
|
||||
"Return a string representation of the tuple."
|
||||
return str(self.tuple)
|
||||
|
||||
def _from_sequence(self, seq):
|
||||
"Initialize the C OGR Envelope structure from the given sequence."
|
||||
self._envelope = OGREnvelope()
|
||||
self._envelope.MinX = seq[0]
|
||||
self._envelope.MinY = seq[1]
|
||||
self._envelope.MaxX = seq[2]
|
||||
self._envelope.MaxY = seq[3]
|
||||
|
||||
def expand_to_include(self, *args):
|
||||
"""
|
||||
Modify the envelope to expand to include the boundaries of
|
||||
the passed-in 2-tuple (a point), 4-tuple (an extent) or
|
||||
envelope.
|
||||
"""
|
||||
# We provide a number of different signatures for this method,
|
||||
# and the logic here is all about converting them into a
|
||||
# 4-tuple single parameter which does the actual work of
|
||||
# expanding the envelope.
|
||||
if len(args) == 1:
|
||||
if isinstance(args[0], Envelope):
|
||||
return self.expand_to_include(args[0].tuple)
|
||||
elif hasattr(args[0], 'x') and hasattr(args[0], 'y'):
|
||||
return self.expand_to_include(args[0].x, args[0].y, args[0].x, args[0].y)
|
||||
elif isinstance(args[0], (tuple, list)):
|
||||
# A tuple was passed in.
|
||||
if len(args[0]) == 2:
|
||||
return self.expand_to_include((args[0][0], args[0][1], args[0][0], args[0][1]))
|
||||
elif len(args[0]) == 4:
|
||||
(minx, miny, maxx, maxy) = args[0]
|
||||
if minx < self._envelope.MinX:
|
||||
self._envelope.MinX = minx
|
||||
if miny < self._envelope.MinY:
|
||||
self._envelope.MinY = miny
|
||||
if maxx > self._envelope.MaxX:
|
||||
self._envelope.MaxX = maxx
|
||||
if maxy > self._envelope.MaxY:
|
||||
self._envelope.MaxY = maxy
|
||||
else:
|
||||
raise GDALException('Incorrect number of tuple elements (%d).' % len(args[0]))
|
||||
else:
|
||||
raise TypeError('Incorrect type of argument: %s' % type(args[0]))
|
||||
elif len(args) == 2:
|
||||
# An x and an y parameter were passed in
|
||||
return self.expand_to_include((args[0], args[1], args[0], args[1]))
|
||||
elif len(args) == 4:
|
||||
# Individual parameters passed in.
|
||||
return self.expand_to_include(args)
|
||||
else:
|
||||
raise GDALException('Incorrect number (%d) of arguments.' % len(args[0]))
|
||||
|
||||
@property
|
||||
def min_x(self):
|
||||
"Return the value of the minimum X coordinate."
|
||||
return self._envelope.MinX
|
||||
|
||||
@property
|
||||
def min_y(self):
|
||||
"Return the value of the minimum Y coordinate."
|
||||
return self._envelope.MinY
|
||||
|
||||
@property
|
||||
def max_x(self):
|
||||
"Return the value of the maximum X coordinate."
|
||||
return self._envelope.MaxX
|
||||
|
||||
@property
|
||||
def max_y(self):
|
||||
"Return the value of the maximum Y coordinate."
|
||||
return self._envelope.MaxY
|
||||
|
||||
@property
|
||||
def ur(self):
|
||||
"Return the upper-right coordinate."
|
||||
return (self.max_x, self.max_y)
|
||||
|
||||
@property
|
||||
def ll(self):
|
||||
"Return the lower-left coordinate."
|
||||
return (self.min_x, self.min_y)
|
||||
|
||||
@property
|
||||
def tuple(self):
|
||||
"Return a tuple representing the envelope."
|
||||
return (self.min_x, self.min_y, self.max_x, self.max_y)
|
||||
|
||||
@property
|
||||
def wkt(self):
|
||||
"Return WKT representing a Polygon for this envelope."
|
||||
# TODO: Fix significant figures.
|
||||
return 'POLYGON((%s %s,%s %s,%s %s,%s %s,%s %s))' % \
|
||||
(self.min_x, self.min_y, self.min_x, self.max_y,
|
||||
self.max_x, self.max_y, self.max_x, self.min_y,
|
||||
self.min_x, self.min_y)
|
||||
@@ -0,0 +1,715 @@
|
||||
"""
|
||||
The OGRGeometry is a wrapper for using the OGR Geometry class
|
||||
(see https://www.gdal.org/classOGRGeometry.html). OGRGeometry
|
||||
may be instantiated when reading geometries from OGR Data Sources
|
||||
(e.g. SHP files), or when given OGC WKT (a string).
|
||||
|
||||
While the 'full' API is not present yet, the API is "pythonic" unlike
|
||||
the traditional and "next-generation" OGR Python bindings. One major
|
||||
advantage OGR Geometries have over their GEOS counterparts is support
|
||||
for spatial reference systems and their transformation.
|
||||
|
||||
Example:
|
||||
>>> from django.contrib.gis.gdal import OGRGeometry, OGRGeomType, SpatialReference
|
||||
>>> wkt1, wkt2 = 'POINT(-90 30)', 'POLYGON((0 0, 5 0, 5 5, 0 5)'
|
||||
>>> pnt = OGRGeometry(wkt1)
|
||||
>>> print(pnt)
|
||||
POINT (-90 30)
|
||||
>>> mpnt = OGRGeometry(OGRGeomType('MultiPoint'), SpatialReference('WGS84'))
|
||||
>>> mpnt.add(wkt1)
|
||||
>>> mpnt.add(wkt1)
|
||||
>>> print(mpnt)
|
||||
MULTIPOINT (-90 30,-90 30)
|
||||
>>> print(mpnt.srs.name)
|
||||
WGS 84
|
||||
>>> print(mpnt.srs.proj)
|
||||
+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs
|
||||
>>> mpnt.transform(SpatialReference('NAD27'))
|
||||
>>> print(mpnt.proj)
|
||||
+proj=longlat +ellps=clrk66 +datum=NAD27 +no_defs
|
||||
>>> print(mpnt)
|
||||
MULTIPOINT (-89.999930378602485 29.999797886557641,-89.999930378602485 29.999797886557641)
|
||||
|
||||
The OGRGeomType class is to make it easy to specify an OGR geometry type:
|
||||
>>> from django.contrib.gis.gdal import OGRGeomType
|
||||
>>> gt1 = OGRGeomType(3) # Using an integer for the type
|
||||
>>> gt2 = OGRGeomType('Polygon') # Using a string
|
||||
>>> gt3 = OGRGeomType('POLYGON') # It's case-insensitive
|
||||
>>> print(gt1 == 3, gt1 == 'Polygon') # Equivalence works w/non-OGRGeomType objects
|
||||
True True
|
||||
"""
|
||||
import sys
|
||||
from binascii import b2a_hex
|
||||
from ctypes import byref, c_char_p, c_double, c_ubyte, c_void_p, string_at
|
||||
|
||||
from django.contrib.gis.gdal.base import GDALBase
|
||||
from django.contrib.gis.gdal.envelope import Envelope, OGREnvelope
|
||||
from django.contrib.gis.gdal.error import GDALException, SRSException
|
||||
from django.contrib.gis.gdal.geomtype import OGRGeomType
|
||||
from django.contrib.gis.gdal.libgdal import GDAL_VERSION
|
||||
from django.contrib.gis.gdal.prototypes import geom as capi, srs as srs_api
|
||||
from django.contrib.gis.gdal.srs import CoordTransform, SpatialReference
|
||||
from django.contrib.gis.geometry import hex_regex, json_regex, wkt_regex
|
||||
from django.utils.encoding import force_bytes
|
||||
|
||||
|
||||
# For more information, see the OGR C API source code:
|
||||
# https://www.gdal.org/ogr__api_8h.html
|
||||
#
|
||||
# The OGR_G_* routines are relevant here.
|
||||
class OGRGeometry(GDALBase):
|
||||
"""Encapsulate an OGR geometry."""
|
||||
destructor = capi.destroy_geom
|
||||
|
||||
def __init__(self, geom_input, srs=None):
|
||||
"""Initialize Geometry on either WKT or an OGR pointer as input."""
|
||||
str_instance = isinstance(geom_input, str)
|
||||
|
||||
# If HEX, unpack input to a binary buffer.
|
||||
if str_instance and hex_regex.match(geom_input):
|
||||
geom_input = memoryview(bytes.fromhex(geom_input))
|
||||
str_instance = False
|
||||
|
||||
# Constructing the geometry,
|
||||
if str_instance:
|
||||
wkt_m = wkt_regex.match(geom_input)
|
||||
json_m = json_regex.match(geom_input)
|
||||
if wkt_m:
|
||||
if wkt_m.group('srid'):
|
||||
# If there's EWKT, set the SRS w/value of the SRID.
|
||||
srs = int(wkt_m.group('srid'))
|
||||
if wkt_m.group('type').upper() == 'LINEARRING':
|
||||
# OGR_G_CreateFromWkt doesn't work with LINEARRING WKT.
|
||||
# See https://trac.osgeo.org/gdal/ticket/1992.
|
||||
g = capi.create_geom(OGRGeomType(wkt_m.group('type')).num)
|
||||
capi.import_wkt(g, byref(c_char_p(wkt_m.group('wkt').encode())))
|
||||
else:
|
||||
g = capi.from_wkt(byref(c_char_p(wkt_m.group('wkt').encode())), None, byref(c_void_p()))
|
||||
elif json_m:
|
||||
g = self._from_json(geom_input.encode())
|
||||
else:
|
||||
# Seeing if the input is a valid short-hand string
|
||||
# (e.g., 'Point', 'POLYGON').
|
||||
OGRGeomType(geom_input)
|
||||
g = capi.create_geom(OGRGeomType(geom_input).num)
|
||||
elif isinstance(geom_input, memoryview):
|
||||
# WKB was passed in
|
||||
g = self._from_wkb(geom_input)
|
||||
elif isinstance(geom_input, OGRGeomType):
|
||||
# OGRGeomType was passed in, an empty geometry will be created.
|
||||
g = capi.create_geom(geom_input.num)
|
||||
elif isinstance(geom_input, self.ptr_type):
|
||||
# OGR pointer (c_void_p) was the input.
|
||||
g = geom_input
|
||||
else:
|
||||
raise GDALException('Invalid input type for OGR Geometry construction: %s' % type(geom_input))
|
||||
|
||||
# Now checking the Geometry pointer before finishing initialization
|
||||
# by setting the pointer for the object.
|
||||
if not g:
|
||||
raise GDALException('Cannot create OGR Geometry from input: %s' % geom_input)
|
||||
self.ptr = g
|
||||
|
||||
# Assigning the SpatialReference object to the geometry, if valid.
|
||||
if srs:
|
||||
self.srs = srs
|
||||
|
||||
# Setting the class depending upon the OGR Geometry Type
|
||||
self.__class__ = GEO_CLASSES[self.geom_type.num]
|
||||
|
||||
# Pickle routines
|
||||
def __getstate__(self):
|
||||
srs = self.srs
|
||||
if srs:
|
||||
srs = srs.wkt
|
||||
else:
|
||||
srs = None
|
||||
return bytes(self.wkb), srs
|
||||
|
||||
def __setstate__(self, state):
|
||||
wkb, srs = state
|
||||
ptr = capi.from_wkb(wkb, None, byref(c_void_p()), len(wkb))
|
||||
if not ptr:
|
||||
raise GDALException('Invalid OGRGeometry loaded from pickled state.')
|
||||
self.ptr = ptr
|
||||
self.srs = srs
|
||||
|
||||
@classmethod
|
||||
def _from_wkb(cls, geom_input):
|
||||
return capi.from_wkb(bytes(geom_input), None, byref(c_void_p()), len(geom_input))
|
||||
|
||||
@staticmethod
|
||||
def _from_json(geom_input):
|
||||
ptr = capi.from_json(geom_input)
|
||||
if GDAL_VERSION < (2, 0):
|
||||
try:
|
||||
capi.get_geom_srs(ptr)
|
||||
except SRSException:
|
||||
srs = SpatialReference(4326)
|
||||
capi.assign_srs(ptr, srs.ptr)
|
||||
return ptr
|
||||
|
||||
@classmethod
|
||||
def from_bbox(cls, bbox):
|
||||
"Construct a Polygon from a bounding box (4-tuple)."
|
||||
x0, y0, x1, y1 = bbox
|
||||
return OGRGeometry('POLYGON((%s %s, %s %s, %s %s, %s %s, %s %s))' % (
|
||||
x0, y0, x0, y1, x1, y1, x1, y0, x0, y0))
|
||||
|
||||
@staticmethod
|
||||
def from_json(geom_input):
|
||||
return OGRGeometry(OGRGeometry._from_json(force_bytes(geom_input)))
|
||||
|
||||
@classmethod
|
||||
def from_gml(cls, gml_string):
|
||||
return cls(capi.from_gml(force_bytes(gml_string)))
|
||||
|
||||
# ### Geometry set-like operations ###
|
||||
# g = g1 | g2
|
||||
def __or__(self, other):
|
||||
"Return the union of the two geometries."
|
||||
return self.union(other)
|
||||
|
||||
# g = g1 & g2
|
||||
def __and__(self, other):
|
||||
"Return the intersection of this Geometry and the other."
|
||||
return self.intersection(other)
|
||||
|
||||
# g = g1 - g2
|
||||
def __sub__(self, other):
|
||||
"Return the difference this Geometry and the other."
|
||||
return self.difference(other)
|
||||
|
||||
# g = g1 ^ g2
|
||||
def __xor__(self, other):
|
||||
"Return the symmetric difference of this Geometry and the other."
|
||||
return self.sym_difference(other)
|
||||
|
||||
def __eq__(self, other):
|
||||
"Is this Geometry equal to the other?"
|
||||
return isinstance(other, OGRGeometry) and self.equals(other)
|
||||
|
||||
def __str__(self):
|
||||
"WKT is used for the string representation."
|
||||
return self.wkt
|
||||
|
||||
# #### Geometry Properties ####
|
||||
@property
|
||||
def dimension(self):
|
||||
"Return 0 for points, 1 for lines, and 2 for surfaces."
|
||||
return capi.get_dims(self.ptr)
|
||||
|
||||
def _get_coord_dim(self):
|
||||
"Return the coordinate dimension of the Geometry."
|
||||
return capi.get_coord_dim(self.ptr)
|
||||
|
||||
def _set_coord_dim(self, dim):
|
||||
"Set the coordinate dimension of this Geometry."
|
||||
if dim not in (2, 3):
|
||||
raise ValueError('Geometry dimension must be either 2 or 3')
|
||||
capi.set_coord_dim(self.ptr, dim)
|
||||
|
||||
coord_dim = property(_get_coord_dim, _set_coord_dim)
|
||||
|
||||
@property
|
||||
def geom_count(self):
|
||||
"Return the number of elements in this Geometry."
|
||||
return capi.get_geom_count(self.ptr)
|
||||
|
||||
@property
|
||||
def point_count(self):
|
||||
"Return the number of Points in this Geometry."
|
||||
return capi.get_point_count(self.ptr)
|
||||
|
||||
@property
|
||||
def num_points(self):
|
||||
"Alias for `point_count` (same name method in GEOS API.)"
|
||||
return self.point_count
|
||||
|
||||
@property
|
||||
def num_coords(self):
|
||||
"Alias for `point_count`."
|
||||
return self.point_count
|
||||
|
||||
@property
|
||||
def geom_type(self):
|
||||
"Return the Type for this Geometry."
|
||||
return OGRGeomType(capi.get_geom_type(self.ptr))
|
||||
|
||||
@property
|
||||
def geom_name(self):
|
||||
"Return the Name of this Geometry."
|
||||
return capi.get_geom_name(self.ptr)
|
||||
|
||||
@property
|
||||
def area(self):
|
||||
"Return the area for a LinearRing, Polygon, or MultiPolygon; 0 otherwise."
|
||||
return capi.get_area(self.ptr)
|
||||
|
||||
@property
|
||||
def envelope(self):
|
||||
"Return the envelope for this Geometry."
|
||||
# TODO: Fix Envelope() for Point geometries.
|
||||
return Envelope(capi.get_envelope(self.ptr, byref(OGREnvelope())))
|
||||
|
||||
@property
|
||||
def empty(self):
|
||||
return capi.is_empty(self.ptr)
|
||||
|
||||
@property
|
||||
def extent(self):
|
||||
"Return the envelope as a 4-tuple, instead of as an Envelope object."
|
||||
return self.envelope.tuple
|
||||
|
||||
# #### SpatialReference-related Properties ####
|
||||
|
||||
# The SRS property
|
||||
def _get_srs(self):
|
||||
"Return the Spatial Reference for this Geometry."
|
||||
try:
|
||||
srs_ptr = capi.get_geom_srs(self.ptr)
|
||||
return SpatialReference(srs_api.clone_srs(srs_ptr))
|
||||
except SRSException:
|
||||
return None
|
||||
|
||||
def _set_srs(self, srs):
|
||||
"Set the SpatialReference for this geometry."
|
||||
# Do not have to clone the `SpatialReference` object pointer because
|
||||
# when it is assigned to this `OGRGeometry` it's internal OGR
|
||||
# reference count is incremented, and will likewise be released
|
||||
# (decremented) when this geometry's destructor is called.
|
||||
if isinstance(srs, SpatialReference):
|
||||
srs_ptr = srs.ptr
|
||||
elif isinstance(srs, (int, str)):
|
||||
sr = SpatialReference(srs)
|
||||
srs_ptr = sr.ptr
|
||||
elif srs is None:
|
||||
srs_ptr = None
|
||||
else:
|
||||
raise TypeError('Cannot assign spatial reference with object of type: %s' % type(srs))
|
||||
capi.assign_srs(self.ptr, srs_ptr)
|
||||
|
||||
srs = property(_get_srs, _set_srs)
|
||||
|
||||
# The SRID property
|
||||
def _get_srid(self):
|
||||
srs = self.srs
|
||||
if srs:
|
||||
return srs.srid
|
||||
return None
|
||||
|
||||
def _set_srid(self, srid):
|
||||
if isinstance(srid, int) or srid is None:
|
||||
self.srs = srid
|
||||
else:
|
||||
raise TypeError('SRID must be set with an integer.')
|
||||
|
||||
srid = property(_get_srid, _set_srid)
|
||||
|
||||
# #### Output Methods ####
|
||||
def _geos_ptr(self):
|
||||
from django.contrib.gis.geos import GEOSGeometry
|
||||
return GEOSGeometry._from_wkb(self.wkb)
|
||||
|
||||
@property
|
||||
def geos(self):
|
||||
"Return a GEOSGeometry object from this OGRGeometry."
|
||||
from django.contrib.gis.geos import GEOSGeometry
|
||||
return GEOSGeometry(self._geos_ptr(), self.srid)
|
||||
|
||||
@property
|
||||
def gml(self):
|
||||
"Return the GML representation of the Geometry."
|
||||
return capi.to_gml(self.ptr)
|
||||
|
||||
@property
|
||||
def hex(self):
|
||||
"Return the hexadecimal representation of the WKB (a string)."
|
||||
return b2a_hex(self.wkb).upper()
|
||||
|
||||
@property
|
||||
def json(self):
|
||||
"""
|
||||
Return the GeoJSON representation of this Geometry.
|
||||
"""
|
||||
return capi.to_json(self.ptr)
|
||||
geojson = json
|
||||
|
||||
@property
|
||||
def kml(self):
|
||||
"Return the KML representation of the Geometry."
|
||||
return capi.to_kml(self.ptr, None)
|
||||
|
||||
@property
|
||||
def wkb_size(self):
|
||||
"Return the size of the WKB buffer."
|
||||
return capi.get_wkbsize(self.ptr)
|
||||
|
||||
@property
|
||||
def wkb(self):
|
||||
"Return the WKB representation of the Geometry."
|
||||
if sys.byteorder == 'little':
|
||||
byteorder = 1 # wkbNDR (from ogr_core.h)
|
||||
else:
|
||||
byteorder = 0 # wkbXDR
|
||||
sz = self.wkb_size
|
||||
# Creating the unsigned character buffer, and passing it in by reference.
|
||||
buf = (c_ubyte * sz)()
|
||||
capi.to_wkb(self.ptr, byteorder, byref(buf))
|
||||
# Returning a buffer of the string at the pointer.
|
||||
return memoryview(string_at(buf, sz))
|
||||
|
||||
@property
|
||||
def wkt(self):
|
||||
"Return the WKT representation of the Geometry."
|
||||
return capi.to_wkt(self.ptr, byref(c_char_p()))
|
||||
|
||||
@property
|
||||
def ewkt(self):
|
||||
"Return the EWKT representation of the Geometry."
|
||||
srs = self.srs
|
||||
if srs and srs.srid:
|
||||
return 'SRID=%s;%s' % (srs.srid, self.wkt)
|
||||
else:
|
||||
return self.wkt
|
||||
|
||||
# #### Geometry Methods ####
|
||||
def clone(self):
|
||||
"Clone this OGR Geometry."
|
||||
return OGRGeometry(capi.clone_geom(self.ptr), self.srs)
|
||||
|
||||
def close_rings(self):
|
||||
"""
|
||||
If there are any rings within this geometry that have not been
|
||||
closed, this routine will do so by adding the starting point at the
|
||||
end.
|
||||
"""
|
||||
# Closing the open rings.
|
||||
capi.geom_close_rings(self.ptr)
|
||||
|
||||
def transform(self, coord_trans, clone=False):
|
||||
"""
|
||||
Transform this geometry to a different spatial reference system.
|
||||
May take a CoordTransform object, a SpatialReference object, string
|
||||
WKT or PROJ.4, and/or an integer SRID. By default, return nothing
|
||||
and transform the geometry in-place. However, if the `clone` keyword is
|
||||
set, return a transformed clone of this geometry.
|
||||
"""
|
||||
if clone:
|
||||
klone = self.clone()
|
||||
klone.transform(coord_trans)
|
||||
return klone
|
||||
|
||||
# Depending on the input type, use the appropriate OGR routine
|
||||
# to perform the transformation.
|
||||
if isinstance(coord_trans, CoordTransform):
|
||||
capi.geom_transform(self.ptr, coord_trans.ptr)
|
||||
elif isinstance(coord_trans, SpatialReference):
|
||||
capi.geom_transform_to(self.ptr, coord_trans.ptr)
|
||||
elif isinstance(coord_trans, (int, str)):
|
||||
sr = SpatialReference(coord_trans)
|
||||
capi.geom_transform_to(self.ptr, sr.ptr)
|
||||
else:
|
||||
raise TypeError('Transform only accepts CoordTransform, '
|
||||
'SpatialReference, string, and integer objects.')
|
||||
|
||||
# #### Topology Methods ####
|
||||
def _topology(self, func, other):
|
||||
"""A generalized function for topology operations, takes a GDAL function and
|
||||
the other geometry to perform the operation on."""
|
||||
if not isinstance(other, OGRGeometry):
|
||||
raise TypeError('Must use another OGRGeometry object for topology operations!')
|
||||
|
||||
# Returning the output of the given function with the other geometry's
|
||||
# pointer.
|
||||
return func(self.ptr, other.ptr)
|
||||
|
||||
def intersects(self, other):
|
||||
"Return True if this geometry intersects with the other."
|
||||
return self._topology(capi.ogr_intersects, other)
|
||||
|
||||
def equals(self, other):
|
||||
"Return True if this geometry is equivalent to the other."
|
||||
return self._topology(capi.ogr_equals, other)
|
||||
|
||||
def disjoint(self, other):
|
||||
"Return True if this geometry and the other are spatially disjoint."
|
||||
return self._topology(capi.ogr_disjoint, other)
|
||||
|
||||
def touches(self, other):
|
||||
"Return True if this geometry touches the other."
|
||||
return self._topology(capi.ogr_touches, other)
|
||||
|
||||
def crosses(self, other):
|
||||
"Return True if this geometry crosses the other."
|
||||
return self._topology(capi.ogr_crosses, other)
|
||||
|
||||
def within(self, other):
|
||||
"Return True if this geometry is within the other."
|
||||
return self._topology(capi.ogr_within, other)
|
||||
|
||||
def contains(self, other):
|
||||
"Return True if this geometry contains the other."
|
||||
return self._topology(capi.ogr_contains, other)
|
||||
|
||||
def overlaps(self, other):
|
||||
"Return True if this geometry overlaps the other."
|
||||
return self._topology(capi.ogr_overlaps, other)
|
||||
|
||||
# #### Geometry-generation Methods ####
|
||||
def _geomgen(self, gen_func, other=None):
|
||||
"A helper routine for the OGR routines that generate geometries."
|
||||
if isinstance(other, OGRGeometry):
|
||||
return OGRGeometry(gen_func(self.ptr, other.ptr), self.srs)
|
||||
else:
|
||||
return OGRGeometry(gen_func(self.ptr), self.srs)
|
||||
|
||||
@property
|
||||
def boundary(self):
|
||||
"Return the boundary of this geometry."
|
||||
return self._geomgen(capi.get_boundary)
|
||||
|
||||
@property
|
||||
def convex_hull(self):
|
||||
"""
|
||||
Return the smallest convex Polygon that contains all the points in
|
||||
this Geometry.
|
||||
"""
|
||||
return self._geomgen(capi.geom_convex_hull)
|
||||
|
||||
def difference(self, other):
|
||||
"""
|
||||
Return a new geometry consisting of the region which is the difference
|
||||
of this geometry and the other.
|
||||
"""
|
||||
return self._geomgen(capi.geom_diff, other)
|
||||
|
||||
def intersection(self, other):
|
||||
"""
|
||||
Return a new geometry consisting of the region of intersection of this
|
||||
geometry and the other.
|
||||
"""
|
||||
return self._geomgen(capi.geom_intersection, other)
|
||||
|
||||
def sym_difference(self, other):
|
||||
"""
|
||||
Return a new geometry which is the symmetric difference of this
|
||||
geometry and the other.
|
||||
"""
|
||||
return self._geomgen(capi.geom_sym_diff, other)
|
||||
|
||||
def union(self, other):
|
||||
"""
|
||||
Return a new geometry consisting of the region which is the union of
|
||||
this geometry and the other.
|
||||
"""
|
||||
return self._geomgen(capi.geom_union, other)
|
||||
|
||||
|
||||
# The subclasses for OGR Geometry.
|
||||
class Point(OGRGeometry):
|
||||
|
||||
def _geos_ptr(self):
|
||||
from django.contrib.gis import geos
|
||||
return geos.Point._create_empty() if self.empty else super()._geos_ptr()
|
||||
|
||||
@classmethod
|
||||
def _create_empty(cls):
|
||||
return capi.create_geom(OGRGeomType('point').num)
|
||||
|
||||
@property
|
||||
def x(self):
|
||||
"Return the X coordinate for this Point."
|
||||
return capi.getx(self.ptr, 0)
|
||||
|
||||
@property
|
||||
def y(self):
|
||||
"Return the Y coordinate for this Point."
|
||||
return capi.gety(self.ptr, 0)
|
||||
|
||||
@property
|
||||
def z(self):
|
||||
"Return the Z coordinate for this Point."
|
||||
if self.coord_dim == 3:
|
||||
return capi.getz(self.ptr, 0)
|
||||
|
||||
@property
|
||||
def tuple(self):
|
||||
"Return the tuple of this point."
|
||||
if self.coord_dim == 2:
|
||||
return (self.x, self.y)
|
||||
elif self.coord_dim == 3:
|
||||
return (self.x, self.y, self.z)
|
||||
coords = tuple
|
||||
|
||||
|
||||
class LineString(OGRGeometry):
|
||||
|
||||
def __getitem__(self, index):
|
||||
"Return the Point at the given index."
|
||||
if 0 <= index < self.point_count:
|
||||
x, y, z = c_double(), c_double(), c_double()
|
||||
capi.get_point(self.ptr, index, byref(x), byref(y), byref(z))
|
||||
dim = self.coord_dim
|
||||
if dim == 1:
|
||||
return (x.value,)
|
||||
elif dim == 2:
|
||||
return (x.value, y.value)
|
||||
elif dim == 3:
|
||||
return (x.value, y.value, z.value)
|
||||
else:
|
||||
raise IndexError('Index out of range when accessing points of a line string: %s.' % index)
|
||||
|
||||
def __len__(self):
|
||||
"Return the number of points in the LineString."
|
||||
return self.point_count
|
||||
|
||||
@property
|
||||
def tuple(self):
|
||||
"Return the tuple representation of this LineString."
|
||||
return tuple(self[i] for i in range(len(self)))
|
||||
coords = tuple
|
||||
|
||||
def _listarr(self, func):
|
||||
"""
|
||||
Internal routine that returns a sequence (list) corresponding with
|
||||
the given function.
|
||||
"""
|
||||
return [func(self.ptr, i) for i in range(len(self))]
|
||||
|
||||
@property
|
||||
def x(self):
|
||||
"Return the X coordinates in a list."
|
||||
return self._listarr(capi.getx)
|
||||
|
||||
@property
|
||||
def y(self):
|
||||
"Return the Y coordinates in a list."
|
||||
return self._listarr(capi.gety)
|
||||
|
||||
@property
|
||||
def z(self):
|
||||
"Return the Z coordinates in a list."
|
||||
if self.coord_dim == 3:
|
||||
return self._listarr(capi.getz)
|
||||
|
||||
|
||||
# LinearRings are used in Polygons.
|
||||
class LinearRing(LineString):
|
||||
pass
|
||||
|
||||
|
||||
class Polygon(OGRGeometry):
|
||||
|
||||
def __len__(self):
|
||||
"Return the number of interior rings in this Polygon."
|
||||
return self.geom_count
|
||||
|
||||
def __getitem__(self, index):
|
||||
"Get the ring at the specified index."
|
||||
if 0 <= index < self.geom_count:
|
||||
return OGRGeometry(capi.clone_geom(capi.get_geom_ref(self.ptr, index)), self.srs)
|
||||
else:
|
||||
raise IndexError('Index out of range when accessing rings of a polygon: %s.' % index)
|
||||
|
||||
# Polygon Properties
|
||||
@property
|
||||
def shell(self):
|
||||
"Return the shell of this Polygon."
|
||||
return self[0] # First ring is the shell
|
||||
exterior_ring = shell
|
||||
|
||||
@property
|
||||
def tuple(self):
|
||||
"Return a tuple of LinearRing coordinate tuples."
|
||||
return tuple(self[i].tuple for i in range(self.geom_count))
|
||||
coords = tuple
|
||||
|
||||
@property
|
||||
def point_count(self):
|
||||
"Return the number of Points in this Polygon."
|
||||
# Summing up the number of points in each ring of the Polygon.
|
||||
return sum(self[i].point_count for i in range(self.geom_count))
|
||||
|
||||
@property
|
||||
def centroid(self):
|
||||
"Return the centroid (a Point) of this Polygon."
|
||||
# The centroid is a Point, create a geometry for this.
|
||||
p = OGRGeometry(OGRGeomType('Point'))
|
||||
capi.get_centroid(self.ptr, p.ptr)
|
||||
return p
|
||||
|
||||
|
||||
# Geometry Collection base class.
|
||||
class GeometryCollection(OGRGeometry):
|
||||
"The Geometry Collection class."
|
||||
|
||||
def __getitem__(self, index):
|
||||
"Get the Geometry at the specified index."
|
||||
if 0 <= index < self.geom_count:
|
||||
return OGRGeometry(capi.clone_geom(capi.get_geom_ref(self.ptr, index)), self.srs)
|
||||
else:
|
||||
raise IndexError('Index out of range when accessing geometry in a collection: %s.' % index)
|
||||
|
||||
def __len__(self):
|
||||
"Return the number of geometries in this Geometry Collection."
|
||||
return self.geom_count
|
||||
|
||||
def add(self, geom):
|
||||
"Add the geometry to this Geometry Collection."
|
||||
if isinstance(geom, OGRGeometry):
|
||||
if isinstance(geom, self.__class__):
|
||||
for g in geom:
|
||||
capi.add_geom(self.ptr, g.ptr)
|
||||
else:
|
||||
capi.add_geom(self.ptr, geom.ptr)
|
||||
elif isinstance(geom, str):
|
||||
tmp = OGRGeometry(geom)
|
||||
capi.add_geom(self.ptr, tmp.ptr)
|
||||
else:
|
||||
raise GDALException('Must add an OGRGeometry.')
|
||||
|
||||
@property
|
||||
def point_count(self):
|
||||
"Return the number of Points in this Geometry Collection."
|
||||
# Summing up the number of points in each geometry in this collection
|
||||
return sum(self[i].point_count for i in range(self.geom_count))
|
||||
|
||||
@property
|
||||
def tuple(self):
|
||||
"Return a tuple representation of this Geometry Collection."
|
||||
return tuple(self[i].tuple for i in range(self.geom_count))
|
||||
coords = tuple
|
||||
|
||||
|
||||
# Multiple Geometry types.
|
||||
class MultiPoint(GeometryCollection):
|
||||
pass
|
||||
|
||||
|
||||
class MultiLineString(GeometryCollection):
|
||||
pass
|
||||
|
||||
|
||||
class MultiPolygon(GeometryCollection):
|
||||
pass
|
||||
|
||||
|
||||
# Class mapping dictionary (using the OGRwkbGeometryType as the key)
|
||||
GEO_CLASSES = {
|
||||
1: Point,
|
||||
2: LineString,
|
||||
3: Polygon,
|
||||
4: MultiPoint,
|
||||
5: MultiLineString,
|
||||
6: MultiPolygon,
|
||||
7: GeometryCollection,
|
||||
101: LinearRing,
|
||||
1 + OGRGeomType.wkb25bit: Point,
|
||||
2 + OGRGeomType.wkb25bit: LineString,
|
||||
3 + OGRGeomType.wkb25bit: Polygon,
|
||||
4 + OGRGeomType.wkb25bit: MultiPoint,
|
||||
5 + OGRGeomType.wkb25bit: MultiLineString,
|
||||
6 + OGRGeomType.wkb25bit: MultiPolygon,
|
||||
7 + OGRGeomType.wkb25bit: GeometryCollection,
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
"""
|
||||
This module contains functions that generate ctypes prototypes for the
|
||||
GDAL routines.
|
||||
"""
|
||||
from ctypes import POINTER, c_char_p, c_double, c_int, c_int64, c_void_p
|
||||
from functools import partial
|
||||
|
||||
from django.contrib.gis.gdal.prototypes.errcheck import (
|
||||
check_arg_errcode, check_const_string, check_errcode, check_geom,
|
||||
check_geom_offset, check_pointer, check_srs, check_str_arg, check_string,
|
||||
)
|
||||
|
||||
|
||||
class gdal_char_p(c_char_p):
|
||||
pass
|
||||
|
||||
|
||||
def double_output(func, argtypes, errcheck=False, strarg=False, cpl=False):
|
||||
"Generate a ctypes function that returns a double value."
|
||||
func.argtypes = argtypes
|
||||
func.restype = c_double
|
||||
if errcheck:
|
||||
func.errcheck = partial(check_arg_errcode, cpl=cpl)
|
||||
if strarg:
|
||||
func.errcheck = check_str_arg
|
||||
return func
|
||||
|
||||
|
||||
def geom_output(func, argtypes, offset=None):
|
||||
"""
|
||||
Generate a function that returns a Geometry either by reference
|
||||
or directly (if the return_geom keyword is set to True).
|
||||
"""
|
||||
# Setting the argument types
|
||||
func.argtypes = argtypes
|
||||
|
||||
if not offset:
|
||||
# When a geometry pointer is directly returned.
|
||||
func.restype = c_void_p
|
||||
func.errcheck = check_geom
|
||||
else:
|
||||
# Error code returned, geometry is returned by-reference.
|
||||
func.restype = c_int
|
||||
|
||||
def geomerrcheck(result, func, cargs):
|
||||
return check_geom_offset(result, func, cargs, offset)
|
||||
func.errcheck = geomerrcheck
|
||||
|
||||
return func
|
||||
|
||||
|
||||
def int_output(func, argtypes, errcheck=None):
|
||||
"Generate a ctypes function that returns an integer value."
|
||||
func.argtypes = argtypes
|
||||
func.restype = c_int
|
||||
if errcheck:
|
||||
func.errcheck = errcheck
|
||||
return func
|
||||
|
||||
|
||||
def int64_output(func, argtypes):
|
||||
"Generate a ctypes function that returns a 64-bit integer value."
|
||||
func.argtypes = argtypes
|
||||
func.restype = c_int64
|
||||
return func
|
||||
|
||||
|
||||
def srs_output(func, argtypes):
|
||||
"""
|
||||
Generate a ctypes prototype for the given function with
|
||||
the given C arguments that returns a pointer to an OGR
|
||||
Spatial Reference System.
|
||||
"""
|
||||
func.argtypes = argtypes
|
||||
func.restype = c_void_p
|
||||
func.errcheck = check_srs
|
||||
return func
|
||||
|
||||
|
||||
def const_string_output(func, argtypes, offset=None, decoding=None, cpl=False):
|
||||
func.argtypes = argtypes
|
||||
if offset:
|
||||
func.restype = c_int
|
||||
else:
|
||||
func.restype = c_char_p
|
||||
|
||||
def _check_const(result, func, cargs):
|
||||
res = check_const_string(result, func, cargs, offset=offset, cpl=cpl)
|
||||
if res and decoding:
|
||||
res = res.decode(decoding)
|
||||
return res
|
||||
func.errcheck = _check_const
|
||||
|
||||
return func
|
||||
|
||||
|
||||
def string_output(func, argtypes, offset=-1, str_result=False, decoding=None):
|
||||
"""
|
||||
Generate a ctypes prototype for the given function with the
|
||||
given argument types that returns a string from a GDAL pointer.
|
||||
The `const` flag indicates whether the allocated pointer should
|
||||
be freed via the GDAL library routine VSIFree -- but only applies
|
||||
only when `str_result` is True.
|
||||
"""
|
||||
func.argtypes = argtypes
|
||||
if str_result:
|
||||
# Use subclass of c_char_p so the error checking routine
|
||||
# can free the memory at the pointer's address.
|
||||
func.restype = gdal_char_p
|
||||
else:
|
||||
# Error code is returned
|
||||
func.restype = c_int
|
||||
|
||||
# Dynamically defining our error-checking function with the
|
||||
# given offset.
|
||||
def _check_str(result, func, cargs):
|
||||
res = check_string(result, func, cargs, offset=offset, str_result=str_result)
|
||||
if res and decoding:
|
||||
res = res.decode(decoding)
|
||||
return res
|
||||
func.errcheck = _check_str
|
||||
return func
|
||||
|
||||
|
||||
def void_output(func, argtypes, errcheck=True, cpl=False):
|
||||
"""
|
||||
For functions that don't only return an error code that needs to
|
||||
be examined.
|
||||
"""
|
||||
if argtypes:
|
||||
func.argtypes = argtypes
|
||||
if errcheck:
|
||||
# `errcheck` keyword may be set to False for routines that
|
||||
# return void, rather than a status code.
|
||||
func.restype = c_int
|
||||
func.errcheck = partial(check_errcode, cpl=cpl)
|
||||
else:
|
||||
func.restype = None
|
||||
|
||||
return func
|
||||
|
||||
|
||||
def voidptr_output(func, argtypes, errcheck=True):
|
||||
"For functions that return c_void_p."
|
||||
func.argtypes = argtypes
|
||||
func.restype = c_void_p
|
||||
if errcheck:
|
||||
func.errcheck = check_pointer
|
||||
return func
|
||||
|
||||
|
||||
def chararray_output(func, argtypes, errcheck=True):
|
||||
"""For functions that return a c_char_p array."""
|
||||
func.argtypes = argtypes
|
||||
func.restype = POINTER(c_char_p)
|
||||
if errcheck:
|
||||
func.errcheck = check_pointer
|
||||
return func
|
||||
@@ -0,0 +1,108 @@
|
||||
"""
|
||||
This module houses the ctypes function prototypes for GDAL DataSource (raster)
|
||||
related data structures.
|
||||
"""
|
||||
from ctypes import POINTER, c_bool, c_char_p, c_double, c_int, c_void_p
|
||||
from functools import partial
|
||||
|
||||
from django.contrib.gis.gdal.libgdal import GDAL_VERSION, std_call
|
||||
from django.contrib.gis.gdal.prototypes.generation import (
|
||||
chararray_output, const_string_output, double_output, int_output,
|
||||
void_output, voidptr_output,
|
||||
)
|
||||
|
||||
# For more detail about c function names and definitions see
|
||||
# https://gdal.org/gdal_8h.html
|
||||
# https://gdal.org/gdalwarper_8h.html
|
||||
# https://www.gdal.org/gdal__utils_8h.html
|
||||
|
||||
# Prepare partial functions that use cpl error codes
|
||||
void_output = partial(void_output, cpl=True)
|
||||
const_string_output = partial(const_string_output, cpl=True)
|
||||
double_output = partial(double_output, cpl=True)
|
||||
|
||||
# Raster Driver Routines
|
||||
register_all = void_output(std_call('GDALAllRegister'), [], errcheck=False)
|
||||
get_driver = voidptr_output(std_call('GDALGetDriver'), [c_int])
|
||||
get_driver_by_name = voidptr_output(std_call('GDALGetDriverByName'), [c_char_p], errcheck=False)
|
||||
get_driver_count = int_output(std_call('GDALGetDriverCount'), [])
|
||||
get_driver_description = const_string_output(std_call('GDALGetDescription'), [c_void_p])
|
||||
|
||||
# Raster Data Source Routines
|
||||
create_ds = voidptr_output(std_call('GDALCreate'), [c_void_p, c_char_p, c_int, c_int, c_int, c_int, c_void_p])
|
||||
open_ds = voidptr_output(std_call('GDALOpen'), [c_char_p, c_int])
|
||||
close_ds = void_output(std_call('GDALClose'), [c_void_p], errcheck=False)
|
||||
flush_ds = int_output(std_call('GDALFlushCache'), [c_void_p])
|
||||
copy_ds = voidptr_output(
|
||||
std_call('GDALCreateCopy'),
|
||||
[c_void_p, c_char_p, c_void_p, c_int, POINTER(c_char_p), c_void_p, c_void_p]
|
||||
)
|
||||
add_band_ds = void_output(std_call('GDALAddBand'), [c_void_p, c_int])
|
||||
get_ds_description = const_string_output(std_call('GDALGetDescription'), [c_void_p])
|
||||
get_ds_driver = voidptr_output(std_call('GDALGetDatasetDriver'), [c_void_p])
|
||||
get_ds_xsize = int_output(std_call('GDALGetRasterXSize'), [c_void_p])
|
||||
get_ds_ysize = int_output(std_call('GDALGetRasterYSize'), [c_void_p])
|
||||
get_ds_raster_count = int_output(std_call('GDALGetRasterCount'), [c_void_p])
|
||||
get_ds_raster_band = voidptr_output(std_call('GDALGetRasterBand'), [c_void_p, c_int])
|
||||
get_ds_projection_ref = const_string_output(std_call('GDALGetProjectionRef'), [c_void_p])
|
||||
set_ds_projection_ref = void_output(std_call('GDALSetProjection'), [c_void_p, c_char_p])
|
||||
get_ds_geotransform = void_output(std_call('GDALGetGeoTransform'), [c_void_p, POINTER(c_double * 6)], errcheck=False)
|
||||
set_ds_geotransform = void_output(std_call('GDALSetGeoTransform'), [c_void_p, POINTER(c_double * 6)])
|
||||
|
||||
get_ds_metadata = chararray_output(std_call('GDALGetMetadata'), [c_void_p, c_char_p], errcheck=False)
|
||||
set_ds_metadata = void_output(std_call('GDALSetMetadata'), [c_void_p, POINTER(c_char_p), c_char_p])
|
||||
get_ds_metadata_domain_list = chararray_output(std_call('GDALGetMetadataDomainList'), [c_void_p], errcheck=False)
|
||||
get_ds_metadata_item = const_string_output(std_call('GDALGetMetadataItem'), [c_void_p, c_char_p, c_char_p])
|
||||
set_ds_metadata_item = const_string_output(std_call('GDALSetMetadataItem'), [c_void_p, c_char_p, c_char_p, c_char_p])
|
||||
free_dsl = void_output(std_call('CSLDestroy'), [POINTER(c_char_p)], errcheck=False)
|
||||
|
||||
if GDAL_VERSION >= (2, 1):
|
||||
get_ds_info = const_string_output(std_call('GDALInfo'), [c_void_p, c_void_p])
|
||||
else:
|
||||
get_ds_info = None
|
||||
|
||||
# Raster Band Routines
|
||||
band_io = void_output(
|
||||
std_call('GDALRasterIO'),
|
||||
[c_void_p, c_int, c_int, c_int, c_int, c_int, c_void_p, c_int, c_int, c_int, c_int, c_int]
|
||||
)
|
||||
get_band_xsize = int_output(std_call('GDALGetRasterBandXSize'), [c_void_p])
|
||||
get_band_ysize = int_output(std_call('GDALGetRasterBandYSize'), [c_void_p])
|
||||
get_band_index = int_output(std_call('GDALGetBandNumber'), [c_void_p])
|
||||
get_band_description = const_string_output(std_call('GDALGetDescription'), [c_void_p])
|
||||
get_band_ds = voidptr_output(std_call('GDALGetBandDataset'), [c_void_p])
|
||||
get_band_datatype = int_output(std_call('GDALGetRasterDataType'), [c_void_p])
|
||||
get_band_color_interp = int_output(std_call('GDALGetRasterColorInterpretation'), [c_void_p])
|
||||
get_band_nodata_value = double_output(std_call('GDALGetRasterNoDataValue'), [c_void_p, POINTER(c_int)])
|
||||
set_band_nodata_value = void_output(std_call('GDALSetRasterNoDataValue'), [c_void_p, c_double])
|
||||
if GDAL_VERSION >= (2, 1):
|
||||
delete_band_nodata_value = void_output(std_call('GDALDeleteRasterNoDataValue'), [c_void_p])
|
||||
else:
|
||||
delete_band_nodata_value = None
|
||||
get_band_statistics = void_output(
|
||||
std_call('GDALGetRasterStatistics'),
|
||||
[
|
||||
c_void_p, c_int, c_int, POINTER(c_double), POINTER(c_double),
|
||||
POINTER(c_double), POINTER(c_double), c_void_p, c_void_p,
|
||||
],
|
||||
)
|
||||
compute_band_statistics = void_output(
|
||||
std_call('GDALComputeRasterStatistics'),
|
||||
[c_void_p, c_int, POINTER(c_double), POINTER(c_double), POINTER(c_double), POINTER(c_double), c_void_p, c_void_p],
|
||||
)
|
||||
|
||||
# Reprojection routine
|
||||
reproject_image = void_output(
|
||||
std_call('GDALReprojectImage'),
|
||||
[c_void_p, c_char_p, c_void_p, c_char_p, c_int, c_double, c_double, c_void_p, c_void_p, c_void_p]
|
||||
)
|
||||
auto_create_warped_vrt = voidptr_output(
|
||||
std_call('GDALAutoCreateWarpedVRT'),
|
||||
[c_void_p, c_char_p, c_char_p, c_int, c_double, c_void_p]
|
||||
)
|
||||
|
||||
# Create VSI gdal raster files from in-memory buffers.
|
||||
# https://gdal.org/cpl__vsi_8h.html
|
||||
create_vsi_file_from_mem_buffer = voidptr_output(std_call('VSIFileFromMemBuffer'), [c_char_p, c_void_p, c_int, c_int])
|
||||
get_mem_buffer_from_vsi_file = voidptr_output(std_call('VSIGetMemFileBuffer'), [c_char_p, POINTER(c_int), c_bool])
|
||||
unlink_vsi_file = int_output(std_call('VSIUnlink'), [c_char_p])
|
||||
@@ -0,0 +1,80 @@
|
||||
from ctypes import POINTER, c_char_p, c_int, c_void_p
|
||||
|
||||
from django.contrib.gis.gdal.libgdal import lgdal, std_call
|
||||
from django.contrib.gis.gdal.prototypes.generation import (
|
||||
const_string_output, double_output, int_output, srs_output, string_output,
|
||||
void_output,
|
||||
)
|
||||
|
||||
|
||||
# Shortcut generation for routines with known parameters.
|
||||
def srs_double(f):
|
||||
"""
|
||||
Create a function prototype for the OSR routines that take
|
||||
the OSRSpatialReference object and return a double value.
|
||||
"""
|
||||
return double_output(f, [c_void_p, POINTER(c_int)], errcheck=True)
|
||||
|
||||
|
||||
def units_func(f):
|
||||
"""
|
||||
Create a ctypes function prototype for OSR units functions, e.g.,
|
||||
OSRGetAngularUnits, OSRGetLinearUnits.
|
||||
"""
|
||||
return double_output(f, [c_void_p, POINTER(c_char_p)], strarg=True)
|
||||
|
||||
|
||||
# Creation & destruction.
|
||||
clone_srs = srs_output(std_call('OSRClone'), [c_void_p])
|
||||
new_srs = srs_output(std_call('OSRNewSpatialReference'), [c_char_p])
|
||||
release_srs = void_output(lgdal.OSRRelease, [c_void_p], errcheck=False)
|
||||
destroy_srs = void_output(std_call('OSRDestroySpatialReference'), [c_void_p], errcheck=False)
|
||||
srs_validate = void_output(lgdal.OSRValidate, [c_void_p])
|
||||
|
||||
# Getting the semi_major, semi_minor, and flattening functions.
|
||||
semi_major = srs_double(lgdal.OSRGetSemiMajor)
|
||||
semi_minor = srs_double(lgdal.OSRGetSemiMinor)
|
||||
invflattening = srs_double(lgdal.OSRGetInvFlattening)
|
||||
|
||||
# WKT, PROJ, EPSG, XML importation routines.
|
||||
from_wkt = void_output(lgdal.OSRImportFromWkt, [c_void_p, POINTER(c_char_p)])
|
||||
from_proj = void_output(lgdal.OSRImportFromProj4, [c_void_p, c_char_p])
|
||||
from_epsg = void_output(std_call('OSRImportFromEPSG'), [c_void_p, c_int])
|
||||
from_xml = void_output(lgdal.OSRImportFromXML, [c_void_p, c_char_p])
|
||||
from_user_input = void_output(std_call('OSRSetFromUserInput'), [c_void_p, c_char_p])
|
||||
|
||||
# Morphing to/from ESRI WKT.
|
||||
morph_to_esri = void_output(lgdal.OSRMorphToESRI, [c_void_p])
|
||||
morph_from_esri = void_output(lgdal.OSRMorphFromESRI, [c_void_p])
|
||||
|
||||
# Identifying the EPSG
|
||||
identify_epsg = void_output(lgdal.OSRAutoIdentifyEPSG, [c_void_p])
|
||||
|
||||
# Getting the angular_units, linear_units functions
|
||||
linear_units = units_func(lgdal.OSRGetLinearUnits)
|
||||
angular_units = units_func(lgdal.OSRGetAngularUnits)
|
||||
|
||||
# For exporting to WKT, PROJ.4, "Pretty" WKT, and XML.
|
||||
to_wkt = string_output(std_call('OSRExportToWkt'), [c_void_p, POINTER(c_char_p)], decoding='utf-8')
|
||||
to_proj = string_output(std_call('OSRExportToProj4'), [c_void_p, POINTER(c_char_p)], decoding='ascii')
|
||||
to_pretty_wkt = string_output(
|
||||
std_call('OSRExportToPrettyWkt'),
|
||||
[c_void_p, POINTER(c_char_p), c_int], offset=-2, decoding='utf-8'
|
||||
)
|
||||
|
||||
# Memory leak fixed in GDAL 1.5; still exists in 1.4.
|
||||
to_xml = string_output(lgdal.OSRExportToXML, [c_void_p, POINTER(c_char_p), c_char_p], offset=-2, decoding='utf-8')
|
||||
|
||||
# String attribute retrival routines.
|
||||
get_attr_value = const_string_output(std_call('OSRGetAttrValue'), [c_void_p, c_char_p, c_int], decoding='utf-8')
|
||||
get_auth_name = const_string_output(lgdal.OSRGetAuthorityName, [c_void_p, c_char_p], decoding='ascii')
|
||||
get_auth_code = const_string_output(lgdal.OSRGetAuthorityCode, [c_void_p, c_char_p], decoding='ascii')
|
||||
|
||||
# SRS Properties
|
||||
isgeographic = int_output(lgdal.OSRIsGeographic, [c_void_p])
|
||||
islocal = int_output(lgdal.OSRIsLocal, [c_void_p])
|
||||
isprojected = int_output(lgdal.OSRIsProjected, [c_void_p])
|
||||
|
||||
# Coordinate transformation
|
||||
new_ct = srs_output(std_call('OCTNewCoordinateTransformation'), [c_void_p, c_void_p])
|
||||
destroy_ct = void_output(std_call('OCTDestroyCoordinateTransformation'), [c_void_p], errcheck=False)
|
||||
@@ -0,0 +1,252 @@
|
||||
from ctypes import byref, c_double, c_int, c_void_p
|
||||
|
||||
from django.contrib.gis.gdal.error import GDALException
|
||||
from django.contrib.gis.gdal.prototypes import raster as capi
|
||||
from django.contrib.gis.gdal.raster.base import GDALRasterBase
|
||||
from django.contrib.gis.shortcuts import numpy
|
||||
from django.utils.encoding import force_text
|
||||
|
||||
from .const import (
|
||||
GDAL_COLOR_TYPES, GDAL_INTEGER_TYPES, GDAL_PIXEL_TYPES, GDAL_TO_CTYPES,
|
||||
)
|
||||
|
||||
|
||||
class GDALBand(GDALRasterBase):
|
||||
"""
|
||||
Wrap a GDAL raster band, needs to be obtained from a GDALRaster object.
|
||||
"""
|
||||
def __init__(self, source, index):
|
||||
self.source = source
|
||||
self._ptr = capi.get_ds_raster_band(source._ptr, index)
|
||||
|
||||
def _flush(self):
|
||||
"""
|
||||
Call the flush method on the Band's parent raster and force a refresh
|
||||
of the statistics attribute when requested the next time.
|
||||
"""
|
||||
self.source._flush()
|
||||
self._stats_refresh = True
|
||||
|
||||
@property
|
||||
def description(self):
|
||||
"""
|
||||
Return the description string of the band.
|
||||
"""
|
||||
return force_text(capi.get_band_description(self._ptr))
|
||||
|
||||
@property
|
||||
def width(self):
|
||||
"""
|
||||
Width (X axis) in pixels of the band.
|
||||
"""
|
||||
return capi.get_band_xsize(self._ptr)
|
||||
|
||||
@property
|
||||
def height(self):
|
||||
"""
|
||||
Height (Y axis) in pixels of the band.
|
||||
"""
|
||||
return capi.get_band_ysize(self._ptr)
|
||||
|
||||
@property
|
||||
def pixel_count(self):
|
||||
"""
|
||||
Return the total number of pixels in this band.
|
||||
"""
|
||||
return self.width * self.height
|
||||
|
||||
_stats_refresh = False
|
||||
|
||||
def statistics(self, refresh=False, approximate=False):
|
||||
"""
|
||||
Compute statistics on the pixel values of this band.
|
||||
|
||||
The return value is a tuple with the following structure:
|
||||
(minimum, maximum, mean, standard deviation).
|
||||
|
||||
If approximate=True, the statistics may be computed based on overviews
|
||||
or a subset of image tiles.
|
||||
|
||||
If refresh=True, the statistics will be computed from the data directly,
|
||||
and the cache will be updated where applicable.
|
||||
|
||||
For empty bands (where all pixel values are nodata), all statistics
|
||||
values are returned as None.
|
||||
|
||||
For raster formats using Persistent Auxiliary Metadata (PAM) services,
|
||||
the statistics might be cached in an auxiliary file.
|
||||
"""
|
||||
# Prepare array with arguments for capi function
|
||||
smin, smax, smean, sstd = c_double(), c_double(), c_double(), c_double()
|
||||
stats_args = [
|
||||
self._ptr, c_int(approximate), byref(smin), byref(smax),
|
||||
byref(smean), byref(sstd), c_void_p(), c_void_p(),
|
||||
]
|
||||
|
||||
if refresh or self._stats_refresh:
|
||||
func = capi.compute_band_statistics
|
||||
else:
|
||||
# Add additional argument to force computation if there is no
|
||||
# existing PAM file to take the values from.
|
||||
force = True
|
||||
stats_args.insert(2, c_int(force))
|
||||
func = capi.get_band_statistics
|
||||
|
||||
# Computation of statistics fails for empty bands.
|
||||
try:
|
||||
func(*stats_args)
|
||||
result = smin.value, smax.value, smean.value, sstd.value
|
||||
except GDALException:
|
||||
result = (None, None, None, None)
|
||||
|
||||
self._stats_refresh = False
|
||||
|
||||
return result
|
||||
|
||||
@property
|
||||
def min(self):
|
||||
"""
|
||||
Return the minimum pixel value for this band.
|
||||
"""
|
||||
return self.statistics()[0]
|
||||
|
||||
@property
|
||||
def max(self):
|
||||
"""
|
||||
Return the maximum pixel value for this band.
|
||||
"""
|
||||
return self.statistics()[1]
|
||||
|
||||
@property
|
||||
def mean(self):
|
||||
"""
|
||||
Return the mean of all pixel values of this band.
|
||||
"""
|
||||
return self.statistics()[2]
|
||||
|
||||
@property
|
||||
def std(self):
|
||||
"""
|
||||
Return the standard deviation of all pixel values of this band.
|
||||
"""
|
||||
return self.statistics()[3]
|
||||
|
||||
@property
|
||||
def nodata_value(self):
|
||||
"""
|
||||
Return the nodata value for this band, or None if it isn't set.
|
||||
"""
|
||||
# Get value and nodata exists flag
|
||||
nodata_exists = c_int()
|
||||
value = capi.get_band_nodata_value(self._ptr, nodata_exists)
|
||||
if not nodata_exists:
|
||||
value = None
|
||||
# If the pixeltype is an integer, convert to int
|
||||
elif self.datatype() in GDAL_INTEGER_TYPES:
|
||||
value = int(value)
|
||||
return value
|
||||
|
||||
@nodata_value.setter
|
||||
def nodata_value(self, value):
|
||||
"""
|
||||
Set the nodata value for this band.
|
||||
"""
|
||||
if value is None:
|
||||
if not capi.delete_band_nodata_value:
|
||||
raise ValueError('GDAL >= 2.1 required to delete nodata values.')
|
||||
capi.delete_band_nodata_value(self._ptr)
|
||||
elif not isinstance(value, (int, float)):
|
||||
raise ValueError('Nodata value must be numeric or None.')
|
||||
else:
|
||||
capi.set_band_nodata_value(self._ptr, value)
|
||||
self._flush()
|
||||
|
||||
def datatype(self, as_string=False):
|
||||
"""
|
||||
Return the GDAL Pixel Datatype for this band.
|
||||
"""
|
||||
dtype = capi.get_band_datatype(self._ptr)
|
||||
if as_string:
|
||||
dtype = GDAL_PIXEL_TYPES[dtype]
|
||||
return dtype
|
||||
|
||||
def color_interp(self, as_string=False):
|
||||
"""Return the GDAL color interpretation for this band."""
|
||||
color = capi.get_band_color_interp(self._ptr)
|
||||
if as_string:
|
||||
color = GDAL_COLOR_TYPES[color]
|
||||
return color
|
||||
|
||||
def data(self, data=None, offset=None, size=None, shape=None, as_memoryview=False):
|
||||
"""
|
||||
Read or writes pixel values for this band. Blocks of data can
|
||||
be accessed by specifying the width, height and offset of the
|
||||
desired block. The same specification can be used to update
|
||||
parts of a raster by providing an array of values.
|
||||
|
||||
Allowed input data types are bytes, memoryview, list, tuple, and array.
|
||||
"""
|
||||
offset = offset or (0, 0)
|
||||
size = size or (self.width - offset[0], self.height - offset[1])
|
||||
shape = shape or size
|
||||
if any(x <= 0 for x in size):
|
||||
raise ValueError('Offset too big for this raster.')
|
||||
|
||||
if size[0] > self.width or size[1] > self.height:
|
||||
raise ValueError('Size is larger than raster.')
|
||||
|
||||
# Create ctypes type array generator
|
||||
ctypes_array = GDAL_TO_CTYPES[self.datatype()] * (shape[0] * shape[1])
|
||||
|
||||
if data is None:
|
||||
# Set read mode
|
||||
access_flag = 0
|
||||
# Prepare empty ctypes array
|
||||
data_array = ctypes_array()
|
||||
else:
|
||||
# Set write mode
|
||||
access_flag = 1
|
||||
|
||||
# Instantiate ctypes array holding the input data
|
||||
if isinstance(data, (bytes, memoryview)) or (numpy and isinstance(data, numpy.ndarray)):
|
||||
data_array = ctypes_array.from_buffer_copy(data)
|
||||
else:
|
||||
data_array = ctypes_array(*data)
|
||||
|
||||
# Access band
|
||||
capi.band_io(self._ptr, access_flag, offset[0], offset[1],
|
||||
size[0], size[1], byref(data_array), shape[0],
|
||||
shape[1], self.datatype(), 0, 0)
|
||||
|
||||
# Return data as numpy array if possible, otherwise as list
|
||||
if data is None:
|
||||
if as_memoryview:
|
||||
return memoryview(data_array)
|
||||
elif numpy:
|
||||
# reshape() needs a reshape parameter with the height first.
|
||||
return numpy.frombuffer(
|
||||
data_array, dtype=numpy.dtype(data_array)
|
||||
).reshape(tuple(reversed(size)))
|
||||
else:
|
||||
return list(data_array)
|
||||
else:
|
||||
self._flush()
|
||||
|
||||
|
||||
class BandList(list):
|
||||
def __init__(self, source):
|
||||
self.source = source
|
||||
super().__init__()
|
||||
|
||||
def __iter__(self):
|
||||
for idx in range(1, len(self) + 1):
|
||||
yield GDALBand(self.source, idx)
|
||||
|
||||
def __len__(self):
|
||||
return capi.get_ds_raster_count(self.source._ptr)
|
||||
|
||||
def __getitem__(self, index):
|
||||
try:
|
||||
return GDALBand(self.source, index + 1)
|
||||
except GDALException:
|
||||
raise GDALException('Unable to get band index %d' % index)
|
||||
@@ -0,0 +1,75 @@
|
||||
from django.contrib.gis.gdal.base import GDALBase
|
||||
from django.contrib.gis.gdal.prototypes import raster as capi
|
||||
|
||||
|
||||
class GDALRasterBase(GDALBase):
|
||||
"""
|
||||
Attributes that exist on both GDALRaster and GDALBand.
|
||||
"""
|
||||
@property
|
||||
def metadata(self):
|
||||
"""
|
||||
Return the metadata for this raster or band. The return value is a
|
||||
nested dictionary, where the first-level key is the metadata domain and
|
||||
the second-level is the metadata item names and values for that domain.
|
||||
"""
|
||||
# The initial metadata domain list contains the default domain.
|
||||
# The default is returned if domain name is None.
|
||||
domain_list = ['DEFAULT']
|
||||
|
||||
# Get additional metadata domains from the raster.
|
||||
meta_list = capi.get_ds_metadata_domain_list(self._ptr)
|
||||
if meta_list:
|
||||
# The number of domains is unknown, so retrieve data until there
|
||||
# are no more values in the ctypes array.
|
||||
counter = 0
|
||||
domain = meta_list[counter]
|
||||
while domain:
|
||||
domain_list.append(domain.decode())
|
||||
counter += 1
|
||||
domain = meta_list[counter]
|
||||
|
||||
# Free domain list array.
|
||||
capi.free_dsl(meta_list)
|
||||
|
||||
# Retrieve metadata values for each domain.
|
||||
result = {}
|
||||
for domain in domain_list:
|
||||
# Get metadata for this domain.
|
||||
data = capi.get_ds_metadata(
|
||||
self._ptr,
|
||||
(None if domain == 'DEFAULT' else domain.encode()),
|
||||
)
|
||||
if not data:
|
||||
continue
|
||||
# The number of metadata items is unknown, so retrieve data until
|
||||
# there are no more values in the ctypes array.
|
||||
domain_meta = {}
|
||||
counter = 0
|
||||
item = data[counter]
|
||||
while item:
|
||||
key, val = item.decode().split('=')
|
||||
domain_meta[key] = val
|
||||
counter += 1
|
||||
item = data[counter]
|
||||
# The default domain values are returned if domain is None.
|
||||
result[domain or 'DEFAULT'] = domain_meta
|
||||
return result
|
||||
|
||||
@metadata.setter
|
||||
def metadata(self, value):
|
||||
"""
|
||||
Set the metadata. Update only the domains that are contained in the
|
||||
value dictionary.
|
||||
"""
|
||||
# Loop through domains.
|
||||
for domain, metadata in value.items():
|
||||
# Set the domain to None for the default, otherwise encode.
|
||||
domain = None if domain == 'DEFAULT' else domain.encode()
|
||||
# Set each metadata entry separately.
|
||||
for meta_name, meta_value in metadata.items():
|
||||
capi.set_ds_metadata_item(
|
||||
self._ptr, meta_name.encode(),
|
||||
meta_value.encode() if meta_value else None,
|
||||
domain,
|
||||
)
|
||||
@@ -0,0 +1,467 @@
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import uuid
|
||||
from ctypes import (
|
||||
addressof, byref, c_buffer, c_char_p, c_double, c_int, c_void_p, string_at,
|
||||
)
|
||||
|
||||
from django.contrib.gis.gdal.driver import Driver
|
||||
from django.contrib.gis.gdal.error import GDALException
|
||||
from django.contrib.gis.gdal.prototypes import raster as capi
|
||||
from django.contrib.gis.gdal.raster.band import BandList
|
||||
from django.contrib.gis.gdal.raster.base import GDALRasterBase
|
||||
from django.contrib.gis.gdal.raster.const import (
|
||||
GDAL_RESAMPLE_ALGORITHMS, VSI_DELETE_BUFFER_ON_READ,
|
||||
VSI_FILESYSTEM_BASE_PATH, VSI_TAKE_BUFFER_OWNERSHIP,
|
||||
)
|
||||
from django.contrib.gis.gdal.srs import SpatialReference, SRSException
|
||||
from django.contrib.gis.geometry import json_regex
|
||||
from django.utils.encoding import force_bytes, force_text
|
||||
from django.utils.functional import cached_property
|
||||
|
||||
|
||||
class TransformPoint(list):
|
||||
indices = {
|
||||
'origin': (0, 3),
|
||||
'scale': (1, 5),
|
||||
'skew': (2, 4),
|
||||
}
|
||||
|
||||
def __init__(self, raster, prop):
|
||||
x = raster.geotransform[self.indices[prop][0]]
|
||||
y = raster.geotransform[self.indices[prop][1]]
|
||||
super().__init__([x, y])
|
||||
self._raster = raster
|
||||
self._prop = prop
|
||||
|
||||
@property
|
||||
def x(self):
|
||||
return self[0]
|
||||
|
||||
@x.setter
|
||||
def x(self, value):
|
||||
gtf = self._raster.geotransform
|
||||
gtf[self.indices[self._prop][0]] = value
|
||||
self._raster.geotransform = gtf
|
||||
|
||||
@property
|
||||
def y(self):
|
||||
return self[1]
|
||||
|
||||
@y.setter
|
||||
def y(self, value):
|
||||
gtf = self._raster.geotransform
|
||||
gtf[self.indices[self._prop][1]] = value
|
||||
self._raster.geotransform = gtf
|
||||
|
||||
|
||||
class GDALRaster(GDALRasterBase):
|
||||
"""
|
||||
Wrap a raster GDAL Data Source object.
|
||||
"""
|
||||
destructor = capi.close_ds
|
||||
|
||||
def __init__(self, ds_input, write=False):
|
||||
self._write = 1 if write else 0
|
||||
Driver.ensure_registered()
|
||||
|
||||
# Preprocess json inputs. This converts json strings to dictionaries,
|
||||
# which are parsed below the same way as direct dictionary inputs.
|
||||
if isinstance(ds_input, str) and json_regex.match(ds_input):
|
||||
ds_input = json.loads(ds_input)
|
||||
|
||||
# If input is a valid file path, try setting file as source.
|
||||
if isinstance(ds_input, str):
|
||||
try:
|
||||
# GDALOpen will auto-detect the data source type.
|
||||
self._ptr = capi.open_ds(force_bytes(ds_input), self._write)
|
||||
except GDALException as err:
|
||||
raise GDALException('Could not open the datasource at "{}" ({}).'.format(ds_input, err))
|
||||
elif isinstance(ds_input, bytes):
|
||||
# Create a new raster in write mode.
|
||||
self._write = 1
|
||||
# Get size of buffer.
|
||||
size = sys.getsizeof(ds_input)
|
||||
# Pass data to ctypes, keeping a reference to the ctypes object so
|
||||
# that the vsimem file remains available until the GDALRaster is
|
||||
# deleted.
|
||||
self._ds_input = c_buffer(ds_input)
|
||||
# Create random name to reference in vsimem filesystem.
|
||||
vsi_path = os.path.join(VSI_FILESYSTEM_BASE_PATH, str(uuid.uuid4()))
|
||||
# Create vsimem file from buffer.
|
||||
capi.create_vsi_file_from_mem_buffer(
|
||||
force_bytes(vsi_path),
|
||||
byref(self._ds_input),
|
||||
size,
|
||||
VSI_TAKE_BUFFER_OWNERSHIP,
|
||||
)
|
||||
# Open the new vsimem file as a GDALRaster.
|
||||
try:
|
||||
self._ptr = capi.open_ds(force_bytes(vsi_path), self._write)
|
||||
except GDALException:
|
||||
# Remove the broken file from the VSI filesystem.
|
||||
capi.unlink_vsi_file(force_bytes(vsi_path))
|
||||
raise GDALException('Failed creating VSI raster from the input buffer.')
|
||||
elif isinstance(ds_input, dict):
|
||||
# A new raster needs to be created in write mode
|
||||
self._write = 1
|
||||
|
||||
# Create driver (in memory by default)
|
||||
driver = Driver(ds_input.get('driver', 'MEM'))
|
||||
|
||||
# For out of memory drivers, check filename argument
|
||||
if driver.name != 'MEM' and 'name' not in ds_input:
|
||||
raise GDALException('Specify name for creation of raster with driver "{}".'.format(driver.name))
|
||||
|
||||
# Check if width and height where specified
|
||||
if 'width' not in ds_input or 'height' not in ds_input:
|
||||
raise GDALException('Specify width and height attributes for JSON or dict input.')
|
||||
|
||||
# Check if srid was specified
|
||||
if 'srid' not in ds_input:
|
||||
raise GDALException('Specify srid for JSON or dict input.')
|
||||
|
||||
# Create null terminated gdal options array.
|
||||
papsz_options = []
|
||||
for key, val in ds_input.get('papsz_options', {}).items():
|
||||
option = '{}={}'.format(key, val)
|
||||
papsz_options.append(option.upper().encode())
|
||||
papsz_options.append(None)
|
||||
|
||||
# Convert papszlist to ctypes array.
|
||||
papsz_options = (c_char_p * len(papsz_options))(*papsz_options)
|
||||
|
||||
# Create GDAL Raster
|
||||
self._ptr = capi.create_ds(
|
||||
driver._ptr,
|
||||
force_bytes(ds_input.get('name', '')),
|
||||
ds_input['width'],
|
||||
ds_input['height'],
|
||||
ds_input.get('nr_of_bands', len(ds_input.get('bands', []))),
|
||||
ds_input.get('datatype', 6),
|
||||
byref(papsz_options),
|
||||
)
|
||||
|
||||
# Set band data if provided
|
||||
for i, band_input in enumerate(ds_input.get('bands', [])):
|
||||
band = self.bands[i]
|
||||
if 'nodata_value' in band_input:
|
||||
band.nodata_value = band_input['nodata_value']
|
||||
# Instantiate band filled with nodata values if only
|
||||
# partial input data has been provided.
|
||||
if band.nodata_value is not None and (
|
||||
'data' not in band_input or
|
||||
'size' in band_input or
|
||||
'shape' in band_input):
|
||||
band.data(data=(band.nodata_value,), shape=(1, 1))
|
||||
# Set band data values from input.
|
||||
band.data(
|
||||
data=band_input.get('data'),
|
||||
size=band_input.get('size'),
|
||||
shape=band_input.get('shape'),
|
||||
offset=band_input.get('offset'),
|
||||
)
|
||||
|
||||
# Set SRID
|
||||
self.srs = ds_input.get('srid')
|
||||
|
||||
# Set additional properties if provided
|
||||
if 'origin' in ds_input:
|
||||
self.origin.x, self.origin.y = ds_input['origin']
|
||||
|
||||
if 'scale' in ds_input:
|
||||
self.scale.x, self.scale.y = ds_input['scale']
|
||||
|
||||
if 'skew' in ds_input:
|
||||
self.skew.x, self.skew.y = ds_input['skew']
|
||||
elif isinstance(ds_input, c_void_p):
|
||||
# Instantiate the object using an existing pointer to a gdal raster.
|
||||
self._ptr = ds_input
|
||||
else:
|
||||
raise GDALException('Invalid data source input type: "{}".'.format(type(ds_input)))
|
||||
|
||||
def __del__(self):
|
||||
if self.is_vsi_based:
|
||||
# Remove the temporary file from the VSI in-memory filesystem.
|
||||
capi.unlink_vsi_file(force_bytes(self.name))
|
||||
super().__del__()
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def __repr__(self):
|
||||
"""
|
||||
Short-hand representation because WKB may be very large.
|
||||
"""
|
||||
return '<Raster object at %s>' % hex(addressof(self._ptr))
|
||||
|
||||
def _flush(self):
|
||||
"""
|
||||
Flush all data from memory into the source file if it exists.
|
||||
The data that needs flushing are geotransforms, coordinate systems,
|
||||
nodata_values and pixel values. This function will be called
|
||||
automatically wherever it is needed.
|
||||
"""
|
||||
# Raise an Exception if the value is being changed in read mode.
|
||||
if not self._write:
|
||||
raise GDALException('Raster needs to be opened in write mode to change values.')
|
||||
capi.flush_ds(self._ptr)
|
||||
|
||||
@property
|
||||
def vsi_buffer(self):
|
||||
if not self.is_vsi_based:
|
||||
return None
|
||||
# Prepare an integer that will contain the buffer length.
|
||||
out_length = c_int()
|
||||
# Get the data using the vsi file name.
|
||||
dat = capi.get_mem_buffer_from_vsi_file(
|
||||
force_bytes(self.name),
|
||||
byref(out_length),
|
||||
VSI_DELETE_BUFFER_ON_READ,
|
||||
)
|
||||
# Read the full buffer pointer.
|
||||
return string_at(dat, out_length.value)
|
||||
|
||||
@cached_property
|
||||
def is_vsi_based(self):
|
||||
return self.name.startswith(VSI_FILESYSTEM_BASE_PATH)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""
|
||||
Return the name of this raster. Corresponds to filename
|
||||
for file-based rasters.
|
||||
"""
|
||||
return force_text(capi.get_ds_description(self._ptr))
|
||||
|
||||
@cached_property
|
||||
def driver(self):
|
||||
"""
|
||||
Return the GDAL Driver used for this raster.
|
||||
"""
|
||||
ds_driver = capi.get_ds_driver(self._ptr)
|
||||
return Driver(ds_driver)
|
||||
|
||||
@property
|
||||
def width(self):
|
||||
"""
|
||||
Width (X axis) in pixels.
|
||||
"""
|
||||
return capi.get_ds_xsize(self._ptr)
|
||||
|
||||
@property
|
||||
def height(self):
|
||||
"""
|
||||
Height (Y axis) in pixels.
|
||||
"""
|
||||
return capi.get_ds_ysize(self._ptr)
|
||||
|
||||
@property
|
||||
def srs(self):
|
||||
"""
|
||||
Return the SpatialReference used in this GDALRaster.
|
||||
"""
|
||||
try:
|
||||
wkt = capi.get_ds_projection_ref(self._ptr)
|
||||
if not wkt:
|
||||
return None
|
||||
return SpatialReference(wkt, srs_type='wkt')
|
||||
except SRSException:
|
||||
return None
|
||||
|
||||
@srs.setter
|
||||
def srs(self, value):
|
||||
"""
|
||||
Set the spatial reference used in this GDALRaster. The input can be
|
||||
a SpatialReference or any parameter accepted by the SpatialReference
|
||||
constructor.
|
||||
"""
|
||||
if isinstance(value, SpatialReference):
|
||||
srs = value
|
||||
elif isinstance(value, (int, str)):
|
||||
srs = SpatialReference(value)
|
||||
else:
|
||||
raise ValueError('Could not create a SpatialReference from input.')
|
||||
capi.set_ds_projection_ref(self._ptr, srs.wkt.encode())
|
||||
self._flush()
|
||||
|
||||
@property
|
||||
def srid(self):
|
||||
"""
|
||||
Shortcut to access the srid of this GDALRaster.
|
||||
"""
|
||||
return self.srs.srid
|
||||
|
||||
@srid.setter
|
||||
def srid(self, value):
|
||||
"""
|
||||
Shortcut to set this GDALRaster's srs from an srid.
|
||||
"""
|
||||
self.srs = value
|
||||
|
||||
@property
|
||||
def geotransform(self):
|
||||
"""
|
||||
Return the geotransform of the data source.
|
||||
Return the default geotransform if it does not exist or has not been
|
||||
set previously. The default is [0.0, 1.0, 0.0, 0.0, 0.0, -1.0].
|
||||
"""
|
||||
# Create empty ctypes double array for data
|
||||
gtf = (c_double * 6)()
|
||||
capi.get_ds_geotransform(self._ptr, byref(gtf))
|
||||
return list(gtf)
|
||||
|
||||
@geotransform.setter
|
||||
def geotransform(self, values):
|
||||
"Set the geotransform for the data source."
|
||||
if len(values) != 6 or not all(isinstance(x, (int, float)) for x in values):
|
||||
raise ValueError('Geotransform must consist of 6 numeric values.')
|
||||
# Create ctypes double array with input and write data
|
||||
values = (c_double * 6)(*values)
|
||||
capi.set_ds_geotransform(self._ptr, byref(values))
|
||||
self._flush()
|
||||
|
||||
@property
|
||||
def origin(self):
|
||||
"""
|
||||
Coordinates of the raster origin.
|
||||
"""
|
||||
return TransformPoint(self, 'origin')
|
||||
|
||||
@property
|
||||
def scale(self):
|
||||
"""
|
||||
Pixel scale in units of the raster projection.
|
||||
"""
|
||||
return TransformPoint(self, 'scale')
|
||||
|
||||
@property
|
||||
def skew(self):
|
||||
"""
|
||||
Skew of pixels (rotation parameters).
|
||||
"""
|
||||
return TransformPoint(self, 'skew')
|
||||
|
||||
@property
|
||||
def extent(self):
|
||||
"""
|
||||
Return the extent as a 4-tuple (xmin, ymin, xmax, ymax).
|
||||
"""
|
||||
# Calculate boundary values based on scale and size
|
||||
xval = self.origin.x + self.scale.x * self.width
|
||||
yval = self.origin.y + self.scale.y * self.height
|
||||
# Calculate min and max values
|
||||
xmin = min(xval, self.origin.x)
|
||||
xmax = max(xval, self.origin.x)
|
||||
ymin = min(yval, self.origin.y)
|
||||
ymax = max(yval, self.origin.y)
|
||||
|
||||
return xmin, ymin, xmax, ymax
|
||||
|
||||
@property
|
||||
def bands(self):
|
||||
return BandList(self)
|
||||
|
||||
def warp(self, ds_input, resampling='NearestNeighbour', max_error=0.0):
|
||||
"""
|
||||
Return a warped GDALRaster with the given input characteristics.
|
||||
|
||||
The input is expected to be a dictionary containing the parameters
|
||||
of the target raster. Allowed values are width, height, SRID, origin,
|
||||
scale, skew, datatype, driver, and name (filename).
|
||||
|
||||
By default, the warp functions keeps all parameters equal to the values
|
||||
of the original source raster. For the name of the target raster, the
|
||||
name of the source raster will be used and appended with
|
||||
_copy. + source_driver_name.
|
||||
|
||||
In addition, the resampling algorithm can be specified with the "resampling"
|
||||
input parameter. The default is NearestNeighbor. For a list of all options
|
||||
consult the GDAL_RESAMPLE_ALGORITHMS constant.
|
||||
"""
|
||||
# Get the parameters defining the geotransform, srid, and size of the raster
|
||||
ds_input.setdefault('width', self.width)
|
||||
ds_input.setdefault('height', self.height)
|
||||
ds_input.setdefault('srid', self.srs.srid)
|
||||
ds_input.setdefault('origin', self.origin)
|
||||
ds_input.setdefault('scale', self.scale)
|
||||
ds_input.setdefault('skew', self.skew)
|
||||
# Get the driver, name, and datatype of the target raster
|
||||
ds_input.setdefault('driver', self.driver.name)
|
||||
|
||||
if 'name' not in ds_input:
|
||||
ds_input['name'] = self.name + '_copy.' + self.driver.name
|
||||
|
||||
if 'datatype' not in ds_input:
|
||||
ds_input['datatype'] = self.bands[0].datatype()
|
||||
|
||||
# Instantiate raster bands filled with nodata values.
|
||||
ds_input['bands'] = [{'nodata_value': bnd.nodata_value} for bnd in self.bands]
|
||||
|
||||
# Create target raster
|
||||
target = GDALRaster(ds_input, write=True)
|
||||
|
||||
# Select resampling algorithm
|
||||
algorithm = GDAL_RESAMPLE_ALGORITHMS[resampling]
|
||||
|
||||
# Reproject image
|
||||
capi.reproject_image(
|
||||
self._ptr, self.srs.wkt.encode(),
|
||||
target._ptr, target.srs.wkt.encode(),
|
||||
algorithm, 0.0, max_error,
|
||||
c_void_p(), c_void_p(), c_void_p()
|
||||
)
|
||||
|
||||
# Make sure all data is written to file
|
||||
target._flush()
|
||||
|
||||
return target
|
||||
|
||||
def transform(self, srid, driver=None, name=None, resampling='NearestNeighbour',
|
||||
max_error=0.0):
|
||||
"""
|
||||
Return a copy of this raster reprojected into the given SRID.
|
||||
"""
|
||||
# Convert the resampling algorithm name into an algorithm id
|
||||
algorithm = GDAL_RESAMPLE_ALGORITHMS[resampling]
|
||||
|
||||
# Instantiate target spatial reference system
|
||||
target_srs = SpatialReference(srid)
|
||||
|
||||
# Create warped virtual dataset in the target reference system
|
||||
target = capi.auto_create_warped_vrt(
|
||||
self._ptr, self.srs.wkt.encode(), target_srs.wkt.encode(),
|
||||
algorithm, max_error, c_void_p()
|
||||
)
|
||||
target = GDALRaster(target)
|
||||
|
||||
# Construct the target warp dictionary from the virtual raster
|
||||
data = {
|
||||
'srid': srid,
|
||||
'width': target.width,
|
||||
'height': target.height,
|
||||
'origin': [target.origin.x, target.origin.y],
|
||||
'scale': [target.scale.x, target.scale.y],
|
||||
'skew': [target.skew.x, target.skew.y],
|
||||
}
|
||||
|
||||
# Set the driver and filepath if provided
|
||||
if driver:
|
||||
data['driver'] = driver
|
||||
|
||||
if name:
|
||||
data['name'] = name
|
||||
|
||||
# Warp the raster into new srid
|
||||
return self.warp(data, resampling=resampling, max_error=max_error)
|
||||
|
||||
@property
|
||||
def info(self):
|
||||
"""
|
||||
Return information about this raster in a string format equivalent
|
||||
to the output of the gdalinfo command line utility.
|
||||
"""
|
||||
if not capi.get_ds_info:
|
||||
raise ValueError('GDAL ≥ 2.1 is required for using the info property.')
|
||||
return capi.get_ds_info(self.ptr, None).decode()
|
||||
@@ -0,0 +1,335 @@
|
||||
"""
|
||||
The Spatial Reference class, represents OGR Spatial Reference objects.
|
||||
|
||||
Example:
|
||||
>>> from django.contrib.gis.gdal import SpatialReference
|
||||
>>> srs = SpatialReference('WGS84')
|
||||
>>> print(srs)
|
||||
GEOGCS["WGS 84",
|
||||
DATUM["WGS_1984",
|
||||
SPHEROID["WGS 84",6378137,298.257223563,
|
||||
AUTHORITY["EPSG","7030"]],
|
||||
TOWGS84[0,0,0,0,0,0,0],
|
||||
AUTHORITY["EPSG","6326"]],
|
||||
PRIMEM["Greenwich",0,
|
||||
AUTHORITY["EPSG","8901"]],
|
||||
UNIT["degree",0.01745329251994328,
|
||||
AUTHORITY["EPSG","9122"]],
|
||||
AUTHORITY["EPSG","4326"]]
|
||||
>>> print(srs.proj)
|
||||
+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs
|
||||
>>> print(srs.ellipsoid)
|
||||
(6378137.0, 6356752.3142451793, 298.25722356300003)
|
||||
>>> print(srs.projected, srs.geographic)
|
||||
False True
|
||||
>>> srs.import_epsg(32140)
|
||||
>>> print(srs.name)
|
||||
NAD83 / Texas South Central
|
||||
"""
|
||||
from ctypes import byref, c_char_p, c_int
|
||||
|
||||
from django.contrib.gis.gdal.base import GDALBase
|
||||
from django.contrib.gis.gdal.error import SRSException
|
||||
from django.contrib.gis.gdal.prototypes import srs as capi
|
||||
from django.utils.encoding import force_bytes, force_text
|
||||
|
||||
|
||||
class SpatialReference(GDALBase):
|
||||
"""
|
||||
A wrapper for the OGRSpatialReference object. According to the GDAL Web site,
|
||||
the SpatialReference object "provide[s] services to represent coordinate
|
||||
systems (projections and datums) and to transform between them."
|
||||
"""
|
||||
destructor = capi.release_srs
|
||||
|
||||
def __init__(self, srs_input='', srs_type='user'):
|
||||
"""
|
||||
Create a GDAL OSR Spatial Reference object from the given input.
|
||||
The input may be string of OGC Well Known Text (WKT), an integer
|
||||
EPSG code, a PROJ.4 string, and/or a projection "well known" shorthand
|
||||
string (one of 'WGS84', 'WGS72', 'NAD27', 'NAD83').
|
||||
"""
|
||||
|
||||
if srs_type == 'wkt':
|
||||
self.ptr = capi.new_srs(c_char_p(b''))
|
||||
self.import_wkt(srs_input)
|
||||
return
|
||||
elif isinstance(srs_input, str):
|
||||
try:
|
||||
# If SRID is a string, e.g., '4326', then make acceptable
|
||||
# as user input.
|
||||
srid = int(srs_input)
|
||||
srs_input = 'EPSG:%d' % srid
|
||||
except ValueError:
|
||||
pass
|
||||
elif isinstance(srs_input, int):
|
||||
# EPSG integer code was input.
|
||||
srs_type = 'epsg'
|
||||
elif isinstance(srs_input, self.ptr_type):
|
||||
srs = srs_input
|
||||
srs_type = 'ogr'
|
||||
else:
|
||||
raise TypeError('Invalid SRS type "%s"' % srs_type)
|
||||
|
||||
if srs_type == 'ogr':
|
||||
# Input is already an SRS pointer.
|
||||
srs = srs_input
|
||||
else:
|
||||
# Creating a new SRS pointer, using the string buffer.
|
||||
buf = c_char_p(b'')
|
||||
srs = capi.new_srs(buf)
|
||||
|
||||
# If the pointer is NULL, throw an exception.
|
||||
if not srs:
|
||||
raise SRSException('Could not create spatial reference from: %s' % srs_input)
|
||||
else:
|
||||
self.ptr = srs
|
||||
|
||||
# Importing from either the user input string or an integer SRID.
|
||||
if srs_type == 'user':
|
||||
self.import_user_input(srs_input)
|
||||
elif srs_type == 'epsg':
|
||||
self.import_epsg(srs_input)
|
||||
|
||||
def __getitem__(self, target):
|
||||
"""
|
||||
Return the value of the given string attribute node, None if the node
|
||||
doesn't exist. Can also take a tuple as a parameter, (target, child),
|
||||
where child is the index of the attribute in the WKT. For example:
|
||||
|
||||
>>> wkt = 'GEOGCS["WGS 84", DATUM["WGS_1984, ... AUTHORITY["EPSG","4326"]]'
|
||||
>>> srs = SpatialReference(wkt) # could also use 'WGS84', or 4326
|
||||
>>> print(srs['GEOGCS'])
|
||||
WGS 84
|
||||
>>> print(srs['DATUM'])
|
||||
WGS_1984
|
||||
>>> print(srs['AUTHORITY'])
|
||||
EPSG
|
||||
>>> print(srs['AUTHORITY', 1]) # The authority value
|
||||
4326
|
||||
>>> print(srs['TOWGS84', 4]) # the fourth value in this wkt
|
||||
0
|
||||
>>> print(srs['UNIT|AUTHORITY']) # For the units authority, have to use the pipe symbole.
|
||||
EPSG
|
||||
>>> print(srs['UNIT|AUTHORITY', 1]) # The authority value for the units
|
||||
9122
|
||||
"""
|
||||
if isinstance(target, tuple):
|
||||
return self.attr_value(*target)
|
||||
else:
|
||||
return self.attr_value(target)
|
||||
|
||||
def __str__(self):
|
||||
"Use 'pretty' WKT."
|
||||
return self.pretty_wkt
|
||||
|
||||
# #### SpatialReference Methods ####
|
||||
def attr_value(self, target, index=0):
|
||||
"""
|
||||
The attribute value for the given target node (e.g. 'PROJCS'). The index
|
||||
keyword specifies an index of the child node to return.
|
||||
"""
|
||||
if not isinstance(target, str) or not isinstance(index, int):
|
||||
raise TypeError
|
||||
return capi.get_attr_value(self.ptr, force_bytes(target), index)
|
||||
|
||||
def auth_name(self, target):
|
||||
"Return the authority name for the given string target node."
|
||||
return capi.get_auth_name(self.ptr, force_bytes(target))
|
||||
|
||||
def auth_code(self, target):
|
||||
"Return the authority code for the given string target node."
|
||||
return capi.get_auth_code(self.ptr, force_bytes(target))
|
||||
|
||||
def clone(self):
|
||||
"Return a clone of this SpatialReference object."
|
||||
return SpatialReference(capi.clone_srs(self.ptr))
|
||||
|
||||
def from_esri(self):
|
||||
"Morph this SpatialReference from ESRI's format to EPSG."
|
||||
capi.morph_from_esri(self.ptr)
|
||||
|
||||
def identify_epsg(self):
|
||||
"""
|
||||
This method inspects the WKT of this SpatialReference, and will
|
||||
add EPSG authority nodes where an EPSG identifier is applicable.
|
||||
"""
|
||||
capi.identify_epsg(self.ptr)
|
||||
|
||||
def to_esri(self):
|
||||
"Morph this SpatialReference to ESRI's format."
|
||||
capi.morph_to_esri(self.ptr)
|
||||
|
||||
def validate(self):
|
||||
"Check to see if the given spatial reference is valid."
|
||||
capi.srs_validate(self.ptr)
|
||||
|
||||
# #### Name & SRID properties ####
|
||||
@property
|
||||
def name(self):
|
||||
"Return the name of this Spatial Reference."
|
||||
if self.projected:
|
||||
return self.attr_value('PROJCS')
|
||||
elif self.geographic:
|
||||
return self.attr_value('GEOGCS')
|
||||
elif self.local:
|
||||
return self.attr_value('LOCAL_CS')
|
||||
else:
|
||||
return None
|
||||
|
||||
@property
|
||||
def srid(self):
|
||||
"Return the SRID of top-level authority, or None if undefined."
|
||||
try:
|
||||
return int(self.attr_value('AUTHORITY', 1))
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
|
||||
# #### Unit Properties ####
|
||||
@property
|
||||
def linear_name(self):
|
||||
"Return the name of the linear units."
|
||||
units, name = capi.linear_units(self.ptr, byref(c_char_p()))
|
||||
return name
|
||||
|
||||
@property
|
||||
def linear_units(self):
|
||||
"Return the value of the linear units."
|
||||
units, name = capi.linear_units(self.ptr, byref(c_char_p()))
|
||||
return units
|
||||
|
||||
@property
|
||||
def angular_name(self):
|
||||
"Return the name of the angular units."
|
||||
units, name = capi.angular_units(self.ptr, byref(c_char_p()))
|
||||
return name
|
||||
|
||||
@property
|
||||
def angular_units(self):
|
||||
"Return the value of the angular units."
|
||||
units, name = capi.angular_units(self.ptr, byref(c_char_p()))
|
||||
return units
|
||||
|
||||
@property
|
||||
def units(self):
|
||||
"""
|
||||
Return a 2-tuple of the units value and the units name. Automatically
|
||||
determine whether to return the linear or angular units.
|
||||
"""
|
||||
units, name = None, None
|
||||
if self.projected or self.local:
|
||||
units, name = capi.linear_units(self.ptr, byref(c_char_p()))
|
||||
elif self.geographic:
|
||||
units, name = capi.angular_units(self.ptr, byref(c_char_p()))
|
||||
if name is not None:
|
||||
name = force_text(name)
|
||||
return (units, name)
|
||||
|
||||
# #### Spheroid/Ellipsoid Properties ####
|
||||
@property
|
||||
def ellipsoid(self):
|
||||
"""
|
||||
Return a tuple of the ellipsoid parameters:
|
||||
(semimajor axis, semiminor axis, and inverse flattening)
|
||||
"""
|
||||
return (self.semi_major, self.semi_minor, self.inverse_flattening)
|
||||
|
||||
@property
|
||||
def semi_major(self):
|
||||
"Return the Semi Major Axis for this Spatial Reference."
|
||||
return capi.semi_major(self.ptr, byref(c_int()))
|
||||
|
||||
@property
|
||||
def semi_minor(self):
|
||||
"Return the Semi Minor Axis for this Spatial Reference."
|
||||
return capi.semi_minor(self.ptr, byref(c_int()))
|
||||
|
||||
@property
|
||||
def inverse_flattening(self):
|
||||
"Return the Inverse Flattening for this Spatial Reference."
|
||||
return capi.invflattening(self.ptr, byref(c_int()))
|
||||
|
||||
# #### Boolean Properties ####
|
||||
@property
|
||||
def geographic(self):
|
||||
"""
|
||||
Return True if this SpatialReference is geographic
|
||||
(root node is GEOGCS).
|
||||
"""
|
||||
return bool(capi.isgeographic(self.ptr))
|
||||
|
||||
@property
|
||||
def local(self):
|
||||
"Return True if this SpatialReference is local (root node is LOCAL_CS)."
|
||||
return bool(capi.islocal(self.ptr))
|
||||
|
||||
@property
|
||||
def projected(self):
|
||||
"""
|
||||
Return True if this SpatialReference is a projected coordinate system
|
||||
(root node is PROJCS).
|
||||
"""
|
||||
return bool(capi.isprojected(self.ptr))
|
||||
|
||||
# #### Import Routines #####
|
||||
def import_epsg(self, epsg):
|
||||
"Import the Spatial Reference from the EPSG code (an integer)."
|
||||
capi.from_epsg(self.ptr, epsg)
|
||||
|
||||
def import_proj(self, proj):
|
||||
"Import the Spatial Reference from a PROJ.4 string."
|
||||
capi.from_proj(self.ptr, proj)
|
||||
|
||||
def import_user_input(self, user_input):
|
||||
"Import the Spatial Reference from the given user input string."
|
||||
capi.from_user_input(self.ptr, force_bytes(user_input))
|
||||
|
||||
def import_wkt(self, wkt):
|
||||
"Import the Spatial Reference from OGC WKT (string)"
|
||||
capi.from_wkt(self.ptr, byref(c_char_p(force_bytes(wkt))))
|
||||
|
||||
def import_xml(self, xml):
|
||||
"Import the Spatial Reference from an XML string."
|
||||
capi.from_xml(self.ptr, xml)
|
||||
|
||||
# #### Export Properties ####
|
||||
@property
|
||||
def wkt(self):
|
||||
"Return the WKT representation of this Spatial Reference."
|
||||
return capi.to_wkt(self.ptr, byref(c_char_p()))
|
||||
|
||||
@property
|
||||
def pretty_wkt(self, simplify=0):
|
||||
"Return the 'pretty' representation of the WKT."
|
||||
return capi.to_pretty_wkt(self.ptr, byref(c_char_p()), simplify)
|
||||
|
||||
@property
|
||||
def proj(self):
|
||||
"Return the PROJ.4 representation for this Spatial Reference."
|
||||
return capi.to_proj(self.ptr, byref(c_char_p()))
|
||||
|
||||
@property
|
||||
def proj4(self):
|
||||
"Alias for proj()."
|
||||
return self.proj
|
||||
|
||||
@property
|
||||
def xml(self, dialect=''):
|
||||
"Return the XML representation of this Spatial Reference."
|
||||
return capi.to_xml(self.ptr, byref(c_char_p()), force_bytes(dialect))
|
||||
|
||||
|
||||
class CoordTransform(GDALBase):
|
||||
"The coordinate system transformation object."
|
||||
destructor = capi.destroy_ct
|
||||
|
||||
def __init__(self, source, target):
|
||||
"Initialize on a source and target SpatialReference objects."
|
||||
if not isinstance(source, SpatialReference) or not isinstance(target, SpatialReference):
|
||||
raise TypeError('source and target must be of type SpatialReference')
|
||||
self.ptr = capi.new_ct(source._ptr, target._ptr)
|
||||
self._srs1_name = source.name
|
||||
self._srs2_name = target.name
|
||||
|
||||
def __str__(self):
|
||||
return 'Transform from "%s" to "%s"' % (self._srs1_name, self._srs2_name)
|
||||
@@ -0,0 +1,23 @@
|
||||
"""
|
||||
This module houses the GeoIP2 object, a wrapper for the MaxMind GeoIP2(R)
|
||||
Python API (https://geoip2.readthedocs.io/). This is an alternative to the
|
||||
Python GeoIP2 interface provided by MaxMind.
|
||||
|
||||
GeoIP(R) is a registered trademark of MaxMind, Inc.
|
||||
|
||||
For IP-based geolocation, this module requires the GeoLite2 Country and City
|
||||
datasets, in binary format (CSV will not work!). The datasets may be
|
||||
downloaded from MaxMind at http://dev.maxmind.com/geoip/geoip2/geolite2/.
|
||||
Grab GeoLite2-Country.mmdb.gz and GeoLite2-City.mmdb.gz, and unzip them in the
|
||||
directory corresponding to settings.GEOIP_PATH.
|
||||
"""
|
||||
__all__ = ['HAS_GEOIP2']
|
||||
|
||||
try:
|
||||
import geoip2 # NOQA
|
||||
except ImportError:
|
||||
HAS_GEOIP2 = False
|
||||
else:
|
||||
from .base import GeoIP2, GeoIP2Exception
|
||||
HAS_GEOIP2 = True
|
||||
__all__ += ['GeoIP2', 'GeoIP2Exception']
|
||||
@@ -0,0 +1,21 @@
|
||||
def City(response):
|
||||
return {
|
||||
'city': response.city.name,
|
||||
'continent_code': response.continent.code,
|
||||
'continent_name': response.continent.name,
|
||||
'country_code': response.country.iso_code,
|
||||
'country_name': response.country.name,
|
||||
'dma_code': response.location.metro_code,
|
||||
'latitude': response.location.latitude,
|
||||
'longitude': response.location.longitude,
|
||||
'postal_code': response.postal.code,
|
||||
'region': response.subdivisions[0].iso_code if response.subdivisions else None,
|
||||
'time_zone': response.location.time_zone,
|
||||
}
|
||||
|
||||
|
||||
def Country(response):
|
||||
return {
|
||||
'country_code': response.country.iso_code,
|
||||
'country_name': response.country.name,
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
Copyright (c) 2007-2009 Justin Bronn
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of GEOSGeometry nor the names of its contributors may be used
|
||||
to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
@@ -0,0 +1,196 @@
|
||||
"""
|
||||
This module houses the GEOSCoordSeq object, which is used internally
|
||||
by GEOSGeometry to house the actual coordinates of the Point,
|
||||
LineString, and LinearRing geometries.
|
||||
"""
|
||||
from ctypes import byref, c_double, c_uint
|
||||
|
||||
from django.contrib.gis.geos import prototypes as capi
|
||||
from django.contrib.gis.geos.base import GEOSBase
|
||||
from django.contrib.gis.geos.error import GEOSException
|
||||
from django.contrib.gis.geos.libgeos import CS_PTR
|
||||
from django.contrib.gis.shortcuts import numpy
|
||||
|
||||
|
||||
class GEOSCoordSeq(GEOSBase):
|
||||
"The internal representation of a list of coordinates inside a Geometry."
|
||||
|
||||
ptr_type = CS_PTR
|
||||
|
||||
def __init__(self, ptr, z=False):
|
||||
"Initialize from a GEOS pointer."
|
||||
if not isinstance(ptr, CS_PTR):
|
||||
raise TypeError('Coordinate sequence should initialize with a CS_PTR.')
|
||||
self._ptr = ptr
|
||||
self._z = z
|
||||
|
||||
def __iter__(self):
|
||||
"Iterate over each point in the coordinate sequence."
|
||||
for i in range(self.size):
|
||||
yield self[i]
|
||||
|
||||
def __len__(self):
|
||||
"Return the number of points in the coordinate sequence."
|
||||
return int(self.size)
|
||||
|
||||
def __str__(self):
|
||||
"Return the string representation of the coordinate sequence."
|
||||
return str(self.tuple)
|
||||
|
||||
def __getitem__(self, index):
|
||||
"Return the coordinate sequence value at the given index."
|
||||
self._checkindex(index)
|
||||
return self._point_getter(index)
|
||||
|
||||
def __setitem__(self, index, value):
|
||||
"Set the coordinate sequence value at the given index."
|
||||
# Checking the input value
|
||||
if isinstance(value, (list, tuple)):
|
||||
pass
|
||||
elif numpy and isinstance(value, numpy.ndarray):
|
||||
pass
|
||||
else:
|
||||
raise TypeError('Must set coordinate with a sequence (list, tuple, or numpy array).')
|
||||
# Checking the dims of the input
|
||||
if self.dims == 3 and self._z:
|
||||
n_args = 3
|
||||
point_setter = self._set_point_3d
|
||||
else:
|
||||
n_args = 2
|
||||
point_setter = self._set_point_2d
|
||||
if len(value) != n_args:
|
||||
raise TypeError('Dimension of value does not match.')
|
||||
self._checkindex(index)
|
||||
point_setter(index, value)
|
||||
|
||||
# #### Internal Routines ####
|
||||
def _checkindex(self, index):
|
||||
"Check the given index."
|
||||
if not (0 <= index < self.size):
|
||||
raise IndexError('invalid GEOS Geometry index: %s' % index)
|
||||
|
||||
def _checkdim(self, dim):
|
||||
"Check the given dimension."
|
||||
if dim < 0 or dim > 2:
|
||||
raise GEOSException('invalid ordinate dimension "%d"' % dim)
|
||||
|
||||
def _get_x(self, index):
|
||||
return capi.cs_getx(self.ptr, index, byref(c_double()))
|
||||
|
||||
def _get_y(self, index):
|
||||
return capi.cs_gety(self.ptr, index, byref(c_double()))
|
||||
|
||||
def _get_z(self, index):
|
||||
return capi.cs_getz(self.ptr, index, byref(c_double()))
|
||||
|
||||
def _set_x(self, index, value):
|
||||
capi.cs_setx(self.ptr, index, value)
|
||||
|
||||
def _set_y(self, index, value):
|
||||
capi.cs_sety(self.ptr, index, value)
|
||||
|
||||
def _set_z(self, index, value):
|
||||
capi.cs_setz(self.ptr, index, value)
|
||||
|
||||
@property
|
||||
def _point_getter(self):
|
||||
return self._get_point_3d if self.dims == 3 and self._z else self._get_point_2d
|
||||
|
||||
def _get_point_2d(self, index):
|
||||
return (self._get_x(index), self._get_y(index))
|
||||
|
||||
def _get_point_3d(self, index):
|
||||
return (self._get_x(index), self._get_y(index), self._get_z(index))
|
||||
|
||||
def _set_point_2d(self, index, value):
|
||||
x, y = value
|
||||
self._set_x(index, x)
|
||||
self._set_y(index, y)
|
||||
|
||||
def _set_point_3d(self, index, value):
|
||||
x, y, z = value
|
||||
self._set_x(index, x)
|
||||
self._set_y(index, y)
|
||||
self._set_z(index, z)
|
||||
|
||||
# #### Ordinate getting and setting routines ####
|
||||
def getOrdinate(self, dimension, index):
|
||||
"Return the value for the given dimension and index."
|
||||
self._checkindex(index)
|
||||
self._checkdim(dimension)
|
||||
return capi.cs_getordinate(self.ptr, index, dimension, byref(c_double()))
|
||||
|
||||
def setOrdinate(self, dimension, index, value):
|
||||
"Set the value for the given dimension and index."
|
||||
self._checkindex(index)
|
||||
self._checkdim(dimension)
|
||||
capi.cs_setordinate(self.ptr, index, dimension, value)
|
||||
|
||||
def getX(self, index):
|
||||
"Get the X value at the index."
|
||||
return self.getOrdinate(0, index)
|
||||
|
||||
def setX(self, index, value):
|
||||
"Set X with the value at the given index."
|
||||
self.setOrdinate(0, index, value)
|
||||
|
||||
def getY(self, index):
|
||||
"Get the Y value at the given index."
|
||||
return self.getOrdinate(1, index)
|
||||
|
||||
def setY(self, index, value):
|
||||
"Set Y with the value at the given index."
|
||||
self.setOrdinate(1, index, value)
|
||||
|
||||
def getZ(self, index):
|
||||
"Get Z with the value at the given index."
|
||||
return self.getOrdinate(2, index)
|
||||
|
||||
def setZ(self, index, value):
|
||||
"Set Z with the value at the given index."
|
||||
self.setOrdinate(2, index, value)
|
||||
|
||||
# ### Dimensions ###
|
||||
@property
|
||||
def size(self):
|
||||
"Return the size of this coordinate sequence."
|
||||
return capi.cs_getsize(self.ptr, byref(c_uint()))
|
||||
|
||||
@property
|
||||
def dims(self):
|
||||
"Return the dimensions of this coordinate sequence."
|
||||
return capi.cs_getdims(self.ptr, byref(c_uint()))
|
||||
|
||||
@property
|
||||
def hasz(self):
|
||||
"""
|
||||
Return whether this coordinate sequence is 3D. This property value is
|
||||
inherited from the parent Geometry.
|
||||
"""
|
||||
return self._z
|
||||
|
||||
# ### Other Methods ###
|
||||
def clone(self):
|
||||
"Clone this coordinate sequence."
|
||||
return GEOSCoordSeq(capi.cs_clone(self.ptr), self.hasz)
|
||||
|
||||
@property
|
||||
def kml(self):
|
||||
"Return the KML representation for the coordinates."
|
||||
# Getting the substitution string depending on whether the coordinates have
|
||||
# a Z dimension.
|
||||
if self.hasz:
|
||||
substr = '%s,%s,%s '
|
||||
else:
|
||||
substr = '%s,%s,0 '
|
||||
return '<coordinates>%s</coordinates>' % \
|
||||
''.join(substr % self[i] for i in range(len(self))).strip()
|
||||
|
||||
@property
|
||||
def tuple(self):
|
||||
"Return a tuple version of this coordinate sequence."
|
||||
n = self.size
|
||||
get_point = self._point_getter
|
||||
if n == 1:
|
||||
return get_point(0)
|
||||
return tuple(get_point(i) for i in range(n))
|
||||
@@ -0,0 +1,3 @@
|
||||
class GEOSException(Exception):
|
||||
"The base GEOS exception, indicates a GEOS-related error."
|
||||
pass
|
||||
@@ -0,0 +1,738 @@
|
||||
"""
|
||||
This module contains the 'base' GEOSGeometry object -- all GEOS Geometries
|
||||
inherit from this object.
|
||||
"""
|
||||
import re
|
||||
from ctypes import addressof, byref, c_double
|
||||
|
||||
from django.contrib.gis import gdal
|
||||
from django.contrib.gis.geometry import hex_regex, json_regex, wkt_regex
|
||||
from django.contrib.gis.geos import prototypes as capi
|
||||
from django.contrib.gis.geos.base import GEOSBase
|
||||
from django.contrib.gis.geos.coordseq import GEOSCoordSeq
|
||||
from django.contrib.gis.geos.error import GEOSException
|
||||
from django.contrib.gis.geos.libgeos import GEOM_PTR
|
||||
from django.contrib.gis.geos.mutable_list import ListMixin
|
||||
from django.contrib.gis.geos.prepared import PreparedGeometry
|
||||
from django.contrib.gis.geos.prototypes.io import (
|
||||
ewkb_w, wkb_r, wkb_w, wkt_r, wkt_w,
|
||||
)
|
||||
from django.utils.deconstruct import deconstructible
|
||||
from django.utils.encoding import force_bytes, force_text
|
||||
|
||||
|
||||
class GEOSGeometryBase(GEOSBase):
|
||||
|
||||
_GEOS_CLASSES = None
|
||||
|
||||
ptr_type = GEOM_PTR
|
||||
destructor = capi.destroy_geom
|
||||
has_cs = False # Only Point, LineString, LinearRing have coordinate sequences
|
||||
|
||||
def __init__(self, ptr, cls):
|
||||
self._ptr = ptr
|
||||
|
||||
# Setting the class type (e.g., Point, Polygon, etc.)
|
||||
if type(self) in (GEOSGeometryBase, GEOSGeometry):
|
||||
if cls is None:
|
||||
if GEOSGeometryBase._GEOS_CLASSES is None:
|
||||
# Inner imports avoid import conflicts with GEOSGeometry.
|
||||
from .linestring import LineString, LinearRing
|
||||
from .point import Point
|
||||
from .polygon import Polygon
|
||||
from .collections import (
|
||||
GeometryCollection, MultiPoint, MultiLineString, MultiPolygon,
|
||||
)
|
||||
GEOSGeometryBase._GEOS_CLASSES = {
|
||||
0: Point,
|
||||
1: LineString,
|
||||
2: LinearRing,
|
||||
3: Polygon,
|
||||
4: MultiPoint,
|
||||
5: MultiLineString,
|
||||
6: MultiPolygon,
|
||||
7: GeometryCollection,
|
||||
}
|
||||
cls = GEOSGeometryBase._GEOS_CLASSES[self.geom_typeid]
|
||||
self.__class__ = cls
|
||||
self._post_init()
|
||||
|
||||
def _post_init(self):
|
||||
"Perform post-initialization setup."
|
||||
# Setting the coordinate sequence for the geometry (will be None on
|
||||
# geometries that do not have coordinate sequences)
|
||||
self._cs = GEOSCoordSeq(capi.get_cs(self.ptr), self.hasz) if self.has_cs else None
|
||||
|
||||
def __copy__(self):
|
||||
"""
|
||||
Return a clone because the copy of a GEOSGeometry may contain an
|
||||
invalid pointer location if the original is garbage collected.
|
||||
"""
|
||||
return self.clone()
|
||||
|
||||
def __deepcopy__(self, memodict):
|
||||
"""
|
||||
The `deepcopy` routine is used by the `Node` class of django.utils.tree;
|
||||
thus, the protocol routine needs to be implemented to return correct
|
||||
copies (clones) of these GEOS objects, which use C pointers.
|
||||
"""
|
||||
return self.clone()
|
||||
|
||||
def __str__(self):
|
||||
"EWKT is used for the string representation."
|
||||
return self.ewkt
|
||||
|
||||
def __repr__(self):
|
||||
"Short-hand representation because WKT may be very large."
|
||||
return '<%s object at %s>' % (self.geom_type, hex(addressof(self.ptr)))
|
||||
|
||||
# Pickling support
|
||||
def _to_pickle_wkb(self):
|
||||
return bytes(self.wkb)
|
||||
|
||||
def _from_pickle_wkb(self, wkb):
|
||||
return wkb_r().read(memoryview(wkb))
|
||||
|
||||
def __getstate__(self):
|
||||
# The pickled state is simply a tuple of the WKB (in string form)
|
||||
# and the SRID.
|
||||
return self._to_pickle_wkb(), self.srid
|
||||
|
||||
def __setstate__(self, state):
|
||||
# Instantiating from the tuple state that was pickled.
|
||||
wkb, srid = state
|
||||
ptr = self._from_pickle_wkb(wkb)
|
||||
if not ptr:
|
||||
raise GEOSException('Invalid Geometry loaded from pickled state.')
|
||||
self.ptr = ptr
|
||||
self._post_init()
|
||||
self.srid = srid
|
||||
|
||||
@classmethod
|
||||
def _from_wkb(cls, wkb):
|
||||
return wkb_r().read(wkb)
|
||||
|
||||
@staticmethod
|
||||
def from_ewkt(ewkt):
|
||||
ewkt = force_bytes(ewkt)
|
||||
srid = None
|
||||
parts = ewkt.split(b';', 1)
|
||||
if len(parts) == 2:
|
||||
srid_part, wkt = parts
|
||||
match = re.match(br'SRID=(?P<srid>\-?\d+)', srid_part)
|
||||
if not match:
|
||||
raise ValueError('EWKT has invalid SRID part.')
|
||||
srid = int(match.group('srid'))
|
||||
else:
|
||||
wkt = ewkt
|
||||
if not wkt:
|
||||
raise ValueError('Expected WKT but got an empty string.')
|
||||
return GEOSGeometry(GEOSGeometry._from_wkt(wkt), srid=srid)
|
||||
|
||||
@staticmethod
|
||||
def _from_wkt(wkt):
|
||||
return wkt_r().read(wkt)
|
||||
|
||||
@classmethod
|
||||
def from_gml(cls, gml_string):
|
||||
return gdal.OGRGeometry.from_gml(gml_string).geos
|
||||
|
||||
# Comparison operators
|
||||
def __eq__(self, other):
|
||||
"""
|
||||
Equivalence testing, a Geometry may be compared with another Geometry
|
||||
or an EWKT representation.
|
||||
"""
|
||||
if isinstance(other, str):
|
||||
try:
|
||||
other = GEOSGeometry.from_ewkt(other)
|
||||
except (ValueError, GEOSException):
|
||||
return False
|
||||
return isinstance(other, GEOSGeometry) and self.srid == other.srid and self.equals_exact(other)
|
||||
|
||||
def __hash__(self):
|
||||
return hash((self.srid, self.wkt))
|
||||
|
||||
# ### Geometry set-like operations ###
|
||||
# Thanks to Sean Gillies for inspiration:
|
||||
# http://lists.gispython.org/pipermail/community/2007-July/001034.html
|
||||
# g = g1 | g2
|
||||
def __or__(self, other):
|
||||
"Return the union of this Geometry and the other."
|
||||
return self.union(other)
|
||||
|
||||
# g = g1 & g2
|
||||
def __and__(self, other):
|
||||
"Return the intersection of this Geometry and the other."
|
||||
return self.intersection(other)
|
||||
|
||||
# g = g1 - g2
|
||||
def __sub__(self, other):
|
||||
"Return the difference this Geometry and the other."
|
||||
return self.difference(other)
|
||||
|
||||
# g = g1 ^ g2
|
||||
def __xor__(self, other):
|
||||
"Return the symmetric difference of this Geometry and the other."
|
||||
return self.sym_difference(other)
|
||||
|
||||
# #### Coordinate Sequence Routines ####
|
||||
@property
|
||||
def coord_seq(self):
|
||||
"Return a clone of the coordinate sequence for this Geometry."
|
||||
if self.has_cs:
|
||||
return self._cs.clone()
|
||||
|
||||
# #### Geometry Info ####
|
||||
@property
|
||||
def geom_type(self):
|
||||
"Return a string representing the Geometry type, e.g. 'Polygon'"
|
||||
return capi.geos_type(self.ptr).decode()
|
||||
|
||||
@property
|
||||
def geom_typeid(self):
|
||||
"Return an integer representing the Geometry type."
|
||||
return capi.geos_typeid(self.ptr)
|
||||
|
||||
@property
|
||||
def num_geom(self):
|
||||
"Return the number of geometries in the Geometry."
|
||||
return capi.get_num_geoms(self.ptr)
|
||||
|
||||
@property
|
||||
def num_coords(self):
|
||||
"Return the number of coordinates in the Geometry."
|
||||
return capi.get_num_coords(self.ptr)
|
||||
|
||||
@property
|
||||
def num_points(self):
|
||||
"Return the number points, or coordinates, in the Geometry."
|
||||
return self.num_coords
|
||||
|
||||
@property
|
||||
def dims(self):
|
||||
"Return the dimension of this Geometry (0=point, 1=line, 2=surface)."
|
||||
return capi.get_dims(self.ptr)
|
||||
|
||||
def normalize(self):
|
||||
"Convert this Geometry to normal form (or canonical form)."
|
||||
capi.geos_normalize(self.ptr)
|
||||
|
||||
# #### Unary predicates ####
|
||||
@property
|
||||
def empty(self):
|
||||
"""
|
||||
Return a boolean indicating whether the set of points in this Geometry
|
||||
are empty.
|
||||
"""
|
||||
return capi.geos_isempty(self.ptr)
|
||||
|
||||
@property
|
||||
def hasz(self):
|
||||
"Return whether the geometry has a 3D dimension."
|
||||
return capi.geos_hasz(self.ptr)
|
||||
|
||||
@property
|
||||
def ring(self):
|
||||
"Return whether or not the geometry is a ring."
|
||||
return capi.geos_isring(self.ptr)
|
||||
|
||||
@property
|
||||
def simple(self):
|
||||
"Return false if the Geometry isn't simple."
|
||||
return capi.geos_issimple(self.ptr)
|
||||
|
||||
@property
|
||||
def valid(self):
|
||||
"Test the validity of this Geometry."
|
||||
return capi.geos_isvalid(self.ptr)
|
||||
|
||||
@property
|
||||
def valid_reason(self):
|
||||
"""
|
||||
Return a string containing the reason for any invalidity.
|
||||
"""
|
||||
return capi.geos_isvalidreason(self.ptr).decode()
|
||||
|
||||
# #### Binary predicates. ####
|
||||
def contains(self, other):
|
||||
"Return true if other.within(this) returns true."
|
||||
return capi.geos_contains(self.ptr, other.ptr)
|
||||
|
||||
def covers(self, other):
|
||||
"""
|
||||
Return True if the DE-9IM Intersection Matrix for the two geometries is
|
||||
T*****FF*, *T****FF*, ***T**FF*, or ****T*FF*. If either geometry is
|
||||
empty, return False.
|
||||
"""
|
||||
return capi.geos_covers(self.ptr, other.ptr)
|
||||
|
||||
def crosses(self, other):
|
||||
"""
|
||||
Return true if the DE-9IM intersection matrix for the two Geometries
|
||||
is T*T****** (for a point and a curve,a point and an area or a line and
|
||||
an area) 0******** (for two curves).
|
||||
"""
|
||||
return capi.geos_crosses(self.ptr, other.ptr)
|
||||
|
||||
def disjoint(self, other):
|
||||
"""
|
||||
Return true if the DE-9IM intersection matrix for the two Geometries
|
||||
is FF*FF****.
|
||||
"""
|
||||
return capi.geos_disjoint(self.ptr, other.ptr)
|
||||
|
||||
def equals(self, other):
|
||||
"""
|
||||
Return true if the DE-9IM intersection matrix for the two Geometries
|
||||
is T*F**FFF*.
|
||||
"""
|
||||
return capi.geos_equals(self.ptr, other.ptr)
|
||||
|
||||
def equals_exact(self, other, tolerance=0):
|
||||
"""
|
||||
Return true if the two Geometries are exactly equal, up to a
|
||||
specified tolerance.
|
||||
"""
|
||||
return capi.geos_equalsexact(self.ptr, other.ptr, float(tolerance))
|
||||
|
||||
def intersects(self, other):
|
||||
"Return true if disjoint return false."
|
||||
return capi.geos_intersects(self.ptr, other.ptr)
|
||||
|
||||
def overlaps(self, other):
|
||||
"""
|
||||
Return true if the DE-9IM intersection matrix for the two Geometries
|
||||
is T*T***T** (for two points or two surfaces) 1*T***T** (for two curves).
|
||||
"""
|
||||
return capi.geos_overlaps(self.ptr, other.ptr)
|
||||
|
||||
def relate_pattern(self, other, pattern):
|
||||
"""
|
||||
Return true if the elements in the DE-9IM intersection matrix for the
|
||||
two Geometries match the elements in pattern.
|
||||
"""
|
||||
if not isinstance(pattern, str) or len(pattern) > 9:
|
||||
raise GEOSException('invalid intersection matrix pattern')
|
||||
return capi.geos_relatepattern(self.ptr, other.ptr, force_bytes(pattern))
|
||||
|
||||
def touches(self, other):
|
||||
"""
|
||||
Return true if the DE-9IM intersection matrix for the two Geometries
|
||||
is FT*******, F**T***** or F***T****.
|
||||
"""
|
||||
return capi.geos_touches(self.ptr, other.ptr)
|
||||
|
||||
def within(self, other):
|
||||
"""
|
||||
Return true if the DE-9IM intersection matrix for the two Geometries
|
||||
is T*F**F***.
|
||||
"""
|
||||
return capi.geos_within(self.ptr, other.ptr)
|
||||
|
||||
# #### SRID Routines ####
|
||||
@property
|
||||
def srid(self):
|
||||
"Get the SRID for the geometry. Return None if no SRID is set."
|
||||
s = capi.geos_get_srid(self.ptr)
|
||||
if s == 0:
|
||||
return None
|
||||
else:
|
||||
return s
|
||||
|
||||
@srid.setter
|
||||
def srid(self, srid):
|
||||
"Set the SRID for the geometry."
|
||||
capi.geos_set_srid(self.ptr, 0 if srid is None else srid)
|
||||
|
||||
# #### Output Routines ####
|
||||
@property
|
||||
def ewkt(self):
|
||||
"""
|
||||
Return the EWKT (SRID + WKT) of the Geometry.
|
||||
"""
|
||||
srid = self.srid
|
||||
return 'SRID=%s;%s' % (srid, self.wkt) if srid else self.wkt
|
||||
|
||||
@property
|
||||
def wkt(self):
|
||||
"Return the WKT (Well-Known Text) representation of this Geometry."
|
||||
return wkt_w(dim=3 if self.hasz else 2, trim=True).write(self).decode()
|
||||
|
||||
@property
|
||||
def hex(self):
|
||||
"""
|
||||
Return the WKB of this Geometry in hexadecimal form. Please note
|
||||
that the SRID is not included in this representation because it is not
|
||||
a part of the OGC specification (use the `hexewkb` property instead).
|
||||
"""
|
||||
# A possible faster, all-python, implementation:
|
||||
# str(self.wkb).encode('hex')
|
||||
return wkb_w(dim=3 if self.hasz else 2).write_hex(self)
|
||||
|
||||
@property
|
||||
def hexewkb(self):
|
||||
"""
|
||||
Return the EWKB of this Geometry in hexadecimal form. This is an
|
||||
extension of the WKB specification that includes SRID value that are
|
||||
a part of this geometry.
|
||||
"""
|
||||
return ewkb_w(dim=3 if self.hasz else 2).write_hex(self)
|
||||
|
||||
@property
|
||||
def json(self):
|
||||
"""
|
||||
Return GeoJSON representation of this Geometry.
|
||||
"""
|
||||
return self.ogr.json
|
||||
geojson = json
|
||||
|
||||
@property
|
||||
def wkb(self):
|
||||
"""
|
||||
Return the WKB (Well-Known Binary) representation of this Geometry
|
||||
as a Python buffer. SRID and Z values are not included, use the
|
||||
`ewkb` property instead.
|
||||
"""
|
||||
return wkb_w(3 if self.hasz else 2).write(self)
|
||||
|
||||
@property
|
||||
def ewkb(self):
|
||||
"""
|
||||
Return the EWKB representation of this Geometry as a Python buffer.
|
||||
This is an extension of the WKB specification that includes any SRID
|
||||
value that are a part of this geometry.
|
||||
"""
|
||||
return ewkb_w(3 if self.hasz else 2).write(self)
|
||||
|
||||
@property
|
||||
def kml(self):
|
||||
"Return the KML representation of this Geometry."
|
||||
gtype = self.geom_type
|
||||
return '<%s>%s</%s>' % (gtype, self.coord_seq.kml, gtype)
|
||||
|
||||
@property
|
||||
def prepared(self):
|
||||
"""
|
||||
Return a PreparedGeometry corresponding to this geometry -- it is
|
||||
optimized for the contains, intersects, and covers operations.
|
||||
"""
|
||||
return PreparedGeometry(self)
|
||||
|
||||
# #### GDAL-specific output routines ####
|
||||
def _ogr_ptr(self):
|
||||
return gdal.OGRGeometry._from_wkb(self.wkb)
|
||||
|
||||
@property
|
||||
def ogr(self):
|
||||
"Return the OGR Geometry for this Geometry."
|
||||
return gdal.OGRGeometry(self._ogr_ptr(), self.srs)
|
||||
|
||||
@property
|
||||
def srs(self):
|
||||
"Return the OSR SpatialReference for SRID of this Geometry."
|
||||
if self.srid:
|
||||
try:
|
||||
return gdal.SpatialReference(self.srid)
|
||||
except gdal.SRSException:
|
||||
pass
|
||||
return None
|
||||
|
||||
@property
|
||||
def crs(self):
|
||||
"Alias for `srs` property."
|
||||
return self.srs
|
||||
|
||||
def transform(self, ct, clone=False):
|
||||
"""
|
||||
Requires GDAL. Transform the geometry according to the given
|
||||
transformation object, which may be an integer SRID, and WKT or
|
||||
PROJ.4 string. By default, transform the geometry in-place and return
|
||||
nothing. However if the `clone` keyword is set, don't modify the
|
||||
geometry and return a transformed clone instead.
|
||||
"""
|
||||
srid = self.srid
|
||||
|
||||
if ct == srid:
|
||||
# short-circuit where source & dest SRIDs match
|
||||
if clone:
|
||||
return self.clone()
|
||||
else:
|
||||
return
|
||||
|
||||
if isinstance(ct, gdal.CoordTransform):
|
||||
# We don't care about SRID because CoordTransform presupposes
|
||||
# source SRS.
|
||||
srid = None
|
||||
elif srid is None or srid < 0:
|
||||
raise GEOSException("Calling transform() with no SRID set is not supported")
|
||||
|
||||
# Creating an OGR Geometry, which is then transformed.
|
||||
g = gdal.OGRGeometry(self._ogr_ptr(), srid)
|
||||
g.transform(ct)
|
||||
# Getting a new GEOS pointer
|
||||
ptr = g._geos_ptr()
|
||||
if clone:
|
||||
# User wants a cloned transformed geometry returned.
|
||||
return GEOSGeometry(ptr, srid=g.srid)
|
||||
if ptr:
|
||||
# Reassigning pointer, and performing post-initialization setup
|
||||
# again due to the reassignment.
|
||||
capi.destroy_geom(self.ptr)
|
||||
self.ptr = ptr
|
||||
self._post_init()
|
||||
self.srid = g.srid
|
||||
else:
|
||||
raise GEOSException('Transformed WKB was invalid.')
|
||||
|
||||
# #### Topology Routines ####
|
||||
def _topology(self, gptr):
|
||||
"Return Geometry from the given pointer."
|
||||
return GEOSGeometry(gptr, srid=self.srid)
|
||||
|
||||
@property
|
||||
def boundary(self):
|
||||
"Return the boundary as a newly allocated Geometry object."
|
||||
return self._topology(capi.geos_boundary(self.ptr))
|
||||
|
||||
def buffer(self, width, quadsegs=8):
|
||||
"""
|
||||
Return a geometry that represents all points whose distance from this
|
||||
Geometry is less than or equal to distance. Calculations are in the
|
||||
Spatial Reference System of this Geometry. The optional third parameter sets
|
||||
the number of segment used to approximate a quarter circle (defaults to 8).
|
||||
(Text from PostGIS documentation at ch. 6.1.3)
|
||||
"""
|
||||
return self._topology(capi.geos_buffer(self.ptr, width, quadsegs))
|
||||
|
||||
def buffer_with_style(self, width, quadsegs=8, end_cap_style=1, join_style=1, mitre_limit=5.0):
|
||||
"""
|
||||
Same as buffer() but allows customizing the style of the buffer.
|
||||
|
||||
End cap style can be round (1), flat (2), or square (3).
|
||||
Join style can be round (1), mitre (2), or bevel (3).
|
||||
Mitre ratio limit only affects mitered join style.
|
||||
"""
|
||||
return self._topology(
|
||||
capi.geos_bufferwithstyle(self.ptr, width, quadsegs, end_cap_style, join_style, mitre_limit),
|
||||
)
|
||||
|
||||
@property
|
||||
def centroid(self):
|
||||
"""
|
||||
The centroid is equal to the centroid of the set of component Geometries
|
||||
of highest dimension (since the lower-dimension geometries contribute zero
|
||||
"weight" to the centroid).
|
||||
"""
|
||||
return self._topology(capi.geos_centroid(self.ptr))
|
||||
|
||||
@property
|
||||
def convex_hull(self):
|
||||
"""
|
||||
Return the smallest convex Polygon that contains all the points
|
||||
in the Geometry.
|
||||
"""
|
||||
return self._topology(capi.geos_convexhull(self.ptr))
|
||||
|
||||
def difference(self, other):
|
||||
"""
|
||||
Return a Geometry representing the points making up this Geometry
|
||||
that do not make up other.
|
||||
"""
|
||||
return self._topology(capi.geos_difference(self.ptr, other.ptr))
|
||||
|
||||
@property
|
||||
def envelope(self):
|
||||
"Return the envelope for this geometry (a polygon)."
|
||||
return self._topology(capi.geos_envelope(self.ptr))
|
||||
|
||||
def intersection(self, other):
|
||||
"Return a Geometry representing the points shared by this Geometry and other."
|
||||
return self._topology(capi.geos_intersection(self.ptr, other.ptr))
|
||||
|
||||
@property
|
||||
def point_on_surface(self):
|
||||
"Compute an interior point of this Geometry."
|
||||
return self._topology(capi.geos_pointonsurface(self.ptr))
|
||||
|
||||
def relate(self, other):
|
||||
"Return the DE-9IM intersection matrix for this Geometry and the other."
|
||||
return capi.geos_relate(self.ptr, other.ptr).decode()
|
||||
|
||||
def simplify(self, tolerance=0.0, preserve_topology=False):
|
||||
"""
|
||||
Return the Geometry, simplified using the Douglas-Peucker algorithm
|
||||
to the specified tolerance (higher tolerance => less points). If no
|
||||
tolerance provided, defaults to 0.
|
||||
|
||||
By default, don't preserve topology - e.g. polygons can be split,
|
||||
collapse to lines or disappear holes can be created or disappear, and
|
||||
lines can cross. By specifying preserve_topology=True, the result will
|
||||
have the same dimension and number of components as the input. This is
|
||||
significantly slower.
|
||||
"""
|
||||
if preserve_topology:
|
||||
return self._topology(capi.geos_preservesimplify(self.ptr, tolerance))
|
||||
else:
|
||||
return self._topology(capi.geos_simplify(self.ptr, tolerance))
|
||||
|
||||
def sym_difference(self, other):
|
||||
"""
|
||||
Return a set combining the points in this Geometry not in other,
|
||||
and the points in other not in this Geometry.
|
||||
"""
|
||||
return self._topology(capi.geos_symdifference(self.ptr, other.ptr))
|
||||
|
||||
@property
|
||||
def unary_union(self):
|
||||
"Return the union of all the elements of this geometry."
|
||||
return self._topology(capi.geos_unary_union(self.ptr))
|
||||
|
||||
def union(self, other):
|
||||
"Return a Geometry representing all the points in this Geometry and other."
|
||||
return self._topology(capi.geos_union(self.ptr, other.ptr))
|
||||
|
||||
# #### Other Routines ####
|
||||
@property
|
||||
def area(self):
|
||||
"Return the area of the Geometry."
|
||||
return capi.geos_area(self.ptr, byref(c_double()))
|
||||
|
||||
def distance(self, other):
|
||||
"""
|
||||
Return the distance between the closest points on this Geometry
|
||||
and the other. Units will be in those of the coordinate system of
|
||||
the Geometry.
|
||||
"""
|
||||
if not isinstance(other, GEOSGeometry):
|
||||
raise TypeError('distance() works only on other GEOS Geometries.')
|
||||
return capi.geos_distance(self.ptr, other.ptr, byref(c_double()))
|
||||
|
||||
@property
|
||||
def extent(self):
|
||||
"""
|
||||
Return the extent of this geometry as a 4-tuple, consisting of
|
||||
(xmin, ymin, xmax, ymax).
|
||||
"""
|
||||
from .point import Point
|
||||
env = self.envelope
|
||||
if isinstance(env, Point):
|
||||
xmin, ymin = env.tuple
|
||||
xmax, ymax = xmin, ymin
|
||||
else:
|
||||
xmin, ymin = env[0][0]
|
||||
xmax, ymax = env[0][2]
|
||||
return (xmin, ymin, xmax, ymax)
|
||||
|
||||
@property
|
||||
def length(self):
|
||||
"""
|
||||
Return the length of this Geometry (e.g., 0 for point, or the
|
||||
circumference of a Polygon).
|
||||
"""
|
||||
return capi.geos_length(self.ptr, byref(c_double()))
|
||||
|
||||
def clone(self):
|
||||
"Clone this Geometry."
|
||||
return GEOSGeometry(capi.geom_clone(self.ptr))
|
||||
|
||||
|
||||
class LinearGeometryMixin:
|
||||
"""
|
||||
Used for LineString and MultiLineString.
|
||||
"""
|
||||
def interpolate(self, distance):
|
||||
return self._topology(capi.geos_interpolate(self.ptr, distance))
|
||||
|
||||
def interpolate_normalized(self, distance):
|
||||
return self._topology(capi.geos_interpolate_normalized(self.ptr, distance))
|
||||
|
||||
def project(self, point):
|
||||
from .point import Point
|
||||
if not isinstance(point, Point):
|
||||
raise TypeError('locate_point argument must be a Point')
|
||||
return capi.geos_project(self.ptr, point.ptr)
|
||||
|
||||
def project_normalized(self, point):
|
||||
from .point import Point
|
||||
if not isinstance(point, Point):
|
||||
raise TypeError('locate_point argument must be a Point')
|
||||
return capi.geos_project_normalized(self.ptr, point.ptr)
|
||||
|
||||
@property
|
||||
def merged(self):
|
||||
"""
|
||||
Return the line merge of this Geometry.
|
||||
"""
|
||||
return self._topology(capi.geos_linemerge(self.ptr))
|
||||
|
||||
@property
|
||||
def closed(self):
|
||||
"""
|
||||
Return whether or not this Geometry is closed.
|
||||
"""
|
||||
return capi.geos_isclosed(self.ptr)
|
||||
|
||||
|
||||
@deconstructible
|
||||
class GEOSGeometry(GEOSGeometryBase, ListMixin):
|
||||
"A class that, generally, encapsulates a GEOS geometry."
|
||||
|
||||
def __init__(self, geo_input, srid=None):
|
||||
"""
|
||||
The base constructor for GEOS geometry objects. It may take the
|
||||
following inputs:
|
||||
|
||||
* strings:
|
||||
- WKT
|
||||
- HEXEWKB (a PostGIS-specific canonical form)
|
||||
- GeoJSON (requires GDAL)
|
||||
* buffer:
|
||||
- WKB
|
||||
|
||||
The `srid` keyword specifies the Source Reference Identifier (SRID)
|
||||
number for this Geometry. If not provided, it defaults to None.
|
||||
"""
|
||||
input_srid = None
|
||||
if isinstance(geo_input, bytes):
|
||||
geo_input = force_text(geo_input)
|
||||
if isinstance(geo_input, str):
|
||||
wkt_m = wkt_regex.match(geo_input)
|
||||
if wkt_m:
|
||||
# Handle WKT input.
|
||||
if wkt_m.group('srid'):
|
||||
input_srid = int(wkt_m.group('srid'))
|
||||
g = self._from_wkt(force_bytes(wkt_m.group('wkt')))
|
||||
elif hex_regex.match(geo_input):
|
||||
# Handle HEXEWKB input.
|
||||
g = wkb_r().read(force_bytes(geo_input))
|
||||
elif json_regex.match(geo_input):
|
||||
# Handle GeoJSON input.
|
||||
ogr = gdal.OGRGeometry.from_json(geo_input)
|
||||
g = ogr._geos_ptr()
|
||||
input_srid = ogr.srid
|
||||
else:
|
||||
raise ValueError('String input unrecognized as WKT EWKT, and HEXEWKB.')
|
||||
elif isinstance(geo_input, GEOM_PTR):
|
||||
# When the input is a pointer to a geometry (GEOM_PTR).
|
||||
g = geo_input
|
||||
elif isinstance(geo_input, memoryview):
|
||||
# When the input is a buffer (WKB).
|
||||
g = wkb_r().read(geo_input)
|
||||
elif isinstance(geo_input, GEOSGeometry):
|
||||
g = capi.geom_clone(geo_input.ptr)
|
||||
else:
|
||||
raise TypeError('Improper geometry input type: %s' % type(geo_input))
|
||||
|
||||
if not g:
|
||||
raise GEOSException('Could not initialize GEOS Geometry with given input.')
|
||||
|
||||
input_srid = input_srid or capi.geos_get_srid(g) or None
|
||||
if input_srid and srid and input_srid != srid:
|
||||
raise ValueError('Input geometry already has SRID: %d.' % input_srid)
|
||||
|
||||
super().__init__(g, None)
|
||||
# Set the SRID, if given.
|
||||
srid = input_srid or srid
|
||||
if srid and isinstance(srid, int):
|
||||
self.srid = srid
|
||||
@@ -0,0 +1,175 @@
|
||||
"""
|
||||
This module houses the ctypes initialization procedures, as well
|
||||
as the notice and error handler function callbacks (get called
|
||||
when an error occurs in GEOS).
|
||||
|
||||
This module also houses GEOS Pointer utilities, including
|
||||
get_pointer_arr(), and GEOM_PTR.
|
||||
"""
|
||||
import logging
|
||||
import os
|
||||
from ctypes import CDLL, CFUNCTYPE, POINTER, Structure, c_char_p
|
||||
from ctypes.util import find_library
|
||||
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.utils.functional import SimpleLazyObject, cached_property
|
||||
from django.utils.version import get_version_tuple
|
||||
|
||||
logger = logging.getLogger('django.contrib.gis')
|
||||
|
||||
|
||||
def load_geos():
|
||||
# Custom library path set?
|
||||
try:
|
||||
from django.conf import settings
|
||||
lib_path = settings.GEOS_LIBRARY_PATH
|
||||
except (AttributeError, EnvironmentError,
|
||||
ImportError, ImproperlyConfigured):
|
||||
lib_path = None
|
||||
|
||||
# Setting the appropriate names for the GEOS-C library.
|
||||
if lib_path:
|
||||
lib_names = None
|
||||
elif os.name == 'nt':
|
||||
# Windows NT libraries
|
||||
lib_names = ['geos_c', 'libgeos_c-1']
|
||||
elif os.name == 'posix':
|
||||
# *NIX libraries
|
||||
lib_names = ['geos_c', 'GEOS']
|
||||
else:
|
||||
raise ImportError('Unsupported OS "%s"' % os.name)
|
||||
|
||||
# Using the ctypes `find_library` utility to find the path to the GEOS
|
||||
# shared library. This is better than manually specifying each library name
|
||||
# and extension (e.g., libgeos_c.[so|so.1|dylib].).
|
||||
if lib_names:
|
||||
for lib_name in lib_names:
|
||||
lib_path = find_library(lib_name)
|
||||
if lib_path is not None:
|
||||
break
|
||||
|
||||
# No GEOS library could be found.
|
||||
if lib_path is None:
|
||||
raise ImportError(
|
||||
'Could not find the GEOS library (tried "%s"). '
|
||||
'Try setting GEOS_LIBRARY_PATH in your settings.' %
|
||||
'", "'.join(lib_names)
|
||||
)
|
||||
# Getting the GEOS C library. The C interface (CDLL) is used for
|
||||
# both *NIX and Windows.
|
||||
# See the GEOS C API source code for more details on the library function calls:
|
||||
# http://geos.refractions.net/ro/doxygen_docs/html/geos__c_8h-source.html
|
||||
_lgeos = CDLL(lib_path)
|
||||
# Here we set up the prototypes for the initGEOS_r and finishGEOS_r
|
||||
# routines. These functions aren't actually called until they are
|
||||
# attached to a GEOS context handle -- this actually occurs in
|
||||
# geos/prototypes/threadsafe.py.
|
||||
_lgeos.initGEOS_r.restype = CONTEXT_PTR
|
||||
_lgeos.finishGEOS_r.argtypes = [CONTEXT_PTR]
|
||||
# Set restype for compatibility across 32 and 64-bit platforms.
|
||||
_lgeos.GEOSversion.restype = c_char_p
|
||||
return _lgeos
|
||||
|
||||
|
||||
# The notice and error handler C function callback definitions.
|
||||
# Supposed to mimic the GEOS message handler (C below):
|
||||
# typedef void (*GEOSMessageHandler)(const char *fmt, ...);
|
||||
NOTICEFUNC = CFUNCTYPE(None, c_char_p, c_char_p)
|
||||
|
||||
|
||||
def notice_h(fmt, lst):
|
||||
fmt, lst = fmt.decode(), lst.decode()
|
||||
try:
|
||||
warn_msg = fmt % lst
|
||||
except TypeError:
|
||||
warn_msg = fmt
|
||||
logger.warning('GEOS_NOTICE: %s\n', warn_msg)
|
||||
|
||||
|
||||
notice_h = NOTICEFUNC(notice_h)
|
||||
|
||||
ERRORFUNC = CFUNCTYPE(None, c_char_p, c_char_p)
|
||||
|
||||
|
||||
def error_h(fmt, lst):
|
||||
fmt, lst = fmt.decode(), lst.decode()
|
||||
try:
|
||||
err_msg = fmt % lst
|
||||
except TypeError:
|
||||
err_msg = fmt
|
||||
logger.error('GEOS_ERROR: %s\n', err_msg)
|
||||
|
||||
|
||||
error_h = ERRORFUNC(error_h)
|
||||
|
||||
# #### GEOS Geometry C data structures, and utility functions. ####
|
||||
|
||||
|
||||
# Opaque GEOS geometry structures, used for GEOM_PTR and CS_PTR
|
||||
class GEOSGeom_t(Structure):
|
||||
pass
|
||||
|
||||
|
||||
class GEOSPrepGeom_t(Structure):
|
||||
pass
|
||||
|
||||
|
||||
class GEOSCoordSeq_t(Structure):
|
||||
pass
|
||||
|
||||
|
||||
class GEOSContextHandle_t(Structure):
|
||||
pass
|
||||
|
||||
|
||||
# Pointers to opaque GEOS geometry structures.
|
||||
GEOM_PTR = POINTER(GEOSGeom_t)
|
||||
PREPGEOM_PTR = POINTER(GEOSPrepGeom_t)
|
||||
CS_PTR = POINTER(GEOSCoordSeq_t)
|
||||
CONTEXT_PTR = POINTER(GEOSContextHandle_t)
|
||||
|
||||
|
||||
lgeos = SimpleLazyObject(load_geos)
|
||||
|
||||
|
||||
class GEOSFuncFactory:
|
||||
"""
|
||||
Lazy loading of GEOS functions.
|
||||
"""
|
||||
argtypes = None
|
||||
restype = None
|
||||
errcheck = None
|
||||
|
||||
def __init__(self, func_name, *args, restype=None, errcheck=None, argtypes=None, **kwargs):
|
||||
self.func_name = func_name
|
||||
if restype is not None:
|
||||
self.restype = restype
|
||||
if errcheck is not None:
|
||||
self.errcheck = errcheck
|
||||
if argtypes is not None:
|
||||
self.argtypes = argtypes
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
return self.func(*args, **kwargs)
|
||||
|
||||
@cached_property
|
||||
def func(self):
|
||||
from django.contrib.gis.geos.prototypes.threadsafe import GEOSFunc
|
||||
func = GEOSFunc(self.func_name)
|
||||
func.argtypes = self.argtypes or []
|
||||
func.restype = self.restype
|
||||
if self.errcheck:
|
||||
func.errcheck = self.errcheck
|
||||
return func
|
||||
|
||||
|
||||
def geos_version():
|
||||
"""Return the string version of the GEOS library."""
|
||||
return lgeos.GEOSversion()
|
||||
|
||||
|
||||
def geos_version_tuple():
|
||||
"""Return the GEOS version as a tuple (major, minor, subminor)."""
|
||||
return get_version_tuple(geos_version().decode())
|
||||
@@ -0,0 +1,49 @@
|
||||
from .base import GEOSBase
|
||||
from .prototypes import prepared as capi
|
||||
|
||||
|
||||
class PreparedGeometry(GEOSBase):
|
||||
"""
|
||||
A geometry that is prepared for performing certain operations.
|
||||
At the moment this includes the contains covers, and intersects
|
||||
operations.
|
||||
"""
|
||||
ptr_type = capi.PREPGEOM_PTR
|
||||
destructor = capi.prepared_destroy
|
||||
|
||||
def __init__(self, geom):
|
||||
# Keeping a reference to the original geometry object to prevent it
|
||||
# from being garbage collected which could then crash the prepared one
|
||||
# See #21662
|
||||
self._base_geom = geom
|
||||
from .geometry import GEOSGeometry
|
||||
if not isinstance(geom, GEOSGeometry):
|
||||
raise TypeError
|
||||
self.ptr = capi.geos_prepare(geom.ptr)
|
||||
|
||||
def contains(self, other):
|
||||
return capi.prepared_contains(self.ptr, other.ptr)
|
||||
|
||||
def contains_properly(self, other):
|
||||
return capi.prepared_contains_properly(self.ptr, other.ptr)
|
||||
|
||||
def covers(self, other):
|
||||
return capi.prepared_covers(self.ptr, other.ptr)
|
||||
|
||||
def intersects(self, other):
|
||||
return capi.prepared_intersects(self.ptr, other.ptr)
|
||||
|
||||
def crosses(self, other):
|
||||
return capi.prepared_crosses(self.ptr, other.ptr)
|
||||
|
||||
def disjoint(self, other):
|
||||
return capi.prepared_disjoint(self.ptr, other.ptr)
|
||||
|
||||
def overlaps(self, other):
|
||||
return capi.prepared_overlaps(self.ptr, other.ptr)
|
||||
|
||||
def touches(self, other):
|
||||
return capi.prepared_touches(self.ptr, other.ptr)
|
||||
|
||||
def within(self, other):
|
||||
return capi.prepared_within(self.ptr, other.ptr)
|
||||
@@ -0,0 +1,91 @@
|
||||
from ctypes import POINTER, c_double, c_int, c_uint
|
||||
|
||||
from django.contrib.gis.geos.libgeos import CS_PTR, GEOM_PTR, GEOSFuncFactory
|
||||
from django.contrib.gis.geos.prototypes.errcheck import (
|
||||
GEOSException, last_arg_byref,
|
||||
)
|
||||
|
||||
|
||||
# ## Error-checking routines specific to coordinate sequences. ##
|
||||
def check_cs_op(result, func, cargs):
|
||||
"Check the status code of a coordinate sequence operation."
|
||||
if result == 0:
|
||||
raise GEOSException('Could not set value on coordinate sequence')
|
||||
else:
|
||||
return result
|
||||
|
||||
|
||||
def check_cs_get(result, func, cargs):
|
||||
"Check the coordinate sequence retrieval."
|
||||
check_cs_op(result, func, cargs)
|
||||
# Object in by reference, return its value.
|
||||
return last_arg_byref(cargs)
|
||||
|
||||
|
||||
# ## Coordinate sequence prototype factory classes. ##
|
||||
class CsInt(GEOSFuncFactory):
|
||||
"For coordinate sequence routines that return an integer."
|
||||
argtypes = [CS_PTR, POINTER(c_uint)]
|
||||
restype = c_int
|
||||
errcheck = staticmethod(check_cs_get)
|
||||
|
||||
|
||||
class CsOperation(GEOSFuncFactory):
|
||||
"For coordinate sequence operations."
|
||||
restype = c_int
|
||||
|
||||
def __init__(self, *args, ordinate=False, get=False, **kwargs):
|
||||
if get:
|
||||
# Get routines have double parameter passed-in by reference.
|
||||
errcheck = check_cs_get
|
||||
dbl_param = POINTER(c_double)
|
||||
else:
|
||||
errcheck = check_cs_op
|
||||
dbl_param = c_double
|
||||
|
||||
if ordinate:
|
||||
# Get/Set ordinate routines have an extra uint parameter.
|
||||
argtypes = [CS_PTR, c_uint, c_uint, dbl_param]
|
||||
else:
|
||||
argtypes = [CS_PTR, c_uint, dbl_param]
|
||||
|
||||
super().__init__(*args, **{**kwargs, 'errcheck': errcheck, 'argtypes': argtypes})
|
||||
|
||||
|
||||
class CsOutput(GEOSFuncFactory):
|
||||
restype = CS_PTR
|
||||
|
||||
@staticmethod
|
||||
def errcheck(result, func, cargs):
|
||||
if not result:
|
||||
raise GEOSException(
|
||||
'Error encountered checking Coordinate Sequence returned from GEOS '
|
||||
'C function "%s".' % func.__name__
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
# ## Coordinate Sequence ctypes prototypes ##
|
||||
|
||||
# Coordinate Sequence constructors & cloning.
|
||||
cs_clone = CsOutput('GEOSCoordSeq_clone', argtypes=[CS_PTR])
|
||||
create_cs = CsOutput('GEOSCoordSeq_create', argtypes=[c_uint, c_uint])
|
||||
get_cs = CsOutput('GEOSGeom_getCoordSeq', argtypes=[GEOM_PTR])
|
||||
|
||||
# Getting, setting ordinate
|
||||
cs_getordinate = CsOperation('GEOSCoordSeq_getOrdinate', ordinate=True, get=True)
|
||||
cs_setordinate = CsOperation('GEOSCoordSeq_setOrdinate', ordinate=True)
|
||||
|
||||
# For getting, x, y, z
|
||||
cs_getx = CsOperation('GEOSCoordSeq_getX', get=True)
|
||||
cs_gety = CsOperation('GEOSCoordSeq_getY', get=True)
|
||||
cs_getz = CsOperation('GEOSCoordSeq_getZ', get=True)
|
||||
|
||||
# For setting, x, y, z
|
||||
cs_setx = CsOperation('GEOSCoordSeq_setX')
|
||||
cs_sety = CsOperation('GEOSCoordSeq_setY')
|
||||
cs_setz = CsOperation('GEOSCoordSeq_setZ')
|
||||
|
||||
# These routines return size & dimensions.
|
||||
cs_getsize = CsInt('GEOSCoordSeq_getSize')
|
||||
cs_getdims = CsInt('GEOSCoordSeq_getDimensions')
|
||||
@@ -0,0 +1,53 @@
|
||||
"""
|
||||
This module houses the GEOS ctypes prototype functions for the
|
||||
topological operations on geometries.
|
||||
"""
|
||||
from ctypes import c_double, c_int
|
||||
|
||||
from django.contrib.gis.geos.libgeos import GEOM_PTR, GEOSFuncFactory
|
||||
from django.contrib.gis.geos.prototypes.errcheck import (
|
||||
check_geom, check_minus_one, check_string,
|
||||
)
|
||||
from django.contrib.gis.geos.prototypes.geom import geos_char_p
|
||||
|
||||
|
||||
class Topology(GEOSFuncFactory):
|
||||
"For GEOS unary topology functions."
|
||||
argtypes = [GEOM_PTR]
|
||||
restype = GEOM_PTR
|
||||
errcheck = staticmethod(check_geom)
|
||||
|
||||
|
||||
# Topology Routines
|
||||
geos_boundary = Topology('GEOSBoundary')
|
||||
geos_buffer = Topology('GEOSBuffer', argtypes=[GEOM_PTR, c_double, c_int])
|
||||
geos_bufferwithstyle = Topology('GEOSBufferWithStyle', argtypes=[GEOM_PTR, c_double, c_int, c_int, c_int, c_double])
|
||||
geos_centroid = Topology('GEOSGetCentroid')
|
||||
geos_convexhull = Topology('GEOSConvexHull')
|
||||
geos_difference = Topology('GEOSDifference', argtypes=[GEOM_PTR, GEOM_PTR])
|
||||
geos_envelope = Topology('GEOSEnvelope')
|
||||
geos_intersection = Topology('GEOSIntersection', argtypes=[GEOM_PTR, GEOM_PTR])
|
||||
geos_linemerge = Topology('GEOSLineMerge')
|
||||
geos_pointonsurface = Topology('GEOSPointOnSurface')
|
||||
geos_preservesimplify = Topology('GEOSTopologyPreserveSimplify', argtypes=[GEOM_PTR, c_double])
|
||||
geos_simplify = Topology('GEOSSimplify', argtypes=[GEOM_PTR, c_double])
|
||||
geos_symdifference = Topology('GEOSSymDifference', argtypes=[GEOM_PTR, GEOM_PTR])
|
||||
geos_union = Topology('GEOSUnion', argtypes=[GEOM_PTR, GEOM_PTR])
|
||||
|
||||
geos_unary_union = GEOSFuncFactory('GEOSUnaryUnion', argtypes=[GEOM_PTR], restype=GEOM_PTR)
|
||||
|
||||
# GEOSRelate returns a string, not a geometry.
|
||||
geos_relate = GEOSFuncFactory(
|
||||
'GEOSRelate', argtypes=[GEOM_PTR, GEOM_PTR], restype=geos_char_p, errcheck=check_string
|
||||
)
|
||||
|
||||
# Linear referencing routines
|
||||
geos_project = GEOSFuncFactory(
|
||||
'GEOSProject', argtypes=[GEOM_PTR, GEOM_PTR], restype=c_double, errcheck=check_minus_one
|
||||
)
|
||||
geos_interpolate = Topology('GEOSInterpolate', argtypes=[GEOM_PTR, c_double])
|
||||
|
||||
geos_project_normalized = GEOSFuncFactory(
|
||||
'GEOSProjectNormalized', argtypes=[GEOM_PTR, GEOM_PTR], restype=c_double, errcheck=check_minus_one
|
||||
)
|
||||
geos_interpolate_normalized = Topology('GEOSInterpolateNormalized', argtypes=[GEOM_PTR, c_double])
|
||||
Binary file not shown.
@@ -0,0 +1,90 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Bashar Al-Abdulhadi, 2015-2016
|
||||
# Bashar Al-Abdulhadi, 2014
|
||||
# Eyad Toma <d.eyad.t@gmail.com>, 2013
|
||||
# Jannis Leidel <jannis@leidel.info>, 2011
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-01-19 16:49+0100\n"
|
||||
"PO-Revision-Date: 2017-09-19 16:40+0000\n"
|
||||
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
|
||||
"Language-Team: Arabic (http://www.transifex.com/django/django/language/ar/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: ar\n"
|
||||
"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 "
|
||||
"&& n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n"
|
||||
|
||||
msgid "GIS"
|
||||
msgstr "نظم المعلومات الجغرافية GIS"
|
||||
|
||||
msgid "The base GIS field."
|
||||
msgstr "حقل نظم المعلومات الجغرافية الرئيسي"
|
||||
|
||||
msgid ""
|
||||
"The base Geometry field -- maps to the OpenGIS Specification Geometry type."
|
||||
msgstr ""
|
||||
"قاعدة الحقل الهندسي ترتكز لمواصفات نوع OpenGIS (نظم المعلومات الجغرافية "
|
||||
"المفتوحة) الهندسية."
|
||||
|
||||
msgid "Point"
|
||||
msgstr "نقطة إحداثية"
|
||||
|
||||
msgid "Line string"
|
||||
msgstr "سطر تسلسل أحرف"
|
||||
|
||||
msgid "Polygon"
|
||||
msgstr "مُضلّع إحداثي"
|
||||
|
||||
msgid "Multi-point"
|
||||
msgstr "نقاط إحداثية"
|
||||
|
||||
msgid "Multi-line string"
|
||||
msgstr "تسلسل أحرف متعدد الأسطر"
|
||||
|
||||
msgid "Multi polygon"
|
||||
msgstr "مجموعة مُضلعات إحداثية"
|
||||
|
||||
msgid "Geometry collection"
|
||||
msgstr "مجموعة إحداثية"
|
||||
|
||||
msgid "Extent Aggregate Field"
|
||||
msgstr "حقل مجموع التحصيل"
|
||||
|
||||
msgid "Raster Field"
|
||||
msgstr "حقل خطوط المسح التسامتي"
|
||||
|
||||
msgid "No geometry value provided."
|
||||
msgstr "لم تُدخل أي أحداثيات."
|
||||
|
||||
msgid "Invalid geometry value."
|
||||
msgstr "الإحداثيات غير صحيحة."
|
||||
|
||||
msgid "Invalid geometry type."
|
||||
msgstr "نوع الإحداثيات غير صحيح."
|
||||
|
||||
msgid ""
|
||||
"An error occurred when transforming the geometry to the SRID of the geometry "
|
||||
"form field."
|
||||
msgstr "حدث خطأ أثناء تحويل geometry إلى حقل SRID."
|
||||
|
||||
msgid "Delete all Features"
|
||||
msgstr "حذف جميع المميزات"
|
||||
|
||||
msgid "WKT debugging window:"
|
||||
msgstr "نافذة تدقيق WKT:"
|
||||
|
||||
msgid "Debugging window (serialized value)"
|
||||
msgstr "نافذة التدقيق (قيمة تسلسلية)"
|
||||
|
||||
msgid "No feeds are registered."
|
||||
msgstr "لا موجز مسجّل"
|
||||
|
||||
#, python-format
|
||||
msgid "Slug %r isn't registered."
|
||||
msgstr "Slug %r غير مسجّل"
|
||||
Binary file not shown.
@@ -0,0 +1,90 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Georgi Kostadinov <grgkostadinov@gmail.com>, 2012
|
||||
# Todor Lubenov <tgl.sysdev@gmail.com>, 2016
|
||||
# Todor Lubenov <tgl.sysdev@gmail.com>, 2011,2015
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-01-19 16:49+0100\n"
|
||||
"PO-Revision-Date: 2017-09-19 16:40+0000\n"
|
||||
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
|
||||
"Language-Team: Bulgarian (http://www.transifex.com/django/django/language/"
|
||||
"bg/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: bg\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "GIS"
|
||||
msgstr "GIS"
|
||||
|
||||
msgid "The base GIS field."
|
||||
msgstr "Базово ГИС поле."
|
||||
|
||||
msgid ""
|
||||
"The base Geometry field -- maps to the OpenGIS Specification Geometry type."
|
||||
msgstr ""
|
||||
"Основното ГИС поле - карти за спецификация от OpenGIS геометричен тип."
|
||||
|
||||
msgid "Point"
|
||||
msgstr "Точка"
|
||||
|
||||
msgid "Line string"
|
||||
msgstr "Линеен елемент"
|
||||
|
||||
msgid "Polygon"
|
||||
msgstr "Полигон"
|
||||
|
||||
msgid "Multi-point"
|
||||
msgstr "Комплексна-точка"
|
||||
|
||||
msgid "Multi-line string"
|
||||
msgstr "Комплексен-линеен елемент"
|
||||
|
||||
msgid "Multi polygon"
|
||||
msgstr "Комплексен полигон"
|
||||
|
||||
msgid "Geometry collection"
|
||||
msgstr "Геометрична колекция"
|
||||
|
||||
msgid "Extent Aggregate Field"
|
||||
msgstr "Разшири Полето за обединяване"
|
||||
|
||||
msgid "Raster Field"
|
||||
msgstr "Растерно Поле"
|
||||
|
||||
msgid "No geometry value provided."
|
||||
msgstr "Няма предоставена геометрична стойност."
|
||||
|
||||
msgid "Invalid geometry value."
|
||||
msgstr "Невалидна геометрична стойност."
|
||||
|
||||
msgid "Invalid geometry type."
|
||||
msgstr "Невалиден геометричен тип."
|
||||
|
||||
msgid ""
|
||||
"An error occurred when transforming the geometry to the SRID of the geometry "
|
||||
"form field."
|
||||
msgstr ""
|
||||
"Възникна грешка при трансформиране на геометрията на SRID от полето "
|
||||
"геометрия."
|
||||
|
||||
msgid "Delete all Features"
|
||||
msgstr "Изтрий всички обекти"
|
||||
|
||||
msgid "WKT debugging window:"
|
||||
msgstr "WKT прозорец за проверка:"
|
||||
|
||||
msgid "Debugging window (serialized value)"
|
||||
msgstr "Debugging прозорец (сериализирана стойност)"
|
||||
|
||||
msgid "No feeds are registered."
|
||||
msgstr "Няма регистрирани фийда."
|
||||
|
||||
#, python-format
|
||||
msgid "Slug %r isn't registered."
|
||||
msgstr "Слъгът %r не е регистриран."
|
||||
Binary file not shown.
@@ -0,0 +1,93 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Anastasiadis Stavros <anastasiadis.st00@gmail.com>, 2014
|
||||
# Dimitris Glezos <glezos@transifex.com>, 2011
|
||||
# Elena Andreou <helenaandreou.ha@gmail.com>, 2016
|
||||
# Kostas Papadimitriou <vinilios@gmail.com>, 2012
|
||||
# Nick Mavrakis <mavrakis.n@gmail.com>, 2016
|
||||
# Pãnoș <panos.laganakos@gmail.com>, 2016
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-01-19 16:49+0100\n"
|
||||
"PO-Revision-Date: 2017-09-23 18:54+0000\n"
|
||||
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
|
||||
"Language-Team: Greek (http://www.transifex.com/django/django/language/el/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: el\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "GIS"
|
||||
msgstr " Γεωγραφικά Συστήματα Πληροφορίας"
|
||||
|
||||
msgid "The base GIS field."
|
||||
msgstr "Το βασικό GIS πεδίο."
|
||||
|
||||
msgid ""
|
||||
"The base Geometry field -- maps to the OpenGIS Specification Geometry type."
|
||||
msgstr ""
|
||||
"Το βασικό Γεωμετρικό πεδίο -- αντιστοιχεί στον τύπο της Γεωμετρικής "
|
||||
"Προδιαγραφής OpenGIS."
|
||||
|
||||
msgid "Point"
|
||||
msgstr "Σημείο"
|
||||
|
||||
msgid "Line string"
|
||||
msgstr "Γραμμή string"
|
||||
|
||||
msgid "Polygon"
|
||||
msgstr "Πολύγωνο"
|
||||
|
||||
msgid "Multi-point"
|
||||
msgstr "Πολλαπλό σημείο"
|
||||
|
||||
msgid "Multi-line string"
|
||||
msgstr "Multi-line string"
|
||||
|
||||
msgid "Multi polygon"
|
||||
msgstr "Πολλαπλό πολύγωνο"
|
||||
|
||||
msgid "Geometry collection"
|
||||
msgstr "Συλλογή γεωμετριών"
|
||||
|
||||
msgid "Extent Aggregate Field"
|
||||
msgstr "Πεδίο Extent Aggregate"
|
||||
|
||||
msgid "Raster Field"
|
||||
msgstr "Πεδίο Raster"
|
||||
|
||||
msgid "No geometry value provided."
|
||||
msgstr "Δε δόθηκε τιμή γεωμετρίας."
|
||||
|
||||
msgid "Invalid geometry value."
|
||||
msgstr "Άκυρη γεωμετρική τιμή."
|
||||
|
||||
msgid "Invalid geometry type."
|
||||
msgstr "Άκυρος γεωμετρικός τύπος."
|
||||
|
||||
msgid ""
|
||||
"An error occurred when transforming the geometry to the SRID of the geometry "
|
||||
"form field."
|
||||
msgstr ""
|
||||
"Παρουσιάστηκε σφάλμα κατά τη μετατροπή της γεωμετρίας στο SRID του πεδίου "
|
||||
"της φόρμας γεωμετρίας."
|
||||
|
||||
msgid "Delete all Features"
|
||||
msgstr "Διαγραφή όλων των αντικειμένων"
|
||||
|
||||
msgid "WKT debugging window:"
|
||||
msgstr "Παράθυρο αποσφαλμάτωσης WKT"
|
||||
|
||||
msgid "Debugging window (serialized value)"
|
||||
msgstr "Παράθυρο αποσφαλμάτωσης (σειριακή τιμή)"
|
||||
|
||||
msgid "No feeds are registered."
|
||||
msgstr "Δεν υπάρχουν εγγεγραμμένες ροές ειδήσεων."
|
||||
|
||||
#, python-format
|
||||
msgid "Slug %r isn't registered."
|
||||
msgstr "Το slug %r δεν έχει καταχωρηθεί."
|
||||
Binary file not shown.
@@ -0,0 +1,93 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Antoni Aloy <aaloy@apsl.net>, 2012,2015-2016
|
||||
# Ernesto Avilés Vázquez <whippiii@gmail.com>, 2015
|
||||
# Igor Támara <igor@tamarapatino.org>, 2015
|
||||
# Jannis Leidel <jannis@leidel.info>, 2011
|
||||
# Josue Naaman Nistal Guerra <josuenistal@hotmail.com>, 2014
|
||||
# Marc Garcia <garcia.marc@gmail.com>, 2011
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-01-19 16:49+0100\n"
|
||||
"PO-Revision-Date: 2017-09-19 16:40+0000\n"
|
||||
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
|
||||
"Language-Team: Spanish (http://www.transifex.com/django/django/language/"
|
||||
"es/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: es\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "GIS"
|
||||
msgstr "GIS"
|
||||
|
||||
msgid "The base GIS field."
|
||||
msgstr "El campo GIS base."
|
||||
|
||||
msgid ""
|
||||
"The base Geometry field -- maps to the OpenGIS Specification Geometry type."
|
||||
msgstr ""
|
||||
"El campo base Geometry -- casa con el tipo OpenGis Specification Geometry"
|
||||
|
||||
msgid "Point"
|
||||
msgstr "Punto"
|
||||
|
||||
msgid "Line string"
|
||||
msgstr "Cadena de línea"
|
||||
|
||||
msgid "Polygon"
|
||||
msgstr "Polígono"
|
||||
|
||||
msgid "Multi-point"
|
||||
msgstr "Punto múltiple"
|
||||
|
||||
msgid "Multi-line string"
|
||||
msgstr "Cadena de línea múltiple"
|
||||
|
||||
msgid "Multi polygon"
|
||||
msgstr "Polígono múltiple"
|
||||
|
||||
msgid "Geometry collection"
|
||||
msgstr "Colección de \"Geometry\""
|
||||
|
||||
msgid "Extent Aggregate Field"
|
||||
msgstr "Extensión de campo agregado"
|
||||
|
||||
msgid "Raster Field"
|
||||
msgstr "Campo Raster"
|
||||
|
||||
msgid "No geometry value provided."
|
||||
msgstr "No se indico ningún valor de geometría."
|
||||
|
||||
msgid "Invalid geometry value."
|
||||
msgstr "Valor de geometría inválido."
|
||||
|
||||
msgid "Invalid geometry type."
|
||||
msgstr "Tipo de geometría inválido."
|
||||
|
||||
msgid ""
|
||||
"An error occurred when transforming the geometry to the SRID of the geometry "
|
||||
"form field."
|
||||
msgstr ""
|
||||
"Ocurrió un error al transformar la geometria al SRID de la geometria del "
|
||||
"campo de formulario."
|
||||
|
||||
msgid "Delete all Features"
|
||||
msgstr "Borrar todos los elementos"
|
||||
|
||||
msgid "WKT debugging window:"
|
||||
msgstr "Ventana de depuración WKT"
|
||||
|
||||
msgid "Debugging window (serialized value)"
|
||||
msgstr "Ventana de depuración (valores serializados)"
|
||||
|
||||
msgid "No feeds are registered."
|
||||
msgstr "No se han registrado canales de contenido."
|
||||
|
||||
#, python-format
|
||||
msgid "Slug %r isn't registered."
|
||||
msgstr "El slug %r no está registrado."
|
||||
BIN
Binary file not shown.
+87
@@ -0,0 +1,87 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Abraham Estrada, 2011-2012
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-01-19 16:49+0100\n"
|
||||
"PO-Revision-Date: 2017-09-19 16:40+0000\n"
|
||||
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
|
||||
"Language-Team: Spanish (Mexico) (http://www.transifex.com/django/django/"
|
||||
"language/es_MX/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: es_MX\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "GIS"
|
||||
msgstr ""
|
||||
|
||||
msgid "The base GIS field."
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"The base Geometry field -- maps to the OpenGIS Specification Geometry type."
|
||||
msgstr ""
|
||||
|
||||
msgid "Point"
|
||||
msgstr "Punto"
|
||||
|
||||
msgid "Line string"
|
||||
msgstr "Secuencia de líneas"
|
||||
|
||||
msgid "Polygon"
|
||||
msgstr "Polígono"
|
||||
|
||||
msgid "Multi-point"
|
||||
msgstr "Multi-punto"
|
||||
|
||||
msgid "Multi-line string"
|
||||
msgstr "Cadena multi-línea"
|
||||
|
||||
msgid "Multi polygon"
|
||||
msgstr "Multi polígonos"
|
||||
|
||||
msgid "Geometry collection"
|
||||
msgstr "Colección de geometrías"
|
||||
|
||||
msgid "Extent Aggregate Field"
|
||||
msgstr ""
|
||||
|
||||
msgid "Raster Field"
|
||||
msgstr ""
|
||||
|
||||
msgid "No geometry value provided."
|
||||
msgstr "No se ha proporcionado un valor de geometría."
|
||||
|
||||
msgid "Invalid geometry value."
|
||||
msgstr "Valor de geometría no válido."
|
||||
|
||||
msgid "Invalid geometry type."
|
||||
msgstr "Tipo de geometría no válido."
|
||||
|
||||
msgid ""
|
||||
"An error occurred when transforming the geometry to the SRID of the geometry "
|
||||
"form field."
|
||||
msgstr ""
|
||||
"Ha ocurrido un error mientras se transformaba la geometría al SRID del campo "
|
||||
"de formulario de la misma."
|
||||
|
||||
msgid "Delete all Features"
|
||||
msgstr ""
|
||||
|
||||
msgid "WKT debugging window:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Debugging window (serialized value)"
|
||||
msgstr ""
|
||||
|
||||
msgid "No feeds are registered."
|
||||
msgstr "No hay feeds registrados."
|
||||
|
||||
#, python-format
|
||||
msgid "Slug %r isn't registered."
|
||||
msgstr "El slug %r no está registrado."
|
||||
@@ -0,0 +1,90 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Ali Nikneshan <ali@nikneshan.com>, 2012-2013
|
||||
# Alireza Savand <alireza.savand@gmail.com>, 2013
|
||||
# Ali Vakilzade <ali.vakilzade@gmail.com>, 2015
|
||||
# Jannis Leidel <jannis@leidel.info>, 2011
|
||||
# Pouya Abbassi, 2016
|
||||
# Saeed <sd.javadi@gmail.com>, 2011
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-01-19 16:49+0100\n"
|
||||
"PO-Revision-Date: 2017-09-23 18:54+0000\n"
|
||||
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
|
||||
"Language-Team: Persian (http://www.transifex.com/django/django/language/"
|
||||
"fa/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: fa\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
|
||||
msgid "GIS"
|
||||
msgstr "جیآیاس"
|
||||
|
||||
msgid "The base GIS field."
|
||||
msgstr "پایهی سیستم اطلاعات جغرافیایی میدانی."
|
||||
|
||||
msgid ""
|
||||
"The base Geometry field -- maps to the OpenGIS Specification Geometry type."
|
||||
msgstr "پایهی فیلد هندسی --نوع خاص هندسهی نقشههای نوع OpenGIS"
|
||||
|
||||
msgid "Point"
|
||||
msgstr "نقطه"
|
||||
|
||||
msgid "Line string"
|
||||
msgstr "رشته خط"
|
||||
|
||||
msgid "Polygon"
|
||||
msgstr "چندضلعی"
|
||||
|
||||
msgid "Multi-point"
|
||||
msgstr "چند نقطه ای"
|
||||
|
||||
msgid "Multi-line string"
|
||||
msgstr "چند خط رشته"
|
||||
|
||||
msgid "Multi polygon"
|
||||
msgstr "چندین چند ضلعی "
|
||||
|
||||
msgid "Geometry collection"
|
||||
msgstr "مجموعه هندسی"
|
||||
|
||||
msgid "Extent Aggregate Field"
|
||||
msgstr "تراکم وسعت حوزهی "
|
||||
|
||||
msgid "Raster Field"
|
||||
msgstr "حوزهی شطرنجی"
|
||||
|
||||
msgid "No geometry value provided."
|
||||
msgstr "مقدار جغرافیاییای مقرر نشده است."
|
||||
|
||||
msgid "Invalid geometry value."
|
||||
msgstr "مقدار جغرافیایی نامعتبر"
|
||||
|
||||
msgid "Invalid geometry type."
|
||||
msgstr "نوعِ جغرافیایی نامعتبر"
|
||||
|
||||
msgid ""
|
||||
"An error occurred when transforming the geometry to the SRID of the geometry "
|
||||
"form field."
|
||||
msgstr "مشکلی در هنگام انتقال مختصات هندسی از فیلد به SRID رخ داد."
|
||||
|
||||
msgid "Delete all Features"
|
||||
msgstr "حذف همهی اشیاء"
|
||||
|
||||
msgid "WKT debugging window:"
|
||||
msgstr "پنجرهی اشکال زدایی «متن قابل درک»"
|
||||
|
||||
msgid "Debugging window (serialized value)"
|
||||
msgstr "پنجرهی اشکال زدایی (ارزش متوالی)"
|
||||
|
||||
msgid "No feeds are registered."
|
||||
msgstr "هیچ فیدی ثبت شده است."
|
||||
|
||||
#, python-format
|
||||
msgid "Slug %r isn't registered."
|
||||
msgstr "Slug %r ثبت نشده"
|
||||
Binary file not shown.
@@ -0,0 +1,90 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Claude Paroz <claude@2xlibre.net>, 2014-2015,2017
|
||||
# Claude Paroz <claude@2xlibre.net>, 2012
|
||||
# Jannis Leidel <jannis@leidel.info>, 2011
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-01-19 16:49+0100\n"
|
||||
"PO-Revision-Date: 2017-09-19 16:40+0000\n"
|
||||
"Last-Translator: Claude Paroz <claude@2xlibre.net>\n"
|
||||
"Language-Team: French (http://www.transifex.com/django/django/language/fr/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: fr\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
|
||||
msgid "GIS"
|
||||
msgstr "SIG"
|
||||
|
||||
msgid "The base GIS field."
|
||||
msgstr "Le champ SIG de base."
|
||||
|
||||
msgid ""
|
||||
"The base Geometry field -- maps to the OpenGIS Specification Geometry type."
|
||||
msgstr ""
|
||||
"Le champ géométrique de base, correspond au type Geometry de la "
|
||||
"spécification OpenGIS."
|
||||
|
||||
msgid "Point"
|
||||
msgstr "Point"
|
||||
|
||||
msgid "Line string"
|
||||
msgstr "Chaîne de segment"
|
||||
|
||||
msgid "Polygon"
|
||||
msgstr "Polygone"
|
||||
|
||||
msgid "Multi-point"
|
||||
msgstr "Multipoint"
|
||||
|
||||
msgid "Multi-line string"
|
||||
msgstr "Chaîne multisegment"
|
||||
|
||||
msgid "Multi polygon"
|
||||
msgstr "Multipolygone"
|
||||
|
||||
msgid "Geometry collection"
|
||||
msgstr "Collection géométrique"
|
||||
|
||||
msgid "Extent Aggregate Field"
|
||||
msgstr "Champ d'agrégation d'étendue"
|
||||
|
||||
msgid "Raster Field"
|
||||
msgstr "Champ matriciel"
|
||||
|
||||
msgid "No geometry value provided."
|
||||
msgstr "Aucune valeur géométrique fournie."
|
||||
|
||||
msgid "Invalid geometry value."
|
||||
msgstr "Valeur géométrique non valide."
|
||||
|
||||
msgid "Invalid geometry type."
|
||||
msgstr "Type de géométrie non valide."
|
||||
|
||||
msgid ""
|
||||
"An error occurred when transforming the geometry to the SRID of the geometry "
|
||||
"form field."
|
||||
msgstr ""
|
||||
"Une erreur est survenue lors de la transformation de l'objet géométrique "
|
||||
"dans le SRID du champ de formulaire géométrique."
|
||||
|
||||
msgid "Delete all Features"
|
||||
msgstr "Supprimer toutes les localisations"
|
||||
|
||||
msgid "WKT debugging window:"
|
||||
msgstr "Fenêtre de débogage WKT :"
|
||||
|
||||
msgid "Debugging window (serialized value)"
|
||||
msgstr "Fenêtre de débogage (valeur sérialisée)"
|
||||
|
||||
msgid "No feeds are registered."
|
||||
msgstr "Aucun flux enregistré."
|
||||
|
||||
#, python-format
|
||||
msgid "Slug %r isn't registered."
|
||||
msgstr "Le slug %r n'est pas enregistré."
|
||||
Binary file not shown.
@@ -0,0 +1,80 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-03-18 09:16+0100\n"
|
||||
"PO-Revision-Date: 2015-03-18 08:35+0000\n"
|
||||
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
|
||||
"Language-Team: Western Frisian (http://www.transifex.com/projects/p/django/"
|
||||
"language/fy/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: fy\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "GIS"
|
||||
msgstr ""
|
||||
|
||||
msgid "The base GIS field -- maps to the OpenGIS Specification Geometry type."
|
||||
msgstr ""
|
||||
|
||||
msgid "Point"
|
||||
msgstr ""
|
||||
|
||||
msgid "Line string"
|
||||
msgstr ""
|
||||
|
||||
msgid "Polygon"
|
||||
msgstr ""
|
||||
|
||||
msgid "Multi-point"
|
||||
msgstr ""
|
||||
|
||||
msgid "Multi-line string"
|
||||
msgstr ""
|
||||
|
||||
msgid "Multi polygon"
|
||||
msgstr ""
|
||||
|
||||
msgid "Geometry collection"
|
||||
msgstr ""
|
||||
|
||||
msgid "Extent Aggregate Field"
|
||||
msgstr ""
|
||||
|
||||
msgid "No geometry value provided."
|
||||
msgstr ""
|
||||
|
||||
msgid "Invalid geometry value."
|
||||
msgstr ""
|
||||
|
||||
msgid "Invalid geometry type."
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"An error occurred when transforming the geometry to the SRID of the geometry "
|
||||
"form field."
|
||||
msgstr ""
|
||||
|
||||
msgid "Delete all Features"
|
||||
msgstr ""
|
||||
|
||||
msgid "WKT debugging window:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Google Maps via GeoDjango"
|
||||
msgstr ""
|
||||
|
||||
msgid "Debugging window (serialized value)"
|
||||
msgstr ""
|
||||
|
||||
msgid "No feeds are registered."
|
||||
msgstr ""
|
||||
|
||||
#, python-format
|
||||
msgid "Slug %r isn't registered."
|
||||
msgstr ""
|
||||
@@ -0,0 +1,88 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Jannis Leidel <jannis@leidel.info>, 2011
|
||||
# Michael Thornhill <michael@maithu.com>, 2012
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-01-19 16:49+0100\n"
|
||||
"PO-Revision-Date: 2017-09-23 18:54+0000\n"
|
||||
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
|
||||
"Language-Team: Irish (http://www.transifex.com/django/django/language/ga/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: ga\n"
|
||||
"Plural-Forms: nplurals=5; plural=(n==1 ? 0 : n==2 ? 1 : n<7 ? 2 : n<11 ? 3 : "
|
||||
"4);\n"
|
||||
|
||||
msgid "GIS"
|
||||
msgstr ""
|
||||
|
||||
msgid "The base GIS field."
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"The base Geometry field -- maps to the OpenGIS Specification Geometry type."
|
||||
msgstr ""
|
||||
|
||||
msgid "Point"
|
||||
msgstr "Pointe"
|
||||
|
||||
msgid "Line string"
|
||||
msgstr "Líne teaghrán"
|
||||
|
||||
msgid "Polygon"
|
||||
msgstr "Polagán"
|
||||
|
||||
msgid "Multi-point"
|
||||
msgstr "Il-phointe"
|
||||
|
||||
msgid "Multi-line string"
|
||||
msgstr "Il-líne teaghrán"
|
||||
|
||||
msgid "Multi polygon"
|
||||
msgstr "Il polagán"
|
||||
|
||||
msgid "Geometry collection"
|
||||
msgstr "Céimseata bhailiú"
|
||||
|
||||
msgid "Extent Aggregate Field"
|
||||
msgstr ""
|
||||
|
||||
msgid "Raster Field"
|
||||
msgstr ""
|
||||
|
||||
msgid "No geometry value provided."
|
||||
msgstr "Ní soláthair méid geoiméadracht"
|
||||
|
||||
msgid "Invalid geometry value."
|
||||
msgstr "Méid geoiméadracht neamhbhailí"
|
||||
|
||||
msgid "Invalid geometry type."
|
||||
msgstr "Tíopa geoiméadracht neamhbhailí"
|
||||
|
||||
msgid ""
|
||||
"An error occurred when transforming the geometry to the SRID of the geometry "
|
||||
"form field."
|
||||
msgstr ""
|
||||
"Tharla earráid ag claochlú an geoiméadracht go dtí SRID an réimse fhoirm "
|
||||
"geoiméadracht."
|
||||
|
||||
msgid "Delete all Features"
|
||||
msgstr ""
|
||||
|
||||
msgid "WKT debugging window:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Debugging window (serialized value)"
|
||||
msgstr ""
|
||||
|
||||
msgid "No feeds are registered."
|
||||
msgstr "Níl fothaí cláraithe."
|
||||
|
||||
#, python-format
|
||||
msgid "Slug %r isn't registered."
|
||||
msgstr "Níl slug %r cláraithe."
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,91 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# berislavlopac <berislav.lopac@gmail.com>, 2012
|
||||
# Davor Lučić <r.dav.lc@gmail.com>, 2012
|
||||
# Filip Cuk <filipcuk2@gmail.com>, 2016
|
||||
# Jannis Leidel <jannis@leidel.info>, 2011
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-01-19 16:49+0100\n"
|
||||
"PO-Revision-Date: 2017-09-19 16:40+0000\n"
|
||||
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
|
||||
"Language-Team: Croatian (http://www.transifex.com/django/django/language/"
|
||||
"hr/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: hr\n"
|
||||
"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
|
||||
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
|
||||
|
||||
msgid "GIS"
|
||||
msgstr ""
|
||||
|
||||
msgid "The base GIS field."
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"The base Geometry field -- maps to the OpenGIS Specification Geometry type."
|
||||
msgstr ""
|
||||
|
||||
msgid "Point"
|
||||
msgstr "Točka"
|
||||
|
||||
msgid "Line string"
|
||||
msgstr "Linija (Line string)"
|
||||
|
||||
msgid "Polygon"
|
||||
msgstr "Poligon"
|
||||
|
||||
msgid "Multi-point"
|
||||
msgstr "Više točaka"
|
||||
|
||||
msgid "Multi-line string"
|
||||
msgstr "Više linija (Line string)"
|
||||
|
||||
msgid "Multi polygon"
|
||||
msgstr "Više poligona"
|
||||
|
||||
msgid "Geometry collection"
|
||||
msgstr "Geometrijska kolekcija"
|
||||
|
||||
msgid "Extent Aggregate Field"
|
||||
msgstr ""
|
||||
|
||||
msgid "Raster Field"
|
||||
msgstr ""
|
||||
|
||||
msgid "No geometry value provided."
|
||||
msgstr "Geometrijska vrijednost nije priložena."
|
||||
|
||||
msgid "Invalid geometry value."
|
||||
msgstr "Neispravna geometrijska vrijednost."
|
||||
|
||||
msgid "Invalid geometry type."
|
||||
msgstr "Neispravan geometrijski tip."
|
||||
|
||||
msgid ""
|
||||
"An error occurred when transforming the geometry to the SRID of the geometry "
|
||||
"form field."
|
||||
msgstr ""
|
||||
"Došlo je do greške pri transformaciji geometrije na SRID geometrijskog polja "
|
||||
"forme."
|
||||
|
||||
msgid "Delete all Features"
|
||||
msgstr "Izbriši sve značajke"
|
||||
|
||||
msgid "WKT debugging window:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Debugging window (serialized value)"
|
||||
msgstr ""
|
||||
|
||||
msgid "No feeds are registered."
|
||||
msgstr "Nema registriranih izvora."
|
||||
|
||||
#, python-format
|
||||
msgid "Slug %r isn't registered."
|
||||
msgstr "Slug %r nije registriran."
|
||||
Binary file not shown.
@@ -0,0 +1,90 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Michael Wolf <milupo@sorbzilla.de>, 2016
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-01-19 16:49+0100\n"
|
||||
"PO-Revision-Date: 2017-09-23 00:02+0000\n"
|
||||
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
|
||||
"Language-Team: Upper Sorbian (http://www.transifex.com/django/django/"
|
||||
"language/hsb/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: hsb\n"
|
||||
"Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n"
|
||||
"%100==4 ? 2 : 3);\n"
|
||||
|
||||
msgid "GIS"
|
||||
msgstr "GIS"
|
||||
|
||||
msgid "The base GIS field."
|
||||
msgstr "Bazowe polo GIS."
|
||||
|
||||
msgid ""
|
||||
"The base Geometry field -- maps to the OpenGIS Specification Geometry type."
|
||||
msgstr ""
|
||||
"Bazowe geometrijowe polo -- zarysowane do geometrijoweho typa specifikacije "
|
||||
"OpenGIS. "
|
||||
|
||||
msgid "Point"
|
||||
msgstr "Dypk"
|
||||
|
||||
msgid "Line string"
|
||||
msgstr "Čara"
|
||||
|
||||
msgid "Polygon"
|
||||
msgstr "Wjeleróžk"
|
||||
|
||||
msgid "Multi-point"
|
||||
msgstr "Wjacore dypki"
|
||||
|
||||
msgid "Multi-line string"
|
||||
msgstr "Wjacore čary"
|
||||
|
||||
msgid "Multi polygon"
|
||||
msgstr "Wjacore wjelróžki"
|
||||
|
||||
msgid "Geometry collection"
|
||||
msgstr "Geometrijowa zběrka"
|
||||
|
||||
msgid "Extent Aggregate Field"
|
||||
msgstr "Polo „extent aggregate“"
|
||||
|
||||
msgid "Raster Field"
|
||||
msgstr "Rasterowe polo"
|
||||
|
||||
msgid "No geometry value provided."
|
||||
msgstr "Žana geometrijowa hódnota podata."
|
||||
|
||||
msgid "Invalid geometry value."
|
||||
msgstr "Njepłaćiwa geometrijowa hódnota."
|
||||
|
||||
msgid "Invalid geometry type."
|
||||
msgstr "Njepłaćiwy geometrijowy typ."
|
||||
|
||||
msgid ""
|
||||
"An error occurred when transforming the geometry to the SRID of the geometry "
|
||||
"form field."
|
||||
msgstr ""
|
||||
"Zmylk je wustupił, hdyž so geometrija do SRID pola geometrijoweho formulara "
|
||||
"přetwori."
|
||||
|
||||
msgid "Delete all Features"
|
||||
msgstr "Wšě funkcije zhašeć"
|
||||
|
||||
msgid "WKT debugging window:"
|
||||
msgstr "Wokno pytanja zmylkow WKT:"
|
||||
|
||||
msgid "Debugging window (serialized value)"
|
||||
msgstr "Wokno pytanja zmylkow (serijalizowana hódnota)"
|
||||
|
||||
msgid "No feeds are registered."
|
||||
msgstr "Žane kanale zregistrowane."
|
||||
|
||||
#, python-format
|
||||
msgid "Slug %r isn't registered."
|
||||
msgstr "Adresowe mjeno %r njeje zregistrowane."
|
||||
@@ -0,0 +1,90 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# András Veres-Szentkirályi, 2016
|
||||
# Jannis Leidel <jannis@leidel.info>, 2011
|
||||
# Kristóf Gruber <>, 2012
|
||||
# Szilveszter Farkas <szilveszter.farkas@gmail.com>, 2011
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-01-19 16:49+0100\n"
|
||||
"PO-Revision-Date: 2017-09-23 18:54+0000\n"
|
||||
"Last-Translator: János R (Hangya)\n"
|
||||
"Language-Team: Hungarian (http://www.transifex.com/django/django/language/"
|
||||
"hu/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: hu\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "GIS"
|
||||
msgstr "GIS"
|
||||
|
||||
msgid "The base GIS field."
|
||||
msgstr "Az alap GIS mező."
|
||||
|
||||
msgid ""
|
||||
"The base Geometry field -- maps to the OpenGIS Specification Geometry type."
|
||||
msgstr ""
|
||||
"Az alap Geometry mező -- az OpenGIS specifikáció geometria típusának "
|
||||
"megfelelően"
|
||||
|
||||
msgid "Point"
|
||||
msgstr "Pont"
|
||||
|
||||
msgid "Line string"
|
||||
msgstr "Vonallánc"
|
||||
|
||||
msgid "Polygon"
|
||||
msgstr "Poligon"
|
||||
|
||||
msgid "Multi-point"
|
||||
msgstr "Multi-pont"
|
||||
|
||||
msgid "Multi-line string"
|
||||
msgstr "Többes vonallánc"
|
||||
|
||||
msgid "Multi polygon"
|
||||
msgstr "Multi-poligon"
|
||||
|
||||
msgid "Geometry collection"
|
||||
msgstr "Geometria gyűjtemény"
|
||||
|
||||
msgid "Extent Aggregate Field"
|
||||
msgstr "Terület összegző mező"
|
||||
|
||||
msgid "Raster Field"
|
||||
msgstr "Raszter mező"
|
||||
|
||||
msgid "No geometry value provided."
|
||||
msgstr "Geometriai adat nem került megadásra."
|
||||
|
||||
msgid "Invalid geometry value."
|
||||
msgstr "Érvénytelen geometriai érték."
|
||||
|
||||
msgid "Invalid geometry type."
|
||||
msgstr "Érvénytelen geometriai típus."
|
||||
|
||||
msgid ""
|
||||
"An error occurred when transforming the geometry to the SRID of the geometry "
|
||||
"form field."
|
||||
msgstr "Hiba történt a geometriai transzformáció során."
|
||||
|
||||
msgid "Delete all Features"
|
||||
msgstr "Minden Feature törlése"
|
||||
|
||||
msgid "WKT debugging window:"
|
||||
msgstr "WKT debug ablak:"
|
||||
|
||||
msgid "Debugging window (serialized value)"
|
||||
msgstr "Debug ablak (szerializált érték)"
|
||||
|
||||
msgid "No feeds are registered."
|
||||
msgstr "Nincs regisztrált feed."
|
||||
|
||||
#, python-format
|
||||
msgid "Slug %r isn't registered."
|
||||
msgstr "%r slug nincs regisztrálva."
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,89 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Martijn Dekker <mcdutchie@hotmail.com>, 2012,2016
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-01-19 16:49+0100\n"
|
||||
"PO-Revision-Date: 2017-09-19 16:40+0000\n"
|
||||
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
|
||||
"Language-Team: Interlingua (http://www.transifex.com/django/django/language/"
|
||||
"ia/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: ia\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "GIS"
|
||||
msgstr ""
|
||||
|
||||
msgid "The base GIS field."
|
||||
msgstr "Le campo GIS de base."
|
||||
|
||||
msgid ""
|
||||
"The base Geometry field -- maps to the OpenGIS Specification Geometry type."
|
||||
msgstr ""
|
||||
"Le campo Geometry de base. Corresponde al typo geometric del specification "
|
||||
"OpenGIS."
|
||||
|
||||
msgid "Point"
|
||||
msgstr "Puncto"
|
||||
|
||||
msgid "Line string"
|
||||
msgstr "Texto de linea"
|
||||
|
||||
msgid "Polygon"
|
||||
msgstr "Polygono"
|
||||
|
||||
msgid "Multi-point"
|
||||
msgstr "Plure punctos"
|
||||
|
||||
msgid "Multi-line string"
|
||||
msgstr "Texto con plure lineas"
|
||||
|
||||
msgid "Multi polygon"
|
||||
msgstr "Plure polygonos"
|
||||
|
||||
msgid "Geometry collection"
|
||||
msgstr "Collection geometric"
|
||||
|
||||
msgid "Extent Aggregate Field"
|
||||
msgstr ""
|
||||
|
||||
msgid "Raster Field"
|
||||
msgstr ""
|
||||
|
||||
msgid "No geometry value provided."
|
||||
msgstr "Nulle valor geometric fornite."
|
||||
|
||||
msgid "Invalid geometry value."
|
||||
msgstr "Valor geometric invalide."
|
||||
|
||||
msgid "Invalid geometry type."
|
||||
msgstr "Typo geometric invalide."
|
||||
|
||||
msgid ""
|
||||
"An error occurred when transforming the geometry to the SRID of the geometry "
|
||||
"form field."
|
||||
msgstr ""
|
||||
"Un error occurreva durante le transformation del geometria al SRID del campo "
|
||||
"del formulario geometric."
|
||||
|
||||
msgid "Delete all Features"
|
||||
msgstr "Deler tote le elementos"
|
||||
|
||||
msgid "WKT debugging window:"
|
||||
msgstr "Fenestra pro debugging WKT:"
|
||||
|
||||
msgid "Debugging window (serialized value)"
|
||||
msgstr "Fenestra pro debugging (valor serialisate)"
|
||||
|
||||
msgid "No feeds are registered."
|
||||
msgstr "Nulle syndication es registrate."
|
||||
|
||||
#, python-format
|
||||
msgid "Slug %r isn't registered."
|
||||
msgstr "Le denotation %r non es registrate."
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,93 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# yakky <i.spalletti@nephila.it>, 2015
|
||||
# Jannis Leidel <jannis@leidel.info>, 2011
|
||||
# Marco Bonetti, 2014
|
||||
# Nicola Larosa <transifex@teknico.net>, 2012
|
||||
# palmux <palmux@gmail.com>, 2015
|
||||
# Mattia Procopio <promat85@gmail.com>, 2015
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-01-19 16:49+0100\n"
|
||||
"PO-Revision-Date: 2017-09-23 18:54+0000\n"
|
||||
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
|
||||
"Language-Team: Italian (http://www.transifex.com/django/django/language/"
|
||||
"it/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: it\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "GIS"
|
||||
msgstr "GIS"
|
||||
|
||||
msgid "The base GIS field."
|
||||
msgstr "Il campo GIS base."
|
||||
|
||||
msgid ""
|
||||
"The base Geometry field -- maps to the OpenGIS Specification Geometry type."
|
||||
msgstr ""
|
||||
"Il campo base GIS -- corrisponde al tipo Geometry delle specifiche OpenGIS."
|
||||
|
||||
msgid "Point"
|
||||
msgstr "Punto"
|
||||
|
||||
msgid "Line string"
|
||||
msgstr "Stringa linea"
|
||||
|
||||
msgid "Polygon"
|
||||
msgstr "Poligono"
|
||||
|
||||
msgid "Multi-point"
|
||||
msgstr "Multipunto"
|
||||
|
||||
msgid "Multi-line string"
|
||||
msgstr "Stringa multilinea"
|
||||
|
||||
msgid "Multi polygon"
|
||||
msgstr "Multi poligono"
|
||||
|
||||
msgid "Geometry collection"
|
||||
msgstr "Raccolta Geometry"
|
||||
|
||||
msgid "Extent Aggregate Field"
|
||||
msgstr "Campo di aggregazione esteso"
|
||||
|
||||
msgid "Raster Field"
|
||||
msgstr "Campo raster"
|
||||
|
||||
msgid "No geometry value provided."
|
||||
msgstr "Nessun valore geometrico fornito."
|
||||
|
||||
msgid "Invalid geometry value."
|
||||
msgstr "Valore geometrico non valido."
|
||||
|
||||
msgid "Invalid geometry type."
|
||||
msgstr "Tipo geometrico non valido."
|
||||
|
||||
msgid ""
|
||||
"An error occurred when transforming the geometry to the SRID of the geometry "
|
||||
"form field."
|
||||
msgstr ""
|
||||
"Si è verificato un errore durante la trasformazione della geometria nello "
|
||||
"SRID del campo geometria della form."
|
||||
|
||||
msgid "Delete all Features"
|
||||
msgstr "Cancella tutti gli oggetti"
|
||||
|
||||
msgid "WKT debugging window:"
|
||||
msgstr "Finestra di debug WKT:"
|
||||
|
||||
msgid "Debugging window (serialized value)"
|
||||
msgstr "Finestra di debug (valore serializzato)"
|
||||
|
||||
msgid "No feeds are registered."
|
||||
msgstr "Non ci sono feed registrati."
|
||||
|
||||
#, python-format
|
||||
msgid "Slug %r isn't registered."
|
||||
msgstr "Lo slug %r non è registrato."
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,80 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-03-18 09:16+0100\n"
|
||||
"PO-Revision-Date: 2015-03-18 08:35+0000\n"
|
||||
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
|
||||
"Language-Team: Khmer (http://www.transifex.com/projects/p/django/language/"
|
||||
"km/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: km\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
|
||||
msgid "GIS"
|
||||
msgstr ""
|
||||
|
||||
msgid "The base GIS field -- maps to the OpenGIS Specification Geometry type."
|
||||
msgstr ""
|
||||
|
||||
msgid "Point"
|
||||
msgstr ""
|
||||
|
||||
msgid "Line string"
|
||||
msgstr ""
|
||||
|
||||
msgid "Polygon"
|
||||
msgstr ""
|
||||
|
||||
msgid "Multi-point"
|
||||
msgstr ""
|
||||
|
||||
msgid "Multi-line string"
|
||||
msgstr ""
|
||||
|
||||
msgid "Multi polygon"
|
||||
msgstr ""
|
||||
|
||||
msgid "Geometry collection"
|
||||
msgstr ""
|
||||
|
||||
msgid "Extent Aggregate Field"
|
||||
msgstr ""
|
||||
|
||||
msgid "No geometry value provided."
|
||||
msgstr ""
|
||||
|
||||
msgid "Invalid geometry value."
|
||||
msgstr ""
|
||||
|
||||
msgid "Invalid geometry type."
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"An error occurred when transforming the geometry to the SRID of the geometry "
|
||||
"form field."
|
||||
msgstr ""
|
||||
|
||||
msgid "Delete all Features"
|
||||
msgstr ""
|
||||
|
||||
msgid "WKT debugging window:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Google Maps via GeoDjango"
|
||||
msgstr ""
|
||||
|
||||
msgid "Debugging window (serialized value)"
|
||||
msgstr ""
|
||||
|
||||
msgid "No feeds are registered."
|
||||
msgstr ""
|
||||
|
||||
#, python-format
|
||||
msgid "Slug %r isn't registered."
|
||||
msgstr ""
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,80 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-03-18 09:16+0100\n"
|
||||
"PO-Revision-Date: 2015-03-18 08:35+0000\n"
|
||||
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
|
||||
"Language-Team: Luxembourgish (http://www.transifex.com/projects/p/django/"
|
||||
"language/lb/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: lb\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "GIS"
|
||||
msgstr ""
|
||||
|
||||
msgid "The base GIS field -- maps to the OpenGIS Specification Geometry type."
|
||||
msgstr ""
|
||||
|
||||
msgid "Point"
|
||||
msgstr ""
|
||||
|
||||
msgid "Line string"
|
||||
msgstr ""
|
||||
|
||||
msgid "Polygon"
|
||||
msgstr ""
|
||||
|
||||
msgid "Multi-point"
|
||||
msgstr ""
|
||||
|
||||
msgid "Multi-line string"
|
||||
msgstr ""
|
||||
|
||||
msgid "Multi polygon"
|
||||
msgstr ""
|
||||
|
||||
msgid "Geometry collection"
|
||||
msgstr ""
|
||||
|
||||
msgid "Extent Aggregate Field"
|
||||
msgstr ""
|
||||
|
||||
msgid "No geometry value provided."
|
||||
msgstr ""
|
||||
|
||||
msgid "Invalid geometry value."
|
||||
msgstr ""
|
||||
|
||||
msgid "Invalid geometry type."
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"An error occurred when transforming the geometry to the SRID of the geometry "
|
||||
"form field."
|
||||
msgstr ""
|
||||
|
||||
msgid "Delete all Features"
|
||||
msgstr ""
|
||||
|
||||
msgid "WKT debugging window:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Google Maps via GeoDjango"
|
||||
msgstr ""
|
||||
|
||||
msgid "Debugging window (serialized value)"
|
||||
msgstr ""
|
||||
|
||||
msgid "No feeds are registered."
|
||||
msgstr ""
|
||||
|
||||
#, python-format
|
||||
msgid "Slug %r isn't registered."
|
||||
msgstr ""
|
||||
@@ -0,0 +1,88 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Aby Thomas <abyzthomas@gmail.com>, 2014
|
||||
# Jannis Leidel <jannis@leidel.info>, 2011
|
||||
# Rajeesh Nair <rajeeshrnair@gmail.com>, 2011-2012
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-01-19 16:49+0100\n"
|
||||
"PO-Revision-Date: 2017-09-19 16:40+0000\n"
|
||||
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
|
||||
"Language-Team: Malayalam (http://www.transifex.com/django/django/language/"
|
||||
"ml/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: ml\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "GIS"
|
||||
msgstr "ജി.ഐ.എസ്"
|
||||
|
||||
msgid "The base GIS field."
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"The base Geometry field -- maps to the OpenGIS Specification Geometry type."
|
||||
msgstr ""
|
||||
|
||||
msgid "Point"
|
||||
msgstr "ബിന്ദു"
|
||||
|
||||
msgid "Line string"
|
||||
msgstr "രേഖാ സ്ട്രിങ്ങ്"
|
||||
|
||||
msgid "Polygon"
|
||||
msgstr "ബഹുഭുജം"
|
||||
|
||||
msgid "Multi-point"
|
||||
msgstr "ബഹുബിന്ദു"
|
||||
|
||||
msgid "Multi-line string"
|
||||
msgstr "ബഹു രേഖാ സ്ട്രിങ്ങ്"
|
||||
|
||||
msgid "Multi polygon"
|
||||
msgstr "ബഹു ബഹുഭുജം"
|
||||
|
||||
msgid "Geometry collection"
|
||||
msgstr "ജ്യാമിതി ശേഖരം"
|
||||
|
||||
msgid "Extent Aggregate Field"
|
||||
msgstr ""
|
||||
|
||||
msgid "Raster Field"
|
||||
msgstr ""
|
||||
|
||||
msgid "No geometry value provided."
|
||||
msgstr "ജ്യാമിതീയ മൂല്യമൊന്നും തന്നിട്ടില്ല."
|
||||
|
||||
msgid "Invalid geometry value."
|
||||
msgstr "തെറ്റായ ജ്യാമിതീയ മൂല്യം."
|
||||
|
||||
msgid "Invalid geometry type."
|
||||
msgstr "തെറ്റായ തരം ജ്യാമിതി."
|
||||
|
||||
msgid ""
|
||||
"An error occurred when transforming the geometry to the SRID of the geometry "
|
||||
"form field."
|
||||
msgstr ""
|
||||
"ജ്യാമിതീയ രൂപത്തെ ജ്യാമിതി കളത്തിന്റെ SRID-ലേക്കു മാറ്റുമ്പോള് എന്തോ തകരാറു സംഭവിച്ചിട്ടുണ്ട്."
|
||||
|
||||
msgid "Delete all Features"
|
||||
msgstr ""
|
||||
|
||||
msgid "WKT debugging window:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Debugging window (serialized value)"
|
||||
msgstr ""
|
||||
|
||||
msgid "No feeds are registered."
|
||||
msgstr " ഫീഡുകളൊന്നും രജിസ്റ്റര് ചെയ്തിട്ടില്ല."
|
||||
|
||||
#, python-format
|
||||
msgid "Slug %r isn't registered."
|
||||
msgstr "%r എന്ന സ്ലഗ് രജിസ്റ്റര് ചെയ്തിട്ടില്ല."
|
||||
@@ -0,0 +1,80 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-03-18 09:16+0100\n"
|
||||
"PO-Revision-Date: 2015-03-18 08:35+0000\n"
|
||||
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
|
||||
"Language-Team: Marathi (http://www.transifex.com/projects/p/django/language/"
|
||||
"mr/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: mr\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "GIS"
|
||||
msgstr ""
|
||||
|
||||
msgid "The base GIS field -- maps to the OpenGIS Specification Geometry type."
|
||||
msgstr ""
|
||||
|
||||
msgid "Point"
|
||||
msgstr ""
|
||||
|
||||
msgid "Line string"
|
||||
msgstr ""
|
||||
|
||||
msgid "Polygon"
|
||||
msgstr ""
|
||||
|
||||
msgid "Multi-point"
|
||||
msgstr ""
|
||||
|
||||
msgid "Multi-line string"
|
||||
msgstr ""
|
||||
|
||||
msgid "Multi polygon"
|
||||
msgstr ""
|
||||
|
||||
msgid "Geometry collection"
|
||||
msgstr ""
|
||||
|
||||
msgid "Extent Aggregate Field"
|
||||
msgstr ""
|
||||
|
||||
msgid "No geometry value provided."
|
||||
msgstr ""
|
||||
|
||||
msgid "Invalid geometry value."
|
||||
msgstr ""
|
||||
|
||||
msgid "Invalid geometry type."
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"An error occurred when transforming the geometry to the SRID of the geometry "
|
||||
"form field."
|
||||
msgstr ""
|
||||
|
||||
msgid "Delete all Features"
|
||||
msgstr ""
|
||||
|
||||
msgid "WKT debugging window:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Google Maps via GeoDjango"
|
||||
msgstr ""
|
||||
|
||||
msgid "Debugging window (serialized value)"
|
||||
msgstr ""
|
||||
|
||||
msgid "No feeds are registered."
|
||||
msgstr ""
|
||||
|
||||
#, python-format
|
||||
msgid "Slug %r isn't registered."
|
||||
msgstr ""
|
||||
Binary file not shown.
@@ -0,0 +1,85 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Yhal Htet Aung <jumoun@gmail.com>, 2015
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-01-19 16:49+0100\n"
|
||||
"PO-Revision-Date: 2017-09-20 03:01+0000\n"
|
||||
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
|
||||
"Language-Team: Burmese (http://www.transifex.com/django/django/language/"
|
||||
"my/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: my\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
|
||||
msgid "GIS"
|
||||
msgstr "ဂျီအိုင်အက်စ်"
|
||||
|
||||
msgid "The base GIS field."
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"The base Geometry field -- maps to the OpenGIS Specification Geometry type."
|
||||
msgstr ""
|
||||
|
||||
msgid "Point"
|
||||
msgstr ""
|
||||
|
||||
msgid "Line string"
|
||||
msgstr ""
|
||||
|
||||
msgid "Polygon"
|
||||
msgstr ""
|
||||
|
||||
msgid "Multi-point"
|
||||
msgstr ""
|
||||
|
||||
msgid "Multi-line string"
|
||||
msgstr ""
|
||||
|
||||
msgid "Multi polygon"
|
||||
msgstr ""
|
||||
|
||||
msgid "Geometry collection"
|
||||
msgstr ""
|
||||
|
||||
msgid "Extent Aggregate Field"
|
||||
msgstr ""
|
||||
|
||||
msgid "Raster Field"
|
||||
msgstr ""
|
||||
|
||||
msgid "No geometry value provided."
|
||||
msgstr ""
|
||||
|
||||
msgid "Invalid geometry value."
|
||||
msgstr ""
|
||||
|
||||
msgid "Invalid geometry type."
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"An error occurred when transforming the geometry to the SRID of the geometry "
|
||||
"form field."
|
||||
msgstr ""
|
||||
|
||||
msgid "Delete all Features"
|
||||
msgstr ""
|
||||
|
||||
msgid "WKT debugging window:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Debugging window (serialized value)"
|
||||
msgstr ""
|
||||
|
||||
msgid "No feeds are registered."
|
||||
msgstr ""
|
||||
|
||||
#, python-format
|
||||
msgid "Slug %r isn't registered."
|
||||
msgstr ""
|
||||
Binary file not shown.
@@ -0,0 +1,84 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Sagar Chalise <chalisesagar@gmail.com>, 2011
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-01-19 16:49+0100\n"
|
||||
"PO-Revision-Date: 2017-09-19 16:40+0000\n"
|
||||
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
|
||||
"Language-Team: Nepali (http://www.transifex.com/django/django/language/ne/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: ne\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "GIS"
|
||||
msgstr ""
|
||||
|
||||
msgid "The base GIS field."
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"The base Geometry field -- maps to the OpenGIS Specification Geometry type."
|
||||
msgstr ""
|
||||
|
||||
msgid "Point"
|
||||
msgstr "बिन्दु"
|
||||
|
||||
msgid "Line string"
|
||||
msgstr ""
|
||||
|
||||
msgid "Polygon"
|
||||
msgstr "बहुभुज"
|
||||
|
||||
msgid "Multi-point"
|
||||
msgstr ""
|
||||
|
||||
msgid "Multi-line string"
|
||||
msgstr ""
|
||||
|
||||
msgid "Multi polygon"
|
||||
msgstr ""
|
||||
|
||||
msgid "Geometry collection"
|
||||
msgstr "ज्यामिति संकलन"
|
||||
|
||||
msgid "Extent Aggregate Field"
|
||||
msgstr ""
|
||||
|
||||
msgid "Raster Field"
|
||||
msgstr ""
|
||||
|
||||
msgid "No geometry value provided."
|
||||
msgstr "ज्यामिति मान उपलब्ध छैन ।"
|
||||
|
||||
msgid "Invalid geometry value."
|
||||
msgstr "उनुपयुक्त ज्यामिति मान"
|
||||
|
||||
msgid "Invalid geometry type."
|
||||
msgstr "उनुपयुक्त ज्यामिति प्रकार"
|
||||
|
||||
msgid ""
|
||||
"An error occurred when transforming the geometry to the SRID of the geometry "
|
||||
"form field."
|
||||
msgstr ""
|
||||
|
||||
msgid "Delete all Features"
|
||||
msgstr ""
|
||||
|
||||
msgid "WKT debugging window:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Debugging window (serialized value)"
|
||||
msgstr ""
|
||||
|
||||
msgid "No feeds are registered."
|
||||
msgstr ""
|
||||
|
||||
#, python-format
|
||||
msgid "Slug %r isn't registered."
|
||||
msgstr ""
|
||||
Binary file not shown.
@@ -0,0 +1,94 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Blue <alexandervanratingen@gmail.com>, 2011
|
||||
# Harro van der Klauw <hvdklauw@gmail.com>, 2012
|
||||
# Ilja Maas <iljamaas@dreamsolution.nl>, 2015
|
||||
# Jannis Leidel <jannis@leidel.info>, 2011
|
||||
# Jeffrey Gelens <jeffrey@noppo.pro>, 2011
|
||||
# Sander Steffann <inactive+steffann@transifex.com>, 2015
|
||||
# Tonnes <tonnes.mb@gmail.com>, 2019
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-01-19 16:49+0100\n"
|
||||
"PO-Revision-Date: 2019-02-24 16:22+0000\n"
|
||||
"Last-Translator: Tonnes <tonnes.mb@gmail.com>\n"
|
||||
"Language-Team: Dutch (http://www.transifex.com/django/django/language/nl/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: nl\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "GIS"
|
||||
msgstr "GIS"
|
||||
|
||||
msgid "The base GIS field."
|
||||
msgstr "Het basis-GIS-veld."
|
||||
|
||||
msgid ""
|
||||
"The base Geometry field -- maps to the OpenGIS Specification Geometry type."
|
||||
msgstr ""
|
||||
"Het basis-Geometrie-veld -- correspondeert met het Geometrie-type van de "
|
||||
"OpenGIS-specificatie."
|
||||
|
||||
msgid "Point"
|
||||
msgstr "Punt"
|
||||
|
||||
msgid "Line string"
|
||||
msgstr "Tekenreeks"
|
||||
|
||||
msgid "Polygon"
|
||||
msgstr "Polygoon"
|
||||
|
||||
msgid "Multi-point"
|
||||
msgstr "Multipunt"
|
||||
|
||||
msgid "Multi-line string"
|
||||
msgstr "Multi-tekenreeks"
|
||||
|
||||
msgid "Multi polygon"
|
||||
msgstr "Multi-polygoon"
|
||||
|
||||
msgid "Geometry collection"
|
||||
msgstr "Geometrie-verzameling"
|
||||
|
||||
msgid "Extent Aggregate Field"
|
||||
msgstr "Gebieds-aggregatieveld"
|
||||
|
||||
msgid "Raster Field"
|
||||
msgstr "Rasterveld"
|
||||
|
||||
msgid "No geometry value provided."
|
||||
msgstr "Geen geometriewaarde opgegeven."
|
||||
|
||||
msgid "Invalid geometry value."
|
||||
msgstr "Ongeldige geometriewaarde."
|
||||
|
||||
msgid "Invalid geometry type."
|
||||
msgstr "Ongeldig geometrietype."
|
||||
|
||||
msgid ""
|
||||
"An error occurred when transforming the geometry to the SRID of the geometry "
|
||||
"form field."
|
||||
msgstr ""
|
||||
"Er is een fout opgetreden bij het omvormen van de geometrie naar de SRID van "
|
||||
"het geometrieveld."
|
||||
|
||||
msgid "Delete all Features"
|
||||
msgstr "Alle kenmerken verwijderen"
|
||||
|
||||
msgid "WKT debugging window:"
|
||||
msgstr "WKT-debugvenster:"
|
||||
|
||||
msgid "Debugging window (serialized value)"
|
||||
msgstr "Debugvenster (geserialiseerde waarde)"
|
||||
|
||||
msgid "No feeds are registered."
|
||||
msgstr "Er zijn geen feeds geregistreerd."
|
||||
|
||||
#, python-format
|
||||
msgid "Slug %r isn't registered."
|
||||
msgstr "Slug %r is niet geregistreerd."
|
||||
Binary file not shown.
@@ -0,0 +1,95 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Claudio Fernandes <squeral@gmail.com>, 2015
|
||||
# Jannis Leidel <jannis@leidel.info>, 2011
|
||||
# jorgecarleitao <jorgecarleitao@gmail.com>, 2015
|
||||
# Manuela Silva <inactive+h_manuela_rodsilva@transifex.com>, 2015
|
||||
# Nuno Mariz <nmariz@gmail.com>, 2011-2012,2015
|
||||
# Paulo Köch <paulo.koch@gmail.com>, 2011
|
||||
# Raúl Pedro Fernandes Santos, 2014
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-01-19 16:49+0100\n"
|
||||
"PO-Revision-Date: 2017-09-23 18:54+0000\n"
|
||||
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
|
||||
"Language-Team: Portuguese (http://www.transifex.com/django/django/language/"
|
||||
"pt/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: pt\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "GIS"
|
||||
msgstr "SIG"
|
||||
|
||||
msgid "The base GIS field."
|
||||
msgstr "O campo GIS base."
|
||||
|
||||
msgid ""
|
||||
"The base Geometry field -- maps to the OpenGIS Specification Geometry type."
|
||||
msgstr ""
|
||||
"O campo de Geometria base -- mapeia para o tipo de Geometria de "
|
||||
"Especificação do OpenGIS."
|
||||
|
||||
msgid "Point"
|
||||
msgstr "Ponto"
|
||||
|
||||
msgid "Line string"
|
||||
msgstr "Linha"
|
||||
|
||||
msgid "Polygon"
|
||||
msgstr "Polígono"
|
||||
|
||||
msgid "Multi-point"
|
||||
msgstr "Multi-ponto"
|
||||
|
||||
msgid "Multi-line string"
|
||||
msgstr "Multi-linha"
|
||||
|
||||
msgid "Multi polygon"
|
||||
msgstr "Multi-polígono"
|
||||
|
||||
msgid "Geometry collection"
|
||||
msgstr "Coleção geométrica"
|
||||
|
||||
msgid "Extent Aggregate Field"
|
||||
msgstr "Extender Campo Agregado"
|
||||
|
||||
msgid "Raster Field"
|
||||
msgstr "Campo Raster"
|
||||
|
||||
msgid "No geometry value provided."
|
||||
msgstr "Não foi submetido nenhum valor do tipo geometria."
|
||||
|
||||
msgid "Invalid geometry value."
|
||||
msgstr "Valor inválido de geometria."
|
||||
|
||||
msgid "Invalid geometry type."
|
||||
msgstr "Tipo inválido de geometria."
|
||||
|
||||
msgid ""
|
||||
"An error occurred when transforming the geometry to the SRID of the geometry "
|
||||
"form field."
|
||||
msgstr ""
|
||||
"Ocorreu um erro na transformação da geometria para o SRID da geometria do "
|
||||
"campo do formulário."
|
||||
|
||||
msgid "Delete all Features"
|
||||
msgstr "Eliminar todas as Caraterísticas"
|
||||
|
||||
msgid "WKT debugging window:"
|
||||
msgstr "Janela de depuração de WKT:"
|
||||
|
||||
msgid "Debugging window (serialized value)"
|
||||
msgstr "Janela de depuração (valor serializado)"
|
||||
|
||||
msgid "No feeds are registered."
|
||||
msgstr "Nenhum feed está registado."
|
||||
|
||||
#, python-format
|
||||
msgid "Slug %r isn't registered."
|
||||
msgstr "O slug %r não está registado."
|
||||
BIN
Binary file not shown.
+94
@@ -0,0 +1,94 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Allisson Azevedo <allisson@gmail.com>, 2014
|
||||
# Carlos Leite <caduado@gmail.com>, 2015-2016
|
||||
# Eduardo Cereto Carvalho, 2011
|
||||
# semente, 2012
|
||||
# Jannis Leidel <jannis@leidel.info>, 2011
|
||||
# Lucas Infante <maccinza@gmail.com>, 2015
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-01-19 16:49+0100\n"
|
||||
"PO-Revision-Date: 2017-09-23 18:54+0000\n"
|
||||
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
|
||||
"Language-Team: Portuguese (Brazil) (http://www.transifex.com/django/django/"
|
||||
"language/pt_BR/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: pt_BR\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
|
||||
msgid "GIS"
|
||||
msgstr "GIS"
|
||||
|
||||
msgid "The base GIS field."
|
||||
msgstr "O campo GIS base."
|
||||
|
||||
msgid ""
|
||||
"The base Geometry field -- maps to the OpenGIS Specification Geometry type."
|
||||
msgstr ""
|
||||
"O campo base de geometria -- mapeia para o tipo de geometria da "
|
||||
"especificação OpenGis"
|
||||
|
||||
msgid "Point"
|
||||
msgstr "Ponto"
|
||||
|
||||
msgid "Line string"
|
||||
msgstr "Linha string"
|
||||
|
||||
msgid "Polygon"
|
||||
msgstr "Polígono"
|
||||
|
||||
msgid "Multi-point"
|
||||
msgstr "Multiponto"
|
||||
|
||||
msgid "Multi-line string"
|
||||
msgstr "Multilinha string"
|
||||
|
||||
msgid "Multi polygon"
|
||||
msgstr "Multipolígono"
|
||||
|
||||
msgid "Geometry collection"
|
||||
msgstr "Coleção geométrica"
|
||||
|
||||
msgid "Extent Aggregate Field"
|
||||
msgstr "Campo agregado extendido"
|
||||
|
||||
msgid "Raster Field"
|
||||
msgstr "Campo Raster."
|
||||
|
||||
msgid "No geometry value provided."
|
||||
msgstr "Nenhum valor geométrico fornecido."
|
||||
|
||||
msgid "Invalid geometry value."
|
||||
msgstr "Valor geométrico inválido."
|
||||
|
||||
msgid "Invalid geometry type."
|
||||
msgstr "Tipo geométrico inválido."
|
||||
|
||||
msgid ""
|
||||
"An error occurred when transforming the geometry to the SRID of the geometry "
|
||||
"form field."
|
||||
msgstr ""
|
||||
"Ocorreu um erro ao transformar a geometria para o SRID do campo de "
|
||||
"formulário de geometria."
|
||||
|
||||
msgid "Delete all Features"
|
||||
msgstr "Deletar todas os elementos"
|
||||
|
||||
msgid "WKT debugging window:"
|
||||
msgstr "Janela de debug WKT"
|
||||
|
||||
msgid "Debugging window (serialized value)"
|
||||
msgstr "Janela de debug (valor seralizado)"
|
||||
|
||||
msgid "No feeds are registered."
|
||||
msgstr "Nenhum feed foi registrado."
|
||||
|
||||
#, python-format
|
||||
msgid "Slug %r isn't registered."
|
||||
msgstr "O slug %r não foi registrado."
|
||||
Binary file not shown.
@@ -0,0 +1,92 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Jannis Leidel <jannis@leidel.info>, 2011
|
||||
# Primož Verdnik <primoz.verdnik@gmail.com>, 2017
|
||||
# zejn <zejn@kiberpipa.org>, 2016
|
||||
# zejn <zejn@kiberpipa.org>, 2012
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-01-19 16:49+0100\n"
|
||||
"PO-Revision-Date: 2017-09-23 18:54+0000\n"
|
||||
"Last-Translator: Primož Verdnik <primoz.verdnik@gmail.com>\n"
|
||||
"Language-Team: Slovenian (http://www.transifex.com/django/django/language/"
|
||||
"sl/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: sl\n"
|
||||
"Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n"
|
||||
"%100==4 ? 2 : 3);\n"
|
||||
|
||||
msgid "GIS"
|
||||
msgstr "GIS"
|
||||
|
||||
msgid "The base GIS field."
|
||||
msgstr "Osnovno GIS polje"
|
||||
|
||||
msgid ""
|
||||
"The base Geometry field -- maps to the OpenGIS Specification Geometry type."
|
||||
msgstr ""
|
||||
"Osnovno polje GIS -- se preslika v vrsto Geometry po določilih OpenGIS."
|
||||
|
||||
msgid "Point"
|
||||
msgstr "Točka"
|
||||
|
||||
msgid "Line string"
|
||||
msgstr "Črtni niz"
|
||||
|
||||
msgid "Polygon"
|
||||
msgstr "Mnogokotnik"
|
||||
|
||||
msgid "Multi-point"
|
||||
msgstr "Več-točkovi predmet"
|
||||
|
||||
msgid "Multi-line string"
|
||||
msgstr "Več črtni niz"
|
||||
|
||||
msgid "Multi polygon"
|
||||
msgstr "Večkratni mnogokotnikov"
|
||||
|
||||
msgid "Geometry collection"
|
||||
msgstr "Zbirka likov"
|
||||
|
||||
msgid "Extent Aggregate Field"
|
||||
msgstr "Polje z agregiranim območjem"
|
||||
|
||||
msgid "Raster Field"
|
||||
msgstr "Rastersko polje"
|
||||
|
||||
msgid "No geometry value provided."
|
||||
msgstr "Ni navedene geometrijske vrednosti."
|
||||
|
||||
msgid "Invalid geometry value."
|
||||
msgstr "Neveljavna geometrijska vrednost."
|
||||
|
||||
msgid "Invalid geometry type."
|
||||
msgstr "Neveljavna vrsta geometrije."
|
||||
|
||||
msgid ""
|
||||
"An error occurred when transforming the geometry to the SRID of the geometry "
|
||||
"form field."
|
||||
msgstr ""
|
||||
"Med pretvarjanjem geometrijskega zapisa v SRID geometrijskega polja je "
|
||||
"prišlo do napake."
|
||||
|
||||
msgid "Delete all Features"
|
||||
msgstr "Pobriši vse atribute"
|
||||
|
||||
msgid "WKT debugging window:"
|
||||
msgstr "Okno za razhroščevanje WKT"
|
||||
|
||||
msgid "Debugging window (serialized value)"
|
||||
msgstr "Okno za razhroščevanje (serializirana vrednost)"
|
||||
|
||||
msgid "No feeds are registered."
|
||||
msgstr "Ni vpisanih virov."
|
||||
|
||||
#, python-format
|
||||
msgid "Slug %r isn't registered."
|
||||
msgstr "Okrajšava %r ni vpisana."
|
||||
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,80 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-03-18 09:16+0100\n"
|
||||
"PO-Revision-Date: 2015-03-18 08:35+0000\n"
|
||||
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
|
||||
"Language-Team: Tamil (http://www.transifex.com/projects/p/django/language/"
|
||||
"ta/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: ta\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "GIS"
|
||||
msgstr ""
|
||||
|
||||
msgid "The base GIS field -- maps to the OpenGIS Specification Geometry type."
|
||||
msgstr ""
|
||||
|
||||
msgid "Point"
|
||||
msgstr ""
|
||||
|
||||
msgid "Line string"
|
||||
msgstr ""
|
||||
|
||||
msgid "Polygon"
|
||||
msgstr ""
|
||||
|
||||
msgid "Multi-point"
|
||||
msgstr ""
|
||||
|
||||
msgid "Multi-line string"
|
||||
msgstr ""
|
||||
|
||||
msgid "Multi polygon"
|
||||
msgstr ""
|
||||
|
||||
msgid "Geometry collection"
|
||||
msgstr ""
|
||||
|
||||
msgid "Extent Aggregate Field"
|
||||
msgstr ""
|
||||
|
||||
msgid "No geometry value provided."
|
||||
msgstr ""
|
||||
|
||||
msgid "Invalid geometry value."
|
||||
msgstr ""
|
||||
|
||||
msgid "Invalid geometry type."
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"An error occurred when transforming the geometry to the SRID of the geometry "
|
||||
"form field."
|
||||
msgstr ""
|
||||
|
||||
msgid "Delete all Features"
|
||||
msgstr ""
|
||||
|
||||
msgid "WKT debugging window:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Google Maps via GeoDjango"
|
||||
msgstr ""
|
||||
|
||||
msgid "Debugging window (serialized value)"
|
||||
msgstr ""
|
||||
|
||||
msgid "No feeds are registered."
|
||||
msgstr ""
|
||||
|
||||
#, python-format
|
||||
msgid "Slug %r isn't registered."
|
||||
msgstr ""
|
||||
@@ -0,0 +1,84 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Jannis Leidel <jannis@leidel.info>, 2011
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-01-19 16:49+0100\n"
|
||||
"PO-Revision-Date: 2017-09-19 16:40+0000\n"
|
||||
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
|
||||
"Language-Team: Telugu (http://www.transifex.com/django/django/language/te/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: te\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "GIS"
|
||||
msgstr ""
|
||||
|
||||
msgid "The base GIS field."
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"The base Geometry field -- maps to the OpenGIS Specification Geometry type."
|
||||
msgstr ""
|
||||
|
||||
msgid "Point"
|
||||
msgstr ""
|
||||
|
||||
msgid "Line string"
|
||||
msgstr ""
|
||||
|
||||
msgid "Polygon"
|
||||
msgstr ""
|
||||
|
||||
msgid "Multi-point"
|
||||
msgstr ""
|
||||
|
||||
msgid "Multi-line string"
|
||||
msgstr ""
|
||||
|
||||
msgid "Multi polygon"
|
||||
msgstr ""
|
||||
|
||||
msgid "Geometry collection"
|
||||
msgstr ""
|
||||
|
||||
msgid "Extent Aggregate Field"
|
||||
msgstr ""
|
||||
|
||||
msgid "Raster Field"
|
||||
msgstr ""
|
||||
|
||||
msgid "No geometry value provided."
|
||||
msgstr "క్షెత్రగనిత మూల్యము ఇవ్వలెదు."
|
||||
|
||||
msgid "Invalid geometry value."
|
||||
msgstr "సరికాని క్షేత్రగణిత మూల్యము."
|
||||
|
||||
msgid "Invalid geometry type."
|
||||
msgstr "సరికాని క్షేత్రగణిత రకం."
|
||||
|
||||
msgid ""
|
||||
"An error occurred when transforming the geometry to the SRID of the geometry "
|
||||
"form field."
|
||||
msgstr ""
|
||||
|
||||
msgid "Delete all Features"
|
||||
msgstr ""
|
||||
|
||||
msgid "WKT debugging window:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Debugging window (serialized value)"
|
||||
msgstr ""
|
||||
|
||||
msgid "No feeds are registered."
|
||||
msgstr ""
|
||||
|
||||
#, python-format
|
||||
msgid "Slug %r isn't registered."
|
||||
msgstr ""
|
||||
@@ -0,0 +1,86 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Jannis Leidel <jannis@leidel.info>, 2011
|
||||
# Kowit Charoenratchatabhan <kowit.s.c@gmail.com>, 2012
|
||||
# Vichai Vongvorakul <vongvichai@gmail.com>, 2012
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-01-19 16:49+0100\n"
|
||||
"PO-Revision-Date: 2017-09-19 16:40+0000\n"
|
||||
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
|
||||
"Language-Team: Thai (http://www.transifex.com/django/django/language/th/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: th\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
|
||||
msgid "GIS"
|
||||
msgstr ""
|
||||
|
||||
msgid "The base GIS field."
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"The base Geometry field -- maps to the OpenGIS Specification Geometry type."
|
||||
msgstr ""
|
||||
|
||||
msgid "Point"
|
||||
msgstr "จุด"
|
||||
|
||||
msgid "Line string"
|
||||
msgstr "สายสตริง"
|
||||
|
||||
msgid "Polygon"
|
||||
msgstr "รูปหลายเหลี่ยม"
|
||||
|
||||
msgid "Multi-point"
|
||||
msgstr "หลาย ๆ จุด"
|
||||
|
||||
msgid "Multi-line string"
|
||||
msgstr "สตริงหลายบรรทัด"
|
||||
|
||||
msgid "Multi polygon"
|
||||
msgstr "รูปหลายเหลี่ยมหลายรูป"
|
||||
|
||||
msgid "Geometry collection"
|
||||
msgstr "คอลเลกชันรูปทรงเรขาคณิต"
|
||||
|
||||
msgid "Extent Aggregate Field"
|
||||
msgstr ""
|
||||
|
||||
msgid "Raster Field"
|
||||
msgstr ""
|
||||
|
||||
msgid "No geometry value provided."
|
||||
msgstr "ไม่พบข้อมูลพิกัด"
|
||||
|
||||
msgid "Invalid geometry value."
|
||||
msgstr "ค่าพิกัดผิดพลาด "
|
||||
|
||||
msgid "Invalid geometry type."
|
||||
msgstr "ขนิดข้อมูลพิกัดผิดพลาด"
|
||||
|
||||
msgid ""
|
||||
"An error occurred when transforming the geometry to the SRID of the geometry "
|
||||
"form field."
|
||||
msgstr "ข้อผิดพลาดที่เกิดขึ้นเมื่อการเปลี่ยนรูปทรงเรขาคณิตที่ SRID ของเขตข้อมูลฟอร์มเรขาคณิต"
|
||||
|
||||
msgid "Delete all Features"
|
||||
msgstr ""
|
||||
|
||||
msgid "WKT debugging window:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Debugging window (serialized value)"
|
||||
msgstr ""
|
||||
|
||||
msgid "No feeds are registered."
|
||||
msgstr "ไม่มีฟีดที่ลงทะเบียน"
|
||||
|
||||
#, python-format
|
||||
msgid "Slug %r isn't registered."
|
||||
msgstr "Slug %r ไม่ได้ลงทะเบียน"
|
||||
@@ -0,0 +1,91 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# BouRock, 2015
|
||||
# BouRock, 2014-2015
|
||||
# Jannis Leidel <jannis@leidel.info>, 2011
|
||||
# Murat Çorlu <muratcorlu@gmail.com>, 2012
|
||||
# Murat Sahin <martinamca@gmail.com>, 2012
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-01-19 16:49+0100\n"
|
||||
"PO-Revision-Date: 2017-09-23 18:54+0000\n"
|
||||
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
|
||||
"Language-Team: Turkish (http://www.transifex.com/django/django/language/"
|
||||
"tr/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: tr\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
|
||||
msgid "GIS"
|
||||
msgstr "GIS"
|
||||
|
||||
msgid "The base GIS field."
|
||||
msgstr "Temel GIS alanı."
|
||||
|
||||
msgid ""
|
||||
"The base Geometry field -- maps to the OpenGIS Specification Geometry type."
|
||||
msgstr "Temel Geometri alanı - OpenGIS Özellikleri Geometri türüyle eşlenir."
|
||||
|
||||
msgid "Point"
|
||||
msgstr "Nokta"
|
||||
|
||||
msgid "Line string"
|
||||
msgstr "Satır dizgesi"
|
||||
|
||||
msgid "Polygon"
|
||||
msgstr "Çokgen"
|
||||
|
||||
msgid "Multi-point"
|
||||
msgstr "Çok noktalı"
|
||||
|
||||
msgid "Multi-line string"
|
||||
msgstr "Çok satırlı dizge"
|
||||
|
||||
msgid "Multi polygon"
|
||||
msgstr "Çoklu çokgen"
|
||||
|
||||
msgid "Geometry collection"
|
||||
msgstr "Geometri koleksiyonu"
|
||||
|
||||
msgid "Extent Aggregate Field"
|
||||
msgstr "Toplama Alanını Genişlet"
|
||||
|
||||
msgid "Raster Field"
|
||||
msgstr "Tarama Alanı"
|
||||
|
||||
msgid "No geometry value provided."
|
||||
msgstr "Verilen hiç geometri değeri yok."
|
||||
|
||||
msgid "Invalid geometry value."
|
||||
msgstr "Geçersiz geometri değeri."
|
||||
|
||||
msgid "Invalid geometry type."
|
||||
msgstr "Geçersiz geometri türü."
|
||||
|
||||
msgid ""
|
||||
"An error occurred when transforming the geometry to the SRID of the geometry "
|
||||
"form field."
|
||||
msgstr ""
|
||||
"Geometri verisi geometri form alanının SRID değerine dönüştürülürken bir "
|
||||
"hata meydana geldi."
|
||||
|
||||
msgid "Delete all Features"
|
||||
msgstr "Tüm Özellikleri Sil"
|
||||
|
||||
msgid "WKT debugging window:"
|
||||
msgstr "WKT hata ayıklama penceresi:"
|
||||
|
||||
msgid "Debugging window (serialized value)"
|
||||
msgstr "Hata ayıklama penceresi (serileştirilmiş değer)"
|
||||
|
||||
msgid "No feeds are registered."
|
||||
msgstr "Hiçbir besleme kayıtlı değil."
|
||||
|
||||
#, python-format
|
||||
msgid "Slug %r isn't registered."
|
||||
msgstr "%r kısaltması kayıtlı değil."
|
||||
@@ -0,0 +1,86 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Mansoorulhaq Mansoor <mnsrknp@gmail.com>, 2011
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-01-19 16:49+0100\n"
|
||||
"PO-Revision-Date: 2017-09-19 16:40+0000\n"
|
||||
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
|
||||
"Language-Team: Urdu (http://www.transifex.com/django/django/language/ur/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: ur\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "GIS"
|
||||
msgstr ""
|
||||
|
||||
msgid "The base GIS field."
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"The base Geometry field -- maps to the OpenGIS Specification Geometry type."
|
||||
msgstr ""
|
||||
|
||||
msgid "Point"
|
||||
msgstr "نقطہ"
|
||||
|
||||
msgid "Line string"
|
||||
msgstr "لائن سٹرنگ"
|
||||
|
||||
msgid "Polygon"
|
||||
msgstr "پولی گان"
|
||||
|
||||
msgid "Multi-point"
|
||||
msgstr "کثیر النقاط"
|
||||
|
||||
msgid "Multi-line string"
|
||||
msgstr "متعدد لائنوں والا سٹرنگ"
|
||||
|
||||
msgid "Multi polygon"
|
||||
msgstr "ملٹی پولی گان"
|
||||
|
||||
msgid "Geometry collection"
|
||||
msgstr "جیومیٹری کا ذخیرہ"
|
||||
|
||||
msgid "Extent Aggregate Field"
|
||||
msgstr ""
|
||||
|
||||
msgid "Raster Field"
|
||||
msgstr ""
|
||||
|
||||
msgid "No geometry value provided."
|
||||
msgstr "کوئی جیومیٹری ویلیو مھیا نھیں کی گئی۔"
|
||||
|
||||
msgid "Invalid geometry value."
|
||||
msgstr "غلط جیومیٹری ویلیو۔"
|
||||
|
||||
msgid "Invalid geometry type."
|
||||
msgstr "غلط جیومیٹری ٹائپ"
|
||||
|
||||
msgid ""
|
||||
"An error occurred when transforming the geometry to the SRID of the geometry "
|
||||
"form field."
|
||||
msgstr ""
|
||||
"جیومیٹری کو جیومیٹری کے فارم کے SRID خانے میں تبدیل کرتے ھوئےکوئی خرابی واقع "
|
||||
"ھو گئی ھے۔"
|
||||
|
||||
msgid "Delete all Features"
|
||||
msgstr ""
|
||||
|
||||
msgid "WKT debugging window:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Debugging window (serialized value)"
|
||||
msgstr ""
|
||||
|
||||
msgid "No feeds are registered."
|
||||
msgstr ""
|
||||
|
||||
#, python-format
|
||||
msgid "Slug %r isn't registered."
|
||||
msgstr ""
|
||||
Binary file not shown.
+92
@@ -0,0 +1,92 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Daniel Duan <DaNmarner@gmail.com>, 2011
|
||||
# Jannis Leidel <jannis@leidel.info>, 2011
|
||||
# Lei Yang <yanglei.go@gmail.com>, 2011
|
||||
# Lele Long <schemacs@gmail.com>, 2011,2015
|
||||
# mozillazg <opensource.mozillazg@gmail.com>, 2016
|
||||
# Ronald White <tkliuxing@me.com>, 2014
|
||||
# Kevin Sze <leiarix@gmail.com>, 2012
|
||||
# 航 柯 <korhang@163.com>, 2018
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-01-19 16:49+0100\n"
|
||||
"PO-Revision-Date: 2018-10-15 08:06+0000\n"
|
||||
"Last-Translator: 航 柯 <korhang@163.com>\n"
|
||||
"Language-Team: Chinese (China) (http://www.transifex.com/django/django/"
|
||||
"language/zh_CN/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: zh_CN\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
|
||||
msgid "GIS"
|
||||
msgstr "GIS"
|
||||
|
||||
msgid "The base GIS field."
|
||||
msgstr "基础 GIS 字段。"
|
||||
|
||||
msgid ""
|
||||
"The base Geometry field -- maps to the OpenGIS Specification Geometry type."
|
||||
msgstr "基础 Geometry 字段 -- 映射到 OpenGIS 规范几何类型。"
|
||||
|
||||
msgid "Point"
|
||||
msgstr "点"
|
||||
|
||||
msgid "Line string"
|
||||
msgstr "行字串"
|
||||
|
||||
msgid "Polygon"
|
||||
msgstr "多边形"
|
||||
|
||||
msgid "Multi-point"
|
||||
msgstr "多点"
|
||||
|
||||
msgid "Multi-line string"
|
||||
msgstr "多行字符串"
|
||||
|
||||
msgid "Multi polygon"
|
||||
msgstr "多个多边形"
|
||||
|
||||
msgid "Geometry collection"
|
||||
msgstr "几何集合"
|
||||
|
||||
msgid "Extent Aggregate Field"
|
||||
msgstr "扩展聚集字段"
|
||||
|
||||
msgid "Raster Field"
|
||||
msgstr "光栅字段"
|
||||
|
||||
msgid "No geometry value provided."
|
||||
msgstr "未提供几何信息。"
|
||||
|
||||
msgid "Invalid geometry value."
|
||||
msgstr "无效几何信息。"
|
||||
|
||||
msgid "Invalid geometry type."
|
||||
msgstr "无效几何类型。"
|
||||
|
||||
msgid ""
|
||||
"An error occurred when transforming the geometry to the SRID of the geometry "
|
||||
"form field."
|
||||
msgstr "几何形状转换SRID字段将发生错误"
|
||||
|
||||
msgid "Delete all Features"
|
||||
msgstr "删除所有要素"
|
||||
|
||||
msgid "WKT debugging window:"
|
||||
msgstr "WKT 调试窗口:"
|
||||
|
||||
msgid "Debugging window (serialized value)"
|
||||
msgstr "调试窗口(已序列化的值)"
|
||||
|
||||
msgid "No feeds are registered."
|
||||
msgstr "没有已注册的源。"
|
||||
|
||||
#, python-format
|
||||
msgid "Slug %r isn't registered."
|
||||
msgstr "Slug %r 没有注册."
|
||||
BIN
Binary file not shown.
+89
@@ -0,0 +1,89 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Chen Chun-Chia <ccc.larc@gmail.com>, 2015
|
||||
# ilay <ilay@ilay.tw>, 2012
|
||||
# Jannis Leidel <jannis@leidel.info>, 2011
|
||||
# quantum9876 <quantum9876@gmail.com>, 2011
|
||||
# Tzu-ping Chung <uranusjr@gmail.com>, 2016
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-01-19 16:49+0100\n"
|
||||
"PO-Revision-Date: 2017-09-19 16:40+0000\n"
|
||||
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
|
||||
"Language-Team: Chinese (Taiwan) (http://www.transifex.com/django/django/"
|
||||
"language/zh_TW/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: zh_TW\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
|
||||
msgid "GIS"
|
||||
msgstr "GIS"
|
||||
|
||||
msgid "The base GIS field."
|
||||
msgstr "基礎 GIS 欄位。"
|
||||
|
||||
msgid ""
|
||||
"The base Geometry field -- maps to the OpenGIS Specification Geometry type."
|
||||
msgstr "基本的 Geometry 欄位——對應於 OpenGIS 規格的 Geometry 類型。"
|
||||
|
||||
msgid "Point"
|
||||
msgstr "Point 類別"
|
||||
|
||||
msgid "Line string"
|
||||
msgstr "Line string 類別"
|
||||
|
||||
msgid "Polygon"
|
||||
msgstr "Polygon 類別"
|
||||
|
||||
msgid "Multi-point"
|
||||
msgstr "Multi-point 類別"
|
||||
|
||||
msgid "Multi-line string"
|
||||
msgstr "Multi-line string 類別"
|
||||
|
||||
msgid "Multi polygon"
|
||||
msgstr "Multi polygon 類別"
|
||||
|
||||
msgid "Geometry collection"
|
||||
msgstr "Geometry collection (幾何型別之叢集) 類別"
|
||||
|
||||
msgid "Extent Aggregate Field"
|
||||
msgstr "聚集程度欄位"
|
||||
|
||||
msgid "Raster Field"
|
||||
msgstr "柵格欄位"
|
||||
|
||||
msgid "No geometry value provided."
|
||||
msgstr "沒有幾何資訊。"
|
||||
|
||||
msgid "Invalid geometry value."
|
||||
msgstr "無效的幾何參數。"
|
||||
|
||||
msgid "Invalid geometry type."
|
||||
msgstr "無效的幾何類型。"
|
||||
|
||||
msgid ""
|
||||
"An error occurred when transforming the geometry to the SRID of the geometry "
|
||||
"form field."
|
||||
msgstr "當把目前地理資訊-GIS,轉成空間參考識別碼-SRID時發生錯誤。"
|
||||
|
||||
msgid "Delete all Features"
|
||||
msgstr "刪除所有特徵"
|
||||
|
||||
msgid "WKT debugging window:"
|
||||
msgstr "WKT 除錯視窗:"
|
||||
|
||||
msgid "Debugging window (serialized value)"
|
||||
msgstr "除錯視窗 (序列化值)"
|
||||
|
||||
msgid "No feeds are registered."
|
||||
msgstr "沒有已註冊的 feed。"
|
||||
|
||||
#, python-format
|
||||
msgid "Slug %r isn't registered."
|
||||
msgstr "嵌入式語法 %r 尚未註冊。"
|
||||
@@ -0,0 +1,4 @@
|
||||
# Geo-enabled Sitemap classes.
|
||||
from django.contrib.gis.sitemaps.kml import KMLSitemap, KMZSitemap
|
||||
|
||||
__all__ = ['KMLSitemap', 'KMZSitemap']
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user