Files
galaxy/config/plugins/visualizations/openlayers/src/script.js
T

261 lines
8.8 KiB
JavaScript

import $ from "jquery";
import _ from "underscore";
import { Map, View, Graticule } from "ol";
import { OSM, Vector } from "ol/source";
import { GeoJSON } from "ol/format";
import * as interaction from "ol/interaction";
import * as style from "ol/style";
import * as layer from "ol/layer";
import * as control from "ol/control";
import { saveAs } from "file-saver";
import shp from "shpjs";
import axios from "axios";
/* This will be part of the charts/viz standard lib in 23.1 */
const slashCleanup = /(\/)+/g;
function prefixedDownloadUrl(root, path) {
return `${root}/${path}`.replace(slashCleanup, "/");
}
var MapViewer = (function(mv) {
mv.gMap = null;
/** Set the style properties of shapes */
mv.setStyle = selectedColor => {
const styles = {
Polygon: new style.Style({
stroke: new style.Stroke({
color: selectedColor,
width: 1
}),
fill: new style.Fill({
color: "rgba(0, 0, 255, 0.1)"
})
}),
Circle: new style.Style({
stroke: new style.Stroke({
color: selectedColor,
width: 1
}),
fill: new style.Fill({
color: "rgba(0, 0, 255, 0.1)"
})
}),
Point: new style.Style({
image: new style.Circle({
radius: 5,
fill: new style.Fill({
color: "rgba(0, 0, 255, 0.1)"
}),
stroke: new style.Stroke({ color: selectedColor, width: 1 })
})
}),
LineString: new style.Style({
stroke: new style.Stroke({
color: selectedColor,
width: 1
})
}),
MultiLineString: new style.Style({
stroke: new style.Stroke({
color: selectedColor,
width: 1
})
}),
MultiPoint: new style.Style({
image: new style.Circle({
radius: 5,
fill: new style.Fill({
color: "rgba(0, 0, 255, 0.1)"
}),
stroke: new style.Stroke({ color: selectedColor, width: 1 })
})
}),
MultiPolygon: new style.Style({
stroke: new style.Stroke({
color: selectedColor,
width: 1
}),
fill: new style.Fill({
color: "rgba(0, 0, 255, 0.1)"
})
}),
GeometryCollection: new style.Style({
stroke: new style.Stroke({
color: selectedColor,
width: 1
}),
fill: new style.Fill({
color: selectedColor
}),
image: new style.Circle({
radius: 10,
fill: null,
stroke: new style.Stroke({
color: selectedColor
})
})
})
};
return styles;
};
/** Set up events and methods for interactions with map view*/
mv.setInteractions = (source, options) => {
const geometryType = options.chart.settings.get("geometry_type");
const geometryColor = options.chart.settings.get("geometry_color");
let drawInteraction;
const addInteraction = () => {
if (geometryType !== "None") {
drawInteraction = new interaction.Draw({
source: source,
type: geometryType,
freehand: true
});
drawInteraction.on("drawstart", event => {
const sty = new style.Style({
stroke: new style.Stroke({
color: geometryColor,
width: 2
}),
fill: new style.Fill({
color: "rgba(0, 0, 255, 0.1)"
})
});
event.feature.setStyle(sty);
});
mv.gMap.addInteraction(drawInteraction);
}
};
addInteraction();
};
/** Export the map view to PNG image*/
mv.exportMap = () => {
mv.gMap.once("rendercomplete", event => {
const canvas = event.context.canvas;
let fileName = Math.random()
.toString(11)
.replace("0.", "");
fileName += ".png";
if (navigator.msSaveBlob) {
navigator.msSaveBlob(canvas.msToBlob(), fileName);
} else {
canvas.toBlob(blob => {
saveAs(blob, fileName);
});
}
});
mv.gMap.renderSync();
};
/** Create the map view */
mv.setMap = (vSource, target, options, styleFunction) => {
const tile = new layer.Tile({ source: new OSM() });
// add fullscreen handle
const fullScreen = new control.FullScreen();
// add scale to the map
const scaleLineControl = new control.ScaleLine();
// create vector with styles
const vectorLayer = new layer.Vector({
source: vSource,
style: styleFunction
});
const view = new View({
center: [0, 0],
zoom: 2
});
// create map view
mv.gMap = new Map({
controls: control.defaults().extend([scaleLineControl, fullScreen]),
interactions: interaction.defaults().extend([new interaction.DragRotateAndZoom()]),
layers: [tile, vectorLayer],
target: target,
loadTilesWhileInteracting: true,
view: view
});
// add grid lines
const graticule = new Graticule({
strokeStyle: new style.Stroke({
color: "rgba(255, 120, 0, 0.9)",
width: 2,
lineDash: [0.5, 4]
}),
showLabels: true
});
mv.gMap.addInteraction(new interaction.Modify({ source: vSource }));
mv.gMap.addControl(new control.ZoomSlider());
graticule.setMap(mv.gMap);
mv.setInteractions(vSource, options);
};
/** Load the map GeoJson and Shapefiles*/
mv.loadFile = (filePath, fileType, options, chart) => {
const target = options.target;
const formatType = new GeoJSON();
const toExport = options.chart.settings.get("export_map");
const geometryColor = options.chart.settings.get("geometry_color");
const selectedStyles = mv.setStyle(geometryColor);
const styleFunction = feature => {
return selectedStyles[feature.getGeometry().getType()];
};
if (toExport === "export") {
mv.exportMap();
}
if (fileType === "geojson") {
const sourceVec = new Vector({ format: formatType, url: filePath, wrapX: false });
mv.createMap(filePath, sourceVec, options, chart, styleFunction, target);
} else if (fileType === "shp") {
axios.get(filePath, { responseType: "arraybuffer" }).then(shpfile => {
console.debug(shpfile);
shp(shpfile.data).then(
geojson => {
const url = window.URL.createObjectURL(
new Blob([JSON.stringify(geojson)], { type: "application/json" })
);
const sourceVec = new Vector({ format: formatType, url: url, wrapX: false });
mv.createMap(url, sourceVec, options, chart, styleFunction, target);
},
failure => {
console.debug("FAILURE!", failure);
}
);
});
}
};
mv.createMap = (filePath, sourceVec, options, chart, styleFunction, target) => {
mv.setMap(sourceVec, target, options, styleFunction);
chart.state("ok", "Chart drawn.");
options.process.resolve();
};
return mv;
})(MapViewer || {});
window.bundleEntries = window.bundleEntries || {};
window.bundleEntries.load = function (options) {
const chart = options.chart;
const dataset = options.dataset;
const downloadUrl = prefixedDownloadUrl(options.root, dataset.download_url);
$.ajax({
url: downloadUrl,
success: () => {
MapViewer.loadFile(downloadUrl, dataset.extension, options, chart);
},
error: () => {
chart.state("failed", "Failed to access dataset.");
options.process.resolve();
}
});
}