227 строки
7.4 KiB
Python
227 строки
7.4 KiB
Python
#!/usr/bin/env python
|
|
# -*- coding: utf-8 -*-
|
|
# This code is in the public domain, so as to serve as a template for
|
|
# real-world plugins.
|
|
# or, at the choice of the licensee,
|
|
# Copyright 2019 Even Rouault
|
|
# SPDX-License-Identifier: MIT
|
|
|
|
# Metadata parsed by GDAL C++ code at driver pre-loading, starting with '# gdal: '
|
|
# Required and with that exact syntax since it is parsed by non-Python
|
|
# aware code. So just literal values, no expressions, etc.
|
|
# gdal: DRIVER_NAME = "DUMMY"
|
|
# API version(s) supported. Must include 1 currently
|
|
# gdal: DRIVER_SUPPORTED_API_VERSION = [1]
|
|
# gdal: DRIVER_DCAP_VECTOR = "YES"
|
|
# gdal: DRIVER_DMD_LONGNAME = "my super plugin"
|
|
|
|
# Optional driver metadata items.
|
|
# # gdal: DRIVER_DMD_EXTENSIONS = "ext1 est2"
|
|
# # gdal: DRIVER_DMD_HELPTOPIC = "http://example.com/my_help.html"
|
|
|
|
try:
|
|
# The gdal_python_driver module is defined by the GDAL library at runtime
|
|
from gdal_python_driver import BaseDataset, BaseDriver, BaseLayer
|
|
except ImportError:
|
|
# To be able to run in standalone mode
|
|
class BaseDriver(object):
|
|
pass
|
|
|
|
class BaseDataset(object):
|
|
pass
|
|
|
|
class BaseLayer(object):
|
|
pass
|
|
|
|
|
|
class Layer(BaseLayer):
|
|
def __init__(self):
|
|
|
|
# Reserved attribute names. Either those or the corresponding method
|
|
# must be defined
|
|
self.name = "my_layer" # Required, or name() method
|
|
|
|
self.fid_name = "my_fid" # Optional
|
|
|
|
self.fields = [
|
|
{"name": "boolField", "type": "Boolean"},
|
|
{"name": "int16Field", "type": "Integer16"},
|
|
{"name": "int32Field", "type": "Integer"},
|
|
{"name": "int64Field", "type": "Integer64"},
|
|
{"name": "realField", "type": "Real"},
|
|
{"name": "floatField", "type": "Float"},
|
|
{"name": "strField", "type": "String"},
|
|
{"name": "strNullField", "type": "String"},
|
|
{"name": "strUnsetField", "type": "String"},
|
|
{"name": "binaryField", "type": "Binary"},
|
|
{"name": "timeField", "type": "Time"},
|
|
{"name": "dateField", "type": "Date"},
|
|
{"name": "datetimeField", "type": "DateTime"},
|
|
] # Required, or fields() method
|
|
|
|
self.geometry_fields = [
|
|
{
|
|
"name": "geomField",
|
|
"type": "Point", # optional
|
|
"srs": "EPSG:4326", # optional
|
|
}
|
|
] # Required, or geometry_fields() method
|
|
|
|
self.metadata = {"foo": "bar"} # optional
|
|
|
|
# uncomment if __iter__() honour self.attribute_filter
|
|
# self.iterator_honour_attribute_filter = True
|
|
|
|
# uncomment if __iter__() honour self.spatial_filter
|
|
# self.iterator_honour_spatial_filter = True
|
|
|
|
# uncomment if feature_count() honour self.attribute_filter
|
|
# self.feature_count_honour_attribute_filter = True
|
|
|
|
# uncomment if feature_count() honour self.spatial_filter
|
|
# self.feature_count_honour_spatial_filter = True
|
|
|
|
# End of reserved attribute names
|
|
|
|
self.count = 5
|
|
|
|
# Required, unless self.name attribute is defined
|
|
# def name(self):
|
|
# return 'my_layer'
|
|
|
|
# Optional. If not defined, fid name is 'fid'
|
|
# def fid_name(self):
|
|
# return 'my_fid'
|
|
|
|
# Required, unless self.geometry_fields attribute is defined
|
|
# def geometry_fields(self):
|
|
# return [...]
|
|
|
|
# Required, unless self.required attribute is defined
|
|
# def fields(self):
|
|
# return [...]
|
|
|
|
# Optional. Only to be usd if self.metadata field is not defined
|
|
# def metadata(self, domain):
|
|
# if domain is None:
|
|
# return {'foo': 'bar'}
|
|
# return None
|
|
|
|
# Optional. Called when self.attribute_filter is changed by GDAL
|
|
# def attribute_filter_changed(self):
|
|
# # You may change self.iterator_honour_attribute_filter
|
|
# # or feature_count_honour_attribute_filter
|
|
# pass
|
|
|
|
# Optional. Called when self.spatial_filter is changed by GDAL
|
|
# def spatial_filter_changed(self):
|
|
# # You may change self.iterator_honour_spatial_filter
|
|
# # or feature_count_honour_spatial_filter
|
|
# pass
|
|
|
|
# Optional
|
|
def test_capability(self, cap):
|
|
if cap == BaseLayer.FastGetExtent:
|
|
return True
|
|
if cap == BaseLayer.StringsAsUTF8:
|
|
return True
|
|
# if cap == BaseLayer.FastSpatialFilter:
|
|
# return False
|
|
# if cap == BaseLayer.RandomRead:
|
|
# return False
|
|
if cap == BaseLayer.FastFeatureCount:
|
|
return self.attribute_filter is None and self.spatial_filter is None
|
|
return False
|
|
|
|
# Optional
|
|
def extent(self, force_computation):
|
|
return [2.1, 49, 3, 50] # minx, miny, maxx, maxy
|
|
|
|
# Optional.
|
|
def feature_count(self, force_computation):
|
|
# As we did not declare feature_count_honour_attribute_filter and
|
|
# feature_count_honour_spatial_filter, the below case cannot happen
|
|
# But this is to illustrate that you can callback the default implementation
|
|
# if needed
|
|
# if self.attribute_filter is not None or \
|
|
# self.spatial_filter is not None:
|
|
# return super(Layer, self).feature_count(force_computation)
|
|
|
|
return self.count
|
|
|
|
# Required. You do not need to handle the case of simultaneous iterators on
|
|
# the same Layer object.
|
|
def __iter__(self):
|
|
for i in range(self.count):
|
|
properties = {
|
|
"boolField": True,
|
|
"int16Field": 32767,
|
|
"int32Field": i + 2,
|
|
"int64Field": 1234567890123,
|
|
"realField": 1.23,
|
|
"floatField": 1.2,
|
|
"strField": "foo",
|
|
"strNullField": None,
|
|
"binaryField": b"\x01\x00\x02",
|
|
"timeField": "12:34:56.789",
|
|
"dateField": "2017-04-26",
|
|
"datetimeField": "2017-04-26T12:34:56.789Z",
|
|
}
|
|
|
|
yield {
|
|
"type": "OGRFeature",
|
|
"id": i + 1,
|
|
"fields": properties,
|
|
"geometry_fields": {"geomField": "POINT(2 49)"},
|
|
"style": "SYMBOL(a:0)" if i % 2 == 0 else None,
|
|
}
|
|
|
|
# Optional
|
|
# def feature_by_id(self, fid):
|
|
# return {}
|
|
|
|
|
|
class Dataset(BaseDataset):
|
|
|
|
# Optional, but implementations will generally need it
|
|
def __init__(self, filename):
|
|
# If the layers member is set, layer_count() and layer() will not be used
|
|
self.layers = [Layer()]
|
|
self.metadata = {"foo": "bar"}
|
|
|
|
# Optional, called on native object destruction
|
|
def close(self):
|
|
pass
|
|
|
|
# Optional. Only to be usd if self.metadata field is not defined
|
|
# def metadata(self, domain):
|
|
# if domain is None:
|
|
# return {'foo': 'bar'}
|
|
# return None
|
|
|
|
# Required, unless a layers attribute is set in __init__
|
|
# def layer_count(self):
|
|
# return len(self.layers)
|
|
|
|
# Required, unless a layers attribute is set in __init__
|
|
# def layer(self, idx):
|
|
# return self.layers[idx]
|
|
|
|
|
|
# Required: class deriving from BaseDriver
|
|
class Driver(BaseDriver):
|
|
|
|
# Optional. Called the first time the driver is loaded
|
|
def __init__(self):
|
|
pass
|
|
|
|
# Required
|
|
def identify(self, filename, first_bytes, open_flags, open_options={}):
|
|
return filename == "DUMMY:"
|
|
|
|
# Required
|
|
def open(self, filename, first_bytes, open_flags, open_options={}):
|
|
if not self.identify(filename, first_bytes, open_flags):
|
|
return None
|
|
return Dataset(filename)
|