tmcgee/cmv-widgets

View on GitHub
widgets/OpenExternalMap.js

Summary

Maintainability
A
0 mins
Test Coverage
define([
    'dojo/_base/declare',
    'dijit/_WidgetBase',
    'dijit/_TemplatedMixin',
    'dijit/_WidgetsInTemplateMixin',

    'dojo/_base/lang',
    'dojo/_base/array',
    'dojo/topic',
    'dojo/dom-style',
    'dojo/dom-construct',
    'dojo/number',
    'dojo/aspect',

    'esri/geometry/Point',

    'proj4js/proj4',

    'dojo/text!./OpenExternalMap/templates/OpenExternalMap.html',

    'dojo/i18n!./OpenExternalMap/nls/resource',

    'gis/plugins/Google',

    'dijit/form/Button',
    'dijit/form/ToggleButton',

    'xstyle/css!./OpenExternalMap/css/OpenExternalMap.css'
], function (
    declare,
    _WidgetBase,
    _TemplatedMixin,
    _WidgetsInTemplateMixin,

    lang,
    array,
    topic,
    domStyle,
    domConstruct,
    number,
    aspect,

    Point,

    proj4,

    template,

    i18n,

    GoogleMapsLoader,

    Button
) {
    var google = null;
    return declare([_WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin], {
        widgetsInTemplate: true,
        templateString: template,
        i18n: i18n,
        mapClickMode: null,

        // the map providers and buttons  to include
        mapProviders: [
            {
                name: 'Google',
                buttons: [
                    {
                        label: 'Hybrid Map',
                        iconClass: 'fab fa-google fa-fw',
                        url: 'https://www.google.com/maps/place/@{lat},{lng},{zoom}z/data=!3m1!1e3' //https://www.google.com/maps/@{lat},{lng},1000a,20y,0t/data=!3m1!1e3'
                    },
                    {
                        label: 'StreetView',
                        iconClass: 'fas fa-street-view fa-fw',
                        isStreetView: true,
                        url: 'https://maps.google.com/maps?q=&layer=c&cbll={lat},{lng}'
                    },
                    {
                        label: 'Google Earth',
                        iconClass: 'fas fa-globe fa-fw',
                        url: 'https://earth.google.com/web/@{lat},{lng},146.726a,666.616d,35y,0h,45t,0r'
                    }
                ]
            },
            {
                name: 'Bing Maps',
                buttons: [
                    {
                        label: 'Hybrid Map',
                        iconClass: 'fab fa-windows fa-fw',
                        url: 'https://www.bing.com/maps?cp={lat}~{lng}&lvl={zoom}&sstyle=h'
                    },
                    {
                        label: 'Bird\'s Eye',
                        iconClass: 'fas fa-building fa-fw',
                        url: 'https://bing.com/maps/?cp={lat}~{lng}&style=o'
                    },
                    {
                        label: 'Streetside',
                        iconClass: 'fas fa-street-view fa-fw',
                        url: 'https://bing.com/maps/?cp={lat}~{lng}&lvl=19&dir=113.8911&pi=-0.284&style=x&v=2&sV=1'
                    }
                ]
            },
            {
                name: 'Other Maps',
                buttons: [
                    {
                        label: 'MapQuest',
                        iconClass: 'fas fa-globe fa-fw',
                        url: 'https://www.mapquest.com/map?q={lat},{lng}&zoom={zoom}&maptype=hybrid'
                    },
                    {
                        label: 'OpenStreetMap',
                        iconClass: 'fas fa-globe fa-fw',
                        url: 'http://www.openstreetmap.org/?lat={lat}&lon={lng}&zoom={zoom}'
                    },
                    {
                        // https://mapchannels.com/DualMaps.aspx
                        // Use your own Google API key: https://mapchannels.com/ApiKey.aspx
                        label: 'Dual Maps',
                        iconClass: 'fas fa-globe fa-fw',
                        url: 'http://data.mapchannels.com/mm/dual2/map.htm?x={lng}&y={lat}&z={zoom}&gm=0&ve=3&gc=0&xb={lng}&yb={lat}zb=1&db=0&bar=0&mw=1&sv=1&svb=0&mi=0&mg=1&mv=1'
                    }
                ]
            }
        ],

        // group the providers by their name, otherwise group all buttons together
        groupProviders: true,

        // set the Lat/Lng from the map's center point when the map's extent is updated
        useMapCenter: true,

        // Show coordinate in Degrees, Minutes and Seconds
        showDMS: true,

        // conversion factor for DMS
        unitScale: 2,

        // is street view available at the coordinates
        streetViewAvailable: false,

        // not currently implemented
        useEmbeddedWindow: false,

        // in case this changes some day
        proj4BaseURL: 'https://epsg.io/',

        //  options are ESRI, EPSG and SR-ORG
        // See http://spatialreference.org/ for more information
        proj4Catalog: 'EPSG',

        // if desired, you can load a projection file from your server
        // instead of using one from spatialreference.org
        // i.e., http://server/projections/102642.js
        projCustomURL: null,

        _mapButtons: [],

        postCreate: function () {
            this.inherited(arguments);

            this.own(topic.subscribe('mapClickMode/currentSet', lang.hitch(this, 'setMapClickMode')));

            if (this.parentWidget) {
                if (this.parentWidget.toggleable) {
                    this.own(aspect.after(this.parentWidget, 'toggle', lang.hitch(this, function () {
                        this.onLayoutChange(this.parentWidget.open);
                    })));
                }
            }

            //load the google api asynchronously
            GoogleMapsLoader.load(lang.hitch(this, function (g) {
                //store a reference to google
                google = g;
            }));

            var wkid = this.map.spatialReference.wkid;
            if (wkid === 102100) { // ESRI --> EPSG
                wkid = 3857;
            }
            var key = this.proj4Catalog + ':' + String(wkid);
            if (!proj4.defs[key]) {
                var url = this.proj4BaseURL + String(wkid) + '.js';
                require([url]);
            }

            this.addProviders();

            this.map.on('click', lang.hitch(this, 'onMapPoint'));
            if (this.useMapCenter) {
                this.map.on('extent-change', lang.hitch(this, 'onMapCenter'));
                if (this.map.extent) {
                    this.onMapCenter({
                        extent: this.map.extent
                    });
                }
            }
        },

        addProviders: function () {
            var btnContainer = null,
                btn = null,
                providerClass = null,
                buttonsClass = null,
                buttonClass = null;
            if (!this.groupProviders) {
                btnContainer = domConstruct.create('div', {
                    class: 'externalMapButtons'
                }, this.providerContainer);
            }
            array.forEach(this.mapProviders, lang.hitch(this, function (provider) {
                if (this.groupProviders) {
                    if (provider.name) {
                        providerClass = 'mapLabel-' + provider.name.replace(' ', '-').toLowerCase();
                        domConstruct.create('div', {
                            class: 'mapLabel ' + providerClass,
                            innerHTML: provider.name
                        }, this.providerContainer);
                    }

                    buttonsClass = 'mapButtons-' + provider.name.replace(' ', '-').toLowerCase();
                    btnContainer = domConstruct.create('div', {
                        class: 'mapButtons ' + buttonsClass
                    }, this.providerContainer);
                }

                array.forEach(provider.buttons, lang.hitch(this, function (button) {
                    providerClass = (provider.name) ? 'mapButton-' + provider.name.replace(' ', '-').toLowerCase() : '';
                    buttonClass = ((providerClass.length > 0) ? providerClass : 'mapButton') + '-' + button.label.replace(' ', '-').toLowerCase();
                    btn = new Button({
                        label: button.label,
                        class: 'mapButton ' + buttonClass + ' ' + providerClass,
                        iconClass: button.iconClass
                    });
                    btn.on('click', lang.hitch(this, 'openMap', button));
                    btn.startup();
                    btn.set('disabled', true);
                    btn.placeAt(btnContainer, 'last');
                    this._mapButtons.push(btn);
                }));
            }));
        },

        onMapPoint: function (evt) {
            if (this.mapClickMode === 'externalmap') {
                var mapPoint = evt.mapPoint;
                if (!mapPoint) {
                    return;
                }
                this.processPoint(mapPoint);
            }
        },

        onMapCenter: function (opts) {
            var extent = opts.extent;
            var centerPoint = extent.getCenter();
            this.processPoint(centerPoint);
        },

        onClose: function () {
            if (this.mapClickMode === 'externalmap') {
                this.connectMapClick();
            }
        },

        onLayoutChange: function (open) {
            if (!open) {
                this.onClose();
            }
        },

        placePoint: function () {
            if (this.OEMapButtonDijit.get('checked')) {
                this.disconnectMapClick();
            } else {
                this.connectMapClick();
            }
        },

        disconnectMapClick: function () {
            this.OEMapButtonDijit.set('checked', true);
            this.map.setMapCursor('crosshair');
            topic.publish('mapClickMode/setCurrent', 'externalmap');
        },

        connectMapClick: function () {
            this.OEMapButtonDijit.set('checked', false);
            if (this.mapClickMode === 'externalmap') {
                this.map.setMapCursor('auto');
                topic.publish('mapClickMode/setDefault');
            }
        },

        processPoint: function (point) {
            var latLng = this.convertCoordinates(point);
            this.checkStreetView(latLng);

            this.OEMapLatitudeDijit.set('value', latLng[1].toFixed(6));
            this.OEMapLongitudeDijit.set('value', latLng[0].toFixed(6));
            window.setTimeout(lang.hitch(this, 'connectMapClick'), 100);

            this.checkButtons();
        },

        openMap: function (button) {
            if (!this.checkCoordinates(button)) {
                return;
            }
            var lat = this.OEMapLatitudeDijit.get('value');
            var lng = this.OEMapLongitudeDijit.get('value');
            var zoom = '';
            if (this.map.spatialReference.wkid === 102100) {
                zoom = this.map.getLevel();
            }
            var url = lang.replace(button.url, {
                lat: lat,
                lng: lng,
                zoom: zoom
            });

            if (this.useEmbeddedWindow) {
                // do something here with floating window
            } else {
                window.open(url);
            }
        },

        checkCoordinates: function (button) {
            if (!this.validateCoordinates('lat')) {
                topic.publish('growler/growl', {
                    title: button.label,
                    message: i18n.latitudeInvalid,
                    level: 'error',
                    timeout: 3000
                });
                return false;
            }

            if (!this.validateCoordinates('lng')) {
                topic.publish('growler/growl', {
                    title: button.label,
                    message: i18n.longitudeInvalid,
                    level: 'error',
                    timeout: 3000
                });
                return false;
            }

            if (button.isStreetView && !this.streetViewAvailable) {
                topic.publish('growler/growl', {
                    title: button.label,
                    message: i18n.streetViewNotAvailable,
                    level: 'error',
                    timeout: 3000
                });
                return false;
            }

            return true;

        },

        validateCoordinates: function (checkOne) {
            var lat = this.OEMapLatitudeDijit.get('value');
            if (checkOne === 'lat' || !checkOne) {
                if (lat === '' || isNaN(lat) || lat < -85.05 || lat > 85.05) {
                    return false;
                }
            }

            var lng = this.OEMapLongitudeDijit.get('value');
            if (checkOne === 'lng' || !checkOne) {
                if (lng === '' || isNaN(lng) || lng < -180 || lng > 180) {
                    return false;
                }
            }

            return true;
        },

        checkStreetView: function (latLng) {
            /*
                Only check for availability of street view if a key is provided.
                Currently, there is not a reliable method to check that the key is valid.
            */
            if (!GoogleMapsLoader.KEY || !google) {
                this.streetViewAvailable = true;
                return;
            }

            this.streetViewAvailable = false;
            if (!this.panoramaService) {
                this.panoramaService = new google.maps.StreetViewService();
            }
            if (google) {
                var googlePt = new google.maps.LatLng(latLng[1], latLng[0]);
                this.panoramaService.getPanoramaByLocation(googlePt, 50, lang.hitch(this, function (geoPt, res) {
                    this.streetViewAvailable = (res === google.maps.StreetViewStatus.OK);
                }));
            }
        },

        checkButtons: function () {
            var valid = this.validateCoordinates();
            array.forEach(this._mapButtons, function (button) {
                button.set('disabled', !valid);
            });
        },

        latitudeChanged: function () {
            var valid = this.validateCoordinates('lat'), dms = '';
            if (valid && this.showDMS) {
                var val = this.OEMapLatitudeDijit.get('value');
                dms = this.decToDMS(val, true);
            }
            this.OEMapLatitudeDMSDijit.innerHTML = dms;
            this.checkButtons();
        },

        longitudeChanged: function () {
            var valid = this.validateCoordinates('lng'), dms = '';
            if (valid && this.showDMS) {
                var val = this.OEMapLongitudeDijit.get('value');
                dms = this.decToDMS(val, false);
            }
            this.OEMapLongitudeDMSDijit.innerHTML = dms;
            this.checkButtons();
        },

        decToDMS: function (l, lat) {
            var dir = '?',
                abs = Math.abs(l),
                deg = parseInt(abs, 10),
                min = (abs - deg) * 60,
                minInt = parseInt(min, 10),
                sec = number.round((min - minInt) * 60, this.unitScale),
                minIntTxt = (minInt < 10) ? '0' + minInt : minInt,
                secTxt = (sec < 10) ? '0' + sec : sec;

            if (lat) {
                dir = (l > 0) ? 'N' : 'S';
            } else {
                dir = (l > 0) ? 'E' : 'W';
            }

            return deg + '&deg;&nbsp;' + minIntTxt + '\'&nbsp;' + secTxt + '"&nbsp;' + dir;
        },

        convertCoordinates: function (mapPoint) {
            // convert the map point's coordinate system into lat/long
            var wkid = this.map.spatialReference.wkid;
            if (wkid === 102100) { // ESRI --> EPSG
                wkid = 3857;
            }
            var key = this.proj4Catalog + ':' + String(wkid);
            return proj4(proj4.defs[key]).inverse([mapPoint.x, mapPoint.y]);
        },

        setMapClickMode: function (mode) {
            this.mapClickMode = mode;
        }
    });
});