|
// ==UserScript== |
|
// @id kml-exporter |
|
// @name IITC plugin: KML Exporter |
|
// @category Tools |
|
// @version 1 |
|
// @namespace https://gist.github.com/johnd0e |
|
// @homepageURL https://gist.github.com/johnd0e/cedbbc998888c5f497aec4afcd641e58 |
|
// @supportURL https://gist.github.com/johnd0e/cedbbc998888c5f497aec4afcd641e58#new_comment_field |
|
// @updateURL https://gist.github.com/johnd0e/cedbbc998888c5f497aec4afcd641e58/raw/KML-exporter.user.js |
|
// @downloadURL https://gist.github.com/johnd0e/cedbbc998888c5f497aec4afcd641e58/raw/KML-exporter.user.js |
|
// @description Exports portals currently in view for use with Google Map /Earth |
|
// (KML Format incl. extra data). |
|
// !! Currently styles are optimized for mobile GE |
|
// @match https://intel.ingress.com/* |
|
// @grant none |
|
// ==/UserScript== |
|
|
|
function wrapper(plugin_info) { |
|
'use strict'; |
|
|
|
// ensure plugin framework is there, even if iitc is not yet loaded |
|
if(typeof window.plugin !== 'function') window.plugin = function() {}; |
|
|
|
// base context for plugin |
|
window.plugin.ingressKMLexporter = {}; |
|
const self = window.plugin.ingressKMLexporter; |
|
// custom dialog wrapper with more flexibility |
|
|
|
const TEAM_TO_CSS = ['none', 'res', 'enl']; |
|
|
|
self.default = { |
|
icon: 'http://www.gstatic.com/mapspro/images/stock/959-wht-circle-blank.png', |
|
balloon: '<a href="$[portal/url]"><img src="$[portal/img]" height="80%" title="$[name]"/></a>', // mobile GE |
|
iconColorMode: 'normal', // 'random': not working on mobile GE |
|
labelColorMode: 'normal', |
|
teams: { |
|
none: { |
|
iconColor: 'B00066FF' |
|
}, |
|
res: { |
|
iconColor: 'B0D09205' |
|
}, |
|
enl: { |
|
iconColor: 'B002BF02' |
|
} |
|
} |
|
}; |
|
|
|
self.normal = { |
|
iconScale: 0.3, |
|
labelScale: 0.4, |
|
teams: {} // default |
|
}; |
|
|
|
self.highlight = { |
|
iconScale: 0.7, |
|
labelScale: 0.7, |
|
teams: {} // default |
|
}; |
|
|
|
//desktop GE |
|
//self.BALLOON = '<a href="$[portal/url]"><img src="$[portal/img]" height="180" title="$[name]"/></a>' |
|
//self.listBALLOON = '<a href="#$[id];balloonFlyto"><img src="$[portal/img]" height="180" title="fly to $[name]"/></a>'// |
|
|
|
function genStyle (type,team) { |
|
const s = L.extend({}, self.default, self[type], self.default.teams[team], self[type].teams[team]); |
|
return ` |
|
<Style id="${team}-${type}"> |
|
<IconStyle> |
|
<Icon><href>${s.icon}</href></Icon> |
|
<color>${s.iconColor}</color> |
|
<colorMode>${s.iconColorMode}</colorMode> |
|
<scale>${s.iconScale}</scale> |
|
</IconStyle> |
|
<LabelStyle><scale>${s.labelScale}</scale><colorMode>${s.labelColorMode}</colorMode></LabelStyle> |
|
<BalloonStyle> |
|
<text><![CDATA[${s.balloon}]]></text>' |
|
</BalloonStyle> |
|
</Style>`; |
|
/* |
|
'<BalloonStyle>' + |
|
' <text><![CDATA[' + self.BALLOON + ']]></text>' + // desktop: self.listBALLOON |
|
' <displayMode>hide</displayMode>' + // desktop only |
|
'</BalloonStyle>'); |
|
*/ |
|
} |
|
|
|
function genStyles () { |
|
const o = []; |
|
['none','enl','res'].forEach(function (team) { |
|
o.push(genStyle('normal',team)); |
|
o.push(genStyle('highlight',team)); |
|
}); |
|
return o.join('\n'); |
|
} |
|
|
|
function genPortal (p) { |
|
const d = p.options.data; // title, level, resCount, health, image |
|
const teamStyle = TEAM_TO_CSS[p.options.team]; |
|
const ll = p.getLatLng(); // lat, lng |
|
const url = `https://intel.ingress.com/intel?pll=${ll.lat},${ll.lng}`; |
|
// const url = 'https://intel.ingress.com' + window.makePermalink(ll); // iitc-ce |
|
const snippet = `<a href=${url}>${ll.lat},${ll.lng}</a>`; |
|
const description = `<img src="${d.image}"/>${url}`; |
|
/* |
|
function safe_html(str) { //window.escapeHtmlSpecialChars |
|
const span = document.createElement('span'); |
|
span.textContent = str; |
|
return span.innerHTML; |
|
} |
|
const title_html = safe_html(title) |
|
*/ |
|
return ` |
|
<Placemark id="${p.options.guid}"> |
|
<styleUrl>#${teamStyle}</styleUrl> |
|
<name><![CDATA[${d.title}]]></name> |
|
<description><![CDATA[${description}]]></description> |
|
<Snippet maxLines="1"><![CDATA[${snippet}]]></Snippet> |
|
<Point> |
|
<coordinates>${ll.lng},${ll.lat},0.0</coordinates> |
|
</Point> |
|
<ExtendedData> |
|
<SchemaData schemaUrl="#portal"> |
|
<SimpleData name="team">${teamStyle}</SimpleData> |
|
<SimpleData name="level">${d.level}</SimpleData> |
|
<SimpleData name="resCount">${d.resCount}</SimpleData> |
|
<SimpleData name="health">${d.health}</SimpleData> |
|
<SimpleData name="url">${url}</SimpleData> |
|
<SimpleData name="img">${d.image}</SimpleData> |
|
<!--SimpleData name="gx_media_links">${d.image}</SimpleData--> |
|
</SchemaData> |
|
</ExtendedData> |
|
</Placemark>`; |
|
} |
|
|
|
function getPortals () { |
|
const mapBounds = map.getBounds(); |
|
function inBounds (portal) { |
|
return mapBounds.contains(portal.getLatLng()); |
|
} |
|
const portals = []; |
|
for (let x in window.portals) { portals.push(window.portals[x]); } |
|
return portals.filter(inBounds); |
|
} |
|
|
|
function gen (portals,title) { |
|
return `<?xml version="1.0" encoding="UTF-8"?> |
|
<kml xmlns="http://www.opengis.net/kml/2.2"> |
|
<Document> |
|
<name>[${title || 'Import from IITC'}]</name> |
|
${portals.map(genPortal).join('\n')} |
|
|
|
${genStyles()} |
|
<Schema name="portal" id="portal"> |
|
<SimpleField type="string" name="team"></SimpleField> |
|
<SimpleField type="uint" name="level"></SimpleField> |
|
<SimpleField type="uint" name="resCount"></SimpleField> |
|
<SimpleField type="uint" name="health"></SimpleField> |
|
<SimpleField type="string" name="img"></SimpleField> |
|
<SimpleField type="string" name="url"></SimpleField> |
|
</Schema> |
|
</Document> |
|
</kml> |
|
`; |
|
} |
|
|
|
function saveAs (data,filename,dataType) { |
|
|
|
const link = document.createElement('a'); |
|
/* |
|
dataType = dataType || 'text/plain;charset=utf-8'; // https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types |
|
const uri = L.Util.template('data:{dataType};charset=utf-8,{content}',{ |
|
dataType: dataType, |
|
content: encodeURIComponent(data) |
|
}); |
|
link.href = data; // !!fails with large amounts of data |
|
*/ |
|
|
|
if (!(data instanceof Array)) { data = [data]; } |
|
const file = new Blob(data, {type: dataType}); // https://developer.mozilla.org/en-US/docs/Web/API/Blob |
|
const objectURL = URL.createObjectURL(file); // https://www.w3.org/TR/FileAPI/#blob-url |
|
|
|
link.href = objectURL; |
|
link.download = filename; |
|
link.style.display = 'none'; |
|
document.body.appendChild(link); |
|
link.click(); |
|
link.remove(); |
|
//setTimeout(function () { |
|
URL.revokeObjectURL(objectURL); |
|
//}); |
|
|
|
// alternative: https://github.com/eligrey/FileSaver.js/blob/master/src/FileSaver.js |
|
|
|
return true; |
|
} |
|
|
|
function genDlg (data, filename, dataType) { |
|
const description = $('<p>').html('Save the data below to a KML file and import it on <a href=https://google.com/maps/d> <code>google.com/maps/d</code></a>.'); |
|
const textarea = $('<textarea>',{rows: 30, style: 'width: 100%', autofocus: true}).val(data); |
|
const buttons = $('<p>'); |
|
$('<button>').html('Copy').click(function () { |
|
textarea.focus(); |
|
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Interact_with_the_clipboard |
|
document.execCommand("copy"); // https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand |
|
}).appendTo(buttons); |
|
/* |
|
if (typeof android !== 'undefined' && android.copy) { |
|
$('<button>').html('Copy').click(function () { |
|
const el = textarea[0]; |
|
android.copy(el.value.substring(el.selectionStart, el.selectionEnd)); |
|
}).appendTo(buttons); |
|
} |
|
*/ |
|
if (typeof android !== 'undefined' && android.shareString) { |
|
$('<button>').html('Share').click(function () { |
|
const el = textarea[0]; |
|
android.shareString(el.value.substring(el.selectionStart, el.selectionEnd)); |
|
}).appendTo(buttons); |
|
// alt: https://developers.google.com/web/updates/2016/09/navigator-share |
|
} |
|
const androidSaveButton = $('<button>').html('Save').appendTo(buttons); |
|
if (typeof android !== 'undefined' && android.saveFile) { // opposite: getFileRequestUrlPrefix/requestFile |
|
androidSaveButton.click(function () { |
|
android.saveFile(filename, dataType, data); |
|
}); |
|
} else { |
|
androidSaveButton.click(function () { |
|
saveAs(data, filename, dataType); |
|
}); |
|
} |
|
const dialog = window.dialog({ |
|
title: 'Ingress KML Exporter', |
|
html: $('<div>') |
|
.append(description) |
|
.append(buttons) |
|
.append(textarea), |
|
width: 600 |
|
}); |
|
dialog.parent().find('.ui-dialog-buttonpane').remove(); |
|
|
|
textarea.focus(function () { |
|
const el = textarea[0]; |
|
if (el.selectionStart === el.selectionEnd) { |
|
textarea.select(); |
|
} |
|
}); |
|
} |
|
|
|
function setup () { |
|
/* |
|
function pad (str, len) { |
|
str = str.toString(); |
|
const n = (len || 2) - str.length; |
|
return '0'.repeat(n>0 ? n : 0) + str; |
|
} |
|
|
|
const dt = new Date(); |
|
const dtf = pad(dt.getMonth()+1) + pad(dt.getDate()) + '_' + pad(dt.getHours()) + pad(dt.getMinutes()) + pad(dt.getSeconds()); |
|
*/ |
|
const file = $('<a title="Download KML-list of portals."><code>[.kml]</code></a>') |
|
.click(function () { |
|
const date = new Date().toLocaleString('ja',{dateStyle:'short',timeStyle:'medium',hour12:false}); |
|
const filename = date.replace(/[/:]/g,'').replace(' ','_') + '.kml'; |
|
const data = gen(getPortals(),`Import from IITC [${date}]`); |
|
const dataType = 'application/vnd.google-earth.kml+xml'; |
|
if (typeof android !== 'undefined' && android.saveFile) { // opposite: getFileRequestUrlPrefix/requestFile |
|
android.saveFile(filename, dataType, data); |
|
} else { |
|
saveAs(data,filename,dataType); |
|
} |
|
}); |
|
} |
|
|
|
const dlg = $('<a title="Generate KML list of portals">KML Export</a>') |
|
.click(function () { |
|
const date = new Date().toLocaleString('ja',{dateStyle:'short',timeStyle:'medium',hour12:false}); |
|
const data = gen(getPortals(),`Import from IITC [${date}]`); |
|
const filename = date.replace(/[/:]/g,'').replace(' ','_') + '.kml'; |
|
const dataType = 'application/vnd.google-earth.kml+xml'; |
|
genDlg(data,filename,dataType); |
|
}) |
|
.appendTo('#toolbox') |
|
.on('taphold',function (e) { |
|
$(this).after(download).off('taphold'); |
|
}); |
|
} |
|
|
|
setup.info = plugin_info; //add the script info data to the function as a property |
|
if(!window.bootPlugins) window.bootPlugins = []; |
|
window.bootPlugins.push(setup); |
|
// if IITC has already booted, immediately run the 'setup' function |
|
if(window.iitcLoaded && typeof setup === 'function') setup(); |
|
} // wrapper end |
|
// inject code into site context |
|
var script = document.createElement('script'); |
|
var info = {}; |
|
if (typeof GM_info !== 'undefined' && GM_info && GM_info.script) info.script = { version: GM_info.script.version, name: GM_info.script.name, description: GM_info.script.description }; |
|
script.appendChild(document.createTextNode('('+ wrapper +')('+JSON.stringify(info)+');')); |
|
(document.body || document.head || document.documentElement).appendChild(script); |
|
|