æ£å°åœ±ïŒçäœãã¥ãŒïŒã«ç¹åãã WebGL2 ãããã¬ã³ããªã³ã°ãã¬ãŒã ã¯ãŒã¯
Worker ããŒã¹äžŠåãã€ãã©ã€ã³ · OffscreenCanvas · Gint / GeoPBF / AltPBF çµ±å
OrthoMap ã¯ã¡ã€ã³ã¹ã¬ããã«æåœ±æŒç®ã»ã€ãã³ãåŠçã»ã¬ã€ã€ãŒç®¡çã®ã¿ã眮ãã ãã¹ãŠã®æç»ã OffscreenCanvas + Web Worker ã«å§è²ããèšèšã§ããã åã¬ã€ã€ãŒã¯ç¬ç«ãã Worker äžã§åäœããã¡ã€ã³ã¹ã¬ãããžã®æç»ããããã³ã°ããŒãã«ãªãã
åæåãããŒã¯ 3 ã¹ãããã§å®çµããã
// index.js â ãšã³ããªãŒãã€ã³ã
const map = await orthoMap({
target: d3.select("#container"), // çç¥æã¯ body
baseName: "osm.street",
center: [139.7, 35.7],
zoom: 4,
accessories: { stars: { maxCount: 500 } },
gadgets: false,
});
// å
éšã·ãŒã±ã³ã¹:
// 1. orthographic(map, opts) â æåœ±ã»ã€ãã³ãã»æäœç³»ã»ããã¢ãã
// 2. createLayers(map, opts) â Worker èµ·åã»3 ã¬ã€ã€ãŒçæ
// 3. createGadgets(map) â UI ã³ã³ããŒãã³ãç»é²
D3 ã® geoOrthographic ã¯çäœã®ååŸãã¹ã¯ãªãŒã³ãã¯ã»ã«ã§è¡šã scale ãæã€ã
OrthoMap ã¯ããããŠã§ãå°å³æšæºã®ãºãŒã ã¬ãã«ã«å€æããã
ãºãŒã 5.5ïŒthresholdïŒãå¢ã«ããã以äžã¯ããŒã¹çäœç»åã®ã¿ããã以äžã¯ã¹ã©ã€ãã«ã¿ã€ã«ãéããã
ããŠã¹ãã©ãã°ã»ãã³ãæäœã¯ D3 ã® zoom ã€ãã³ããããŒã¹ãšãã
çé¢äžã®æ£ç¢ºãªè¿œåŸã®ããã« versorïŒåäœåå
æ°ïŒ ãçšããã
// ãã©ãã°éå§: ã¿ããç¹ã®çé¢åº§æšããã«ã«ã3Dãž
v0 = cartesian(proj.invert(pt0)); // [lng,lat] â [x,y,z]
q0 = versor(r0 = proj.rotate()); // çŸåšå転ãåå
æ°å
// ãã©ãã°äž: çé¢äžã®æå°å転ãåå
æ°åæ
const v1 = cartesian(proj.rotate(r0).invert(pt));
const q1 = multiply(q0, delta(v0, v1));
proj.rotate(rotation(q1));
2 ç¹ãã³ãæã¯æã®è§åºŠå·®ã远å ã®åå æ°ã§åæããããšã§ Z 軞å転ïŒå°å³ã®åŸãïŒ ãåæã«æäœã§ããã
Ctrl ãŸã㯠Meta ããŒæŒäžæã®ãã€ãŒã«ã¯ ã«ãŒãœã«äœçœ®ãäžå¿ã«çã Z 軞å転ãããã
ã«ãŒãœã«ã®ç»é¢åº§æšããã«ãŒãœã«ãŸã§ã®çé¢ãã¯ãã« [x,y,z] ãæ±ãã
ãããå転軞ãšããåå
æ°ãçŸåšã®å転ã«åæããã
åçŽãª camera rotate ã§ã¯ãªãçäœäžã®å®ç¹ã軞ãšããç©ççãªå転ã§ããã
äž¡é¢æ°ã¯ã¢ãã¡ãŒã·ã§ã³äžã«å¥æäœãå²ã蟌ãã å Žåã«å³ãã£ã³ã»ã«ã§ãããã flyTicketïŒå調å¢å ã«ãŠã³ã¿ïŒã§å¶åŸ¡ããã
| 颿° | ã¢ã«ãŽãªãºã | ç¹åŸŽ |
|---|---|---|
flyToFeature |
d3.interpolateZoomïŒãºãŒã ã¢ãŠãâç§»åâãºãŒã ã€ã³ïŒ+ ç·åœ¢ã€ãŒãžã³ã° | é·è·é¢ç§»åæã«ã空ãé£ã¶ããããªåŒ§ãæã |
zoomToFeature |
2 ãã§ãŒãºïŒâ threshold zoom ãŸã§å転移åãâ¡ ç®çå°ã§ãºãŒã ã€ã³ | ã¿ã€ã«äžèŠé åã§ã®ç§»å â å°çåŸã®ãºãŒã ã€ã³ã§ã¿ã€ã«èªã¿èŸŒã¿ãæå°å |
// zoomToFeature â 2ãã§ãŒãºæ§æ
// Phase1: ç§»åäžã¯ zoom 5.5 ãŸã§ïŒtravelMs â è·é¢ à 2500msïŒ
const sTrav = min(s1, zval2scale(5.5));
// Phase2: å°çåŸ 2000ms ã§ãºãŒã ã€ã³ïŒ600ms ãªãŒããŒã©ããïŒ
proj.scale(s0 + (sTrav - s0) * tRotEased // ç§»åãã§ãŒãº
+ (s1 - sTrav) * easeCubicInOut(tZoom)); // ãºãŒã ãã§ãŒãº
D3 Dispatch ãããŒã¹ã«ããŠãŒã¶ãŒç»é²ïŒç¡åïŒãšã·ã¹ãã ç»é²ïŒååä»ãïŒãåãã API ãæäŸããã
// ãŠãŒã¶ãŒã€ãã³ãç»é² â èªåæ¡çªã§ããŒã ã¹ããŒã¹ç®¡ç
const handler = map.onClick(e => console.log(e.lng, e.lat));
handler.destroy(); // ç»é²è§£é€
// ã€ãã³ãçš®é¡
// Enter / Move / Leave / Drop / Click / ContextMenu
// Drawing / Drawn / Resize / Change / LoadStart / LoadEnd
// Move / Click ã«ã¯æšé«ãä»ãïŒAltPBF çµ±åïŒ
// e = { lng, lat, alt, x, y, shiftKey, metaKey }
åã¬ã€ã€ãŒã¯ OffscreenCanvas.transferControlToOffscreen() ã§ Canvas ã®æææš©ã Worker ã«ç§»è²ãã
ã¡ã€ã³ã¹ã¬ãããšã¯ postMessage ã®ã¿ã§éä¿¡ãããæç»ã«ãŒãå
šäœã Worker å
ã§å®çµããã
init â offscreen / dpr / workersset â ããŒã¿æå
¥ïŒcmd å¥ïŒdrawing â scale / rotate / panningdrawn â ãã³çµäºåŸã®ç¢ºå®æç»move â ã«ãŒãœã«åº§æšresize â width / heightdestroy â Worker çµäº{action:"done", type:"init"}{action:"done", type:"set"}{action:"done", type:"resize"}{action:"identify", featureId}{action:"click", featureId, lng, lat}{action:"redraw"} â context restored åŸ
drawing ã¯ãã³ã»ãºãŒã äžã«ãã¬ãŒã ããšã«é£ã¶ã峿æç»ãåœä»€ã§ããã
ç°¡æå質ã§ã®é«éæç»ãç®çãšããïŒã¿ã€ã«ã®æ°èŠèªã¿èŸŒã¿ã¯ããªãïŒã
drawn ã¯ãã€ã³ã¿ãæ¢ãŸã£ããšãã«äžåºŠã ãéããããç¢ºå®æç»ãåœä»€ã§ã
FBO ãžã®é«å質ã¬ã³ããªã³ã°ã»picking ãããã¡æŽæ°ãªã©éãåŠçã¯ããã§è¡ãã
set ã¡ãã»ãŒãžã¯æç»çšãã€ããªããŒã¿ãå«ãå ŽåãTransferable ãšããŠéãããšã§ãŒãã³ããŒè»¢éããã
// rawBuffers ãå«ãŸããå Žåã®ã¿ Transferable 転é
if (prop?.rawBuffers) {
const { rawBuffers, ...rest } = prop;
worker.postMessage({ type: "set", cmd, data, prop: rest, rawBuffers }, rawBuffers);
}
base.js Worker 㯠å
šççç©åœ¢ç»åïŒ8192Ã4096ïŒ ããã¯ã¹ãã£ãšããŠçäœã«è²Œãä»ãã
ãºãŒã ã€ã³æã¯ã¹ã©ã€ãã«ã¿ã€ã«ãäžããéãã 2 æ®µæ§æãåãã
ãã©ã°ã¡ã³ãã·ã§ãŒããŒå ã§ãã¯ã»ã«ã®ç»é¢åº§æšããçé¢åº§æšãéç®ããçç©åœ¢ãã¯ã¹ãã£ãçŽæ¥ãµã³ããªã³ã°ããã CPU åŽã§ã®æåœ±å€æãããŒããã¯ã¹ãããã¡ã¯äžèŠã«ãªãã
// GLSL Fragment Shader (baseFs) â æ£å°åœ±ã®é倿
float x = (gl_FragCoord.x - translate.x) / scale;
float y = (translate.y - gl_FragCoord.y) / scale;
float rho = sqrt(x*x + y*y);
if (rho <= 1.0) { // çäœåå
ã®ã¿
float c = asin(rho);
float lng = atan(x * sin(c), rho * cos(c));
float lat = asin(y * sin(c) / rho);
applyRotation(rx, ry, rz, lng, lat); // ãŠãŒã¶ãŒå転ãé©çš
vec2 uv = vec2((lng + pi) / (2.0*pi), (lat + pi/2.0) / pi);
outColor = texture(u_image, uv);
} else { discard; } // çäœå€ã¯ãã©ã°ã¡ã³ãç Žæ£
ããŒã¹ãã¯ã¹ãã£ã¯çµåºŠæ¹åïŒS 軞ïŒã REPEATã緯床æ¹åïŒT 軞ïŒã CLAMP_TO_EDGE ã«èšå®ããã
ããã«ããçµåºŠ ±180° ã®æ¥ä»å€æŽç·ãè·šãæç»ã§ã·ãŒã ãçºçããªãã
ã¿ã€ã«ãã¯ã¹ãã£ã¯é£ã¿ã€ã«ãšã®æ»²ã¿ãé²ããã䞡軞 CLAMP_TO_EDGEã
ã¿ã€ã« Worker ããŒã«ïŒããã©ã«ã 4 䞊åïŒã URL çµç±ã§ç»åã fetch ããImageBitmap ã«å€æããŠ
createTileTexture ã§ GL ãã¯ã¹ãã£åããã
// ã¿ã€ã«åªå
床: ç»é¢äžå€®ã«è¿ãã¿ã€ã«ãå
ã«ããŒã
ents.sort((a, b) => a[4] - b[4]) // [4] = äžå¿è·é¢
.forEach(([x, y, zo, pos, dist]) => {
// zo = 極æ¹åãªãã»ããïŒY2T/Y4T ããŒãã«ã§æ±ºå®ïŒ
// åžæãºãŒã ã«ãªããã° z-1, z-2, z-3 ã®ãã£ãã·ã¥ã代çš
for (let dz of [0, 1, 2, 3]) {
const z = Z0 - (zo + dz); if (z < 0) continue;
const img = TileTub.get(name);
if (img) { /* UV ã芪ã¿ã€ã«é åã«å€æããŠæç» */ return; }
}
});
Y2T / Y4T ããŒãã«: é«ç·¯åºŠã¿ã€ã«ã¯ Mercator æåœ±ã®æªã¿ã«ãã衚瀺é¢ç©ã極端ã«å°ãããªãã ãããè£æ£ãããããºãŒã ã¬ãã«ããšã«ç·¯åºŠæ¹åã® LOD ãªãã»ããïŒzoïŒãå®çŸ©ãã宿°ããŒãã«ã§ã 極ä»è¿ã¿ã€ã«ã 1ã2 ãºãŒã èœãšããŠããŒãããã
| Name | ããŒã¹çäœç»å | ã¿ã€ã«ãœãŒã¹ | MaxZoom |
|---|---|---|---|
whiteEarth | whiteEarth.webp | ãªã | 7 |
google.street | naturalEarth.webp | Google MapsïŒlyrs=mïŒ | 22 |
google.satellite | google.satellite.webp | Google MapsïŒlyrs=sïŒ | 22 |
osm.street | naturalEarth.webp | OpenStreetMap JP | 19 |
osm.satellite | osm.satellite.webp | Bing MapsïŒååæšã€ã³ããã¯ã¹ïŒ | 19 |
cyberjapan.std | naturalEarth.webp | åœåå°çé¢ïŒæ¥æ¬åïŒ+ OSMïŒåå€ïŒ | 19 |
Firefox ã§ã¯ createImageBitmap(blob) ã®çµæã WebGL ãã¯ã¹ãã£ãšããŠæ£åžžã«æ±ããªãåé¡ãããã
UA å€å®ã«ãã OffscreenCanvas ãäžéãããã¡ãšããŠæç»ãã
transferToImageBitmap() ã§å倿ãã workaround ãå
¥ã£ãŠããã
gintBorder.js 㯠Gint ã®æç»æ©èœã®ãã¡ ã©ã€ã³æç»ã®ã¿ ãå®è£
ãã軜éçã§ããã
hover pickingã»ããªãŽã³å¡ãã»FBO ã¯æãããåºå®ã¹ã¿ã€ã«ã®è€æ°ããŒã¿ã»ããã 1 ãã¹ã§æãã
// 4 ã¬ã€ã€ãŒã® Gint ããŒã¿ïŒGeoPBF çµç±ã§ããŒãïŒ
const geoNames = [
"ne_110m_graticules_10", // çµç·¯ç· 10°ã°ãªãã
"ne_50m_admin_0_boundary_lines_land", // åœå¢ç·
"ne_50m_admin_0_boundary_lines_maritime_indicator", // æµ·äžå¢ç
"ne_50m_geographic_lines", // èµ€éã»ååž°ç· ç
];
// åããŒã¿ã»ããã®æç»ã¹ã¿ã€ã«ïŒããã·ã¥ã¯ã¹ã¯ãªãŒã³ãã¯ã»ã«åºå®ïŒ
const BORDER_GL_STYLES = [
{ color: [1.0, 1.0, 1.0, 0.6], lineWidth: 1.0, dash: [0, 0] }, // graticule (solid)
{ color: [1.0, 1.0, 1.0, 1.0], lineWidth: 1.0, dash: [4, 2] }, // åœå¢ç·
{ color: [0.5, 0.5, 1.0, 0.8], lineWidth: 0.8, dash: [4, 2] }, // maritime
{ color: [1.0, 1.0, 1.0, 0.6], lineWidth: 0.8, dash: [4, 2] }, // geographic
];
ããã·ã¥é·ã¯ãºãŒã ã«é¢ä¿ãªãåžžã«åãèŠèŠãµã€ãºã«ãªããããé ç¹ã·ã§ãŒããŒå
ã§
v_dist_base = vertex_index à scale à sin(1°) ãšããŠçޝç©è·é¢ãèšç®ããã
ã¹ã¯ãªãŒã³åº§æšã§ã®ãã¯ã»ã«è·é¢ãäžå®ãªã®ã§ããºãŒã å€åã§ããã·ã¥ãã¿ãŒã³ãå€åœ¢ããªãã
ã©ã€ã³å士ãéãªã£ãéã«åéæã®ã¢ã«ãã¡ãç©ã¿éãªã£ãŠããŸãåé¡ãé²ãããã
blendFuncSeparate ã§ã«ã©ãŒãã£ã³ãã«ãš alpha ãã£ã³ãã«ã®ãã¬ã³ããåé¢ããã
gl.blendFuncSeparate(
gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, // RGB: éåžžã¢ã«ãã¡ãã¬ã³ã
gl.ONE, gl.ONE_MINUS_SRC_ALPHA // A: å
alpha ãä¿æ
);
ã¹ãã³ã·ã«ãã¹çšã®é ç¹ã·ã§ãŒããŒã¯ãå°çã®è£é¢ïŒz < 0ïŒã«åã£ãé ç¹ã NDC åç¹ (0,0) ã«æœ°ãããå°å¹³ç·åïŒååŸ = u_scaleïŒã®äžã«æŒãåºãã ããã«ããåœå¢ç·ãå°å¹³ç·ããŸããããªãŽã³ã§ãéããæåœ¢ãæ£ããæ§æããã å¡ãã€ã¶ãã«ç Žç¶»ãçããªãã
// GLSL VS_STENCIL â è£é¢é ç¹ã®å°å¹³ç·æŒãåºã
if (p.z < 0.0) {
vec2 v = p.xy - u_viewport * 0.5;
float d = length(v);
gl_Position = d < 1e-4
? vec4(0.0, 0.0, 0.0, 1.0) // åç¹çŽäžã¯åç¹ãž
: toNDC(u_viewport * 0.5 + v * (u_scale / d)); // å°å¹³ç·åäžãž
return;
}
border.js Worker 㯠Canvas2D ã³ã³ããã¹ãã§åäœãã
倩æããŒã¿ã»å°å³è¡šç€ºè£å©æ
å ±ã 1 ç§ã€ã³ã¿ãŒãã«ã¿ã€ããŒã§æŽæ°ããã
æã»æåº§ç·ã®æç»ã«ã¯çŸåšæå»ã«å¯Ÿå¿ãã空ã®åããå¿ èŠã§ãGST ãèšç®ããã
èµ€çµïŒRight AscensionïŒãã GST ãåŒããæè§ïŒHour AngleïŒã䜿ããçé¢å€©æåº§æšãã ã¹ã¯ãªãŒã³ 2D 座æšã«å€æããã
ææã«ã¿ãã°ïŒstars.6ïŒã®åæãèµ€çµã»èµ€ç·¯ â ã¹ã¯ãªãŒã³åº§æšã«å€æãã
B-V ã«ã©ãŒã€ã³ããã¯ã¹ããè²ã決å®ãççŽããååŸãšéæåºŠãèšç®ããã
// B-V â æã®è²ïŒæž©åºŠã¹ãã¯ãã«ïŒ
const bvColor = bv =>
bv < -0.3 ? "#b2c8ff" : // O å â éçœ
bv < 0.0 ? "#d9e2ff" : // B å â éçœ
bv < 0.3 ? "#f8faff" : // A å â çœ
bv < 0.6 ? "#fff8f0" : // F å â é»çœ
bv < 0.8 ? "#fff2c8" : // G å â é»ïŒå€ªéœïŒ
bv < 1.1 ? "#ffe0b5" : // K å â æ©
"#ffab91"; // M å â èµ€
// ççŽ â ååŸã»éæåºŠ
radius = (9 - mag) * 0.20; // æããæã»ã©å€§ãã
alpha = 1 - mag / 15; // æããæã»ã©äžéæ
æåº§ç·ã¯èµ€çµã»èµ€ç·¯ãã¢ã®ãªã¹ãïŒMultiLineStringïŒãšããŠæ ŒçŽãããŠããã çé¢åº§æš â ãã«ã«ã3D â çé¢æåœ±å€æã§åç¹ã®å¯èŠæ§ãå€å®ããŠããæç»ããã
// èµ€éåº§æš (ra, dec) â çé¢æåœ± (px, py)
l = ra - skyRot; // æè§
z = sinÏ*sinÎŽ + cosÏ*cosÎŽ*cos(l); // z < 0 ãªãå°å¹³ç·ä»¥äž
x = cosÎŽ * (-sin(l));
y = cosÏ*sinÎŽ - sinÏ*cosÎŽ*cos(l);
px = cx + sr * (x*cosγ - y*sinγ); // sr = çäœååŸãã倧ããªã¹ã«ã€ããŒã ååŸ
py = cy - sr * (x*sinγ + y*cosγ);
倩äœã®çš®é¡ïŒtypeïŒã«å¿ããŠã·ã³ãã«ãæãåããã
| type | åç§° | ã·ã³ãã« |
|---|---|---|
gc | çç¶æå£ | å + åå |
gx / gg | éæ²³ | æ¥å |
oc | æ£éæå£ | ç Žç·å |
| ãã®ä» | æé² ç | ç©åœ¢ |
ç»é¢å蟺ãå 30 ç¹ãã€ãµã³ããªã³ã°ïŒèš 120 ç¹ïŒããŠå°ç座æšã«é倿ãã ããã°ããŒãæåœ±ã«åæåœ±ããå€è§åœ¢ãšããŠèŠéé åïŒreticleïŒãæç»ããã
// viewport å蟺ã®å°ç座æšãµã³ããªã³ã°
const N = 30;
const edge = (x0, y0, x1, y1) =>
Array.from({ length: N }, (_, i) => {
const t = i / N;
return proj.invert([x0 + (x1-x0)*t, y0 + (y1-y0)*t]);
});
const geoPts = [...edge(0,0,w,0), ...edge(w,0,w,h), ...edge(w,h,0,h), ...edge(0,h,0,0)];
| æ©èœ | å 容 |
|---|---|
| å€åŽïŒnightïŒ | 倪éœèµ€ç·¯ã»çµåºŠããçµç«¯ç·ã 31 ç¹ã®ããªãŽã³ã§èšç®ãåéæã®æè²ã§å¡ãã€ã¶ã |
| æèšïŒclockïŒ | UTC ã倧ããªãã©ã³ãã§è¡šç€ºããºãŒã ã¢ãŠãæïŒzoom < 2ïŒã®ã¿ |
| 座æšè¡šç€ºïŒlatlngïŒ | ã«ãŒãœã«ç§»åã§ç·¯åºŠã»çµåºŠã»æšé«ããªã¢ã«ã¿ã€ã æŽæ° |
| ã¹ã±ãŒã«ããŒïŒscaleïŒ | log10 äžžã + åäœèªåéžæïŒm/km åãæ¿ããmetric/imperial 察å¿ïŒ |
| åž°å±è¡šç€ºïŒcreditïŒ | åããŒã¹ãããã®ã©ã€ã»ã³ã¹è¡šèšïŒattribution ããããã£ããïŒ |
gint.js Worker ã¯ãŠãŒã¶ãŒã createRemoteLayer({ type:"gint" }) ã§çæããæ±çšã¬ã€ã€ãŒã§ã
GeoPBF ç±æ¥ã® Gint ãã€ããªãåãåããã©ã€ã³ã»ããªãŽã³ã»ãã€ã³ããæç»ããã
hover pickingã»ã¯ãªãã¯ã»ãã€ã©ã€ãã»ããªãŽã³å¡ãã€ã¶ãããã¹ãŠãµããŒãããã
| ãã¡ã€ã« | åœ¹å² |
|---|---|
gintState.js | å
±æ GL ç¶æ
ãªããžã§ã¯ã (s) ã®å®çŸ© |
gintPrograms.js | GLSL ã·ã§ãŒããŒã®ã³ã³ãã€ã«ã»ãªã³ã¯ã»ãŠããã©ãŒã ååŸ |
gintTextures.js | arcTex / metaTex / ptTex ãã¯ã¹ãã£çæã»åé€ |
gintFBO.js | baseFBOïŒã¯ãªãŒã³ã·ãŒã³çšïŒã»pickFBOïŒãããã³ã°çšïŒã®çæã»åé€ |
gintRenderPasses.js | renderCleanScene / drawOverlay / renderPickingBuffer ã® 3 ãã¹ |
gintIdentify.js | GPU picking + JS ããªãŽã³å å å€å®ãhover / leave ãã³ãã© |
// ãããã³ã° FBO ã«ãã£ãŒãã£ãŒ ID ã RGB 24bit ã§ãšã³ã³ãŒã
uint fid1 = meta.a + 1u; // 0 = "ããããªã" â +1 ããŠããã
v_color = vec4(
float(fid1 & 255u) / 255.0,
float((fid1 >> 8u) & 255u) / 255.0,
float((fid1 >> 16u) & 255u) / 255.0,
1.0
);
// readPixels ã§ã«ãŒãœã« 1px ãèªã¿åã
gl.readPixels(pickX, pickY, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, px);
const fid1 = px[0] | (px[1] << 8) | (px[2] << 16);
const featureId = fid1 === 0 ? null : fid1 - 1;
GPU picking ããããããªãïŒfid1 === 0ïŒãã€ããªãŽã³ããŒã¿ãååšããå Žåã
ã«ãŒãœã«ã®å°ç座æšã Morton æŽæ°åº§æšã«å€æã㊠findPolygonïŒgeopbfïŒãåŒã¶ã
çž®å°è¡šç€ºã§ã°ããŒããå°ããå
šã³ãŒããŒãçäœå€ã«ãªã lastViewBbox = null ã«ãªãå Žåãåäœããã
// activeId ãå€åãããšãïŒhover æ€åºåŸïŒ
// 1. baseFBO â canvas ãž blitïŒã¯ãªãŒã³ã·ãŒã³ã埩å
ïŒ
gl.blitFramebuffer(0, 0, w, h, 0, 0, w, h, gl.COLOR_BUFFER_BIT, gl.NEAREST);
// 2. ã¢ã¯ãã£ã feature ã®ãšããžã倪ãïŒé»è²ã§äžæžãïŒu_pass=1ïŒ
gl.uniform1f(uRender.u_line_width, lineWidth + 2.0);
gl.uniform1i(uRender.u_active_id, activeId);
// 3. ããªãŽã³ç¯å²ãæ¢ç¥ãªã stencil ã§ã¢ã¯ãã£ãå€ã dim
// ïŒmaskColor ã® alpha > 0 ã〠polyEdgeByFid ã«ç¯å²ãããå Žåã®ã¿ïŒ
hover æ€åº â drawOverlay ã®å
šãããŒã Worker å
ã§å®çµããã
postMessage("redraw") ã¯äœ¿ãããreadPixels â drawOverlay ã
åäž Worker å
ã§é£ç¶å®è¡ãããããäž»ã¹ã¬ããã®ã©ãŠã³ãããªããã¬ã€ãã³ã·ãçºçããªãã
canvas.addEventListener("webglcontextlost", e => {
e.preventDefault();
// ãã¯ã¹ãã£ã»FBOã»ããã°ã©ã ã®åç
§ããã¹ãŠã¯ãªã¢
s.arcTex = s.metaTex = s.ptTex = s.ptMetaTex = null;
s.baseFBO = s.pickFBO = s.programs = null;
});
canvas.addEventListener("webglcontextrestored", () => {
s.programs = createGintPrograms(s.gl);
createFBOs(); uploadGintTextures();
postMessage({ action: "redraw" }); // äž»ã¹ã¬ããã«åæç»èŠæ±
});
native-bucket ã® CacheïŒIndexedDB ã©ãããŒïŒã䜿ãã
ãã¥ãŒç¶æ
ãšããŒã¹ãããåãã»ãã·ã§ã³éã§ä¿æããã
map.stat = await Cache("GIS/stat");
map.baseName = await map.stat("base") || "osm.street";
map.view = await map.stat("view") || [[135, 35, 0], 2];
// view = [[çµåºŠ, 緯床, å転è§], ãºãŒã ]
// ãã³ã»ãºãŒã 確å®åŸïŒdrawn ã€ãã³ãïŒã«èªåä¿å
map.stat("view", getView());
altpbf ã® createGetHeight ãè¿ã颿°ã map.getHeight ãšããŠç»é²ããã
Move / Click ã€ãã³ãã« alt ãã£ãŒã«ãã远å ããã座æšè¡šç€ºãæšé«æé¢ã«å©çšã§ããã
UI ã³ã³ããŒãã³ã㯠map.gadget(name, func) ã§ç»é²ãããã©ã°ã€ã³æ¹åŒã§ã
å
èµã¬ãžã§ãããšãŠãŒã¶ãŒå®çŸ©ã¬ãžã§ãããåçã«æ±ããã
ã¬ãžã§ãã㯠this = map ãšããŠåŒã°ãã颿°ã§ã
map.addFrame ã§ç¢ºä¿ãã DOM é åã«èªç±ã« UI ãæ§ç¯ã§ããã
// å
èµã¬ãžã§ããã®åŒã³åºã
map.gadget.zoom(); // ãºãŒã ãã¿ã³ã leftTop ã«é
眮
map.gadget.layers(); // ããŒã¹ãããåãæ¿ããã¿ã³ã远å
const setTip = map.gadget.tip(); // ã«ãŒãœã«è¿œåŸ tip ã®å¶åŸ¡é¢æ°ãè¿ã
setTip("ã¯ãªãã¯ã§è©³çŽ°è¡šç€º"); // tip ããã¹ããèšå®
// ãŠãŒã¶ãŒå®çŸ©ã¬ãžã§ããã®ç»é²ãšåŒã³åºã
map.gadget("myTool", function () {
map.addFrame("rightTop");
map.rightTop.append("div").text("Hello");
});
map.gadget.myTool();
| ãã¬ãŒã å | äœçœ® | äž»ãªçšé |
|---|---|---|
leftTop | å·Šäž | ãºãŒã ã»æ¹äœã»ã¬ã€ã€ãŒåãæ¿ãçã®æäœãã¿ã³ |
rightTop | å³äž | è£å©ãã¿ã³ã»ãŠãŒã¶ãŒ UI |
leftBottom | å·Šäž | å¡äŸ |
rightBottom | å³äž | è£å©æ å ± |
leftFrame | å°å³å·Šã«åºå®ããã« | ãµã€ãã¡ãã¥ãŒïŒãªãµã€ãºå¯Ÿå¿ïŒ |
rightFrame | å°å³å³ã«åºå®ããã« | 屿§ããã«ïŒãªãµã€ãºå¯Ÿå¿ïŒ |
overlays | å°å³äžå šé¢ | tip / pop / explain / loading ãªã© |
| ã¬ãžã§ãã | 説æ |
|---|---|
zoom() |
ãºãŒã ã€ã³ïŒ+ïŒã»ã¢ãŠãïŒâïŒãã¿ã³ãã¯ãªãã¯ããšã«æŽæ°ãºãŒã ã¬ãã«ãžã¹ãããããªãã map.mag() ãã¢ãã¡ãŒã·ã§ã³å®è¡ãã |
north() |
çŸåšã® Z 軞å転è§ïŒå°å³ã®åŸãïŒããã¿ã³ã®ã¢ã€ã³ã³å
SVG ã§ãªã¢ã«ã¿ã€ã 衚瀺ããã¯ãªãã¯ãããš map.north() ã§ååãã«æ»ã |
layers() |
ããŒã¹ãããåãæ¿ãããããããŠã³ãmap.Layers ã«ç»é²ãããŠããã¬ã€ã€ãŒåãçŸåšãã±ãŒã«ã§è¡šç€ºããéžæãããš map.setBase() ãåŒã¶ |
leftPanel(opts)rightPanel(opts) |
ãµã€ãããã«ã®ééã管çãããopts.active = true ã«ãããšå°å³ãšãªã¢ãå®éã«æŒãåºããªãµã€ãºã¢ãŒãã
çç¥æã¯å°å³äžã«éãªãã¢ãŒãã«ã¢ãŒãã§åäœããã
ééã¢ãã¡ãŒã·ã§ã³ã¯ d3.easeCubicïŒå€æŽå¯ïŒã§è£éããã
æ»ãå€ã¯ããã«å
ã³ã³ãã³ããšãªã¢ã® D3 Selection
|
full() |
Fullscreen API ã䜿ã£ãŠãã«ã¹ã¯ãªãŒã³åãæ¿ãããã«ã¹ã¯ãªãŒã³ç¶æ ã«å¿ããŠã¢ã€ã³ã³ãèªååæ¿ãã |
constellation() |
æåº§ç·ã»æåº§åã®è¡šç€ºãã°ã«ãã¯ãªãã¯ãããš Accessories Worker ã« toggle-constellations ãéãã¡ã·ãšå€©äœè¡šç€ºãé£åããŠåãæ¿ãã |
cpos() |
Geolocation API ã§çŸåšå°ãååŸã flyToFeature ã§ç§»åããã
çŸåšå°ã¯ã¢ãã¡ãŒã·ã§ã³ SVG ããŒã«ãŒïŒãã«ã¹åïŒã§ãªãŒããŒã¬ã€è¡šç€ºããã
å°å³ãåãããš map.tester() ã§å¯èŠå€å®ããŠèªåçã«è¡šç€º/é衚瀺ãåãæ¿ãã
|
shot() |
ã¹ã¯ãªãŒã³ã·ã§ãããããŠã³ããŒãããã
å
š Canvas ã¬ã€ã€ãŒã OffscreenCanvas ã«åæããåŸã
html2canvas ã§ãªãŒããŒã¬ã€ïŒãã¿ã³ã»noprint èŠçŽ ãé€ãïŒãéãã
WebP 圢åŒã§ããŠã³ããŒããã
|
print() |
å°å·çšã« 3 åè§£å床ã§ã¬ã³ããªã³ã°ããæ°èŠãŠã£ã³ããŠã§ window.print() ãå®è¡ããã
瞊é·ã»æšªé·ãèªåå€å®ã㊠CSS ã§é©åã«é
眮ãã
|
measure() |
è·é¢ã»é¢ç©ã®èšæž¬ã¢ãŒãã«åãæ¿ãããã°ã«ãã¿ã³ã
æå¹æã¯ Click ã€ãã³ãããã£ã³ã»ã«ã㊠createPolygon ããŒã«ãèµ·åãã
ã¯ãªãã¯ã§é ç¹ã远å ããªãããªã¢ã«ã¿ã€ã ã§è·é¢ã»é¢ç©ã衚瀺ãã
|
| ã¬ãžã§ãã | 説æ |
|---|---|
loading() |
LoadStart / LoadEnd ã€ãã³ãã«é£åããŠã¿ã€ã«èªã¿èŸŒã¿äžã®ã€ã³ãžã±ãŒã¿ãŒã衚瀺ããã
è€æ°ã®ã¿ã€ã«ãåæã«èªã¿èŸŒãŸããå Žåã¯ãã¹ãŠã®ååããªã¹ã衚瀺ãã
ãã¹ãŠå®äºãããèªåçã«é衚瀺ã«ãªã
|
explain() |
å°å³å·Šäžã«èª¬æããã¹ãã HTML ã衚瀺ããããã«ã
è¿ãå€ã¯ setContent(html) 颿°ã§ãæååã»é¢æ°ïŒD3 Selection ãåãåãïŒã®ã©ã¡ããåãä»ããã
opts.permanent = true ã«ãããšéãããã¿ã³ã衚瀺ããªã
|
legend() |
å°å³å·Šäžã«å¡äŸã衚瀺ããããã«ãã¯ãªãã¯ã§æãããã¿ãåå±éãã¿ã³ã§åŸ©å
ã§ããã
explain ãšåæ§ã« HTML ãŸã㯠D3 颿°ã§ã³ã³ãã³ããèšå®ãã
|
tip() |
ã«ãŒãœã«äœçœ®ã«è¿œåŸããããŒã«ãããã
è¿ãå€ã®é¢æ°ã«ããã¹ããæž¡ããšè¡šç€ºãnull ãæž¡ããšé衚瀺ã«ãªãã
ç»é¢ç«¯ã«è¿ãå Žåã¯èªåçã«å察åŽã«åã蟌ãã
ã¿ããããã€ã¹ã§ã¯ãªãã»ãããåºãã«ãšã
|
pop() |
å°å³äžã®ç¹å®å°ç¹ã«åºå®ããããããã¢ãããäœæããã
è¿ãå€ã®é¢æ° pop(content, event) ãåŒã¶ãã³ã«æ°ãããããã¢ãããçæãããã
ãããã¢ããããå°ç¹ãžåŒãåºãç·ïŒCanvas2DïŒãèªåæç»ãã
ãã©ãã°ç§»åã»ãã³åºå®ïŒäœçœ®åºå®ïŒãå¯èœãpop.clear() ã§å
šæ¶å»
|
contextmenu() |
å³ã¯ãªãã¯ïŒãŸãã¯é·æŒãïŒã§è¡šç€ºããã³ã³ããã¹ãã¡ãã¥ãŒã
è¿ãå€ã®é¢æ°ã« [{ icon, name, func }] ã®é
åãæž¡ããŠã¡ãã¥ãŒé
ç®ãèšå®ãã
|
// pop ã®äœ¿çšäŸ
const pop = map.gadget.pop();
map.onClick(e => {
if (!e) return;
pop(`緯床: ${e.lat.toFixed(4)}<br>çµåºŠ: ${e.lng.toFixed(4)}`, e);
});
// contextmenu ã®äœ¿çšäŸ
const setMenu = map.gadget.contextmenu();
setMenu([
{ name: "ãã®å°ç¹ãäžå¿ã«", func: map => map.flyToFeature({ type: "Point", coordinates: [map.center[0], map.center[1]] }) },
{ name: "åã«åãã", func: map => map.north() },
]);
map.setProperties() 㯠CSS ã«ã¹ã¿ã ããããã£ãããã¥ã¡ã³ãã«ãŒãã«æžã蟌ãã
èšå®ã§ããäž»ãªããããã£ïŒ
spaceColorïŒå®å®èæ¯è²ïŒãearthFilterïŒçäœãã£ã«ã¿ãŒïŒã
borderColorïŒãã¿ã³æ ïŒãbuttonSizeãfontSizeãfontFamily çã
| ã¡ãœãã | 説æ |
|---|---|
map.setBase(name) | ããŒã¹ããããåãæ¿ããïŒã¿ã€ã«ãã£ãã·ã¥ãªã»ããå«ãïŒ |
map.setView(center, zoom, angle) | å³åº§ã«ãã¥ãŒå€æŽ |
map.flyToFeature(feature, opts) | d3.interpolateZoom ã¢ãã¡ãŒã·ã§ã³ã§ç§»å |
map.zoomToFeature(feature, opts) | 2 ãã§ãŒãºã¢ãã¡ãŒã·ã§ã³ã§ç§»å |
map.bbox([x0,y0,x1,y1]) | ããŠã³ãã£ã³ã°ããã¯ã¹ãžãºãŒã / çŸåšã® bbox ååŸ |
map.mag(n, duration) | çŸåšã¹ã±ãŒã«ã n åã«ã¢ãã¡ãŒã·ã§ã³ |
map.north(duration) | Z å転ïŒåŸãïŒãã¢ãã¡ãŒã·ã§ã³ã§ 0 ã«æ»ã |
map.autoRotate(true/false) | èªåå転ïŒd3.timer ã§ 0.01°/msïŒ |
map.tester([lng,lat]) | ç¹ãçŸåšãã¥ãŒããŒãã«è¡šç€ºãããŠãããç¢ºèª |
map.createLayer(opts) | Canvas2D ã¬ã€ã€ãŒã远å ïŒGeoJSON æç»çšïŒ |
map.createRemoteLayer(opts) | Worker + OffscreenCanvas ã¬ã€ã€ãŒã远å |