kml.js (5771B)
1 import {decode as utf8_decode} from 'utf8'; 2 import {xmlGetNodeText} from './xmlUtils'; 3 import stripBom from '~/lib/stripBom'; 4 import JSUnzip from '~/vendored/github.com/augustl/js-unzip/js-unzip'; 5 import jsInflate from './jsInflate'; 6 7 function parseKml(txt, name) { 8 var error; 9 function getLinestringPoints(coordinates_element) { 10 // convert multiline text value of tag to single line 11 var coordinates_string = xmlGetNodeText(coordinates_element); 12 var points_strings = coordinates_string.split(/\s+/u); 13 var points = []; 14 for (var i = 0; i < points_strings.length; i++) { 15 if (points_strings[i].length) { 16 var point = points_strings[i].split(','); 17 var lat = parseFloat(point[1]); 18 var lng = parseFloat(point[0]); 19 if (isNaN(lat) || isNaN(lng)) { 20 error = 'CORRUPT'; 21 break; 22 } 23 points.push({lat: lat, lng: lng}); 24 } 25 } 26 return points; 27 } 28 29 function getLinestrings(xml) { 30 var segments_elements = xml.getElementsByTagName('LineString'); 31 var segments = []; 32 for (var i = 0; i < segments_elements.length; i++) { 33 var coordinates_element = segments_elements[i].getElementsByTagName('coordinates'); 34 if (coordinates_element.length) { 35 var segment_points = getLinestringPoints(coordinates_element[0]); 36 if (segment_points.length) { 37 segments.push(segment_points); 38 } 39 } 40 } 41 return segments; 42 } 43 44 function getPoints(dom) { 45 var points = [], 46 placemarks, i, coord, name, lat, lng, pointObjs; 47 placemarks = dom.getElementsByTagName('Placemark'); 48 for (i = 0; i < placemarks.length; i++) { 49 pointObjs = placemarks[i].getElementsByTagName('Point'); 50 if (pointObjs.length === 0) { 51 continue; 52 } else if (pointObjs.length > 1) { 53 error = 'CORRUPT'; 54 break; 55 } 56 coord = pointObjs[0].getElementsByTagName('coordinates'); 57 if (coord.length !== 1) { 58 error = 'CORRUPT'; 59 break; 60 } 61 coord = xmlGetNodeText(coord[0]); 62 coord = coord.split(','); 63 lat = parseFloat(coord[1]); 64 lng = parseFloat(coord[0]); 65 if (isNaN(lat) || isNaN(lng)) { 66 error = 'CORRUPT'; 67 break; 68 } 69 name = ''; 70 const nameTags = placemarks[i].getElementsByTagName('name'); 71 if (nameTags[0]) { 72 try { 73 name = xmlGetNodeText(nameTags[0]); 74 name = utf8_decode(name); 75 } catch (e) { 76 error = 'CORRUPT'; 77 } 78 } 79 points.push({ 80 name: name, 81 lat: lat, 82 lng: lng 83 } 84 ); 85 } 86 return points; 87 } 88 89 function getTracks(dom) { 90 const segments = []; 91 for (const track of dom.getElementsByTagName('gx_Track')) { 92 const points = []; 93 for (const coord of track.getElementsByTagName('gx_coord')) { 94 const [lng, lat] = (xmlGetNodeText(coord) ?? '').split(/\s+/u).map(Number); 95 if (isNaN(lat) || isNaN(lng)) { 96 error = 'CORRUPT'; 97 break; 98 } 99 points.push({lat, lng}); 100 } 101 if (points.length > 0) { 102 segments.push(points); 103 } 104 } 105 return segments; 106 } 107 108 txt = stripBom(txt); 109 txt = txt.replace(/<([^ >]+):([^ >]+)/ug, '<$1_$2'); 110 let dom; 111 try { 112 dom = (new DOMParser()).parseFromString(txt, "text/xml"); 113 } catch (e) { 114 return null; 115 } 116 if (dom.documentElement.nodeName === 'parsererror') { 117 return null; 118 } 119 if (dom.getElementsByTagName('kml').length === 0) { 120 return null; 121 } 122 123 return [{name: name, tracks: [...getLinestrings(dom), ...getTracks(dom)], points: getPoints(dom), error: error}]; 124 } 125 126 function parseKmz(txt, name) { 127 var uncompressed; 128 let unzipper; 129 try { 130 unzipper = new JSUnzip(txt); 131 } catch (e) { 132 return null; 133 } 134 var tracks = [], 135 points = [], 136 geodata, 137 error; 138 var hasDocKml = false; 139 if (!unzipper.isZipFile()) { 140 return null; 141 } 142 try { 143 unzipper.readEntries(); 144 } catch (e) { 145 return null; 146 } 147 var i, entry; 148 for (i = 0; i < unzipper.entries.length; i++) { 149 entry = unzipper.entries[i]; 150 if (entry.fileName === 'doc.kml') { 151 hasDocKml = true; 152 break; 153 } 154 } 155 if (!hasDocKml) { 156 return null; 157 } 158 159 for (i = 0; i < unzipper.entries.length; i++) { 160 entry = unzipper.entries[i]; 161 if (entry.fileName.match(/\.kml$/iu)) { 162 if (entry.compressionMethod === 0) { 163 uncompressed = entry.data; 164 } else if (entry.compressionMethod === 8) { 165 uncompressed = jsInflate(entry.data, entry.uncompressedSize); 166 } else { 167 return null; 168 } 169 geodata = parseKml(uncompressed, 'dummmy'); 170 if (geodata) { 171 error = error || geodata[0].error; 172 tracks.push(...geodata[0].tracks); 173 points.push(...geodata[0].points); 174 } 175 } 176 } 177 178 geodata = [{name: name, error: error, tracks: tracks, points: points}]; 179 return geodata; 180 } 181 182 export {parseKml, parseKmz};