WME SC Emergency Management Closures

Adds a road closure layer for Sourth Carolina Emergency Management

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 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();
})();