diff --git a/src/main/java/org/gcube/portlets/user/geoportaldataviewer/client/GeoportalDataViewer.java b/src/main/java/org/gcube/portlets/user/geoportaldataviewer/client/GeoportalDataViewer.java index 575bcdd..8236661 100644 --- a/src/main/java/org/gcube/portlets/user/geoportaldataviewer/client/GeoportalDataViewer.java +++ b/src/main/java/org/gcube/portlets/user/geoportaldataviewer/client/GeoportalDataViewer.java @@ -22,6 +22,7 @@ import org.gcube.portlets.user.geoportaldataviewer.client.events.ZoomOutOverMini import org.gcube.portlets.user.geoportaldataviewer.client.events.ZoomOutOverMinimumEventHandler; import org.gcube.portlets.user.geoportaldataviewer.client.gis.MapUtils; import org.gcube.portlets.user.geoportaldataviewer.client.ui.GeonaDataViewMainPanel; +import org.gcube.portlets.user.geoportaldataviewer.client.util.URLUtil; import org.gcube.portlets.user.geoportaldataviewer.shared.gis.GeoQuery; import com.google.gwt.core.client.EntryPoint; @@ -168,7 +169,10 @@ public class GeoportalDataViewer implements EntryPoint { return; } - GWT.log("The layerItem is: " + layerItem); + GWT.log("ON module load The layerItem is: " + layerItem); + String layerName = URLUtil.getValueOfParameter("layers", layerItem.getWmsLink()); + layerItem.setName(layerName); + layerManager.setBaseLayerFromIsProfile(layerItem); layerManager.addLayer(theItemType, null, null, layerItem.getWmsLink(), false, false, null, false, null, null); diff --git a/src/main/java/org/gcube/portlets/user/geoportaldataviewer/client/GeoportalDataViewerConstants.java b/src/main/java/org/gcube/portlets/user/geoportaldataviewer/client/GeoportalDataViewerConstants.java index d7ae1db..6e0d9e1 100644 --- a/src/main/java/org/gcube/portlets/user/geoportaldataviewer/client/GeoportalDataViewerConstants.java +++ b/src/main/java/org/gcube/portlets/user/geoportaldataviewer/client/GeoportalDataViewerConstants.java @@ -26,7 +26,7 @@ public class GeoportalDataViewerConstants { public static final String GET_CENTER_MAP_TO_LONG_LAT = OpenLayersMapParameters.OL_MAP_PARAM.centermap.name(); public static enum MapEventType { - INIT, MAP_ZOOM_END, MOVE_END, ADDED_LAYER_TO_MAP + MOUSE_CLICK, MAP_ZOOM_END, MOVE_END, ADDED_LAYER_TO_MAP } /** @@ -74,7 +74,7 @@ public class GeoportalDataViewerConstants { public static final int LIGHT_MAP_ITALY_FIT_ZOOM_ON = 5; - public static final int MAP_ITALY_FIT_ZOOM_ON = 6; + public static final int MAP_ITALY_FIT_ZOOM_ON = 5; /** * Prints the. diff --git a/src/main/java/org/gcube/portlets/user/geoportaldataviewer/client/GeoportalDataViewerService.java b/src/main/java/org/gcube/portlets/user/geoportaldataviewer/client/GeoportalDataViewerService.java index 0167a55..b4f9611 100644 --- a/src/main/java/org/gcube/portlets/user/geoportaldataviewer/client/GeoportalDataViewerService.java +++ b/src/main/java/org/gcube/portlets/user/geoportaldataviewer/client/GeoportalDataViewerService.java @@ -90,7 +90,7 @@ public interface GeoportalDataViewerService extends RemoteService { * @return the uploaded images for id * @throws Exception */ - List getUploadedImagesForId(String itemType, String itemId, int maxImages) throws Exception; + List getUploadedImagesForId(String itemType, String itemId, Integer maxImages) throws Exception; /** * Gets the public links for. @@ -124,4 +124,5 @@ public interface GeoportalDataViewerService extends RemoteService { List getWFSFeatures(List layerObjects, String mapSrsName, BoundsMap selectBBOX, int maxWFSFeature, double zoomLevel); + } diff --git a/src/main/java/org/gcube/portlets/user/geoportaldataviewer/client/GeoportalDataViewerServiceAsync.java b/src/main/java/org/gcube/portlets/user/geoportaldataviewer/client/GeoportalDataViewerServiceAsync.java index 564b6dd..fd50869 100644 --- a/src/main/java/org/gcube/portlets/user/geoportaldataviewer/client/GeoportalDataViewerServiceAsync.java +++ b/src/main/java/org/gcube/portlets/user/geoportaldataviewer/client/GeoportalDataViewerServiceAsync.java @@ -47,7 +47,8 @@ public interface GeoportalDataViewerServiceAsync { void getGeoNaDataViewProfile(AsyncCallback callback); - void getUploadedImagesForId(String itemType, String itemId, int maxImages, AsyncCallback> callback); + void getUploadedImagesForId(String itemType, String itemId, Integer maxImages, + AsyncCallback> callback); void getPublicLinksFor(GeoNaItemRef item, AsyncCallback asyncCallback); diff --git a/src/main/java/org/gcube/portlets/user/geoportaldataviewer/client/LayerManager.java b/src/main/java/org/gcube/portlets/user/geoportaldataviewer/client/LayerManager.java index 382fff5..5b93a9b 100644 --- a/src/main/java/org/gcube/portlets/user/geoportaldataviewer/client/LayerManager.java +++ b/src/main/java/org/gcube/portlets/user/geoportaldataviewer/client/LayerManager.java @@ -14,6 +14,7 @@ import org.gcube.application.geoportalcommon.shared.products.model.LayerConcessi import org.gcube.application.geoportalcommon.shared.products.model.UploadedImageDV; import org.gcube.portlets.user.geoportaldataviewer.client.GeoportalDataViewerConstants.LayerType; import org.gcube.portlets.user.geoportaldataviewer.client.GeoportalDataViewerConstants.MAP_PROJECTION; +import org.gcube.portlets.user.geoportaldataviewer.client.GeoportalDataViewerConstants.MapEventType; import org.gcube.portlets.user.geoportaldataviewer.client.events.AddedLayerToMapEvent; import org.gcube.portlets.user.geoportaldataviewer.client.events.AddedLayerToMapEventHandler; import org.gcube.portlets.user.geoportaldataviewer.client.events.QueryDataEvent; @@ -35,10 +36,13 @@ import org.gcube.portlets.user.geoportaldataviewer.shared.gis.wms.GeoInformation import org.gcube.portlets.user.geoportaldataviewer.shared.gis.wms.ZAxis; import com.github.gwtbootstrap.client.ui.Button; +import com.github.gwtbootstrap.client.ui.Label; import com.github.gwtbootstrap.client.ui.constants.ButtonType; +import com.github.gwtbootstrap.client.ui.constants.LabelType; import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.Scheduler; import com.google.gwt.core.client.Scheduler.ScheduledCommand; +import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.shared.HandlerManager; @@ -52,6 +56,7 @@ import com.google.gwt.user.client.Window; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.FlexTable; import com.google.gwt.user.client.ui.HTML; +import com.google.gwt.user.client.ui.ScrollPanel; import com.google.gwt.user.client.ui.VerticalPanel; import ol.Coordinate; @@ -78,6 +83,8 @@ public class LayerManager { private HandlerManager applicationBus; + private org.gcube.application.geoportalcommon.shared.LayerItem baseLayerFromISProfile; + /** * Instantiates a new layer manager. * @@ -110,23 +117,9 @@ public class LayerManager { // for (LayerObject layerObj : layerObjects.values()) { // // TODO // isLayerVisible(layerObj.getLayerItem()); -// // } BoundsMap mapBBOX = new BoundsMap(); - // THIS IS SHOULD BE CORRET, THE BOUND AND CRS ARE READ FROM MAP - /* - * mapBBOX.setCrs(olMap.getProjectionCode()); - * mapBBOX.setLowerLeftX(olMap.getExtent().getLowerLeftX()); - * mapBBOX.setLowerLeftY(olMap.getExtent().getLowerLeftY()); - * mapBBOX.setUpperRightX(olMap.getExtent().getUpperRightX()); - * mapBBOX.setUpperRightY(olMap.getExtent().getUpperRightY()); - */ - - // THE FOLLOWING SHOULD BE NOT NEEDED BUT THE GETFEATURE WITH EPSG_3857 SEEMS - // NOT WORK IN OUR GEOSERVER -// double minX = olMap.getExtent().getLowerLeftX(); -// double minY = olMap.getExtent().getLowerLeftY(); double minX = queryEvent.getGeoQuery().getX1(); double minY = queryEvent.getGeoQuery().getY1(); Coordinate centerCoordinate = new Coordinate(minX, minY); @@ -150,6 +143,16 @@ public class LayerManager { GWT.log("Bounds is: " + mapBBOX); GWT.log("MAX_WFS_FEATURES is: " + GeoportalDataViewerConstants.MAX_WFS_FEATURES); // GeoportalDataViewerConstants.print("calling getDataResult"); + +// List mapLayers = olMap.getLayers(); +//// List listLO = new ArrayList(mapLayers.size()); +// for (String layerName : mapLayers) { +//// LayerItem layerItem = new LayerItem(); +//// layerItem.setName(layerName); +//// listLO.add(new LayerObject("Concessione",layerItem)); +// GWT.log("Layer found: "+layerName); +// } + List listLO = new ArrayList(layerObjects.values()); GeoportalDataViewerServiceAsync.Util.getInstance().getDataResult(listLO, olMap.getProjectionCode(), mapBBOX, GeoportalDataViewerConstants.MAX_WFS_FEATURES, olMap.getCurrentZoomLevel(), @@ -165,28 +168,26 @@ public class LayerManager { public void onSuccess(List listGeonaDataObjects) { GWT.log("GeoNaDataObject's is/are " + listGeonaDataObjects.size()); - GWT.log("GeoNaDataObject's: " + listGeonaDataObjects); + //GWT.log("GeoNaDataObject's: " + listGeonaDataObjects); // GeoportalDataViewerConstants.print("GeoNaDataObject's: // "+listGeonaDataObjects); if (listGeonaDataObjects == null || listGeonaDataObjects.isEmpty()) return; - FlexTable flex = new FlexTable(); - flex.setCellPadding(1); - flex.setCellSpacing(1); - flex.getElement().addClassName("popup-table"); - boolean featureFound = false; FeatureRow feature = null; // TODO SWTCH FOR EARCH ITEM TYPE for (GeoNaSpatialQueryResult geoNaDataObject : listGeonaDataObjects) { List features = geoNaDataObject.getFeatures(); + GWT.log("GeoNaDataObject Source layer item name: " + + geoNaDataObject.getSourceLayerObject().getLayerItem().getName()); + // USING ONLY THE FIRST FEATURE IN THE LIST if (features != null && features.size() > 0) { String theProductId = null; - // I need to show exaclty the feature with produc_id == recordId + // I need to show exactly the feature with produc_id == recordId if (mongoItemId != null) { for (FeatureRow fRow : features) { List productIdLst = fRow.getMapProperties() @@ -212,9 +213,19 @@ public class LayerManager { List productIdLst = feature.getMapProperties() .get("product_id"); if (productIdLst == null) { - // in this case the feature returned is a (detail) layer belonging + // in this case the feature/s returned is/are a (detail) layer/s + // belonging // to a record/concessione (not centroid layer), - // so returning + // so calling show popuup info on detail layers if the + // following events are true. + if (queryEvent.getSourceMapEventType() + .equals(MapEventType.MOUSE_CLICK) + && olMap.getCurrentZoomLevel() > OLMapManager.QUERY_MIN_ZOOM_LEVEL) { + + //Here I need to pass only the visible layers + showPopupInfoForLayer(listGeonaDataObjects, + queryEvent.getOnFailureCenterTo()); + } return; } theProductId = productIdLst.get(0); @@ -227,8 +238,9 @@ public class LayerManager { } GWT.log("the product id is: " + theProductId); - //Displaying popup info for centroid layer - showPopupInfoForCentroidLayer(geoNaDataObject, feature, queryEvent.getOnFailureCenterTo()); + // Displaying popup info for centroid layer + showPopupInfoForCentroidLayer(geoNaDataObject, feature, + queryEvent.getOnFailureCenterTo()); // retrieving and showing WMS layers of a concessione if the ZOOM level is > // QUERY_MIN_ZOOM_LEVEL @@ -261,186 +273,7 @@ public class LayerManager { // QUERY_MIN_ZOOM_LEVEL olMap.removeAllDetailLayers(); } - - - // Showing properties belonging to concessioni centroid layer -// Map> entries = feature.getMapProperties(); -// -// String nome = ""; -// String descrizione = ""; -// String date = ""; -// -// for (String key : entries.keySet()) { -// String theValue = entries.get(key).get(0); -// if (key.equalsIgnoreCase("nome")) { -// nome = theValue != null ? theValue : ""; -// } else if (key.equalsIgnoreCase("descrizione")) { -// descrizione = theValue != null ? theValue : ""; -// } else if (key.equalsIgnoreCase("date_scavo")) { -// date = theValue != null ? theValue : ""; -// } -// } - - // GeoportalDataViewerConstants.print("0: "+nome); -// flex.setHTML(0, 0, new HTML(nome).toString()); -// try { -// descrizione = StringUtil.ellipsize(descrizione, 100); -// GWT.log("reduced: " + descrizione); -// } catch (Exception e) { -// GWT.log("error: " + e.getMessage()); -// } -// // GeoportalDataViewerConstants.print("1: "+descrizione); -// flex.setText(1, 0, descrizione); -// // GeoportalDataViewerConstants.print("2: "+date); -// date = StringUtil.formatDate(date); -// flex.setHTML(2, 0, new HTML("" + date + "").toString()); - -// if (geoNaDataObject.getMapImages() != null) { -// for (String key : geoNaDataObject.getMapImages().keySet()) { -// List listUI = geoNaDataObject.getMapImages() -// .get(key); -// GWT.log("Adding images: " + listUI); -// if (listUI != null && listUI.size() > 0) { -// UploadedImageDV img = listUI.get(0); -// if (img.getListWsContent() != null) { -// WorkspaceContentDV wsContent = img.getListWsContent() -// .get(img.getListWsContent().size() - 1); -// String theImgHTML = ""; -// GWT.log("theImgHTML: " + theImgHTML); -// // GeoportalDataViewerConstants.print("The row are: -// // "+flex.getRowCount()); -// flex.setHTML(flex.getRowCount() + 1, 0, theImgHTML); -// } -// } -// } -// } -// -// featureFound = true; - // break; //Only the first one } - -// if (feature == null) -// return; -// -// GWT.log("The selected Feature is: " + feature); -// // GeoportalDataViewerConstants.print("The selected Feature is: "+feature); -// FeatureRow theFeature = feature; -// Button button = null; -// if (!featureFound) { -// flex.setHTML(0, 0, new HTML("No data available").toString()); -// } else { -// button = new Button("Open Details"); -// button.getElement().setId("open-details"); -// button.setType(ButtonType.LINK); -// } -// -// VerticalPanel vpPanel = new VerticalPanel(); -// vpPanel.add(flex); - -// if (button != null) { -// vpPanel.add(button); -// button.setType(ButtonType.LINK); -// button.addClickHandler(new ClickHandler() { -// -// @Override -// public void onClick(ClickEvent event) { -// ShowDetailsEvent toEvent = parseGeonaReferences( -// geoNaDataObject.getSourceLayerObject().getItemType(), -// theFeature); -// applicationBus.fireEvent(toEvent); -// -// } -// }); -// } - -// Coordinate centerTo = null; -// GWT.log("geometry is: " + feature.getGeometry()); -// if (feature.getGeometry() != null) { -// GWT.log("trasforming geometry: " + feature.getGeometry().getToJSONObject()); -// -// Geometry geom = new GeoJson() -// .readGeometry(feature.getGeometry().getToJSONObject(), null); -// -// // POINT -// if (geom.getType().equalsIgnoreCase("Point")) { -// GWT.log("geometry: is a point"); -// String coordinateJSON = feature.getGeometry().getCoordinatesJSON(); -// JSONArray centerJSON = (JSONArray) JSONParser -// .parseStrict(coordinateJSON); -//// Coordinate center = OLFactory.createCoordinate( -//// new Double(centerJSON.get(0).toString()), -//// new Double(centerJSON.get(1).toString())); -// -// Coordinate center = new Coordinate( -// new Double(centerJSON.get(0).toString()), -// new Double(centerJSON.get(1).toString())); -// -// if (feature.getCrsName() != null && feature.getCrsName() -// .endsWith(MAP_PROJECTION.EPSG_4326.getId())) { -// -// center = MapUtils.transformCoordiante(center, -// MAP_PROJECTION.EPSG_4326.getName(), -// MAP_PROJECTION.EPSG_3857.getName()); -// } -// centerTo = center; -// } else { -// -// Extent geomExtent = geom.getExtent(); -// -// Coordinate lower = OLFactory.createCoordinate( -// geomExtent.getLowerLeftX(), geomExtent.getLowerLeftY()); -// Coordinate upper = OLFactory.createCoordinate( -// geomExtent.getUpperRightX(), geomExtent.getUpperRightY()); -// Coordinate lowerCoord = lower; -// Coordinate upperCoord = upper; -// if (feature.getCrsName() != null && feature.getCrsName() -// .endsWith(MAP_PROJECTION.EPSG_4326.getId())) { -// -// lowerCoord = MapUtils.transformCoordiante(lower, -// MAP_PROJECTION.EPSG_4326.getName(), -// MAP_PROJECTION.EPSG_3857.getName()); -// upperCoord = MapUtils.transformCoordiante(upper, -// MAP_PROJECTION.EPSG_4326.getName(), -// MAP_PROJECTION.EPSG_3857.getName()); -// } -// -// ExtentWrapped ew = new ExtentWrapped(lowerCoord.getX(), -// lowerCoord.getY(), upperCoord.getX(), upperCoord.getY()); -// -// centerTo = new Coordinate(ew.getCenter().getX(), ew.getCenter().getY()); -// -// } -// GWT.log("center is: " + centerTo); -// } - -// // fallback -// if (centerTo == null) -// centerTo = queryEvent.getOnFailureCenterTo(); -// -// olMap.showPopup(vpPanel.toString(), centerTo); -// -// Scheduler.get().scheduleDeferred(new ScheduledCommand() { -// -// @Override -// public void execute() { -// Element buttonElement = DOM.getElementById("open-details"); -// Event.sinkEvents(buttonElement, Event.ONCLICK); -// Event.setEventListener(buttonElement, new EventListener() { -// -// @Override -// public void onBrowserEvent(Event event) { -// if (Event.ONCLICK == event.getTypeInt()) { -// ShowDetailsEvent toEvent = parseGeonaReferences( -// geoNaDataObject.getSourceLayerObject() -// .getItemType(), -// theFeature); -// applicationBus.fireEvent(toEvent); -// } -// } -// }); -// } -// }); } } }); @@ -655,11 +488,96 @@ public class LayerManager { return layerItem; } + /** + * Show popup info for layer. + * + * @param geoNaDataObject the geo na data object + * @param feature the feature + * @param onFailureCenterTo the on failure center to + */ + public void showPopupInfoForLayer(List listGeoNaDataObject, + Coordinate onFailureCenterTo) { + + ScrollPanel scrollPanel = new ScrollPanel(); + VerticalPanel vpPanel = new VerticalPanel(); + scrollPanel.add(vpPanel); + + for (GeoNaSpatialQueryResult geoNaSpatialQueryResult : listGeoNaDataObject) { + + GWT.log("baseLayerFromISProfile.getName() :"+baseLayerFromISProfile.getName()); + String layerSource = geoNaSpatialQueryResult.getSourceLayerObject().getLayerItem().getName(); + + //skipping centroid layer + if(layerSource==null || layerSource.compareToIgnoreCase(baseLayerFromISProfile.getName())==0) { + continue; + } + + Label layerLabel = new Label(); + layerLabel.setType(LabelType.INFO); + + layerLabel.setText(layerSource); + layerLabel.getElement().getStyle().setMarginTop(10, Unit.PX); + layerLabel.getElement().getStyle().setMarginBottom(5, Unit.PX); + vpPanel.add(layerLabel); + + List features = geoNaSpatialQueryResult.getFeatures(); + + if (features == null || features.isEmpty()) { + FlexTable flex = new FlexTable(); + flex.setCellPadding(1); + flex.setCellSpacing(1); + flex.getElement().addClassName("table-feature"); + flex.setHTML(0, 0, new HTML("No data available").toString()); + vpPanel.add(flex); + continue; + //olMap.showPopup(vpPanel.toString(), onFailureCenterTo); + } + + for (FeatureRow feature : features) { + + FlexTable intFlex = new FlexTable(); + intFlex.setCellPadding(1); + intFlex.setCellSpacing(1); + intFlex.getElement().addClassName("table-feature"); + + intFlex.setHTML(0, 0, new HTML("Feature Id").toString()); + intFlex.setHTML(1, 0, new HTML(feature.getId()).toString()); + + // Showing properties belonging to concessioni centroid layer + Map> entries = feature.getMapProperties(); + + if (entries.size() == 0) { + intFlex.setHTML(1, 1, new HTML("No property available").toString()); + } + int j = 1; + for (String key : entries.keySet()) { + List theValues = entries.get(key); + String valueToDisplay = ""; + for (String value : theValues) { + valueToDisplay+=value+", "; + + } + valueToDisplay = valueToDisplay.substring(0,valueToDisplay.length()-2); + intFlex.setHTML(0, j, new HTML(key).toString()); + intFlex.setHTML(1, j, new HTML(valueToDisplay).toString()); + j++; + + } + + vpPanel.add(intFlex); + } + + } + + olMap.showPopup(scrollPanel.toString(), onFailureCenterTo); + + } + /** * Show popup info for centroid layer. * - * @param geoNaDataObject the geo na data object - * @param feature the feature + * @param geoNaDataObject the geo na data object + * @param feature the feature * @param onFailureCenterTo the on failure center to */ public void showPopupInfoForCentroidLayer(GeoNaSpatialQueryResult geoNaDataObject, FeatureRow feature, @@ -673,8 +591,8 @@ public class LayerManager { vpPanel.add(flex); if (feature == null) { - olMap.showPopup(vpPanel.toString(), onFailureCenterTo); flex.setHTML(0, 0, new HTML("No data available").toString()); + olMap.showPopup(vpPanel.toString(), onFailureCenterTo); return; } @@ -856,4 +774,12 @@ public class LayerManager { return layerManagerBus; } + public void setBaseLayerFromIsProfile(org.gcube.application.geoportalcommon.shared.LayerItem layerItem) { + this.baseLayerFromISProfile = layerItem; + } + + public org.gcube.application.geoportalcommon.shared.LayerItem getBaseLayerFromISProile() { + return baseLayerFromISProfile; + } + } diff --git a/src/main/java/org/gcube/portlets/user/geoportaldataviewer/client/OLMapManager.java b/src/main/java/org/gcube/portlets/user/geoportaldataviewer/client/OLMapManager.java index 216f6ab..12908fd 100644 --- a/src/main/java/org/gcube/portlets/user/geoportaldataviewer/client/OLMapManager.java +++ b/src/main/java/org/gcube/portlets/user/geoportaldataviewer/client/OLMapManager.java @@ -73,7 +73,7 @@ public class OLMapManager { // } GeoQuery select = toDataPointQuery(coordinate, true); - layerManagerBus.fireEvent(new QueryDataEvent(select, coordinate, null, true, MapEventType.INIT)); + layerManagerBus.fireEvent(new QueryDataEvent(select, coordinate, null, true, MapEventType.MOUSE_CLICK)); } @@ -152,6 +152,7 @@ public class OLMapManager { // ratio - mapPixelWeight : bboxWeight = 20px : geoRectangleWidth // where 10px is the pixel diameter dimension of the clicked point double bboxWidth = Math.abs(olMap.getExtent().getLowerLeftX() - olMap.getExtent().getUpperRightX()); + double geoWidth = 0; // adding a tolerance in case of manual click if (manualClick) { diff --git a/src/main/java/org/gcube/portlets/user/geoportaldataviewer/client/gis/OpenLayerOSM.java b/src/main/java/org/gcube/portlets/user/geoportaldataviewer/client/gis/OpenLayerOSM.java index ddb62c0..2cf916d 100644 --- a/src/main/java/org/gcube/portlets/user/geoportaldataviewer/client/gis/OpenLayerOSM.java +++ b/src/main/java/org/gcube/portlets/user/geoportaldataviewer/client/gis/OpenLayerOSM.java @@ -1,7 +1,10 @@ package org.gcube.portlets.user.geoportaldataviewer.client.gis; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; +import org.gcube.portlets.user.geoportaldataviewer.client.GeoportalDataViewerConstants; import org.gcube.portlets.user.geoportaldataviewer.client.GeoportalDataViewerConstants.MAP_PROJECTION; import org.gcube.portlets.user.geoportaldataviewer.client.events.AddedLayerToMapEvent; import org.gcube.portlets.user.geoportaldataviewer.shared.gis.LayerItem; @@ -9,6 +12,9 @@ import org.gcube.portlets.user.geoportaldataviewer.shared.gis.LayerItem; import com.google.gwt.core.client.GWT; import com.google.gwt.dom.client.Style.Visibility; import com.google.gwt.event.shared.HandlerManager; +import com.google.gwt.json.client.JSONObject; +import com.google.gwt.json.client.JSONParser; +import com.google.gwt.json.client.JSONValue; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Element; import com.google.gwt.user.client.Event; @@ -48,10 +54,10 @@ import ol.source.ImageWms; import ol.source.ImageWmsOptions; import ol.source.ImageWmsParams; import ol.source.Osm; +import ol.source.Source; import ol.source.Vector; import ol.source.XyzOptions; - /** * The Class OpenLayerOSM. * @@ -142,7 +148,7 @@ public abstract class OpenLayerOSM { XyzOptions osmSourceOptions = OLFactory.createOptions(); // osmSourceOptions.setCrossOrigin("Anonymous"); // osmSourceOptions.setTileLoadFunction(null); - + Osm osmSource = new Osm(osmSourceOptions); LayerOptions osmLayerOptions = OLFactory.createOptions(); osmLayerOptions.setSource(osmSource); @@ -335,12 +341,12 @@ public abstract class OpenLayerOSM { LayerOptions layerOptions = OLFactory.createOptions(); layerOptions.setSource(imageWMSSource); - - //Settings MIN and MAX Resolution - if(layerItem.getMinResolution()!=null) { + + // Settings MIN and MAX Resolution + if (layerItem.getMinResolution() != null) { layerOptions.setMinResolution(layerItem.getMinResolution()); } - if(layerItem.getMaxResolution()!=null) { + if (layerItem.getMaxResolution() != null) { layerOptions.setMaxResolution(layerItem.getMaxResolution()); } @@ -349,7 +355,7 @@ public abstract class OpenLayerOSM { // visibleLayerItems map.addLayer(wmsLayer); - GWT.log("Added WMSLayer for layer: "+layerItem.getName()); + GWT.log("Added WMSLayer for layer: " + layerItem.getName()); eventBus.fireEvent(new AddedLayerToMapEvent(layerItem)); } @@ -382,11 +388,11 @@ public abstract class OpenLayerOSM { LayerOptions layerOptions = OLFactory.createOptions(); layerOptions.setSource(imageWMSSource); - //Settings MIN and MAX Resolution - if(layerItem.getMinResolution()!=null) { + // Settings MIN and MAX Resolution + if (layerItem.getMinResolution() != null) { layerOptions.setMinResolution(layerItem.getMinResolution()); } - if(layerItem.getMaxResolution()!=null) { + if (layerItem.getMaxResolution() != null) { layerOptions.setMaxResolution(layerItem.getMaxResolution()); } @@ -397,7 +403,7 @@ public abstract class OpenLayerOSM { map.addLayer(wmsLayer); wmsDetailsLayerMap.put(key, wmsLayer); - GWT.log("Added WMSDetailLayer for layer name: "+layerItem.getName()); + GWT.log("Added WMSDetailLayer for layer name: " + layerItem.getName()); eventBus.fireEvent(new AddedLayerToMapEvent(layerItem)); } else { GWT.log("The detail layer with key: " + key + " already exists, skipping"); @@ -409,22 +415,19 @@ public abstract class OpenLayerOSM { * Removes the all detail layers. */ public void removeAllDetailLayers() { - - //NOT NEEDED ANYMORE.. I'M USING MIN/MAX LAYER RESOLUTION - - /* - if (wmsDetailsLayerMap == null) - return; - - GWT.log("Removing layers: " + wmsDetailsLayerMap.keySet() + " from map"); - for (String key : wmsDetailsLayerMap.keySet()) { - Image layer = wmsDetailsLayerMap.get(key); - map.removeLayer(layer); - } + // NOT NEEDED ANYMORE.. I'M USING MIN/MAX LAYER RESOLUTION - wmsDetailsLayerMap.clear(); - */ + /* + * if (wmsDetailsLayerMap == null) return; + * + * GWT.log("Removing layers: " + wmsDetailsLayerMap.keySet() + " from map"); + * + * for (String key : wmsDetailsLayerMap.keySet()) { Image layer = + * wmsDetailsLayerMap.get(key); map.removeLayer(layer); } + * + * wmsDetailsLayerMap.clear(); + */ } @@ -576,13 +579,12 @@ public abstract class OpenLayerOSM { return false; } - /** * Gets the layer property. * * @param layerName the layer name - * @param property the property + * @param property the property * @return the layer property */ public String getLayerProperty(String layerName, String property) { @@ -620,7 +622,7 @@ public abstract class OpenLayerOSM { public double getCurrentZoomLevel() { return map.getView().getZoom(); } - + /** * Gets the current zoom level. * @@ -696,4 +698,39 @@ public abstract class OpenLayerOSM { return this.map != null; } + /** + * Gets the layers. + * + * @return the layers + */ + public List getLayers() { + Collection layers = map.getLayers(); + List layerNames = null; + if (layers != null) { + Base[] layersArr = layers.getArray(); + layerNames = new ArrayList(layersArr.length); + for (int i = 0; i < layersArr.length; i++) { + Base layer = layersArr[i]; + if (layer instanceof Image) { + Image layerImage = (Image) layer; + + Source source = layerImage.getSource(); +// GWT.log("source: "+source.toString()); + //GeoportalDataViewerConstants.printJsObj(source); + String sorceRootObj = GeoportalDataViewerConstants.toJsonObj(source); + JSONValue jsonObj = JSONParser.parseStrict(sorceRootObj); + // GWT.log("jsonObj: " + jsonObj.toString()); + JSONObject jsonSourceObj = (JSONObject) jsonObj; + + JSONObject jsonParamsObj = (JSONObject) jsonSourceObj.get("params_"); + // GWT.log("jsonParamsObj is: "+jsonParamsObj); + JSONValue jsonLayers = jsonParamsObj.get("LAYERS"); + GWT.log("theLayerName name is: " + jsonLayers); + layerNames.add(jsonLayers.toString()); + } + } + } + return layerNames; + } + } diff --git a/src/main/java/org/gcube/portlets/user/geoportaldataviewer/server/ConcessioneImageUtil.java b/src/main/java/org/gcube/portlets/user/geoportaldataviewer/server/ConcessioneImageUtil.java new file mode 100644 index 0000000..0fefc04 --- /dev/null +++ b/src/main/java/org/gcube/portlets/user/geoportaldataviewer/server/ConcessioneImageUtil.java @@ -0,0 +1,89 @@ +package org.gcube.portlets.user.geoportaldataviewer.server; + +import static org.gcube.application.geoportal.client.GeoportalAbstractPlugin.statefulMongoConcessioni; + +import java.util.ArrayList; +import java.util.List; + +import javax.servlet.http.HttpServletRequest; + +import org.gcube.application.geoportal.client.legacy.ConcessioniManagerI; +import org.gcube.application.geoportal.common.model.legacy.Concessione; +import org.gcube.application.geoportal.common.model.legacy.UploadedImage; +import org.gcube.application.geoportalcommon.ConvertToDataViewModel; +import org.gcube.application.geoportalcommon.shared.products.model.UploadedImageDV; +import org.gcube.portlets.user.geoportaldataviewer.server.util.SessionUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The Class ConcessioneImageUtil. + * + * @author Francesco Mangiacrapa at ISTI-CNR francesco.mangiacrapa@isti.cnr.it + * + * Sep 7, 2021 + */ +public class ConcessioneImageUtil { + + private static final Logger LOG = LoggerFactory.getLogger(ConcessioneImageUtil.class); + + /** + * Gets the uploaded images for id. + * + * @param httpServletRequest the http servlet request + * @param itemType the item type + * @param itemId the item id + * @param maxImages the max images + * @return the uploaded images for id + * @throws Exception the exception + */ + public List getUploadedImagesForId(HttpServletRequest httpServletRequest, String itemType, + String itemId, Integer maxImages) throws Exception { + LOG.info("getUploadedImagesForId [itemId: " + itemId + ", itemType: " + itemType + "] called"); + + if (itemType == null) + throw new Exception("Invalid parameter. The itemType is null"); + + if (itemId == null) + throw new Exception("Invalid parameter. The itemId is null"); + + List listUI = null; + + try { + + if (itemType.equalsIgnoreCase("concessione")) { + + LOG.info("Trying to get concessione for id " + itemId); + SessionUtil.getCurrentContext(httpServletRequest, true); + SessionUtil.getCurrentToken(httpServletRequest, true); + ConcessioniManagerI concessioniManager = statefulMongoConcessioni().build(); + Concessione concessione = concessioniManager.getById(itemId); + if (concessione != null) { + LOG.info("For id " + itemId + ", got concessione " + concessione.getNome() + " from service"); + List images = concessione.getImmaginiRappresentative(); + + if (images != null) { + listUI = new ArrayList(); + int max = maxImages < images.size() ? maxImages : images.size(); + for (int i = 0; i < max; i++) { + UploadedImageDV ui = ConvertToDataViewModel.toUploadedImage(images.get(i)); + listUI.add(ui); + } + LOG.info("For id " + itemId + ", got " + listUI.size() + " image/s"); + } + } else + throw new Exception("Concessione with id '" + itemId + "' not available"); + } + + return listUI; + + } catch (Exception e) { + String erroMsg = UploadedImage.class.getSimpleName() + " not available for " + + Concessione.class.getSimpleName() + " with id " + itemId; + LOG.error(erroMsg, e); + throw new Exception(erroMsg); + } + + } + +} diff --git a/src/main/java/org/gcube/portlets/user/geoportaldataviewer/server/GeoportalDataViewerServiceImpl.java b/src/main/java/org/gcube/portlets/user/geoportaldataviewer/server/GeoportalDataViewerServiceImpl.java index 83b63de..ee5178e 100644 --- a/src/main/java/org/gcube/portlets/user/geoportaldataviewer/server/GeoportalDataViewerServiceImpl.java +++ b/src/main/java/org/gcube/portlets/user/geoportaldataviewer/server/GeoportalDataViewerServiceImpl.java @@ -3,6 +3,7 @@ package org.gcube.portlets.user.geoportaldataviewer.server; import static org.gcube.application.geoportal.client.GeoportalAbstractPlugin.statefulMongoConcessioni; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; @@ -11,7 +12,6 @@ import java.util.Map; import org.gcube.application.geoportal.client.legacy.ConcessioniManagerI; import org.gcube.application.geoportal.common.model.legacy.Concessione; import org.gcube.application.geoportal.common.model.legacy.LayerConcessione; -import org.gcube.application.geoportal.common.model.legacy.UploadedImage; import org.gcube.application.geoportalcommon.ConvertToDataViewModel; import org.gcube.application.geoportalcommon.GeoNaDataViewerProfileReader; import org.gcube.application.geoportalcommon.GeoportalCommon; @@ -147,9 +147,10 @@ public class GeoportalDataViewerServiceImpl extends RemoteServiceServlet impleme if (concessioneIds != null && concessioneIds.size() > 0) { String cId = concessioneIds.get(0); try { - List listUI = getUploadedImagesForId("Concessione", cId, 1); + UploadedImageDV uplImg = SessionUtil.getPreviewImageForConcessione(this.getThreadLocalRequest(), "Concessione", cId); + //List listUI = getUploadedImagesForId("Concessione", cId, 1); Map> mapImages = new LinkedHashMap>(); - mapImages.put(cId, listUI); + mapImages.put(cId, Arrays.asList(uplImg)); geoDAO.setMapImages(mapImages); } catch (Exception e) { LOG.warn("Error on loading uploaded images for concessione: " + cId, e); @@ -176,55 +177,12 @@ public class GeoportalDataViewerServiceImpl extends RemoteServiceServlet impleme * @throws Exception the exception */ @Override - public List getUploadedImagesForId(String itemType, String itemId, int maxImages) + public List getUploadedImagesForId(String itemType, String itemId, Integer maxImages) throws Exception { LOG.info("getUploadedImagesForId [itemId: " + itemId + ", itemType: " + itemType + "] called"); - if (itemType == null) - throw new Exception("Invalid parameter. The itemType is null"); - - if (itemId == null) - throw new Exception("Invalid parameter. The itemId is null"); - - List listUI = null; - - try { - - SessionUtil.getCurrentContext(this.getThreadLocalRequest(), true); - - if (itemType.equalsIgnoreCase("concessione")) { - - LOG.info("Trying to get concessione for id " + itemId); - SessionUtil.getCurrentContext(this.getThreadLocalRequest(), true); - SessionUtil.getCurrentToken(this.getThreadLocalRequest(), true); - ConcessioniManagerI concessioniManager = statefulMongoConcessioni().build(); - Concessione concessione = concessioniManager.getById(itemId); - if (concessione != null) { - LOG.info("For id " + itemId + ", got concessione " + concessione.getNome() + " from service"); - List images = concessione.getImmaginiRappresentative(); - - if (images != null) { - listUI = new ArrayList(); - int max = maxImages < images.size() ? maxImages : images.size(); - for (int i = 0; i < max; i++) { - UploadedImageDV ui = ConvertToDataViewModel.toUploadedImage(images.get(i)); - listUI.add(ui); - } - LOG.info("For id " + itemId + ", got " + listUI.size() + " image/s"); - } - } else - throw new Exception("Concessione with id '" + itemId + "' not available"); - } - - return listUI; - - } catch (Exception e) { - String erroMsg = UploadedImage.class.getSimpleName() + " not available for " - + Concessione.class.getSimpleName() + " with id " + itemId; - LOG.error(erroMsg, e); - throw new Exception(erroMsg); - } - + return new ConcessioneImageUtil().getUploadedImagesForId(this.getThreadLocalRequest(), itemType, itemId, + maxImages); } /** diff --git a/src/main/java/org/gcube/portlets/user/geoportaldataviewer/server/gis/FeatureParser.java b/src/main/java/org/gcube/portlets/user/geoportaldataviewer/server/gis/FeatureParser.java index f7f52b8..b398432 100644 --- a/src/main/java/org/gcube/portlets/user/geoportaldataviewer/server/gis/FeatureParser.java +++ b/src/main/java/org/gcube/portlets/user/geoportaldataviewer/server/gis/FeatureParser.java @@ -124,29 +124,18 @@ public class FeatureParser { LOG.debug("Building at index: " + i); try { + String fetaureId = theFeature.getString("id"); + row.setId(fetaureId); JSONObject geometry = theFeature.getJSONObject("geometry"); String typeValue = geometry.getString("type"); - // JSONArray coordinates = geometry.getJSONArray("coordinates"); - // String toCoordinates = coordinates.toString(); -// String x1 = coordinates.get(0).toString(); -// String y1 = coordinates.get(1).toString(); -// LOG.debug("Coordinate x1: "+x1); -// LOG.debug("Coordinate y1: "+y1); -// Double coordX = null; -// Double coordY = null; FeatureGeometry fg = new FeatureGeometry(); fg.setType(typeValue); - // TODO ONLY POINT GEOMETRY + try { JSONArray coordinates = geometry.getJSONArray("coordinates"); String coordinateJSONString = coordinates.toString(); LOG.debug("coordinates are: " + coordinateJSONString); fg.setCoordinatesJSON(coordinates.toString()); -// coordX = Double.parseDouble(x1); -// coordY = Double.parseDouble(y1); -// Coordinate coord = new Coordinate(coordX, coordY); -// fg.setPath(new PointsPath(new Coordinate[] {coord})); - } catch (Exception e) { LOG.warn("Not able to parse the 'coordinates' field: ", e); } diff --git a/src/main/java/org/gcube/portlets/user/geoportaldataviewer/server/util/SessionUtil.java b/src/main/java/org/gcube/portlets/user/geoportaldataviewer/server/util/SessionUtil.java index d7c552f..7293449 100644 --- a/src/main/java/org/gcube/portlets/user/geoportaldataviewer/server/util/SessionUtil.java +++ b/src/main/java/org/gcube/portlets/user/geoportaldataviewer/server/util/SessionUtil.java @@ -3,12 +3,18 @@ */ package org.gcube.portlets.user.geoportaldataviewer.server.util; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; +import org.gcube.application.geoportalcommon.shared.products.model.UploadedImageDV; import org.gcube.common.authorization.library.provider.SecurityTokenProvider; import org.gcube.common.portal.PortalContext; import org.gcube.common.scope.api.ScopeProvider; +import org.gcube.portlets.user.geoportaldataviewer.server.ConcessioneImageUtil; import org.gcube.portlets.user.urlshortener.UrlShortener; import org.gcube.vomanagement.usermanagement.GroupManager; import org.gcube.vomanagement.usermanagement.exception.GroupRetrievalFault; @@ -35,6 +41,8 @@ public class SessionUtil { public static final String URL_SHORTENER_SERVICE = "URL_SHORTENER_SERVICE"; + public static final String CACHE_IMAGE_PREVIEW_FOR_CONCESSIONE = "MAP_IMAGE_PREVIEW_FOR_CONCESSIONE"; + /** * Checks if is into portal. * @@ -168,4 +176,50 @@ public class SessionUtil { return shortener; } + + /** + * Gets the preview image for concessione. It is the first image retrieved from + * mongoService for mongoConcessioneId. Caching it into session + * + * @param httpServletRequest the http servlet request + * @param itemType the item type + * @param mongoConcessioneId the mongo concessione id + * @return the preview image for concessione + */ + public static UploadedImageDV getPreviewImageForConcessione(HttpServletRequest httpServletRequest, String itemType, + String mongoConcessioneId) { + + HttpSession session = httpServletRequest.getSession(); + Map> mapImages = null; + List lUI = null; + try { + mapImages = (LinkedHashMap) session.getAttribute(CACHE_IMAGE_PREVIEW_FOR_CONCESSIONE); + + if (mapImages == null) { + mapImages = new LinkedHashMap>(); + } + + List imagePreviewForConcessione = mapImages.get(mongoConcessioneId); + + if (imagePreviewForConcessione == null || imagePreviewForConcessione.size() == 0) { + LOG.info("Into " + CACHE_IMAGE_PREVIEW_FOR_CONCESSIONE + " object session the mongoConcessioneId " + + mongoConcessioneId + " is empty or null, loading from service and filling it"); + lUI = new ConcessioneImageUtil().getUploadedImagesForId(httpServletRequest, itemType, + mongoConcessioneId, 1); + mapImages.put(mongoConcessioneId, lUI); + } + + lUI = mapImages.get(mongoConcessioneId); + LOG.info("From " + CACHE_IMAGE_PREVIEW_FOR_CONCESSIONE + " object session read image: " + lUI); + session.setAttribute(CACHE_IMAGE_PREVIEW_FOR_CONCESSIONE, mapImages); + + } catch (Exception e) { + LOG.warn("Error occurred when instancing the " + UrlShortener.class.getSimpleName(), e); + } + + if (lUI == null || lUI.isEmpty()) + return null; + + return lUI.get(0); + } } diff --git a/src/main/java/org/gcube/portlets/user/geoportaldataviewer/shared/GeoNaSpatialQueryResult.java b/src/main/java/org/gcube/portlets/user/geoportaldataviewer/shared/GeoNaSpatialQueryResult.java index 6decadf..6c80e33 100644 --- a/src/main/java/org/gcube/portlets/user/geoportaldataviewer/shared/GeoNaSpatialQueryResult.java +++ b/src/main/java/org/gcube/portlets/user/geoportaldataviewer/shared/GeoNaSpatialQueryResult.java @@ -24,7 +24,7 @@ public class GeoNaSpatialQueryResult implements Serializable { private static final long serialVersionUID = 3513120677727206958L; private List features; private LayerObject sourceLayerObject; - // Map with couple (mongoId concessione, list of uplaoded Images for the concessione) + // Map with couple (mongoId concessione, list of uploaded Images for the concessione) private Map> mapImages = null; /** diff --git a/src/main/java/org/gcube/portlets/user/geoportaldataviewer/shared/gis/LayerObject.java b/src/main/java/org/gcube/portlets/user/geoportaldataviewer/shared/gis/LayerObject.java index de25d4c..9f7f72c 100644 --- a/src/main/java/org/gcube/portlets/user/geoportaldataviewer/shared/gis/LayerObject.java +++ b/src/main/java/org/gcube/portlets/user/geoportaldataviewer/shared/gis/LayerObject.java @@ -2,13 +2,12 @@ package org.gcube.portlets.user.geoportaldataviewer.shared.gis; import java.io.Serializable; - /** * The Class LayerObject. * * @author Francesco Mangiacrapa at ISTI-CNR (francesco.mangiacrapa@isti.cnr.it) * - * Nov 13, 2020 + * Nov 13, 2020 */ public class LayerObject implements Serializable { @@ -24,7 +23,12 @@ public class LayerObject implements Serializable { * Instantiates a new layer object. */ public LayerObject() { - // TODO Auto-generated constructor stub + } + + public LayerObject(String itemType, LayerItem layerItem) { + super(); + this.itemType = itemType; + this.layerItem = layerItem; } /** diff --git a/src/main/java/org/gcube/portlets/user/geoportaldataviewer/shared/gis/wfs/FeatureRow.java b/src/main/java/org/gcube/portlets/user/geoportaldataviewer/shared/gis/wfs/FeatureRow.java index 0027806..b9d365d 100644 --- a/src/main/java/org/gcube/portlets/user/geoportaldataviewer/shared/gis/wfs/FeatureRow.java +++ b/src/main/java/org/gcube/portlets/user/geoportaldataviewer/shared/gis/wfs/FeatureRow.java @@ -21,10 +21,12 @@ public class FeatureRow implements Serializable { */ private static final long serialVersionUID = 6254861811998867626L; + private String id; + private Map> mapProperties; private FeatureGeometry geometry; - + private String crsName; public FeatureRow() { @@ -36,6 +38,14 @@ public class FeatureRow implements Serializable { this.setGeometry(geometry); } + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + public Map> getMapProperties() { return mapProperties; } @@ -63,14 +73,16 @@ public class FeatureRow implements Serializable { @Override public String toString() { StringBuilder builder = new StringBuilder(); - builder.append("FeatureRow [mapProperties="); + builder.append("FeatureRow [id="); + builder.append(id); + builder.append(", mapProperties="); builder.append(mapProperties); builder.append(", geometry="); builder.append(geometry); + builder.append(", crsName="); + builder.append(crsName); builder.append("]"); return builder.toString(); } - - } diff --git a/src/main/webapp/GeoportalDataViewer.css b/src/main/webapp/GeoportalDataViewer.css index dba823e..8efa665 100644 --- a/src/main/webapp/GeoportalDataViewer.css +++ b/src/main/webapp/GeoportalDataViewer.css @@ -31,7 +31,7 @@ body { bottom: 12px; left: -50px; min-width: 280px; - max-width: 350px; + max-width: 500px; visibility: hidden; } @@ -94,12 +94,9 @@ body { padding-top: 5px; } - /* #page-view-details .page-header h1 { font-size: 28px; } */ - - #page-view-details p { font-size: 14px; padding: 10px; @@ -198,14 +195,15 @@ body { } .inner-toolbar .btn-link { - border: 1px solid #eee; - background-color: #fcfcfc; - text-align: center; - margin: 1px; + border: 1px solid #eee; + background-color: #fcfcfc; + text-align: center; + margin: 1px; } + .inner-toolbar .btn-link:hover, .inner-toolbar .btn-link:focus { - background-color: #f3f3f3 !important; - text-decoration: none; + background-color: #f3f3f3 !important; + text-decoration: none; } .style-layer { @@ -219,7 +217,7 @@ body { } .style-layer table { - width: 100%; + width: 100%; } .info-interaction { @@ -232,10 +230,7 @@ body { padding-bottom: 5px; } - - /** NanoGallery2 **/ - .nGY2Viewer { background: rgba(4, 4, 4, .8) !important; } @@ -244,7 +239,6 @@ body { background: rgba(4, 4, 4, .8) !important; } - .nGY2 .toolbar .ngbt { font-size: 1em !important; padding: 12px 12px !important; @@ -265,3 +259,20 @@ body { color: #696969 !important; font-family: arial !important; } + +.table-feature { + border: 1px solid #ddd; + text-align: left; + border-collapse: collapse; + width: 100%; +} + +.table-feature td { + border: 1px solid #ddd; + text-align: left; + padding: 5px; +} + +.table-feature tr:first-child { + font-weight: bold; +} \ No newline at end of file