使用OpenLayers 5在地图上添加旋转的卫星图像

问题描述 投票:0回答:1

我正在尝试使用OpenLayers 5在地图上添加卫星图像。

问题是我无法做到这一点,因为我刚刚找到了一个选项,可以在地图上添加图像,传递图像范围(xmin,ymin,xmax,ymax)而不是边界框。图像应该适合边界框。出于这个原因,图像被扭曲了。

该图像位于JPG文件中(属性feature.properties.icon)。示例:http://exampleserver.com/220/063/353LGN00/353LGN00_thumb_large.jpg

我想要的结果是这样的:

enter image description here

我得到的结果是:

enter image description here

我在地图上添加此图像的代码如下:

import ImageLayer from 'ol/layer/Image'
import Static from 'ol/source/ImageStatic'

...

this.olmap = new Map({
    target: 'map',
    layers: [
      baseLayerGroup, rasterLayerGroup, vectorLayer
    ],
    view: new View({
        projection: 'EPSG:4326',
        center: [ -45.8392, -3.65286 ],
        zoom: 8
    })
})

...

this.rasterLayerGroup.getLayers().push(
    new ImageLayer({
        source: new Static({
            url: feature.properties.icon,
            projection: 'EPSG:4326',
            imageExtent: [
              feature.properties.bl_longitude, feature.properties.bl_latitude,
              feature.properties.tr_longitude, feature.properties.tr_latitude
            ]  
        })
    })
)

有人知道如何传递图像边界框而不仅仅是图像范围吗?

先感谢您。

编辑1:迈克的解决方案

通过Mike的解决方案,我能够修复一些图像所具有的错误(靠近赤道线)。出于这个原因,他的答案解决了我的问题,它将图像插入了一个更好的位置,这是我在创建问题的那一刻所期待的。

但是,这个解决方案对我来说非常适合赤道线附近的图像。两极旁边的图像保持扭曲(编辑2)。

我在下面发送一张说明最终结果的图片:

enter image description here

编辑2:新问题?

我正在测试一些图像,我发现了一个新的bug。现在我发现图像应该放在边界框内。如果图像不适合bbox内部,它会保持扭曲状态,例如我在下面发送的打印图示。

enter image description here

图像应该放在框内,如下图[PS1]所示:

enter image description here

我相信它可能是重投影的问题,但我不知道,因为视图投影和图像投影都是EPSG:4326。

我尝试在Openlayers网站上按照关于光栅重投影[1。]的解释,但是我无法重现它,因为正如我所说,两个投影(视图和图像)都是相同的(或它们应该是)。

