Commit 4ccc448f authored by zksx's avatar zksx
Browse files

leaflet.sld.js is now working with geostac

parent 89ec03a6
Loading
Loading
Loading
Loading
+21 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<StyledLayerDescriptor version="1.1.0" xsi:schemaLocation="http://www.opengis.net/sld StyledLayerDescriptor.xsd" xmlns="http://www.opengis.net/sld" xmlns:ogc="http://www.opengis.net/ogc" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:se="http://www.opengis.net/se">
  <NamedLayer>
    <se:Name>GeoStyler Demo</se:Name>
    <UserStyle>
      <se:Name>GeoStyler Demo</se:Name>
      <se:Title>GeoStyler Demo</se:Title>
      <se:FeatureTypeStyle>
        <se:Rule>
          <se:Name>Rule 1</se:Name>
          <se:PolygonSymbolizer>
            <se:Stroke>
              <se:SvgParameter name="stroke">#ff0000</se:SvgParameter>
              <se:SvgParameter name="stroke-width">5</se:SvgParameter>
            </se:Stroke>
          </se:PolygonSymbolizer>
        </se:Rule>
      </se:FeatureTypeStyle>
    </UserStyle>
  </NamedLayer>
</StyledLayerDescriptor>
 No newline at end of file
+1 −0
Original line number Diff line number Diff line
@@ -3,5 +3,6 @@ import ReactDOM from "react-dom";
import App from "./components/container/App.jsx";
import "./styles.css";
import "leaflet";
import "./leaflet.sld.js";

