Skip to content

Commit

Permalink
perf(map): optimize geoPathsFor method
Browse files Browse the repository at this point in the history
  • Loading branch information
marcelgerber committed Dec 5, 2022
1 parent 1c1f54f commit 6fded52
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 18 deletions.
31 changes: 13 additions & 18 deletions packages/@ourworldindata/grapher/src/mapCharts/MapChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
DEFAULT_BOUNDS,
flatten,
getRelativeMouse,
identity,
sortBy,
guid,
minBy,
Expand All @@ -23,7 +22,11 @@ import {
HorizontalColorLegendManager,
HorizontalNumericColorLegend,
} from "../horizontalColorLegend/HorizontalColorLegends"
import { MapProjectionName, MapProjectionGeos } from "./MapProjections"
import {
MapProjectionName,
MapProjectionGeos,
PathToStringWithRoundedNumbersContext,
} from "./MapProjections"
import { select } from "d3-selection"
import { easeCubic } from "d3-ease"
import { MapTooltip } from "./MapTooltip"
Expand Down Expand Up @@ -96,25 +99,17 @@ const geoPathCache = new Map<MapProjectionName, string[]>()
const geoPathsFor = (projectionName: MapProjectionName): string[] => {
if (geoPathCache.has(projectionName))
return geoPathCache.get(projectionName)!
const projectionGeo = MapProjectionGeos[projectionName]
const strs = GeoFeatures.map((feature) => {
const s = projectionGeo(feature) as string
const paths = s.split(/Z/).filter(identity)

const newPaths = paths.map((path) => {
const points = path.split(/[MLZ]/).filter((f: any) => f)
const rounded = points.map((point) =>
point
.split(/,/)
.map((v) => parseFloat(v).toFixed(1))
.join(",")
)
return "M" + rounded.join("L")
})

return newPaths.join("Z") + "Z"
const ctx = new PathToStringWithRoundedNumbersContext()
const projectionGeo = MapProjectionGeos[projectionName].context(ctx)
const strs = GeoFeatures.map((feature) => {
ctx.beginPath() // restart the path
projectionGeo(feature)
return ctx.result()
})

projectionGeo.context(null) // reset the context for future calls

geoPathCache.set(projectionName, strs)
return geoPathCache.get(projectionName)!
}
Expand Down
32 changes: 32 additions & 0 deletions packages/@ourworldindata/grapher/src/mapCharts/MapProjections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
geoConicConformal,
geoAzimuthalEqualArea,
GeoPath,
GeoContext,
} from "d3-geo"

import { geoRobinson, geoPatterson } from "./d3-geo-projection.js"
Expand Down Expand Up @@ -84,3 +85,34 @@ export const MapProjectionGeos: { [key in MapProjectionName]: GeoPath } = {
.parallels([-10, -30])
),
} as const

// Can be used as a d3 projection context to convert a geojson feature to a SVG path
// In contrast to what d3-geo does by default, it will round all coordinates to one decimal place
// Adapted from https://github.com/d3/d3-geo/blob/8d3f3a98c034b087e2c808f752d2381d51c30015/src/path/string.js
export class PathToStringWithRoundedNumbersContext implements GeoContext {
_string: string[] = []

beginPath(): void {
this._string = []
}

moveTo(x: number, y: number): void {
this._string.push("M" + x.toFixed(1) + "," + y.toFixed(1))
}

lineTo(x: number, y: number): void {
this._string.push("L" + x.toFixed(1) + "," + y.toFixed(1))
}

arc(_x: number, _y: number, _radius: number): void {
throw new Error("Method not implemented.")
}

closePath(): void {
this._string.push("Z")
}

result(): string {
return this._string.join("")
}
}

0 comments on commit 6fded52

Please sign in to comment.