+ );
+ } else if (legendHTML.error) {
+ return getNoActiveElem(legendHTML.error);
+ }
+ return getNoActiveElem('');
+};
+
+Legend.propTypes = {
+ indicatorData: PropTypes.object,
+ transparency: PropTypes.number,
+ legendProps: PropTypes.object,
+ loc: PropTypes.func
+};
+
+const contextWrapped = withContext(Legend);
+export {contextWrapped as Legend};
diff --git a/bundles/statistics/statsgrid2016/components/classification/classification.scss b/bundles/statistics/statsgrid2016/components/classification/classification.scss
new file mode 100644
index 0000000000..6bf7be3fa5
--- /dev/null
+++ b/bundles/statistics/statsgrid2016/components/classification/classification.scss
@@ -0,0 +1,21 @@
+.statsgrid-classification-container {
+ background-color: #FFFFFF;
+ width: 300px;
+ font-size: 12px;
+ border: 1px solid rgba(0,0,0,0.2);
+ pointer-events: auto;
+ text-align: left;
+}
+
+.statsgrid-classification-plugin-transparent {
+ .statsgrid-classification-container {
+ background-color: transparent;
+ border: 1px solid transparent;
+ color:#FFFFFF;
+ text-shadow:
+ -1px -1px 0 #000,
+ 1px -1px 0 #000,
+ -1px 1px 0 #000,
+ 1px 1px 0 #000;
+ }
+}
diff --git a/bundles/statistics/statsgrid2016/components/classification/editclassification/Checkbox.jsx b/bundles/statistics/statsgrid2016/components/classification/editclassification/Checkbox.jsx
new file mode 100644
index 0000000000..5a82cff9f2
--- /dev/null
+++ b/bundles/statistics/statsgrid2016/components/classification/editclassification/Checkbox.jsx
@@ -0,0 +1,25 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import {withContext} from '../../../../../../src/react/util.jsx';
+
+const Checkbox = ({properties, value, handleChange, disabled}) => {
+ return (
+
+ );
+};
+
+Checkbox.propTypes = {
+ properties: PropTypes.object,
+ disabled: PropTypes.bool,
+ value: PropTypes.bool,
+ handleChange: PropTypes.func
+};
+
+const contextWrapped = withContext(Checkbox);
+export {contextWrapped as Checkbox};
diff --git a/bundles/statistics/statsgrid2016/components/classification/editclassification/Color.jsx b/bundles/statistics/statsgrid2016/components/classification/editclassification/Color.jsx
new file mode 100644
index 0000000000..a4dc2b416d
--- /dev/null
+++ b/bundles/statistics/statsgrid2016/components/classification/editclassification/Color.jsx
@@ -0,0 +1,48 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import {withContext} from '../../../../../../src/react/util.jsx';
+import {ColorSelect} from './ColorSelect';
+import './color.scss';
+
+const handleReverseColors = (service, isReverse) => {
+ service.getStateService().updateActiveClassification('reverseColors', isReverse);
+};
+const handleColorChange = (service, value) => {
+ service.getStateService().updateActiveClassification('name', value);
+};
+
+const Color = ({colors, values, loc, service, disabled}) => {
+ let label = loc('colorset.button');
+ const isSimple = values.mapStyle !== 'choropleth';
+ const opacity = values.transparency / 100 || 1;
+ if (isSimple) {
+ label = loc('classify.map.color');
+ }
+ return (
+
+ );
+};
+Color.propTypes = {
+ colors: PropTypes.array,
+ values: PropTypes.object,
+ disabled: PropTypes.bool,
+ service: PropTypes.object,
+ loc: PropTypes.func
+};
+
+const contextWrapped = withContext(Color);
+export {contextWrapped as Color};
diff --git a/bundles/statistics/statsgrid2016/components/classification/editclassification/ColorSelect.jsx b/bundles/statistics/statsgrid2016/components/classification/editclassification/ColorSelect.jsx
new file mode 100644
index 0000000000..5cc0db675b
--- /dev/null
+++ b/bundles/statistics/statsgrid2016/components/classification/editclassification/ColorSelect.jsx
@@ -0,0 +1,133 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import {withContext, handleBinder} from '../../../../../../src/react/util.jsx';
+import {colorSetColorWidth} from './colorselect.scss';
+
+class ColorSelect extends React.Component {
+ constructor (props) {
+ super(props);
+ this.state = {
+ isOpen: false
+ };
+ handleBinder(this);
+ }
+ componentDidMount () {
+ document.addEventListener('mouseup', this.handleClick);
+ }
+ componentWillUnmount () {
+ document.removeEventListener('mouseup', this.handleClick);
+ }
+ handleClick (event) {
+ if (this.props.disabled) {
+ return;
+ }
+ const outside = this.wrapperRef && !this.wrapperRef.contains(event.target);
+ if (outside && this.state.isOpen) {
+ this.setState({isOpen: false});
+ } else if (!outside) {
+ this.setState(oldState => ({ isOpen: !oldState.isOpen }));
+ }
+ }
+ handleWrapperRef (node) {
+ this.wrapperRef = node;
+ }
+
+ // color: 'deebf7'
+ getSimpleColorElement (color) {
+ return (
+
+ );
+ }
+
+ // color: {id:'Blues', value:['deebf7','9ecae1','3182bd']}
+ getMultiColorElement (color) {
+ const colors = color.value;
+ const width = parseInt(colorSetColorWidth) * colors.length;
+ return (
+
+ );
+ }
+
+ getColorSelection () {
+ if (!this.state.isOpen) {
+ return;
+ }
+ const colors = this.props.colors;
+ return (
+
+ );
+ }
+ getSelectedColor () {
+ const value = this.props.value;
+ const colors = this.props.colors;
+ let selected;
+ if (this.props.isSimple) {
+ selected = colors[value];
+ } else {
+ selected = colors.find(color => color.id === value);
+ }
+ if (selected) {
+ return selected;
+ }
+ return colors[0];
+ }
+
+ render () {
+ const selected = this.getSelectedColor();
+ const className = this.props.disabled ? 'oskari-color-selection-main disabled' : 'oskari-color-selection-main';
+ return (
+
+ );
+ }
+};
+
+ColorSelect.propTypes = {
+ colors: PropTypes.array,
+ value: PropTypes.oneOfType([
+ PropTypes.string,
+ PropTypes.number
+ ]),
+ disabled: PropTypes.bool,
+ isSimple: PropTypes.bool,
+ opacity: PropTypes.number,
+ handleColorChange: PropTypes.func,
+ service: PropTypes.object,
+ loc: PropTypes.func
+};
+
+const contextWrapped = withContext(ColorSelect);
+export {contextWrapped as ColorSelect};
diff --git a/bundles/statistics/statsgrid2016/components/classification/editclassification/EditClassification.jsx b/bundles/statistics/statsgrid2016/components/classification/editclassification/EditClassification.jsx
new file mode 100644
index 0000000000..e842c8728b
--- /dev/null
+++ b/bundles/statistics/statsgrid2016/components/classification/editclassification/EditClassification.jsx
@@ -0,0 +1,188 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import {withContext} from '../../../../../../src/react/util.jsx';
+import {Select} from './Select';
+import {Slider} from './Slider';
+import {Checkbox} from './Checkbox';
+import {Color} from './Color';
+import {ManualClassification} from '../../manualClassification/ManualClassification';
+import './editclassification.scss';
+
+const getLocalizedOptions = (options, locObj, disabledOptions) => {
+ if (!Array.isArray(options)) {
+ return [];
+ }
+ return options.map(option => {
+ const obj = {
+ value: option
+ };
+ if (locObj[option]) {
+ obj.text = locObj[option];
+ }
+ if (Array.isArray(disabledOptions) && disabledOptions.includes(option)) {
+ obj.disabled = true;
+ }
+ return obj;
+ });
+};
+const getTransparencyOptions = transparency => {
+ var options = [];
+ for (var i = 100; i >= 30; i -= 10) {
+ options.push({
+ value: i,
+ text: i + ' %'
+ });
+ }
+ options.push({
+ value: transparency,
+ text: transparency + ' %',
+ hidden: true
+ });
+ return options;
+};
+
+const getValidCountRange = classifications => {
+ const validOptions = classifications.validOptions;
+ const range = classifications.countRange;
+ if (validOptions && validOptions.maxCount) {
+ return range.map(count => {
+ let disabled = false;
+ if (count > validOptions.maxCount) {
+ disabled = true;
+ }
+ return {
+ value: count,
+ disabled: disabled
+ };
+ });
+ }
+ return range;
+};
+const getDisabledOptions = props => {
+ const disabled = {};
+ // Discontinous mode causes trouble with manually set bounds. Causes error if some class gets no hits.
+ // Disabling it for data series
+ if (props.indicators.active.series) {
+ disabled.mode = ['discontinuous'];
+ }
+ return disabled;
+};
+
+const handleSelectChange = (service, properties, value) => {
+ if (properties.valueType === 'int') {
+ value = parseInt(value);
+ }
+ service.getStateService().updateActiveClassification(properties.id, value);
+};
+
+const handleCheckboxChange = (service, id, isSelected) => {
+ service.getStateService().updateActiveClassification(id, isSelected);
+};
+
+const EditClassification = props => {
+ const {indicators, service, loc} = props;
+ const {methods, values, disabled, modes, colors, types, mapStyles} = props.classifications;
+ const disabledOptions = getDisabledOptions(props);
+
+ return (
+
+ );
+};
+EditClassification.propTypes = {
+ indicators: PropTypes.object,
+ state: PropTypes.object,
+ classifications: PropTypes.object,
+ service: PropTypes.object,
+ loc: PropTypes.func
+};
+
+const contextWrapped = withContext(EditClassification);
+export {contextWrapped as EditClassification};
diff --git a/bundles/statistics/statsgrid2016/components/classification/editclassification/Select.jsx b/bundles/statistics/statsgrid2016/components/classification/editclassification/Select.jsx
new file mode 100644
index 0000000000..339c1b9401
--- /dev/null
+++ b/bundles/statistics/statsgrid2016/components/classification/editclassification/Select.jsx
@@ -0,0 +1,34 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import {withContext} from '../../../../../../src/react/util.jsx';
+
+const Select = ({properties, options, value, disabled, handleChange}) => {
+ // options => array of values [1,3,4,5] or array of objects with properties: value and optionally: text, hidden, disabled
+ return (
+
+ );
+};
+Select.propTypes = {
+ properties: PropTypes.object,
+ options: PropTypes.array,
+ disabled: PropTypes.bool,
+ value: PropTypes.any,
+ handleChange: PropTypes.func
+};
+
+const contextWrapped = withContext(Select);
+export {contextWrapped as Select};
diff --git a/bundles/statistics/statsgrid2016/components/classification/editclassification/Slider.jsx b/bundles/statistics/statsgrid2016/components/classification/editclassification/Slider.jsx
new file mode 100644
index 0000000000..a2a96e60fd
--- /dev/null
+++ b/bundles/statistics/statsgrid2016/components/classification/editclassification/Slider.jsx
@@ -0,0 +1,78 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import {withContext} from '../../../../../../src/react/util.jsx';
+import './slider.scss';
+
+class Slider extends React.Component {
+ constructor (props) {
+ super(props);
+ this._rangeSlider = {
+ min: 10,
+ max: 120,
+ step: 5
+ };
+ }
+ componentDidMount () {
+ const minRange = this.props.values.count * this._rangeSlider.step;
+ this.$el = jQuery(this.el);
+ this.$el.slider({
+ min: this._rangeSlider.min,
+ max: this._rangeSlider.max,
+ step: this._rangeSlider.step,
+ range: true,
+ disabled: this.props.disabled,
+ values: [this.props.values.min, this.props.values.max],
+ slide: (event, ui) => {
+ const min = ui.values[0];
+ const max = ui.values[1];
+ if (max - min >= minRange) {
+ return true;
+ }
+ return false;
+ },
+ stop: (event, ui) => {
+ const value = {
+ min: ui.values[0],
+ max: ui.values[1]
+ };
+ this.props.service.getStateService().updateActiveClassificationObj(value);
+ }
+ });
+ }
+ componentDidUpdate () {
+ if (!this.$el) {
+ return;
+ }
+ if (this.props.disabled) {
+ this.$el.slider('disable');
+ } else {
+ this.$el.slider('enable');
+ }
+ }
+ componentWillUnmount () {
+ this.$el.slider('destroy');
+ }
+ render () {
+ const loc = this.props.loc;
+ return (
+
+
{loc('classify.map.pointSize')}
+
+
{loc('classify.map.min')}
+
{loc('classify.map.max')}
+
+
{
+ this.el = el;
+ }}/>
+
+ );
+ }
+};
+Slider.propTypes = {
+ values: PropTypes.object,
+ disabled: PropTypes.bool,
+ service: PropTypes.object,
+ loc: PropTypes.func
+};
+const cls = withContext(Slider);
+export {cls as Slider};
diff --git a/bundles/statistics/statsgrid2016/components/classification/editclassification/color.scss b/bundles/statistics/statsgrid2016/components/classification/editclassification/color.scss
new file mode 100644
index 0000000000..843681c7f8
--- /dev/null
+++ b/bundles/statistics/statsgrid2016/components/classification/editclassification/color.scss
@@ -0,0 +1,30 @@
+.statsgrid-classification-container {
+ .classification-colors {
+ .flip-colors {
+ margin-left: 10px;
+ line-height: 27px;
+ }
+ }
+}
+
+.statsgrid-classification-plugin-transparent {
+ .oskari-color-selection-main {
+ .color-selection-arrow {
+ display: none;
+ }
+ .oskari-selected-color {
+ border: 1px solid transparent;
+ }
+ .oskari-color-selection {
+ border: 1px solid transparent;
+ }
+ &.disabled {
+ .oskari-color-option {
+ cursor: auto;
+ }
+ .oskari-selected-color, .color-selection-arrow {
+ cursor: auto;
+ }
+ }
+ }
+}
diff --git a/bundles/statistics/statsgrid2016/components/classification/editclassification/colorselect.scss b/bundles/statistics/statsgrid2016/components/classification/editclassification/colorselect.scss
new file mode 100644
index 0000000000..a33010f32e
--- /dev/null
+++ b/bundles/statistics/statsgrid2016/components/classification/editclassification/colorselect.scss
@@ -0,0 +1,87 @@
+$borderColor: #AAAAAA;
+$disabledBorderColor: #DDDDDD;
+$colorBorder: 1px solid #555555;
+$hoverColor: #306BC8;
+$simpleColorWidth: 16px;
+$colorSetColorWidth: 6px;
+$colorHeight: 16px;
+
+.oskari-color-selection-main {
+ position: relative;
+
+ &.disabled {
+ .oskari-selected-color {
+ border: 1px solid $disabledBorderColor;
+ }
+ .color-selection-arrow {
+ border: 1px solid $disabledBorderColor;
+ .icon-arrow-down {
+ opacity: 0.6;
+ }
+ }
+ }
+ .oskari-color-option {
+ cursor: pointer;
+ .oskari-color {
+ height: 100%;
+ width: 100%;
+ float: left;
+ }
+ .oskari-color-set {
+ border: $colorBorder;
+ height: $colorHeight;
+ .oskari-color {
+ width: $colorSetColorWidth;
+ }
+ }
+ .oskari-color-simple {
+ border: $colorBorder;
+ height: $colorHeight;
+ width: $simpleColorWidth;
+ }
+ }
+ .oskari-color-selection {
+ top: 28px;
+ left: 0px;
+ width: 101px;
+ z-index: 10;
+ border: 1px solid $borderColor;
+ border-top: none;
+ max-height: 100px;
+ overflow: auto;
+ position: absolute;
+ .oskari-color-option {
+ padding: 2px;
+ background-color: #FFFFFF;
+ }
+ .oskari-color-option:hover{
+ background-color: $hoverColor;
+ }
+
+ }
+
+ .oskari-selected-color {
+ border: 1px solid $borderColor;
+ padding: 4px;
+ width: 80px;
+ height: 18px;
+ cursor: pointer;
+ float: left;
+ .oskari-color-option {
+ background-color: transparent;
+ }
+ }
+ .color-selection-arrow {
+ float: left;
+ padding-top: 6px;
+ width: 12px;
+ height: 20px;
+ border: 1px solid $borderColor;
+ border-left: none;
+ cursor: pointer;
+ }
+}
+
+:export {
+ colorSetColorWidth: $colorSetColorWidth
+}
diff --git a/bundles/statistics/statsgrid2016/components/classification/editclassification/editclassification.scss b/bundles/statistics/statsgrid2016/components/classification/editclassification/editclassification.scss
new file mode 100644
index 0000000000..007f4cd1ea
--- /dev/null
+++ b/bundles/statistics/statsgrid2016/components/classification/editclassification/editclassification.scss
@@ -0,0 +1,64 @@
+.statsgrid-classification-container {
+ .classification-edit {
+ background-color: #fafafa;
+
+ .classification-options {
+ margin-left: 8px;
+ padding: 5px;
+ .option {
+ clear: both;
+ }
+ .select {
+ width: 96%;
+ font-size: 12px;
+ padding: 1px 2px;
+ }
+ .select-label {
+ font-weight: bold;
+ }
+ .show-values {
+ .label {
+ display: block;
+ margin-bottom: 4px;
+ margin-top: 8px;
+ }
+ }
+ }
+ }
+}
+
+/* published/embedded map classification background transparent */
+.statsgrid-classification-plugin-transparent {
+ .classification-edit {
+ background-color: transparent;
+ }
+ .dropdown {
+ text-shadow: none;
+ color: #000;
+ }
+ .select {
+ cursor: pointer;
+ appearance: none;
+ -webkit-appearance: none;
+ margin-left: 12px;
+ background-color: transparent;
+ background: none;
+ border: none;
+ color: #ffffff;
+ text-shadow:
+ -1px -1px 0 #000,
+ 1px -1px 0 #000,
+ -1px 1px 0 #000,
+ 1px 1px 0 #000;
+ &:disabled {
+ cursor: auto;
+ }
+ }
+ option {
+ color: #3c3c3c;
+ &:disabled {
+ color: graytext;
+ }
+ }
+
+}
diff --git a/bundles/statistics/statsgrid2016/components/classification/editclassification/slider.scss b/bundles/statistics/statsgrid2016/components/classification/editclassification/slider.scss
new file mode 100644
index 0000000000..29bc393e17
--- /dev/null
+++ b/bundles/statistics/statsgrid2016/components/classification/editclassification/slider.scss
@@ -0,0 +1,21 @@
+.oskari-slider {
+ .slider-label {
+ font-weight: bold;
+ }
+ .minmaxlabels {
+ margin-bottom: '25px';
+ .min {
+ float:left;
+ }
+ .max {
+ float:right;
+ margin-right: 4%;
+ }
+ }
+ .point-range {
+ clear: both;
+ width: 90%;
+ background-image: url(../../../resources/images/opacity_slider_long.png);
+ background-repeat: no-repeat;
+ }
+}
\ No newline at end of file
diff --git a/bundles/statistics/statsgrid2016/components/classification/header.scss b/bundles/statistics/statsgrid2016/components/classification/header.scss
new file mode 100644
index 0000000000..4a9b89a47a
--- /dev/null
+++ b/bundles/statistics/statsgrid2016/components/classification/header.scss
@@ -0,0 +1,71 @@
+.statsgrid-classification-container {
+ .active-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 5px;
+ position: relative;
+ &.multi-selected select {
+ width: 94%;
+ padding: 2px;
+ }
+ &.multi-selected:hover:after {
+ content: attr(data-selected-indicator);
+ display: block;
+ position: absolute;
+ bottom: 32px;
+ background: #ffffff;
+ border: 1px solid #ddd;
+ padding: 5px;
+ font-size: 10px;
+ line-height: 17px;
+ }
+
+ .title {
+ width: 90%;
+ font-weight: bold;
+ }
+ }
+
+ .edit-button {
+ cursor: pointer;
+ height: 16px;
+ width: 16px;
+ margin-left: 2px;
+ margin-right: -2px;
+ right: 0 !important;
+ background-image: url(../../resources/images/edit-dark.png);
+ }
+
+ .edit-active {
+ height: 16px;
+ width: 16px;
+ right: 0 !important;
+ background-image: url(../../resources/images/edit-hover.png);
+ }
+}
+
+.statsgrid-classification-plugin-transparent {
+ .active-header.multi-selected:hover:after {
+ background-color: transparent;
+ border: none;
+ font-size: 12px;
+ }
+
+ .active-header.single-selected {
+ border: 1px solid #aaa;
+ color: #444;
+ background-color: #fff;;
+ border-radius: 5px;
+ text-shadow: none;
+ padding: 3px 5px;
+ font-size: 13px;
+ background: linear-gradient(#fff 20%,#f6f6f6 50%,#eee 52%,#f4f4f4);
+ box-shadow: inset 0 0 3px #fff, 0 1px 1px rgba(0,0,0,.1);
+ margin-bottom: 6px;
+ .title {
+ font-weight: 400;
+ }
+
+ }
+}
diff --git a/bundles/statistics/statsgrid2016/components/classification/legend.scss b/bundles/statistics/statsgrid2016/components/classification/legend.scss
new file mode 100644
index 0000000000..a29422250b
--- /dev/null
+++ b/bundles/statistics/statsgrid2016/components/classification/legend.scss
@@ -0,0 +1,41 @@
+.statsgrid-classification-container {
+
+ .active-legend {
+ height: 100%;
+ }
+
+ /* overrides for geostats CSS */
+ .active-legend div {
+ clear: both;
+ }
+ .geostats-legend-title {
+ display: none;
+ }
+ .legend-noactive {
+ padding: 3px 10px 5px 10px;
+ }
+ .statsgrid-svg {
+ float: left;
+ }
+ .statsgrid-svg-legend {
+ margin: 0 auto;
+ width: 90%;
+ overflow: hidden;
+ }
+ .statsgrid-counter {
+ float: left;
+ }
+ .statsgrid-range {
+ float: left;
+ }
+}
+
+.statsgrid-classification-plugin-transparent {
+ .geostats-legend-counter {
+ color: #ffffff;
+ }
+ .statsgrid-svg-legend {
+ text-shadow: none;
+ color: #000;
+ }
+}
diff --git a/bundles/statistics/statsgrid2016/components/manualClassification/ManualClassification.jsx b/bundles/statistics/statsgrid2016/components/manualClassification/ManualClassification.jsx
new file mode 100644
index 0000000000..d116969f19
--- /dev/null
+++ b/bundles/statistics/statsgrid2016/components/manualClassification/ManualClassification.jsx
@@ -0,0 +1,47 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import {withContext} from '../../../../../src/react/util.jsx';
+import {ManualClassificationView} from './View';
+
+// TODO: change to use general oskari react button and pass props.handleClick = handleManualClassification
+
+const update = (stateService, bounds) => {
+ stateService.updateActiveClassification('manualBounds', bounds);
+};
+
+const handleManualClassification = ({service, indicators}) => {
+ const {series, state, classification, color} = service.getAllServices();
+ const ind = indicators.active;
+ const view = new ManualClassificationView(classification, color, ind.classification);
+
+ if (ind.series && indicators.serieStats && indicators.serieStats.serie) {
+ view.setData(indicators.serieStats.serie);
+ } else if (indicators.data) {
+ view.setData(indicators.data);
+ } else {
+ return; // failed to get serie or data -> don't open
+ }
+ series.setAnimating(false);
+ view.openEditor(bounds => update(state, bounds));
+};
+
+const ManualClassification = props => {
+ return (
+
+ handleManualClassification(props)}/>
+
+ );
+};
+
+ManualClassification.propTypes = {
+ disabled: PropTypes.bool,
+ service: PropTypes.object,
+ indicators: PropTypes.object,
+ loc: PropTypes.func
+};
+const contextWrapped = withContext(ManualClassification);
+export {contextWrapped as ManualClassification};
diff --git a/bundles/statistics/statsgrid2016/components/manualClassification/View.js b/bundles/statistics/statsgrid2016/components/manualClassification/View.js
index a173e390d2..d073ac82ac 100644
--- a/bundles/statistics/statsgrid2016/components/manualClassification/View.js
+++ b/bundles/statistics/statsgrid2016/components/manualClassification/View.js
@@ -1,4 +1,4 @@
-import {manualClassificationEditor} from './editor';
+import { manualClassificationEditor } from './editor';
import '../../resources/scss/manualClassification.scss';
const loc = Oskari.getMsg.bind(null, 'StatsGrid');
@@ -52,7 +52,7 @@ export class ManualClassificationView {
okButton.setHandler(() => {
dialog.close();
this.manualBounds = editedBounds;
- okCallback();
+ okCallback(this.manualBounds);
});
const buttons = [dialog.createCloseButton(), okButton];
const content = jQuery('
');
diff --git a/bundles/statistics/statsgrid2016/components/manualClassification/editor.js b/bundles/statistics/statsgrid2016/components/manualClassification/editor.js
index 5eb50bc9dc..af3ae639cf 100644
--- a/bundles/statistics/statsgrid2016/components/manualClassification/editor.js
+++ b/bundles/statistics/statsgrid2016/components/manualClassification/editor.js
@@ -1,8 +1,8 @@
-import {histogram} from './histogram';
-import {edgeLines} from './edgeLines';
-import {inputGuide} from './inputGuide';
-import {updateBandBlocks} from './updateBandBlocks';
-import {updateDragHandles} from './updateDragHandles';
+import { histogram } from './histogram';
+import { edgeLines } from './edgeLines';
+import { inputGuide } from './inputGuide';
+import { updateBandBlocks } from './updateBandBlocks';
+import { updateDragHandles } from './updateDragHandles';
const width = 500;
const height = 303;
diff --git a/bundles/statistics/statsgrid2016/instance.js b/bundles/statistics/statsgrid2016/instance.js
index 4249e4ee4d..54106bb9c3 100755
--- a/bundles/statistics/statsgrid2016/instance.js
+++ b/bundles/statistics/statsgrid2016/instance.js
@@ -34,6 +34,7 @@ Oskari.clazz.define(
this.regionsetViewer = null;
this.flyoutManager = null;
+ this._layerId = 'STATS_LAYER';
}, {
afterStart: function (sandbox) {
var me = this;
@@ -151,12 +152,15 @@ Oskari.clazz.define(
hasData: function () {
return !!this.statsService.getDatasource().length;
},
+ getLayerId: function () {
+ return this._layerId;
+ },
/**
* Update visibility of classification / legend based on idicators length & stats layer visibility
*/
_updateClassficationViewVisibility: function () {
var indicatorsExist = this.statsService.getStateService().getIndicators().length > 0;
- var layer = this.getLayerService().findMapLayer('STATS_LAYER');
+ var layer = this.getLayerService().findMapLayer(this._layerId);
var layerVisible = layer ? layer.isVisible() : true;
this.createClassficationView(indicatorsExist && layerVisible);
@@ -167,7 +171,7 @@ Oskari.clazz.define(
_updateSeriesControlVisibility: function () {
const ind = this.statsService.getStateService().getActiveIndicator();
const isSeriesActive = ind && !!ind.series;
- const layer = this.getLayerService().findMapLayer('STATS_LAYER');
+ const layer = this.getLayerService().findMapLayer(this._layerId);
const layerVisible = layer ? layer.isVisible() : true;
this.setSeriesControlVisible(isSeriesActive && layerVisible);
@@ -290,7 +294,7 @@ Oskari.clazz.define(
},
AfterMapLayerRemoveEvent: function (event) {
var layer = event.getMapLayer();
- if (!layer || layer.getId() !== 'STATS_LAYER') {
+ if (!layer || layer.getId() !== this._layerId) {
return;
}
var emptyState = {};
@@ -321,7 +325,7 @@ Oskari.clazz.define(
},
MapLayerVisibilityChangedEvent: function (event) {
var layer = event.getMapLayer();
- if (!layer || layer.getId() !== 'STATS_LAYER') {
+ if (!layer || layer.getId() !== this._layerId) {
return;
}
this._updateClassficationViewVisibility();
@@ -331,6 +335,9 @@ Oskari.clazz.define(
this.statsService.notifyOskariEvent(evt);
},
AfterChangeMapLayerOpacityEvent: function (evt) {
+ if (evt.getMapLayer().getId() !== this._layerId) {
+ return;
+ }
this.statsService.notifyOskariEvent(evt);
// record opacity for published map etc
var ind = this.statsService.getStateService().getActiveIndicator();
diff --git a/bundles/statistics/statsgrid2016/plugin/ClassificationPlugin.js b/bundles/statistics/statsgrid2016/plugin/ClassificationPlugin.js
index ec0b9c5012..835ef23fdc 100755
--- a/bundles/statistics/statsgrid2016/plugin/ClassificationPlugin.js
+++ b/bundles/statistics/statsgrid2016/plugin/ClassificationPlugin.js
@@ -1,3 +1,8 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import { GenericContext } from '../../../../src/react/util.jsx';
+import { Classification } from '../components/classification/Classification';
+import '../resources/scss/classificationplugin.scss';
/**
* @class Oskari.statistics.statsgrid.ClassificationPlugin
*/
@@ -21,7 +26,7 @@ Oskari.clazz.define('Oskari.statistics.statsgrid.ClassificationPlugin',
me._name = 'ClassificationPlugin';
me.element = null;
me._templates = {
- main: jQuery('
')
+ main: jQuery('
')
};
// for publisher dragndrop to work needs to have at least:
// - mapplugin-class in parent template
@@ -34,21 +39,11 @@ Oskari.clazz.define('Oskari.statistics.statsgrid.ClassificationPlugin',
me.log = Oskari.log('Oskari.statistics.statsgrid.ClassificationPlugin');
Oskari.makeObservable(this);
- this.__legend = Oskari.clazz.create('Oskari.statistics.statsgrid.Legend', sandbox, this._locale);
- this.__legend.on('rendered', function () {
- me._calculatePluginSize();
- });
- this.__legend.on('edit-legend', function (isEdit) {
- if (isEdit) {
- me._overflowCheck(true);
- } else {
- me._restoreOverflow();
- }
- });
- this.__legend.on('content-rendered', function () {
- me._overflowCheck();
- });
+ this.service = sandbox.getService('Oskari.statistics.statsgrid.StatisticsService');
this._overflowedOffset = null;
+ this._previousIsEdit = false;
+ this._transparent = false;
+ this._bindToEvents();
}, {
_setLayerToolsEditModeImpl: function () {
if (!this.getElement()) {
@@ -68,9 +63,114 @@ Oskari.clazz.define('Oskari.statistics.statsgrid.ClassificationPlugin',
}
this.element = this._templates.main.clone();
this.element.css('z-index', 15001);
- this.__legend.render(this.element);
+ this.render();
return this.element;
},
+ rendered: function (isUpdate, isEdit) {
+ if (isUpdate) {
+ // check if edit classification state is changed
+ if (isEdit !== this._previousIsEdit) {
+ if (isEdit) {
+ this._overflowCheck(true);
+ } else {
+ this._restoreOverflow();
+ }
+ this._previousIsEdit = isEdit;
+ }
+ this._overflowCheck();
+ } else {
+ this._calculatePluginSize();
+ this._overflowCheck();
+ }
+ },
+ render: function (activeClassfication) {
+ if (!this.element) return;
+ const node = this.element.get(0);
+ const indicators = this.getIndicatorProps();
+ const classifications = this.getClassificationProps(indicators, activeClassfication);
+ const legendProps = this.getLegendProps(indicators, classifications);
+ const classification = legendProps.classification;
+ if (classification && classifications.values.count !== classification.getGroups().length) {
+ // classification count changed!!
+ this.service.getStateService().updateActiveClassification('count', classification.getGroups().length);
+ return;
+ }
+
+ ReactDOM.render((
+
+
+
+ ), node);
+ },
+ getIndicatorProps: function () {
+ const indicators = {
+ selected: [],
+ data: {}
+ };
+ const state = this.service.getStateService();
+ const active = state.getActiveIndicator();
+ indicators.active = active;
+ indicators.regionset = state.getRegionset();
+ if (active.series) {
+ indicators.serieStats = this.service.getSeriesService().getSeriesStats(active.hash);
+ }
+ this.service.getStateService().getIndicators().forEach((ind) => {
+ this.service.getUILabels(ind, label => {
+ indicators.selected.push({
+ id: ind.hash,
+ title: label.full
+ });
+ });
+ });
+ this.service.getIndicatorData(active.datasource, active.indicator, active.selections, active.series, indicators.regionset, (err, data) => {
+ if (data) {
+ indicators.data = data;
+ }
+ if (err) {
+ this.log.warn('Error getting indicator data', active, indicators.regionset);
+ }
+ });
+ return indicators;
+ },
+ getClassificationProps: function (indicators, classification) {
+ const props = {
+ countRange: []
+ };
+ const service = this.service.getClassificationService();
+ const colorsService = this.service.getColorService();
+ const values = classification || this.service.getStateService().getClassificationOpts(indicators.active.hash);
+ props.values = values;
+ props.methods = service.getAvailableMethods();
+ props.modes = service.getAvailableModes();
+ props.mapStyles = service.getAvailableMapStyles();
+ props.types = colorsService.getAvailableTypes();
+ props.validOptions = service.getAvailableOptions(indicators.data);
+ props.disabled = !this.service.getStateService().isClassificationEnabled();
+ if (values.mapStyle !== 'choropleth') {
+ props.colors = colorsService.getDefaultSimpleColors();
+ } else {
+ props.colors = colorsService.getOptionsForType(values.type, values.count, values.reverseColors);
+ }
+ const range = colorsService.getRange(values.type, values.style);
+ for (let i = range.min; i <= range.max; i++) {
+ props.countRange.push(i);
+ }
+ return props;
+ },
+ getLegendProps: function (indicators, classifications) {
+ const data = indicators.data;
+ const serieStats = indicators.serieStats;
+ const classificationOpts = classifications.values;
+ const props = {};
+ if (Object.keys(data).length !== 0) {
+ props.classification = this.service.getClassificationService().getClassification(data, classificationOpts, serieStats);
+ }
+ props.colors = this.service.getColorService().getColorsForClassification(classificationOpts, true);
+ return props;
+ },
+
redrawUI: function () {
this.teardownUI();
this._buildUI();
@@ -84,6 +184,7 @@ Oskari.clazz.define('Oskari.statistics.statsgrid.ClassificationPlugin',
var element = this.getElement();
// detach old element from screen
if (element) {
+ ReactDOM.unmountComponentAtNode(element.get(0));
this.removeFromPluginContainer(element, true);
this.element = null;
this.trigger('hide');
@@ -95,6 +196,8 @@ Oskari.clazz.define('Oskari.statistics.statsgrid.ClassificationPlugin',
this._overflowCheck();
if (this._instance.isEmbedded() && this._config.transparent) {
this.makeTransparent(true);
+ } else if (this._transparent === true) {
+ this.makeTransparent(true);
}
this.trigger('show');
},
@@ -102,25 +205,24 @@ Oskari.clazz.define('Oskari.statistics.statsgrid.ClassificationPlugin',
this.getElement().draggable();
},
makeTransparent: function (transparent) {
+ this._transparent = transparent;
var element = this.getElement();
if (!element) {
return;
}
if (transparent) {
- element.removeClass('statsgrid-legend-plugin');
- element.addClass('statsgrid-legend-plugin-transparent');
- element.find('.statsgrid-legend-container').addClass('legend-transparent');
+ element.removeClass('statsgrid-classification-plugin');
+ element.addClass('statsgrid-classification-plugin-transparent');
} else {
- element.removeClass('statsgrid-legend-plugin-transparent');
- element.addClass('statsgrid-legend-plugin');
- element.find('.statsgrid-legend-container').removeClass('legend-transparent');
+ element.removeClass('statsgrid-classification-plugin-transparent');
+ element.addClass('statsgrid-classification-plugin');
}
},
getElement: function () {
return this.element;
},
enableClassification: function (enabled) {
- this.__legend.allowClassification(enabled);
+ this.service.getStateService().enableClassification(enabled);
},
stopPlugin: function () {
this.teardownUI();
@@ -146,7 +248,6 @@ Oskari.clazz.define('Oskari.statistics.statsgrid.ClassificationPlugin',
var headerHeight = element.find('.active-header').first().height();
if (Oskari.util.isMobile()) {
element.find('.accordion').css({
- 'overflow': 'auto',
'max-height': (height * 0.8 - headerHeight) + 'px'
});
} else if (!Oskari.util.isMobile()) {
@@ -193,6 +294,24 @@ Oskari.clazz.define('Oskari.statistics.statsgrid.ClassificationPlugin',
// otherwise publisher would stop this plugin and start it again when leaving the publisher,
// resulting a misfuctioning duplicate classification element on screen.
return false;
+ },
+ _bindToEvents: function () {
+ // if indicator is removed/added - recalculate the source 1/2 etc links
+ this.service.on('StatsGrid.IndicatorEvent', event => this.render());
+
+ // Always show the active indicator - also handles "no indicator selected"
+ // if the selected indicator has no data & edit panel is open -> close it
+ this.service.on('StatsGrid.ActiveIndicatorChangedEvent', event => this.render());
+
+ // need to update the legend as data changes when regionset changes
+ this.service.on('StatsGrid.RegionsetChangedEvent', event => this.render());
+
+ this.service.on('StatsGrid.ClassificationChangedEvent', event => this.render(event.getCurrent()));
+
+ // UI styling changes e.g. disable classification editing, make transparent
+ this.service.getStateService().on('ClassificationContainerChanged', () => this.render());
+
+ this.service.on('AfterChangeMapLayerOpacityEvent', (event) => this.render());
}
}, {
'extend': ['Oskari.mapping.mapmodule.plugin.BasicMapModulePlugin'],
diff --git a/bundles/statistics/statsgrid2016/resources/scss/classificationplugin.scss b/bundles/statistics/statsgrid2016/resources/scss/classificationplugin.scss
new file mode 100644
index 0000000000..18e83fd990
--- /dev/null
+++ b/bundles/statistics/statsgrid2016/resources/scss/classificationplugin.scss
@@ -0,0 +1,4 @@
+.statsgrid-classification-plugin, .statsgrid-classification-plugin-transparent {
+ /* Float right to not overlap with other plugins */
+ float: right;
+}
diff --git a/bundles/statistics/statsgrid2016/resources/scss/manualClassification.scss b/bundles/statistics/statsgrid2016/resources/scss/manualClassification.scss
index 355a320e42..7da7885c8f 100644
--- a/bundles/statistics/statsgrid2016/resources/scss/manualClassification.scss
+++ b/bundles/statistics/statsgrid2016/resources/scss/manualClassification.scss
@@ -33,4 +33,25 @@
background-color: #faa;
}
}
-}
\ No newline at end of file
+}
+
+.classification-manual .oskari-button {
+ margin-bottom: 4px;
+ margin-top: 8px;
+ padding: 2px 5px;
+ font-size: 12px;
+}
+
+.statsgrid-classification-plugin-transparent .classification-manual{
+ .oskari-button, .oskari-button:hover:enabled {
+ background-color: transparent;
+ background: none;
+ border: none;
+ color: #ffffff;
+ text-shadow:
+ -1px -1px 0 #000,
+ 1px -1px 0 #000,
+ -1px 1px 0 #000,
+ 1px 1px 0 #000;
+ }
+}
diff --git a/bundles/statistics/statsgrid2016/resources/scss/style.scss b/bundles/statistics/statsgrid2016/resources/scss/style.scss
index 1118b50c27..e5e5bbb1c6 100755
--- a/bundles/statistics/statsgrid2016/resources/scss/style.scss
+++ b/bundles/statistics/statsgrid2016/resources/scss/style.scss
@@ -8,16 +8,6 @@ $buttonWidth: 86%;
$flyoutPadding: 16px;
-/* overrides for geostats CSS */
-.statsgrid-legend-container .active-legend div {
- clear: both;
-}
-
-.statsgrid-legend-container .geostats-legend-title {
- display: none;
-}
-
-
/* ********************************************
* These are for common code in statsgrid
* ******************************************* */
@@ -560,249 +550,6 @@ div.oskari-flyout {
display: flex;
}
-/* ********************************************
- * Statsgrid legend
- * ******************************************* */
-.statsgrid-legend-plugin {
- border: 1px solid rgba(0,0,0,0.2);
- pointer-events: auto;
- text-align: left;
- /* Float right to not overlap with other plugins */
- float: right;
-}
-.statsgrid-legend-plugin-transparent {
- color:#FFFFFF;
- text-shadow:
- -1px -1px 0 #000,
- 1px -1px 0 #000,
- -1px 1px 0 #000,
- 1px 1px 0 #000;
- pointer-events: auto;
- text-align: left;
- /* Float right to not overlap with other plugins */
- float: right; }
- .statsgrid-legend-plugin-transparent .geostats-legend-counter {
- color: #ffffff;
- }
- .statsgrid-legend-plugin-transparent .accordion .accordion_panel {
- background-color: transparent;
- }
- .statsgrid-legend-plugin-transparent .accordion .accordion_panel.open {
- background-color: transparent;
- }
- .statsgrid-legend-plugin-transparent .dropdown {
- text-shadow: none;
- color: #000;
- }
- .statsgrid-legend-plugin-transparent .statsgrid-svg-legend {
- text-shadow: none;
- color: #000;
- }
-.active-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 5px;
- position: relative;
-
- &.multi-select-legend:hover:after {
- content: attr(data-selected-indicator);
- display: block;
- position: absolute;
- bottom: 32px;
- background: #ffffff;
- border: 1px solid #ddd;
- padding: 5px;
- font-size: 10px;
- line-height: 17px;
- }
-}
-.active-header > .title {
- width: 90%;
- font-weight: bold;
-}
-.active-legend {
- height:100%;
-}
-
-.edit-legend {
- cursor: pointer;
- height: 16px;
- width: 16px;
- margin-left: 2px;
- margin-right: -2px;
- right: 0 !important;
- background-image: url(../images/edit-dark.png);
-}
-.edit-active {
- height: 16px;
- width: 16px;
- right: 0 !important;
- background-image: url(../images/edit-hover.png);
-}
-.statsgrid-legend-container {
- background-color: #FFFFFF;
- width: 300px;
- font-size: 12px;
-
- .classification-colors.value {
- display: inline-block;
- vertical-align: top;
- }
-
- .visible-map-style-choropleth.flip-colors {
- margin-left: 10px;
- line-height: 27px;
- }
-
- >div.header {
- background-color: #fdf8d9;
- margin: 0;
- height:auto;
- padding: 6px;
- width: auto;
- font-weight: bold;
-
- div.title {
- font-size: 14px;
- }
- div.link {
- cursor:pointer;
- color:#4292c5;
- float: right;
- text-decoration: underline;
- }
- div.sourcename {
- font-weight: normal;
- }
- }
-
- .legend-noactive {
- padding: 3px 10px 5px 10px;
- }
-
- div.accordion {
- div.content {
- padding: 0;
- margin: 0;
- margin-left: 8px;
- }
-
-
- div.geostats-legend div {
- margin: 2px 6px 2px 2px;
- }
- }
-
- > .classification .accordion .accordion_panel {
- border: 0px;
- overflow: visible;
-
- .header .headerIcon {
- margin: 0;
- margin-left: 4px;
- }
-
- .header .headerText {
- font-size: 12px;
- padding: 0;
- font-weight: normal;
- }
-
- .content .classifications {
- .classification-options {
- .numeric-value {
- .oskari-formcomponent {
- margin-bottom: 4px;
- margin-top: 8px;
- }
- }
- }
- .label {
- font-weight: bold;
- }
-
- select {
- width: 96%;
- font-size: 12px;
- padding: 1px 2px;
- }
-
- .classification-manual input {
- margin-bottom: 4px;
- margin-top: 8px;
- padding: 2px 5px;
- font-size: 12px;
- }
-
- .point-size {
- .minmaxlabels {
- .min {
- float:left;
- }
- .max {
- float:right;
- margin-right: 4%;
- }
- .clear {
- clear:both;
- }
- }
- .point-range {
- width: 90%;
- background-image: url(../images/opacity_slider_long.png);
- background-repeat: no-repeat;
- }
- }
- }
-
- .oskari-color-selection {
- z-index: 10;
- }
- }
-
- .statsgrid-svg {
- float: left;
- }
- .statsgrid-svg-legend {
- margin: 0 auto;
- width: 90%;
- overflow: hidden;
- }
- .statsgrid-range {
- float: left;
- }
- .statsgrid-counter {
- float: left;
- }
- .clear {
- clear: both;
- }
-}
-.legend-transparent {
- background-color: transparent;
-}
-/* Mobile popup tuning */
-.divmanazerpopup.statsgrid-mobile-legend {
- .popup-body {
- >div.content {
- margin: 0px;
- }
-
- div.accordion {
- div.content {
- padding: 0;
- margin: 0;
- margin-left: 8px;
- }
- }
-
- div.geostats-legend div {
- margin: 2px 6px 2px 2px;
- }
- }
-}
-
/** Toggle buttons */
div.mapplugins .mappluginsContainer .mappluginsContent .mapplugin.statsgrid-published-toggle-buttons {
border: 1px solid rgba(0, 0, 0, 0.2);
@@ -882,13 +629,6 @@ div.mapplugins .mappluginsContainer .mappluginsContent .mapplugin.statsgrid-publ
}
-.statsgrid-legend-flyout {
- .oskari-flyoutcontentcontainer{
- overflow-x:hidden;
- overflow-y: auto;
- }
-}
-
/* ********************************************
* Statsgrid tile styles
diff --git a/bundles/statistics/statsgrid2016/service/ClassificationService.js b/bundles/statistics/statsgrid2016/service/ClassificationService.js
index 23700e2379..16171b1671 100755
--- a/bundles/statistics/statsgrid2016/service/ClassificationService.js
+++ b/bundles/statistics/statsgrid2016/service/ClassificationService.js
@@ -1,4 +1,4 @@
-import {equalSizeBands} from '../util/equalSizeBands';
+import { equalSizeBands } from '../util/equalSizeBands';
import geostats from 'geostats/lib/geostats.min.js';
import 'geostats/lib/geostats.css';
@@ -35,7 +35,9 @@ Oskari.clazz.define('Oskari.statistics.statsgrid.ClassificationService',
// values recognized by the code (and geostats)
method: ['jenks', 'quantile', 'equal', 'manual'],
// values recognized by the code (and geostats)
- mode: ['distinct', 'discontinuous']
+ mode: ['distinct', 'discontinuous'],
+ // values recognized by the code (and geostats)
+ mapStyle: ['choropleth', 'points']
},
getAvailableMethods: function () {
return this.limits.method.slice(0);
@@ -43,6 +45,9 @@ Oskari.clazz.define('Oskari.statistics.statsgrid.ClassificationService',
getAvailableModes: function () {
return this.limits.mode.slice(0);
},
+ getAvailableMapStyles: function () {
+ return this.limits.mapStyle.slice(0);
+ },
getAvailableOptions: function (data) {
var validOpts = {};
var list = Array.isArray(data) ? data : this._getDataAsList(data);
@@ -224,9 +229,9 @@ Oskari.clazz.define('Oskari.statistics.statsgrid.ClassificationService',
formatter
);
}
-
// Choropleth legend
stats.setColors(colors);
+
return stats.getHtmlLegend(null, title || '', true, formatter.format, opts.mode);
};
this.lastUsedBounds = response.bounds;
@@ -398,7 +403,7 @@ Oskari.clazz.define('Oskari.statistics.statsgrid.ClassificationService',
var circle = point.find('circle');
circle.attr({
- 'fill': '#' + color
+ 'fill': color
});
svg.find('svg.symbols').prepend(point.html());
diff --git a/bundles/statistics/statsgrid2016/service/ColorService.js b/bundles/statistics/statsgrid2016/service/ColorService.js
index 1ccf9b5d2f..abf4aad67e 100755
--- a/bundles/statistics/statsgrid2016/service/ColorService.js
+++ b/bundles/statistics/statsgrid2016/service/ColorService.js
@@ -63,28 +63,24 @@ Oskari.clazz.define('Oskari.statistics.statsgrid.ColorService',
* @return {Object[]} array of colors to use for legend and map
*/
getColorsForClassification: function (classification, includeHash) {
- var colors = [];
- var set = null;
if (classification.mapStyle !== 'points') {
- set = this.getColorset(classification.count, classification.type, classification.name);
- set.forEach(function (color) {
- if (includeHash) {
- color = '#' + color;
- }
- colors.push(color);
- });
- } else {
- var colorIndex = 0;
- if (classification.name) {
- colorIndex = !isNaN(classification.name) ? parseFloat(classification.name) : 0;
+ let set = this.getColorset(classification.count, classification.type, classification.name);
+ if (classification.reverseColors) {
+ set.reverse();
+ }
+ if (includeHash) {
+ return set.map(color => '#' + color);
}
- colors = Array.apply(null, Array(this._basicColors.length)).map(String.prototype.valueOf, this._basicColors[colorIndex]);
+ return set;
}
-
- if (classification.mapStyle !== 'points' && classification.reverseColors) {
- colors.reverse();
+ let color = this._basicColors[0];
+ if (classification.name) {
+ const colorIndex = !isNaN(classification.name) ? parseFloat(classification.name) : 0;
+ if (colorIndex < this._basicColors.length) {
+ color = this._basicColors[colorIndex];
+ }
}
- return colors;
+ return Array.apply(null, Array(this._basicColors.length)).map(String.prototype.valueOf, (includeHash ? '#' + color : color));
},
/**
* Tries to return an array of colors where length equals count parameter.
diff --git a/bundles/statistics/statsgrid2016/service/StateService.js b/bundles/statistics/statsgrid2016/service/StateService.js
index 26921ec01b..87ad221688 100755
--- a/bundles/statistics/statsgrid2016/service/StateService.js
+++ b/bundles/statistics/statsgrid2016/service/StateService.js
@@ -23,10 +23,18 @@ Oskari.clazz.define('Oskari.statistics.statsgrid.StateService',
name: 'Blues',
type: 'seq',
mode: 'discontinuous',
- reverseColors: false
+ reverseColors: false,
+ mapStyle: 'choropleth',
+ transparency: 100, // or from statslayer
+ min: 10,
+ max: 60,
+ showValues: false,
+ fractionDigits: 0
}
};
this._timers = {};
+ this.classificationEnabled = true;
+ Oskari.makeObservable(this);
}, {
__name: 'StatsGrid.StateService',
__qname: 'Oskari.statistics.statsgrid.StateService',
@@ -37,6 +45,13 @@ Oskari.clazz.define('Oskari.statistics.statsgrid.StateService',
getName: function () {
return this.__name;
},
+ isClassificationEnabled: function () {
+ return this.classificationEnabled;
+ },
+ enableClassification: function (enable) {
+ this.classificationEnabled = !!enable;
+ this.trigger('ClassificationContainerChanged');
+ },
/**
* Resets the current state and sends events about the changes.
* Removes all selected indicators, selected region and regionset is set to undefined
@@ -134,6 +149,28 @@ Oskari.clazz.define('Oskari.statistics.statsgrid.StateService',
}
}
},
+ updateActiveClassification: function (key, value) {
+ const indicator = this.getActiveIndicator();
+ if (indicator) {
+ indicator.classification[key] = value;
+ var eventBuilder = Oskari.eventBuilder('StatsGrid.ClassificationChangedEvent');
+ if (eventBuilder) {
+ this.sandbox.notifyAll(eventBuilder(indicator.classification));
+ }
+ }
+ },
+ updateActiveClassificationObj: function (valueObj) {
+ const indicator = this.getActiveIndicator();
+ if (indicator) {
+ Object.keys(valueObj).forEach(key => {
+ indicator.classification[key] = valueObj[key];
+ });
+ const eventBuilder = Oskari.eventBuilder('StatsGrid.ClassificationChangedEvent');
+ if (eventBuilder) {
+ this.sandbox.notifyAll(eventBuilder(indicator.classification));
+ }
+ }
+ },
/**
* Gets getClassificationOpts
* @param {String} indicatorHash indicator hash
@@ -281,7 +318,7 @@ Oskari.clazz.define('Oskari.statistics.statsgrid.StateService',
ind.selections[series.id] = this.seriesService.getValue();
// Discontinuos mode is problematic for series data,
// because each class has to get at least one hit -> set distinct mode.
- ind.classification = jQuery.extend({}, ind.classification || {}, {mode: 'distinct'});
+ ind.classification = jQuery.extend({}, ind.classification || {}, { mode: 'distinct' });
}
this.indicators.push(ind);
diff --git a/bundles/statistics/statsgrid2016/service/StatisticsService.js b/bundles/statistics/statsgrid2016/service/StatisticsService.js
index 60953f26c9..2655e6a3f1 100755
--- a/bundles/statistics/statsgrid2016/service/StatisticsService.js
+++ b/bundles/statistics/statsgrid2016/service/StatisticsService.js
@@ -82,6 +82,15 @@
getErrorService: function () {
return this.error;
},
+ getAllServices: function () {
+ return {
+ series: this.series,
+ state: this.state,
+ classification: this.classification,
+ color: this.colors,
+ error: this.error
+ };
+ },
addDatasource: function (ds) {
if (!ds) {
// log error message
diff --git a/bundles/statistics/statsgrid2016/view/DiagramFlyout.js b/bundles/statistics/statsgrid2016/view/DiagramFlyout.js
index 01691b50a4..1f6930fce7 100755
--- a/bundles/statistics/statsgrid2016/view/DiagramFlyout.js
+++ b/bundles/statistics/statsgrid2016/view/DiagramFlyout.js
@@ -60,7 +60,7 @@ Oskari.clazz.define('Oskari.statistics.statsgrid.view.DiagramFlyout', function (
this._diagram.createDataSortOption(el.find('.chart-controls .dropdown'));
// this.loc.datacharts.descColor
// Oskari.clazz.define('Oskari.statistics.statsgrid.SelectedIndicatorsMenu');
- var options = {resizable: this.isResizable()};
+ var options = { resizable: this.isResizable() };
this._diagram.render(el.find('.chart'), options);
this.setElement(el);
}
diff --git a/bundles/statistics/statsgrid2016/view/SearchFlyout.js b/bundles/statistics/statsgrid2016/view/SearchFlyout.js
index 16623f9421..29789ab74b 100755
--- a/bundles/statistics/statsgrid2016/view/SearchFlyout.js
+++ b/bundles/statistics/statsgrid2016/view/SearchFlyout.js
@@ -161,8 +161,8 @@ Oskari.clazz.define('Oskari.statistics.statsgrid.view.SearchFlyout', function (t
// Overrides selection key and value from provided search values.
const getSearchWithModifiedParam = (values, paramKey, paramValue) => {
- const modSelection = {...values.selection, [paramKey]: paramValue};
- return {...values, selections: modSelection};
+ const modSelection = { ...values.selection, [paramKey]: paramValue };
+ return { ...values, selections: modSelection };
};
let metadataCounter = 0;
@@ -181,14 +181,14 @@ Oskari.clazz.define('Oskari.statistics.statsgrid.view.SearchFlyout', function (t
}
// Overrides indicator array to make this search indicator specific.
const addSearchValues = values => {
- refinedSearchValues.push({...values, indicator});
+ refinedSearchValues.push({ ...values, indicator });
};
// Get indicator metadata to check the search valididty
this.service.getIndicatorMetadata(commonSearchValues.datasource, indicator, (err, metadata) => {
// Map possible errors by indicator name
const indicatorName = metadata && metadata.name ? Oskari.getLocalized(metadata.name) : indicator;
if (err || !metadata) {
- errorMap.set(indicatorName, {metadataNotFound: true});
+ errorMap.set(indicatorName, { metadataNotFound: true });
checkDone();
return;
}
@@ -246,10 +246,10 @@ Oskari.clazz.define('Oskari.statistics.statsgrid.view.SearchFlyout', function (t
_getRefinedSearch (metadata, commonSearchValues) {
// Make a deep clone of search values
var indSearchValues = jQuery.extend(true, {}, commonSearchValues);
- const {regionset, selections, series} = indSearchValues;
+ const { regionset, selections, series } = indSearchValues;
if (Array.isArray(metadata.regionsets) && !metadata.regionsets.includes(Number(regionset))) {
- indSearchValues.error = {notAllowed: 'regionset'};
+ indSearchValues.error = { notAllowed: 'regionset' };
return indSearchValues;
}
if (!selections) {
@@ -268,7 +268,7 @@ Oskari.clazz.define('Oskari.statistics.statsgrid.view.SearchFlyout', function (t
if (!Array.isArray(value)) {
// Single option
if (!selector.allowedValues.includes(value)) {
- indSearchValues.error = {notAllowed: selectionKey};
+ indSearchValues.error = { notAllowed: selectionKey };
}
return;
}
@@ -278,7 +278,7 @@ Oskari.clazz.define('Oskari.statistics.statsgrid.view.SearchFlyout', function (t
!selector.allowedValues.includes(cur) && !selector.allowedValues.find(obj => obj.id === cur));
// Set multiselect status for search
- indSearchValues.multiselectStatus = {selector: selectionKey, invalid: notAllowed, requested: [...value]};
+ indSearchValues.multiselectStatus = { selector: selectionKey, invalid: notAllowed, requested: [...value] };
if (notAllowed.length === 0) {
// Selected values are valid
@@ -287,7 +287,7 @@ Oskari.clazz.define('Oskari.statistics.statsgrid.view.SearchFlyout', function (t
if (notAllowed.length === value.length) {
// All selected values are out of range
delete selections[selectionKey];
- indSearchValues.error = {notAllowed: selectionKey};
+ indSearchValues.error = { notAllowed: selectionKey };
return;
}
// Filter out unsupported search param values
@@ -370,7 +370,7 @@ Oskari.clazz.define('Oskari.statistics.statsgrid.view.SearchFlyout', function (t
const consumeBatch = batch => {
const dataPromises = [];
batch.forEach((search, index) => {
- const {datasource, indicator, selections, series, regionset} = search;
+ const { datasource, indicator, selections, series, regionset } = search;
const promise = new Promise((resolve, reject) => {
this.service.getIndicatorData(datasource, indicator, selections, series, regionset, (err, data) => {
if (err || !data) {
@@ -401,7 +401,7 @@ Oskari.clazz.define('Oskari.statistics.statsgrid.view.SearchFlyout', function (t
return;
}
if (!indicatorsHavingData.has(failedSearch.indicator)) {
- errors.set(failedSearch.indicatorName, {datasetEmpty: true});
+ errors.set(failedSearch.indicatorName, { datasetEmpty: true });
return;
}
const multiselectStatus = multiselectStatusMap.get(failedSearch.indicatorName);
@@ -437,7 +437,7 @@ Oskari.clazz.define('Oskari.statistics.statsgrid.view.SearchFlyout', function (t
if (indicatorMessages.length > 0) {
const dialog = Oskari.clazz.create('Oskari.userinterface.component.Popup');
const okBtn = dialog.createCloseButton('OK');
- const title = this.loc('errors.noDataForIndicators', {indicators: indicatorMessages.length});
+ const title = this.loc('errors.noDataForIndicators', { indicators: indicatorMessages.length });
dialog.show(title, indicatorMessages.join('
'), [okBtn]);
}
},
@@ -492,7 +492,7 @@ Oskari.clazz.define('Oskari.statistics.statsgrid.view.SearchFlyout', function (t
_addIndicators: function (searchValues) {
let latestNewSearch = null;
searchValues.forEach(values => {
- const {datasource, indicator, selections, series} = values;
+ const { datasource, indicator, selections, series } = values;
if (this.service.getStateService().addIndicator(datasource, indicator, selections, series)) {
// Indicator was not already present at the service
latestNewSearch = values;
@@ -500,7 +500,7 @@ Oskari.clazz.define('Oskari.statistics.statsgrid.view.SearchFlyout', function (t
});
if (latestNewSearch) {
// Search added some new indicators, let's set the last one as the active indicator.
- const {datasource, indicator, selections, series} = latestNewSearch;
+ const { datasource, indicator, selections, series } = latestNewSearch;
const hash = this.service.getStateService().getHash(datasource, indicator, selections, series);
this.service.getStateService().setActiveIndicator(hash);
}
diff --git a/package.json b/package.json
index 81d2f70241..9168b17ff2 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "oskari-frontend",
- "version": "0.0.1",
+ "version": "1.52.0-dev",
"description": "Build tools for Oskari core",
"homepage": "http://oskari.org",
"keywords": [
@@ -9,6 +9,7 @@
"main": "grunt.js",
"scripts": {
"test": "eslint . --quiet",
+ "lintfix": "eslint . --quiet --fix",
"build": "webpack --progress --mode production --env.appdef=devapp:applications/sample",
"start": "webpack-dev-server --hot --env.appdef=devapp:applications/sample",
"sprite": "node webpack/sprite.js",
@@ -23,51 +24,51 @@
},
"license": "MIT",
"dependencies": {
- "antd": "3.15.0",
- "jsts": "2.0.0",
- "ol": "5.3.0",
- "ol-mapbox-style": "3.3.0",
- "react": "16.7.0",
- "react-dom": "16.7.0",
- "styled-components": "^4.1.3"
+ "antd": "3.15.2",
+ "jsts": "2.0.3",
+ "ol": "5.3.1",
+ "ol-mapbox-style": "4.2.1",
+ "react": "16.8.5",
+ "react-dom": "16.8.5",
+ "lodash": "4.17.11",
+ "styled-components": "4.1.3"
},
"devDependencies": {
- "@babel/core": "7.1.2",
- "@babel/polyfill": "7.0.0",
- "@babel/preset-env": "7.1.0",
+ "@babel/core": "7.4.0",
+ "@babel/polyfill": "7.4.0",
+ "@babel/preset-env": "7.4.2",
"@babel/preset-react": "7.0.0",
- "@storybook/react": "^5.0.1",
- "babel-loader": "8.0.4",
- "babel-plugin-styled-components": "^1.10.0",
+ "@storybook/react": "5.0.3",
+ "babel-loader": "8.0.5",
+ "babel-plugin-styled-components": "1.10.0",
"babel-plugin-transform-remove-strict-mode": "0.0.2",
- "copy-webpack-plugin": "4.5.2",
- "css-loader": "0.28.11",
- "eslint": "4.19.1",
- "eslint-config-standard": "10.2.1",
- "eslint-plugin-import": "2.14.0",
- "eslint-plugin-node": "5.2.1",
- "eslint-plugin-promise": "3.8.0",
+ "copy-webpack-plugin": "5.0.2",
+ "css-loader": "2.1.1",
+ "eslint": "5.15.3",
+ "eslint-config-standard": "12.0.0",
+ "eslint-plugin-import": "2.16.0",
+ "eslint-plugin-node": "8.0.1",
+ "eslint-plugin-promise": "4.0.1",
"eslint-plugin-react": "7.12.4",
- "eslint-plugin-standard": "3.1.0",
+ "eslint-plugin-standard": "4.0.0",
"expose-loader": "0.7.5",
- "file-loader": "1.1.11",
- "fs-extra": "0.26.7",
+ "file-loader": "3.0.1",
+ "fs-extra": "7.0.1",
"geostats": "1.7.0",
- "gm": "1.21.1",
+ "gm": "1.23.1",
"imports-loader": "0.8.0",
- "loader-utils": "1.1.0",
- "lodash": "4.12.0",
- "merge": "1.2.0",
- "mini-css-extract-plugin": "0.4.5",
- "node-sass": "4.9.0",
- "sass-loader": "7.0.3",
+ "loader-utils": "1.2.3",
+ "merge": "1.2.1",
+ "mini-css-extract-plugin": "0.5.0",
+ "node-sass": "4.11.0",
+ "sass-loader": "7.1.0",
"script-loader": "0.7.2",
- "style-loader": "0.21.0",
+ "style-loader": "0.23.1",
"sumoselect": "3.0.5",
- "uglifyjs-webpack-plugin": "1.2.7",
- "webpack": "4.25.1",
- "webpack-cli": "3.1.2",
- "webpack-dev-server": "3.1.10"
+ "uglifyjs-webpack-plugin": "2.1.2",
+ "webpack": "4.29.6",
+ "webpack-cli": "3.3.0",
+ "webpack-dev-server": "3.2.1"
},
"private": true
}
diff --git a/packages/framework/bundle/divmanazer/bundle.js b/packages/framework/bundle/divmanazer/bundle.js
index ed32e4efa8..4faa06552c 100755
--- a/packages/framework/bundle/divmanazer/bundle.js
+++ b/packages/framework/bundle/divmanazer/bundle.js
@@ -160,9 +160,6 @@ Oskari.clazz.define("Oskari.userinterface.bundle.ui.UserInterfaceBundle", functi
}, {
"type": "text/javascript",
"src": "../../../../bundles/framework/divmanazer/component/Chart.js"
- }, {
- "type": "text/javascript",
- "src": "../../../../bundles/framework/divmanazer/component/ColorSelect.js"
}, {
"type": "text/javascript",
"src": "../../../../bundles/framework/divmanazer/component/LanguageSelect.js"
@@ -317,9 +314,6 @@ Oskari.clazz.define("Oskari.userinterface.bundle.ui.UserInterfaceBundle", functi
}, {
"type": "text/css",
"src": "../../../../bundles/framework/divmanazer/resources/scss/popover.scss"
- }, {
- "type": "text/css",
- "src": "../../../../bundles/framework/divmanazer/resources/scss/colorselect.scss"
}, {
"type": "text/javascript",
"src": "../../../../libraries/jquery/plugins/jquery-placeholder/jquery.placeholder.js"
diff --git a/packages/statistics/statsgrid/bundle.js b/packages/statistics/statsgrid/bundle.js
index 18e1a71de4..16a89ce1d3 100755
--- a/packages/statistics/statsgrid/bundle.js
+++ b/packages/statistics/statsgrid/bundle.js
@@ -109,18 +109,12 @@ Oskari.clazz.define("Oskari.statistics.statsgrid.StatsGridBundle",
}, {
"type": "text/javascript",
"src": "../../../bundles/statistics/statsgrid2016/plugin/TogglePlugin.js"
- }, {
- "type": "text/javascript",
- "src": "../../../bundles/statistics/statsgrid2016/components/Legend.js"
}, {
"type": "text/javascript",
"src": "../../../bundles/statistics/statsgrid2016/components/SeriesControl.js"
}, {
"type": "text/javascript",
"src": "../../../bundles/statistics/statsgrid2016/publisher/SeriesToggleTool.js"
- }, {
- "type": "text/javascript",
- "src": "../../../bundles/statistics/statsgrid2016/components/EditClassification.js"
}, {
"type": "text/javascript",
"src": "../../../bundles/statistics/statsgrid2016/components/RegionsetViewer.js"
diff --git a/webpack.config.js b/webpack.config.js
index 27f269d8fc..4f1524285b 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -10,13 +10,13 @@ const proxyPort = 8081;
module.exports = (env, argv) => {
const isProd = argv.mode === 'production';
- const {version, pathParam, publicPathPrefix} = parseParams(env);
+ const { version, pathParam, publicPathPrefix } = parseParams(env);
const isDirectory = source => lstatSync(source).isDirectory();
const getDirectories = source => readdirSync(source).map(name => path.join(source, name)).filter(isDirectory);
const appsetupPaths = getDirectories(path.resolve(pathParam));
- const {entries, plugins} = generateEntries(appsetupPaths, isProd, __dirname);
+ const { entries, plugins } = generateEntries(appsetupPaths, isProd, __dirname);
plugins.push(new MiniCssExtractPlugin({
filename: '[name]/oskari.min.css'
}));
@@ -71,14 +71,18 @@ module.exports = (env, argv) => {
test: /\.css$/,
use: [
styleLoaderImpl,
- { loader: 'css-loader', options: { minimize: true } }
+ { loader: 'css-loader', options: { } }
+ // https://github.com/webpack-contrib/css-loader/issues/863
+ // { loader: 'css-loader', options: { minimize: true } }
]
},
{
test: /\.scss$/,
use: [
styleLoaderImpl,
- { loader: 'css-loader', options: { minimize: true } },
+ // https://github.com/webpack-contrib/css-loader/issues/863
+ // { loader: 'css-loader', options: { minimize: true } },
+ { loader: 'css-loader', options: { } },
'sass-loader' // compiles Sass to CSS
]
},
diff --git a/webpack/generateEntries.js b/webpack/generateEntries.js
index b37913c829..a76b5c13a0 100644
--- a/webpack/generateEntries.js
+++ b/webpack/generateEntries.js
@@ -47,5 +47,5 @@ module.exports = function generateEntries (appsetupPaths, isProd, context) {
plugins.push(new LocalizationPlugin(appName));
});
- return {entries, plugins};
+ return { entries, plugins };
};
diff --git a/webpack/oskariLazyLoader.js b/webpack/oskariLazyLoader.js
index 4672d6087a..2f2c4d86fe 100644
--- a/webpack/oskariLazyLoader.js
+++ b/webpack/oskariLazyLoader.js
@@ -1,4 +1,4 @@
-const {stringifyRequest} = require('loader-utils');
+const { stringifyRequest } = require('loader-utils');
module.exports = function (source) {
if (!this.query) {