galadrielmap_sk
Version:
a server-based chartplotter navigation software for pleasure crafts, motorhomes, and off-road cars. It's can be used on tablets and smartphones without install any app. Only browser need.
1,191 lines (1,083 loc) • 123 kB
JavaScript
/* global depthInData, drivedPolyLineOptions, tooggleEditRoute
updClaster(pointsLayer); // galadrielmap.js
createSuperclaster(geojson); // galadrielmap.js
depends polycolorRenderer, supercluster, Leaflet.TextPath
*/
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.omnivore = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
"use strict"
var xhr = require('corslite'),
csv2geojson = require('csv2geojson'),
wellknown = require('wellknown'),
polyline = require('polyline'),
topojson = require('topojson'),
toGeoJSON = require('togeojson');
module.exports.polyline = polylineLoad;
module.exports.polyline.parse = polylineParse;
module.exports.geojson = geojsonLoad;
module.exports.topojson = topojsonLoad;
module.exports.topojson.parse = topojsonParse;
module.exports.csv = csvLoad;
module.exports.csv.parse = csvParse;
module.exports.gpx = gpxLoad;
module.exports.gpx.parse = gpxParse;
module.exports.kml = kmlLoad;
module.exports.kml.parse = kmlParse;
module.exports.wkt = wktLoad;
module.exports.wkt.parse = wktParse;
//module.exports.toGeoJSON = toGeoJSON;
function addData(l, d) { // layer geojson
/* Загружает geojson в layer
l - layer
d - data
*/
if ('setGeoJSON' in l) {
l.setGeoJSON(d);
} else if ('addData' in l) {
l.addData(d);
}
} // end function addData
/**
* Load a [GeoJSON](http://geojson.org/) document into a layer and return the layer.
*
* @param {string} url
* @param {object} options
* @param {object} customLayer
* @returns {object}
*/
function geojsonLoad(url, options, customLayer) {
var layer = customLayer || L.geoJson();
xhr(url, function(err, response) {
if (err) return layer.fire('error', { error: err });
addData(layer, JSON.parse(response.responseText));
layer.fire('ready');
});
return layer;
}
/**
* Load a [TopoJSON](https://github.com/mbostock/topojson) document into a layer and return the layer.
*
* @param {string} url
* @param {object} options
* @param {object} customLayer
* @returns {object}
*/
function topojsonLoad(url, options, customLayer) {
var layer = customLayer || L.geoJson();
xhr(url, onload);
function onload(err, response) {
if (err) return layer.fire('error', { error: err });
topojsonParse(response.responseText, options, layer);
layer.fire('ready');
}
return layer;
}
/**
* Load a CSV document into a layer and return the layer.
*
* @param {string} url
* @param {object} options
* @param {object} customLayer
* @returns {object}
*/
function csvLoad(url, options, customLayer) {
if(customLayer) var layer = L.layerGroup([customLayer]);
else var layer = L.layerGroup();
xhr(url, onload);
function onload(err, response) {
var error;
if (err) return layer.fire('error', { error: err });
function avoidReady() {
error = true;
}
layer.on('error', avoidReady);
csvParse(response.responseText, options, layer);
layer.off('error', avoidReady);
if (!error) layer.fire('ready');
}
return layer;
}
/**
* Load a GPX document into a layer and return the layer.
*
* @param {string} url
* @param {object} options
* @param {object} customLayer
* @returns {object}
*/
function gpxLoad(url, options, customLayer) {
if(customLayer) var layer = L.layerGroup([customLayer]);
else var layer = L.layerGroup();
//console.log(url);
xhr(url, onload);
function onload(err, response) {
var error;
if (err) return layer.fire('error', { error: err });
function avoidReady() {
error = true;
}
layer.on('error', avoidReady);
//console.log('leaflet-omnivore [gpxLoad] onload response:',response.responseText);
gpxParse(response.responseXML || response.responseText, options, layer);
// gpxParse(response.responseText, options, layer);
layer.off('error', avoidReady);
if (!error) layer.fire('ready');
}
return layer;
}
/**
* Load a [KML](https://developers.google.com/kml/documentation/) document into a layer and return the layer.
*
* @param {string} url
* @param {object} options
* @param {object} customLayer
* @returns {object}
*/
function kmlLoad(url, options, customLayer) {
if(customLayer) var layer = L.layerGroup([customLayer]);
else var layer = L.layerGroup();
xhr(url, onload);
function onload(err, response) {
var error;
if (err) return layer.fire('error', { error: err });
function avoidReady() {
error = true;
}
layer.on('error', avoidReady);
kmlParse(response.responseXML || response.responseText, options, layer);
layer.off('error', avoidReady);
if (!error) layer.fire('ready');
}
return layer;
}
/**
* Load a WKT (Well Known Text) string into a layer and return the layer
*
* @param {string} url
* @param {object} options
* @param {object} customLayer
* @returns {object}
*/
function wktLoad(url, options, customLayer) {
var layer = customLayer || L.geoJson();
xhr(url, onload);
function onload(err, response) {
if (err) return layer.fire('error', { error: err });
wktParse(response.responseText, options, layer);
layer.fire('ready');
}
return layer;
}
/**
* Load a polyline string into a layer and return the layer
*
* @param {string} url
* @param {object} options
* @param {object} customLayer
* @returns {object}
*/
function polylineLoad(url, options, customLayer) {
var layer = customLayer || L.geoJson();
xhr(url, onload);
function onload(err, response) {
if (err) return layer.fire('error', { error: err });
polylineParse(response.responseText, options, layer);
layer.fire('ready');
}
return layer;
}
function topojsonParse(data, options, layer) {
var o = typeof data === 'string' ?
JSON.parse(data) : data;
layer = layer || L.geoJson();
for (var i in o.objects) {
var ft = topojson.feature(o, o.objects[i]);
if (ft.features) addData(layer, ft.features);
else addData(layer, ft);
}
return layer;
}
function csvParse(csv, options, layer) {
/**/
//console.log('[csvParse]',csv,options,layer);
if(layer) {
if("getLayers" in layer) { // это layerGroup
var featuresLayer = layer.getLayers()[0] || L.geoJson();
}
else { // это одиночный Layer
var featuresLayer = layer;
layer = new L.layerGroup([featuresLayer]); // попробуем сменть тип на layerGroup, но это обычно боком выходит
}
}
else {
var featuresLayer = L.geoJson();
var layer = new L.layerGroup([featuresLayer]);
}
var color = 0xFFFFFF;
if(typeof globalCurrentColor !== 'undefined') { // если оно определено, то определена и функция nextColor
color = globalCurrentColor;
globalCurrentColor = nextColor(globalCurrentColor); // сменим текущий цвет, from galadrielmap.js
}
if(color == 0xFFFFFF) featuresLayer.options.color = 0x3388FF; // умолчальный цвет линий
else featuresLayer.options.color = color; // цвет линий
if(options.featureNameNode) { // li с именем файла, из которого делаем layer
options.featureNameNode.style.backgroundColor = '#'+('000000' + color.toString(16)).slice(-6);
}
featuresLayer.options.onEachFeature = getPopUpToLine; // функция, вызываемая для каждой feature при её создании
featuresLayer.options.style = function(geoJsonFeature){return{color: '#'+('000000' + featuresLayer.options.color.toString(16)).slice(-6)};}; // A Function defining the Path options for styling GeoJSON lines and polygons, called internally when data is added.
if(! layer.hasLayer(featuresLayer)) layer.addLayer(featuresLayer);
var pointsLayer = L.geoJson();
pointsLayer.options.color = color; // цвет значков
pointsLayer.options.pointToLayer = function (geoJsonPoint, latlng) { // функция, вызываемая для каждой точки при её создании
let marker;
if(geoJsonPoint.properties.marker) {
//console.log('Маркер уже есть');
marker = geoJsonPoint.properties.marker.setLatLng(latlng);
}
else {
// parameters тут -- это options создаваемого маркера, плюс разное, типа - color
//console.log('Новый маркер');
let parameters = {color: pointsLayer.options.color}; // таким образом мы забросим цвет в создание маркера
marker = getMarkerToPoint(geoJsonPoint, latlng, parameters);
geoJsonPoint.properties.marker = marker;
};
return marker;
};
layer.addLayer(pointsLayer);
options = options || {};
csv2geojson.csv2geojson(csv, options, onparse);
function onparse(err, geojson) {
if (err) return layer.fire('error', { error: err });
//console.log('[onparse] geojson:',geojson);
var Points=[];
var Features=[];
//console.log(layer.options.markerColor);
for(var i=0; i<geojson.features.length;i++) {
if(geojson.features[i].geometry.type=='Point') {
geojson.features[i].properties.color = layer.options.markerColor;
Points.push(geojson.features[i]);
}
else Features.push(geojson.features[i]);
}
addData(featuresLayer, Features); // добавим и покажем всё остальное
if(Points.length) {
doClastering(pointsLayer, Points); // закластеризуем точки
updClaster(pointsLayer); // galadrielmap.js и покажем
}
} // end function onparse
return layer;
} // end function csvParse
function gpxParse(gpx, options, layer) {
/*
Создаёт layerGroup из двух слоёв, в одном - линии, в другом - точки.
*/
//console.log('leaflet-omnivore [gpxParse] gpx:',gpx);
if(layer) {
//console.log('leaflet-omnivore [gpxParse] layer:',layer,layer instanceof L.layerGroup,"getLayers" in layer);
if(layer instanceof L.LayerGroup) { // это layerGroup
//if("getLayers" in layer) { // это layerGroup
var featuresLayer = layer.getLayers()[0] || L.geoJson();
}
else { // это одиночный Layer
var featuresLayer = layer;
layer = L.layerGroup([featuresLayer]); // попробуем сменть тип на layerGroup, но это обычно боком выходит. Но, вообще-то, layer создаётся как layerGroup.
}
}
else {
var featuresLayer = L.geoJson();
layer = L.layerGroup([featuresLayer]);
};
var xml = parseXML(gpx); // делает DOM XML, если gpx -- строка, иначе не делает ничего
const errorNode = xml.querySelector("parsererror");
if (errorNode) return layer.fire('error', {
error: 'Could not parse GPX'
});
//console.log('leaflet-omnivore [gpxParse] xml:',xml);
var geojson = toGeoJSON.gpx(xml);
//console.log('leaflet-omnivore [gpxParse] geojson:',geojson);
if(layer.properties) Object.assign(layer.properties,geojson.properties);
else layer.properties = geojson.properties;
var Points=[];
var Features=[];
for(let i=0; i<geojson.features.length;i++) {
if(geojson.features[i].geometry.type=='Point') Points.push(geojson.features[i]);
else {
//console.log(geojson.features[i]);
//if(geojson.features[i].properties.isRoute) geojson.features[i].properties.fileName = options.featureNameNode.innerText.trim(); // оно не надо
Features.push(geojson.features[i]);
}
}
//console.log('leaflet-omnivore [gpxParse] options:',options);
//console.log('leaflet-omnivore [gpxParse] Points:',Points);
//console.log('leaflet-omnivore [gpxParse] Features:',Features);
var color = 0xFFFFFF;
if(typeof globalCurrentColor !== 'undefined') { // если оно определено, то определена и функция nextColor
color = globalCurrentColor;
globalCurrentColor = nextColor(globalCurrentColor); // сменим текущий цвет, from galadrielmap.js
}
if(color == 0xFFFFFF) featuresLayer.options.color = 0x3388FF; // умолчальный цвет линий
else featuresLayer.options.color = color; // цвет линий
if(options && options.featureNameNode) { // li с именем файла, из которого делаем layer
options.featureNameNode.style.backgroundColor = '#'+('000000' + color.toString(16)).slice(-6);
}
featuresLayer.options.onEachFeature = function (feature, layer){ // функция, вызываемая для каждой feature при её создании
getPopUpToLine(feature, layer);
//console.log('[featuresLayer.options.onEachFeature] feature:',feature);
//console.log('[featuresLayer.options.onEachFeature] layer:',layer);
//console.log('[featuresLayer.options.onEachFeature] depthInData:',depthInData,options.featureNameNode.classList.contains('currentTrackName'));
// Leaflet.TextPath несовместимо с polycolorRenderer, разбираться лень, поэтому стрелочки
// будем лепить только если нет данных о глубине или не велено глубину показывать
// лепить стрелочки на линию, только если это не текущий трек, который всё время перерисовывается. Ибо чёта стрелочки затратно...
if(!(depthInData && depthInData.display && feature.properties && feature.properties.depths)
&&
!(options && options.featureNameNode && options.featureNameNode.classList && options.featureNameNode.classList.contains('currentTrackName'))
){
//console.log('Рисуем стрелочки направления движения');
layer.setText(' > ',
{repeat: true,
offset: '0.6ch', // сдвиг вправо от линии на половину ширины символа (плюс поправочка) размером font-size. ch - Предварительная мера (ширина) глифа "0" шрифта элемента
attributes: {fill: layer.options.color,
'font-size': '1.5rem',
'font-weight': 'bold',
'opacity': 0.7
}
}); // Leaflet.TextPath
};
};
featuresLayer.options.style = function(geoJsonFeature){ // A Function defining the Path options for styling GeoJSON lines and polygons, called internally when data is added.
// вот тут надо вычислить цвета и указать рендерер
let style = {};
//console.log('leaflet-omnivore.js [featuresLayer.options.style] geoJsonFeature:',geoJsonFeature);
//console.log('leaflet-omnivore.js [featuresLayer.options.style] depthInData:',depthInData);
if(typeof depthInData !== 'undefined' && depthInData.display && geoJsonFeature.properties && geoJsonFeature.properties.depths){ // depthInData - global from options.js
let colors = [], weights = [];
for(let i=0; i < geoJsonFeature.properties.depths.length; i++){
if(Array.isArray(geoJsonFeature.properties.depths[i])) {
if(!geoJsonFeature.geometry.coordinates[i].length) continue; // если этот сегмент GeoJSON MultiLineString пустой (например, сделан из <trkseg></trkseg>), то Leaflet этот сегмент вообще опустит, что ему не говори.
let colors1 = [], weights1 = [];
for(let depth of geoJsonFeature.properties.depths[i]){
if(depth === null){
colors1.push(null);
weights1.push(null);
}
else {
colors1.push(value2color(depth,depthInData.minvalue||0,depthInData.maxvalue||10,depthInData.minColor||[255,0,0],depthInData.maxColor||[0,255,0],depthInData.underMinColor||"rgb(155,0,0)",depthInData.upperMaxColor||"rgb(200,250,240)"));
weights1.push(5);
}
}
const _weights = [...new Set(weights1)];
if(_weights.length == 1) weights1 = _weights;
colors.push(colors1);
weights.push(weights1);
}
else {
if(geoJsonFeature.properties.depths[i] === null){
colors.push(null);
weights.push(null);
}
else {
//console.log('leaflet-omnivore.js [featuresLayer.options.style] depth=',geoJsonFeature.properties.depths[i],'value2color:',value2color(geoJsonFeature.properties.depths[i],depthInData.minvalue,depthInData.maxvalue));
colors.push(value2color(geoJsonFeature.properties.depths[i],depthInData.minvalue||0,depthInData.maxvalue||10,depthInData.minColor||[255,0,0],depthInData.maxColor||[0,255,0],depthInData.underMinColor||"rgb(155,0,0)",depthInData.upperMaxColor||"rgb(200,250,240)"));
weights.push(5);
}
}
}
const _weights = [...new Set(weights)];
if(_weights.length == 1) weights = _weights;
//console.log('leaflet-omnivore.js [featuresLayer.options.style] colors:',colors,'weights:',weights);
style = {
noClip: true, // отключить всякое упрощение линии Leaflet'ом
smoothFactor: 0,
renderer: new polycolorRenderer(),
color: '#'+('000000' + featuresLayer.options.color.toString(16)).slice(-6),
colors: colors,
useGradient: true,
opacity: 0.8,
weights: weights
}
}
else {
style.color = '#'+('000000' + featuresLayer.options.color.toString(16)).slice(-6);
style.opacity = 0.7; // если применять на суше, то непрозрачная линия ложится на тропу, и непонятно. Слегка прозрачная решает проблему.
//console.log('style.color:',style.color);
// поскольку параметры из drivedPolyLineOptions применяются позже... А почему они позже?
if(geoJsonFeature.properties && geoJsonFeature.properties.isRoute){
style.dashArray = `0,${drivedPolyLineOptions.options.weight+2}`; // [длина, толщина] Если длина 0, то будут кружочки. где я в этом месте достану ширину рисуемой линии, если она ещё не задана? А drivedPolyLineOptions для этого и предназначено.
//style.lineCap = "bitt";
}
else {
};
}
return style;
}; // end featuresLayer.options.style = function
// Добавим в слой объекты
featuresLayer.addData(Features); // добавим и покажем всё остальное
if(! layer.hasLayer(featuresLayer)) layer.addLayer(featuresLayer);
//console.log(featuresLayer);
// Теперь добавим точки
if(Points.length) {
var pointsLayer = L.geoJson();
pointsLayer.options.color = color; // цвет значков
// pointToLayer вызывается рашьше, чем onEachFeature, и всё это вызывается в addData
pointsLayer.options.pointToLayer = function (geoJsonPoint, latlng) { // функция, вызываемая для каждой точки при её создании
//console.log('[csvParse] pointToLayer',geoJsonPoint);
let marker;
if(geoJsonPoint.properties.marker) {
//console.log('Маркер уже есть');
marker = geoJsonPoint.properties.marker.setLatLng(latlng);
}
else {
// parameters тут -- это options создаваемого маркера, плюс разное, типа - color
//console.log('Новый маркер');
let parameters = {color: pointsLayer.options.color}; // таким образом мы забросим цвет в создание маркера
marker = getMarkerToPoint(geoJsonPoint, latlng, parameters);
if(typeof tooggleEditRoute === 'function') {
//marker.on('dblclick', L.DomEvent.stop).on('dblclick', tooggleEditRoute);
//marker.on('click', L.DomEvent.stop).on('click', tooggleEditRoute); // galadrielmap.js чёта stop не работает?
marker.on('click', tooggleEditRoute); // galadrielmap.js
}
marker.on('editable:dragstart', function(event){
// Нужно будет перестроить superclaster с точкой с новыми координатами
removeFromSuperclaster(pointsLayer,event.target); // galadrielmap.js
});
marker.on('editable:dragend', function(event){
// Нужно перестроить superclaster с точкой с новыми координатами
//console.log('leaflet-omnivore.js [marker.on editable:dragend] pointsLayer:',pointsLayer);
pointsLayer.supercluster.points.push(event.target.toGeoJSON());
pointsLayer.supercluster = createSuperclaster(pointsLayer.supercluster.points); // galadrielmap.js создание нового и загрузка в суперкластер точек
});
//geoJsonPoint.properties.marker = marker;
}
return marker;
};
doClastering(pointsLayer, Points); // закластеризуем точки
updClaster(pointsLayer); // galadrielmap.js и покажем
layer.addLayer(pointsLayer);
}
//layer.options.fileName = options.featureNameNode.innerText.trim(); // Оно не надо?
//console.log(layer);
//console.log(layer.getLayers());
return layer;
}; // end function gpxParse
function doClastering(layer, geojson) {
/* Кластеризует wpt в layer, если они там есть
Требует наличия supercluster.js
*/
/*
const index = new Supercluster({
log: false, // вывод лога в консоль
radius: 40,
extent: 256,
maxZoom: 15,
}).load(geojson); // собственно, загрузка в суперкластер точек index
*/
layer.supercluster = createSuperclaster(geojson); // galadrielmap.js
layer.on('click', (e) => { // клик по любому значку (вообще по любому месту?) :-( потому что нам нужен layer
//console.log('leaflet-omnivore.js : doClastering start by click');
//console.log(e);
if (e.layer.feature.properties.cluster_id) { // кликнутый значёк - кластер
const expansionZoom = e.target.supercluster.getClusterExpansionZoom(e.layer.feature.properties.cluster_id); // получим масштаб, при котором этот кластер разделится
map.flyTo(e.latlng,expansionZoom);
}
});
return layer;
} // end function doClastering
function getMarkerToPoint(geoJsonPoint, latlng, parameters) { // https://leafletjs.com/reference-1.3.4.html#geojson
// Функция, которая в latlng рисует маркер по сведениям из geoJsonPoint
// обычно вызывается как свойство layer.options.pointToLayer
// В geoJsonPoint.properties собираются:
//'ele' 'name', 'cmt', 'desc', 'src', 'number', 'author', 'copyright', 'sym', 'type', 'time', 'keywords' в function getProperties(node) для gpx
// 'name' 'icon' 'description' в function getPlacemark(root) для kml
// для csv просто берутся имеющиеся имена атрибутов, поэтому будем парсить имена отсюда: https://www.gpsbabel.org/htmldoc-1.5.4/fmt_unicsv.html
//console.log(parameters);
// Сам маркер - Marker
if(!parameters) parameters = {};
if(!parameters.color) parameters.color = 0xFFFFFF;
let marker = L.marker(latlng,parameters); // маркер для этой точки
marker.options.riseOnHover = true;
//console.log('[getMarkerToPoint] marker:',marker,'parameters:',parameters);
if(geoJsonPoint.properties.cluster) { // это кластер
//console.log(geoJsonPoint);
const icon = L.divIcon({
html: `<div style="background-color: #${('000000' + parameters.color.toString(16)).slice(-6)};"><span>${ geoJsonPoint.properties.point_count_abbreviated }</span></div>`,
className: `marker-cluster`,
iconSize: L.point(25, 25),
});
marker.setIcon(icon);
}
else { // это индивидуальная точка
//console.log('marker for point');
// Значёк - Icon
//alert('icon' in marker.options);
let iconNames = []; // возможные имена значков
if(geoJsonPoint.properties.sym) iconNames.push(geoJsonPoint.properties.sym.trim().replace(/ /g, '_').replace(/,/g, '').toLowerCase()); // gpx sym (symbol name) attribyte
if(geoJsonPoint.properties.symbol) iconNames.push(geoJsonPoint.properties.symbol.trim().replace(/ /g, '_').replace(/,/g, '').toLowerCase()); // csv symbol name attribyte
if(geoJsonPoint.properties.symb) iconNames.push(geoJsonPoint.properties.symb.trim().replace(/ /g, '_').replace(/,/g, '').toLowerCase()); // csv symbol name attribyte
if(geoJsonPoint.properties.type) iconNames.push(geoJsonPoint.properties.type.trim().replace(/ /g, '_').replace(/,/g, '').toLowerCase()); // gpx type (classification) attribyte
if(geoJsonPoint.properties.icon) { // kml Icon
//console.log('"'+geoJsonPoint.properties.icon.textContent.trim()+'"');
let iNm = geoJsonPoint.properties.icon.textContent.trim();
iNm = iNm.substring(iNm.lastIndexOf('/')+1);
let iNmExt = iNm.slice((iNm.lastIndexOf(".") - 1 >>> 0) + 2); // icon filename ext https://www.jstips.co/en/javascript/get-file-extension/ потому что там нет естественного пути
if(iNmExt.length) iNm = iNm.slice(0,-(iNmExt.length+1));
//console.log(iNm);
if(iNm.length) iconNames.push(iNm.replace(/ /g, '_').replace(/,/g, '').toLowerCase()); // kml icon name in <Style><IconStyle><Icon> attribyte
}
iconNames = [...new Set(iconNames)]; // только уникальные значения. Сначала из неуникального массива делается Set, потом из Set -- массив.
//console.log("[getMarkerToPoint] iconNames:",iconNames);
iconServer.setIconCustomIcon(marker,iconNames); // заменить в marker icon на нужный асинхронно
//console.log(iconServer.iconsByType);
//console.log('[getMarkerToPoint] marker:',marker);
// Подпись - Tooltip
if(geoJsonPoint.properties.name) {
marker.bindTooltip(geoJsonPoint.properties.name,{
permanent: true, // всегда показывать
//direction: 'auto',
//direction: 'left',
direction: 'top',
offset: [-16,0],
className: 'wpTooltip', // css class
opacity: 0.65,
pane: 'overlayPane',
zIndexOffset: -600
});
//console.log('[getMarkerToPoint] _tooltip:',marker._tooltip);
// поскольку ._container появляется только после добавления на карту, изменение цвета
// выполняется по соответствующему событию.
// Но вообще нормального способа динамически менять цвет нет.
// Устанавливается цвет фона контейнера тултипа в полупрозрачный. Оно не надо: оно потом делается в соответствии со свойствами.
marker._tooltip.on('add',function (event){
//event.target._container.style.backgroundColor = `#${('000000' + parameters.color.toString(16)).slice(-6)}80`;
event.target._container.style.backgroundColor = `#${('000000' + parameters.color.toString(16)).slice(-6)}`;
});
//}).openTooltip(); // и перерисуем подпись под умолчальный маркер. Под другие маркеры перерисуем потом. Но это бессмысленно - она не перерисовывается
}
// Информация о - PopUp
//console.log(geoJsonPoint.properties.link);
var popUpHTML = '';
if(geoJsonPoint.properties.number) popUpHTML = geoJsonPoint.properties.number;
if(geoJsonPoint.properties.name) popUpHTML = "<b>"+geoJsonPoint.properties.name+"</b> "+popUpHTML;
if(!popUpHTML) popUpHTML = latlng.lat+" "+latlng.lng;
//console.log('leaflet-omnivore.js [getMarkerToPoint] latlng:',latlng);
popUpHTML = "<span style='font-size:120%'; onClick='doCopyToClipboard(\""+latlng.lat+" "+latlng.lng+"\")'>" + popUpHTML + "</span><br>";
//popUpHTML = "<span style='font-size:120%';'>" + popUpHTML + "</span><br>";
if(geoJsonPoint.properties.cmt) popUpHTML = popUpHTML+"<p>"+geoJsonPoint.properties.cmt.replace(/\n/g, '<br>')+"</p>"; // gpx description;
if(geoJsonPoint.properties.desc) popUpHTML = popUpHTML+"<p>"+geoJsonPoint.properties.desc.replace(/\n/g, '<br>')+"</p>"; // gpx description
if(geoJsonPoint.properties.notes) popUpHTML = popUpHTML+"<p>"+geoJsonPoint.properties.notes.replace(/\n/g, '<br>')+"</p>"; // csv description
if(geoJsonPoint.properties.description) popUpHTML = popUpHTML+"<p>"+geoJsonPoint.properties.description.replace(/\n/g, '<br>')+"</p>"; // kml description
if(geoJsonPoint.properties.comment) popUpHTML = popUpHTML+"<p>"+geoJsonPoint.properties.comment.replace(/\n/g, '<br>')+"</p>"; // csv description
if(geoJsonPoint.properties.ele) popUpHTML = popUpHTML+"<p>Alt: "+geoJsonPoint.properties.ele+"</p>"; // gpx elevation
if(geoJsonPoint.properties.alt) popUpHTML = popUpHTML+"<p>Alt: "+geoJsonPoint.properties.alt+"</p>"; // csv elevation
if(geoJsonPoint.properties.height) popUpHTML = popUpHTML+"<p>Alt: "+geoJsonPoint.properties.height+"</p>"; // csv elevation
if(geoJsonPoint.properties.depth) popUpHTML = popUpHTML+"<p>Alt: "+geoJsonPoint.properties.depth+"</p>"; // csv depth
popUpHTML += getLinksHTML(geoJsonPoint); // приклеим ссылки
marker.bindPopup(popUpHTML+'<br>'); // создадим PopUp, popUpHTML всегда не пуст
}
return marker;
} // end function getMarkerToPoint
function getLinksHTML(feature) {
/* Возвращает строку,которую можно было бы показать в PopUp,
из атрибутов link в feature. Оформляет ссылки как может.
Пытается обнаружить ссылки на картинки и показывает для них фотоаппаратик.
*/
var camImgPath = leafletOmnivoreScript.src.substr(0, leafletOmnivoreScript.src.lastIndexOf("/"))+"/icons/cam.svg";
var popUpHTML = '';
var links = [];
if(feature.properties.link) {
if(typeof(feature.properties.link)=='object'){ // должно быть, это массив, который получается при импорте сложных объектов
feature.properties.link.forEach(link=>links.push(link));
}
else links.push(feature.properties.link); // а тут, наверно, единичный объект
}
if(feature.properties.url) links.push(feature.properties.url);
if(!links.length) return popUpHTML;
// имеются ссылки
//console.log('[getLinksHTML] имеются ссылки',links);
// Это может быть как просто строка с url из csv, так и строковое представление linkType gpx
let parser = new DOMParser();
for(let linkStr of links){
let linkHTML='', url='', text='', mimeType='';
let link = parser.parseFromString(linkStr, "application/xml");
//console.log('[getLinksHTML] link:',linkStr,link,'parseerror',link.querySelector("parsererror"));
if(link.querySelector("parsererror")) url = linkStr; // типа, там была просто строка, хотя хрен его знает, как этот кривой парсер относится к просто строкам
else {
url = link.activeElement.attributes.href.value;
if(text = link.querySelector("text")) text = text.innerHTML;
else text = '';
if(mimeType = link.querySelector("type")) mimeType = mimeType.innerHTML;
else mimeType = '';
//console.log('[getLinksHTML] link object:',url,text,mimeType);
}
linkHTML = '<a href="'+url+'" target="_blank" >';
if(mimeType.startsWith('image') || (url.slice(-5).toLowerCase()=='.jpeg') || (url.slice(-4).toLowerCase()=='.jpg') || (url.slice(-4).toLowerCase()=='.png') || (url.slice(-4).toLowerCase()=='.svg') || (url.slice(-4).toLowerCase()=='.tif') || (url.slice(-5).toLowerCase()=='.tiff')) {
linkHTML += '<img src="'+camImgPath+'" width="12%" style="vertical-align: middle; margin:auto 1rem;"></a>';
}
if(text) linkHTML += ' '+text;
else linkHTML += ' External link';
linkHTML += '</a><br>';
popUpHTML += linkHTML;
};
if(popUpHTML) popUpHTML = '<br>'+popUpHTML;
return popUpHTML;
}; // end function getLinksHTML
var popupDepthInfo = L.popup(); // popup для отображения глубины на путях с глубиной
function getPopUpToLine(feature, layer) {
/* A Function that will be called once for each created Feature
ПОэтому тут не только подпись и всплывающее окно, но и прочие параметры линии
*/
//console.log('leaflet-omnivore [getPopUpToLine] feature:',feature,'layer:',layer);
if(feature.properties && feature.properties.isRoute) { // это маршрут.
//console.log('leaflet-omnivore [getPopUpToLine] drivedPolyLineOptions:',drivedPolyLineOptions,globalCurrentColor);
Object.assign(layer.options,drivedPolyLineOptions.options); // drivedPolyLineOptions из index.php
Object.assign(layer.feature.properties,drivedPolyLineOptions.feature.properties); // drivedPolyLineOptions из index.php
layer.on('editable:editing', function (event){event.target.updateMeasurements();}); // обновлять расстояния при редактировании
if(typeof tooggleEditRoute === 'function') {
//layer.on('dblclick', L.DomEvent.stop).on('dblclick', tooggleEditRoute);
//layer.on('click', L.DomEvent.stop).on('click', tooggleEditRoute); // galadrielmap.js чёта stop не работает?
layer.on('click', tooggleEditRoute); // galadrielmap.js
}
}
if(feature.properties) {
// Подпись - Tooltip
if(feature.properties.name) {
layer.bindTooltip(feature.properties.name,{
permanent: true, // всегда показывать
direction: 'auto',
//direction: 'center',
//offset: [0,-15],
className: 'wpTooltip', // css class
opacity: 0.75
});
}
// PopUp
var popUpHTML = '';
if(feature.properties.number) popUpHTML = " <span style='font-size:120%;'>"+feature.properties.number+"</span> "+popUpHTML;
if(feature.properties.name) popUpHTML = "<b>"+feature.properties.name+"</b> "+popUpHTML;
if(feature.properties.cmt) popUpHTML += "<p>"+feature.properties.cmt+"</p>";
if(feature.properties.desc) popUpHTML += "<p>"+feature.properties.desc.replace(/\n/g, '<br>')+"</p>"; // gpx description
if(feature.properties.description) popUpHTML += "<p>"+feature.properties.description.replace(/\n/g, '<br>')+"</p>"; // kml description
popUpHTML += getLinksHTML(feature); // приклеим ссылки
//if(feature.properties.name) popUpHTML = "<b>"+feature.properties.name+"</b> "+popUpHTML;
if(popUpHTML) {
layer.bindPopup(popUpHTML+'<br>');
}
if(typeof depthInData !== 'undefined' && depthInData.display && feature.properties.depths){ // есть глубина и её надо показывать depthInData - global from options.js
layer.on('click', function(event) {
//console.log('leaflet-omnivore [gpxParse] event:',event);
// при наличии feature.geometry.coordinates
let index = getNearestSegPoint(event.latlng, feature.geometry.coordinates);
if(feature.properties.depths[index] !== null){
popupDepthInfo.setLatLng({lng:feature.geometry.coordinates[index][0],lat:feature.geometry.coordinates[index][1]}); // в GeoJSON наоборот, чем в Leaflet
popupDepthInfo.setContent(dashboardDepthMesTXT+' '+(Math.round(feature.properties.depths[index]*100)/100)+' '+dashboardMeterMesTXT);
map.openPopup(popupDepthInfo);
}
})
}
}
function getNearestSegPoint(latlng, latlngs){
/* отыскивает координату в latlngs, ближайшую к latlng
широта и долгота -- как в GeoJSON, не как в Leaflet!!!!
*/
let distance, minDistance=999999999999, index;
let latitude = latlng.lat;
let longitude = latlng.lng;
for (let i = 0; i < latlngs.length; i++) {
distance = equirectangularDistance([longitude,latitude],latlngs[i]);
if(distance < minDistance){
minDistance = distance;
index = i;
}
//console.log('leaflet-omnivore [getNearestSegPoint] i=',i,'distance=',distance,'minDistance=',minDistance)
}
return index;
} // end function getNearestSegPoint
//console.log('leaflet-omnivore [getPopUpToLine] layer:',layer);
} // end function getPopUpToLine
function equirectangularDistance(from,to){
// https://www.movable-type.co.uk/scripts/latlong.html
// from,to: как в GeoJSON, не как в Leaflet!!!!
let from_longitude = from[0];
let from_latitude = from[1];
let to_longitude = to[0];
let to_latitude = to[1];
const rad = Math.PI/180;
const φ1 = from_latitude * rad;
const φ2 = to_latitude * rad;
const Δλ = (to_longitude-from_longitude) * rad;
const R = 6371e3; // метров
const x = Δλ * Math.cos((φ1+φ2)/2);
const y = (φ2-φ1);
const d = Math.sqrt(x*x + y*y) * R; // метров
return d;
} // end function equirectangularDistance
// определение имени файла этого скрипта
var scripts = document.getElementsByTagName('script');
var index = scripts.length - 1; // это так, потому что эта часть сработает при загрузке скрипта, и он в этот момент - последний http://feather.elektrum.org/book/src.html
var leafletOmnivoreScript = scripts[index];
//console.log(leafletOmnivoreScript);
var iconServer = {
// типа, объект, централизованно раздающий L.icon, в которых уже есть сама картинка как base64
// объект скачивает требуемые файлы картинок и хранит. Когда надо -- указывает в объекте L.icon как iconUrl.
// Неиспользуемые картинки не удаляются, так что при удаче можно закачать в память все 400 картинок.
// Но это меньше 600Kb.
// Основная цель предварительной закачки картинок -- определить наличие файла с таким именем.
// Список возможных имён iconNames получен из разных мест показываемого (gpx, kml, csv) файла. Там,
// в принципе, могут быть разные слова, которые могут быть поняты как тип объекта, и которые
// могут стать именем файла значка для объекта. Но значка с таким именем в коллекции может не быть.
// Чтобы это понять, и установить умолчальный значёк -- и используется предварительная загрузка файла.
iconsByType: {}, // сюда будем складывать L.icon каждого типа
setIconCustomIcon: function (marker,iconNames) {
/* пытается создать L.icon с iconUrl из iconNames, где они без пути и расширения
при наличии такого файла - создаёт, устанавливает эту L.icon в marker
и складывает в iconsByType
*/
let iconName = iconNames.shift();
//console.log("[iconServer] iconName=",iconName);
if(!iconName) return;
//console.log(this.iconsByType);
if(this.iconsByType[iconName]) {
if(typeof this.iconsByType[iconName] === 'object') { // такая icon уже получена
marker.setIcon(this.iconsByType[iconName]).openTooltip(); // если icon с таким именем уже создавали - посадить значёк и перерисовать подпись
//console.log('icon '+iconName+' из хранилища');
}
else { // такую icon кто-то сейчас получает
// ждать
let vait = setInterval(function(){ // запустим асинхронное ожидание. В результате сначала присвоится умолчальный значёк, а потом - нужный
//console.log('Ждём icon '+iconName);
if(iconServer.iconsByType[iconName] && typeof iconServer.iconsByType[iconName] === 'object') { // такая icon уже получена
marker.setIcon(iconServer.iconsByType[iconName]).openTooltip(); // если icon с таким именем уже создавали посадить значёк и перерисовать подпись
//console.log('Дождались icon '+iconName);
clearInterval(vait); // прекратим ждать
}
else {
if(iconServer.iconsByType[iconName] === false) {
//console.log('Не дождались icon '+iconName);
clearInterval(vait); // оно обломалось, прекратим ждать
}
}
},100); // таймер на милисекунд
}
}
else if(this.iconsByType[iconName] === false) { // такой icon вообще нет, её кто-то пытался получить, но безуспешно
//console.log('Уже был облом с icon '+iconName);
iconServer.setIconCustomIcon(marker,iconNames); // вызовем себя для следующего имени
// ничего не делать - поставится умолчальная
}
else { // такая icon ещё не получена
this.iconsByType[iconName] = true; // укажем, что понеслось получать
// получить асинхронно
// все требуемые картинки значков скачиваются и хранятся в памяти
// а нафига? А так мы узнаем, какой картинки нет.
//console.log(leafletOmnivoreScript.src.substr(0, leafletOmnivoreScript.src.lastIndexOf("/")),iconName);
fetch(leafletOmnivoreScript.src.substr(0, leafletOmnivoreScript.src.lastIndexOf("/"))+"/symbols/"+iconName+".png")
.then(function(response) {
//console.log(response);
if(response.ok) return response.blob(); // руками обработаем ошибки сервера
else throw new Error('Network response was not ok for icon '+iconName); // Перейдём сразу к .catch
})
.then(function(blob){
let iconURL = URL.createObjectURL(blob); // здесь получается blob -- такой хитрый Data URL. В результате его понимает L.icon как ссылку, но файл уже загружен. Вопрос выгрузки остаётся открытым: ведь оно нужновсё время после загрузки, и загружается только один раз. https://developer.mozilla.org/ru/docs/Web/API/URL/createObjectURL
//console.log(iconURL);
let icon = L.icon({
iconUrl: iconURL,
iconSize: [32, 37],
iconAnchor: [16, 37],
tooltipAnchor: [16,-25],
className: 'wpIcon'
});
iconServer.iconsByType[iconName] = icon; // сохраним полученный значёк в кеше
marker.setIcon(icon).openTooltip(); // посадить значёк и перерисовать подпись
//console.log('Create and Set icon '+iconName);
//console.log(marker);
})
.catch(function(error) { // - не работает в случае 404!, поэтому выше throw new Error
iconServer.iconsByType[iconName] = false; // укажем, что со значком облом
console.log('iconServer setIconCustomIcon fetch error: ' + error.message);
iconServer.setIconCustomIcon(marker,iconNames); // вызовем себя для следующего имени
});
}
}, // end function setIconCustomIcon, список атрибутов объекта продолжается
} // end object iconServer
function kmlParse(gpx, options, layer) {
/**/
if(layer) {
if("getLayers" in layer) { // это layerGroup
var featuresLayer = layer.getLayers()[0] || L.geoJson();
}
else { // это одиночный Layer
var featuresLayer = layer;
layer = new L.layerGroup([featuresLayer]); // попробуем сменть тип на layerGroup, но это обычно боком выходит
}
}
else {
var featuresLayer = L.geoJson();
var layer = new L.layerGroup([featuresLayer]);
};
var xml = parseXML(gpx); // делает DOM XML, если gpx -- строка, иначе не делает ничего
const errorNode = xml.querySelector("parsererror");
if (errorNode) return layer.fire('error', {
error: 'Could not parse KML'
});
var geojson = toGeoJSON.kml(xml);
var Points=[];
var Features=[];
for(var i=0; i<geojson.features.length;i++) {
if(geojson.features[i].geometry.type=='Point') Points.push(geojson.features[i]);
else Features.push(geojson.features[i]);
};
var color = 0xFFFFFF;
if(typeof globalCurrentColor !== 'undefined') { // если оно определено, то определена и функция nextColor
color = globalCurrentColor;
globalCurrentColor = nextColor(globalCurrentColor); // сменим текущий цвет, from galadrielmap.js
}
if(color == 0xFFFFFF) featuresLayer.options.color = 0x3388FF; // умолчальный цвет линий
else featuresLayer.options.color = color; // цвет линий
if(options.featureNameNode) { // li с именем файла, из которого делаем layer
options.featureNameNode.style.backgroundColor = '#'+('000000' + color.toString(16)).slice(-6);
}
featuresLayer.options.onEachFeature = function (feature, layer){ // функция, вызываемая для каждой feature при её создании
//console.log('[featuresLayer.options.onEachFeature] feature',feature);
//console.log('KML [featuresLayer.options.onEachFeature] layer',layer);
getPopUpToLine(feature, layer);
if(!options.featureNameNode.classList.contains('currentTrackName')){ // лепить стрелочки на линию, только если это не текущий трек, который всё время перерисовывается. Ибо чёта стрелочки затратно...
layer.setText(' > ',
{repeat: true,
offset: '0.6ch', // сдвиг вправо от линии на половину ширины символа (плюс поправочка) размером font-size. ch - Предварительная мера (ширина) глифа "0" шрифта элемента
attributes: {fill: layer.options.color,
'font-size': '1.5rem',
'font-weight': 'bold',
'opacity': 0.7
}
}); // Leaflet.TextPath
};
};
featuresLayer.options.style = function(geoJsonFeature){ // A Function defining the Path options for styling GeoJSON lines and polygons, called internally when data is added.
const color = '#'+('000000' + featuresLayer.options.color.toString(16)).slice(-6);
return{color: color, opacity: 0.7};
}; // end featuresLayer.options.style = function
addData(featuresLayer, Features); // добавим и покажем всё остальное
if(! layer.hasLayer(featuresLayer)) layer.addLayer(featuresLayer);
if(Points.length) {
var pointsLayer = L.geoJson();
pointsLayer.options.color = color; // цвет значков
pointsLayer.options.pointToLayer = function (geoJsonPoint, latlng) { // функция, вызываемая для каждой точки при её создании
let marker;
if(geoJsonPoint.properties.marker) {
//console.log('Маркер уже есть');
marker = geoJsonPoint.properties.marker.setLatLng(latlng);
}
else {
// parameters тут -- это options создаваемого маркера, плюс разное, типа - color
//console.log('Новый маркер');
let parameters = {color: pointsLayer.options.color}; // таким образом мы забросим цвет в создание маркера
marker = getMarkerToPoint(geoJsonPoint, latlng, parameters);
geoJsonPoint.properties.marker = marker;
}
return marker;
};
doClastering(pointsLayer, Points); // закластеризуем точки
updClaster(pointsLayer); // galadrielmap.js и покажем
layer.addLayer(pointsLayer);
}
return layer;
}
function polylineParse(txt, options, layer) {
layer = layer || L.geoJson();
options = options || {};
var coords = polyline.decode(txt, options.precision);
var geojson = { type: 'LineString', coordinates: [] };
for (var i = 0; i < coords.length; i++) {
// polyline returns coords in lat, lng order, so flip for geojson
geojson.coordinates[i] = [coords[i][1], coords[i][0]];
}
addData(layer, geojson);
return layer;
}
function wktParse(wkt, options, layer) {
layer = layer || L.geoJson();
var geojson = wellknown(wkt);
addData(layer, geojson);
return layer;
}
function parseXML(str) {
//console.log("[parseXML] str:",str);
if (typeof str === 'string') {
return (new DOMParser()).parseFromString(str, 'application/xml');
} else {
return str;
}
}
},{"corslite":3,"csv2geojson":4,"polyline":6,"togeojson":9,"topojson":10,"wellknown":11}],2:[function(require,module,exports){
},{}],3:[function(require,module,exports){
function corslite(url, callback, cors) {
var sent = false;
if (typeof window.XMLHttpRequest === 'undefined') {
return callback(Error('Browser not supported'));
}
if (typeof cors === 'undefined') {
var m = url.match(/^\s*https?:\/\/[^\/]*/);
cors = m && (m[0] !== location.protocol + '//' + location.hostname +
(location.port ? ':' + location.port : ''));
}
var x = new window.XMLHttpRequest();
function isSuccessful(status) {
return status >= 200 && status < 300 || status === 304;
}
/*
if (cors && !('withCredentials' in x)) {
// IE8-9
x = new window.XDomainRequest();
// Ensure callback is never called synchronously, i.e., before
// x.send() returns (this has been observed in the wild).
// See https://github.com/mapbox/mapbox.js/issues/472
// Это костыль к косяку?
var original = callback;
callback = function() {
if (sent) {
original.apply(this, arguments); // это эквивалентно просто вызову callback с её штатными аргументами
} else {
var that = this, args = arguments;
setTimeout(function() { // а это -- вызову callback после завершения текущего цикла корпоративной многозадачности, т.е. здесь -- заведомо не раньше, чем выполнится x.send(null);
original.apply(that, args);
}, 0);
}
}
}
*/
function loaded() {
if (
// XDomainRequest
x.status === undefined ||
// modern browsers
isSuccessful(x.status)) callback.call(x, null, x);
else callback.call(x, x, null);
}
// Both `onreadystatechange` and `onload` can fire. `onreadystatechange`
// has [been supported for longer](http://stackoverflow.com/a/9181508/229001).
if ('onload' in x) {
x.onload = loaded;
} else {
x.onreadystatechange = function readystate() {
if (x.readyState === 4) {
loaded();
}
};
}
// Call the callback with the XMLHttpRequest object as an error and prevent
// it from ever being called again by reassigning it to `noop`
x.onerror = function error(evt) {
// XDomainRequest provides no evt parameter
callback.call(this, evt || true, null);
callback = function() { };
};
// IE9 must have onprogress be set to a unique function.
x.onprogress = function() { };
x.ontimeout = function(evt) {
callback.call(this, evt, null);
callback = function() { };
};
x.onabort = function(evt) {
callback.call(this, evt, null);
callback = function() { };
};
// GET is the only supported HTTP Verb by XDomainRequest and is the
// only one supported here.
x.open('GET', url, true); // асинхронно
//x.open('GET', url, true); // синхронно
x.setRequestHeader("Cache-Control", "no-cache, no-store, max-age=0"); // иначе файл жестоко кешировался браузером, и никакого обновления не происходило
// Send the request. Sending data is not supported.
x.send(null);
sent = true;
return x;
}
if (typeof module !== 'undefined') module.exports = corslite;
},{}],4:[function(require,module,exports){
'use strict';
var dsv = require('d3-dsv'),
sexagesimal = require('sexagesimal');
var latRegex = /(Lat)(itude)?/gi,
lonRegex = /(L)(on|ng)(gitude)?/i;
function guessHeader(row, regexp) {
var name, match, score;
for (var f in row) {
match = f.match(regexp);
if (match && (!name || match[0].length / f.length > score)) {
score = match[0].length / f.length;
name = f;
}
}
return name;
}
function guessLatHeader(row) { return guessHeader(row, latRegex); }
function guessLonHeader(row) { return guessHeader(row, lonRegex); }
function isLat(f) { return !!f.match(latRegex); }
function isLon(f) { return !!f.match(lonRegex); }
function keyCount(o) {
return (typeof o == 'object') ? Object.keys(o).length : 0;
}
function autoDelimiter(x) {
var delimiters = [',', ';', '\t', '|'];
var results = [];
delimiters.forEach(function (delimiter) {
var res = dsv.dsvFormat(delimiter).parse(x);
if (res.length >= 1) {
var count = keyCount(res[0]);
for (var i = 0; i < res.length; i++) {
if (keyCount(res[i]) !== count) return;
}
results.push({
delimiter: delimiter,
arity: Object.keys(res[0]).length,
});
}
});
if (results.length) {
return results.sort(function (a, b) {
return b.arity - a.arity;
})[0].delimiter;
} else {
return null;
}
}
/**
* Silly stopgap for dsv to d3-dsv upgrade
*
* @param {Array} x dsv output
* @returns {Array} array without columns member
*/
function deleteColumns(x) {
delete x.columns;
return x;
}
function auto(x) {
var delimiter = autoDelimiter(x);
if (!delimiter) return null;
return deleteColumns(dsv.dsvFormat(delimiter).parse(x));
}
function csv2geojson(x, options, callback) { // text csv целиком, options, функция onparse как callback
//console.log('[csv2geojson]',callback)
if (!callback) {
callback = options;
options = {};
}
options.delimiter = options.delimiter || ',';
var latfield = options.latfield || '',
lonfield = options.lonfield || '',
crs = options.crs || '';
var features = [], featurecollection = {type: 'FeatureCollection', features: features};
if (crs !== '') {
featurecollection.crs = {type: 'name', properties: {name: crs}};
}
if (options.delimiter === 'auto' && typeof x == 'string') {
options.delimiter = autoDelimiter(x);
if (!options.delimiter) {
callback({
type: 'Error',
message: 'Could not autodetect delimiter'
});
return;
}
}
//console.log('leaflet-omnivore.js [csv2geojson] options.delimiter=',options.delimiter);
// массив объектов из строк csv файла, начиная со второй, с именами атрибутов - из первой
var parsed = (typeof x == 'string') ?
dsv.dsvFormat(