Cesium 加载mapboxgl最新失量切片(mvt)

#原因

参照Openlayers提供的样式加载,发现样式没有mapboxgl加载好看,如果更换mapboxgl版本或者采用自发布地图,还需要调整代码,因此参考mapboxgl底层,重新进行了优化

#思路

传入mapboxgl样式,参考mapboxgl底层样式处理,重新绘制地图

#问题

技术有限,无法将mvt中的Feature的x,y读取为经纬度,有解决的请给我留言

/*
 * Copyright (C) 2008 Apple Inc. All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Ported from Webkit
 * http://svn.webkit.org/repository/webkit/trunk/Source/WebCore/platform/graphics/UnitBezier.h
 */





function UnitBezier(p1x, p1y, p2x, p2y) {
    // Calculate the polynomial coefficients, implicit first and last control points are (0,0) and (1,1).
    this.cx = 3.0 * p1x;
    this.bx = 3.0 * (p2x - p1x) - this.cx;
    this.ax = 1.0 - this.cx - this.bx;


    this.cy = 3.0 * p1y;
    this.by = 3.0 * (p2y - p1y) - this.cy;
    this.ay = 1.0 - this.cy - this.by;


    this.p1x = p1x;
    this.p1y = p2y;
    this.p2x = p2x;
    this.p2y = p2y;
}


UnitBezier.prototype.sampleCurveX = function(t) {
    // `ax t^3 + bx t^2 + cx t' expanded using Horner's rule.
    return ((this.ax * t + this.bx) * t + this.cx) * t;
};


UnitBezier.prototype.sampleCurveY = function(t) {
    return ((this.ay * t + this.by) * t + this.cy) * t;
};


UnitBezier.prototype.sampleCurveDerivativeX = function(t) {
    return (3.0 * this.ax * t + 2.0 * this.bx) * t + this.cx;
};


UnitBezier.prototype.solveCurveX = function(x, epsilon) {
    if (typeof epsilon === 'undefined') epsilon = 1e-6;


    var t0, t1, t2, x2, i;


    // First try a few iterations of Newton's method -- normally very fast.
    for (t2 = x, i = 0; i < 8; i++) {


        x2 = this.sampleCurveX(t2) - x;
        if (Math.abs(x2) < epsilon) return t2;


        var d2 = this.sampleCurveDerivativeX(t2);
        if (Math.abs(d2) < 1e-6) break;


        t2 = t2 - x2 / d2;
    }


    // Fall back to the bisection method for reliability.
    t0 = 0.0;
    t1 = 1.0;
    t2 = x;


    if (t2 < t0) return t0;
    if (t2 > t1) return t1;


    while (t0 < t1) {


        x2 = this.sampleCurveX(t2);
        if (Math.abs(x2 - x) < epsilon) return t2;


        if (x > x2) {
            t0 = t2;
        } else {
            t1 = t2;
        }


        t2 = (t1 - t0) * 0.5 + t0;
    }


    // Failure.
    return t2;
};


UnitBezier.prototype.solve = function(x, epsilon) {
    return this.sampleCurveY(this.solveCurveX(x, epsilon));
};

