Source code for geonode.geoserver.createlayer.utils

#########################################################################
#
# Copyright (C) 2017 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 json
import uuid
import logging
import requests

from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.gis.geos import Polygon
from django.template.defaultfilters import slugify

from geonode import GeoNodeException
from geonode.layers.models import Dataset
from geonode.layers.utils import get_valid_name
from geonode.resource.manager import resource_manager
from geonode.geoserver.helpers import gs_catalog, ogc_server_settings, create_geoserver_db_featurestore


[docs] logger = logging.getLogger(__name__)
[docs] BBOX = [-180, -90, 180, 90]
[docs] DATA_QUALITY_MESSAGE = "Created with GeoNode"
[docs] def create_dataset(name, title, owner_name, geometry_type, attributes=None): """ Create an empty layer in GeoServer and register it in GeoNode. """ # first validate parameters if geometry_type not in ("Point", "LineString", "Polygon"): msg = "geometry must be Point, LineString or Polygon" logger.error(msg) raise GeoNodeException(msg) name = get_valid_name(name) # we can proceed logger.debug("Creating the layer in GeoServer") workspace, datastore = create_gs_dataset(name, title, geometry_type, attributes) logger.debug("Creating the layer in GeoNode") return create_gn_dataset(workspace, datastore, name, title, owner_name)
[docs] def create_gn_dataset(workspace, datastore, name, title, owner_name): """ Associate a layer in GeoNode for a given layer in GeoServer. """ owner = get_user_model().objects.get(username=owner_name) layer = resource_manager.create( str(uuid.uuid4()), resource_type=Dataset, defaults=dict( name=name, workspace=workspace.name, store=datastore.name, subtype="vector", alternate=f"{workspace.name}:{name}", title=title, owner=owner, srid="EPSG:4326", bbox_polygon=Polygon.from_bbox(BBOX), ll_bbox_polygon=Polygon.from_bbox(BBOX), data_quality_statement=DATA_QUALITY_MESSAGE, ), ) to_update = {} if settings.ADMIN_MODERATE_UPLOADS: to_update["is_approved"] = to_update["was_approved"] = False if settings.RESOURCE_PUBLISHING: to_update["is_published"] = to_update["was_published"] = False resource_manager.update(layer.uuid, instance=layer, vals=to_update) resource_manager.set_thumbnail(None, instance=layer) return layer
[docs] def get_attributes(geometry_type, json_attrs=None): """ Convert a JSON representation of attributes to a Python representation. Parameters: - json_attrs: .. code-block:: json { "field_str": "string", "field_int": "integer", "field_date": "date", "field_float": "float" } - geometry_type: A string which can be "Point", "LineString", or "Polygon" Output: .. code-block:: python [ ['the_geom', u'com.vividsolutions.jts.geom.Polygon', {'nillable': False}], ['field_str', 'java.lang.String', {'nillable': True}], ['field_int', 'java.lang.Integer', {'nillable': True}], ['field_date', 'java.util.Date', {'nillable': True}], ['field_float', 'java.lang.Float', {'nillable': True}] ] """ lattrs = [] gattr = [] gattr.append("the_geom") gattr.append(f"com.vividsolutions.jts.geom.{geometry_type}") gattr.append({"nillable": False}) lattrs.append(gattr) if json_attrs: jattrs = json.loads(json_attrs) for jattr in jattrs.items(): lattr = [] attr_name = slugify(jattr[0]) attr_type = jattr[1].lower() if len(attr_name) == 0: msg = f"You must provide an attribute name for attribute of type {attr_type}" logger.error(msg) raise GeoNodeException(msg) if attr_type not in ("float", "date", "string", "integer"): msg = f"{attr_type} is not a valid type for attribute {attr_name}" logger.error(msg) raise GeoNodeException(msg) if attr_type == "date": attr_type = f"java.util.{(attr_type[:1].upper() + attr_type[1:])}" else: attr_type = f"java.lang.{(attr_type[:1].upper() + attr_type[1:])}" lattr.append(attr_name) lattr.append(attr_type) lattr.append({"nillable": True}) lattrs.append(lattr) return lattrs
[docs] def get_or_create_datastore(cat, workspace=None, charset="UTF-8"): """ Get a PostGIS database store or create it in GeoServer if does not exist. """ dsname = ogc_server_settings.datastore_db["NAME"] ds = create_geoserver_db_featurestore(store_name=dsname, workspace=workspace) return ds
[docs] def create_gs_dataset(name, title, geometry_type, attributes=None): """ Create an empty PostGIS layer in GeoServer with a given name, title, geometry_type and attributes. """ native_name = name cat = gs_catalog # get workspace and store workspace = cat.get_default_workspace() # get (or create the datastore) datastore = get_or_create_datastore(cat, workspace) # check if datastore is of PostGIS type if datastore.type != "PostGIS": msg = "To use the createlayer application you must use PostGIS" logger.error(msg) raise GeoNodeException(msg) # check if layer is existing resources = datastore.get_resources() for resource in resources: if resource.name == name: msg = f"There is already a layer named {name} in {workspace}" logger.error(msg) raise GeoNodeException(msg) attributes = get_attributes(geometry_type, attributes) attributes_block = "<attributes>" for spec in attributes: att_name, binding, opts = spec nillable = opts.get("nillable", False) attributes_block += ( "<attribute>" f"<name>{att_name}</name>" f"<binding>{binding}</binding>" f"<nillable>{nillable}</nillable>" "</attribute>" ) attributes_block += "</attributes>" # TODO implement others srs and not only EPSG:4326 xml = ( "<featureType>" f"<name>{name}</name>" f"<nativeName>{native_name}</nativeName>" f"<title>{title}</title>" "<srs>EPSG:4326</srs>" f"<latLonBoundingBox><minx>{BBOX[0]}</minx><maxx>{BBOX[2]}</maxx><miny>{BBOX[1]}</miny><maxy>{BBOX[3]}</maxy>" f"<crs>EPSG:4326</crs></latLonBoundingBox>" f"{attributes_block}" "</featureType>" ) url = f"{ogc_server_settings.rest}/workspaces/{workspace.name}/datastores/{datastore.name}/featuretypes" headers = {"Content-Type": "application/xml"} _user, _password = ogc_server_settings.credentials req = requests.post(url, data=xml, headers=headers, auth=(_user, _password)) if req.status_code != 201: logger.error(f"Request status code was: {req.status_code}") logger.error(f"Response was: {req.text}") raise Exception(f"Dataset could not be created in GeoServer {req.text}") cat.reload() return workspace, datastore