コンテンツにスキップ

Water reflection

0.10.1

A lake reflecting the terrain around it

water_reflection.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Water reflection</title>
  <meta property="og:description" content="A lake reflecting the terrain around it" />
  <meta name="mapray-version" content="0.10.1">
  <meta name="mapray-api-status" content="experimental">
  <script src="./v0.10.6/mapray.min.js"></script>
  <script src="./v0.10.6/maprayui.min.js"></script>
  <link rel="stylesheet" href="./v0.10.6/mapray.css">
  <style>
    body { margin: 0; padding: 0; }
    html, body, div#mapray-container { height: 100%; }
    .caption {
        position: absolute; top: 0; left: 0; margin: 10px;
        font: 13px/1.5 sans-serif; background: rgba(255,255,255,0.85);
        border-radius: 3px; padding: 8px 10px; max-width: 22em;
    }
    .caption b { display: block; margin-bottom: 4px; }
    .caption input { width: 100%; }
  </style>
</head>

<body>
    <div id="mapray-container"></div>
    <div class="caption">
        <b>Reflection</b>
        The surface reflects what is already on screen, so the terrain, the sky and
        the sun all appear in it. Drag to look along the water: the reflection
        stretches as the view gets closer to the surface.
        <label for="time">2026-10-20 <span id="time_value">16:45</span> JST</label>
        <input type="range" id="time" min="840" max="1050" step="5" value="1005">
    </div>
</body>
</html>

<script>
async function main() {
    const cloud_api = new mapray.cloud.CloudApiV2( {
        tokenType: mapray.cloud.CloudApi.TokenType.ACCESS_TOKEN,
        token: "<YOUR_MAPRAY_ACCESS_TOKEN>",
    } );

    const uiviewer = new maprayui.StandardUIViewer( "mapray-container", {
        image_provider: new mapray.StandardImageProvider( {
            url: "https://cyberjapandata.gsi.go.jp/xyz/seamlessphoto/", format: "jpg", min_level: 2, max_level: 18,
        } ),
        dem_provider: new mapray.StandardDemProvider( cloud_api.getDefaultDemAsResource() ),
        sun_visualizer: new mapray.SunVisualizer( 32 ),
        atmosphere: new mapray.Atmosphere(),
    } );

    // Low over the water and looking across it. A grazing view puts the Fresnel
    // term near 1, which is where the reflection is strongest.
    uiviewer.setCameraPosition( { longitude: 138.8813839790466, latitude: 35.41599948957569, height: 1003.0 } );
    uiviewer.setLookAtPosition( { longitude: 138.8124600651, latitude: 35.3937341103, height: 1000.0 } );


    const visualizer = new mapray.WaterPolygonEntityVisualizer();

    await mapray.WaterPolygonEntity.loadWaterPolygonGeoJSON(
        uiviewer.viewer.scene, [ cloud_api.getDatasetAsResource( "5103308951781376" ) ], {
            altitude_mode: mapray.AltitudeMode.ABSOLUTE,
            height: 988.5353277977556,
            visualizer: visualizer,
        }
    );

    // A short, low swell keeps the reflection legible. Long steep waves break it up.
    // 2.5 m/s over the 4.8 km lake gives waves about 3 m long.
    visualizer.setWindSpeed( 2.5 );
    visualizer.setWindFetch( 4806 );
    visualizer.setWaterTurbidity( 0.35 );

    /**
     * Sun direction in GOCS (ECEF) for a UTC time.
     * Meeus ch.25 low-precision solar position + GMST rotation (error < 0.02 deg).
     */
    function sunDirectionGocs( date ) {
        const DEG = Math.PI / 180;
        const jd = date.getTime() / 86400000 + 2440587.5;
        const t  = ( jd - 2451545.0 ) / 36525.0;
        const L0 = 280.46646 + 36000.76983 * t;
        const M  = ( 357.52911 + 35999.05029 * t ) * DEG;
        const C  = 1.914602 * Math.sin( M ) + 0.019993 * Math.sin( 2 * M ) + 0.000289 * Math.sin( 3 * M );
        const lambda = ( L0 + C ) * DEG;
        const eps = ( 23.4392911 - 0.0130042 * t ) * DEG;
        const ra  = Math.atan2( Math.cos( eps ) * Math.sin( lambda ), Math.cos( lambda ) );
        const dec = Math.asin( Math.sin( eps ) * Math.sin( lambda ) );
        let gmst = ( 280.46061837 + 360.98564736629 * ( jd - 2451545.0 ) ) % 360;
        if ( gmst < 0 ) gmst += 360;
        const lon = ra - gmst * DEG;
        return [ Math.cos( dec ) * Math.cos( lon ), Math.cos( dec ) * Math.sin( lon ), Math.sin( dec ) ];
    }

    // The view faces a bearing of 248 deg. In late October the sun sets at about
    // 255 deg around 17:00, so it goes down in frame and lays a glitter path on the lake.
    const time = document.getElementById( "time" );
    const updateSun = function () {
        const minutes = Number( time.value );
        const hh = String( Math.floor( minutes / 60 ) ).padStart( 2, "0" );
        const mm = String( minutes % 60 ).padStart( 2, "0" );
        document.getElementById( "time_value" ).textContent = hh + ":" + mm;
        uiviewer.viewer.sun.setSunDirection( sunDirectionGocs( new Date( "2026-10-20T" + hh + ":" + mm + ":00+09:00" ) ) );
    };
    time.addEventListener( "input", updateSun );
    updateSun();
}

main();
</script>

Info

このサンプルコードは、<YOUR_MAPRAY_ACCESS_TOKEN>を、あなたのMapray CloudのアカウントのOrganization Tokenに置き換えるまで、期待通りに動作しません。