#########################################################################
#
# Copyright (C) 2018 OSGeo
#
# 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/>.
#
#########################################################################
import uuid
import logging
import geoserver
from geoserver.catalog import ConflictingDataError, UploadError
from geoserver.resource import FeatureType, Coverage
from django.conf import settings
from geonode import GeoNodeException
from geonode.layers.utils import dataset_type, get_files
from .helpers import (
GEOSERVER_LAYER_TYPES,
gs_catalog,
get_store,
get_sld_for,
ogc_server_settings,
_create_db_featurestore,
_create_featurestore,
_create_coveragestore,
)
[docs]
logger = logging.getLogger(__name__)
[docs]
def geoserver_dataset_type(filename):
the_type = dataset_type(filename)
return GEOSERVER_LAYER_TYPES[the_type]
[docs]
def geoserver_upload(
dataset,
base_file,
user,
name,
overwrite=True,
title=None,
abstract=None,
permissions=None,
keywords=(),
charset="UTF-8",
):
# Step 2. Check that it is uploading to the same resource type as
# the existing resource
logger.debug(
">>> Step 2. Make sure we are not trying to overwrite a " "existing resource named [%s] with the wrong type",
name,
)
the_dataset_type = geoserver_dataset_type(base_file)
# Get a short handle to the gsconfig geoserver catalog
cat = gs_catalog
# Ahmed Nour: get workspace by name instead of get default one.
workspace = cat.get_workspace(settings.DEFAULT_WORKSPACE)
# Check if the store exists in geoserver
try:
store = get_store(cat, name, workspace=workspace)
except geoserver.catalog.FailedRequestError:
# There is no store, ergo the road is clear
pass
else:
# If we get a store, we do the following:
resources = cat.get_resources(names=[name], stores=[store], workspaces=[workspace])
if len(resources) > 0:
# If our resource is already configured in the store it needs
# to have the right resource type
for resource in resources:
if resource.name == name:
msg = "Name already in use and overwrite is False"
assert overwrite, msg
existing_type = resource.resource_type
if existing_type != the_dataset_type:
msg = (
f"Type of uploaded file {name} ({the_dataset_type}) "
"does not match type of existing "
f"resource type {existing_type}"
)
logger.debug(msg)
raise GeoNodeException(msg)
# Step 3. Identify whether it is vector or raster and which extra files
# are needed.
logger.debug(">>> Step 3. Identifying if [%s] is vector or raster and " "gathering extra files", name)
if the_dataset_type == FeatureType.resource_type:
logger.debug("Uploading vector layer: [%s]", base_file)
if ogc_server_settings.DATASTORE:
create_store_and_resource = _create_db_featurestore
else:
create_store_and_resource = _create_featurestore
elif the_dataset_type == Coverage.resource_type:
logger.debug("Uploading raster layer: [%s]", base_file)
create_store_and_resource = _create_coveragestore
else:
msg = (
f"The layer type for name {name} is {the_dataset_type}. It should be "
f"{FeatureType.resource_type} or {Coverage.resource_type},"
)
logger.warn(msg)
raise GeoNodeException(msg)
# Step 4. Create the store in GeoServer
logger.debug(">>> Step 4. Starting upload of [%s] to GeoServer...", name)
# Get the helper files if they exist
files, _tmpdir = get_files(base_file)
data = files
if "shp" not in files:
data = base_file
try:
store, gs_resource = create_store_and_resource(
name, data, charset=charset, overwrite=overwrite, workspace=workspace
)
except UploadError as e:
msg = f"Could not save the layer {name}, there was an upload " f"error: {e}"
logger.warn(msg)
e.args = (msg,)
raise
except ConflictingDataError as e:
# A datastore of this name already exists
msg = (
f"GeoServer reported a conflict creating a store with name {name}: "
f'"{e}". This should never happen because a brand new name '
"should have been generated. But since it happened, "
"try renaming the file or deleting the store in GeoServer."
)
logger.warn(msg)
e.args = (msg,)
raise
except Exception as e:
logger.error("Error during the creation of the resource in GeoServer", exc_info=e)
raise e
logger.debug(f"The File {name} has been sent to GeoServer without errors.")
# Step 5. Create the resource in GeoServer
logger.debug(f">>> Step 5. Generating the metadata for {name} after successful import to GeoSever")
# Verify the resource was created
if not gs_resource:
gs_resource = gs_catalog.get_resource(name=name, workspace=workspace)
if not gs_resource:
msg = f"GeoNode encountered problems when creating layer {name}.It cannot find the Dataset that matches this Workspace.try renaming your files."
logger.warn(msg)
raise GeoNodeException(msg)
assert gs_resource.name == name
# Step 6. Make sure our data always has a valid projection
logger.debug(f">>> Step 6. Making sure [{name}] has a valid projection")
_native_bbox = None
try:
_native_bbox = gs_resource.native_bbox
except Exception:
pass
if _native_bbox and len(_native_bbox) >= 5 and _native_bbox[4:5][0] == "EPSG:4326":
box = _native_bbox[:4]
minx, maxx, miny, maxy = [float(a) for a in box]
if (
-180 <= round(minx, 5) <= 180
and -180 <= round(maxx, 5) <= 180
and -90 <= round(miny, 5) <= 90
and -90 <= round(maxy, 5) <= 90
):
gs_resource.latlon_bbox = _native_bbox
gs_resource.projection = "EPSG:4326"
else:
logger.warning("BBOX coordinates outside normal EPSG:4326 values for layer " "[%s].", name)
_native_bbox = [-180, -90, 180, 90, "EPSG:4326"]
gs_resource.latlon_bbox = _native_bbox
gs_resource.projection = "EPSG:4326"
logger.debug("BBOX coordinates forced to [-180, -90, 180, 90] for layer [%s].", name)
# Step 7. Create the style and assign it to the created resource
logger.debug(f">>> Step 7. Creating style for [{name}]")
cat.save(gs_resource)
publishing = cat.get_layer(name) or gs_resource
sld = None
try:
if "sld" in files:
with open(files["sld"], "rb") as f:
sld = f.read()
else:
sld = get_sld_for(cat, dataset)
except Exception as e:
logger.exception(e)
style = None
if sld:
try:
style = cat.get_style(name, workspace=workspace)
except geoserver.catalog.FailedRequestError:
style = cat.get_style(name)
try:
overwrite = style or False
cat.create_style(name, sld, overwrite=overwrite, raw=True, workspace=workspace)
cat.reset()
except geoserver.catalog.ConflictingDataError as e:
msg = f"There was already a style named {name}_dataset in GeoServer, " f'try to use: "{e}"'
logger.warn(msg)
e.args = (msg,)
except geoserver.catalog.UploadError as e:
msg = f"Error while trying to upload style named {name}_dataset in GeoServer, " f'try to use: "{e}"'
e.args = (msg,)
logger.exception(e)
if style is None:
try:
style = cat.get_style(name, workspace=workspace) or cat.get_style(name)
except Exception as e:
style = cat.get_style("point")
msg = f'Could not find any suitable style in GeoServer for Dataset: "{name}"'
e.args = (msg,)
logger.exception(e)
if style:
publishing.default_style = style
logger.debug("default style set to %s", name)
try:
cat.save(publishing)
except geoserver.catalog.FailedRequestError as e:
msg = f"Error while trying to save resource named {publishing} in GeoServer, " f'try to use: "{e}"'
e.args = (msg,)
logger.exception(e)
# Step 8. Create the Django record for the layer
logger.debug(">>> Step 8. Creating Django record for [%s]", name)
alternate = f"{workspace.name}:{gs_resource.name}"
dataset_uuid = str(uuid.uuid4())
defaults = dict(
store=gs_resource.store.name,
subtype=gs_resource.store.resource_type,
alternate=alternate,
title=title or gs_resource.title,
uuid=dataset_uuid,
abstract=abstract or gs_resource.abstract or "",
owner=user,
)
return name, workspace.name, defaults, gs_resource