Skip to content

Commit

Permalink
Merge pull request #1082 from OpenGeoscience/grid-feature
Browse files Browse the repository at this point in the history
feat: Add a grid feature
  • Loading branch information
manthey authored Aug 13, 2021
2 parents 84b773e + df6c8dc commit b5a6d9a
Show file tree
Hide file tree
Showing 21 changed files with 1,054 additions and 331 deletions.
2 changes: 1 addition & 1 deletion scripts/datastore.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ var tar = require('tar');

var registry = {
'AdderallCities2015.csv': 'c3e984482cc6db1193a6dca2a55396a2baad8541a5c8c679f33750b76f766f40a119ec3e63abbabcd095d752c3df8ce21bf24cbe629502121f24ba90b4b0674c',
'base-images.tgz': '346dcdcf6e88aa9bfde684a311a452431af01b055f007e73839d42aa19ce0846af20d0bc296f7e1acca0af2759896d647dbbdbf07b20428f0e10464a1764c77e',
'base-images.tgz': '9eed90578ac35e96ba701198e2518cb03bf08447d649375cbf7729524139a9951a5b1b580fc03371532dc128a530794e061a8e05b69aa5b5869efbf98245670e',
'blue.jpg': '867b1f3c568289efc7d0dba97d827a2bc4d83a7465cebcb3b5aec7bac6a38cf70d037d1814402bc97ad1f2f6737cfb5ce97db0a4fb53a716e77fd3ba57a7ab3b',
'cities.csv': '5a665e5feda24f28e5cf4ed0801b67e73bbcf3ea781b2e50d11284214e67b25b68e6a1c48da46e5e4d4d0c54c2ec18f88d292224b4541fb279396cf7b94beac9',
'earthquakes.json': 'f098b6437411384b552419b4a36264c1bb3fed816ccfe9545145175e0b92a0b7ad5ebdcb9dddd0a12a90499143ffa471c02f6e049be5b973db607ff066892500',
Expand Down
2 changes: 1 addition & 1 deletion scripts/make_thumbnails.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@


def process_item(path, opts):
output = (open('/tmp/thumbnail.out', 'ab')
output = (open('/tmp/thumbnail.out', 'a')
if opts.get('verbose', 0) >= 1 else open(os.devnull, 'w'))
data = json.load(open(path))
if data.get('disabled') and not opts.get('all'):
Expand Down
101 changes: 7 additions & 94 deletions src/contourFeature.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ var meshFeature = require('./meshFeature');
* Contour feature specification.
*
* @typedef {geo.feature.spec} geo.contourFeature.spec
* @extends geo.feature.spec
* @property {object[]} [data=[]] An array of arbitrary objects used to
* construct the feature.
* @property {geo.contourFeature.styleSpec} [style] An object that contains
Expand Down Expand Up @@ -71,23 +72,8 @@ var meshFeature = require('./meshFeature');
/**
* Computed contour information.
*
* @typedef {geo.meshFeature.meshInfo} geo.contourFeature.contourInfo
* @property {number[]} value An array of values that have been normalized to a
* range of [0, steps]. There is one value per vertex.
* @property {number[]} opacity An array of opacities per vertex.
* @property {number} minValue the minimum value used for the contour. If
* `rangeValues` was specified, this is the first entry of that array.
* @property {number} maxValue the maximum value used for the contour. If
* `rangeValues` was specified, this is the last entry of that array.
* @property {number} factor If linear value scaling is used, this is the
* number of color values divided by the difference between the maximum and
* minimum values. It is ignored if non-linear value scaling is used.
* @property {geo.geoColorObject} minColor The color used for values below
* minValue. Includes opacity.
* @property {geo.geoColorObject} maxColor The color used for values above
* maxValue. Includes opacity.
* @property {geo.geoColorObject[]} colorMap The specified `colorRange` and
* `opacityRange` converted into objects that include opacity.
* @typedef {geo.meshFeature.meshColoredInfo} geo.contourFeature.contourInfo
* @extends geo.meshFeature.meshColoredInfo
*/

/**
Expand All @@ -110,6 +96,7 @@ var contourFeature = function (arg) {

var $ = require('jquery');
var util = require('./util');
var meshUtil = require('./util/mesh');

arg = arg || {};
meshFeature.call(this, arg);
Expand All @@ -129,82 +116,7 @@ var contourFeature = function (arg) {
* information.
*/
this._createContours = function () {
var contour = m_this.contour,
valueFunc = m_this.style.get('value'),
usedFunc = m_this.style('used') !== undefined ?
m_this.style.get('used') :
function (d, i) { return util.isNonNullFinite(valueFunc(d, i)); },
minmax, val, range, i, k;
var result = m_this._createMesh({
used: usedFunc,
opacity: m_this.style.get('opacity'),
value: valueFunc
});
if (!result.numVertices || !result.numElements) {
return result;
}
var stepped = contour.get('stepped')(result),
opacityRange = contour.get('opacityRange')(result),
rangeValues = contour.get('rangeValues')(result);
result.stepped = stepped === undefined || stepped ? true : false;
/* Create the min/max colors and the color array */
result.colorMap = [];
result.minColor = $.extend(
{a: contour.get('minOpacity')(result) || 0},
util.convertColor(contour.get('minColor')(result)));
result.maxColor = $.extend(
{a: contour.get('maxOpacity')(result) || 0},
util.convertColor(contour.get('maxColor')(result)));
contour.get('colorRange')(result).forEach(function (clr, idx) {
result.colorMap.push($.extend({
a: opacityRange && opacityRange[idx] !== undefined ? opacityRange[idx] : 1
}, util.convertColor(clr)));
});
/* Get min and max values */
minmax = util.getMinMaxValues(result.value, contour.get('min')(result), contour.get('max')(result));
result.minValue = minmax.min;
result.maxValue = minmax.max;
if (!rangeValues || !result.colorMap ||
(rangeValues.length !== result.colorMap.length + 1 && (
stepped || rangeValues.length !== result.colorMap.length))) {
rangeValues = null;
}
if (rangeValues) { /* ensure increasing monotonicity */
for (k = 1; k < rangeValues.length; k += 1) {
if (rangeValues[k - 1] > rangeValues[k]) {
rangeValues = null;
break;
}
}
}
if (rangeValues) {
result.minValue = rangeValues[0];
result.maxValue = rangeValues[rangeValues.length - 1];
}
range = result.maxValue - result.minValue;
if (!range) {
result.colorMap = result.colorMap.slice(0, 1);
range = 1;
rangeValues = null;
}
result.rangeValues = rangeValues;
result.factor = result.colorMap.length / range;
/* Scale values */
for (i = 0; i < result.numVertices; i += 1) {
val = result.value[i];
if (rangeValues && val >= result.minValue && val <= result.maxValue) {
for (k = 1; k < rangeValues.length; k += 1) {
if (val <= rangeValues[k]) {
result.value[i] = k - 1 + (val - rangeValues[k - 1]) /
(rangeValues[k] - rangeValues[k - 1]);
break;
}
}
} else {
result.value[i] = (val - result.minValue) * result.factor;
}
}
return result;
return meshUtil.createColoredMesh(m_this, false);
};

this.contour = m_this.mesh;
Expand All @@ -222,8 +134,9 @@ var contourFeature = function (arg) {
{
opacity: 1.0,
value: function (d, i) {
return m_this.position()(d, i).z;
return util.isNonNullFinite(d) ? d : m_this.position()(d, i).z;
},
position: (d) => d || {x: 0, y: 0},
origin: (p) => (p.length >= 3 ? [p[0], p[1], 0] : [0, 0, 0])
},
arg.style === undefined ? {} : arg.style
Expand Down
175 changes: 175 additions & 0 deletions src/gridFeature.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
var inherit = require('./inherit');
var meshFeature = require('./meshFeature');

/**
* Grid feature specification.
*
* @typedef {geo.feature.spec} geo.gridFeature.spec
* @extends geo.feature.spec
* @property {object[]} [data=[]] An array of arbitrary objects used to
* construct the feature.
* @property {geo.gridFeature.styleSpec} [style] An object that contains
* style values for the feature.
* @property {geo.gridFeature.gridSpec} [grid] The grid specification for the
* feature.
*/

/**
* Style specification for a grid feature.
*
* @typedef {geo.feature.styleSpec} geo.gridFeature.styleSpec
* @extends geo.feature.styleSpec
* @property {geo.geoPosition|function} [position=data] The position of each
* data element. This defaults to just using `x`, `y`, and `z` properties
* of the data element itself. The position is in the feature's gcs
* coordinates.
* @property {number|function} [value=data.z] The value of each data element.
* This defaults to the `z` property of the data elements. If the value of
* a grid point is `null` or `undefined`, the point and elements that use
* that point won't be included in the results.
* @property {number|function} [opacity=1] The opacity for the whole feature on
* a scale of 0 to 1.
* @property {number[]|function} [origin] Origin in map gcs coordinates used
* for to ensure high precision drawing in this location. When called as a
* function, this is passed the vertex positions as a single continuous array
* in map gcs coordinates. It defaults to the first vertex used in the
* grid.
*/

/**
* Grid specification. All of these properties can be functions, which get
* passed the {@link geo.meshFeature.meshInfo} object.
*
* @typedef {geo.meshFeature.meshSpec} geo.gridFeature.gridSpec
* @extends geo.meshFeature.meshSpec
* @property {number} [min] Minimum grid value. If unspecified, taken from
* the computed minimum of the `value` style.
* @property {number} [max] Maximum grid value. If unspecified, taken from
* the computed maximum of the `value` style.
* @property {geo.geoColor} [minColor='black'] Color used for any value below
* the minimum.
* @property {number} [minOpacity=0] Opacity used for any value below the
* minimum.
* @property {geo.geoColor} [maxColor='black'] Color used for any value above
* the maximum.
* @property {number} [maxOpacity=0] Opacity used for any value above the
* maximum.
* @property {boolean} [stepped] If falsy but not `undefined`, smooth
* transitions between colors.
* @property {geo.geoColor[]} [colorRange=<color table>] An array of colors
* used to show the range of values. The default is a 9-step color table.
* @property {number[]} [opacityRange] An array of opacities used to show the
* range of values. If unspecified, the opacity is 1. If this is a shorter
* list than the `colorRange`, an opacity of 1 is used for the entries near
* the end of the color range.
* @property {number[]} [rangeValues] An array used to map values to the
* `colorRange`. By default, values are spaced linearly. If specified, the
* entries must be increasing weakly monotonic, and there must be one more
* entry then the length of `colorRange`.
*/

/**
* Computed grid information.
*
* @typedef {geo.meshFeature.meshColoredInfo} geo.gridFeature.gridInfo
* @extends geo.meshFeature.meshColoredInfo
*/

/**
* Create a new instance of class gridFeature.
*
* @class
* @alias geo.gridFeature
* @extends geo.meshFeature
*
* @borrows geo.gridFeature#mesh as geo.gridFeature#grid
*
* @param {geo.gridFeature.spec} arg
* @returns {geo.gridFeature}
*/
var gridFeature = function (arg) {
'use strict';
if (!(this instanceof gridFeature)) {
return new gridFeature(arg);
}

var $ = require('jquery');
var util = require('./util');
var meshUtil = require('./util/mesh');

arg = arg || {};
meshFeature.call(this, arg);

/**
* @private
*/
var m_this = this,
s_init = this._init;

/**
* Create a set of vertices and values and opacities inside triangles.
* Create a set of triangles of indices into the vertex array. Create a
* color and opacity map corresponding to the values.
*
* @returns {geo.gridFeature.gridInfo} An object with the grid
* information.
*/
this._createGrids = function () {
return meshUtil.createColoredMesh(m_this, true);
};

this.grid = m_this.mesh;

/**
* Initialize.
*
* @param {geo.gridFeature.spec} arg The grid feature specification.
*/
this._init = function (arg) {
s_init.call(m_this, arg);

var defaultStyle = $.extend(
{},
{
opacity: 1.0,
value: function (d, i) {
return util.isNonNullFinite(d) ? d : m_this.position()(d, i).z;
},
position: (d) => d || {x: 0, y: 0},
origin: (p) => (p.length >= 3 ? [p[0], p[1], 0] : [0, 0, 0])
},
arg.style === undefined ? {} : arg.style
);

m_this.style(defaultStyle);

m_this.grid($.extend({}, {
minColor: 'black',
minOpacity: 0,
maxColor: 'black',
maxOpacity: 0,
/* 9-step based on paraview bwr colortable */
colorRange: [
{r: 0.07514311, g: 0.468049805, b: 1},
{r: 0.468487184, g: 0.588057293, b: 1},
{r: 0.656658579, g: 0.707001303, b: 1},
{r: 0.821573924, g: 0.837809045, b: 1},
{r: 0.943467973, g: 0.943498599, b: 0.943398095},
{r: 1, g: 0.788626485, b: 0.750707739},
{r: 1, g: 0.6289553, b: 0.568237474},
{r: 1, g: 0.472800903, b: 0.404551679},
{r: 0.916482116, g: 0.236630659, b: 0.209939162}
]
}, arg.mesh || {}, arg.grid || {}));

if (arg.mesh || arg.grid) {
m_this.dataTime().modified();
}
};

this._init(arg);
return this;
};

inherit(gridFeature, meshFeature);
module.exports = gridFeature;
1 change: 1 addition & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ module.exports = $.extend({
imageTile: require('./imageTile'),
isolineFeature: require('./isolineFeature'),
geojsonReader: require('./geojsonReader'),
gridFeature: require('./gridFeature'),
layer: require('./layer'),
lineFeature: require('./lineFeature'),
map: require('./map'),
Expand Down
Loading

0 comments on commit b5a6d9a

Please sign in to comment.