From CruisersWiki
//
/*
ol3Chartlet.js
Copyright (c) 2016 Vadim Shlyakhov
Licensed MIT
*/
Promise.all( [
// use debug version, to allow patch below
loadJs( 'http://cdnjs.cloudflare.com/ajax/libs/ol3/3.19.1/ol-debug.js'),
loadCss( 'http://cdnjs.cloudflare.com/ajax/libs/ol3/3.19.1/ol.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:Ol3-popup.css');
loadWikiJs( 'CruisersWiki:Ol3-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) {
return feature;
},
null,
this.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 visibility = Storage.get( 'cw-layers-visibility' )
if ( ! visibility || ! visibility[ this.params.standalone ] )
visibility = {
true: {},
false: {},
}
function getVisibility ( layer ) {
var name = layer.get( 'title' )
visibility[ this.params.standalone ][ name ] = layer.getVisible()
}
this.walkLayers( this.map, getVisibility )
Storage.set( 'cw-layers-visibility', visibility )
}
ol3Chartlet.prototype.restoreVisibility = function () {
var visibility = Storage.get( 'cw-layers-visibility' )
if ( ! visibility || ! visibility[ this.params.standalone ] )
return
function setVisibility ( layer ) {
var name = layer.get( 'title' )
var isVisible = visibility[ this.params.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()
})
;
//