UNPKG

atmosx-placefile-parser

Version:
191 lines (175 loc) 9.96 kB
/* _ _ __ __ /\ | | | | (_) \ \ / / / \ | |_ _ __ ___ ___ ___ _ __ | |__ ___ _ __ _ ___ \ V / / /\ \| __| '_ ` _ \ / _ \/ __| '_ \| '_ \ / _ \ '__| |/ __| > < / ____ \ |_| | | | | | (_) \__ \ |_) | | | | __/ | | | (__ / . \ /_/ \_\__|_| |_| |_|\___/|___/ .__/|_| |_|\___|_| |_|\___/_/ \_\ | | |_| Written by: k3yomi@GitHub */ import * as loader from '../bootstrap'; export class AtmosXPlacefileParser { /** * @function httpFetch * @description Fetches data from a given URL with optional headers. * * @param {string} url - The URL to fetch data from. */ static httpFetch(url: string, headers: any = []): Promise<any> { return new Promise(async (resolve, reject) => { let response = await loader.packages.axios.get(url, { headers: headers, maxRedirects: 0, timeout: 5000 }) let validateStatus = (status: number) => { return status == 200 || status == 500 } let { data, status } = response if (validateStatus(status)) { return resolve({success: true, data: data}) } else { return reject({success: false, error: `HTTP Error: ${status}`}) } }) } /** * @function parsePlacefile * @description Parses a placefile from a URL or raw data string. * * @param {string} placefileUrl - The URL of the placefile to parse. * @param {string} placefileData - The raw placefile data as a string. */ static parsePlacefile(placefileData: string = null, placefileUrl: string = null, headers: any = []): Promise<any> { return new Promise(async (resolve, reject) => { if (placefileUrl != null) { let data = await this.httpFetch(placefileUrl, headers) if (data.success) { placefileData = data.data } } let lines = placefileData.split(/\r?\n/) let objects = [] let currentObject: any = {} for (let line of lines) { line = line.trim() if (!line || line.startsWith('//')) { continue } if (line.startsWith('Line:')) { const parts = line.replace(`Line:`, '').trim().split(',') currentObject.line = { width: parseFloat(parts[0]), style: parseFloat(parts[1]), text: parts.slice(2).join(',').trim() } currentObject.coordinates = [] } if (line.match(/^-?\d+(\.\d+)?,-?\d+(\.\d+)?$/)) { const coords = line.split(',') if (currentObject.coordinates) { currentObject.coordinates.push([parseFloat(coords[0]), parseFloat(coords[1])]) } } if (line.startsWith('Object:')) { const coordinates = line.replace(`Object:`, '').trim().split(',') currentObject.object = { coordinates: [parseFloat(coordinates[0]), parseFloat(coordinates[1])], properties: {} } } if (line.startsWith('Color:')) { const parts = line.replace(`Color:`, '').trim().split(' ') currentObject['color'] = { r: parseInt(parts[0]), g: parseInt(parts[1]), b: parseInt(parts[2]), a: parseFloat(parts[3]) } } if (line.startsWith('Icon:')) { const parts = line.replace(`Icon:`, '').trim().split(',') currentObject.icon = { x: parseFloat(parts[0]), y: parseFloat(parts[1]), color: parts[2], scale: parseFloat(parts[3]), type: parts[4], label: parts.slice(5).join(',').trim().replace(/^"|"$/g, '') } } if (line.startsWith('End:') && currentObject && Object.keys(currentObject).length > 0) { objects.push(currentObject); currentObject = {} } if (!lines.includes('End:') && currentObject && Object.keys(currentObject).length > 0) { objects.push(currentObject); currentObject = {} } } resolve(objects) }) } /** * @function parseTable * @description Parses a table-formatted placefile from a URL or raw data string. * * @param {string} placefileUrl - The URL of the placefile to parse. * @param {string} placefileData - The raw placefile data as a string. * @param {Array} headers - Optional headers for the HTTP request. */ static parseTable(placefileData: string = null, placefileUrl: string = null, headers: any = []): Promise<any> { return new Promise(async (resolve, reject) => { if (placefileUrl != null) { let data = await this.httpFetch(placefileUrl, headers) if (data.success) { placefileData = data.data } } let lines = placefileData.split(/\r?\n/) let objects = [] let dict = lines[0].split('|').map((key) => key.trim()) for (let line of lines.slice(1)) { line = line.trim() if (!line || line.startsWith('//')) { continue } if (line.includes('|')) { const parts = line.split('|') let obj: any = {} for (let i = 0; i < dict.length; i++) { obj[dict[i]] = parts[i] ? parts[i].trim() : `N/A` } objects.push(obj) } } resolve(objects) }) } static parseGeoJSON(geojsonData: string = null, geojsonUrl: string = null, headers: any = []): Promise<any> { return new Promise(async (resolve, reject) => { if (geojsonUrl != null) { let data = await this.httpFetch(geojsonUrl, headers) if (data.success) { geojsonData = data.data } } let geojson: any = null try { geojson = (typeof geojsonData === 'string') ? JSON.parse(geojsonData) : geojsonData } catch (e) { return reject({success: false, error: `Invalid JSON data`}) } if (!geojson || !geojson.type || geojson.type !== 'FeatureCollection' || !Array.isArray(geojson.features)) { return reject({success: false, error: `Invalid GeoJSON data`}) } let features = geojson.features.map((feature: any) => { let geometry = feature.geometry || {} let properties = feature.properties || {} let parsedFeature: any = { type: geometry.type || 'N/A', coordinates: geometry.coordinates || [], properties: properties } return parsedFeature }) resolve(features) }) } /** * @function createPlacefile * @description Creates a placefile string from provided data and settings. * * @param {number} placefileRefresh - Refresh interval in minutes. * @param {number} placefileThreshold - Threshold value. * @param {string} placefileTitle - Title of the placefile. * @param {string} settings - Additional settings for the placefile. * @param {Array} data - Array of data objects to include in the placefile. * @param {string} type - Type of placefile ('polygon' or 'point'). */ static createPlacefile(placefileRefresh: number = 10, placefileThreshold: number = 999, placefileTitle: string = `Default Placefile Title`, settings: string = ``, data: any[] = [], type: 'polygon' | 'point' = 'polygon' ): Promise<any> { return new Promise((resolve, reject) => { let placefileText = `Refresh: ${placefileRefresh}\nThreshold: ${placefileThreshold}\nTitle: ${placefileTitle}\n${settings}\n` if (type === 'point') { for (let item of data) { let { description = `No description provided`, point = [], icon = '0,0,000,1,19', text = '0, 15, 1', title = '' } = item || {}; if (Array.isArray(point) && point.length == 2 && typeof point[0] == 'number' && typeof point[1] == 'number') { placefileText += [ `\nObject: ${point[1]},${point[0]}`, `Icon: ${icon},"${description.replace(/\n/g, '\\n')}"`, `Text: ${text}, "${title.split('\n')[0]}"`, `End:\n` ].join('\n'); } } } else { for (let item of data) { let { description = `No description provided`, polygon = [], rgb = `255,255,255,255` } = item || {}; rgb = rgb.replace(/,/g, ' '); let hasCoords = Array.isArray(polygon) && polygon.some(ring => Array.isArray(ring) && ring.some(pt => Array.isArray(pt) && pt.length == 2 && typeof pt[0] == 'number' && typeof pt[1] == 'number')); if (!hasCoords) continue; placefileText += `\nColor: ${rgb}\n\nLine: 3,0, ${description}\n`; for (let ring of polygon) { for (let pt of ring) { if (Array.isArray(pt) && pt.length == 2) { placefileText += `${pt[1]},${pt[0]}\n`; } } } placefileText += `End:\n`; } } resolve(placefileText.trim()) }) } } export default AtmosXPlacefileParser;