WME SC Emergency Management Closures

Adds a road closure layer for Sourth Carolina Emergency Management

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         WME SC Emergency Management Closures
// @namespace    https://greasyfork.org/users/45389
// @version      0.5
// @description  Adds a road closure layer for Sourth Carolina Emergency Management
// @author       MapOMatic
// @include      /^https:\/\/(www|beta)\.waze\.com\/(?!user\/)(.{2,6}\/)?editor\/?.*$/
// @license      GNU GPLv3
// @grant        GM_xmlhttpRequest
// @connect      arcgis.com
// ==/UserScript==

(function() {
    'use strict';

    var _settingsStoreName = 'wme_sc_emergency_management_closures';
    var _alertUpdate = false;
    var _debugLevel = 0;
    var _scriptVersion = GM_info.script.version;
    var _scriptVersionChanges = [
        GM_info.script.name + '\nv' + _scriptVersion + '\n\nWhat\'s New\n------------------------------\n',
        '\n- .',
    ].join('');
    var _tabDiv = {};  // stores the user tab div so it can be restored after switching back from Events mode to Default mode
    var _mapLayer = null;
    var _styles = {};
    var _settings = {};
    var _statesHash = {
        'Alabama': 'AL',
        'Alaska': 'AK',
        'American Samoa': 'AS',
        'Arizona': 'AZ',
        'Arkansas': 'AR',
        'California': 'CA',
        'Colorado': 'CO',
        'Connecticut': 'CT',
        'Delaware': 'DE',
        'District Of Columbia': 'DC',
        'Federated States Of Micronesia': 'FM',
        'Florida': 'FL',
        'Georgia': 'GA',
        'Guam': 'GU',
        'Hawaii': 'HI',
        'Idaho': 'ID',
        'Illinois': 'IL',
        'Indiana': 'IN',
        'Iowa': 'IA',
        'Kansas': 'KS',
        'Kentucky': 'KY',
        'Louisiana': 'LA',
        'Maine': 'ME',
        'Marshall Islands': 'MH',
        'Maryland': 'MD',
        'Massachusetts': 'MA',
        'Michigan': 'MI',
        'Minnesota': 'MN',
        'Mississippi': 'MS',
        'Missouri': 'MO',
        'Montana': 'MT',
        'Nebraska': 'NE',
        'Nevada': 'NV',
        'New Hampshire': 'NH',
        'New Jersey': 'NJ',
        'New Mexico': 'NM',
        'New York': 'NY',
        'North Carolina': 'NC',
        'North Dakota': 'ND',
        'Northern Mariana Islands': 'MP',
        'Ohio': 'OH',
        'Oklahoma': 'OK',
        'Oregon': 'OR',
        'Palau': 'PW',
        'Pennsylvania': 'PA',
        'Puerto Rico': 'PR',
        'Rhode Island': 'RI',
        'South Carolina': 'SC',
        'South Dakota': 'SD',
        'Tennessee': 'TN',
        'Texas': 'TX',
        'Utah': 'UT',
        'Vermont': 'VT',
        'Virgin Islands': 'VI',
        'Virginia': 'VA',
        'Washington': 'WA',
        'West Virginia': 'WV',
        'Wisconsin': 'WI',
        'Wyoming': 'WY'
    };
    var _stateSettings = {
        SC: {
            baseUrl: '',
            mapLayers: [
                { layerID:'https://services1.arcgis.com/VaY7cY9pvUYUP1Lf/ArcGIS/rest/services/CLOSURES_06_OCT_LINES/FeatureServer/0', outFields:[''] },
                { layerID:'https://services1.arcgis.com/VaY7cY9pvUYUP1Lf/arcgis/rest/services/Hurricane_Matthew_Closures_Lines/FeatureServer/0', outFields:[''] },
            ],
            colors: {closure:'#ff0000'}
        }
    };

    function log(message, level) {
        if (message && level <= _debugLevel) {
            console.log('WME SCEM: ' + message);
        }
    }

    function loadSettingsFromStorage() {
        var settings = $.parseJSON(localStorage.getItem(_settingsStoreName));
        if(!settings) {
            settings = {
                lastVersion:null,
                layerVisible:true
            };
        } else {
            settings.layerVisible = true; //(settings.layerVisible === true);
        }
        _settings = settings;
    }

    function saveSettingsToStorage() {
        if (localStorage) {
            var settings = {
                lastVersion: _scriptVersion,
                layerVisible: _mapLayer.visibility,
            };
            localStorage.setItem(_settingsStoreName, JSON.stringify(settings));
            log('Settings saved', 1);
        }
    }

    function getLineWidth() {
        return 8 * Math.pow(1.15, (W.map.getZoom()-1));
    }

    function draw(buckets) {
        _mapLayer.removeAllFeatures();
        for(var i=buckets.length-1; i>-1; i--) {
            _mapLayer.addFeatures(buckets[i]);
        }
    }

    function processFeatures(data,context) {
        var layer = context.layer;
        var stateSettings = context.stateSettings;
        var styles = {};
        for (var prefix in stateSettings.colors) {
            var color = stateSettings.colors[prefix];
            styles[prefix] = {
                strokeColor: color,
                strokeDashstyle: "solid",
                strokeOpacity: 0.6,
                strokeWidth: getLineWidth()
            };
        }

        data.features.forEach(function(feature) {
            var style = styles.closure;
            var lineFeatures = [];
            feature.geometry.paths.forEach(function(path){
                var pointList = [];
                var newPoint = null;
                path.forEach(function(point){
                    pointList.push(new OpenLayers.Geometry.Point(point[0],point[1]));
                });
                context.buckets[0].push( new OpenLayers.Feature.Vector(
                    new OpenLayers.Geometry.LineString(pointList),null,style
                ));
            });
        });

        context.callCount.count -= 1;
        if (context.callCount.count === 0) {
            //console.log(context.buckets);
            draw(context.buckets);
        }
        if (data.exceededTransferLimit) {
            log('Exceeded server\'s feature transfer limit.  Some features may not be drawn.',0);
        }
    }

    function fetchFeatures() {
        var visibleStates = [];
       //debugger;
        console.log(W.model.states.additionalInfo);
        W.model.states.additionalInfo.forEach(function(state) {
            visibleStates.push(_statesHash[state.name]);
        });

        var zoom = W.map.getZoom();
        var ext = W.map.getExtent();

        var geometry = {
            xmin: ext.left,
            ymin: ext.bottom,
            xmax: ext.right,
            ymax: ext.top
        };

        var geometryStr = JSON.stringify(geometry).replaceAll('{','%7B').replaceAll('}','%7D').replaceAll('"','%22').replaceAll(':','%3A').replaceAll(',','%2C');
        var callCount = 0;
        var buckets = [];
        var urls = [];
        var contexts = [];

        visibleStates.forEach(function(stateAbbr) {
            var state = _stateSettings[stateAbbr];
            if (state) {
                for(var i=0; i<7; i++) {buckets.push([]);}
                state.mapLayers.forEach(function(layer) {
                    if (!layer.zoomLevels || layer.zoomLevels.indexOf(zoom) !== -1) {
                        var context = {
                            stateSettings: state,
                            layer: layer,
                            buckets: buckets
                        };
                        var url = state.baseUrl + layer.layerID + '/query?';
                        url += 'geometry=' + geometryStr;
                        url += '&outFields=' + layer.outFields.join('%2C');
                        url += '&returnGeometry=true&spatialRel=esriSpatialRelIntersects&geometryType=esriGeometryEnvelope&inSR=102100&outSR=3857&f=json';
                        urls.push(url);
                        console.log(url);
                        contexts.push(context);
                        callCount += 1;

                    }
                });
            }
        });
        var countObj = {count: callCount};
        for (var j=0; j<urls.length; j++) {
            contexts[j].callCount = countObj;
            console.log(urls[j]);
            GM_xmlhttpRequest({
                context: contexts[j],
                method: "GET",
                url: urls[j],
                onload: function(res) {
                    processFeatures($.parseJSON(res.responseText), res.context);
                }
            });
        }
    }

    function onLayerVisibilityChanged(evt) {
        saveSettingsToStorage();
    }

    function restoreUserTab() {
        // $('#user-tabs > .nav-tabs').append(_tabDiv.tab);
        // $('#user-info > .flex-parent > .tab-content').append(_tabDiv.panel);
        // $('#fcl-state-select').change(function () {
        //     _settings.activeStateAbbr = this.value;
        //     saveSettingsToStorage();
        //     fetchFeatures();
        // });
    }

    function onModeChanged(model, modeId, context) {
        if(!modeId || modeId === 1) {
            restoreUserTab();
        }
    }

    function showScriptInfoAlert() {
        /* Check version and alert on update */
        if (_alertUpdate && _scriptVersion !== _settings.lastVersion) {
            alert(_scriptVersionChanges);
        }
    }

    function initLayer(){
        var mapLayerZIndex = 334;
        _mapLayer = new OpenLayers.Layer.Vector("SC Emergency Closures ", {
            uniqueName: "__SCECRoadsLayer",
            displayInLayerSwitcher: false
        });
        I18n.translations[W.location.locale].layers.name.__FCLayer = "SC Emergency Closures";
        W.map.addLayer(_mapLayer);
        _mapLayer.setZIndex(mapLayerZIndex);

        _mapLayer.displayInLayerSwitcher = true;
        _mapLayer.events.register('visibilitychanged',null,onLayerVisibilityChanged);
        _mapLayer.setVisibility(_settings.layerVisible);
        // Hack to fix layer zIndex.  Some other code is changing it sometimes but I have not been able to figure out why.
        // It may be that the FC layer is added to the map before some Waze code loads the base layers and forces other layers higher.
        var checkLayerZIndex = function(layerZIndex) {
            if (_mapLayer.getZIndex() != mapLayerZIndex)  {
                //log("ADJUSTED LAYER Z-INDEX",1);
                _mapLayer.setZIndex(mapLayerZIndex);
            }
        };

        setInterval(function(){checkLayerZIndex(mapLayerZIndex);}, 200);

        W.map.events.register("moveend",W.map,function(e){
            fetchFeatures();
            return true;
        },true);

        // for(var i=0; i<_activeState.defaultColors.length; i++) {
        //     var color = _activeState.defaultColors[i];
        //     var fc = i + 1;
        //     _styles[fc] = {
        //         strokeColor: color,
        //         strokeDashstyle: "solid",
        //         strokeOpacity: 0.5,
        //         strokeWidth: getLineWidth()
        //     };
        // }
    }

    function initUserPanel() {
        //         _tabDiv.tab = $('<li>').append(
        //             $('<a>', {'data-toggle':'tab', href:'#sidepanel-fc-layer'}).text('FC Layer')
        //         );

        //         _tabDiv.panel = $('<div>', {class:'tab-pane', id:'sidepanel-fc-layer'});/*.append(
        //             $('<div>',  {class:'side-panel-section>'}).append(
        //                 $('<div>', {class:'form-group'}).append(
        //                     $('<label>', {class:'control-label'}).text('Select a state')
        //                 ).append(
        //                     $('<div>', {class:'controls', id:'fcl-state-select-container'}).append(
        //                         $('<div>').append(
        //                             $('<select>', {id:'fcl-state-select',class:'form-control disabled',style:'disabled'})
        //                             .append($('<option>', {value:'IN'}).text('Indiana'))
        //                             //.append($('<option>', {value:'KY'}).text('Kentucky'))
        //                             .append($('<option>', {value:'MD'}).text('Maryland'))
        //                             .append($('<option>', {value:'MI'}).text('Michigan'))
        //                             .append($('<option>', {value:'NC'}).text('North Carolina'))
        //                             .append($('<option>', {value:'VA'}).text('Virginia'))
        //                             .val(_settings.activeStateAbbr)
        //                             // ).append(
        //                             //     $('<div>',{class:'controls-container'})
        //                             //     .append($('<input>', {type:'checkbox',class:'csSettingsCheckBox',name:'csDirectionButtonsCheckBox',id:'csDirectionButtonsCheckBox'}))
        //                             //     .append($('<label>', {for:'csDirectionButtonsCheckBox'}).text('Add road direction buttons'))
        //                             // ).append(
        //                             //     $('<input class="jscolor" value="ab2567">').change(function(evt) {console.log(evt);})
        //                         )
        //                     )
        //                 )
        //             )
        //         );*/

        //         restoreUserTab();
    }

    function initGui() {
        initLayer();
        initUserPanel();
        showScriptInfoAlert();
    }

    function init() {
        loadSettingsFromStorage();
        String.prototype.replaceAll = function(search, replacement) {
            var target = this;
            return target.replace(new RegExp(search, 'g'), replacement);
        };
        //_activeState = _stateSettings[_settings.activeStateAbbr];
        initGui();
        unsafeWindow.addEventListener('beforeunload', function saveOnClose() { saveSettingsToStorage(); }, false);
        Waze.app.modeController.model.bind('change:mode', onModeChanged);
        fetchFeatures();
        log('Initialized.', 0);
    }

    function bootstrap() {
        if (W && W.loginManager &&
            W.loginManager.events.register && W.model && W.model.states && W.model.states.additionalInfo &&
            W.map && W.loginManager.isLoggedIn()) {
            log('Initializing...', 0);
            init();
        } else {
            log('Bootstrap failed. Trying again...', 0);
            setTimeout(function () {
                bootstrap();
            }, 1000);
        }
    }

    log('Bootstrap...', 0);
    bootstrap();
})();