export default UnitBezier;
import UnitBezier from './UnitBezier.js'
import GrahicLayer from './GrahicLayer.js'
function MvtImageryProvider(options) {
    options = Cesium.defaultValue(options, Cesium.defaultValue.EMPTY_OBJECT);

    this._tilingScheme = Cesium.defined(options.tilingScheme) ? options.tilingScheme : new Cesium.WebMercatorTilingScheme({ ellipsoid: options.ellipsoid });
    this._tileWidth = Cesium.defaultValue(options.tileWidth, 512);
    this._tileHeight = Cesium.defaultValue(options.tileHeight, 512);
    this._readyPromise = Cesium.when.resolve(true);
    //new 
    this._layers = options.layers;
    this._maximumLevel = options.maximumLevel;
    this._minimumLevel = options.minimumLevel;

    if (!window.ol) {
        throw new DeveloperError('请引入Openlayers类库!');
    }
    this._ol = window.ol;
    this._mvtParser = new this._ol.format.MVT();

    this._styleClass = new MapboxStreetsV6MvtStyle(options.style);
    this._key = Cesium.defaultValue(options.key, "");
    this._url = Cesium.defaultValue(options.url, "https://api.mapbox.com/v4/mapbox.mapbox-streets-v8,mapbox.mapbox-terrain-v2/{z}/{x}/{y}.vector.pbf?access_token={k}");

    var sw = this._tilingScheme._rectangleSouthwestInMeters;
    var ne = this._tilingScheme._rectangleNortheastInMeters;
    var mapExtent = [sw.x, sw.y, ne.x, ne.y];
    this._resolutions = ol.tilegrid.resolutionsFromExtent(
        mapExtent, 22, this._tileWidth);

    this._pixelRatio = 1;
    this._transform = [0.125, 0, 0, 0.125, 0, 0];
    this._replays = ["Default", "Image", "Polygon", "LineString", "Text"];

    this._tileQueue = new Cesium.TileReplacementQueue();
    this._cacheSize = 1000;
}
Object.defineProperties(MvtImageryProvider.prototype, {
    proxy: {
        get: function () {
            return undefined;
        }
    },

    tileWidth: {
        get: function () {
            return this._tileWidth;
        }
    },

    tileHeight: {
        get: function () {
            return this._tileHeight;
        }
    },
    layers: {
        get: function () {
            return this._layers;
        }
    },

    maximumLevel: {
        get: function () {
            // return undefined;
            return this._maximumLevel;
        }
    },

    minimumLevel: {
        get: function () {
            // return undefined;
            return this._minimumLevel;
        }
    },

    tilingScheme: {
        get: function () {
            return this._tilingScheme;
        }
    },

    rectangle: {
        get: function () {
            return this._tilingScheme.rectangle;
        }
    },

    tileDiscardPolicy: {
        get: function () {
            return undefined;
        }
    },

    errorEvent: {
        get: function () {
            return this._errorEvent;
        }
    },

    ready: {
        get: function () {
            return true;
        }
    },

    readyPromise: {
        get: function () {
            return this._readyPromise;
        }
    },

    credit: {
        get: function () {
            return undefined;
        }
    },

    hasAlphaChannel: {
        get: function () {
            return true;
        }
    }
});

MvtImageryProvider.prototype.getTileCredits = function (x, y, level) {
    return undefined;
};

MvtImageryProvider.prototype.pickFeatures = function (x, y, level, longitude, latitude) {
    return undefined;
};

MvtImageryProvider.prototype.requestImage = function (x, y, level, request) {
    var cacheTile = findTileInQueue(x, y, level, this._tileQueue);
    if (cacheTile != undefined) {
        return cacheTile;
    }
    else {
        var that = this;
        var url = this._url;
        url = url.replace('{x}', x).replace('{y}', y).replace('{z}', level).replace('{k}', this._key);

        var resource = Cesium.Resource.createIfNeeded(url);
        return resource.fetchArrayBuffer().then(function (arrayBuffer) {
            var canvas = document.createElement('canvas');
            canvas.width = 512;
            canvas.height = 512;
            var vectorContext = canvas.getContext('2d');

            var features = that._mvtParser.readFeatures(arrayBuffer);

            var styleFun = that._styleClass.getStyle();

            var extent = [0, 0, 4096, 4096];
            var _replayGroup = new ol.render.canvas.ReplayGroup(0, extent,
                8, true, 100);

            for (var i = 0; i < features.length; i++) {
                var feature =features[i];
                if(feature.getType()==='Point' && feature.get('layer').endsWith('_label'))
                {
                    let xy=ol.proj.transform(feature.getFlatCoordinates(),'EPSG:3857','EPSG:4326');
                    let _x=xy[0]*that._resolutions[level+1]*Cesium.Math.DEGREES_PER_RADIAN
                    let _y= (90 - 180 / (Math.pow(2, level)) * feature.getFlatCoordinates()[0])/that._resolutions[level]*Cesium.Math.DEGREES_PER_RADIAN
                    console.log(_x,_y)
                }
                
                var styles = styleFun(features[i], that._resolutions[level], level, that._styleClass.style);
                for (var j = 0; j < styles.length; j++) {
                    ol.renderer.vector.renderFeature_(_replayGroup, feature, styles[j], 16);
                }
            }
            _replayGroup.finish();

            _replayGroup.replay(vectorContext, that._pixelRatio, that._transform, 0, {}, that._replays, true);
            if (that._tileQueue.count > that._cacheSize) {
                trimTiles(that._tileQueue, that._cacheSize / 2);
            }

            canvas.xMvt = x;
            canvas.yMvt = y;
            canvas.zMvt = level;
            that._tileQueue.markTileRendered(canvas);

            _replayGroup = null;

            return canvas;
        }).otherwise(function (error) {
            return undefined;
        });
    }
};



