/*
* uDig - User Friendly Desktop Internet GIS client
* (C) MangoSystem - www.mangosystem.com
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* (http://www.eclipse.org/legal/epl-v10.html), and the Refractions BSD
* License v1.0 (http://udig.refractions.net/files/bsd3-v10.html).
*/
package org.locationtech.udig.processingtoolbox.tools;
import java.awt.Color;
import java.util.logging.Logger;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.ColorDialog;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Slider;
import org.eclipse.swt.widgets.Spinner;
import org.geotools.brewer.color.BrewerPalette;
import org.geotools.brewer.color.ColorBrewer;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.process.spatialstatistics.core.FeatureTypes;
import org.geotools.process.spatialstatistics.core.FeatureTypes.SimpleShapeType;
import org.geotools.process.spatialstatistics.styler.GraduatedColorStyleBuilder;
import org.geotools.process.spatialstatistics.styler.GraduatedSymbolStyleBuilder;
import org.geotools.process.spatialstatistics.styler.SSStyleBuilder;
import org.geotools.styling.Fill;
import org.geotools.styling.PolygonSymbolizer;
import org.geotools.styling.Stroke;
import org.geotools.styling.Style;
import org.geotools.styling.StyleFactory;
import org.geotools.util.logging.Logging;
import org.locationtech.udig.processingtoolbox.ToolboxPlugin;
import org.locationtech.udig.processingtoolbox.internal.Messages;
import org.locationtech.udig.processingtoolbox.styler.MapUtils;
import org.locationtech.udig.processingtoolbox.styler.MapUtils.FieldType;
import org.locationtech.udig.processingtoolbox.styler.MapUtils.VectorLayerType;
import org.locationtech.udig.project.ILayer;
import org.locationtech.udig.project.IMap;
import org.locationtech.udig.style.sld.SLDContent;
import org.locationtech.udig.style.sld.editor.BorderColorComboListener.Outline;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.filter.FilterFactory2;
/**
* Histogram Dialog
*
* @author Minpa Lee, MangoSystem
*
* @source $URL$
*/
public class ThematicMapDialog extends AbstractGeoProcessingDialog {
protected static final Logger LOGGER = Logging.getLogger(ThematicMapDialog.class);
@SuppressWarnings("nls")
static final String[] NUMERIC_METHOD = new String[] { "Equal Interval", "Natural Breaks",
"Quantile", "Standard Deviation" };
@SuppressWarnings("nls")
static final String[] CATEGORY_METHOD = new String[] { "Unique Values", "LISA Style" };
private ColorBrewer brewer;
private StyleFactory sf = CommonFactoryFinder.getStyleFactory(null);
private FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2(null);
private ILayer activeLayer;
private Button chkReverse, chkSymbol;
private Combo cboLayer, cboField, cboMethod, cboNormal, cboOutline;
private Combo cboColorRamp;
private Spinner spnClass, spnTransparency, spnLineWidth, spnMin, spnMax;
private Slider sldTransparency;
public ThematicMapDialog(Shell parentShell, IMap map) {
super(parentShell, map);
setShellStyle(SWT.CLOSE | SWT.MIN | SWT.TITLE | SWT.BORDER | SWT.MODELESS | SWT.RESIZE);
this.windowTitle = Messages.ThematicMapDialog_title;
this.windowDesc = Messages.ThematicMapDialog_description;
this.windowSize = ToolboxPlugin.rescaleSize(parentShell, 550, 350);
this.brewer = ColorBrewer.instance();
}
@Override
protected Control createDialogArea(final Composite parent) {
Composite area = (Composite) super.createDialogArea(parent);
Composite container = new Composite(area, SWT.NONE);
container.setLayout(new GridLayout(6, false));
container.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
Image image = ToolboxPlugin.getImageDescriptor("icons/public_co.gif").createImage(); //$NON-NLS-1$
// Layer
uiBuilder.createLabel(container, Messages.ThematicMapDialog_InputLayer, EMPTY, image, 1);
cboLayer = uiBuilder.createCombo(container, 5, true);
fillLayers(map, cboLayer, VectorLayerType.ALL);
// Field
uiBuilder.createLabel(container, Messages.ThematicMapDialog_InputField, EMPTY, image, 1);
cboField = uiBuilder.createCombo(container, 2, true);
uiBuilder.createLabel(container, Messages.ThematicMapDialog_Normalization, EMPTY, image, 1);
cboNormal = uiBuilder.createCombo(container, 2, true);
// Class
uiBuilder.createLabel(container, Messages.ThematicMapDialog_Mode, EMPTY, image, 1);
cboMethod = uiBuilder.createCombo(container, 2, true);
uiBuilder.createLabel(container, Messages.ThematicMapDialog_Classes, EMPTY, image, 1);
spnClass = uiBuilder.createSpinner(container, 5, 2, 20, 0, 1, 1, 2);
// Ramp
uiBuilder.createLabel(container, Messages.ThematicMapDialog_ColorRamp, EMPTY, image, 1);
cboColorRamp = uiBuilder.createCombo(container, 5, true);
// Reverse color ramp
uiBuilder.createLabel(container, EMPTY, EMPTY, null, 1);
chkReverse = uiBuilder.createCheckbox(container, Messages.ThematicMapDialog_Reverse, EMPTY,
5);
// Graduated Symbol
uiBuilder.createLabel(container, EMPTY, EMPTY, null, 1);
chkSymbol = uiBuilder
.createCheckbox(container, Messages.ThematicMapDialog_Symbol, EMPTY, 1);
uiBuilder.createLabel(container, Messages.ThematicMapDialog_Minimum, EMPTY, null, 1);
spnMin = uiBuilder.createSpinner(container, 1, 0, 5, 0, 1, 1, 1);
uiBuilder.createLabel(container, Messages.ThematicMapDialog_Maximum, EMPTY, null, 1);
spnMax = uiBuilder.createSpinner(container, 0, 5, 200, 0, 1, 1, 1);
// transparency
uiBuilder.createLabel(container, Messages.ThematicMapDialog_Transparency, EMPTY, image, 1);
Composite subCon = new Composite(container, SWT.NONE);
subCon.setLayout(uiBuilder.createGridLayout(5, false, 0, 5));
subCon.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 5, 1));
sldTransparency = uiBuilder.createSlider(subCon, 10, 0, 100, 0, 1, 10, 4);
spnTransparency = new Spinner(subCon, SWT.RIGHT_TO_LEFT | SWT.BORDER);
GridData gridData = new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1);
gridData.widthHint = 20;
spnTransparency.setLayoutData(gridData);
spnTransparency.setValues(10, 0, 100, 0, 1, 10);
// outline
uiBuilder.createLabel(container, Messages.ThematicMapDialog_OutlineColor, EMPTY, image, 1);
cboOutline = uiBuilder.createCombo(container, 2, true);
uiBuilder.createLabel(container, Messages.ThematicMapDialog_OutlineWidth, EMPTY, image, 1);
spnLineWidth = uiBuilder.createSpinner(container, 5, 0, 100, 1, 1, 10, 2);
cboOutline.setItems(Outline.labels());
cboOutline.addModifyListener(new ModifyListener() {
@Override
public void modifyText(ModifyEvent e) {
int selection = cboOutline.getSelectionIndex();
if (selection == 0) { // None
cboOutline.setData(java.awt.Color.GRAY);
} else if (selection == 1) { // Black
cboOutline.setData(java.awt.Color.BLACK);
} else if (selection == 2) { // White
cboOutline.setData(java.awt.Color.WHITE);
} else if (selection == 3) { // Custom
ColorDialog cd = new ColorDialog(parent.getShell());
cd.setText("Select Custom Color"); //$NON-NLS-1$
if (cboOutline.getData() != null) {
java.awt.Color c = (java.awt.Color) cboOutline.getData();
cd.setRGB(new RGB(c.getRed(), c.getGreen(), c.getBlue()));
}
RGB rgb = cd.open();
if (rgb != null) {
cboOutline.setData(new java.awt.Color(rgb.red, rgb.green, rgb.blue));
}
}
}
});
cboOutline.select(2);
cboLayer.addModifyListener(new ModifyListener() {
@Override
public void modifyText(ModifyEvent e) {
if (cboLayer.getSelectionIndex() == -1) {
return;
}
activeLayer = MapUtils.getLayer(map, cboLayer.getText());
if (activeLayer != null) {
fillFields(cboField, activeLayer.getSchema(), FieldType.Number);
fillFields(cboNormal, activeLayer.getSchema(), FieldType.Number);
cboNormal.add(EMPTY);
cboNormal.select(cboNormal.getItemCount() - 1);
cboMethod.removeAll();
if (FeatureTypes.getSimpleShapeType(activeLayer.getSchema()) == SimpleShapeType.LINESTRING) {
spnMax.setSelection(5);
} else {
spnMax.setSelection(50);
}
}
}
});
cboField.addModifyListener(new ModifyListener() {
@Override
public void modifyText(ModifyEvent e) {
if (cboField.getSelectionIndex() == -1) {
return;
}
boolean numeric = FeatureTypes.isNumeric(activeLayer.getSchema(),
cboField.getText());
cboNormal.setEnabled(numeric);
if (numeric) {
cboMethod.setItems(NUMERIC_METHOD);
} else {
cboMethod.setItems(CATEGORY_METHOD);
}
cboMethod.select(0);
}
});
sldTransparency.addListener(SWT.Selection, new Listener() {
@Override
public void handleEvent(Event event) {
spnTransparency.setSelection(sldTransparency.getSelection());
}
});
spnTransparency.addListener(SWT.Selection, new Listener() {
@Override
public void handleEvent(Event event) {
sldTransparency.setSelection(spnTransparency.getSelection());
}
});
updateColorRamp(0);
area.pack(true);
return area;
}
@SuppressWarnings("nls")
private void updateColorRamp(int selection) {
BrewerPalette[] palettes = null;
if (selection == 0) // All
palettes = brewer.getPalettes(ColorBrewer.ALL);
else if (selection == 1) // Numerical
palettes = brewer.getPalettes(ColorBrewer.SUITABLE_RANGED);
else if (selection == 2) // Sequential
palettes = brewer.getPalettes(ColorBrewer.SEQUENTIAL);
else if (selection == 3) // Diverging
palettes = brewer.getPalettes(ColorBrewer.DIVERGING);
else if (selection == 4) // Categorical
palettes = brewer.getPalettes(ColorBrewer.SUITABLE_UNIQUE);
else
palettes = brewer.getPalettes(ColorBrewer.ALL);
cboColorRamp.removeAll();
for (BrewerPalette palette : palettes) {
String name = String.format("%s(%s)", palette.getName(), palette.getDescription());
// Image image = Glyph.palette(palette.getColors()).createImage();
cboColorRamp.add(name);
}
cboColorRamp.select(cboColorRamp.getItemCount() > 5 ? 5 : 0);
}
@Override
protected void okPressed() {
if (invalidWidgetValue(cboLayer, cboField)) {
openInformation(getShell(), Messages.Task_ParameterRequired);
return;
}
// Style origStyle = (Style) inputLayer.getStyleBlackboard().get(SLDContent.ID);
Runnable runnable = new Runnable() {
@Override
@SuppressWarnings({})
public void run() {
float opacity = (100 - spnTransparency.getSelection()) / 100f;
String fieldName = cboField.getText();
String normalProperty = cboNormal.getText();
String palette = cboColorRamp.getItem(cboColorRamp.getSelectionIndex());
String paletteName = palette.split("\\(")[0]; //$NON-NLS-1$
boolean reverse = chkReverse.getSelection();
java.awt.Color outlineColor = (java.awt.Color) cboOutline.getData();
float outlineWidth = spnLineWidth.getSelection() / (10f * spnLineWidth.getDigits());
if (cboOutline.getSelectionIndex() == 0) {
outlineWidth = 0f;
}
int numClasses = spnClass.getSelection();
String functionName = getFunctionName();
SimpleFeatureCollection features = MapUtils.getFeatures(activeLayer);
SimpleFeatureType schema = activeLayer.getSchema();
// create default style
SSStyleBuilder ssBuilder = new SSStyleBuilder(schema);
ssBuilder.setOpacity(opacity);
Style style = ssBuilder.getDefaultFeatureStyle();
// crate thematic style
if (chkSymbol.getSelection()) {
double minSize = uiBuilder.fromSpinnerValue(spnMin, spnMin.getSelection());
double maxSize = uiBuilder.fromSpinnerValue(spnMax, spnMax.getSelection());
GraduatedSymbolStyleBuilder builder = new GraduatedSymbolStyleBuilder();
builder.setMinSize((float) minSize);
builder.setMaxSize((float) maxSize);
builder.setFillOpacity(opacity);
builder.setOutlineColor(outlineColor);
builder.setOutlineWidth(outlineWidth);
if (outlineWidth == 0) {
builder.setLineOpacity(0.0f);
}
builder.setNormalProperty(normalProperty);
// background symbol
if (FeatureTypes.getSimpleShapeType(schema) == SimpleShapeType.POLYGON) {
String geom = schema.getGeometryDescriptor().getLocalName();
Stroke stroke = sf.createStroke(ff.literal(outlineColor),
ff.literal(opacity));
Fill fill = sf.createFill(ff.literal(new Color(234, 234, 234)),
ff.literal(0.5f));
PolygonSymbolizer backgroundSymbol = sf.createPolygonSymbolizer(stroke,
fill, geom);
builder.setBackgroundSymbol(backgroundSymbol);
}
style = builder.createStyle(features, fieldName, functionName, numClasses,
paletteName, reverse);
} else {
GraduatedColorStyleBuilder builder = new GraduatedColorStyleBuilder();
builder.setFillOpacity(opacity);
builder.setOutlineColor(outlineColor);
builder.setOutlineWidth(outlineWidth);
if (outlineWidth == 0) {
builder.setLineOpacity(0.0f);
}
builder.setNormalProperty(normalProperty);
style = builder.createStyle(features, fieldName, functionName, numClasses,
paletteName, reverse);
}
if (style != null) {
// put the style on the blackboard
activeLayer.getStyleBlackboard().clear();
activeLayer.getStyleBlackboard().put(SLDContent.ID, style);
activeLayer.getStyleBlackboard().flush();
activeLayer.refresh(activeLayer.getBounds(new NullProgressMonitor(), null));
}
}
};
try {
BusyIndicator.showWhile(Display.getCurrent(), runnable);
} catch (Exception e) {
ToolboxPlugin.log(e);
MessageDialog.openError(getParentShell(), Messages.General_Error, e.getMessage());
}
}
@SuppressWarnings("nls")
private String getFunctionName() {
String styleName = cboMethod.getText().toUpperCase();
String functionName = null;
if (styleName.startsWith("LISA")) {
functionName = "LISA Style";
} else if (styleName.startsWith("ZSCORE")) {
functionName = "ZScore";
} else if (styleName.startsWith("ZSCORE STAND")) {
functionName = "ZScore Standard";
} else if (styleName.startsWith("CL") || styleName.startsWith("JE")
|| styleName.startsWith("NA")) {
functionName = "JenksNaturalBreaksFunction";
} else if (styleName.startsWith("E")) {
functionName = "EqualIntervalFunction";
} else if (styleName.startsWith("S")) {
functionName = "StandardDeviationFunction";
} else if (styleName.startsWith("Q")) {
functionName = "QuantileFunction";
} else if (styleName.startsWith("U")) {
functionName = "UniqueIntervalFunction";
}
return functionName;
}
}