[FEATURE-723] Perspective map GeoJSON doesn't accept style

Hi
The perspective map JeoJSON property doersn’t accept any style and I stuck with default blue color for all type of objects(point, polygon,…)
Is there any workaround the change default theming of it?
image

The style key should work - try just color as the inner key value, per the docs?
https://leafletjs.com/examples/geojson/

Unfortunately it doesn’t work. As the image shown I test it for both Point and Polygon.
Please add a ticket to fix it.
image

Okay. We’ll see what we can do. Feature ticket created.

1 Like

Hi,

Are there news about this ticket ??

Thanks

As long as I know nothing happen yet in 8.1.7.
Also consider this: the new vectorClick event doesn’t work for jeojson so you may need this feature.

Any update on this feature?

any update?

1 Like

Hi Yusef,
Any update on this feature?
We prefer something like this method so we have a style for each element.

L.geoJSON(states, {
    style: function(feature) {
        switch (feature.properties.party) {
            case 'Republican': return {color: "#ff0000"};
            case 'Democrat':   return {color: "#0000ff"};
        }
    }
}).addTo(map);

Also, I find two problems:
As I check, clicking on the GeoJSON vector on the map doesn’t generate any onVectorclick event. It is very handy if we have this option too.

The GeoJSON does not update in the map when is updated new value by script

The JeoJSON is very light and use a lot in ArcGIS API response, so it is much faster to consume it in leaflet.

2 Likes

Hi,

Any updates? :slightly_smiling_face:

Ignition version 8.1.14, still not possible to change the style of e.g. a LineString feature.

Wait a little bit it is coming in 8.1.15

1 Like

It should already be part of 8.1.14. Although, you’ll want the latest nightly that fixed a regression related to missing position properties in vector and marker click events. We also added some other features we hope you’ll find useful.

Summary of changes:

The configuration possibilities of the Map components GeoJSON layers has been expanded to allow style options for GeoJSON feature objects. A feature object has a “type” property with a value of “Feature”. If you wish to familiarize yourself with the GeoJSON schema specifications, you can find them here.

The default schema of Polygon, Polyline, Rectangle, and Circle layers has been changed to align with how GeoJSON features are configured, and to help make configuration more straight forward.

The vector and marker click events now get passed a dictionary parameter called “properties”.

GeoJSON layer

  • A styleOptions property can be added to configure style related options of feature objects

    • Add styleOptions at the layer level for shared style options among features
    • Add styleOptions at the feature level to override any shared style options, and apply feature specific style options
    styleOptions: {
        "stroke": true,
        "color": "",
        "weight": 1,
        "opacity": 1,
        "lineCap": "inherit",
        "lineJoin": "inherit",
        "dashArray": "",
        "dashOffset": "",
        "fill": true,
        "fillColor": "",
        "fillOpacity": 1,
        "fillRule": "inherit",
        "interactive": true
    }
    
  • GeoJSON feature objects that are rendered on the map are subscribed to click events, triggering vector and marker click events

  • The properties member object of a feature object is a supplied parameter to vector and marker clicked event handlers

  • Point feature objects, which Leaflet renders as markers by default, accept special configuration

    • May be rendered as a circle marker or icon marker based upon the value of the feature object’s render property

      marker: {
          render: "circle"
      }
      
    • May be configured to show a basic text tooltip, with various options equivalent to Leaflet’s tooltip options here

      marker: {
          tooltip: {
              content: {
                  text: "Forest Products Laboratory"
              },
              options: {
                  direction: "auto",
                  permanent: false,
                  sticky: true,
                  interactive: false,
                  opacity: 0.9,
                  className: ""
              }
          }
      }
      
    • May be configured to show a basic text popup, with various options equivalent to Leaflet’s popup options here

      marker: {
          popup: {
              content: {
                  text: "1 Gifford Pinchot Drive"
              },
              options: {
                  maxWidth: 300,
                  minWidth: 50,
                  maxHeight: null,
                  autoPan: true,
                  keepInView: false,
                  autoClose: true,
                  closeOnEscapeKey: true,
                  className: ""
              }
          }
      }
      
    • Can be configured even further with marker specific options, mostly equivalent to Leaflet’s marker and circle marker options here

      marker: {
          options: {
              opacity: 1,
              riseOffset: 0,
              riseOnHover: false,
              draggable: false,
              clickable: true,
              radius: 10
          }
      }
      

