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)
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']})
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)
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 })
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