Skip to content

Commit

Permalink
Use TerritoryLevel as key for the world object
Browse files Browse the repository at this point in the history
  • Loading branch information
64json committed Jul 24, 2020
1 parent 7ce0242 commit 3049035
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 43 deletions.
2 changes: 1 addition & 1 deletion bin/combine_geo_data
Expand Up @@ -28,7 +28,7 @@ const outputPath = path.resolve(__dirname, '..', 'src', 'assets', 'world.json');
'region-code': continentId,
'region': continentName,
} of countryData) {
const geometry = countryGeometries.find(geometry => geometry.id === countryId);
const geometry = countryGeometries.find(geometry => geometry.id === countryId) ?? null;
if (geometry) {
delete geometry.properties;
}
Expand Down
32 changes: 11 additions & 21 deletions src/d3/geo-map.d3.ts
Expand Up @@ -2,7 +2,7 @@ import * as d3 from 'd3';
import { BaseD3, RenderOptions as BaseRenderOptions } from './base.d3';
import * as topojson from 'topojson';
import { Observable } from 'rxjs';
import { GeoDatum, Territory, TerritoryLevel } from '../datasets/queries/geo.query';
import { GeoDatum, TerritoryLevel } from '../datasets/queries/geo.query';
import * as GeoJSON from 'geojson';
import { GeometryCollection, MultiPolygon, Polygon } from 'topojson-specification';
import { isNotNullish, linearScale } from '../utils/misc';
Expand Down Expand Up @@ -93,7 +93,7 @@ export class GeoMapD3 extends BaseD3<RenderOptions> {

const countryGeometryCollection: GeometryCollection = {
type: 'GeometryCollection',
geometries: Object.values(world.countries).map(country => country.geometry).filter(isNotNullish),
geometries: Object.values(world[TerritoryLevel.COUNTRY]).map(country => country.geometry).filter(isNotNullish),
};

this.boundaryPath = this.svg
Expand Down Expand Up @@ -210,14 +210,18 @@ export class GeoMapD3 extends BaseD3<RenderOptions> {
const maxValue = data.reduce((acc, datum) => Math.max(acc, accessValue(datum)), 0);

for (const datum of data) {
const geometry = this.getGeometry(datum.territory);
const { territory } = datum;
const territoryObject = world[territory.level][territory.id];
const valueRatio = accessValue(datum) / maxValue;

if (geometry) { // for continents, subcontinents, and countries
const territoryPath = this.appendTerritoryPath(geometry, valueRatio);
this.territoryPaths.push(territoryPath);
if ('geometry' in territoryObject) { // for continents, subcontinents, and countries
const { geometry } = territoryObject;
if (geometry) {
const territoryPath = this.appendTerritoryPath(geometry, valueRatio);
this.territoryPaths.push(territoryPath);
}
} else { // for cities
const territoryCircle = this.appendCityCircle(world.cities[datum.territory.id], valueRatio);
const territoryCircle = this.appendCityCircle(territoryObject, valueRatio);
this.territoryCircles.push(territoryCircle);
}
}
Expand Down Expand Up @@ -248,18 +252,4 @@ export class GeoMapD3 extends BaseD3<RenderOptions> {
.attr('fill', colorPrimary)
.attr('stroke', '#FFF');
}

private getGeometry(territory: Territory) {
const { world } = this.renderOptions;
switch (territory.level) {
case TerritoryLevel.CONTINENT:
return world.continents[territory.id].geometry;
case TerritoryLevel.SUBCONTINENT:
return world.subcontinents[territory.id].geometry;
case TerritoryLevel.COUNTRY:
return world.countries[territory.id].geometry;
default:
return null;
}
}
}
30 changes: 18 additions & 12 deletions src/datasets/geo.dataset.ts
Expand Up @@ -5,8 +5,8 @@ import { activeUserMeasure, eventCountMeasure, revenueMeasure } from '../models/
import { createDefault } from '../utils/preferences';
import { generateCube } from '../models/data-cube/generation';
import { Category } from '../models/data-cube/types';
import { createGeoQuery } from './queries/geo.query';
import { City, Country, Subcontinent, World } from './geo.types';
import { createGeoQuery, TerritoryLevel } from './queries/geo.query';
import { City, Continent, Country, RawWorld, World } from './geo.types';
import { isNotNullish } from '../utils/misc';
import * as topojson from 'topojson';

Expand Down Expand Up @@ -47,7 +47,9 @@ export const configMeta: PreferenceMeta<Config> = {
};

export async function fetchWorld(): Promise<World> {
const world: World = (await import('../assets/world.json')) as any;
const world: RawWorld & {
continents: Record<string, Continent>
} = (await import('../assets/world.json')) as any;

Object.entries(world.continents).forEach(([continentId, continent]) => {
Object.entries(continent.subcontinents).forEach(([subcontinentId, subcontinent]) => {
Expand All @@ -69,37 +71,41 @@ export async function fetchWorld(): Promise<World> {

// merge geometries of subordinate territories
const countryGeometries = Object.values(subcontinent.countries).map(country => country.geometry).filter(isNotNullish);
subcontinent.geometry = countryGeometries.length ? topojson.mergeArcs(world.topology, countryGeometries) : undefined;
subcontinent.geometry = countryGeometries.length ? topojson.mergeArcs(world.topology, countryGeometries) : null;
});

// merge geometries of subordinate territories
const subcontinentGeometries = Object.values(continent.subcontinents).map(subcontinent => subcontinent.geometry).filter(isNotNullish);
continent.geometry = subcontinentGeometries.length ? topojson.mergeArcs(world.topology, subcontinentGeometries) : undefined;
continent.geometry = subcontinentGeometries.length ? topojson.mergeArcs(world.topology, subcontinentGeometries) : null;
});

function unwind<T, K extends keyof T>(parentObject: Record<string, T>, key: K) {
return Object.values(parentObject).reduce((acc, object) => ({ ...acc, ...object[key] }), {} as T[K]);
}

const { continents, topology } = world;

// flatten hierarchical data for each dimension
const subcontinents: Record<string, Subcontinent> = unwind(world.continents, 'subcontinents');
const subcontinents = unwind(continents, 'subcontinents');
const countries: Record<string, Country> = unwind(subcontinents, 'countries');
const cities: Record<string, City> = unwind(countries, 'cities');

return {
...world,
subcontinents,
countries,
cities,
[TerritoryLevel.CONTINENT]: continents,
[TerritoryLevel.SUBCONTINENT]: subcontinents,
[TerritoryLevel.COUNTRY]: countries,
[TerritoryLevel.CITY]: cities,
topology,
};
}

export async function create(config: Config): Promise<Dataset> {
const world = await fetchWorld();

const cities = world[TerritoryLevel.CITY];
const cityCategory: Category = {
name: 'city',
values: Object.entries(world.cities).map(([cityId, city]) => ({
values: Object.entries(cities).map(([cityId, city]) => ({
name: cityId,
weight: city.population,
})),
Expand All @@ -119,7 +125,7 @@ export async function create(config: Config): Promise<Dataset> {
createGeoQuery(
dataCube,
measures.map(measure => measure.name),
world.cities,
cities,
),
world,
);
Expand Down
22 changes: 13 additions & 9 deletions src/datasets/geo.types.ts
@@ -1,4 +1,5 @@
import { GeometryCollection, MultiPolygon, Polygon, Topology } from 'topojson-specification';
import { TerritoryLevel } from './queries/geo.query';

interface RawCity {
name: string;
Expand All @@ -10,7 +11,7 @@ interface RawCity {
interface RawCountry {
name: string;
cities: Record<string, RawCity>;
geometry?: Polygon | MultiPolygon;
geometry: Polygon | MultiPolygon | null;
}

interface RawSubcontinent {
Expand All @@ -23,7 +24,7 @@ interface RawContinent {
subcontinents: Record<string, RawSubcontinent>;
}

interface RawWorld {
export interface RawWorld {
continents: Record<string, RawContinent>;
topology: Topology<{ land: GeometryCollection }>;
}
Expand All @@ -43,17 +44,20 @@ export interface Country extends RawCountry {
export interface Subcontinent extends RawSubcontinent {
continentId: string;
countries: Record<string, Country>;
geometry?: MultiPolygon;
geometry: MultiPolygon | null;
}

export interface Continent extends RawContinent {
subcontinents: Record<string, Subcontinent>;
geometry?: MultiPolygon;
geometry: MultiPolygon | null;
}

export interface World extends RawWorld {
continents: Record<string, Continent>;
subcontinents: Record<string, Subcontinent>;
countries: Record<string, Country>;
cities: Record<string, City>;
export type TerritoryObject = Continent | Subcontinent | Country | City;

export interface World {
[TerritoryLevel.CONTINENT]: Record<string, Continent>;
[TerritoryLevel.SUBCONTINENT]: Record<string, Subcontinent>;
[TerritoryLevel.COUNTRY]: Record<string, Country>;
[TerritoryLevel.CITY]: Record<string, City>;
topology: Topology<{ land: GeometryCollection }>;
}

0 comments on commit 3049035

Please sign in to comment.