The Map components GeoJSON layers are not meant to be modified directly via the property editor. It is the expectation that designers will make use of an HTTP binding and a map transform to add these configurations as foreign members (object member properties not part of the GeoJSON schema spec) to their layers and features that we process and pass along to the Leaflet API. Here is an example of a map transform on the returned response of an HTTP binding:

```
def transform(self, value, quality, timestamp):
    from com.inductiveautomation.ignition.common.script.adapters import PyJsonObjectAdapter
    from org.python.core import PyArray
    
    # This function is how the Map component and Leaflet traverses and determines if an GeoJSON object qualifies as a Feature object.  It is generic.  If you know 
    # what the layer looks like, i.e. a FeatureCollection of Point objects, you definitely don't need this.
    def walkFeatures(geojson, operate):
        features = geojson if isinstance(geojson, PyArray) else geojson['features'] if isinstance(geojson, PyJsonObjectAdapter) and isinstance(geojson['features'], PyArray) else None
        if features:
            for idx in range(len(features)):
                feature = features[idx]
                if isinstance(feature, PyJsonObjectAdapter):
                    if feature.has_key('features'):
                        walkFeatures(feature['features'], operate)
                    elif feature.has_key('geometry') or feature.has_key('geometries') or feature.has_key('coordinates'):
                        operate(feature)
                        
        elif isinstance(geojson, PyJsonObjectAdapter):
            if feature.has_key('geometry') or feature.has_key('geometries') or feature.has_key('coordinates'): 
                operate(geojson)
    
    # Operates on a feature, in this example, all of the features of this layer are expected to be Point objects, which we configure as Leaflet markers          
    def operateOnFeature(feature):
        if isinstance(feature, PyJsonObjectAdapter):
            
            marker = {
                'render': 'icon',
                'icon': {
                    'path': 'material/location_on',
                    'size': {
                        'width': 36,
                        'height': 36
                    },
                    'color': '#9bfa03'
                }
            }

            if feature.has_key('properties'):
                properties = feature['properties']
                LOCATION_TYPE = properties.get('LOCATION_TYPE')
                LOCATION_NAME = properties.get('LOCATION_NAME')
                ADDRESS_LINE1 = properties.get('ADDRESS_LINE1')
                if LOCATION_TYPE == 'Headquarters':
                    marker['icon']['color'] = '#ff0000'
                marker['tooltip'] = {
                    'content':  {
                        'text':	LOCATION_NAME
                    }
                }
                marker['popup'] = {
                    'content':  {
                        'text': properties
                    }
                }
            feature['marker'] = marker
    walkFeatures(value, operateOnFeature)
    return value
```

Polygon, Polyline, Rectangle, and Circle layers

  • The default schema of vector Polygon and Polyline objects has been altered to allow specifying a name and properties property which is supplied as a parameter to click event handlers. Previous schema shapes are still accepted. For example,

    Old default schema shape:

    polyline: [ // An array of polyline layers
        { // A single layer of polylines
            polylines: [ // Polylines belonging to this single layer
                [ // A polyline, defined by an array of points
                    { // A single point of this polyline, requires min of two points
                        lat: null,
                        lng: null
                    }
                ]
            ]
        }
    ]
    

    New default schema shape:

    polyline: [ // An array of polyline layers
        { // A single layer of polylines
            polylines: [ // Polylines belonging to this single layer
                {
                    name: "", // The name of this polyline
                    properties: {
                        // Properties of this polyline
                    },
                    points: [ // An array of points
                        { // A single point of this polyline, requires min of two points
                            lat: null,
                            lng: null
                        }
                    ]
                }
            ]
        }
    ]
    
  • name and properties properties where added to circle and rectangle vector schemas

  • The name property which existed at the layer level of vector objects has been hidden from the schema, in exchange for individual object level specifying of name (see above). To prevent regression, the name that was defined on the layer will be used if the name defined on the vector object is empty or undefined.

