Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add <h3-worldmap-demo> <h3-worldmap-projection-select> #25

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@
/docs/node_modules/
custom-elements.json
# only generated for size check
h3-worldmap.bundled.js
h3-worldmap.bundled.js

# mac only
.DS_Store
72 changes: 72 additions & 0 deletions dev/h3-worldmap-demo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/** @prettier */
/**
* @license
* Copyright 2022 Olivier Lange & Rudi Farkas
* SPDX-License-Identifier: BSD-3-Clause
*/

import {LitElement, html, css} from 'lit';
import {H3Worldmap} from '../../h3-worldmap';
import {H3WorldmapProjectionSelect} from '../h3-worldmap-projection-select';
import {PROPS_DEFAULTS} from '../src/defs/defaults.js';
/**
* The root class of the demo application.
*/
export class H3WorldmapDemo extends LitElement {
static get styles() {
return css`
:host {
display: block;
height: 100%;
font-size: 1.5rem;
}

div.debug {
border-style: none; // dashed
border-color: red;
}
`;
}

constructor() {
super();
this._selectedProjection = PROPS_DEFAULTS.PROJECTION;
}

static get properties() {
return {
_selectedProjection: {type: String, state: true},
};
}

selectedChanged = (e) => {
this._selectedProjection = e.detail.value;
};

areas = [
// '80abfffffffffff',
'80e1fffffffffff',
'80a5fffffffffff',
'8035fffffffffff',
'801ffffffffffff',
];

render() {
return html` <div class="debug">
<h3-worldmap
projection="${this._selectedProjection}"
.areas="${this.areas}"
geometry-src="land-50m.json"
geometry-coll="land"
>
</h3-worldmap>
</p>
<h3-worldmap-projection-select
.selected="${this._selectedProjection}"
@update-selected="${this.selectedChanged}"
></h3-worldmap-projection-select>
</div>`;
}
}

customElements.define('h3-worldmap-demo', H3WorldmapDemo);
5 changes: 5 additions & 0 deletions dev/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<script src="../node_modules/@webcomponents/webcomponentsjs/webcomponents-loader.js"></script>
<script src="../node_modules/lit/polyfill-support.js"></script>
<script type="module" src="../h3-worldmap.js"></script>
<script type="module" src="../dev/h3-worldmap-demo.js"></script>
<style>
body { font-family: 'Georgia'; font-size: 1rem; }
h3-worldmap {
Expand All @@ -17,6 +18,10 @@
</style>
</head>
<body>
<h3-worldmap-demo> </h3-worldmap-demo>
<p>This is not child content</p>

<!-- TODO REMOVE THIS -->
<h3-worldmap
projection="naturalEarth"
areas='[
Expand Down
109 changes: 109 additions & 0 deletions h3-worldmap-projection-select.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/** @prettier */
/**
* @license
* Copyright 2022 Olivier Lange & Rudi Farkas
* SPDX-License-Identifier: BSD-3-Clause
*/

import {LitElement, html, css} from 'lit';

import {AVAILABLE_PROJECTIONS} from './src/defs/projections.js';

/**
* convert AVAILABLE_PROJECTIONS to an object like this:
* {
* conicEqualArea: 'Conic Equal-Area',
* orthographic: 'Orthographic',
* naturalEarth: 'Natural Earth',
* stereographic: 'Stereographic',
* gnomonic: 'Gnomonic',
* mercator: 'Mercator',
* }
*/
const projectionOptions = Object.fromEntries(
[...AVAILABLE_PROJECTIONS.entries()].map(([projId, projProps]) => {
return [projId, projProps.name];
})
);

/**
* Dropdown menu selector for the projection.
* Inputs:
* - selectedProjection: the current projection
* Outputs:
* - update-selected (custom event): dispatched when the user selects a new projection
*/
export class H3WorldmapProjectionSelect extends LitElement {
static styles = css`
:host {
padding: 10px;
}

div.wrap {
color: var(--primary-color);
border: 3px solid var(--primary-color);
border-radius: 1rem;
padding: 1rem 1.5rem;
}

.select {
color: var(--primary-color);
font-size: 1.5rem;
}
`;

static get properties() {
return {
label: {type: String},
options: {type: Object},
selected: {type: String},
};
}

constructor() {
super();
this.label = 'Projection:';
this.options = projectionOptions;
this.selected = undefined;
}

firstUpdated() {
this.setSelected('id-select', this.selected);
}

optionsView(options) {
const keys = Object.keys(options);
return html`
${keys.map(
(key) => html`<option value="${key}">${options[key]}</option>`
)}
`;
}

setSelected(id, value) {
let element = this.shadowRoot.getElementById(id);
element.value = value;
}

_updateSelected(e) {
this.dispatchEvent(
new CustomEvent('update-selected', {detail: {value: e.target.value}})
);
}

render() {
return html`
<div class="wrap">
<label> ${this.label} </label>
<select class="select" id="id-select" @change="${this._updateSelected}">
${this.optionsView(this.options)}
</select>
</div>
`;
}
}

customElements.define(
'h3-worldmap-projection-select',
H3WorldmapProjectionSelect
);
39 changes: 33 additions & 6 deletions src/defs/projections.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,32 @@
import * as d3 from 'd3';

/**
* Projection definition object. Holds a human representation of
* the name of a D3-geo projection, along with its _builder function_
* from D3-geo.
*
* Note: a projection _builder function_ is a pattern from D3-geo,
* which declares projections as functions, that can be instantiated
* and later configured for a particular view (scale, center, rotate).
* The function object indeed holds various instance properties.
*
* The pattern isn't explicitely named as such within the docs of
* D3-geo, it is a convention of ours.
*/
class ProjDef {
constructor(name, ctorFn) {
if( typeof ctorFn !== "function") {
throw new TypeError( `Expected a projection function from D3-geo, got ${ctorFn}`)
}
this.name = name;
this.ctorFn = ctorFn;
}

toString() {
return `ProjDef { name: ${this.name}, builderFn: ${this.builderFn} }`;
}
}

/**
* Map of the projection identifiers, which are the possible values of the
* 'projection' attribute of our custom element, with their corresponding
Expand All @@ -12,11 +39,11 @@ import * as d3 from 'd3';
* @default
*/
export const AVAILABLE_PROJECTIONS = new Map([
[ "conicEqualArea", { name: "Conic equal-area", ctorFn: d3.geoConicEqualArea } ],
[ "orthographic", { name: "Orthographic", ctorFn: d3.geoOrthographic } ],
[ "naturalEarth", { name: "Natural Earth", ctorFn: d3.geoNaturalEarth1 } ],
[ "stereographic", { name: "Stereographic", ctorFn: d3.geoStereographic } ],
[ "gnomonic", { name: "Gnomonic", ctorFn: d3.geoGnomonic } ],
[ "mercator", { name: "Mercator", ctorFn: d3.geoMercator } ],
[ "conicEqualArea", new ProjDef( "Conic equal-area", d3.geoConicEqualArea) ],
[ "orthographic", new ProjDef( "Orthographic", d3.geoOrthographic) ],
[ "naturalEarth", new ProjDef( "Natural Earth", d3.geoNaturalEarth1) ],
[ "stereographic", new ProjDef( "Stereographic", d3.geoStereographic) ],
[ "gnomonic", new ProjDef( "Gnomonic", d3.geoGnomonic) ],
[ "mercator", new ProjDef( "Mercator", d3.geoMercator) ],
// TODO: complete list from https://observablehq.com/@d3/projection-comparison
]);