import { create } from "../container/create";
import { render } from "../container/render";
import { camelcasetodash, unique, northangle } from "../helpers/utils";
import { geoNaturalEarth1 } from "d3-geo";
const d3 = Object.assign({}, { geoNaturalEarth1 });
/**
* @function north
* @description The `north` function allows add a North arrow. The function adds a layer to the SVG container and returns the layer identifier. If the container is not defined, then the layer is displayed directly.
* @see {@link https://observablehq.com/@neocartocnrs/layout-marks}
* @see {@link https://observablehq.com/@neocartocnrs/geoviz-scalebar}
* @property {string} [id] - id of the layer
* @property {number[]} [pos = [svg.width - 30, 30]] - position [x,y] on the page. The scale value is relevant for this location on the map
* @property {number} [scale = 1] - a number to rescale the arrow
* @property {number} [rotate = null] - an angle to rotate the arrow. By dedault, il is automaticaly calculated
* @property {string} [fill = "black"] - fill color
* @property {string} [fillOpacity = 1] - fill-opacity
* @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.north(svg, { pos: [100, 300], fill: "brown" }) // where svg is the container
* svg.north({ pos: [100, 300], fill: "brown" }) // where svg is the container
* svg.plot({ type: "north", pos: [100, 300], fill: "brown" }) // where svg is the container
* geoviz.north( { pos: [100, 300], fill: "brown" }) // no container
*/
export function north(arg1, arg2) {
// Test if new container
let newcontainer =
(arguments.length <= 1 || arguments[1] == undefined) &&
!arguments[0]?._groups
? true
: false;
arg1 = newcontainer && arg1 == undefined ? {} : arg1;
arg2 = arg2 == undefined ? {} : arg2;
// Arguments
const options = {
mark: "north",
id: unique(),
rotate: null,
scale: 1,
fill: "black",
fillOpacity: 1,
};
let opts = { ...options, ...(newcontainer ? arg1 : arg2) };
// New container
let svgopts = { projection: d3.geoNaturalEarth1() };
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(svgopts) : arg1;
// Position
opts.pos = opts.pos || [svg.width - 30, 30];
// Warning
if (svg.initproj == "none" && svg.warning) {
svg.warning_message.push(`North mark`);
svg.warning_message.push(
`The North arrow is not relevant without defining a projection function in the SVG container`
);
}
// init layer
let layer = svg.selectAll(`#${opts.id}`).empty()
? svg.append("g").attr("id", opts.id)
: svg.select(`#${opts.id}`);
layer.selectAll("*").remove();
// zoomable layer
if (svg.zoomable && !svg.parent) {
if (!svg.zoomablelayers.map((d) => d.id).includes(opts.id)) {
svg.zoomablelayers.push(opts);
} else {
let i = svg.zoomablelayers.indexOf(
svg.zoomablelayers.find((d) => d.id == opts.id)
);
svg.zoomablelayers[i] = opts;
}
}
// angle
const angle =
opts.rotate === null ? northangle(opts.pos, svg.projection) : opts.rotate;
// Symbol
let symbol =
"M 0.044958496 -17.812349 C -0.26996119 -17.812349 -0.55343697 -17.592424 -0.67127686 -17.257345 L -8.3333415 4.589384 C -8.4444814 4.9067336 -8.3867007 5.2688235 -8.1855469 5.5205933 C -8.036827 5.7055621 -7.8301427 5.8063639 -7.6176229 5.8063639 C -7.541523 5.8063639 -7.4648555 5.7942066 -7.3902466 5.7676066 C -2.7097153 4.1075403 2.3000786 4.1096862 7.5003174 5.7743245 C 7.7838471 5.8647245 8.0866488 5.7625043 8.2826986 5.5112915 C 8.4787474 5.2601218 8.5328574 4.9026136 8.4232585 4.589384 L 0.76119385 -17.257345 C 0.64335397 -17.592424 0.35987718 -17.812349 0.044958496 -17.812349 z M 0.050126139 -14.540715 L 6.4238973 3.6313029 C 6.4089973 3.6273029 6.393912 3.6251008 6.378422 3.6204508 C 0.64157284 -2.0936385 0.097483092 -4.360355 0.050126139 -14.540715 z M -3.3693034 -12.968201 C -9.1352526 -11.453015 -13.401249 -6.1970669 -13.401249 0.03824056 C -13.401249 4.5709812 -11.148377 8.6866709 -7.540625 11.145056 A 8.3801446 8.3801446 0 0 1 -7.0967244 10.275342 C -10.385638 7.9859154 -12.4349 4.2025426 -12.4349 0.03824056 C -12.4349 -5.659418 -8.5956532 -10.47465 -3.3693034 -11.964128 L -3.3693034 -12.968201 z M 3.5574056 -12.940812 L 3.5574056 -11.936739 C 8.7345144 -10.415833 12.526367 -5.6244081 12.526367 0.03824056 C 12.526367 4.1909026 10.478884 7.9752639 7.19646 10.26759 A 8.3801446 8.3801446 0 0 1 7.6558634 11.126453 C 11.247977 8.6637544 13.492716 4.5522912 13.492716 0.03824056 C 13.492716 -6.161767 9.2740539 -11.392436 3.5574056 -12.940812 z M -3.6158 7.7333781 C -3.7267299 7.7341681 -3.8385828 7.7529054 -3.9475627 7.7897054 C -4.3841612 7.9364343 -4.6793009 8.3455548 -4.6793009 8.8072144 L -4.6793009 18.45262 C -4.6793009 19.045959 -4.1988131 19.526457 -3.6054647 19.526457 C -3.0121153 19.526457 -2.5321452 19.045959 -2.5321452 18.45262 L -2.5321452 12.003918 L 2.8411702 19.10116 C 3.04802 19.374569 3.3674991 19.526973 3.6974487 19.526973 C 3.8112586 19.526973 3.9261433 19.508463 4.0385132 19.471163 C 4.4758327 19.324443 4.7702515 18.914787 4.7702515 18.453137 L 4.7702515 8.8072144 C 4.7702515 8.2138749 4.2890446 7.7338949 3.6964152 7.7338949 C 3.1037968 7.7338949 2.6230957 8.2138749 2.6230957 8.8072144 L 2.6230957 15.25695 L -2.7502197 8.1597087 C -2.9590285 7.8833089 -3.2829703 7.7303491 -3.6158 7.7333781 z ";
// Manage options
let entries = Object.entries(opts).map((d) => d[0]);
const layerattr = entries.filter(
(d) => !["mark", "id", "pos", "rotate", "scale"].includes(d)
);
// layer attributes
layerattr.forEach((d) => {
layer.attr(camelcasetodash(d), opts[d]);
});
// layer
layer
.append("path")
.attr("d", symbol)
.attr(
"transform",
`translate(${opts.pos[0]},${opts.pos[1]}) rotate(${angle}) scale(${opts.scale})`
);
// Output
if (newcontainer) {
return render(svg);
} else {
return `#${opts.id}`;
}
}