Source code for eodal.metadata.database.db_model

"""
Implementation of a data model for eodal's metadata DB including
platform specific tables for storing e.g., Sentinel-2 metadata

Copyright (C) 2022 Lukas Valentin Graf

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""
from __future__ import annotations

from sqlalchemy import create_engine
from sqlalchemy import MetaData
from sqlalchemy import ForeignKeyConstraint
from sqlalchemy.orm import declarative_base
from sqlalchemy import Column, String, Float, Date, Integer, Text, Boolean, TIMESTAMP
from geoalchemy2 import Geometry

from eodal.config import get_settings


Settings = get_settings()
logger = Settings.logger

metadata = MetaData(schema=Settings.DEFAULT_SCHEMA)
Base = declarative_base(metadata=metadata)

DB_URL = f"postgresql://{Settings.DB_USER}:" + \
    f"{Settings.DB_PW}@{Settings.DB_HOST}:" + \
    f"{Settings.DB_PORT}/{Settings.DB_NAME}"
engine = create_engine(DB_URL, echo=Settings.ECHO_DB)


[docs] class Regions(Base): __tablename__ = "sentinel2_regions" region_uid = Column(String, nullable=False, primary_key=True) geom = Column(Geometry(geometry_type="POLYGON", srid=4326), nullable=False)
[docs] class S1_Raw_Metadata(Base): __tablename__ = "sentinel1_raw_metadata" scene_id = Column(String, nullable=False, primary_key=True) product_uri = Column(String, nullable=False, primary_key=True) spacecraft_name = Column(String, nullable=False) sensing_orbit_start = Column(Integer, nullable=False) sensing_orbit_stop = Column(Integer, nullable=False) relative_orbit_start = Column(Integer, nullable=False) relative_orbit_stop = Column(Integer, nullable=False) sensing_orbit_direction = Column(String, nullable=False) sensing_time = Column(TIMESTAMP, nullable=False) sensing_date = Column(Date, nullable=False) instrument_mode = Column(String, nullable=False) product_type = Column(String, nullable=False) product_class = Column(String, nullable=False) processing_software_name = Column(String, nullable=False) processing_software_version = Column(String, nullable=False) mission_data_take_id = Column(Integer, nullable=False) storage_device_ip = Column(String) storage_device_ip_alias = Column(String) storage_share = Column(String) # scene footprint geom = Column(Geometry(geometry_type="POLYGON", srid=4326), nullable=False)
[docs] class S2_Raw_Metadata(Base): __tablename__ = "sentinel2_raw_metadata" # scene and tile id scene_id = Column(String, nullable=False, primary_key=True) product_uri = Column(String, nullable=False, primary_key=True) tile_id = Column(String, nullable=False) l1c_tile_id = Column(String) # processing baseline pdgs_baseline = Column(String, nullable=False) # Datatake Information datatakeidentifier = Column(String, nullable=False) # processing level, orbit and spacecraft processing_level = Column(String, nullable=False) sensing_orbit_number = Column(Integer, nullable=False) spacecraft_name = Column(String, nullable=False) sensing_orbit_direction = Column(String, nullable=False) # temporal information sensing_time = Column(TIMESTAMP, nullable=False) sensing_date = Column(Date, nullable=False) # geometry and geolocalisation nrows_10m = Column(Integer, nullable=False) ncols_10m = Column(Integer, nullable=False) nrows_20m = Column(Integer, nullable=False) ncols_20m = Column(Integer, nullable=False) nrows_60m = Column(Integer, nullable=False) ncols_60m = Column(Integer, nullable=False) epsg = Column(Integer, nullable=False) ulx = Column(Float, nullable=False, comment="Upper left corner x coordinate") uly = Column(Float, nullable=False, comment="Upper left corner y coordinate") geom = Column(Geometry(geometry_type="POLYGON", srid=4326), nullable=False) # viewing and illumination angles sun_zenith_angle = Column(Float, nullable=False) sun_azimuth_angle = Column(Float, nullable=False) sensor_zenith_angle = Column(Float, nullable=False) sensor_azimuth_angle = Column(Float, nullable=False) # solar irradiance per spectral band (W/m2sr) solar_irradiance_b01 = Column(Float, nullable=False) solar_irradiance_b02 = Column(Float, nullable=False) solar_irradiance_b03 = Column(Float, nullable=False) solar_irradiance_b04 = Column(Float, nullable=False) solar_irradiance_b05 = Column(Float, nullable=False) solar_irradiance_b06 = Column(Float, nullable=False) solar_irradiance_b07 = Column(Float, nullable=False) solar_irradiance_b08 = Column(Float, nullable=False) solar_irradiance_b8a = Column(Float, nullable=False) solar_irradiance_b09 = Column(Float, nullable=False) solar_irradiance_b10 = Column(Float, nullable=False) solar_irradiance_b11 = Column(Float, nullable=False) solar_irradiance_b12 = Column(Float, nullable=False) # reflectance conversion factor to account for variations in the sun-earth distance reflectance_conversion = Column(Float) # cloudy pixel percentage cloudy_pixel_percentage = Column(Float, nullable=False) degraded_msi_data_percentage = Column(Float, nullable=False) # L2A specific data; therefore nullable nodata_pixel_percentage = Column(Float) dark_features_percentage = Column(Float) cloud_shadow_percentage = Column(Float) vegetation_percentage = Column(Float) not_vegetated_percentage = Column(Float) water_percentage = Column(Float) unclassified_percentage = Column(Float) medium_proba_clouds_percentage = Column(Float) high_proba_clouds_percentage = Column(Float) thin_cirrus_percentage = Column(Float) snow_ice_percentage = Column(Float) # raw representation of metadata XML since not all data was parsed mtd_tl_xml = Column(Text, nullable=False) mtd_msi_xml = Column(Text, nullable=False) # storage location storage_device_ip = Column(String) storage_device_ip_alias = Column(String) # might be necessary storage_share = Column(String, nullable=False) path_type = Column( String, nullable=False, comment="type of the path (e.g., POSIX-Path)" )
[docs] class S2_Processed_Metadata(Base): __tablename__ = "sentinel2_processed_metadata" # scene and tile id scene_id = Column(String, nullable=False, primary_key=True) product_uri = Column(String, nullable=False, primary_key=True) # spatial resolution and used resampling method spatial_resolution = Column(Float, nullable=False) resampling_method = Column(String, nullable=False) # was the scene merged to because of blackfill scene_was_merged = Column(Boolean, nullable=False, default=False) # storage address, filenames storage_device_ip = Column(String, nullable=False) storage_device_ip_alias = Column(String, nullable=False) # Linux storage_share = Column(String, nullable=False) bandstack = Column(String, nullable=False) scl = Column(String) preview = Column(String, nullable=False) path_type = Column( String, nullable=False, comment="type of the path (e.g., POSIX-Path)" ) __table_args__ = ( ForeignKeyConstraint( ["scene_id", "product_uri"], ["sentinel2_raw_metadata.scene_id", "sentinel2_raw_metadata.product_uri"], onupdate="CASCADE", ondelete="CASCADE", ), )
[docs] class PS_SuperDove_Metadata(Base): __tablename__ = "ps_superdove_raw_metadata" scene_id = Column(String, nullable=False, primary_key=True) sensing_time = Column(TIMESTAMP, nullable=False) gsd = Column(Float, nullable=False, comment="Ground Sampling Distance [m]") anomalous_pixels = Column(Integer, nullable=False) clear_confidence_percent = Column(Integer, nullable=False) clear_percent = Column(Integer, nullable=False) cloud_cover = Column(Integer, nullable=False) cloud_percent = Column(Integer, nullable=False) ground_control = Column(Boolean, nullable=False) heavy_haze_percent = Column(Integer, nullable=False) instrument = Column(String, nullable=False) item_type = Column(String, nullable=False) light_haze_percent = Column(Integer, nullable=False) pixel_resolution = Column(Integer, nullable=False) provider = Column(String, nullable=False) quality_category = Column(String, nullable=False) satellite_azimuth = Column(Float, nullable=False) satellite_id = Column(String, nullable=False) shadow_percent = Column(Integer, nullable=False) snow_ice_percent = Column(Integer, nullable=False) strip_id = Column(String, nullable=False) sun_azimuth = Column(Float, nullable=False) sun_elevation = Column(Float, nullable=False) view_angle = Column(Float, nullable=False) visible_confidence_percent = Column(Integer, nullable=False) visible_percent = Column(Integer, nullable=False) geom = Column(Geometry(geometry_type="POLYGON", srid=4326), nullable=False) nrows = Column(Integer, nullable=False) ncols = Column(Integer, nullable=False) epsg = Column(Integer, nullable=False) orbit_direction = Column(String, nullable=False) # storage location storage_device_ip = Column(String) storage_device_ip_alias = Column(String) # might be necessary storage_share = Column(String, nullable=False) path_type = Column( String, nullable=False, comment="type of the path (e.g., POSIX-Path)" )
[docs] def create_tables() -> None: """ creates all Sentinel-2 related tables in the current <DEFAULT_SCHEMA>. """ try: Base.metadata.create_all(bind=engine) for table in Base.metadata.tables.keys(): logger.info(f"Created table {table}") except Exception as e: raise Exception(f"Could not create table: {e}")
if __name__ == "__main__": create_tables()