ReactDOM.render(<App />, document.getElementById("map"));
+27 −2
Original line number Diff line number Diff line
@@ -307,7 +307,32 @@ export default L.Map.AstroMap = L.Map.extend({
          // Wrap features
          let wrappedFeatures = this.cloneWestEast(featureCollections[i].features);

          // Add features to a geoJSON layer, as _geoLayers[i]
          // sldText should be composed of text from geologic_features.sld
          // instead of hardcoding text like this
          let sldText = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\
          <StyledLayerDescriptor version="1.1.0" xsi:schemaLocation="http://www.opengis.net/sld StyledLayerDescriptor.xsd" xmlns="http://www.opengis.net/sld" xmlns:ogc="http://www.opengis.net/ogc" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:se="http://www.opengis.net/se">\
            <NamedLayer>\
              <se:Name>GeoStyler Demo</se:Name>\
              <UserStyle>\
                <se:Name>GeoStyler Demo</se:Name>\
                <se:Title>GeoStyler Demo</se:Title>\
                <se:FeatureTypeStyle>\
                  <se:Rule>\
                    <se:Name>Rule 1</se:Name>\
                    <se:PolygonSymbolizer>\
                      <se:Stroke>\
                        <se:SvgParameter name="stroke">#ff0000</se:SvgParameter>\
                        <se:SvgParameter name="stroke-width">5</se:SvgParameter>\
                      </se:Stroke>\
                    </se:PolygonSymbolizer>\
                  </se:Rule>\
                </se:FeatureTypeStyle>\
              </UserStyle>\
            </NamedLayer>\
          </StyledLayerDescriptor>';

          const SLDStyler = new L.SLDStyler(sldText);

          this._geoLayers[i] = L.geoJSON(wrappedFeatures, {
            id: featureCollections[i].id,
            style: myStyle
@@ -315,10 +340,10 @@ export default L.Map.AstroMap = L.Map.extend({

          this._geoLayers[i].on({click: this.handleClick});  // Add click listener
          

          // Add layers to map if they should be visible
          if(featureCollections[i].id === visibleCollectionId) { 
            this._geoLayers[i].addTo(this);
            SLDStyler.symbolize_with_icons(featureCollections[i], this);
          }

          this._footprintCollection[title] = this._geoLayers[i];

src/leaflet.sld.js

0 → 100644
+509 −0
Original line number Diff line number Diff line
(function() {

class Point{
   constructor(x_coord, y_coord){
         this.x = x_coord;
         this.y = y_coord;
   }
}

class LineSegment{
   constructor(point_1, point_2){
         this.start_point = point_1;
         this.end_point = point_2;
         this.segment_distance = this.get_segment_distance(this.start_point, this.end_point);
   }
   get_line_mid_point(){
         let mid_point = new Point;

         mid_point.x = (this.start_point.x + this.end_point.x) / 2;
         mid_point.y = (this.start_point.y + this.end_point.y) / 2;

         return mid_point
   }
   get_segment_distance(){
         return Math.sqrt( Math.pow(this.end_point.x - this.start_point.x, 2) + Math.pow(this.end_point.y - this.start_point.y, 2) );
   }
}

// default Path style applied if nothing matches
var defaultStyle = {
   stroke: true,
   color: "#03f",
   weight: 5,
   opacity: 1,
   fillOpacity: 1,
   fillColor: '#03f',
   strokeOpacity: 1,
   strokeWidth: 1,
   strokeDashstyle: "solid",
   size: 3,
   dashArray: null,
   lineJoin: null,
   lineCap: null,
};

// attributes converted to numeric values
var numericAttributes = ['weight', 'opacity', 'fillOpacity', 'strokeOpacity', 'size', 'rotation'];

// mapping between SLD attribute names and SVG names
var attributeNameMapping = {
   'stroke': 'color',
   'stroke-width': 'weight',
   'stroke-opacity': 'opacity',
   'fill-opacity': 'fillOpacity',
   'fill': 'fillColor',
   'stroke-opacity': 'strokeOpacity',
   'stroke-dasharray': 'dashArray',
   //strokeDashstyle,
   'stroke-linejoin': 'lineJoin',
   'stroke-linecap': 'lineCap',
   // a bit special for pointsymboler, marker
   'size': 'size',
   'rotation': 'rotation',
   'wellknownname': 'wellKnownName'
};

// mapping SLD operators to shortforms
var comparisionOperatorMapping = {
   'ogc:PropertyIsEqualTo': '==',
   'ogc:PropertyIsNotEqualTo': '!=',
   'ogc:PropertyIsLessThan': '<',
   'ogc:PropertyIsGreaterThan': '>',
   'ogc:PropertyIsLessThanOrEqualTo': '<=',
   'ogc:PropertyIsGreaterThanOrEqualTo': '>=',
   //'ogc:PropertyIsNull': 'isNull',
   //'ogc:PropertyIsBetween'
   // ogc:PropertyIsLike
};

// namespaces for Tag lookup in XML
var namespaceMapping = {
   se: 'http://www.opengis.net/se',
   ogc: 'http://www.opengis.net/ogc',
   sld: 'http://www.opengis.net/sld'
};

function getTagNameArray(element, tagName, childrens) {
   var tagParts = tagName.split(':');
   var ns = null;
   var tags;

   if (tagParts.length == 2) {
      ns = tagParts[0];
      tagName = tagParts[1];
   }

   if(typeof(childrens) !== 'undefined' && childrens) {
      tags = [].filter.call(
         element.children,
         function(element) {
            return element.tagName === ns + ':' + tagName
         }
      )
   } else
      tags = [].slice.call(element.getElementsByTagNameNS(
         namespaceMapping[ns],
         tagName
      ));

   if(!tags.length && ns === 'se')
      return getTagNameArray(element, 'sld:' + tagName, childrens);
   else
      return tags;
};

/**
 * SLD Styler. Reads SLD 1.1.0.
 *
 */
L.SLDStyler = L.Class.extend({
   options: {
      unmatchedStyle: {
         color: false,
         fill: false
      }
   },
   initialize: function(sldStringOrXml, options) {
      L.Util.setOptions(this, options);
      if (sldStringOrXml !== undefined) {
         this.featureTypeStylesNameMap = {};
         this.featureTypeStyles = this.parse(sldStringOrXml);
      }
   },
   

   // translates PolygonSymbolizer attributes into Path attributes
   parseSymbolizer: function(symbolizer) {
      // SvgParameter names below se:Fill and se:Stroke
      // are unique so don't bother parsing them seperatly.
      var parameters = getTagNameArray(symbolizer, 'se:SvgParameter');
      var cssParams = L.extend({}, defaultStyle);

      if(!parameters.length) {
         parameters = getTagNameArray(symbolizer, 'se:CssParameter');
      }

      parameters.forEach(function(param) {
         var key = param.getAttribute('name');
         var mappedKey = attributeNameMapping[key];
         if (false == (mappedKey in cssParams)) {
            console.error("Ignorning unknown SvgParameter name", key);
         } else {
            var value = param.textContent;
            if (numericAttributes.indexOf(mappedKey) > -1) {
               value = parseFloat(value, 10);
            } else if (mappedKey === 'dashArray') {
               value = value.split(' ').join(', ');
            }
            cssParams[mappedKey] = value;
         }
      });
      // invididual tags fro pointsymbolizer which is a bit special
      ['Size', 'WellKnownName', 'Rotation'].forEach(tagName => {
         const tags = getTagNameArray(symbolizer, 'se:' + tagName);
         if (tags.length) {
            const cssName = tagName.toLowerCase();
            const mappedKey = attributeNameMapping[cssName];
            let value = tags[0].textContent;
            if (numericAttributes.indexOf(cssName) > -1) {
               value = parseFloat(value, 10);
            }
            cssParams[mappedKey] = value;
         }
      })
      const sizeTags = getTagNameArray(symbolizer, 'se:Size');
      if (sizeTags.length) {
      }
      const wellKnownNameTags = getTagNameArray(symbolizer, 'se:WellKnownName');
      if (wellKnownNameTags.length) {
         cssParams[attributeNameMapping['wellknown']]
      }
      return cssParams;
   },
   parseFilter: function(filter) {
      if(filter) {
         var hasAnd = getTagNameArray(filter, 'ogc:And').length;
         var hasOr = getTagNameArray(filter, 'ogc:Or').length;
         var filterJson = {
            operator: hasAnd == true ? 'and' : hasOr ?  'or' : null,
            comparisions: []
         };
         Object.keys(comparisionOperatorMapping).forEach(function(key) {
            var comparisionElements = getTagNameArray(filter, key);
            var comparisionOperator = comparisionOperatorMapping[key];
            comparisionElements.forEach(function(comparisionElement) {
               var property = getTagNameArray(comparisionElement, 'ogc:PropertyName')[0].textContent;
               var literal = getTagNameArray(comparisionElement, 'ogc:Literal')[0].textContent;
               filterJson.comparisions.push({
                  operator: comparisionOperator,
                  property: property,
                  literal: literal
               })
            })
         });
         return filterJson;
      }
   },
   parseRule: function(rule) {
      var filter = getTagNameArray(rule, 'ogc:Filter')[0];
      var polygonSymbolizer = getTagNameArray(rule, 'se:PolygonSymbolizer')[0];
      var lineSymbolizer = getTagNameArray(rule, 'se:LineSymbolizer')[0];
      var pointSymbolizer = getTagNameArray(rule, 'se:PointSymbolizer')[0];
      return {
         filter: this.parseFilter(filter),
         polygonSymbolizer: polygonSymbolizer ? this.parseSymbolizer(polygonSymbolizer) : null,
         lineSymbolizer: lineSymbolizer ? this.parseSymbolizer(lineSymbolizer) : null,
         pointSymbolizer: pointSymbolizer ? this.parseSymbolizer(pointSymbolizer) : null
      }
   },
   parse: function(sldStringOrXml) {
      var xmlDoc = sldStringOrXml;
      var self = this;

      if (typeof(sldStringOrXml) === 'string') {
         var parser = new DOMParser();
         xmlDoc = parser.parseFromString(sldStringOrXml, "text/xml");
      }
      var featureTypeStyles = getTagNameArray(xmlDoc, 'se:FeatureTypeStyle');
      window.xmlDoc = xmlDoc;

      featureTypeStyles.forEach(function(element, idx) {
         var layerName = getTagNameArray(
            element.parentElement.parentElement,
            'se:Name',
            true
         ).map(function (node) {
            return node.innerHTML;
         });

         if(layerName.length) {
            self.featureTypeStylesNameMap[layerName] = idx;
         }
      });

      return featureTypeStyles.map(function(featureTypeStyle) {
         var rules = getTagNameArray(featureTypeStyle, 'se:Rule');
         var name = getTagNameArray(
               featureTypeStyle.parentElement,
               'se:Name',
               true
            ).map(function(node) {
               return node.innerHTML;
            });

         if(!name.length)
            name = null;
         else
            name = name[0];

         return {
            'name' : name,
            'rules': rules.map(function(rule) {
               return this.parseRule(rule);
            }, this)
         };
      }, this);
   },
   isFilterMatch: function(filter, properties) {
      if (filter) {
         var operator = filter.operator == null || filter.operator == 'and' ? 'every' : 'some';
         return filter.comparisions[operator](function(comp) {
            if (comp.operator == '==') {
               return properties[comp.property] == comp.literal;
            } else if (comp.operator == '!=') {
               return properties[comp.property] != comp.literal;
            } else if (comp.operator == '<') {
               return properties[comp.property] < comp.literal;
            } else if (comp.operator == '>') {
               return properties[comp.property] > comp.literal;
            } else if (comp.operator == '<=') {
               return properties[comp.property] <= comp.literal;
            } else if (comp.operator == '>=') {
               return properties[comp.property] >= comp.literal;
            } else {
               console.error('Unknown comparision operator', comp.operator);
            }
         });
      } else
         return true;
   },
   matchFn: function (featureTypeStyle, feature) {
      var matchingRule = null;

      featureTypeStyle.rules.some(function (rule) {
         if (this.isFilterMatch(rule.filter, feature.properties)) {
            matchingRule = rule;
            return true;
         }
      }, this);

      return matchingRule;
   },
   styleFn: function (indexOrName, feature) {
      var matchingRule = null;

      if (typeof (indexOrName) !== 'undefined') {
         if (
            typeof (indexOrName) === "string" &&
            indexOrName in this.featureTypeStylesNameMap
         ) {
            indexOrName = this.featureTypeStylesNameMap[indexOrName];
         } else {
            console.error("Unknown layer style '" + indexOrName + "'.")
            return {};
         }

         if(indexOrName in this.featureTypeStyles) {
            matchingRule = this.matchFn(
               this.featureTypeStyles[indexOrName],
               feature
            )
         } else {
            console.error("Unkonwn style index " + indexOrName)
            return {}
         }
      } else
         this.featureTypeStyles.some(function (featureTypeStyle) {
            matchingRule = this.matchFn(featureTypeStyle, feature)
         }, this);

      if (matchingRule != null) {
         switch (feature.geometry.type) {
            case 'LineString':
            case 'MultiLineString':
               return matchingRule.lineSymbolizer;
            case 'Polygon':
            case 'MultiPolygon':
               return matchingRule.polygonSymbolizer;
            case 'Point':
               return matchingRule.pointSymbolizer;
         }
         return this.options.unmatchedStyle;
      }

      return {};
   },
   pointToLayerFunction: function(indexOrName, feature, latlng) {
      var styling = this.styleFn(indexOrName, feature);
      return L.circleMarker(latlng, {
         radius: styling.size || 1,
         interactive: false
      });
   },
   getStyleFunction: function (indexOrName) {
      return this.styleFn.bind(this, indexOrName);
   },
   getPointToLayerFunction: function(indexOrName) {
      return this.pointToLayerFunction.bind(this, indexOrName);
   },
   symbolize_with_icons: function(geoJSON, map){
      let feature = null;
      console.log(geoJSON.id);
      console.log(geoJSON);

      for (let i = 0; i < geoJSON.features.length; i++){
          feature = geoJSON.features[i]

          if(feature.geometry != null){
            if (feature.geometry.type == "LineString"){
            this.symbolize_line(feature.geometry, map);
        } else if ((feature.geometry.type == "Polygon")){
            this.symbolize_polygon(feature.geometry, map);
        }}

      }
  },
   symbolize_line: function(geometry, map){
   let total_line_length = 0;
   let point_arr = [];
   let mid_point_distance = 0;
   let line_segment = null;
   let distance_from_endpoint_to_midpoint = 0;
   let g = 0;
   let mid_point = null;

   point_arr = this.create_arr_of_line_segments(geometry);

   total_line_length = this.get_total_line_length(point_arr);
   mid_point_distance = total_line_length / 2;
   line_segment = this.find_line_segment(mid_point_distance, point_arr);
   distance_from_endpoint_to_midpoint = this.get_distance_from_line_endpoint_to_midpoint(line_segment, mid_point_distance, point_arr);
   g = line_segment.get_segment_distance() - distance_from_endpoint_to_midpoint;

   mid_point = this.get_mid_point( line_segment.start_point, line_segment.end_point, 
                                  g, line_segment.get_segment_distance());

   this.add_symbol(mid_point, map);
   },
   symbolize_polygon: function(geometry, map){

      let point_1 = null;
      let point_2 = null;
      let line_segment = null;
      let mid_point = null;

      // for each coordinate 
      for (let i = 0; i < geometry.coordinates[0].length - 1; i++){

         point_1 = new Point(geometry.coordinates[0][i][0], geometry.coordinates[0][i][1])
         point_2 = new Point(geometry.coordinates[0][i + 1][0], geometry.coordinates[0][i + 1][1])
         line_segment = new LineSegment(point_1, point_2);

         mid_point = line_segment.get_line_mid_point();

         this.add_symbol(mid_point, map);
      }

      point_1 = new Point(geometry.coordinates[0][0][0], geometry.coordinates[0][0][1])
      point_2 = new Point(geometry.coordinates[0][geometry.coordinates[0].length - 1][0], geometry.coordinates[0][geometry.coordinates[0].length - 1][1])
      line_segment = new LineSegment(point_1, point_2);

      mid_point = line_segment.get_line_mid_point()

      this.add_symbol(mid_point, map);
   },
   create_arr_of_line_segments: function(geometry){
      let line_segment = null;
      let point_arr = [];
      let amount_of_vectors = geometry.coordinates.length - 1;

      for (let i = 0; i < amount_of_vectors; i++){
         current_point = geometry.coordinates[i];
         next_point = geometry.coordinates[i+1];
         line_segment = this.create_line_segment_object(current_point, next_point );
         point_arr[i] = line_segment;
      }
      return point_arr;
   },
   get_total_line_length: function(point_arr){
      let total_line_distance = 0;

      for (let i = 0; i < point_arr.length; i++){
         total_line_distance += point_arr[i].get_segment_distance();
      }
      return total_line_distance;
   },
   find_line_segment: function( mid_point_distance, point_arr){

      let current_line_distance = 0;

      for (let i = 0; i < point_arr.length; i++){  
         current_line_distance += point_arr[i].get_segment_distance()

         if( mid_point_distance < current_line_distance ){
            return point_arr[i];
         }
      }
   },
   get_distance_from_line_endpoint_to_midpoint: function(line_segment, mid_point_distance, point_arr){
      let current_line_distance = 0;

      for (let i = 0; i < point_arr.length; i++){
         current_line_distance += point_arr[i].get_segment_distance();

         if (line_segment == point_arr[i])
         {
            return current_line_distance - mid_point_distance;
         }
      }
   },
   get_mid_point: function(point_1, point_2, g, line_length){
      let x = 0;
      let y = 0;
      let mid_point = null;

      x = (point_1.x + (g/line_length) * (point_2.x - point_1.x));
      y = (point_1.y + (g/line_length) * (point_2.y - point_1.y));

      mid_point = new Point(x, y);

      return mid_point;
   },
   create_line_segment_object: function(point_a, point_b){
      let current_point = null;
      let next_point = null;
      let line_segment = null;

      current_point = new Point(point_a[0], point_a[1])
      next_point = new Point(point_b[0], point_b[1])
      line_segment = new LineSegment(current_point, next_point)

      return line_segment;
   },
   add_symbol: function(point, map){
      var svgElement = document.createElementNS("http://www.w3.org/2000/svg", "svg");
      svgElement.setAttribute('xmlns', "http://www.w3.org/2000/svg");
      svgElement.innerHTML = ' <g> <path inkscape:connector-curvature="0" id="path3055" d="M 0.43864824,0.43865001 7.3370882,9.83709" style="fill:none;stroke:#000000;stroke-width:0.87730002;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" /> <path inkscape:connector-curvature="0" id="path3057" d="M 7.3370882,0.43865001 0.43864824,9.83709" style="fill:none;stroke:#000000;stroke-width:0.87730002;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-opacity:1" / </g>';
      var svgElementBounds = [ [ point.y - 0.01, point.x + 0.01 ], [ point.y , point.x ] ];
      L.svgOverlay(svgElement, svgElementBounds).addTo(map);
      map.flyTo([point.y - 0.01, point.x + 0.01], 20)
   },
   test: function(){
      console.log("hey");
   }
});

L.SLDStyler.defaultStyle = defaultStyle;

})();
 No newline at end of file