Geotiff Reprojection
Runnable OpenLayers example: Geotiff Reprojection. Open the live demo, read the complete source, and copy the file into your project. No build step or API key.
single HTML file · CDN dependencies onlyinterface may contain Chinese labels
Source
<!--
示例:栅格重投影 — OpenLayers geotiff-reprojection
核心逻辑:ImageStatic 以 EPSG:4326 声明影像范围,3857 视口下由 ol.reproj 实时重投影
依赖:OpenLayers 10.3.1 · jsDelivr CDN
-->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta name="robots" content="noindex,follow">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>栅格重投影</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/ol@10.3.1/ol.css">
<style>
html,body{height:100%;margin:0}
body{display:flex;flex-direction:column;overflow:hidden;
font:13px/1.6 -apple-system,"Segoe UI","PingFang SC",sans-serif}
.bar{flex:0 0 auto;padding:8px 12px;background:#f6f7f9;border-bottom:1px solid #e3e6ea;
display:flex;gap:12px;align-items:center;font-size:12px;color:#58606e;flex-wrap:wrap}
.bar label{display:flex;gap:5px;align-items:center;cursor:pointer}
#map{flex:1 1 auto;min-height:0;background:#eef0f3}
</style>
</head>
<body>
<div class="bar">
<label><input type="checkbox" id="tgl" checked> 经纬度影像</label>
<label><input type="checkbox" id="frame"> 原始范围框</label>
<span id="info">影像声明为 EPSG:4326,视口为 EPSG:3857</span>
</div>
<div id="map"></div>
<script src="https://cdn.jsdelivr.net/npm/ol@10.3.1/dist/ol.js"></script>
<script>
(function () {
'use strict';
if (typeof ol === 'undefined') {
document.getElementById('map').innerHTML =
'<div style="padding:20px;color:#b42318">OpenLayers 加载失败,请检查能否访问 cdn.jsdelivr.net</div>';
return;
}
/* 影像范围按经纬度给出:东经 100°–125°、北纬 25°–42° */
var imageExtent = [100, 25, 125, 42];
/* 画一张带格网与地物标记的“原始栅格” */
function makeImage() {
var w = 900, h = 600;
var c = document.createElement('canvas');
c.width = w;
c.height = h;
var ctx = c.getContext('2d');
var grad = ctx.createLinearGradient(0, 0, 0, h);
grad.addColorStop(0, '#dbe9d5');
grad.addColorStop(0.5, '#e8dcbc');
grad.addColorStop(1, '#c9dbe8');
ctx.fillStyle = grad;
ctx.fillRect(0, 0, w, h);
/* 纹理:模拟山脊与水系 */
ctx.strokeStyle = 'rgba(120,90,60,0.35)';
for (var i = 0; i < 26; i++) {
ctx.lineWidth = 1 + (i % 3);
ctx.beginPath();
for (var x = 0; x <= w; x += 12) {
var y = h * (0.15 + i / 32) + 18 * Math.sin(x / 60 + i);
x === 0 ? ctx.moveTo(x, y) : ctx.lineTo(x, y);
}
ctx.stroke();
}
ctx.strokeStyle = 'rgba(40,90,140,0.5)';
ctx.lineWidth = 3;
ctx.beginPath();
ctx.moveTo(0, h * 0.72);
ctx.bezierCurveTo(w * 0.3, h * 0.55, w * 0.6, h * 0.9, w, h * 0.6);
ctx.stroke();
/* 经纬网,间距 5° */
ctx.strokeStyle = 'rgba(0,0,0,0.22)';
ctx.lineWidth = 1;
ctx.font = '12px sans-serif';
ctx.fillStyle = 'rgba(0,0,0,0.55)';
for (var lon = 100; lon <= 125; lon += 5) {
var px = (lon - 100) / 25 * w;
ctx.beginPath();
ctx.moveTo(px, 0);
ctx.lineTo(px, h);
ctx.stroke();
ctx.fillText(lon + '°E', px + 4, 16);
}
for (var lat = 25; lat <= 42; lat += 5) {
var py = (42 - lat) / 17 * h;
ctx.beginPath();
ctx.moveTo(0, py);
ctx.lineTo(w, py);
ctx.stroke();
ctx.fillText(lat + '°N', 4, py - 4);
}
return c.toDataURL();
}
var imageSource = new ol.source.ImageStatic({
url: makeImage(),
/* 关键:影像自带投影信息,交给渲染层重投影 */
projection: 'EPSG:4326',
imageExtent: imageExtent,
crossOrigin: 'anonymous'
});
var imageLayer = new ol.layer.Image({ source: imageSource });
/* 原始范围框:经纬度矩形的四条边,同样要转到 3857 */
var frameSource = new ol.source.Vector({
features: [new ol.Feature({
geometry: new ol.geom.Polygon([[
[imageExtent[0], imageExtent[1]],
[imageExtent[2], imageExtent[1]],
[imageExtent[2], imageExtent[3]],
[imageExtent[0], imageExtent[3]],
[imageExtent[0], imageExtent[1]]
]]).transform('EPSG:4326', 'EPSG:3857')
})]
});
var frameLayer = new ol.layer.Vector({
source: frameSource,
visible: false,
style: new ol.style.Style({
stroke: new ol.style.Stroke({ color: '#c92a2a', width: 2, lineDash: [6, 4] })
})
});
var map = new ol.Map({
target: 'map',
layers: [
new ol.layer.Tile({ source: new ol.source.XYZ({url: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}', attributions: 'Tiles © Esri'}) }),
imageLayer,
frameLayer
],
view: new ol.View({
center: ol.proj.fromLonLat([112.5, 33.5]),
zoom: 5,
minZoom: 2
}),
controls: ol.control.defaults.defaults({ attribution: false })
});
document.getElementById('tgl').addEventListener('change', function () {
imageLayer.setVisible(this.checked);
});
document.getElementById('frame').addEventListener('change', function () {
frameLayer.setVisible(this.checked);
});
})();
</script>
</body>
</html>
Language: the code and this page are in English; the full tutorial and API notes for this example are in Chinese. The demo itself is the authoritative version — it runs exactly as shown.