Added portal details, and fixed most other things
This commit is contained in:
parent
ef562b530b
commit
7df1fb0f99
|
@ -9,12 +9,14 @@ This project is a modification of the portals-list from the IITC community editi
|
|||
- Efficient data export
|
||||
- Comprehensive information extraction
|
||||
- Compatibility with other programs and data processing tools
|
||||
- Export data with details including resonators, mods, and owner
|
||||
- Export data within polygons drawn on the map
|
||||
- JSON schema provided for both basic and detailed data
|
||||
|
||||
## Work in Progress
|
||||
This project is still under development. Future updates will include features from other scripts to provide a more comprehensive solution. Here are some of the improvements to look forward to:
|
||||
|
||||
- A more detailed README, including credits
|
||||
- Addition of a license
|
||||
- Inclusion of other portal data, from image links to portal mods
|
||||
|
||||
Please note that this list is not exhaustive and more features may be added based on user feedback and project requirements.
|
||||
|
|
|
@ -16,7 +16,7 @@ function wrapper(plugin_info) {
|
|||
if (typeof window.plugin !== 'function') window.plugin = function() {};
|
||||
plugin_info.pluginId = 'portalsJSON';
|
||||
window.plugin.portalsJSON = function() {};
|
||||
var portalsData = new Map(); // More efficient data lookup
|
||||
var portalsData = new Map();
|
||||
let startTime = null;
|
||||
let exportTime = null;
|
||||
|
||||
|
@ -63,26 +63,14 @@ function wrapper(plugin_info) {
|
|||
return portal.options.data.resCount;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Links',
|
||||
value: function(portal) {
|
||||
var links = window.getPortalLinks(portal.options.guid);
|
||||
portal.links = links; // Save the computed links for reuse
|
||||
return links.in.length + links.out.length;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Fields',
|
||||
value: function(portal) {
|
||||
return getPortalFieldsCount(portal.options.guid);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'AP',
|
||||
value: function(portal) {
|
||||
var links = portal.links; // Use the previously computed links
|
||||
var fields = getPortalFieldsCount(portal.options.guid);
|
||||
return portalApGainMaths(portal.options.data.resCount, links.in.length + links.out.length, fields);
|
||||
var links = window.getPortalLinks(portal.options.guid);
|
||||
portal.links = links;
|
||||
var fields = window.getPortalFields(portal.options.guid);
|
||||
portal.fields = fields;
|
||||
return portalApGainMaths(portal.options.data.resCount, links.in.length + links.out.length, fields.length);
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -114,7 +102,7 @@ function wrapper(plugin_info) {
|
|||
{
|
||||
title: 'Incoming Links',
|
||||
value: function(portal) {
|
||||
var links = portal.links; // Use the previously computed links
|
||||
var links = portal.links;
|
||||
return links.in.map(function(linkGuid) {
|
||||
var link = window.links[linkGuid].options.data;
|
||||
var GUID = link.oGuid;
|
||||
|
@ -133,7 +121,7 @@ function wrapper(plugin_info) {
|
|||
{
|
||||
title: 'Outgoing Links',
|
||||
value: function(portal) {
|
||||
var links = portal.links; // Use the previously computed links
|
||||
var links = portal.links;
|
||||
return links.out.map(function(linkGuid) {
|
||||
var link = window.links[linkGuid].options.data;
|
||||
var GUID = link.dGuid;
|
||||
|
@ -149,6 +137,13 @@ function wrapper(plugin_info) {
|
|||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Fields',
|
||||
value: function(portal) {
|
||||
var fields = portal.fields;
|
||||
return fields;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Scanner Link',
|
||||
value: function(portal) {
|
||||
|
@ -169,7 +164,7 @@ function wrapper(plugin_info) {
|
|||
};
|
||||
|
||||
window.plugin.portalsJSON.exportData = function() {
|
||||
portalsData.clear(); // Clear the map before exporting
|
||||
portalsData.clear();
|
||||
startTime = new Date();
|
||||
var displayBounds = map.getBounds();
|
||||
Object.values(window.portals).forEach(function (portal, index) {
|
||||
|
@ -281,12 +276,11 @@ function wrapper(plugin_info) {
|
|||
};
|
||||
|
||||
window.plugin.portalsJSON.exportPolygons = function() {
|
||||
portalsData.clear(); // Clear the map before exporting
|
||||
portalsData.clear();
|
||||
startTime = new Date();
|
||||
|
||||
var searchItems = []; // Data about shapes that will be searched for portals
|
||||
var searchItems = [];
|
||||
|
||||
// Process drawn items
|
||||
if (window.plugin.drawTools && window.plugin.drawTools.drawnItems) {
|
||||
window.plugin.drawTools.drawnItems.eachLayer(function (drawnItem) {
|
||||
if (drawnItem instanceof L.GeodesicCircle) {
|
||||
|
@ -302,7 +296,6 @@ function wrapper(plugin_info) {
|
|||
});
|
||||
}
|
||||
|
||||
// Process search result areas
|
||||
if (window.search.lastSearch &&
|
||||
window.search.lastSearch.selectedResult &&
|
||||
window.search.lastSearch.selectedResult.layer) {
|
||||
|
@ -316,7 +309,6 @@ function wrapper(plugin_info) {
|
|||
});
|
||||
}
|
||||
|
||||
// Process portals
|
||||
$.each(window.portals, function (guid, portal) {
|
||||
var point = portal.getLatLng();
|
||||
var found = false;
|
||||
|
@ -325,19 +317,19 @@ function wrapper(plugin_info) {
|
|||
case 'circle':
|
||||
if (window.plugin.portalsJSON.pointIsInCircle(point, searchItem)) {
|
||||
found = true;
|
||||
return false; // Breaks the loop
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'polygon':
|
||||
if (window.plugin.portalsJSON.pointIsInPolygon(point, searchItem)) {
|
||||
found = true;
|
||||
return false; // Breaks the loop
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'multipolygon':
|
||||
if (window.plugin.portalsJSON.pointIsInMultiPolygon(point, searchItem.polygons)) {
|
||||
found = true;
|
||||
return false; // Breaks the loop
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
};
|
||||
|
@ -347,8 +339,8 @@ function wrapper(plugin_info) {
|
|||
}
|
||||
});
|
||||
|
||||
// Export data
|
||||
var jsonData = JSON.stringify(Array.from(portalsData.values()), null, 2);
|
||||
|
||||
var blob = new Blob([jsonData], { type: 'application/json' });
|
||||
var url = URL.createObjectURL(blob);
|
||||
|
||||
|
@ -363,37 +355,195 @@ function wrapper(plugin_info) {
|
|||
exportTime = new Date();
|
||||
}
|
||||
|
||||
function downloadFile(data, filename) {
|
||||
window.plugin.portalsJSON.loadPortalDetails = async function(guid, retries = 3) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let retryCount = 0;
|
||||
const loadDetails = () => {
|
||||
window.portalDetail.request(guid);
|
||||
window.addHook('portalDetailLoaded', function(data) {
|
||||
if (data.guid === guid) {
|
||||
resolve(data.details);
|
||||
window.removeHook('portalDetailLoaded', arguments.callee);
|
||||
}
|
||||
});
|
||||
};
|
||||
const errorHandler = (error) => {
|
||||
if (retryCount < retries) {
|
||||
retryCount++;
|
||||
setTimeout(loadDetails, 1000);
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
};
|
||||
loadDetails();
|
||||
});
|
||||
};
|
||||
|
||||
window.plugin.portalsJSON.exportDataWithDetails = async function() {
|
||||
portalsData.clear();
|
||||
startTime = new Date();
|
||||
var displayBounds = map.getBounds();
|
||||
for (const portal of Object.values(window.portals)) {
|
||||
if (!displayBounds.contains(portal.getLatLng())) continue;
|
||||
await window.plugin.portalsJSON.processPortalWithDetails(portal);
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
}
|
||||
|
||||
var jsonData = JSON.stringify(Array.from(portalsData.values()), null, 2);
|
||||
|
||||
var blob = new Blob([jsonData], { type: 'application/json' });
|
||||
var url = URL.createObjectURL(blob);
|
||||
|
||||
var link = document.createElement('a');
|
||||
link.href = 'data:text/json;charset=utf-8,' + encodeURIComponent(data);
|
||||
link.download = filename;
|
||||
link.href = url;
|
||||
link.download = 'portals.json';
|
||||
link.style.display = 'none';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
}
|
||||
|
||||
exportTime = new Date();
|
||||
};
|
||||
|
||||
window.plugin.portalsJSON.exportPolygonsWithDetails = async function() {
|
||||
portalsData.clear();
|
||||
startTime = new Date();
|
||||
|
||||
var searchItems = [];
|
||||
|
||||
if (window.plugin.drawTools && window.plugin.drawTools.drawnItems) {
|
||||
window.plugin.drawTools.drawnItems.eachLayer(function (drawnItem) {
|
||||
if (drawnItem instanceof L.GeodesicCircle) {
|
||||
var searchCircle = window.plugin.portalsJSON.circleToSearchCircle(drawnItem);
|
||||
searchItems.push(searchCircle);
|
||||
}
|
||||
else if (drawnItem instanceof L.GeodesicPolygon) {
|
||||
var searchPolygons = window.plugin.portalsJSON.multiPolygonToSearchPolygons(drawnItem);
|
||||
$.each(searchPolygons, function (index, searchItem) {
|
||||
searchItems.push(searchItem);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (window.search.lastSearch &&
|
||||
window.search.lastSearch.selectedResult &&
|
||||
window.search.lastSearch.selectedResult.layer) {
|
||||
window.search.lastSearch.selectedResult.layer.eachLayer(function (drawnItem) {
|
||||
if (drawnItem instanceof L.Polygon || (typeof L.MultiPolygon == "function" && drawnItem instanceof L.MultiPolygon)) {
|
||||
var searchPolygons = window.plugin.portalsJSON.multiPolygonToSearchPolygons(drawnItem);
|
||||
$.each(searchPolygons, function (index, searchItem) {
|
||||
searchItems.push(searchItem);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (const portal of Object.values(window.portals)) {
|
||||
var point = portal.getLatLng();
|
||||
var found = false;
|
||||
$.each(searchItems, function (index, searchItem) {
|
||||
switch (searchItem.type) {
|
||||
case 'circle':
|
||||
if (window.plugin.portalsJSON.pointIsInCircle(point, searchItem)) {
|
||||
found = true;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'polygon':
|
||||
if (window.plugin.portalsJSON.pointIsInPolygon(point, searchItem)) {
|
||||
found = true;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'multipolygon':
|
||||
if (window.plugin.portalsJSON.pointIsInMultiPolygon(point, searchItem.polygons)) {
|
||||
found = true;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
};
|
||||
});
|
||||
if (found) {
|
||||
await window.plugin.portalsJSON.processPortalWithDetails(portal);
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
}
|
||||
}
|
||||
|
||||
var jsonData = JSON.stringify(Array.from(portalsData.values()), null, 2);
|
||||
|
||||
var blob = new Blob([jsonData], { type: 'application/json' });
|
||||
var url = URL.createObjectURL(blob);
|
||||
|
||||
var link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = 'polygons.json';
|
||||
link.style.display = 'none';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
|
||||
exportTime = new Date();
|
||||
};
|
||||
|
||||
window.plugin.portalsJSON.processPortalWithDetails = async function(portal) {
|
||||
if (!('title' in portal.options.data)) return;
|
||||
var portalData = {};
|
||||
window.plugin.portalsJSON.fields.forEach(function(field) {
|
||||
var value = field.value(portal);
|
||||
portalData[field.title] = value;
|
||||
});
|
||||
try {
|
||||
const details = await window.plugin.portalsJSON.loadPortalDetails(portal.options.guid);
|
||||
portalData['Mods'] = details.mods.map(mod => mod ? {
|
||||
owner: mod.owner,
|
||||
name: mod.name,
|
||||
rarity: mod.rarity,
|
||||
stats: mod.stats
|
||||
} : null);
|
||||
portalData['Resonators'] = details.resonators.map(resonator => resonator ? {
|
||||
owner: resonator.owner,
|
||||
level: resonator.level,
|
||||
energy: resonator.energy
|
||||
} : null);
|
||||
portalData['Owner'] = details.owner;
|
||||
portalsData.set(portal.options.guid, portalData);
|
||||
} catch (error) {
|
||||
console.error(`Failed to load details for portal ${portal.options.guid}:`, error);
|
||||
}
|
||||
};
|
||||
|
||||
window.plugin.portalsJSON.displayPL = function() {
|
||||
var button = document.createElement('button');
|
||||
button.textContent = 'Export Data to JSON';
|
||||
button.addEventListener('click', window.plugin.portalsJSON.exportData);
|
||||
button.style.display = 'block';
|
||||
|
||||
var polygonButton = document.createElement('button');
|
||||
polygonButton.textContent = 'Export Polygons';
|
||||
polygonButton.addEventListener('click', window.plugin.portalsJSON.exportPolygons);
|
||||
var breakLine = document.createElement('br'); // Line break
|
||||
polygonButton.style.display = 'block';
|
||||
|
||||
var detailsButton = document.createElement('button');
|
||||
detailsButton.textContent = 'Export Data with Details';
|
||||
detailsButton.addEventListener('click', window.plugin.portalsJSON.exportDataWithDetails);
|
||||
detailsButton.style.display = 'block';
|
||||
|
||||
var polygonDetailsButton = document.createElement('button');
|
||||
polygonDetailsButton.textContent = 'Export Polygons with Details';
|
||||
polygonDetailsButton.addEventListener('click', window.plugin.portalsJSON.exportPolygonsWithDetails);
|
||||
polygonDetailsButton.style.display = 'block';
|
||||
|
||||
var timer = document.createElement('span');
|
||||
timer.id = 'exportTimer';
|
||||
|
||||
var container = document.createElement('div');
|
||||
container.appendChild(button);
|
||||
container.appendChild(polygonButton);
|
||||
container.appendChild(detailsButton);
|
||||
container.appendChild(polygonDetailsButton);
|
||||
container.appendChild(timer);
|
||||
|
||||
// create a separate div for timer
|
||||
var timerDiv = document.createElement('div');
|
||||
timerDiv.appendChild(timer);
|
||||
container.appendChild(breakLine);
|
||||
container.appendChild(timerDiv);
|
||||
|
||||
if (window.useAppPanes()) {
|
||||
var pane = app.panes.find(function(pane) { return pane.name === 'plugin-portalsJSON'; });
|
||||
pane.setContent(container);
|
||||
|
@ -427,7 +577,7 @@ function wrapper(plugin_info) {
|
|||
if (window.useAppPanes()) {
|
||||
app.addPane('plugin-portalsJSON', 'Portals JSON', 'ic_action_paste');
|
||||
addHook('paneChanged', window.plugin.portalsJSON.onPaneChanged);
|
||||
window.plugin.portalsJSON.onPaneChanged('plugin-portalsJSON'); // Manually trigger pane changed to display button
|
||||
window.plugin.portalsJSON.onPaneChanged('plugin-portalsJSON');
|
||||
} else {
|
||||
var portalJSONLink = document.createElement('a');
|
||||
portalJSONLink.setAttribute('title', 'Exports a JSON of portals in the current view [j]');
|
||||
|
@ -439,14 +589,12 @@ function wrapper(plugin_info) {
|
|||
document.querySelector('#toolbox').appendChild(portalJSONLink);
|
||||
}
|
||||
};
|
||||
// inject code into site context
|
||||
setup.info = plugin_info; //add the script info data to the function as a property
|
||||
setup.info = plugin_info;
|
||||
if (!window.bootPlugins) window.bootPlugins = [];
|
||||
window.bootPlugins.push(setup);
|
||||
if (window.iitcLoaded && typeof setup === 'function') setup();
|
||||
}
|
||||
|
||||
// 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 };
|
||||
|
|
|
@ -24,12 +24,6 @@
|
|||
"Resonators": {
|
||||
"type": "integer"
|
||||
},
|
||||
"Links": {
|
||||
"type": "integer"
|
||||
},
|
||||
"Fields": {
|
||||
"type": "integer"
|
||||
},
|
||||
"AP": {
|
||||
"type": "integer"
|
||||
},
|
||||
|
@ -97,6 +91,12 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"Fields": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"Scanner Link": {
|
||||
"type": "string"
|
||||
}
|
||||
|
@ -107,8 +107,6 @@
|
|||
"Team",
|
||||
"Health",
|
||||
"Resonators",
|
||||
"Links",
|
||||
"Fields",
|
||||
"AP",
|
||||
"Latitude",
|
||||
"Longitude",
|
||||
|
@ -116,6 +114,7 @@
|
|||
"GUID",
|
||||
"Incoming Links",
|
||||
"Outgoing Links",
|
||||
"Fields",
|
||||
"Scanner Link"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -0,0 +1,192 @@
|
|||
{
|
||||
"title": "Portal",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"Portal Name": {
|
||||
"type": "string"
|
||||
},
|
||||
"Level": {
|
||||
"type": "integer"
|
||||
},
|
||||
"Team": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Neutral",
|
||||
"Resistance",
|
||||
"Enlightened",
|
||||
"_MACHINA_",
|
||||
"Unknown"
|
||||
]
|
||||
},
|
||||
"Health": {
|
||||
"type": "integer"
|
||||
},
|
||||
"Resonators": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"owner": {
|
||||
"type": "string"
|
||||
},
|
||||
"level": {
|
||||
"type": "integer"
|
||||
},
|
||||
"energy": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
"AP": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"friendlyAp": {
|
||||
"type": "integer"
|
||||
},
|
||||
"enemyAp": {
|
||||
"type": "integer"
|
||||
},
|
||||
"destroyAp": {
|
||||
"type": "integer"
|
||||
},
|
||||
"destroyResoAp": {
|
||||
"type": "integer"
|
||||
},
|
||||
"captureAp": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"friendlyAp",
|
||||
"enemyAp",
|
||||
"destroyAp",
|
||||
"destroyResoAp",
|
||||
"captureAp"
|
||||
]
|
||||
},
|
||||
"Latitude": {
|
||||
"type": "number"
|
||||
},
|
||||
"Longitude": {
|
||||
"type": "number"
|
||||
},
|
||||
"Image URL": {
|
||||
"type": "string"
|
||||
},
|
||||
"GUID": {
|
||||
"type": "string"
|
||||
},
|
||||
"Incoming Links": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"GUID": {
|
||||
"type": "string"
|
||||
},
|
||||
"Latitude": {
|
||||
"type": "number"
|
||||
},
|
||||
"Longitude": {
|
||||
"type": "number"
|
||||
},
|
||||
"Portal Name": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"GUID",
|
||||
"Latitude",
|
||||
"Longitude",
|
||||
"Portal Name"
|
||||
]
|
||||
}
|
||||
},
|
||||
"Outgoing Links": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"GUID": {
|
||||
"type": "string"
|
||||
},
|
||||
"Latitude": {
|
||||
"type": "number"
|
||||
},
|
||||
"Longitude": {
|
||||
"type": "number"
|
||||
},
|
||||
"Portal Name": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"GUID",
|
||||
"Latitude",
|
||||
"Longitude",
|
||||
"Portal Name"
|
||||
]
|
||||
}
|
||||
},
|
||||
"Fields": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"Scanner Link": {
|
||||
"type": "string"
|
||||
},
|
||||
"Mods": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"owner": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"rarity": {
|
||||
"type": "string"
|
||||
},
|
||||
"stats": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"owner",
|
||||
"name",
|
||||
"rarity",
|
||||
"stats"
|
||||
]
|
||||
}
|
||||
},
|
||||
"Owner": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"Portal Name",
|
||||
"Level",
|
||||
"Team",
|
||||
"Health",
|
||||
"Resonators",
|
||||
"AP",
|
||||
"Latitude",
|
||||
"Longitude",
|
||||
"Image URL",
|
||||
"GUID",
|
||||
"Incoming Links",
|
||||
"Outgoing Links",
|
||||
"Fields",
|
||||
"Scanner Link",
|
||||
"Mods",
|
||||
"Owner"
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue