def metadata_update_redirect(self, url, request=None):
     url = url.replace('/metadata', '')
     resource_identifier = url.split('/')[-1]
     try:
         resource = ResourceBase.objects.get(id=int(resource_identifier))
     except ValueError:
         from geonode.layers.views import _resolve_dataset
         resource = _resolve_dataset(request, resource_identifier,
                                     'base.change_resourcebase',
                                     'Not allowed')
     resource_identifier = resource.id
     resource_type = resource.resource_type
     return resource_detail_url(resource_type, resource_identifier)
Esempio n. 2
0
def dataset_style_upload(request, layername):
    def respond(*args, **kw):
        kw['content_type'] = 'text/html'
        return json_response(*args, **kw)
    form = LayerStyleUploadForm(request.POST, request.FILES)
    if not form.is_valid():
        return respond(errors="Please provide an SLD file.")

    data = form.cleaned_data
    layer = _resolve_dataset(
        request,
        layername,
        'base.change_resourcebase',
        _PERMISSION_MSG_MODIFY)

    sld = request.FILES['sld'].read()
    sld_name = None
    try:
        # Check SLD is valid
        try:
            if sld:
                if isfile(sld):
                    with open(sld) as sld_file:
                        sld = sld_file.read()
                etree.XML(sld)
        except Exception:
            logger.exception("The uploaded SLD file is not valid XML")
            raise Exception(
                "The uploaded SLD file is not valid XML")

        sld_name = extract_name_from_sld(
            gs_catalog, sld, sld_file=request.FILES['sld'])
    except Exception as e:
        respond(errors=f"The uploaded SLD file is not valid XML: {e}")

    name = data.get('name') or sld_name

    set_dataset_style(layer, data.get('title') or name, sld)

    return respond(
        body={
            'success': True,
            'style': data.get('title') or name,
            'updated': data['update']})
Esempio n. 3
0
def dataset_style(request, layername):
    layer = _resolve_dataset(
        request,
        layername,
        'base.change_resourcebase',
        _PERMISSION_MSG_MODIFY)

    style_name = request.POST.get('defaultStyle')

    # would be nice to implement
    # better handling of default style switching
    # in layer model or deeper (gsconfig.py, REST API)

    old_default = layer.default_style
    if old_default.name == style_name:
        return HttpResponse(
            f"Default style for {layer.name} remains {style_name}", status=200)

    # This code assumes without checking
    # that the new default style name is included
    # in the list of possible styles.

    new_style = next(style for style in layer.styles if style.name == style_name)

    # Does this change this in geoserver??
    layer.default_style = new_style
    layer.styles = [
        s for s in layer.styles if s.name != style_name] + [old_default]
    layer.save(notify=True)

    # Invalidate GeoWebCache for the updated resource
    try:
        _stylefilterparams_geowebcache_dataset(layer.alternate)
        _invalidate_geowebcache_dataset(layer.alternate)
    except Exception:
        pass

    return HttpResponse(
        f"Default style for {layer.name} changed to {style_name}", status=200)