function findTileInQueue(x, y, level, tileQueue) {
    var item = tileQueue.head;
    while (item != undefined && !(item.xMvt == x && item.yMvt == y && item.zMvt == level)) {
        item = item.replacementNext;
    }
    return item;
};

function removeQueue(tileReplacementQueue, item) {
    var previous = item.replacementPrevious;
    var next = item.replacementNext;

    if (item === tileReplacementQueue._lastBeforeStartOfFrame) {
        tileReplacementQueue._lastBeforeStartOfFrame = next;
    }

    if (item === tileReplacementQueue.head) {
        tileReplacementQueue.head = next;
    } else {
        previous.replacementNext = next;
    }

    if (item === tileReplacementQueue.tail) {
        tileReplacementQueue.tail = previous;
    } else {
        next.replacementPrevious = previous;
    }

    item.replacementPrevious = undefined;
    item.replacementNext = undefined;

    --tileReplacementQueue.count;
}

function trimTiles(tileQueue, maximumTiles) {
    var tileToTrim = tileQueue.tail;
    while (tileQueue.count > maximumTiles &&
        Cesium.defined(tileToTrim)) {
        var previous = tileToTrim.replacementPrevious;

        removeQueue(tileQueue, tileToTrim);
        tileToTrim = previous;
    }
};


//=========================官方mapbox-streets-v6 vector样式==================

