Элемент GroundOverlay KML / OpenLayers без тегов <Document>не загружается на карту? - PullRequest
0 голосов
/ 29 августа 2018

У меня есть элемент KML без тегов документа, а только с тегами типа ... (некоторые ненужные элементы удалены с помощью ...)

<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
<GroundOverlay>
    <name>...</name>
    <description>...</description>
    <Icon><href>...</href></Icon>
    <LatLonBox>
       ...
    </LatLonBox>
</GroundOverlay>
</kml>

В OpenLayers я не могу загрузить это без тегов документов вокруг GroundOverlay. Однако я могу получить метку места, которая является корневым узлом без тегов документа, чтобы нормально загрузиться.

Есть ли путь к KML с корневым узлом GroundOverlay в KML, в котором нет тегов документа? Кажется, что он отлично загружается в других рендерерах, таких как Google Earth и Cesium.

Ответы [ 2 ]

0 голосов
/ 30 августа 2018

В качестве источника для слоя OpenLayers можно использовать наземные мозаичные наложения. До сих пор я делал это только путем жесткого кодирования координат, размеров листов, путей листов и т. Д., Которые вручную считывались из KML и позволяли OpenLayers вычислять правильную проекцию, и хотя proj4 допускает поворотные проекции, я еще не разобрался с математикой. необходимо обрабатывать повороты. У меня есть демоверсия, основанная на выборочных данных из https://www.dwgwalking.co.uk/garminTryB4Buy.htm Демо http://mikenunn.16mb.com/demo/dwg-aracena-demo.htm использует координаты восток / запад из верхнего ряда и координаты север / юг в левом столбце и игнорирует очень маленькое вращение.

UPDATE

Я сейчас разработал это для потенциальной библиотечной функции для анализа файла KML на предмет наложения на землю и возврата группы слоев источников и слоев ImageStatic. Вращение также поддерживается. Единственный обязательный параметр - это URL документа KML. Поддерживаемые параметры: attributions и crossOrigin.

Вот предыдущая демоверсия мульти-pverlay (с очень маленьким вращением), загруженная непосредственно из документа KML:

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 map = new ol.Map({
    layers: [tileLayer],
    target: 'map',
    logo: false,
    view: new ol.View({
        center:[0, 0],
        zoom: 2
    })
});

var extent = ol.extent.createEmpty();

var group = kmlOverlay.loadUrl(
    'https://www.mikenunn.net/data/dwg/aracena/doc.kml',
    { attributions: 'Copyright David Brawn <a href="https://www.dwgwalking.co.uk" target="_blank">www.dwgwalking.co.uk</a>' }
);

group.getLayers().on('add', function(evt) {
    evt.element.once('change:source', function() {
        if (evt.element.getSource && evt.element.getSource().getProjection) {
            var imageProj = evt.element.getSource().getProjection();
            ol.extent.extend(extent, ol.proj.transformExtent(imageProj.getExtent(), imageProj, map.getView().getProjection()));
            map.getView().fit(extent);
        }
    });
});

group.setOpacity(0.7);
map.addLayer(group);
html, body {
    margin: 0;
    padding: 0;
    width: 100%;
    height: 100%;
}
.map {
    width: 100%;
    height: 100%;
}
<link href="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/css/ol.css" rel="stylesheet" />
<!-- The line below is only needed for old environments like Internet Explorer and Android 4.x -->
<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=requestAnimationFrame,Element.prototype.classList,URL"></script>
<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>
<script>

