geoportal-data-viewer-app/src/main/java/org/gcube/portlets/user/geoportaldataviewer/client/ui/crossfiltering/CrossFilteringLayerPanel.java

608 lines
19 KiB
Java

package org.gcube.portlets.user.geoportaldataviewer.client.ui.crossfiltering;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import org.gcube.application.geoportalcommon.shared.geoportal.config.GroupedLayersDV;
import org.gcube.application.geoportalcommon.shared.geoportal.config.layers.CrossFilteringLayerDV;
import org.gcube.application.geoportalcommon.shared.geoportal.config.layers.LayerIDV;
import org.gcube.application.geoportalcommon.shared.geoportal.materialization.GCubeSDILayer;
import org.gcube.application.geoportalcommon.shared.geoportal.materialization.IndexLayerDV;
import org.gcube.portlets.user.geoportaldataviewer.client.GeoportalDataViewerConstants.MAP_PROJECTION;
import org.gcube.portlets.user.geoportaldataviewer.client.GeoportalDataViewerServiceAsync;
import org.gcube.portlets.user.geoportaldataviewer.client.events.ApplyCQLToLayerOnMapEvent;
import org.gcube.portlets.user.geoportaldataviewer.client.ui.util.OLGeoJSONUtil;
import org.gcube.portlets.user.geoportaldataviewer.client.util.LoaderIcon;
import org.gcube.portlets.user.geoportaldataviewer.client.util.URLUtil;
import org.gcube.portlets.user.geoportaldataviewer.client.util.WFSMakerUtil;
import org.gcube.portlets.user.geoportaldataviewer.shared.GCubeCollection;
import org.gcube.portlets.user.geoportaldataviewer.shared.gis.LayerItem;
import org.gcube.portlets.user.geoportaldataviewer.shared.gis.wms.GeoInformationForWMSRequest;
import com.github.gwtbootstrap.client.ui.Alert;
import com.github.gwtbootstrap.client.ui.Button;
import com.github.gwtbootstrap.client.ui.CheckBox;
import com.github.gwtbootstrap.client.ui.ControlGroup;
import com.github.gwtbootstrap.client.ui.ControlLabel;
import com.github.gwtbootstrap.client.ui.Controls;
import com.github.gwtbootstrap.client.ui.DropdownButton;
import com.github.gwtbootstrap.client.ui.Fieldset;
import com.github.gwtbootstrap.client.ui.ListBox;
import com.github.gwtbootstrap.client.ui.constants.AlertType;
import com.github.gwtbootstrap.client.ui.constants.ButtonType;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.shared.HandlerManager;
import com.google.gwt.http.client.URL;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.Widget;
import jsinterop.base.JsPropertyMap;
import ol.Feature;
/**
* The Class CrossFilteringLayerPanel.
*
* @author Francesco Mangiacrapa at ISTI-CNR francesco.mangiacrapa@isti.cnr.it
*
* May 15, 2023
*/
public class CrossFilteringLayerPanel extends Composite {
public static String COLORSCALERANGE = "COLORSCALERANGE";
@UiField
Fieldset fieldSet;
@UiField
HTMLPanel panelResults;
@UiField
Button buttonReset;
private CheckBox checkbox;
private GeoInformationForWMSRequest geoInformation;
private static CrossFilteringLayerPanelUiBinder uiBinder = GWT.create(CrossFilteringLayerPanelUiBinder.class);
private HandlerManager applicationBus;
private LayerItem layerItem;
private LinkedHashMap<Integer, List<? extends LayerIDV>> mapInnestedFiltering = new LinkedHashMap<Integer, List<? extends LayerIDV>>();
private LinkedHashMap<Integer, HashMap<String, SelectableItem>> mapInnestedFeatures = new LinkedHashMap<Integer, HashMap<String, SelectableItem>>();
private LinkedHashMap<Integer, List<ListBox>> mapInnestedListBoxes = new LinkedHashMap<Integer, List<ListBox>>();
private GroupedLayersDV<? extends LayerIDV> groupedLayersDV;
private GCubeCollection gCubeCollection;
private DropdownButton filterButton;
/**
* The Interface LayerCollectionPanelUiBinder.
*
* @author Francesco Mangiacrapa at ISTI-CNR francesco.mangiacrapa@isti.cnr.it
*
* Jan 16, 2023
*/
interface CrossFilteringLayerPanelUiBinder extends UiBinder<Widget, CrossFilteringLayerPanel> {
}
/**
* Instantiates a new overlay custom layer panel.
*
* @param gCubeCollection the g cube collection
* @param groupedLayersDV the grouped layers DV
* @param applicationBus the application bus
*/
public CrossFilteringLayerPanel(GCubeCollection gCubeCollection,
GroupedLayersDV<? extends LayerIDV> groupedLayersDV, HandlerManager applicationBus) {
initWidget(uiBinder.createAndBindUi(this));
this.applicationBus = applicationBus;
this.groupedLayersDV = groupedLayersDV;
this.gCubeCollection = gCubeCollection;
GWT.log("Building cross-filtering for: " + groupedLayersDV);
buttonReset.setType(ButtonType.LINK);
int level = 0;
// Building mapInnestedFiltering
recursivelyBuildSelectableLevels(level, groupedLayersDV.getListCustomLayers());
GWT.log("cross-filtering mapInnestedFiltering: " + mapInnestedFiltering);
for (Integer theLevel : mapInnestedFiltering.keySet()) {
addListBoxesLevelToPanel(theLevel, mapInnestedFiltering.get(theLevel));
}
fillSelectableLevel(level, null);
bindEvents();
}
/**
* Bind events.
*/
private void bindEvents() {
buttonReset.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
panelResults.clear();
for (int i = 1; i < mapInnestedListBoxes.size(); i++) {
resetListBoxLevel(i);
}
// Selecting the placeholder of the fist list box
List<ListBox> listBox = mapInnestedListBoxes.get(0);
listBox.get(0).setSelectedIndex(0);
// Resetting CQL filtering
applicationBus.fireEvent(new ApplyCQLToLayerOnMapEvent(gCubeCollection.getIndexes().get(0), null));
setFilterHighLighted(false);
}
});
}
/**
* Recursively build selectable levels.
*
* @param level the level
* @param layersIDV the layers IDV
*/
private void recursivelyBuildSelectableLevels(int level, List<? extends LayerIDV> layersIDV) {
if (layersIDV == null)
return;
mapInnestedFiltering.put(level, layersIDV);
for (LayerIDV layerIDV : layersIDV) {
if (layerIDV instanceof CrossFilteringLayerDV) {
CrossFilteringLayerDV crossFilteringLayer = (CrossFilteringLayerDV) layerIDV;
if (crossFilteringLayer.getRelated_to() != null) {
recursivelyBuildSelectableLevels(++level, crossFilteringLayer.getRelated_to());
}
}
}
}
/**
* Fill selectable level.
*
* @param level the level
* @param selectedItem the selected item
*/
private void fillSelectableLevel(int level, SelectableItem selectedItem) {
List<? extends LayerIDV> layersIDV = mapInnestedFiltering.get(level);
List<ListBox> listBoxes = mapInnestedListBoxes.get(level);
if (layersIDV == null)
return;
final HashMap<String, SelectableItem> mapSelectableFeatures = new HashMap<String, CrossFilteringLayerPanel.SelectableItem>();
LayerIDV layerIDV = layersIDV.get(0); // Expected one
if (layerIDV instanceof CrossFilteringLayerDV) {
CrossFilteringLayerDV crossFilteringLayer = (CrossFilteringLayerDV) layerIDV;
String wfsURL = crossFilteringLayer.getWFS_URL();
if (selectedItem != null) {
String cqlFilterValue = crossFilteringLayer.getTable_parent_key_field() + "=" + selectedItem.keyValue;
wfsURL += "&" + WFSMakerUtil.CQL_FILTER_PARAMETER + "=" + URL.encode(cqlFilterValue);
}
GWT.log("wfsURL request: " + wfsURL);
GeoportalDataViewerServiceAsync.Util.getInstance().getHTTPResponseForURL(wfsURL,
new AsyncCallback<String>() {
@Override
public void onSuccess(String response) {
GWT.log("wfsURL response: " + response);
Feature[] features = OLGeoJSONUtil.readGeoJsonFeatures(MAP_PROJECTION.EPSG_4326, response);
for (Feature feature : features) {
JsPropertyMap<Object> properties = feature.getProperties();
Object keyValue = properties.get(crossFilteringLayer.getTable_key_field());
Object itemField = properties.get(crossFilteringLayer.getTable_show_field());
Object parentKey = null;
if (crossFilteringLayer.getTable_parent_key_field() != null
&& !crossFilteringLayer.getTable_parent_key_field().isEmpty())
parentKey = properties.get(crossFilteringLayer.getTable_parent_key_field());
parentKey = parentKey == null ? "" : parentKey;
SelectableItem selectableItem = new SelectableItem(
crossFilteringLayer.getTable_key_field() + "", keyValue + "",
crossFilteringLayer.getTable_parent_key_field(), itemField + "",
crossFilteringLayer.getName(), crossFilteringLayer.getTable_geometry_name());
GWT.log("selectableItem: " + selectableItem);
String pathFeatureKey = pathFeatureKey(selectableItem);
mapSelectableFeatures.put(pathFeatureKey, selectableItem);
}
GWT.log("mapSelectableFeatures: " + mapSelectableFeatures);
mapInnestedFeatures.put(level, mapSelectableFeatures);
String placholder = placeholderLayer(layersIDV.get(0)); // Expected one
fillListBoxLevel(level, mapSelectableFeatures, listBoxes, placholder);
}
@Override
public void onFailure(Throwable caught) {
panelResults.clear();
HTML error = new HTML(
"Sorry, an issue is occurred on loading data for cross-filtering facility. Error is: "
+ caught.getMessage());
panelResults.add(error);
}
});
}
}
/**
* Path feature key.
*
* @param selectableItem the selectable item
* @return the string
*/
private String pathFeatureKey(SelectableItem selectableItem) {
return "root_" + selectableItem.parentKeyField + "_" + selectableItem.keyField + "_" + selectableItem.keyValue;
}
/**
* Placeholder layer.
*
* @param layersIDV the layers IDV
* @return the string
*/
private String placeholderLayer(LayerIDV layersIDV) {
String placeholder = "Select";
// if (layersIDV != null)
// placeholder += " " + layersIDV.getTitle();
placeholder += " ...";
return placeholder;
}
/**
* Adds the list boxes level to panel.
*
* @param level the level
* @param layersIDV the layers IDV
*/
private void addListBoxesLevelToPanel(int level, List<? extends LayerIDV> layersIDV) {
if (layersIDV == null)
return;
List<ListBox> listBoxes = new ArrayList<ListBox>(layersIDV.size());
for (LayerIDV layerIDV : layersIDV) {
ControlGroup cg = new ControlGroup();
ControlLabel cl = new ControlLabel(layerIDV.getTitle());
Controls controls = new Controls();
ListBox listBox = new ListBox();
String placeholder = placeholderLayer(layerIDV);
listBox.addItem(placeholder);
setEnabledBox(listBox, false);
listBoxes.add(listBox);
controls.add(listBox);
cg.add(cl);
cg.add(controls);
fieldSet.add(cg);
}
mapInnestedListBoxes.put(level, listBoxes);
}
/**
* Sets the enabled box.
*
* @param listBox the list box
* @param bool the bool
*/
public void setEnabledBox(ListBox listBox, boolean bool) {
listBox.setEnabled(bool);
}
/**
* Clear list box.
*
* @param listBox the list box
* @param bool the bool
*/
public void clearListBox(ListBox listBox, boolean bool) {
listBox.setEnabled(bool);
}
/**
* Reset list box level.
*
* @param level the level
*/
private void resetListBoxLevel(int level) {
List<ListBox> listBoxes = mapInnestedListBoxes.get(level);
if (listBoxes != null) {
for (ListBox listBox : listBoxes) {
listBox.clear();
setEnabledBox(listBox, false);
}
}
}
/**
* Fill list box level.
*
* @param level the level
* @param mapSelectableFeatures the map selectable features
* @param listBoxes the list boxes
* @param placeholder the placeholder
*/
private void fillListBoxLevel(int level, HashMap<String, SelectableItem> mapSelectableFeatures,
List<ListBox> listBoxes, String placeholder) {
// GWT.log("fillBox level: " + level + " map: " + mapSelectableFeatures);
if (mapSelectableFeatures == null)
return;
ListBox listBox = listBoxes.get(0); // Expected one
listBox.clear();
listBox.addItem(placeholder);
listBox.getElement().getFirstChildElement().setAttribute("disabled", "disabled");
for (String key : mapSelectableFeatures.keySet()) {
SelectableItem selItem = mapSelectableFeatures.get(key);
listBox.addItem(selItem.itemValue, key);
}
listBox.addChangeHandler(new ChangeHandler() {
@Override
public void onChange(ChangeEvent event) {
loadInnerLevel(level, listBox.getSelectedValue());
for (int i = level + 1; i < mapInnestedListBoxes.size(); i++) {
resetListBoxLevel(i);
}
String key = listBox.getSelectedValue();
SelectableItem selectableItem = mapSelectableFeatures.get(key);
buildCrossFilteringCQLAndFireEvent(selectableItem);
setFilterHighLighted(true);
}
});
setEnabledBox(listBox, true);
}
/**
* Builds the cross filtering CQL and fire event.
*
* @param selectableItem the selectable item
*/
private void buildCrossFilteringCQLAndFireEvent(SelectableItem selectableItem) {
String setCqlFilter = "INTERSECTS(geom,querySingle('" + selectableItem.layername + "','"
+ selectableItem.theGeometryName + "','" + selectableItem.keyField + "=''" + selectableItem.keyValue
+ "'''))";
// setCqlFilter = URL.encode(setCqlFilter);
GWT.log("CQL FILTER built: " + setCqlFilter);
IndexLayerDV indexLayer = gCubeCollection.getIndexes().get(0);
try {
GCubeSDILayer layer = indexLayer.getLayer();
String wmsLink = layer.getOgcLinks().get("wms");
String layerName = URLUtil.extractValueOfParameterFromURL("layers", wmsLink);
String serviceURL = URLUtil.getPathURL(wmsLink);
String toLServiceURL = serviceURL.toLowerCase();
if (toLServiceURL.endsWith("wms")) {
toLServiceURL = toLServiceURL.substring(0, toLServiceURL.length() - 3) + "ows";
}
String wfsCrossFilteringQuery = WFSMakerUtil.buildWFSRequest(toLServiceURL, layerName, 1000, "geom",
setCqlFilter);
GWT.log("wfsCrossFilteringQuery: " + wfsCrossFilteringQuery);
showCountResultsOfWFSCrossFiltering(wfsCrossFilteringQuery);
} catch (Exception e) {
// TODO: handle exception
}
applicationBus.fireEvent(new ApplyCQLToLayerOnMapEvent(indexLayer, setCqlFilter));
}
public void showCountResultsOfWFSCrossFiltering(String wfsQuery) {
panelResults.clear();
panelResults.add(new HTML("<hr>"));
panelResults.add(new LoaderIcon("Applying spatial filter..."));
GeoportalDataViewerServiceAsync.Util.getInstance().getHTTPResponseForURL(wfsQuery, new AsyncCallback<String>() {
@Override
public void onFailure(Throwable caught) {
panelResults.clear();
panelResults.add(new HTML("<hr>"));
Alert alert = new Alert("Error on returning number of items");
alert.setType(AlertType.ERROR);
alert.setClose(false);
panelResults.add(alert);
}
@Override
public void onSuccess(String response) {
Feature[] features = OLGeoJSONUtil.readGeoJsonFeatures(MAP_PROJECTION.EPSG_4326, response);
if (features != null) {
int dataCount = features.length;
panelResults.clear();
panelResults.add(new HTML("<hr>"));
String message = "";
if (dataCount <= 0) {
message = "No item found";
} else {
message = "Found " + dataCount;
message += dataCount > 1 ? " items" : " item";
message += ". ";
}
HTML resultMessage = new HTML();
resultMessage.getElement().addClassName("search_result_msg");
resultMessage.setHTML(message);
panelResults.add(resultMessage);
}
}
});
}
/**
* Load inner level.
*
* @param level the level
* @param selectableItemValue the selectable item value
*/
private void loadInnerLevel(int level, String selectableItemValue) {
// GWT.log("selected level " + level + " selectableItemValue " +
// selectableItemValue);
HashMap<String, SelectableItem> mapSelectableItem = mapInnestedFeatures.get(level);
SelectableItem selectedItem = mapSelectableItem.get(selectableItemValue);
// GWT.log("selected selectableItem " + selectedItem);
int innerLevel = level + 1;
List<? extends LayerIDV> selectedLayers = mapInnestedFiltering.get(innerLevel);
// if layer filtering exists at this level
if (selectedLayers != null) {
List<ListBox> selectedListBoxes = mapInnestedListBoxes.get(innerLevel);
GWT.log("loading inner layers " + selectedLayers);
GWT.log("loading selectedListBoxes " + selectedListBoxes);
fillSelectableLevel(innerLevel, selectedItem);
}
}
/**
* Sets the filter high lighted.
*
* @param bool the new filter high lighted
*/
private void setFilterHighLighted(boolean bool) {
if (bool) {
filterButton.getElement().addClassName("highlight-button");
} else {
filterButton.getElement().removeClassName("highlight-button");
}
}
/**
* Sets the search button.
*
* @param searchFacilityButton the new search button
*/
public void setFilterButton(DropdownButton searchFacilityButton) {
this.filterButton = searchFacilityButton;
}
/**
* The Class SelectableItem.
*
* @author Francesco Mangiacrapa at ISTI-CNR francesco.mangiacrapa@isti.cnr.it
*
* May 31, 2023
*/
public class SelectableItem {
protected String keyField;
protected String keyValue;
protected String parentKeyField;
protected String itemValue;
protected String layername;
protected String theGeometryName;
/**
* Instantiates a new selectable item.
*/
SelectableItem() {
}
/**
* Instantiates a new selectable item.
*
* @param keyField the key field
* @param keyValue the key value
* @param parentKeyField the parent key field
* @param itemValue the item value
* @param layername the layername
* @param theGeometryName the the geometry name
*/
public SelectableItem(String keyField, String keyValue, String parentKeyField, String itemValue,
String layername, String theGeometryName) {
super();
this.keyField = keyField;
this.keyValue = keyValue;
this.parentKeyField = parentKeyField;
this.itemValue = itemValue;
this.layername = layername;
this.theGeometryName = theGeometryName;
}
/**
* To string.
*
* @return the string
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("SelectableItem [keyField=");
builder.append(keyField);
builder.append(", keyValue=");
builder.append(keyValue);
builder.append(", parentKeyField=");
builder.append(parentKeyField);
builder.append(", itemValue=");
builder.append(itemValue);
builder.append(", layername=");
builder.append(layername);
builder.append(", theGeometryName=");
builder.append(theGeometryName);
builder.append("]");
return builder.toString();
}
}
}