// Styles for the mapbox-streets-v6 vector tile data set. Loosely based on
// http://a.tiles.mapbox.com/v4/mapbox.mapbox-streets-v6.json
function MapboxStreetsV6MvtStyle(style) {
    this.style = style
}
MapboxStreetsV6MvtStyle.prototype.getStyle = function () {
    var fill = new ol.style.Fill({ color: '' });
    var stroke = new ol.style.Stroke({ color: '', width: 1 });
    var polygon = new ol.style.Style({ fill: fill });
    var strokedPolygon = new ol.style.Style({ fill: fill, stroke: stroke });
    var line = new ol.style.Style({ stroke: stroke });
    var text = new ol.style.Style({
        text: new ol.style.Text({
            text: '', fill: fill, stroke: stroke
        })
    });
    var iconCache = {};
    function getIcon(iconName) {
        var icon = iconCache[iconName];
        if (!icon) {
            icon = new ol.style.Style({
                image: new ol.style.Icon({
                    src: 'https://cdn.rawgit.com/mapbox/maki/master/icons/' + iconName + '-15.svg',
                    imgSize: [15, 15]
                })
            });
            iconCache[iconName] = icon;
        }
        return icon;
    }
    function getData(index, values, zoom) {
        for (let i = index; i < values.length; i += 2) {
            const data = values[i];
            if (i === values.length - 2 && zoom >= data) {
                return values[i + 1]
            }
            if (zoom <= data) {
                return values[i + 1]
            }
        }
    }
    function getMatch(paints, cls) {
        let _dv = paints[paints.length - 1]
        if (paints[1][0] === "get") {
            let _v = cls.get(paints[1][1]);
            for (let i = 2; i < paints.length; i += 2) {
                const _n = paints[i];
                if (Array.isArray(_n)) {
                    if (_n.indexOf(_v) != -1) {
                        return paints[i + 1]
                    }
                    return _dv
                } else if (_v === paints[i]) {
                    return paints[i + 1]
                }
            }
        }
        return _dv
    }

    function getExponential(index, base, values, zoom, cls) {
        let curIndex = index
        let lowlevel = values[curIndex];
        let uplevel = values[values.length - 2];
        if (zoom <= lowlevel) {
            return values[curIndex + 1]
        }
        if (zoom >= uplevel) {
            return values[values.length - 1]
        }
        for (let i = index; i < values.length; i += 2) {
            curIndex = i;
            lowlevel = values[curIndex];
            if (i === values.length - 2) {
                curIndex = i - 2;
                break;
            }
            if (zoom <= lowlevel) {
                break;
            }
        }
        lowlevel = values[curIndex];
        uplevel = values[curIndex + 2];
        let lowVal = values[curIndex + 1]
        let upVal = values[curIndex + 3]
        if (Array.isArray(lowVal)) {
            lowVal = getPaint(lowVal, zoom, cls)
        }
        if (Array.isArray(upVal)) {
            upVal = getPaint(upVal, zoom, cls)
        }
        let dif = uplevel - lowlevel
        let pro = zoom - lowlevel
        let t = (Math.pow(base, pro) - 1) / (Math.pow(base, dif) - 1);
        if (dif === 0) {
            t = 0
            //  return lowVal * (1 - t) + upVal * t
        } else if (base === 1) {
            t = pro / dif
            //   return lowVal * (1 - t) + upVal * t
        }
        if (values[1][0] === "cubic-bezier") {
            //  let cb = getPaint(3, values, zoom);
            let ub = new UnitBezier(values[1][1], values[1][2], values[1][3], values[1][4])
            t = ub.solve(t)

        }
        return lowVal * (1 - t) + upVal * t

    }
    function getInterpolate(paints, level, cls) {
        if (paints[1][0] === "linear") {
            let _ds = getData(3, paints, level);
            // if (Array.isArray(_ds)) {
            //     _ds = getPaint(_ds, level, cls)
            // }
            if (typeof _ds === 'number') {
                return getExponential(3, 1, paints, level, cls)
            }
            return _ds
        }
        else {//if (paints[1][0] === "exponential") {
            // let _ds = getData(3, paints, level);

            //  console.log((Math.pow(paints[1][1],level-getData(3, paints, level))-1)-(Math.pow(paints[1][1],getData(3, paints, level)-level)-1))
            return getExponential(3, paints[1][1], paints, level, cls)
        }
        // else if (paints[1][0] === "cubic-bezier") {
        //     let _ds = getData(3, paints, level);
        //     if (Array.isArray(_ds)) {
        //         return getPaint(_ds, level, cls)
        //     }
        //     return paints[paints.length - 1]
        // }
        return paints;

    }
    function getCoalesce(index, values, cls) {
        for (let i = index; i < values.length; i++) {
            const data = values[i];
            let f = data[1]
            if (f === 'name_en') {
                f = 'name_zh-Hans'
            }
            let _v = cls.get(f)
            if (data[0] === "get" && _v) {
                return _v
            }
        }
    }
    function getPaint(paints, level, cls) {
        const start = paints[0];
        if (start === 'match') {
            let _setp = getMatch(paints, cls)
            if (Array.isArray(_setp)) {
                return getPaint(_setp, level, cls)
            }
            return _setp
        }
        else if (start === 'interpolate') {
            let _setp = getInterpolate(paints, level, cls)
            if (Array.isArray(_setp)) {
                return getPaint(_setp, level, cls)
            }
            return _setp
        }
        //  return getInterpolate(paints, level, cls)
        else if (start === 'step') {
            let _setp = getStep(paints, level, cls)
            if (Array.isArray(_setp)) {
                return getPaint(_setp, level, cls)
            }
            return _setp
        }
        else if (start === 'get') {
            let f = paints[1]
            if (f === 'name_en') {
                f = 'name_zh-Hans'
            }
            let _v = cls.get(f)
            return _v
        }
        else if (start === 'coalesce') {
            return getCoalesce(1, paints, cls)
        }
        return paints;
    }
    function getStep(paints, level, cls) {
        let _v = level
        if (paints[1][0] === "get") {
            _v = cls[paints[1][1]]
        }
        for (let i = 3; i < paints.length; i += 2) {
            if (_v >= paints[i]) {
                return paints[i + 1]
            }

        }
        return paints[2]
    }
    function getF(filters, cls, level) {
        var geom = cls.getGeometry().getType();
        switch (filters[0]) {
            case "all":
                {
                    for (let i = 1; i < filters.length; i++) {
                        if (!getF(filters[i], cls, level))
                            return false;
                    }
                    return true;
                }
            case "match":
                {
                    let _match=getPaint(filters, level, cls);
                    if (Array.isArray(_match)) {
                        return getF(_match, cls, level)
                    }
                    return _match
                    if (Array.isArray(_match)) {
                        console.log(0)
                    }
                    return _match
                }
            case "step":
                {
                    let _setp = getStep(filters, level, cls)
                    if (Array.isArray(_setp)) {
                        return getF(_setp, cls, level)
                    }
                    return _setp
                }
            case '==':
                {
                    if (filters[1][0] === "get" && cls.get(filters[1][1]) === filters[2]) {
                        return true;
                    } else if (filters[1][0] === "geometry-type" && geom.indexOf(filters[2]) != -1) {
                        return true;
                    }
                   
                }
                break
            case '<=':
                let v = cls.get(filters[1][1])
                if (Array.isArray(filters[2])) {
                    if (filters[2][0] === "+") {
                        v += getPaint(filters[2][1], level, cls);
                    }
                }
                if (filters[1][0] === "get" && v <= filters[2]) {
                    return true;
                }
                break
            case '>=':
                if (filters[1][0] === "get" && cls.get(filters[1][1]) >= filters[2]) {
                    return true;
                }
                break
            case '!=':
                if (filters[1][0] === "get" && cls.get(filters[1][1]) != filters[2]) {
                    return true;
                } else if (filters[1][0] === "geometry-type" && geom.indexOf(filters[2]) == -1) {
                    return true;
                }
                break


        }
        return false;
    }
    function getFilter(filters, cls, level) {
        return getF(filters, cls, level)
    }
    var styles = [];
    return (feature, resolution, level, style) => {
        var length = 0;
        var layer = feature.get('layer');
        var maki = feature.get('maki');
        var geom = feature.getGeometry().getType();
        let l_type = 'fill'
        if (geom === "Polygon") {
            l_type = 'fill'
        }
        else if (geom.indexOf("LineString") != -1) {
            l_type = 'line'
        }
        else {
            l_type = 'symbol'
        }
        // geom console.log(layer+geom)
        let lys = style.layers.filter(p => p["source-layer"] === layer && p.type === l_type).sort((a, b) => a.minzoom || 0 - b.minzoom || 0)
        for (let j = 0; j < lys.length; j++) {
            const _ly = lys[j];

            if ((_ly.filter && !getFilter(_ly.filter, feature, level)) || (_ly.minzoom && _ly.minzoom > level) || (_ly.maxzoom && _ly.maxzoom < level)) {

                continue;
            }
            try {
                var fill = new ol.style.Fill({ color: '' });
                var stroke = new ol.style.Stroke({ color: '', width: 1 });
                var polygon = new ol.style.Style({ fill: fill });
                var strokedPolygon = new ol.style.Style({ fill: fill, stroke: stroke });
                var line = new ol.style.Style({ stroke: stroke });
                var text = new ol.style.Style({
                    text: new ol.style.Text({
                        text: '', fill: fill, stroke: stroke
                    })
                });
                // _ly.minzoom = _ly.minzoom || 0
                // _ly.maxzoom = _ly.maxzoom || 40
                //!getFilter(_ly.filter, feature)||
                if (_ly.type === "line") {
                    let color = getPaint(_ly.paint['line-color'], level, feature);
                    let op = 1
                    if (_ly.paint['line-opacity']) {
                        op = getPaint(_ly.paint['line-opacity'], level, feature);

                        if (op != 1 && color.indexOf('hsla') == -1) {
                            color = color.replace('hsl', 'hsla').replace(')', ',' + op + ")")
                        }
                        else if (op != 1) {
                            let ss = color.split(',')
                            ss[ss.length - 1] = op + ")"
                            color = ss.join()
                        }
                    }
                    stroke.setColor(color);
                    if (_ly.paint['line-width']) {
                        let w = getPaint(_ly.paint['line-width'], level, feature);
                        stroke.setWidth(w);
                    }
                    if (_ly.layout['line-cap'])
                        stroke.setLineCap(getPaint(_ly.layout['line-cap'], level, feature));
                    if (_ly.layout['line-join'])
                        stroke.setLineJoin(getPaint(_ly.layout['line-join'], level, feature));
                    if (_ly.paint['line-dasharray']) {
                        let das = getPaint(_ly.paint['line-dasharray'], level, feature);
                        if (das[0] === "literal") {
                            das = das[1]
                        }
                        stroke.setLineDash(das);
                    }

                    styles[length++] = line;
                }
                else if (_ly.type === "fill") {
                    let color = getPaint(_ly.paint['fill-color'], level, feature);
                    if (_ly.paint['fill-opacity']) {
                        let op = getPaint(_ly.paint['fill-opacity'], level, feature);

                        if (op != 1 && color.indexOf('hsla') == -1) {
                            color = color.replace('hsl', 'hsla').replace(')', ',' + op + ")")
                        }
                        else if (op != 1) {
                            let ss = color.split(',')
                            ss[ss.length - 1] = op + ")"
                            color = ss.join()
                        }
                    }
                    fill.setColor(color);
                    if (_ly.paint['fill-outline-color']) {
                        let _color = getPaint(_ly.paint['fill-outline-color'], level, feature);

                        stroke.setColor(_color);
                        stroke.setWidth(1);
                        styles[length++] = strokedPolygon;
                    } else {
                        styles[length++] = polygon;
                    }

                }
                else if (maki) {
                    styles[length++] = getIcon(maki);
                }
                else if (layer.endsWith('_label')) { // 省市县 城市名称 标注 
                    let txtStyle= text.getText();
                    txtStyle.setText(getPaint(_ly.layout['text-field'], level, feature));
                   
                    if (_ly.layout['text-size']) {
                        let fontsize = getPaint(_ly.layout['text-size'], level-4, feature)
                        txtStyle.setFont('normal ' + fontsize + 'px "Open Sans", "Arial Unicode MS"');
                    }
                    if (_ly.layout['text-offset']) {
                        let off = getPaint(_ly.layout['text-offset'], level, feature)
                        if (off[0] === "literal") {
                            off = off[1]
                        }
                        txtStyle.setOffsetX(off[0])
                        txtStyle.setOffsetY(off[1])
                    }
                   
                    fill.setColor(getPaint(_ly.paint['text-color'], level, feature));
                    stroke.setColor('rgba(255,255,255,0.8)');
                    stroke.setWidth(1);

                    styles[length++] = text;
                }

            } catch (e) {
                console.log(e)
            }

        }

        styles.length = length
        return styles;
    };
}
export default MvtImageryProvider;

你可能感兴趣的:(cesium,mvt,gis)