nakarte

Source code of https://map.sikmir.ru (fork)
git clone git://git.sikmir.ru/nakarte
Log | Files | Refs | LICENSE

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