provider.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  1. # -*- coding: utf-8 -*-
  2. """
  3. Create and init the Earth Engine Qgis data provider
  4. """
  5. import json
  6. from qgis.PyQt.QtCore import QObject
  7. from qgis.core import (
  8. QgsRasterDataProvider, QgsRasterIdentifyResult, QgsProviderRegistry,
  9. QgsProviderMetadata, QgsMessageLog, Qgis, QgsRaster, QgsRasterInterface,
  10. QgsVectorDataProvider, QgsDataProvider, QgsCoordinateReferenceSystem
  11. )
  12. BAND_TYPES = {
  13. 'int8': Qgis.Int16,
  14. 'int16': Qgis.Int16,
  15. 'int32': Qgis.Int32,
  16. 'int64': Qgis.Int32,
  17. 'uint8': Qgis.UInt16,
  18. 'uint16': Qgis.UInt16,
  19. 'uint32': Qgis.UInt32,
  20. 'byte': Qgis.Byte,
  21. 'short': Qgis.Int16,
  22. 'int': Qgis.Int16,
  23. 'long': Qgis.Int32,
  24. 'float': Qgis.Float32,
  25. 'double': Qgis.Float64
  26. }
  27. class EarthEngineRasterDataProvider(QgsRasterDataProvider):
  28. PARENT = QObject()
  29. # def __getattribute__(self, attr):
  30. # method = object.__getattribute__(self, attr)
  31. # # if not method:
  32. # # raise Exception("Method %s not implemented" % attr)
  33. # if callable(method):
  34. # print(f"method: {attr}")
  35. # return method
  36. def __init__(self, *args, **kwargs):
  37. super().__init__(*args, **kwargs)
  38. self.ee_object = None
  39. self._args = args
  40. self._kwargs = kwargs
  41. # create WMS provider
  42. self.wms = QgsProviderRegistry.instance().createProvider('wms', *args, **kwargs)
  43. @classmethod
  44. def description(cls):
  45. return 'Google Earth Engine Raster Data Provider'
  46. @classmethod
  47. def providerKey(cls):
  48. return 'EE'
  49. @classmethod
  50. def createProvider(cls, uri, providerOptions, flags=None):
  51. # compatibility with Qgis < 3.16, ReadFlags only available since 3.16
  52. if Qgis.QGIS_VERSION_INT >= 31600:
  53. flags = QgsDataProvider.ReadFlags()
  54. return EarthEngineRasterDataProvider(uri, providerOptions, flags)
  55. else:
  56. return EarthEngineRasterDataProvider(uri, providerOptions)
  57. # ============================
  58. # QgsDataProvider methods
  59. def crs(self):
  60. return QgsCoordinateReferenceSystem('EPSG:3857')
  61. def setDataSourceUri(self, uri):
  62. self.wms.setDataSourceUri(uri)
  63. def dataSourceUri(self, expandAuthConfig = None):
  64. return self.wms.dataSourceUri(expandAuthConfig)
  65. def dataComment(self):
  66. return self.wms.dataComment()
  67. def flags(self):
  68. return self.wms.flags()
  69. def temporalCapabilities(self):
  70. return self.wms.temporalCapabilities()
  71. def extent(self):
  72. return self.wms.extent()
  73. def isValid(self):
  74. return self.wms.isValid()
  75. def updateExtents(self):
  76. return self.wms.updateExtents()
  77. def setSubsetString(self, subset, updateFeatureCount = True):
  78. return self.wms.setSubsetString(subset, updateFeatureCount)
  79. def supportsSubsetString(self):
  80. return self.wms.supportsSubsetString()
  81. def subsetString(self):
  82. return self.wms.subsetString()
  83. def subLayers(self):
  84. return self.wms.subLayers()
  85. def subLayerStyles(self):
  86. return self.wms.subLayerStyles()
  87. def subLayerCount(self):
  88. return self.wms.subLayerCount()
  89. def setLayerOrder(self, layers):
  90. return self.wms.setLayerOrder(layers)
  91. def setSubLayerVisibility(self, name, vis):
  92. return self.wms.setSubLayerVisibility(name, vis)
  93. def name(self):
  94. return 'EE'
  95. def fileVectorFilter(self):
  96. return self.wms.fileVectorFilters()
  97. def fileRasterFilter(self):
  98. return self.wms.fileRasterFilters()
  99. def reloadData(self):
  100. return self.wms.reloadData()
  101. def timestamp(self):
  102. return self.wms.timestamp()
  103. def dataTimestamp(self):
  104. return self.wms.dataTimestamp()
  105. def error(self):
  106. return self.wms.error()
  107. def invalidateConnections(self, connection):
  108. return self.wms.invalidateConnections(connection)
  109. def enterUpdateMode(self):
  110. return self.wms.enterUpdateMode()
  111. def leaveUpdateMode(self):
  112. return self.wms.leaveUpdateMode()
  113. def setListening(self, isListening):
  114. return self.wms.setListening(isListening)
  115. def renderInPreview(self, context):
  116. return self.wms.renderInPreview(context)
  117. def layerMetadata(self):
  118. return self.wms.layerMetadata()
  119. def writeLayerMetadata(self, metadata):
  120. return self.wms.writeLayerMetadata(metadata)
  121. def setTransformContext(self, transformContext):
  122. return self.wms.setTransformContext(transformContext)
  123. def reloadProviderData(self):
  124. return self.wms.reloadProviderData()
  125. # # QgsRasterDataProvider
  126. def providerCapabilities(self):
  127. return self.wms.providerCapabilities()
  128. def fields(self):
  129. return self.wms.fields()
  130. def colorInterpretation(self, bandNo):
  131. return self.wms.colorInterpretation(bandNo)
  132. def reload(self):
  133. return self.wms.reload()
  134. def bandScale(self, bandNo):
  135. return self.wms.bandScale(bandNo)
  136. def bandOffset(self, bandNo):
  137. return self.wms.bandObbset(bandNo)
  138. def sourceHasNoDataValue(self, bandNo):
  139. return self.wms.sourceHasNoDataValue(bandNo)
  140. def useSourceNoDataValue(self, bandNo):
  141. return self.wms.useSourceNoDataValue(bandNo)
  142. def setUseSourceNoDataValue(self, bandNo, use):
  143. return self.wms.setUseSourceNoDataValue(bandNo, use)
  144. def sourceNoDataValue(self, bandNo):
  145. return self.wms.sourceNoDataValue(bandNo)
  146. def setUserNoDataValue(self, bandNo, noData):
  147. return self.wms.setUserNoDataValue(bandNo, noData)
  148. def userNoDataValues(self, bandNo):
  149. return self.wms.userNoDataValues(bandNo)
  150. def colorTable(self, bandNo):
  151. return self.wms.colorTable(bandNo)
  152. def supportsLegendGraphic(self):
  153. return self.wms.supportsLegendGraphic()
  154. def getLegendGraphic(self, scale=0, forceRefresh=False, visibleExtent=None):
  155. return self.wms.getLegendGraphic(scale, forceRefresh, visibleExtent)
  156. def getLegendGraphicFetcher(self, mapSettings):
  157. return self.wms.getLegendGraphicFetcher(mapSettings)
  158. # buildPyramids()
  159. # buildPyramidList()
  160. def htmlMetadata(self):
  161. return json.dumps(self.ee_object.getInfo())
  162. def identify(self, point, format, boundingBox=None, width=None, height=None, dpi=None):
  163. # TODO: speed-up, extend this to maintain cache of visible image, update cache on-the-fly when needed
  164. import ee
  165. from ee_plugin import Map
  166. from ee_plugin import utils
  167. point = utils.geom_to_geo(point)
  168. point_ee = ee.Geometry.Point([point.x(), point.y()])
  169. scale = Map.getScale()
  170. value = self.ee_object.reduceRegion(ee.Reducer.first(), point_ee, scale).getInfo()
  171. band_indices = range(1, self.bandCount() + 1)
  172. band_names = [self.generateBandName(band_no) for band_no in band_indices]
  173. band_values = [value[band_name] for band_name in band_names]
  174. value = dict(zip(band_indices, band_values))
  175. result = QgsRasterIdentifyResult(QgsRaster.IdentifyFormatValue, value)
  176. return result
  177. # sample()
  178. def lastErrorTitle(self):
  179. return self.wms.lastErrorTitle()
  180. def lastError(self):
  181. return self.wms.lastError()
  182. def lastErrorFormat(self):
  183. return self.wms.lastErrorFormat()
  184. def isEditable(self):
  185. return self.wms.isEditable()
  186. def setEditable(self, enabled):
  187. return self.wms.setIsEditable(enabled)
  188. # write()
  189. # setNoDataValue()
  190. def remove(self):
  191. return self.wms.remove()
  192. # validateCreationOptions()
  193. # validatePyramidsConfigOptions()
  194. def stepWidth(self):
  195. return self.wms.stepWidth()
  196. def stepHeight(self):
  197. return self.wms.stepHeight()
  198. def nativeResolutions(self):
  199. return self.wms.nativeResolutions()
  200. def ignoreExtents(self):
  201. return self.wms.ignoreExtents()
  202. def transformCoordinates(self, point, type):
  203. return self.wms.transformCoordinates(point, type)
  204. def enableProviderResampling(self, enable):
  205. return self.wms.enableProviderResampling(enable)
  206. def setZoomedInResamplingMethod(self, method):
  207. return self.wms.setZoomedInResamplingMethod(method)
  208. def setZoomedOutResamplingMethod(self, method):
  209. return self.wms.setZoomedOutResamplingMethod(method)
  210. def setMaxOversampling(self, factor):
  211. return self.wms.setMaxOversampling(factor)
  212. def writeNativeAttributeTable(self, errorMessage):
  213. return self.wms.writeNativeAttributeTable(errorMessage)
  214. def readNativeAttributeTable(self, errorMessage):
  215. return self.wms.readNativeAttributeTable(errorMessage)
  216. # readBlock()
  217. # QgsWmsProvider
  218. def getMapUrl(self):
  219. return self.wms.getMapUrl()
  220. def getFeatureInfoUrl(self):
  221. return self.wms.getFeatureInfoUrl()
  222. def getTileUrl(self):
  223. return self.wms.getTileUrl()
  224. def getLegendGraphicUrl(self):
  225. return self.wms.getLegendGraphicUrl()
  226. # QgsRasterInterface
  227. def clone(self):
  228. provider = EarthEngineRasterDataProvider(*self._args, **self._kwargs)
  229. provider.wms.setDataSourceUri(self.wms.dataSourceUri())
  230. provider.set_ee_object(self.ee_object)
  231. provider.setParent(EarthEngineRasterDataProvider.PARENT)
  232. return provider
  233. def capabilities(self):
  234. caps = QgsRasterInterface.Size | QgsRasterInterface.Identify | QgsRasterInterface.IdentifyValue
  235. return QgsRasterDataProvider.ProviderCapabilities(caps)
  236. def dataType(self, band_no):
  237. return self.wms.dataType(band_no)
  238. def sourceDataType(self, band_no):
  239. return self.wms.sourceDataType(band_no)
  240. def bandCount(self):
  241. if self.ee_object:
  242. return len(self.ee_info['bands'])
  243. else:
  244. return 1 # fall back to default if ee_object is not set
  245. def generateBandName(self, band_no):
  246. return self.ee_info['bands'][band_no - 1]['id']
  247. def xBlockSize(self):
  248. return self.wms.xBlockSize()
  249. def yBlockSize(self):
  250. return self.wms.yBlockSize()
  251. def xSize(self):
  252. return self.wms.xSize()
  253. def ySize(self):
  254. return self.wms.ySize()
  255. def generateBandName(self, band_no):
  256. return self.ee_info['bands'][band_no - 1]['id']
  257. def colorInterpretationName(self, bandNumber):
  258. return self.wms.colorInterpretationName(bandNumber)
  259. def block(self, bandNo, extent, width, height, feedback=None):
  260. return self.wms.block(bandNo, extent, width, height, feedback)
  261. def setInput(self, input):
  262. return self.wms.setInput(input)
  263. def input(self):
  264. return self.wms.input()
  265. def on(self):
  266. return self.wms.on()
  267. def setOn(self, on):
  268. return self.wms.setOn(on)
  269. def sourceInput(self):
  270. return self.wms.sourceInput()
  271. # bandStatistics()
  272. # hasStatistics()
  273. # histogram()
  274. # hasHistogram()
  275. # cumulativeCut()
  276. # writeXml()
  277. # readXml()
  278. def set_ee_object(self, ee_object):
  279. self.ee_object = ee_object
  280. self.ee_info = ee_object.getInfo()
  281. class EarthEngineVectorDataProvider(QgsVectorDataProvider):
  282. # TODO
  283. pass
  284. class EarthEngineRasterCollectionDataProvider(QgsRasterDataProvider):
  285. # TODO
  286. pass
  287. class EarthEngineVectorCollectionDataProvider(QgsVectorDataProvider):
  288. # TODO
  289. pass
  290. def register_data_provider():
  291. metadata = QgsProviderMetadata(
  292. EarthEngineRasterDataProvider.providerKey(),
  293. EarthEngineRasterDataProvider.description(),
  294. EarthEngineRasterDataProvider.createProvider)
  295. registry = QgsProviderRegistry.instance()
  296. registry.registerProvider(metadata)
  297. QgsMessageLog.logMessage('EE provider registered')