|
|
@@ -1,5 +1,5 @@
|
|
|
import React, { Component } from 'react';
|
|
|
-import { Path, PaperScope, Point, Size, Rectangle, Color, Layer, Raster, PointText, Group } from 'paper';
|
|
|
+import { Path, PaperScope, Point, Size, Rectangle, Color, Layer, Raster } from 'paper';
|
|
|
import { Container, CustomInput, FormGroup, Row, Col, Button } from 'reactstrap';
|
|
|
import FileSaver from 'file-saver';
|
|
|
import map from './merseyside-nobg-white.png';
|
|
|
@@ -37,6 +37,57 @@ enum HeatmapType {
|
|
|
Trustworthiness = 'trustworthiness'
|
|
|
}
|
|
|
|
|
|
+type PersonalInformation = {
|
|
|
+ age: string,
|
|
|
+ gender: string,
|
|
|
+ genderCustom: string,
|
|
|
+ birthPlace: string,
|
|
|
+ currentPlace: string,
|
|
|
+ levelEducation: string[],
|
|
|
+ nonNative: string,
|
|
|
+}
|
|
|
+
|
|
|
+type FormPath = {
|
|
|
+ name: string,
|
|
|
+ soundExample: string,
|
|
|
+ associations: string[],
|
|
|
+ correctness: number,
|
|
|
+ friendliness: number,
|
|
|
+ pleasantness: number,
|
|
|
+ trustworthiness: number
|
|
|
+}
|
|
|
+
|
|
|
+type OriginalCanvasData = {
|
|
|
+ form: FormPath,
|
|
|
+ path: string,
|
|
|
+}
|
|
|
+
|
|
|
+type Result = {
|
|
|
+ personalInformation: PersonalInformation,
|
|
|
+ canvas: {
|
|
|
+ form: FormPath,
|
|
|
+ path: paper.Path
|
|
|
+ }[],
|
|
|
+ email: string,
|
|
|
+ id: number,
|
|
|
+}
|
|
|
+
|
|
|
+type HeatmapProps = {
|
|
|
+ data: Result[],
|
|
|
+}
|
|
|
+
|
|
|
+type HeatmapState = {
|
|
|
+ data: HeatmapData[],
|
|
|
+ canvas?: paper.PaperScope,
|
|
|
+ grid: Grid,
|
|
|
+ heatmapLayer: paper.Layer | undefined,
|
|
|
+ mapLayer: paper.Layer | undefined,
|
|
|
+ gridLayer: paper.Layer | undefined,
|
|
|
+ areaAmount: paper.TextItem | undefined,
|
|
|
+ heatmapType: HeatmapType,
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
class Heatmap extends Component<HeatmapProps, HeatmapState> {
|
|
|
|
|
|
constructor(props: HeatmapProps) {
|
|
|
@@ -46,7 +97,9 @@ class Heatmap extends Component<HeatmapProps, HeatmapState> {
|
|
|
data: [],
|
|
|
canvas: undefined,
|
|
|
grid: [],
|
|
|
+ heatmapLayer: undefined,
|
|
|
mapLayer: undefined,
|
|
|
+ gridLayer: undefined,
|
|
|
areaAmount: undefined,
|
|
|
heatmapType: HeatmapType.Amount,
|
|
|
}
|
|
|
@@ -63,7 +116,14 @@ class Heatmap extends Component<HeatmapProps, HeatmapState> {
|
|
|
return _path;
|
|
|
}
|
|
|
|
|
|
- drawMapData = (data: Result[], canvasWidth: number) => {
|
|
|
+ addshape = (path: paper.Path, data: HeatmapData, canvas: paper.PaperScope) => {
|
|
|
+ const _path = new canvas.Path(path.segments);
|
|
|
+ _path.data = data;
|
|
|
+ canvas.project.layers[0].addChild(_path);
|
|
|
+ }
|
|
|
+
|
|
|
+ drawMapData = (data: Result[], canvas: paper.PaperScope) => {
|
|
|
+ canvas.project.layers[0]?.removeChildren();
|
|
|
const _data = data.reduce<Array<HeatmapData>>((newData, result) => {
|
|
|
return [
|
|
|
...newData,
|
|
|
@@ -75,7 +135,7 @@ class Heatmap extends Component<HeatmapProps, HeatmapState> {
|
|
|
pleasantness: shape.form.pleasantness,
|
|
|
trustworthiness: shape.form.trustworthiness,
|
|
|
}
|
|
|
- this.drawShape(shape.path, canvasWidth / result.canvasSize.width, data);
|
|
|
+ this.addshape(shape.path, data, canvas);
|
|
|
return data as HeatmapData;
|
|
|
})
|
|
|
]
|
|
|
@@ -102,15 +162,21 @@ class Heatmap extends Component<HeatmapProps, HeatmapState> {
|
|
|
return chunks;
|
|
|
}
|
|
|
|
|
|
- mkGrid = (canvas: paper.PaperScope): Grid=> {
|
|
|
+ mkGrid = (canvas: paper.PaperScope, itemsLayer: paper.Layer): { grid: Grid, layer: paper.Layer } => {
|
|
|
const bigChunkSizeX = 4, bigChunkSizeY = 5;
|
|
|
- const mainLayer = canvas.project.activeLayer;
|
|
|
- return this.mkChunk(bigChunkSizeX*X_SEGMENTS, Y_SEGMENTS*bigChunkSizeY, canvas.project.view.bounds, mainLayer).map(({rectangle, items}) => {
|
|
|
+ let layer = new canvas.Layer({ name: 'heatmap' });
|
|
|
+ const grid = this.mkChunk(bigChunkSizeX*X_SEGMENTS, Y_SEGMENTS*bigChunkSizeY, canvas.project.view.bounds, itemsLayer).map(({rectangle, items}) => {
|
|
|
+ let area = new canvas.Path.Rectangle(rectangle);
|
|
|
+ layer.addChild(area);
|
|
|
return {
|
|
|
- area: new Path.Rectangle(rectangle),
|
|
|
+ area,
|
|
|
items
|
|
|
}
|
|
|
- })
|
|
|
+ });
|
|
|
+ return {
|
|
|
+ grid,
|
|
|
+ layer
|
|
|
+ }
|
|
|
// this.mkChunk(bigChunkSizeX, bigChunkSizeY, canvas.project.view.bounds, mainLayer, true).forEach((chunk, i) => {
|
|
|
// const x = new Path.Rectangle(chunk.rectangle);
|
|
|
// console.log(x.bounds)
|
|
|
@@ -137,8 +203,8 @@ class Heatmap extends Component<HeatmapProps, HeatmapState> {
|
|
|
return item.data[key] ? tot + item.data[key] : tot;
|
|
|
}, 0);
|
|
|
return total !== 0 ?
|
|
|
- new Color({ hue: RANGE(total / items.length), saturation: 1, brightness: items.length/MAX}) :
|
|
|
- new Color('black')
|
|
|
+ new Color({ hue: RANGE(total / items.length), saturation: 1, brightness: items.length/MAX}) :
|
|
|
+ new Color('black')
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -178,19 +244,37 @@ class Heatmap extends Component<HeatmapProps, HeatmapState> {
|
|
|
const canvas = new PaperScope();
|
|
|
canvas.setup('vom-heatmap-canvas');
|
|
|
canvas.view.viewSize.height = canvas.view.size.width * 1.25;
|
|
|
- const data = this.drawMapData(this.props.data, canvas.view.size.width);
|
|
|
- const grid = this.mkGrid(canvas);
|
|
|
+ const data = this.drawMapData(this.props.data, canvas);
|
|
|
+ const { grid, layer } = this.mkGrid(canvas, canvas.project.activeLayer);
|
|
|
this.changeHeatmapColor(HeatmapType.Friendliness, grid);
|
|
|
|
|
|
this.setState({
|
|
|
data,
|
|
|
canvas,
|
|
|
grid,
|
|
|
+ heatmapLayer: layer,
|
|
|
heatmapType: HeatmapType.Friendliness,
|
|
|
mapLayer: this.showMap(canvas)
|
|
|
})
|
|
|
}
|
|
|
|
|
|
+ componentDidUpdate = (prev: HeatmapProps) => {
|
|
|
+ if (prev.data.length !== this.props.data.length) {
|
|
|
+ const canvas = this.state.canvas!;
|
|
|
+ const data = this.drawMapData(this.props.data, canvas);
|
|
|
+ this.state.heatmapLayer?.remove();
|
|
|
+ const { grid, layer } = this.mkGrid(canvas, canvas.project.layers[0]);
|
|
|
+ this.changeHeatmapColor(HeatmapType.Friendliness, grid);
|
|
|
+ this.showMap(canvas);
|
|
|
+ this.setState({
|
|
|
+ grid,
|
|
|
+ heatmapLayer: layer,
|
|
|
+ data,
|
|
|
+ heatmapType: HeatmapType.Friendliness,
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
handleChangeColor = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
|
const heatmapType = e.currentTarget.value as HeatmapType;
|
|
|
this.changeHeatmapColor(heatmapType, this.state.grid);
|
|
|
@@ -200,122 +284,66 @@ class Heatmap extends Component<HeatmapProps, HeatmapState> {
|
|
|
}
|
|
|
|
|
|
render = () => (
|
|
|
- <Container className="vom-heatmap">
|
|
|
- <Row>
|
|
|
- <Col xs="12" md="8">
|
|
|
- <div className="vom-heatmap-map">
|
|
|
- {
|
|
|
- this.state.heatmapType !== HeatmapType.Amount ? (
|
|
|
- <div id="vom-heatmap-range">
|
|
|
- <div>1</div>
|
|
|
- <div>2</div>
|
|
|
- <div>3</div>
|
|
|
- <div>4</div>
|
|
|
- <div>5</div>
|
|
|
- </div>
|
|
|
- ) : (
|
|
|
- <div id="vom-heatmap-amount-range">
|
|
|
- <div>1</div>
|
|
|
- <div>200</div>
|
|
|
- </div>
|
|
|
- )
|
|
|
- }
|
|
|
- <canvas id="vom-heatmap-canvas"></canvas>
|
|
|
+ <div className="vom-heatmap">
|
|
|
+ <div className="vom-heatmap-map">
|
|
|
+ <canvas id="vom-heatmap-canvas"></canvas>
|
|
|
+ {
|
|
|
+ this.state.heatmapType !== HeatmapType.Amount ? (
|
|
|
+ <div id="vom-heatmap-range">
|
|
|
+ <div>1</div>
|
|
|
+ <div>2</div>
|
|
|
+ <div>3</div>
|
|
|
+ <div>4</div>
|
|
|
+ <div>5</div>
|
|
|
+ </div>
|
|
|
+ ) : (
|
|
|
+ <div id="vom-heatmap-amount-range">
|
|
|
+ <div>1</div>
|
|
|
+ <div>200</div>
|
|
|
+ </div>
|
|
|
+ )
|
|
|
+ }
|
|
|
+ </div>
|
|
|
+ <div className="vom-heatmap-actions">
|
|
|
+ <FormGroup>
|
|
|
+ <Button size="sm" onClick={() => {this.saveAsImage()} }>download current map</Button>
|
|
|
+ <h4>Heatmap type</h4>
|
|
|
+ <div>
|
|
|
+ <CustomInput type="radio" id="heatmap-by-amount" name="by-amount"
|
|
|
+ label="by amount of responses"
|
|
|
+ value={HeatmapType.Amount}
|
|
|
+ checked={this.state.heatmapType === HeatmapType.Amount}
|
|
|
+ onChange={ this.handleChangeColor }
|
|
|
+ />
|
|
|
+ <CustomInput type="radio" id="heatmap-by-friendliness" name="by-friendliness"
|
|
|
+ label="by friendliness"
|
|
|
+ value={HeatmapType.Friendliness}
|
|
|
+ checked={this.state.heatmapType === HeatmapType.Friendliness}
|
|
|
+ onChange={ this.handleChangeColor }
|
|
|
+ />
|
|
|
+ <CustomInput type="radio" id="heatmap-by-correctness" name="by-correctness"
|
|
|
+ label="by correctness"
|
|
|
+ value={HeatmapType.Correctness}
|
|
|
+ checked={this.state.heatmapType === HeatmapType.Correctness}
|
|
|
+ onChange={ this.handleChangeColor }
|
|
|
+ />
|
|
|
+ <CustomInput type="radio" id="heatmap-by-pleasantness" name="by-pleasantness"
|
|
|
+ label="by pleasantness"
|
|
|
+ value={HeatmapType.Pleasantness}
|
|
|
+ checked={this.state.heatmapType === HeatmapType.Pleasantness}
|
|
|
+ onChange={ this.handleChangeColor }
|
|
|
+ />
|
|
|
+ <CustomInput type="radio" id="heatmap-by-trustworthiness" name="by-trustworthiness"
|
|
|
+ label="by trustworthiness"
|
|
|
+ value={HeatmapType.Trustworthiness}
|
|
|
+ checked={this.state.heatmapType === HeatmapType.Trustworthiness}
|
|
|
+ onChange={ this.handleChangeColor }
|
|
|
+ />
|
|
|
</div>
|
|
|
- </Col>
|
|
|
- <Col>
|
|
|
- <div className="vom-heatmap-actions">
|
|
|
- <FormGroup>
|
|
|
- <Button size="sm" onClick={() => {this.saveAsImage()} }>download current map</Button>
|
|
|
- <h4>Heatmap type</h4>
|
|
|
- <div>
|
|
|
- <CustomInput type="radio" id="heatmap-by-amount" name="by-amount"
|
|
|
- label="by amount of responses"
|
|
|
- value={HeatmapType.Amount}
|
|
|
- checked={this.state.heatmapType === HeatmapType.Amount}
|
|
|
- onChange={ this.handleChangeColor }
|
|
|
- />
|
|
|
- <CustomInput type="radio" id="heatmap-by-friendliness" name="by-friendliness"
|
|
|
- label="by friendliness"
|
|
|
- value={HeatmapType.Friendliness}
|
|
|
- checked={this.state.heatmapType === HeatmapType.Friendliness}
|
|
|
- onChange={ this.handleChangeColor }
|
|
|
- />
|
|
|
- <CustomInput type="radio" id="heatmap-by-correctness" name="by-correctness"
|
|
|
- label="by correctness"
|
|
|
- value={HeatmapType.Correctness}
|
|
|
- checked={this.state.heatmapType === HeatmapType.Correctness}
|
|
|
- onChange={ this.handleChangeColor }
|
|
|
- />
|
|
|
- <CustomInput type="radio" id="heatmap-by-pleasantness" name="by-pleasantness"
|
|
|
- label="by pleasantness"
|
|
|
- value={HeatmapType.Pleasantness}
|
|
|
- checked={this.state.heatmapType === HeatmapType.Pleasantness}
|
|
|
- onChange={ this.handleChangeColor }
|
|
|
- />
|
|
|
- <CustomInput type="radio" id="heatmap-by-trustworthiness" name="by-trustworthiness"
|
|
|
- label="by trustworthiness"
|
|
|
- value={HeatmapType.Trustworthiness}
|
|
|
- checked={this.state.heatmapType === HeatmapType.Trustworthiness}
|
|
|
- onChange={ this.handleChangeColor }
|
|
|
- />
|
|
|
- </div>
|
|
|
- </FormGroup>
|
|
|
- </div>
|
|
|
- </Col>
|
|
|
- </Row>
|
|
|
- </Container>
|
|
|
+ </FormGroup>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
)
|
|
|
}
|
|
|
|
|
|
-type PersonalInformation = {
|
|
|
- age: string,
|
|
|
- gender: string,
|
|
|
- genderCustom: string,
|
|
|
- birthPlace: string,
|
|
|
- currentPlace: string,
|
|
|
- levelEducation: string[],
|
|
|
- nonNative: string,
|
|
|
-}
|
|
|
-
|
|
|
-type FormPath = {
|
|
|
- name: string,
|
|
|
- soundExample: string,
|
|
|
- associations: string[],
|
|
|
- correctness: number,
|
|
|
- friendliness: number,
|
|
|
- pleasantness: number,
|
|
|
- trustworthiness: number
|
|
|
-}
|
|
|
-
|
|
|
-type OriginalCanvasData = {
|
|
|
- form: FormPath,
|
|
|
- path: string,
|
|
|
-}
|
|
|
-
|
|
|
-type Result = {
|
|
|
- personalInformation: PersonalInformation,
|
|
|
- canvas: OriginalCanvasData[],
|
|
|
- canvasSize: {
|
|
|
- width: number,
|
|
|
- height: number
|
|
|
- },
|
|
|
- email: string,
|
|
|
- id: number,
|
|
|
-}
|
|
|
-
|
|
|
-type HeatmapProps = {
|
|
|
- data: Result[],
|
|
|
-}
|
|
|
-
|
|
|
-type HeatmapState = {
|
|
|
- data: HeatmapData[],
|
|
|
- canvas?: paper.PaperScope,
|
|
|
- grid: Grid,
|
|
|
- mapLayer: paper.Layer | undefined,
|
|
|
- areaAmount: paper.TextItem | undefined,
|
|
|
- heatmapType: HeatmapType,
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
export default Heatmap;
|