kmlOverlay = function() {

function loadUrl ( url,
                   opt_options  // attributions (defaults to undefined), crossOrigin (defaults to 'anonymous')
) {

    var options = opt_options || {};
    var crossOrigin = options.crossOrigin === undefined ? 'anonymous' : options.crossOrigin;

    var group = new ol.layer.Group();

    function addLayer(name, extent, url, rotation) {  // function to maintain context during async img load

        var imageLayer = new ol.layer.Image({
            title: name
        });
        group.getLayers().push(imageLayer);

        var imageSize = [];
        var img = document.createElement('img');
        img.onload = function() {
            imageSize[0] = img.width;
            imageSize[1] = img.height;
            imageLayer.setSource(
                source (
                    extent,
                    url,
                    rotation,
                    imageSize,
                    { attributions: options.attributions, crossOrigin: crossOrigin }
                )
            );
        };
        img.crossOrigin = crossOrigin;
        img.src = url;

    }

    var last = url.lastIndexOf('/') + 1;
    path = url.slice(0, last);

    var xhr = new XMLHttpRequest();
    xhr.open('GET', url);
    xhr.onload = function() {

        var parser = new DOMParser();
        var xmlDoc = parser.parseFromString(xhr.responseText,'text/xml');

        var elements = xmlDoc.getElementsByTagName('GroundOverlay');

        for (var i=0; i<elements.length; i++) {

            var name;
            if (elements[i].getElementsByTagName('rotation').length > 0) {
                name = elements[i].getElementsByTagName('name')[0].childNodes[0].nodeValue;
            }
            var href = elements[i].getElementsByTagName('href')[0].childNodes[0].nodeValue;
            if (href.indexOf('http:') != 0 && href.indexOf('https:') != 0) {
                href = path + href;
            }
            var north = Number(elements[i].getElementsByTagName('north')[0].childNodes[0].nodeValue);
            var south = Number(elements[i].getElementsByTagName('south')[0].childNodes[0].nodeValue);
            var east = Number(elements[i].getElementsByTagName('east')[0].childNodes[0].nodeValue);
            var west = Number(elements[i].getElementsByTagName('west')[0].childNodes[0].nodeValue);
            var rotation = 0;
            if (elements[i].getElementsByTagName('rotation').length > 0) {
                rotation = Number(elements[i].getElementsByTagName('rotation')[0].childNodes[0].nodeValue);
            }

            addLayer(name, [west, south, east, north], href, rotation);

        }

    }
    xhr.send();
    return group;

}

function source ( kmlExtent, // KMLs specify the extent the unrotated image would occupy
                  url,
                  rotation,
                  imageSize,
                  opt_options  // attributions, crossOrigin (default to undefined)
) {

    var options = opt_options || {};

    // 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(coordinate) {
        var point = new ol.geom.Point(coordinate);
        point.rotate(angle, ol.extent.getCenter(projExtent));
        return point.getCoordinates();
    }

    function normalTransform(coordinate) {
        var point = new ol.geom.Point(coordinate);
        point.rotate(-angle, ol.extent.getCenter(projExtent));
        return point.getCoordinates();
    }

    var rotatedProjection = new ol.proj.Projection({
        code: 'EPSG:' + url + ':rotation:' + rotation,
        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,
        attributions: options.attributions,
        crossOrigin: options.crossOrigin
    });

}

return {
   "loadUrl" : loadUrl,
   "source"  : source
}

} ();

</script>
<div id="map" class="map"></div>

Вот одна демонстрационная программа с очень заметным поворотом, взятая из https://renenyffenegger.ch/notes/tools/Google-Earth/kml/index

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 map = new ol.Map({
    layers: [tileLayer],
    target: 'map',
    logo: false,
    view: new ol.View({
        center:[0, 0],
        zoom: 2
    })
});

var extent = ol.extent.createEmpty();

var group = kmlOverlay.loadUrl(
    'https://raw.githubusercontent.com/ReneNyffenegger/about-GoogleEarth/master/kml/GroundOverlay.kml'
);

group.getLayers().once('add', function(evt) {
    evt.element.once('change:source', function() {
        if (evt.element.getSource && evt.element.getSource().getProjection) {
            var imageProj = evt.element.getSource().getProjection();
            ol.extent.extend(extent, ol.proj.transformExtent(imageProj.getExtent(), imageProj, map.getView().getProjection()));
            map.getView().fit(extent, { constrainResolution: false });
        }
    });
});