Esempio n. 4
0
def dataset_style_manage(request, layername):
    layer = _resolve_dataset(request, layername, 'layers.change_dataset_style',
                             _PERMISSION_MSG_MODIFY)

    if request.method == 'GET':
        try:
            cat = gs_catalog

            # First update the layer style info from GS to GeoNode's DB
            try:
                set_styles(layer, cat)
            except AttributeError:
                logger.warn(
                    'Unable to set the default style.  Ensure Geoserver is running and that this layer exists.'
                )

            gs_styles = []
            # Temporary Hack to remove GeoServer temp styles from the list
            Style.objects.filter(
                name__iregex=r'\w{8}-\w{4}-\w{4}-\w{4}-\w{12}_(ms)_\d{13}'
            ).delete()
            for style in Style.objects.values('name', 'sld_title'):
                gs_styles.append((style['name'], style['sld_title']))
            current_dataset_styles = layer.styles.all()
            dataset_styles = []
            for style in current_dataset_styles:
                sld_title = style.name
                try:
                    if style.sld_title:
                        sld_title = style.sld_title
                except Exception:
                    tb = traceback.format_exc()
                    logger.debug(tb)
                dataset_styles.append((style.name, sld_title))

            # Render the form
            def_sld_name = None  # noqa
            def_sld_title = None  # noqa
            default_style = None
            if layer.default_style:
                def_sld_name = layer.default_style.name  # noqa
                def_sld_title = layer.default_style.name  # noqa
                try:
                    if layer.default_style.sld_title:
                        def_sld_title = layer.default_style.sld_title
                except Exception:
                    tb = traceback.format_exc()
                    logger.debug(tb)
                default_style = (def_sld_name, def_sld_title)

            return render(request,
                          'datasets/dataset_style_manage.html',
                          context={
                              "layer": layer,
                              "gs_styles": gs_styles,
                              "dataset_styles": dataset_styles,
                              "dataset_style_names":
                              [s[0] for s in dataset_styles],
                              "default_style": default_style
                          })
        except (FailedRequestError, OSError):
            tb = traceback.format_exc()
            logger.debug(tb)
            msg = (
                f'Could not connect to geoserver at "{ogc_server_settings.LOCATION}"'
                f'to manage style information for layer "{layer.name}"')
            logger.debug(msg)
            # If geoserver is not online, return an error
            return render(request,
                          'datasets/dataset_style_manage.html',
                          context={
                              "layer": layer,
                              "error": msg
                          })
    elif request.method in ('POST', 'PUT', 'DELETE'):
        try:
            workspace = get_dataset_workspace(
                layer) or settings.DEFAULT_WORKSPACE
            selected_styles = request.POST.getlist('style-select')
            default_style = request.POST['default_style']

            # Save to GeoServer
            cat = gs_catalog
            try:
                gs_dataset = cat.get_layer(layer.name)
            except Exception:
                gs_dataset = None

            if not gs_dataset:
                gs_dataset = cat.get_layer(layer.alternate)

            if gs_dataset:
                _default_style = cat.get_style(default_style) or \
                    cat.get_style(default_style, workspace=workspace)
                if _default_style:
                    gs_dataset.default_style = _default_style
                elif cat.get_style(default_style,
                                   workspace=settings.DEFAULT_WORKSPACE):
                    gs_dataset.default_style = cat.get_style(
                        default_style, workspace=settings.DEFAULT_WORKSPACE)
                styles = []
                for style in selected_styles:
                    _gs_sld = cat.get_style(style) or cat.get_style(
                        style, workspace=workspace)
                    if _gs_sld:
                        styles.append(_gs_sld)
                    elif cat.get_style(style,
                                       workspace=settings.DEFAULT_WORKSPACE):
                        styles.append(
                            cat.get_style(
                                style, workspace=settings.DEFAULT_WORKSPACE))
                    else:
                        Style.objects.filter(name=style).delete()
                gs_dataset.styles = styles
                cat.save(gs_dataset)

            # Save to Django
            set_styles(layer, cat)

            # Invalidate GeoWebCache for the updated resource
            try:
                _stylefilterparams_geowebcache_dataset(layer.alternate)
                _invalidate_geowebcache_dataset(layer.alternate)
            except Exception:
                pass

            return HttpResponseRedirect(layer.get_absolute_url())
        except (FailedRequestError, OSError, MultiValueDictKeyError):
            tb = traceback.format_exc()
            logger.debug(tb)
            msg = (f'Error Saving Styles for Dataset "{layer.name}"')
            logger.warn(msg)
            return render(request,
                          'datasets/dataset_style_manage.html',
                          context={
                              "layer": layer,
                              "error": msg
                          })