To create a basic GeoJSON layer, you can use this mapping tool.
For real world example datasets, see data.gov. Beware, these datasets can be quite large.

Other changes

  • We now supply the true lat, lng coordinates of an element when a vector or marker is clicked. The true coordinates are derived directly from click event data, instead of being initialized from configuration. The change is a result of realizing that it is possible to make a marker draggable through configuration, and as such initializing coordinates from configuration would present incorrect data.

  • Configuration of objects is now more flexible. Any additional configuration is merged and forwarded directly to the Leaflet API. This is an advanced feature, so use with care. Its purpose is to prevent having to submit a request for a property listed in the API, on say, a marker object, and block a designer’s work until that feature is finally added. For example: on a marker object you should be able to make a marker draggable by simply adding the draggable property and setting it to true, as outlined in the Leaflet documentation.

  • Changes to the configuration of a GeoJSON layer will result in that layer being redrawn. We detect changes by performing a deep equality comparison on current layers and previous layers. An approach mostly unique to GeoJSON layers only. Leaflet is a low level JS library, and as such requires some arm twisting to work nicely with React.

3 Likes

Hi @ynejati
Do we have support vector click for GEOJSON as well?

Yes, see the write up above.

Are we able to pull in multiple GeoJson sources from multiple URLs or should those be combined somewhere externally and called from one URL?

EDIT: Second question – When the http binding has polling enabled, every time it polls any currently open popups will get closed and the markers flicker. Is there anyway to prevent a complete redraw? I have been using this on a non-Ignition based map as a Leaflet plugin (https://github.com/perliedman/leaflet-realtime) and it only redraws the things that change before refreshes, so you can animate cars driving or bring in real time values that look more like a traditional HMI.

You can configure multiple GeoJSON layers, if that’s what you’re asking? As for the flickering, we are aware. It was necessary to introduce these changes first.

That’s an interesting plugin. Most of them work well when you have complete control of the data being fed to the map API. Still, it’s something to ponder. Thanks for sharing.

1 Like

That is my question. It doesn't seem to work. If I make a new layer next to the existing "geoJSON" layer called "geoJSON2" it does not work and I get invalid key errors:

image

If I delete the existing geoJSON layer and rename geoJSON2 to just geoJSON it begins working.

Looking at the transform code you provided, I don't see anywhere that has the layer name hardcoded to "geoJSON" that I am overlooking.

Is there something else I am misunderstanding in regards to a second layer?

I’m guessing you figured this out already, but your GeoJSON layers go in other.geoJSON. Similar to how all the other layers work. If you hover over the property in the Property Editor you should get the tooltip explaining this. Hope that helps.

Yeah, I have my GeoJSON layers in GeoJson in the screenshot.

Other.geoJSON[geoJsonHTTPBindingDataHere]
Other.geoJSON2[geoJsonHTTPBindingDataHere]

But the GeoJSON2 layer does not work, but the first does.

Should the structure be this instead?

Other.geoJSON.Layer1[geoJsonHTTPBindingDataHere]
Other.geoJSON.Layer2[geoJsonHTTPBindingDataHere]

Hi @ynejati
Is it possible to add the className property in the Geojson styleOptions so we can pass our style class?
Right now I can do it by modifying .leaflet-interactive class but this causes all polylines in the map effected by it what I want to specific selector like this and only target polyline object I want by passing the class name in styleOptions:

.psc-power .leaflet-interactive{
	filter: drop-shadow( 0 0  8px white)
}
styleOptions: {
    "className": "power"
    "stroke": true,
    "color": "",
    "weight": 1,
    "opacity": 1,
    "lineCap": "inherit",
    "lineJoin": "inherit",
    "dashArray": "",
    "dashOffset": "",
    "fill": true,
    "fillColor": "",
    "fillOpacity": 1,
    "fillRule": "inherit",
    "interactive": true
}