我在下面发送包含与上图相关的信息的GeoJSON。图像可以在“properties.icon”(http://www.dpi.inpe.br/newcatalog/tmp/MOD13Q1/2018/MOD13Q1.A2018017.h13v14.jpg)中找到。 bbox坐标可以在“geometry.coordinates”或“properties.bl_latitude”,“properties.bl_longitude”,“properties.br_latitude”等中找到。 “bl”表示“左下”,“br”表示“右下”,“tl”表示“左上”,“tr”表示“右上”。 “properties”中的这些坐标在“geometry.coordinates”中是相同的。

{
  "geometry": {
    "coordinates": [
      [
        [
          -77.7862,
          -50
        ],
        [
          -100,
          -60
        ],
        [
          -80,
          -60
        ],
        [
          -62.229,
          -50
        ],
        [
          -77.7862,
          -50
        ]
      ]
    ],
    "type": "Polygon"
  },
  "properties": {
    "alternate": "http://www.dpi.inpe.br/opensearch/v2/granule.json?uid=MOD13Q1.A2018017.h13v14",
    "auxpath": null,
    "bitslips": null,
    "bl_latitude": -60,
    "bl_longitude": -100,
    "br_latitude": -60,
    "br_longitude": -80,
    "centerlatitude": -55,
    "centerlongitude": -80.0038,
    "centertime": null,
    "cloud": 0,
    "cloudcovermethod": "M",
    "dataset": "MOD13Q1",
    "date": "2018-01-17T00:00:00",
    "enclosure": [
      {
        "band": "evi",
        "radiometric_processing": "SR",
        "type": "MOSAIC",
        "url": "http://www.dpi.inpe.br/newcatalog/tmp/MOD13Q1/2018/MOD13Q1.A2018017.h13v14.006.2018033223827.hdf"
      },
      {
        "band": "ndvi",
        "radiometric_processing": "SR",
        "type": "MOSAIC",
        "url": "http://www.dpi.inpe.br/newcatalog/tmp/MOD13Q1/2018/MOD13Q1.A2018017.h13v14.006.2018033223827.hdf"
      },
      ...
    ],
    "icon": "http://www.dpi.inpe.br/newcatalog/tmp/MOD13Q1/2018/MOD13Q1.A2018017.h13v14.jpg",
    "id": "http://www.dpi.inpe.br/opensearch/v2/granule.json?uid=MOD13Q1.A2018017.h13v14",
    "orbit": 0,
    "path": 14,
    "provider": "OP_CBERS1",
    "row": 13,
    "satellite": "T1",
    "sensor": "MODIS",
    "title": "MOD13Q1.A2018017.h13v14",
    "tl_latitude": -50,
    "tl_longitude": -77.7862,
    "tr_latitude": -50,
    "tr_longitude": -62.229,
    "type": "IMAGES",
    "updated": "2018-03-01T18:51:56",
    "via": "http://www.dpi.inpe.br/opensearch/v2/metadata/MOD13Q1.A2018017.h13v14"
  },
  "type": "Feature"
}

有人有新想法吗?

[PS 1]:使图像适合bbox内部的原始代码是Leaflet代码[2.],我将其发送到下面:

var map = L.map('map').setView([-15.22, -53.23], 5)

...

var anchor = [
    [feature.properties.tl_latitude, feature.properties.tl_longitude],
    [feature.properties.tr_latitude, feature.properties.tr_longitude],
    [feature.properties.br_latitude, feature.properties.br_longitude],
    [feature.properties.bl_latitude, feature.properties.bl_longitude]
]

layer._quicklook = L.imageTransform(feature.properties.icon, anchor).addTo(map)

[1] https://openlayers.org/en/latest/doc/tutorials/raster-reprojection.html

[2] https://github.com/ScanEx/Leaflet.imageTransform

gis openlayers openlayers-5 satellite-image
1个回答
1
投票

如果坐标是照片的坐标,并且包含旋转照片的jpg位于EPSG:4326(即与经线和平行线对齐),那么你需要一个包含照片所有角落的边界框

import {boundingExtent} from 'ol/extent';

....

this.rasterLayerGroup.getLayers().push(
    new ImageLayer({
        source: new Static({
            url: feature.properties.icon,
            projection: 'EPSG:4326',
            imageExtent: boundingExtent([
              [feature.properties.bl_longitude, feature.properties.bl_latitude],
              [feature.properties.br_longitude, feature.properties.br_latitude],
              [feature.properties.tl_longitude, feature.properties.tl_latitude],
              [feature.properties.tr_longitude, feature.properties.tr_latitude]
            ])  
        })
    })
)

然而,你的顶部截图有jpg本身旋转。如果需要,投影不是EPSG:4326,您需要定义一个自定义投影来处理旋转。

我已经设法得到了一些东西,但简单地拉伸图像以适应多边形并不能给出传单方法所做的一侧的精确对齐

var properties = {
    "bl_latitude": -60,
    "bl_longitude": -100,
    "br_latitude": -60,
    "br_longitude": -80,
    "centerlatitude": -55,
    "centerlongitude": -80.0038,
    "icon": "https://www.mikenunn.net/demo/MOD13Q1.A2018017.h13v14.jpg",
    "tl_latitude": -50,
    "tl_longitude": -77.7862,
    "tr_latitude": -50,
    "tr_longitude": -62.229,
};

function overlaySource ( properties ) {

    var projection = ol.proj.get('EPSG:3857');  // leaflet projection

    var extentSize = [0, 0, 4096, 4096];  // arbitary extent for the projection transforms
    var size0 = extentSize[2];
    var size1 = extentSize[3];

    var url = properties.icon;

    var bl = ol.proj.transform([properties.bl_longitude, properties.bl_latitude], 'EPSG:4326', projection);
    var tl = ol.proj.transform([properties.tl_longitude, properties.tl_latitude], 'EPSG:4326', projection);
    var br = ol.proj.transform([properties.br_longitude, properties.br_latitude], 'EPSG:4326', projection);
    var tr = ol.proj.transform([properties.tr_longitude, properties.tr_latitude], 'EPSG:4326', projection);

    function normalTransform(coordinates, output, dimensions) {
        var dims = dimensions || 2;
        for (var i=0; i<coordinates.length; i+=dims) {

            var left = bl[0] + (tl[0]-bl[0]) * coordinates[i+1]/size1;
            var right = br[0] + (tr[0]-br[0]) * coordinates[i+1]/size1;

            var top = tl[1] + (tr[1]-tl[1]) * coordinates[i]/size0;
            var bottom = bl[1] + (br[1]-bl[1]) * coordinates[i]/size0;

            var newCoordinates0 = left + (right-left) * coordinates[i]/size0;
            var newCoordinates1 = bottom + (top-bottom) * coordinates[i+1]/size1;

            c = ol.proj.transform([newCoordinates0, newCoordinates1], projection, 'EPSG:3857');

            //console.log(coordinates[i] + ' ' + coordinates[i+1] + ' ' + c[0] + ' ' + c[1]);

            coordinates[i] = c[0];
            coordinates[i+1] = c[1];

        }
        return coordinates;
    }

    function rotateTransform(coordinates, output, dimensions) {
        var dims = dimensions || 2;
        for (var i=0; i<coordinates.length; i+=dims) {

            c = ol.proj.transform([coordinates[i], coordinates[i+1]], 'EPSG:3857', projection);

            var left = bl[0] + (tl[0]-bl[0]) * (c[1]-bl[1]) /(tl[1]-bl[1]);
            var right = br[0] + (tr[0]-br[0]) * (c[1]-br[1]) /(tr[1]-br[1]);

            var top = tl[1] + (tr[1]-tl[1]) * (c[0]-tl[0])/(tr[0]-tl[0]);
            var bottom = bl[1] + (br[1]-bl[1]) * (c[0]-bl[0])/(br[0]-bl[0]);

            var newCoordinates0 = (c[0]-left)*size0/(right-left);
            var newCoordinates1 = (c[1]-bottom)*size1/(top-bottom);

            //console.log(coordinates[i] + ' ' + coordinates[i+1] + ' ' + newCoordinates0 + ' ' + newCoordinates1);

            coordinates[i] = newCoordinates0;
            coordinates[i+1] = newCoordinates1;

        }
        return coordinates;
    }

    var rotatedProjection = new ol.proj.Projection({
        code: 'EPSG:' + url + 'rotated',
        units: 'm',
        extent: extentSize
    });
    ol.proj.addProjection(rotatedProjection);

    ol.proj.addCoordinateTransforms('EPSG:3857', rotatedProjection,
        function(coordinate) {
            return rotateTransform(coordinate);
        },
        function(coordinate) {
            return normalTransform(coordinate);
        }
    );

    ol.proj.addCoordinateTransforms('EPSG:4326', rotatedProjection,
        function(coordinate) {
            return rotateTransform(ol.proj.transform(coordinate, "EPSG:4326", "EPSG:3857"));
        },
        function(coordinate) {
            return ol.proj.transform(normalTransform(coordinate), "EPSG:3857", "EPSG:4326");
        }
    );

    return new ol.source.ImageStatic({
        projection: rotatedProjection,
        imageExtent: extentSize,
        url: url
    });

}

var tileLayer = new ol.layer.Tile({
    source: new ol.source.XYZ({
        attributions: [
            'Powered by Esri',
            'Source: Esri, DigitalGlobe, GeoEye, Earthstar Geographics, CNES/Airbus DS, USDA, USGS, AeroGRID, IGN, and the GIS User Community'
        ],
        //attributionsCollapsible: false,
        url: 'https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
        maxZoom: 23
    })
});

var imageLayer = new ol.layer.Image({
    source:  overlaySource( properties ),
    opacity: 0.7
})

var map = new ol.Map({
    layers: [tileLayer, imageLayer],
    target: 'map',
    logo: false,
    view: new ol.View()
});

var imageProj = imageLayer.getSource().getProjection();
map.getView().fit(ol.proj.transformExtent(imageProj.getExtent(), imageProj, map.getView().getProjection()), {constrainResolution: false});
html, body, .map {
    margin: 0;
    padding: 0;
    width: 100%;
    height: 100%;
}
<link href="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/css/ol.css" rel="stylesheet" />
<script src="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/build/ol.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/proj4js/2.5.0/proj4.js"></script>
<div id="map" class="map"></div>

这是我的KML方法,但这是一个矩形按指定角度的简单旋转,而不是将其扭曲成四边形,其中只有两个边平行。

function kmlOverlaySource ( kmlExtent, // KMLs specify the extent the unrotated image would occupy
                            url,
                            rotation,
                            imageSize,
) {

    // calculate latitude of true scale of equidistant cylindrical projection based on pixels per degree on each axis

    proj4.defs('EPSG:' + url, '+proj=eqc +lat_ts=' +
                              (Math.acos((ol.extent.getHeight(kmlExtent)/imageSize[1])
                                        /(ol.extent.getWidth(kmlExtent)/imageSize[0]))*180/Math.PI) +
                              ' +lat_0=0 +lon_0=0 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs');

    if (ol.proj.proj4 && ol.proj.proj4.register) { ol.proj.proj4.register(proj4); } // if OL5 register proj4

    // convert the extents to source projection coordinates

    var projection = ol.proj.get('EPSG:' + url);
    var projExtent = ol.proj.transformExtent(kmlExtent, 'EPSG:4326', projection);

    var angle = -rotation * Math.PI/180;

    function rotateTransform(coordinates, output, dimensions) {
        var dims = dimensions || 2;
        for (var i=0; i<coordinates.length; i+=dims) {
            var point = new ol.geom.Point([coordinates[i],coordinates[i+1]]);
            point.rotate(angle, ol.extent.getCenter(projExtent));
            var newCoordinates = point.getCoordinates();
            coordinates[i] = newCoordinates[0];
            coordinates[i+1] = newCoordinates[1];
        }
        return coordinates;
    }

    function normalTransform(coordinates, output, dimensions) {
        var dims = dimensions || 2;
        for (var i=0; i<coordinates.length; i+=dims) {
            var point = new ol.geom.Point([coordinates[i],coordinates[i+1]]);
            point.rotate(-angle, ol.extent.getCenter(projExtent));
            var newCoordinates = point.getCoordinates();
            coordinates[i] = newCoordinates[0];
            coordinates[i+1] = newCoordinates[1];
        }
        return coordinates;
    }

    var rotatedProjection = new ol.proj.Projection({
        code: 'EPSG:' + url + 'rotated',
        units: 'm',
        extent: projExtent
    });
    ol.proj.addProjection(rotatedProjection);

    ol.proj.addCoordinateTransforms('EPSG:4326', rotatedProjection,
        function(coordinate) {
            return rotateTransform(ol.proj.transform(coordinate, 'EPSG:4326', projection));
        },
        function(coordinate) {
            return ol.proj.transform(normalTransform(coordinate), projection, 'EPSG:4326');
        }
    );

    ol.proj.addCoordinateTransforms('EPSG:3857', rotatedProjection,
        function(coordinate) {
            return rotateTransform(ol.proj.transform(coordinate, 'EPSG:3857', projection));
        },
        function(coordinate) {
            return ol.proj.transform(normalTransform(coordinate), projection, 'EPSG:3857');
        }
    );

    return new ol.source.ImageStatic({
        projection: rotatedProjection,
        url: url,
        imageExtent: projExtent
    });

}

var tileLayer = new ol.layer.Tile({
    source: new ol.source.XYZ({
        attributions: [
            'Powered by Esri',
            'Source: Esri, DigitalGlobe, GeoEye, Earthstar Geographics, CNES/Airbus DS, USDA, USGS, AeroGRID, IGN, and the GIS User Community'
        ],
        //attributionsCollapsible: false,
        url: 'https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
        maxZoom: 23
    })
});

// these would normally be parsed from a KML file
var kmlExtent = [8.433995415151397, 46.65804355828784, 9.144871415151389, 46.77980155828784];
var url = 'https://raw.githubusercontent.com/ReneNyffenegger/about-GoogleEarth/master/kml/the_png_says.png'
var rotation = 30;
var imageSize = [];

var img = document.createElement('img');
img.onload = imageLoaded;
img.src = url;

function imageLoaded() {

    imageSize[0] = img.width;
    imageSize[1] = img.height;

    var imageLayer = new ol.layer.Image({
        source: kmlOverlaySource(kmlExtent, url, rotation, imageSize),
    });

    var map = new ol.Map({
        layers: [tileLayer, imageLayer],
        target: 'map',
        logo: false,
        view: new ol.View()
    });

    var imageProj = imageLayer.getSource().getProjection();
    map.getView().fit(ol.proj.transformExtent(imageProj.getExtent(), imageProj, map.getView().getProjection()), {constrainResolution: false});

}
html, body, .map {
    margin: 0;
    padding: 0;
    width: 100%;
    height: 100%;
}
<link href="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/css/ol.css" rel="stylesheet" />
<script src="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/build/ol.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/proj4js/2.5.0/proj4.js"></script>
<div id="map" class="map"></div>
© www.soinside.com 2019 - 2024. All rights reserved.