import { create } from "../container/create";
import { render } from "../container/render";
import { geoCircle, geoPath, geoNaturalEarth1 } from "d3-geo";
const d3 = Object.assign({}, { geoPath, geoCircle, geoNaturalEarth1 });
/**
* @function tissot
* @description The `tissot` function aims to draw Tissot circles to visualize the deformations due to the projection
* @see {@link https://observablehq.com/@neocartocnrs/https://observablehq.com/@neocartocnrs/map-projections}
*
* @property {number} [step = 20] - step between circles
* @property {string} [id] - id of the layer
* @property {string} [fill = "red"] - fill color.
* @property {string} [stroke = "white"] - stroke color.
* @property {number} [strokeOpacity = 0.5] - stroke color.
* @property {*} [*] - *other SVG attributes that can be applied (strokeDasharray, strokeWidth, opacity, strokeLinecap...)*
* @property {*} [svg_*] - *parameters of the svg container created if the layer is not called inside a container (e.g svg_width)*
* @example
* // There are several ways to use this function
* geoviz.tissot(svg, { step: 25 }) // where svg is the container
* svg.tissot({ step: 25 }) // where svg is the container
* svg.plot({ type: "tissot", step: 25 }) // where svg is the container
* geoviz.tissot({ step: 25 }) // no container
*/
export function tissot(arg1, arg2) {
let newcontainer =
(arguments.length <= 1 || arguments[1] == undefined) &&
!arguments[0]?._groups
? true
: false;
let options = newcontainer ? arg1 : arg2;
// Default values
let opts = {
mark: "tissot",
step: 20,
fill: "red",
fillOpacity: 0.5,
stroke: "white",
};
opts = { ...opts, ...options };
opts.datum = regularcircles(opts.step);
let svgopts = {};
Object.keys(opts)
.filter((str) => str.slice(0, 4) == "svg_")
.forEach((d) => {
Object.assign(svgopts, {
[d.slice(0, 4) == "svg_" ? d.slice(4) : d]: opts[d],
});
delete opts[d];
});
let svg = newcontainer
? create({ projection: d3.geoNaturalEarth1(), ...svgopts })
: arg1;
svg.path(opts);
if (newcontainer) {
return render(svg);
} else {
return `#${opts.id}`;
}
}
// Create circles
function regularcircles(step) {
const circle = d3
.geoCircle()
.center((d) => d)
.radius(step / 4)
.precision(10);
const features = [];
for (let y = -80; y <= 80; y += step) {
for (let x = -180; x < 180; x += step) {
features.push({
type: "Feature",
properties: {},
geometry: {
type: "MultiPolygon",
coordinates: [circle([x, y]).coordinates],
},
});
}
}
return { type: "FeatureCollection", features: features };
}