group.setOpacity(0.8);
map.addLayer(group);
html, body {
    margin: 0;
    padding: 0;
    width: 100%;
    height: 100%;
}
.map {
    width: 100%;
    height: 100%;
}
<link href="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/css/ol.css" rel="stylesheet" />
<!-- The line below is only needed for old environments like Internet Explorer and Android 4.x -->
<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=requestAnimationFrame,Element.prototype.classList,URL"></script>
<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>
<script>

kmlOverlay = function() {

function loadUrl ( url,
                   opt_options  // attributions (defaults to undefined), crossOrigin (defaults to 'anonymous')
) {

    var options = opt_options || {};
    var crossOrigin = options.crossOrigin === undefined ? 'anonymous' : options.crossOrigin;

    var group = new ol.layer.Group();

    function addLayer(name, extent, url, rotation) {  // function to maintain context during async img load

        var imageLayer = new ol.layer.Image({
            title: name
        });
        group.getLayers().push(imageLayer);

        var imageSize = [];
        var img = document.createElement('img');
        img.onload = function() {
            imageSize[0] = img.width;
            imageSize[1] = img.height;
            imageLayer.setSource(
                source (
                    extent,
                    url,
                    rotation,
                    imageSize,
                    { attributions: options.attributions, crossOrigin: crossOrigin }
                )
            );
        };
        img.crossOrigin = crossOrigin;
        img.src = url;

    }

    var last = url.lastIndexOf('/') + 1;
    path = url.slice(0, last);

    var xhr = new XMLHttpRequest();
    xhr.open('GET', url);
    xhr.onload = function() {

        var parser = new DOMParser();
        var xmlDoc = parser.parseFromString(xhr.responseText,'text/xml');

        var elements = xmlDoc.getElementsByTagName('GroundOverlay');

        for (var i=0; i<elements.length; i++) {

            var name;
            if (elements[i].getElementsByTagName('rotation').length > 0) {
                name = elements[i].getElementsByTagName('name')[0].childNodes[0].nodeValue;
            }
            var href = elements[i].getElementsByTagName('href')[0].childNodes[0].nodeValue;
            if (href.indexOf('http:') != 0 && href.indexOf('https:') != 0) {
                href = path + href;
            }
            var north = Number(elements[i].getElementsByTagName('north')[0].childNodes[0].nodeValue);
            var south = Number(elements[i].getElementsByTagName('south')[0].childNodes[0].nodeValue);
            var east = Number(elements[i].getElementsByTagName('east')[0].childNodes[0].nodeValue);
            var west = Number(elements[i].getElementsByTagName('west')[0].childNodes[0].nodeValue);
            var rotation = 0;
            if (elements[i].getElementsByTagName('rotation').length > 0) {
                rotation = Number(elements[i].getElementsByTagName('rotation')[0].childNodes[0].nodeValue);
            }

            addLayer(name, [west, south, east, north], href, rotation);

        }

    }
    xhr.send();
    return group;

}

function source ( kmlExtent, // KMLs specify the extent the unrotated image would occupy
                  url,
                  rotation,
                  imageSize,
                  opt_options  // attributions, crossOrigin (default to undefined)
) {

    var options = opt_options || {};

    // 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(coordinate) {
        var point = new ol.geom.Point(coordinate);
        point.rotate(angle, ol.extent.getCenter(projExtent));
        return point.getCoordinates();
    }

    function normalTransform(coordinate) {
        var point = new ol.geom.Point(coordinate);
        point.rotate(-angle, ol.extent.getCenter(projExtent));
        return point.getCoordinates();
    }

    var rotatedProjection = new ol.proj.Projection({
        code: 'EPSG:' + url + ':rotation:' + rotation,
        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,
        attributions: options.attributions,
        crossOrigin: options.crossOrigin
    });

}

return {
   "loadUrl" : loadUrl,
   "source"  : source
}

} ();

</script>
<div id="map" class="map"></div>
0 голосов
/ 29 августа 2018

Не поддерживается в OpenLayers

https://github.com/openlayers/openlayers/issues/2941

...