diff --git a/src/draw.js b/src/draw.js index f803d52..fa19e08 100644 --- a/src/draw.js +++ b/src/draw.js @@ -20,6 +20,7 @@ import { bubble } from "./layers/bubble.js"; import { square } from "./layers/square.js"; import { regularbubble } from "./layers/regularbubble.js"; import { regularsquare } from "./layers/regularsquare.js"; +import { joyplot } from "./layers/joyplot.js"; import { regulargrid } from "./layers/regulargrid.js"; import { smooth } from "./layers/smooth.js"; import { mushroom } from "./layers/mushroom.js"; @@ -748,6 +749,31 @@ export function draw({ params = {}, layers = {} } = {}) { ); } + // joyplot + + if (layer.type == "joyplot") { + joyplot( + svg, + projection, + { + geojson: layer.geojson, + values: layer.values, + step: layer.step, + blur: layer.blur, + k: layer.k, + fill: layer.fill, + stroke: layer.stroke, + strokeWidth: layer.strokeWidth, + fillOpacity: layer.fillOpacity, + strokeDasharray: layer.strokeDasharray, + strokeOpacity: layer.strokeOpacity, + }, + clipid, + width, + height + ); + } + // Points Bertin (carrés) if (layer.type == "regularsquare") { diff --git a/src/helpers/grid.js b/src/helpers/grid.js index 1c71e98..92f4364 100644 --- a/src/helpers/grid.js +++ b/src/helpers/grid.js @@ -2,7 +2,7 @@ import booleanPointInPolygon from "@turf/boolean-point-in-polygon"; //import area from "@turf/area"; import bbox from "@turf/bbox"; import intersect from "@turf/intersect"; -import RBush from 'rbush'; +import RBush from "rbush"; const turf = Object.assign({}, { booleanPointInPolygon, intersect, bbox }); import { range, rollup, ascending, blur2, sum, flatGroup } from "d3-array"; @@ -35,7 +35,8 @@ export function grid({ height, // height of the map step = 20, // gap between points output = "dots", // dots or squares - blur = 0, // blur value with d3.blur2() + blur = 0, // blur value with d3.blur2(), + keep = false, } = {}) { // --------------- // Build empty grid @@ -186,14 +187,15 @@ export function grid({ // Create an RTree const polysRTree = new RBush(); - polysRTree.load( // + polysRTree.load( + // polys.features.map((d, i) => { const b = getBbox(d); // We store the index of the feature alongside the bbox, // so we can retrieve the feature later. b.ix = i; return b; - }), + }) ); // Build intersected pieces @@ -285,13 +287,20 @@ export function grid({ }); } - let FeatureCollection = { - type: "FeatureCollection", - features: features - .filter((d) => d.properties.value > 0) - .sort((a, b) => d3.ascending(a.properties.value, b.properties.value)), - }; - - return FeatureCollection; + if (keep == true) { + return { + type: "FeatureCollection", + features: features + //.filter((d) => d.properties.value > 0) + .sort((a, b) => d3.ascending(a.properties.value, b.properties.value)), + }; + } else { + return { + type: "FeatureCollection", + features: features + .filter((d) => d.properties.value > 0) + .sort((a, b) => d3.ascending(a.properties.value, b.properties.value)), + }; + } } } diff --git a/src/index.js b/src/index.js index 9ac83d2..f027519 100644 --- a/src/index.js +++ b/src/index.js @@ -7,3 +7,7 @@ export { links } from "./links.js"; export { borders } from "./borders.js"; export { bbox } from "./bbox.js"; export { properties } from "./properties.js"; + +// test + +export { grid } from "./helpers/grid.js"; diff --git a/src/layers/joyplot.js b/src/layers/joyplot.js new file mode 100644 index 0000000..679983f --- /dev/null +++ b/src/layers/joyplot.js @@ -0,0 +1,105 @@ +import { grid } from "../helpers/grid.js"; +import { figuration } from "../helpers/figuration.js"; +import { select } from "d3-selection"; +import { scaleLinear } from "d3-scale"; +import { ascending, max } from "d3-array"; +import { geoPath } from "d3-geo"; + +const d3 = Object.assign( + {}, + { + geoPath, + max, + scaleLinear, + ascending, + select, + } +); + +export function joyplot( + selection, + projection, + options = {}, + clipid, + width, + height +) { + let k = options.k ? options.k : 100; + let fill = options.fill ? options.fill : "#508bab"; + let stroke = options.stroke ? options.stroke : "white"; + let strokeWidth = options.strokeWidth ? options.strokeWidth : 0.5; + let fillOpacity = options.fillOpacity ? options.fillOpacity : 1; + let strokeDasharray = options.strokeDasharray + ? options.strokeDasharray + : "none"; + let strokeOpacity = options.strokeOpacity ? options.strokeOpacity : 1; + + let mygrid = grid({ + geojson: options.geojson, + blur: options.blur, + values: options.values, + projection: projection, + width: width, + height: height, + keep: true, + step: options.step, + }) + .features.map((d) => ({ + x: d.geometry.coordinates[0], + y: d.geometry.coordinates[1], + value: d.properties.value, + })) + .sort((a, b) => d3.ascending(a.y, b.y) || d3.ascending(a.x, b.x)); + + let ycoords = Array.from(new Set(mygrid.map((d) => d.y))); + + let scale = d3 + .scaleLinear() + .domain([0, d3.max(mygrid.map((d) => d.value))]) + .range([0, k]); + + let features = []; + + ycoords.forEach((y) => { + let coords = []; + mygrid + .filter((d) => d.y == y) + .forEach((d) => coords.push([d.x, d.y - scale(d.value)])); + + let linetring = { + type: "LineString", + coordinates: coords, + }; + + features.push({ type: "Feature", properties: {}, geometry: linetring }); + }); + + // svg + // .append("clipPath") + // .attr("id", `clip_${clipid}`) + // .append("path") + // .datum({ type: "Sphere" }) + // .attr("d", d3.geoPath(projection)); + + selection + .append("clipPath") + .attr("id", "test") + .append("path") + .datum(options.geojson) + .attr("d", d3.geoPath(projection)); + + selection + .append("g") + //.attr("clip-path", clipid == null ? `none` : `url(#clip_${clipid})`) + //.attr("clip-path", `url(#test)`) + .attr("fill", fill) + .attr("stroke", stroke) + .attr("stroke-width", strokeWidth) + .attr("fill-opacity", fillOpacity) + .attr("stroke-opacity", strokeOpacity) + .attr("stroke-dasharray", strokeDasharray) + .selectAll("path") + .data(features) + .join("path") + .attr("d", d3.geoPath()); +}