Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add a grid feature #1082

Merged
merged 1 commit into from
Aug 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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