CruisersWiki:TOl3chartlet.js
From CruisersWiki
(Difference between revisions)
Line 577: | Line 577: | ||
evt.pixel, | evt.pixel, | ||
function(feature, layer) { | function(feature, layer) { | ||
- | return | + | return feature; |
- | } | + | }, |
+ | { layerFilter: isClickableLayer } | ||
); | ); | ||
if (feature && ! feature.get('noclickable')) { | if (feature && ! feature.get('noclickable')) { |
Revision as of 11:02, 29 October 2017
///* ol3Chartlet.js Copyright (c) 2016 Vadim Shlyakhov Licensed MIT */ Promise.all( [ // use debug version, to allow patch below loadJs( 'https://cdnjs.cloudflare.com/ajax/libs/ol3/4.4.2/ol-debug.js' ), loadCss( 'https://cdnjs.cloudflare.com/ajax/libs/ol3/4.4.2/ol-debug.css' ) ]) .then( function () { // try to recover from /extensions/TreeAndMenu/dtree.js overwriting Node if (Node.ELEMENT_NODE === undefined) { if (document.firstElementChild.__proto__.ELEMENT_NODE == 1) { Node = document.firstElementChild.__proto__; } else { Node.ELEMENT_NODE = 1 } ol.xml.isNode = function(value) { // return value instanceof Node; return typeof value.nodeName === "string"; }; } }) .then( function () { var Navionics = { token: null, KEY: 'Navionics_webapi_00572', DOMAIN: 'www.cruiserswiki.org', BASE_TILE_SERVER: '//backend.navionics.io', TILE_SUFFIX: '/tile/{z}/{x}/{y}', KEY_REQ_SUFFIX: '/tile/get_key', MAX_RESOLUTION: 20480, MIN_RESOLUTION: 0.625, disclaimer_msg: '', //'<b>© Navionics</b> <a href="http://www.navionics.com/en/acknowledgements" target="_new" class="navionics-acknowledgements">Acknowledgements</a> | Not to be used for navigation', depthUnits: { 'm': 1, 'metre': 1, 'meter': 1, 'ft': 2, 'feet': 2, 'fathom': 3, }, attribution: [ '<div class="navionics-attribution navionics-off">', '<a class="navionics-logo" href="http://www.navionics.com/" target="_blank">', '<span></span>', '</a>', '<div class="navionics-acknowledgements">', '<div>', '<a href="http://www.navionics.com/en/acknowledgements" target="_blank">', 'Acknowledgements', '</a>', '<span> | Not to be used for navigation</span>', '</div>', '</div>', '</div>' ].join(''), } Navionics.getToken = function () { if ( Navionics.token != null ) return Navionics.token var url = Navionics.BASE_TILE_SERVER + Navionics.KEY_REQ_SUFFIX + '/' + Navionics.KEY + '/' + Navionics.DOMAIN return Navionics.token = Promise.resolve( $.ajax({ url: url, crossDomain: true, dataType: 'text', //cache: false, }) ) .catch( function( err ) { return console.log( 'Navionics.getToken', err ) }) } Navionics.source = function ( options ) { options = options || {} var sourceOptions = { crossOrigin: 'anonymous', maxResolution: Navionics.MAX_RESOLUTION, minResolution: Navionics.MIN_RESOLUTION } ol.source.XYZ.call( this, sourceOptions ) var self = this Navionics.getToken() .then( function ( token ) { options.token = token var url = Navionics.getTileUrl( options ) self.setUrl( url ) }) } ol.inherits( Navionics.source, ol.source.XYZ ) Navionics.getTileUrl = function ( options ) { // sonar, overlay, depthUnit, safeDepth, showUGC options = options || {} var layerConfig = [ 'config', Navionics.depthUnits[ options.depthUnit ] || 1, ( options.safeDepth || 20 ).toFixed( 2 ), options.sonar ? 1 : 0 ] params = $.param({ LAYERS: layerConfig.join('_'), TRANSPARENT: !! options.transparency, UGC: !! options.showUGC, navtoken: options.token }) return Navionics.BASE_TILE_SERVER + Navionics.TILE_SUFFIX + '?' + params } Navionics.setAttribution = function ( map, layers ) { if ( ! layers ) return if ( ! Array.isArray( layers )) layers = [ layers ] var attribution = $( Navionics.attribution ) var control = new ol.control.Control({ element: attribution.get( 0 ) }) map.addControl( control ) function attributionVisibility ( evt ) { // console.log( 'change:visible', evt ) var visible = this.getVisible() attribution.toggleClass( 'navionics-off', ! visible ) } for ( var i=0, li=layers.length; i < li; i++ ) { var layer = layers[ i ] layer.on( 'change', attributionVisibility.bind( layer )) layer.on( 'change:visible', attributionVisibility.bind( layer )) } } var Esri = { uri_map: { 'satellite': "http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer", 'topo': "http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer", 'terrain': 'http://server.arcgisonline.com/arcgis/rest/services/World_Terrain_Base/MapServer', 'physical': 'http://services.arcgisonline.com/ArcGIS/rest/services/World_Physical_Map/MapServer', 'relief': 'http://server.arcgisonline.com/arcgis/rest/services/World_Shaded_Relief/MapServer', 'light-gray': "http://services.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Light_Gray_Reference/MapServer", 'dark-gray': "http://services.arcgisonline.com/arcgis/rest/services/Canvas/World_Dark_Gray_Base/MapServer", 'street': "http://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer", 'hybrid': "http://services.arcgisonline.com/ArcGIS/rest/services/Reference/World_Boundaries_and_Places/MapServer", 'oceans-reference': "http://server.arcgisonline.com/arcgis/rest/services/Ocean/World_Ocean_Reference/MapServer", 'oceans': 'http://server.arcgisonline.com/arcgis/rest/services/Ocean_Basemap/MapServer', 'national-geographic': "http://server.arcgisonline.com/ArcGIS/rest/services/NatGeo_World_Map/MapServer", }, } /* Esri.source = function ( id ) { ol.source.TileArcGISRest.call( this, { url: Esri.uri_map[ id ], params: { FORMAT: 'JPG', TRANSPARENT: false, }, attributions: [ new ol.Attribution({ html: 'Tiles by ESRI <a href="' + Esri.uri_map[ id ] + '">Acknowledgements</a>' }) ] }) }; ol.inherits( Esri.source, ol.source.TileArcGISRest ) */ Esri.source = function ( id ) { ol.source.XYZ.call( this, { url: Esri.uri_map[ id ] + '/tile/{z}/{y}/{x}', crossOrigin: null, attributions: [ new ol.Attribution({ html: 'Tiles by ESRI <a href="' + Esri.uri_map[ id ] + '">Acknowledgements</a>' }) ], }) }; ol.inherits( Esri.source, ol.source.XYZ ) var baselayersGroupName = 'Base'; var overlaysGroupName = 'Overlays'; function ol3Chartlet(chartlet_div, standalone) { this.styleLoadHooks = []; console.log('ol3Chartlet'); this.params = parseParams( standalone && location.hash ? decodeURI( location.hash.slice( 1 )) : $( chartlet_div ).text() ) this.params.standalone = standalone var overlays var openseamap = new ol.layer.Tile({ title: 'OpenSeaMap', source: new ol.source.XYZ({ url: 'http://t1.openseamap.org/seamark/{z}/{x}/{y}.png', crossOrigin: null, attributions: [ new ol.Attribution({ html: 'Tiles by <a href="http://www.openseamap.org">OpenSeaMap</a>' }) ], }) }) var nx_layer_ovl = new ol.layer.Tile({ title: 'Navionics Boating', source: new Navionics.source({ transparency: true }), }) var baseLayers var nx_layer = new ol.layer.Tile({ title: 'Navionics', source: new Navionics.source(), }) var osm = new ol.layer.Tile({ title: 'OpenStreetMap', source: new ol.source.OSM() }) var esri_world = new ol.layer.Tile({ title: 'ESRI World Imagery', source: new Esri.source( 'satellite' ) }) var esri_street = new ol.layer.Tile({ title: 'ESRI World Street Map', source: new Esri.source( 'street' ) }) var watercolor = new ol.layer.Tile({ title: 'Water color', source: new ol.source.Stamen({ layer: 'watercolor', //~ url: 'http://tile.stamen.com/watercolor/{z}/{x}/{y}.jpg' url: '//stamen-tiles-{a-d}.a.ssl.fastly.net/watercolor/{z}/{x}/{y}.jpg' }) }) var osm_base = new ol.layer.Tile({ title: 'OSM base', source: new ol.source.OSM({ crossOrigin: null, url: 'http://{a-c}.tiles.wmflabs.org/osm-no-labels/{z}/{x}/{y}.png' }) }) var no_background = new ol.layer.Vector({ title: 'Plain background', source: new ol.source.Vector({ }) }) if ( standalone ) { overlays = [ nx_layer_ovl, openseamap, ] openseamap.set( 'visible', false ) baseLayers = [ osm, esri_world, // esri_street, // osm_base, // no_background, watercolor, // nx_layer, ] } else { overlays = [ nx_layer_ovl, openseamap, ] nx_layer_ovl.set( 'visible', false ) baseLayers = [ osm, // esri_street, // osm_base, // no_background, watercolor, esri_world, ] } for (var i = 0; i < baseLayers.length; i++) { baseLayers[i].set( 'type' , 'base' ) baseLayers[i].set( 'visible' , false ) } baseLayers[baseLayers.length-1].set( 'visible', true ) var layers = [ new ol.layer.Group({ title: baselayersGroupName, layers: baseLayers }), new ol.layer.Group({ title: overlaysGroupName, layers: overlays }) ]; var map = this.map = new ol.Map({ target: chartlet_div, layers: layers, view: new ol.View({ center: ol.proj.transform([this.params.lon, this.params.lat], 'EPSG:4326', 'EPSG:3857'), zoom: this.params.zoom }), interactions: ol.interaction.defaults( standalone ? {} : { mouseWheelZoom:false, // doubleClickZoom :false, } ), controls: ol.control.defaults({ attributionOptions: /** @type {olx.control.AttributionOptions} */ { collapsible: false } }).extend([ new ol.control.FullScreen(), new ol.control.ScaleLine({ units: 'nautical' }) ]) }); this.restoreVisibility() Navionics.setAttribution( map, nx_layer_ovl ) loadWikiCss( 'CruisersWiki:Ol3-layerswitcher.css' ) loadWikiJs( 'CruisersWiki:Ol3-layerswitcher.js') .then( function () { var layerSwitcher = new ol.control.LayerSwitcher({ // tipLabel: 'Légende' // Optional label for button }); map.addControl(layerSwitcher); }) if (standalone) { this.params.page && this.addPoiLayer(); this.trackHash(); this.addGraticule(); this.addCentreControl( map ); } else if (this.params.pageFeatures) { this.addPoiLayer(); } this.addStoreVisibilityToolstoreVisibilitystoreVisibility() }; ol3Chartlet.prototype.addCentreControl = function ( map ) { var elem = $( '<div class="chartlet-centre"></div>' ).get( 0 ) var control = new ol.control.Control({ element: elem }) map.addControl( control ) } // add to a layer group ol3Chartlet.prototype.addNonBaseLayer = function (new_layer) { this.map.getLayers().forEach( function(layer) { if (layer.get('title') === overlaysGroupName) { layer.getLayers().push(new_layer); } } ); }; ol3Chartlet.prototype.trackHash = function() { var doNotTrackHash = false; var setHashTimeoutID = null; var view = this.map.getView(); var params = this.params; var onChangeCenterZoom = function () { var setHashDelay = 1000; var setHash = function () { var centre = view.getCenter(); var lonlat = ol.proj.transform(centre, view.getProjection(), 'EPSG:4326'); doNotTrackHash = true; setHashTimeoutID = null; params.lon = lonlat[0]; params.lat = lonlat[1]; params.zoom = view.getZoom(); //console.log(center, zoom); location.hash = 'lat=' + round(params.lat) + '|lon=' + round(params.lon) + '|zoom=' + params.zoom + (params.layer ? '|layer=' + params.layer : '') + (params.page ? '|page=' + params.page : ''); }; if (setHashTimeoutID) { window.clearTimeout(setHashTimeoutID); } setHashTimeoutID = window.setTimeout(setHash, setHashDelay); }; view.on('change:center', onChangeCenterZoom); view.on('change:resolution', onChangeCenterZoom); window.addEventListener('hashchange', function () { if (!location.hash || doNotTrackHash) { doNotTrackHash = false; return; } params = parseParams(location.hash.slice(1)); if (params.lat != null && params.lon != null) { var centre = [params.lon, params.lat]; view.setCenter(ol.proj.transform(centre, 'EPSG:4326', view.getProjection())) view.setZoom(params.zoom); } }); }; // Create the graticule ol3Chartlet.prototype.addGraticule = function () { // after http://map.openseamap.org/javascript/grid_wgs.js var this_ = this; loadWikiJs( 'CruisersWiki:Ol3chartlet-graticule.js' ) .then( function () { var layer = graticuleLayer(this_.map); this_.addNonBaseLayer(layer); }) /* var graticule = new ol.Graticule({ map: this.map, // the style to use for the lines, optional. strokeStyle: new ol.style.Stroke({ color: 'rgba(255,120,0,0.9)', width: 2, lineDash: [0.5, 4] }) }); // graticule.setMap(this.map); */ }; ol3Chartlet.prototype.addPoiLayer = function () { var options = $.extend({}, this.params); var loader = function(extent, resolution, projection) { /** * @param {ol.Extent} extent Extent. * @param {number} resolution Resolution. * @param {ol.proj.Projection} projection Projection. * @this {ol.source.Vector|ol.VectorTile} */ options.projection = projection; options.addFeature = addFeature; loadWikiJs( $('.chartlet-test').length == 0 ? 'CruisersWiki:Ol3chartlet-features.js' : 'CruisersWiki:Ol3chartlet-features-test.js' ) .then( function () { loadFeatures(options); }) }; var source = new ol.source.Vector({ loader: loader, }); var layer = new ol.layer.Vector({ title: 'POI', visible: true, style: styleFunction, source: source, }); var this_ = this; var defferedFeatures = []; function addFeature (feature) { if (!this_.styles) { defferedFeatures.push(feature); return; } if (defferedFeatures) { source.addFeatures(defferedFeatures); defferedFeatures = null; } if (feature) { source.addFeature(feature); } }; this.styleLoadHooks.push(addFeature); function styleFunction (feature, resolution) { var category = feature.get('category') || 'other'; var geom = feature.getGeometry(); var style; if (geom.getType() == 'Point') { var styleOptions = { //zIndex: 2 }; options.icons && (styleOptions.image = this_.getStyle(category, 'Icon')); options.captions && (styleOptions.text = this_.getTextStyle(category, feature.get('name'))); style = new ol.style.Style(styleOptions); } else { style = new ol.style.Style({ fill: this_.getStyle(category, 'Fill'), stroke: this_.getStyle(category, 'Stroke'), }); } return style; }; this.addNonBaseLayer(layer); loadWikiCss('CruisersWiki:tOl3-popup.css'); loadWikiJs( 'CruisersWiki:tOl3-popup.js' ) .then( this.initPopups.bind( this )) this.trackPointer(); this.highlightFeatures(); this.loadStyles(); }; ol3Chartlet.prototype.styles = null; ol3Chartlet.prototype.styleLoadHooks = null; ol3Chartlet.prototype.loadStyles = function () { var this_ = this; loadWikiJs( 'CruisersWiki:Ol3chartlet-styles-test.js' ) .then( function () { return loadStyles() }) .then( function (styles) { this_.styles = styles; var hooks = this_.styleLoadHooks; this_.styleLoadHooks = []; hooks.map( function(fn) { fn() }); }); }; ol3Chartlet.prototype.getStyle = function (category, subType) { var style = this.styles[category + '.' + subType]; return style ? style : this.styles['default' + '.' + subType]; } ol3Chartlet.prototype.getTextStyle = function (category, text) { var style = this.getStyle(category, 'Text').clone(); style.setText(text); return style; } ol3Chartlet.prototype.isClickableLayer = function (layer) { return ! layer.get('noclickable'); }; ol3Chartlet.prototype.initPopups = function () { var map = this.map; var popup = new ol.Overlay.Popup(); map.addOverlay(popup); var popTemplate = '<div class="cw-popup-name"><a href="{href}" target="_blank">{name}</a></div>\n' + '<div class="cw-popup-category">{category}</div>\n' + '<div class="cw-popup-content">{content}</div>'; // display popup on click map.on('click', function(evt) { var feature = map.forEachFeatureAtPixel( evt.pixel, function(feature, layer) { return feature; }, { layerFilter: isClickableLayer } ); if (feature && ! feature.get('noclickable')) { var data = { href: feature.get("url"), name: feature.get("name"), category: feature.get("category"), content: feature.get("description"), }; popup.show(evt.coordinate, format(popTemplate, data)); } else { popup.hide(); } }); }; ol3Chartlet.prototype.trackPointer = function () { // change mouse cursor when over marker var map = this.map; map.on('pointermove', function(e) { //~ if (e.dragging) { //~ $(element).popover('destroy'); //~ return; //~ } var pixel = map.getEventPixel(e.originalEvent); var hit = map.hasFeatureAtPixel(pixel, this.isClickableLayer); map.getTarget().style.cursor = hit ? 'pointer' : ''; }); }; ol3Chartlet.prototype.highlightFeatures = function () { var map = this.map; var highlightStyle; this.styleLoadHooks.push(function () { highlightStyle = new ol.style.Style({ fill: this.getStyle('highlight', 'Fill'), stroke: this.getStyle('highlight', 'Stroke'), }); }.bind(this)); function styleFunction (feature, resolution) { var geom = feature.getGeometry(); if (geom.getType() != 'Point') { return highlightStyle; } } var highlightOverlay = new ol.layer.Vector({ source: new ol.source.Vector(), map: map, style: styleFunction }); highlightOverlay.set('noclickable', true); var highlight; var highlightFeature = function(evt) { if (evt.dragging) { return; } var pixel = map.getEventPixel(evt.originalEvent); var feature = map.forEachFeatureAtPixel(pixel, function(feature) { return feature; }); if (feature !== highlight) { if (highlight) { highlightOverlay.getSource().removeFeature(highlight); } if (feature) { highlightOverlay.getSource().addFeature(feature); } highlight = feature; } }; map.on('pointermove', highlightFeature); }; ol3Chartlet.prototype.addStoreVisibilityToolstoreVisibilitystoreVisibility = function () { if ( $( '#cw-save-layers' ).length > 0 ) return var $a = $( '<a id="cw-save-layers" style="cursor:pointer">Save layers</a>' ) .click( this.storeVisibility.bind( this )) $( '#p-tb ul' ).append( $( '<li>' ).append( $a )) } ol3Chartlet.prototype.walkLayers = function ( group, action ) { var items = group.getLayers().getArray() for ( var i=0, li=items.length; i < li; i++ ) { var item = items[ i ] if ( item instanceof ol.layer.Group ) { this.walkLayers( item, action ) continue } action( item ) } } ol3Chartlet.prototype.storeVisibility = function () { var standalone = this.params.standalone var visibility = Storage.get( 'cw-layers-visibility' ) if ( ! visibility || ! visibility[ standalone ] ) visibility = { true: {}, false: {}, } function getVisibility ( layer ) { var name = layer.get( 'title' ) visibility[ standalone ][ name ] = layer.getVisible() } this.walkLayers( this.map, getVisibility ) Storage.set( 'cw-layers-visibility', visibility ) } ol3Chartlet.prototype.restoreVisibility = function () { var standalone = this.params.standalone var visibility = Storage.get( 'cw-layers-visibility' ) if ( ! visibility || ! visibility[ standalone ] ) return function setVisibility ( layer ) { var name = layer.get( 'title' ) var isVisible = visibility[ standalone ][ name ] if ( isVisible != null ) layer.setVisible( isVisible ) } this.walkLayers( this.map, setVisibility ) } function dmsh2deg( dmsh ) { var dmsFactors = [ 1, 60, 3600 ] var deg = 0 if ( ! dmsh ) return deg var parts = dmsh.split( '_' ) for ( var i=0, li = parts.length; i < li; i++ ) { var p = parts[ i ] if ( isNaN(+ p) ) { if (p == 'S' || p == 's' || p == 'W' || p == 'w') deg = -deg break } else { deg += p / dmsFactors[i] } } return deg } var zoomDelta = 0; // -3; // adjust zoom levels function parseParams(param_str) { var out = { icons: true, //recursive: 1, //childrenlocations: true, //captions: true, }; var params = param_str.split('|'); for (var i=0, li = params.length; i < li; i++) { var keyVal = params[i].split('=', 2); var key = $.trim(keyVal[0] || '').toLowerCase(); var val = $.trim(keyVal[1] || ''); if (key == 'lon') { out.lon = dmsh2deg(val); } else if (key == 'lat') { out.lat = dmsh2deg(val); } else if (key == 'zoom') { out.zoom = +(val || 12) + zoomDelta; } else if (key == 'layer') { out.layer = val || 'N'; } else if (key == 'page') { out.page = val; } else if (key == 'pagefeatures') { out.pageFeatures = !!val; } else if (key == 'captions') { out.captions = !!val; } else if (key == 'childrenlocations') { out.childrenlocations = true; out.recursive = 1; } else if (key == 'recursive') { out.recursive = val; } else if (key == 'icons') { out.icons = !!val; } else if (key == 'nopagelocation') { out.nopagelocation = !!val; } } return out; } function round(val) { var fact = 100000; return Math.round(val * fact) / fact; } function chartlet() { var $chartlets = $('.chartlet'); if ($chartlets.length === 0) { return; } var standalone_div = $('.chartlet-standalone')[0]; // if (standalone && location.hash) { // $('body').empty().append(standalone_div); // $('body').addClass('cw-chartlet-extend'); // } // fill in to a full window if URL hash is set if (standalone_div && location.hash) { var body = document.body; $('body').addClass('chartlet-extend'); while (body.firstChild) { body.removeChild(body.firstChild); } body.appendChild(standalone_div); setImmediate(function() { new ol3Chartlet( standalone_div, true ); }); } else { $chartlets.each( function () { new ol3Chartlet( this, !! standalone_div ); }); } }; chartlet() }) ; //