Skip to content

Commit

Permalink
attempts to improve shapecolorsize draw speed
Browse files Browse the repository at this point in the history
  • Loading branch information
joewdavies committed Nov 21, 2024
1 parent b0e9448 commit 18ab75c
Show file tree
Hide file tree
Showing 6 changed files with 304 additions and 156 deletions.
175 changes: 99 additions & 76 deletions dist/gridviz.js

Large diffs are not rendered by default.

57 changes: 57 additions & 0 deletions dist/src_dataset_worker_js.gridviz.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 18 additions & 3 deletions examples/test/rendering-test.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,16 @@
map,
'https://raw.githubusercontent.com/jgaffuri/tiledgrids/main/data/europe/population2/' +
resolution +
'm/'
'm/',
{
preprocess: (c) => {
//for each cell, compute 2011 -> 2021 change and store it in a new "change" column
if (!c.TOT_P_2011 && !c.TOT_P_2021) c.change = 0
else if (!c.TOT_P_2011 && c.TOT_P_2021) c.change = +c.TOT_P_2021
else if (c.TOT_P_2011 && !c.TOT_P_2021) c.change = -c.TOT_P_2011
else c.change = c.TOT_P_2021 - c.TOT_P_2011
},
}
)
)

Expand All @@ -38,10 +47,16 @@
}

//define style
const style = new gridviz.ShapeColorSizeStyle({ color: colorFunction })
const style = new gridviz.ShapeColorSizeStyle({
color: colorFunction,
})

//add layer to map
map.layers = [new gridviz.GridLayer(dataset, [style], { minPixelsPerCell: 1 })]
map.layers = [
new gridviz.GridLayer(dataset, [style], {
minPixelsPerCell: 1,
}),
]
</script>
</body>
</html>
18 changes: 18 additions & 0 deletions src/dataset/worker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
self.onmessage = function (e) {
const { type, cells, preprocess } = e.data
if (type === 'processCells') {
const result = processCells(cells, preprocess)
self.postMessage({ type: 'processedCells', result })
}
}

