Added portal details, and fixed most other things

This commit is contained in:
cyi1341 2023-10-09 19:21:05 +08:00
parent ef562b530b
commit 7df1fb0f99
4 changed files with 395 additions and 54 deletions

View File

@ -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.

View File

@ -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 };

View File

@ -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"
]
}

View File

@ -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"
]
}