Esempio n. 5
0
def add_datasets_to_map_config(
        request, map_obj, dataset_names, add_base_datasets=True):
    DEFAULT_MAP_CONFIG, DEFAULT_BASE_LAYERS = default_map_config(request)

    bbox = []
    layers = []
    for dataset_name in dataset_names:
        try:
            layer = _resolve_dataset(request, dataset_name)
        except ObjectDoesNotExist:
            # bad layer, skip
            continue
        except Http404:
            # can't find the layer, skip it.
            continue

        if not request.user.has_perm(
                'view_resourcebase',
                obj=layer.get_self_resource()):
            # invisible layer, skip inclusion
            continue

        dataset_bbox = layer.bbox[0:4]
        bbox = dataset_bbox[:]
        bbox[0] = dataset_bbox[0]
        bbox[1] = dataset_bbox[2]
        bbox[2] = dataset_bbox[1]
        bbox[3] = dataset_bbox[3]
        # assert False, str(dataset_bbox)

        def decimal_encode(bbox):
            import decimal
            _bbox = []
            for o in [float(coord) for coord in bbox]:
                if isinstance(o, decimal.Decimal):
                    o = (str(o) for o in [o])
                _bbox.append(o)
            # Must be in the form : [x0, x1, y0, y1
            return [_bbox[0], _bbox[2], _bbox[1], _bbox[3]]

        def sld_definition(style):
            _sld = {
                "title": style.sld_title or style.name,
                "legend": {
                    "height": "40",
                    "width": "22",
                    "href": f"{layer.ows_url}?service=wms&request=GetLegendGraphic&format=image%2Fpng&width=20&height=20&layer={quote(layer.service_typename, safe='')}",
                    "format": "image/png"
                },
                "name": style.name
            }
            return _sld

        config = layer.attribute_config()
        if hasattr(layer, 'srid'):
            config['crs'] = {
                'type': 'name',
                'properties': layer.srid
            }
        # Add required parameters for GXP lazy-loading
        attribution = f"{layer.owner.first_name} {layer.owner.last_name}" if layer.owner.first_name or layer.owner.last_name else str(layer.owner)  # noqa
        srs = getattr(settings, 'DEFAULT_MAP_CRS', 'EPSG:3857')
        srs_srid = int(srs.split(":")[1]) if srs != "EPSG:900913" else 3857
        config["attribution"] = f"<span class='gx-attribution-title'>{attribution}</span>"
        config["format"] = getattr(
            settings, 'DEFAULT_LAYER_FORMAT', 'image/png')
        config["title"] = layer.title
        config["wrapDateLine"] = True
        config["visibility"] = True
        config["srs"] = srs
        config["bbox"] = decimal_encode(
            bbox_to_projection([float(coord) for coord in dataset_bbox] + [layer.srid, ],
                               target_srid=int(srs.split(":")[1]))[:4])
        config["capability"] = {
            "abstract": layer.abstract,
            "store": layer.store,
            "name": layer.alternate,
            "title": layer.title,
            "style": '',
            "queryable": True,
            "subtype": layer.subtype,
            "bbox": {
                layer.srid: {
                    "srs": layer.srid,
                    "bbox": decimal_encode(bbox)
                },
                srs: {
                    "srs": srs,
                    "bbox": decimal_encode(
                        bbox_to_projection([float(coord) for coord in dataset_bbox] + [layer.srid, ],
                                           target_srid=srs_srid)[:4])
                },
                "EPSG:4326": {
                    "srs": "EPSG:4326",
                    "bbox": decimal_encode(bbox) if layer.srid == 'EPSG:4326' else
                    bbox_to_projection(
                        [float(coord) for coord in dataset_bbox] + [layer.srid, ], target_srid=4326)[:4]
                },
                "EPSG:900913": {
                    "srs": "EPSG:900913",
                    "bbox": decimal_encode(bbox) if layer.srid == 'EPSG:900913' else
                    bbox_to_projection(
                        [float(coord) for coord in dataset_bbox] + [layer.srid, ], target_srid=3857)[:4]
                }
            },
            "srs": {
                srs: True
            },
            "formats": ["image/png", "application/atom xml", "application/atom+xml", "application/json;type=utfgrid",
                        "application/openlayers", "application/pdf", "application/rss xml", "application/rss+xml",
                        "application/vnd.google-earth.kml", "application/vnd.google-earth.kml xml",
                        "application/vnd.google-earth.kml+xml", "application/vnd.google-earth.kml+xml;mode=networklink",
                        "application/vnd.google-earth.kmz", "application/vnd.google-earth.kmz xml",
                        "application/vnd.google-earth.kmz+xml", "application/vnd.google-earth.kmz;mode=networklink",
                        "atom", "image/geotiff", "image/geotiff8", "image/gif", "image/gif;subtype=animated",
                        "image/jpeg", "image/png8", "image/png; mode=8bit", "image/svg", "image/svg xml",
                        "image/svg+xml", "image/tiff", "image/tiff8", "image/vnd.jpeg-png",
                        "kml", "kmz", "openlayers", "rss", "text/html; subtype=openlayers", "utfgrid"],
            "attribution": {
                "title": attribution
            },
            "infoFormats": ["text/plain", "application/vnd.ogc.gml", "text/xml", "application/vnd.ogc.gml/3.1.1",
                            "text/xml; subtype=gml/3.1.1", "text/html", "application/json"],
            "styles": [sld_definition(s) for s in layer.styles.all()],
            "prefix": layer.alternate.split(":")[0] if ":" in layer.alternate else "",
            "keywords": [k.name for k in layer.keywords.all()] if layer.keywords else [],
            "llbbox": decimal_encode(bbox) if layer.srid == 'EPSG:4326' else
            bbox_to_projection(
                [float(coord) for coord in dataset_bbox] + [layer.srid, ], target_srid=4326)[:4]
        }

        all_times = None
        if check_ogc_backend(geoserver.BACKEND_PACKAGE):
            if layer.has_time:
                from geonode.geoserver.views import get_capabilities
                # WARNING Please make sure to have enabled DJANGO CACHE as per
                # https://docs.djangoproject.com/en/2.0/topics/cache/#filesystem-caching
                wms_capabilities_resp = get_capabilities(
                    request, layer.id, tolerant=True)
                if wms_capabilities_resp.status_code >= 200 and wms_capabilities_resp.status_code < 400:
                    wms_capabilities = wms_capabilities_resp.getvalue()
                    if wms_capabilities:
                        from owslib.etree import etree as dlxml
                        namespaces = {'wms': 'http://www.opengis.net/wms',
                                      'xlink': 'http://www.w3.org/1999/xlink',
                                      'xsi': 'http://www.w3.org/2001/XMLSchema-instance'}
                        e = dlxml.fromstring(wms_capabilities)
                        for atype in e.findall(
                                f"./[wms:Name='{layer.alternate}']/wms:Dimension[@name='time']", namespaces):
                            dim_name = atype.get('name')
                            if dim_name:
                                dim_name = str(dim_name).lower()
                                if dim_name == 'time':
                                    dim_values = atype.text
                                    if dim_values:
                                        all_times = dim_values.split(",")
                                        break
                if all_times:
                    config["capability"]["dimensions"] = {
                        "time": {
                            "name": "time",
                            "units": "ISO8601",
                            "unitsymbol": None,
                            "nearestVal": False,
                            "multipleVal": False,
                            "current": False,
                            "default": "current",
                            "values": all_times
                        }
                    }

        if layer.subtype in ['tileStore', 'remote']:
            service = layer.remote_service
            source_params = {}
            if service.type in ('REST_MAP', 'REST_IMG'):
                source_params = {
                    "ptype": service.ptype,
                    "remote": True,
                    "url": service.service_url,
                    "name": service.name,
                    "title": f"[R] {service.title}"}
            maplayer = MapLayer(map=map_obj,
                                name=layer.alternate,
                                ows_url=layer.ows_url,
                                dataset_params=json.dumps(config),
                                visibility=True,
                                source_params=json.dumps(source_params)
                                )
        else:
            ogc_server_url = urlsplit(
                ogc_server_settings.PUBLIC_LOCATION).netloc
            dataset_url = urlsplit(layer.ows_url).netloc

            access_token = request.session['access_token'] if request and 'access_token' in request.session else None
            if access_token and ogc_server_url == dataset_url and 'access_token' not in layer.ows_url:
                url = f'{layer.ows_url}?access_token={access_token}'
            else:
                url = layer.ows_url
            maplayer = MapLayer(
                map=map_obj,
                name=layer.alternate,
                ows_url=url,
                # use DjangoJSONEncoder to handle Decimal values
                dataset_params=json.dumps(config, cls=DjangoJSONEncoder),
                visibility=True
            )

        layers.append(maplayer)

    if bbox and len(bbox) >= 4:
        minx, maxx, miny, maxy = [float(coord) for coord in bbox]
        x = (minx + maxx) / 2
        y = (miny + maxy) / 2

        if getattr(
            settings,
            'DEFAULT_MAP_CRS',
                'EPSG:3857') == "EPSG:4326":
            center = list((x, y))
        else:
            center = list(forward_mercator((x, y)))

        if center[1] == float('-inf'):
            center[1] = 0

        BBOX_DIFFERENCE_THRESHOLD = 1e-5

        # Check if the bbox is invalid
        valid_x = (maxx - minx) ** 2 > BBOX_DIFFERENCE_THRESHOLD
        valid_y = (maxy - miny) ** 2 > BBOX_DIFFERENCE_THRESHOLD

        if valid_x:
            width_zoom = math.log(360 / abs(maxx - minx), 2)
        else:
            width_zoom = 15

        if valid_y:
            height_zoom = math.log(360 / abs(maxy - miny), 2)
        else:
            height_zoom = 15

        map_obj.center_x = center[0]
        map_obj.center_y = center[1]
        map_obj.zoom = math.ceil(min(width_zoom, height_zoom))

    map_obj.handle_moderated_uploads()

    if add_base_datasets:
        layers_to_add = DEFAULT_BASE_LAYERS + layers
    else:
        layers_to_add = layers
    config = map_obj.viewer_json(
        request, *layers_to_add)

    config['fromLayer'] = True
    return config