function processCells(cells, preprocess) {
// If no preprocess function is provided, return the cells as they are
if (!preprocess) {
return cells
}

// Apply preprocess function to each cell if available
const preprocessedCells = cells.map((cell) => preprocess(cell)).filter((cell) => cell !== false)
return preprocessedCells
}
175 changes: 99 additions & 76 deletions src/style/ShapeColorSizeStyle.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,118 +15,141 @@ export class ShapeColorSizeStyle extends Style {
constructor(opts) {
super(opts)
opts = opts || {}
this.opts = opts

/** A function returning the color of the cell.
* @type {function(import('../core/Dataset.js').Cell, number, number, object):string} */
this.color = opts.color || (() => "#EA6BAC") //(c,r,z,vs) => {}
this.color = opts.color || (() => '#EA6BAC') //(c,r,z,vs) => {}

/** A function returning the size of a cell in geographical unit.
* @type {function(import('../core/Dataset.js').Cell, number, number, object):number} */
this.size = opts.size || ((cell, resolution) => resolution) //(c,r,z,vs) => {}

/** A function returning the shape of a cell.
* @type {function(import("../core/Dataset.js").Cell,number, number,object):import("../core/Style.js").Shape} */
this.shape = opts.shape || (() => "square") //(c,r,z,vs) => {}
this.shape = opts.shape || (() => 'square') //(c,r,z,vs) => {}
}

/**
* Draw cells as squares, with various colors and sizes.
*
*
* @param {Array.<import("../core/Dataset.js").Cell>} cells
* @param {import("../core/GeoCanvas.js").GeoCanvas} geoCanvas
* @param {number} resolution
* @override
*/
draw(cells, geoCanvas, resolution) {

//filter
// Filter cells early
if (this.filter) cells = cells.filter(this.filter)

//zoom
// Precompute constants
const r2 = resolution * 0.5
const z = geoCanvas.view.z

//get view scale
// Determine view scale if applicable
const viewScale = this.viewScale ? this.viewScale(cells, resolution, z) : undefined

const r2 = resolution * 0.5
for (let c of cells) {
//color
let col = this.color ? this.color(c, resolution, z, viewScale) : "black"
const ctx = geoCanvas.ctx

// Loop through cells
for (const c of cells) {
// Determine color
const col = this.color ? this.color(c, resolution, z, viewScale) : 'black'
if (!col || col === 'none') continue

//size
// Determine size
const size = this.size ? this.size(c, resolution, z, viewScale) : resolution
if (!size) continue

//shape
// Determine shape
const shape = this.shape ? this.shape(c, resolution, z, viewScale) : 'square'
if (shape === 'none') continue

//get offset
const offset = this.offset(c, resolution, z)

geoCanvas.ctx.fillStyle = col
if (shape === 'square') {
//draw square
const d = resolution * (1 - size / resolution) * 0.5
geoCanvas.ctx.fillRect(c.x + d + offset.dx, c.y + d + offset.dy, size, size)
} else if (shape === 'circle') {
//draw circle
geoCanvas.ctx.beginPath()
geoCanvas.ctx.arc(c.x + r2 + offset.dx, c.y + r2 + offset.dy, size * 0.5, 0, 2 * Math.PI, false)
geoCanvas.ctx.fill()
} else if (shape === 'donut') {
//draw donut
const xc = c.x + r2 + offset.dx,
yc = c.y + r2 + offset.dy
geoCanvas.ctx.beginPath()
geoCanvas.ctx.moveTo(xc, yc)
geoCanvas.ctx.arc(xc, yc, r2, 0, 2 * Math.PI)
geoCanvas.ctx.arc(xc, yc, (1 - size / resolution) * r2, 0, 2 * Math.PI, true)
geoCanvas.ctx.closePath()
geoCanvas.ctx.fill()
} else if (shape === 'diamond') {
const s2 = size * 0.5
geoCanvas.ctx.beginPath()
geoCanvas.ctx.moveTo(c.x + r2 - s2, c.y + r2)
geoCanvas.ctx.lineTo(c.x + r2, c.y + r2 + s2)
geoCanvas.ctx.lineTo(c.x + r2 + s2, c.y + r2)
geoCanvas.ctx.lineTo(c.x + r2, c.y + r2 - s2)
geoCanvas.ctx.fill()
} else if (shape === 'triangle_up') {
const dr2 = (size - resolution) / 2
geoCanvas.ctx.beginPath()
geoCanvas.ctx.moveTo(c.x - dr2, c.y - dr2)
geoCanvas.ctx.lineTo(c.x + r2, c.y + resolution + dr2)
geoCanvas.ctx.lineTo(c.x + resolution + dr2, c.y - dr2)
geoCanvas.ctx.fill()
} else if (shape === 'triangle_down') {
const dr2 = (size - resolution) / 2
geoCanvas.ctx.beginPath()
geoCanvas.ctx.moveTo(c.x - dr2, c.y + resolution + dr2)
geoCanvas.ctx.lineTo(c.x + r2, c.y - dr2)
geoCanvas.ctx.lineTo(c.x + resolution + dr2, c.y + resolution + dr2)
geoCanvas.ctx.fill()
} else if (shape === 'triangle_left') {
const dr2 = (size - resolution) / 2
geoCanvas.ctx.beginPath()
geoCanvas.ctx.moveTo(c.x + resolution + dr2, c.y + resolution + dr2)
geoCanvas.ctx.lineTo(c.x - dr2, c.y + r2)
geoCanvas.ctx.lineTo(c.x + resolution + dr2, c.y - dr2)
geoCanvas.ctx.fill()
} else if (shape === 'triangle_right') {
const dr2 = (size - resolution) / 2
geoCanvas.ctx.beginPath()
geoCanvas.ctx.moveTo(c.x - dr2, c.y - dr2)
geoCanvas.ctx.lineTo(c.x + resolution + dr2, c.y + r2)
geoCanvas.ctx.lineTo(c.x - dr2, c.y + resolution + dr2)
geoCanvas.ctx.fill()
} else {
throw new Error('Unexpected shape:' + shape)
// Get offsets
const { dx, dy } = this.offset(c, resolution, z)

// Apply color
ctx.fillStyle = col

// Draw the appropriate shape
const x = c.x + dx
const y = c.y + dy

switch (shape) {
case 'square': {
const d = resolution * (1 - size / resolution) * 0.5
ctx.fillRect(x + d, y + d, size, size)
break
}
case 'circle': {
ctx.beginPath()
ctx.arc(x + r2, y + r2, size * 0.5, 0, 2 * Math.PI)
ctx.fill()
break
}
case 'donut': {
const xc = x + r2,
yc = y + r2
ctx.beginPath()
ctx.arc(xc, yc, r2, 0, 2 * Math.PI)
ctx.arc(xc, yc, (1 - size / resolution) * r2, 0, 2 * Math.PI, true)
ctx.closePath()
ctx.fill()
break
}
case 'diamond': {
const s2 = size * 0.5
ctx.beginPath()
ctx.moveTo(x + r2 - s2, y + r2)
ctx.lineTo(x + r2, y + r2 + s2)
ctx.lineTo(x + r2 + s2, y + r2)
ctx.lineTo(x + r2, y + r2 - s2)
ctx.fill()
break
}
case 'triangle_up': {
const dr2 = (size - resolution) / 2
ctx.beginPath()
ctx.moveTo(x - dr2, y - dr2)
ctx.lineTo(x + r2, y + resolution + dr2)
ctx.lineTo(x + resolution + dr2, y - dr2)
ctx.fill()
break
}
case 'triangle_down': {
const dr2 = (size - resolution) / 2
ctx.beginPath()
ctx.moveTo(x - dr2, y + resolution + dr2)
ctx.lineTo(x + r2, y - dr2)
ctx.lineTo(x + resolution + dr2, y + resolution + dr2)
ctx.fill()
break
}
case 'triangle_left': {
const dr2 = (size - resolution) / 2
ctx.beginPath()
ctx.moveTo(x + resolution + dr2, y + resolution + dr2)
ctx.lineTo(x - dr2, y + r2)
ctx.lineTo(x + resolution + dr2, y - dr2)
ctx.fill()
break
}
case 'triangle_right': {
const dr2 = (size - resolution) / 2
ctx.beginPath()
ctx.moveTo(x - dr2, y - dr2)
ctx.lineTo(x + resolution + dr2, y + r2)
ctx.lineTo(x - dr2, y + resolution + dr2)
ctx.fill()
break
}
default:
console.error('Unexpected shape:', shape)
break
}
}

//update legends
this.updateLegends({ viewScale: viewScale, resolution: resolution, z: z, cells: cells })
// Update legends
this.updateLegends({ viewScale, resolution, z, cells })
}
}
14 changes: 13 additions & 1 deletion webpack.config.dev.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,18 @@ module.exports = {
//syncWebAssembly: true
},

// webpack.config.js

// other config options
module: {
rules: [
{
test: /\.worker\.js$/,
use: { loader: 'worker-loader' },
},
// other rules
],
},
//this is to test data from a local folder, exposed via a local server. Replace directory and port on request.
devServer: {
static: {
Expand All @@ -26,6 +38,6 @@ module.exports = {
directory: '/home/juju/gisco/grid_pop_c2021/',
},
port: 1234,
headers: { 'Access-Control-Allow-Origin': '*', },
headers: { 'Access-Control-Allow-Origin': '*' },
},
}

0 comments on commit 18ab75c

Please sign in to comment.