Map marker in perspective

Hi,

I am new to ignition, i want to use the Map component to place some moving vehicles, i have a lot of vehicles to show and it is time consuming to go and create the map marker for each car, the data source is from API, I did create a UDT for the vehicle and took instance from it for each car, I did some scripting to get the data from the API and put it in each car instance, now i want to put the markers on the map, i want to link the long, lat, and name in the tooltip to show it as a text above the marker

can you please share some detailed steps on how to do so as i dont have a lot of experience with ignition so far,

thanks

image

Welcome to the forum.

Please post a sample of your data for multiple cars so we know how to transform it. Post formatted data - not a picture of data so we don't have to type it all out again. See Wiki - how to post code on this forum.

here

{
  "name": "vehicles",
  "tagType": "Folder",
  "tags": [
    {
      "name": "9155 SJR",
      "typeId": "vehicle_1_template",
      "tagType": "UdtInstance",
      "tags": [
        {
          "value": 25.622837,
          "name": "Latitude",
          "tagType": "AtomicTag"
        },
        {
          "value": 49.548363,
          "name": "Longitude",
          "tagType": "AtomicTag"
        },
        {
          "value": "0",
          "name": "Speed",
          "tagType": "AtomicTag"
        },
        {
          "value": "2024-05-12T11:47:09",
          "name": "GPS_date",
          "tagType": "AtomicTag"
        },
        {
          "value": "95545.1",
          "name": "Mileage",
          "tagType": "AtomicTag"
        }
      ]
    },
    {
      "name": "5958 XJR",
      "typeId": "vehicle_1_template",
      "tagType": "UdtInstance",
      "tags": [
        {
          "value": "26",
          "name": "Speed",
          "tagType": "AtomicTag"
        },
        {
          "value": 26.230768,
          "name": "Latitude",
          "tagType": "AtomicTag"
        },
        {
          "value": 49.977375,
          "name": "Longitude",
          "tagType": "AtomicTag"
        },
        {
          "value": "2024-05-12T11:53:26",
          "name": "GPS_date",
          "tagType": "AtomicTag"
        },
        {
          "value": "103574.89",
          "name": "Mileage",
          "tagType": "AtomicTag"
        }
      ]
    },
    {
      "name": "5956 XJR",
      "typeId": "vehicle_1_template",
      "tagType": "UdtInstance",
      "tags": [
        {
          "value": "28",
          "name": "Speed",
          "tagType": "AtomicTag"
        },
        {
          "value": 25.938225,
          "name": "Latitude",
          "tagType": "AtomicTag"
        },
        {
          "value": "2024-05-12T11:53:23",
          "name": "GPS_date",
          "tagType": "AtomicTag"
        },
        {
          "value": 49.938877,
          "name": "Longitude",
          "tagType": "AtomicTag"
        },
        {
          "value": "130669.87",
          "name": "Mileage",
          "tagType": "AtomicTag"
        }
      ]
    }
  ]
}

I think, a dataset type tag to render the data seems like a straightforward and logical approach. While there are multiple ways to implement this, here's a script that you can use
Paste following binding on marker prop
image

marker binding
{
  "type": "expr",
  "config": {
    "expression": "now(60000)"
  },
  "transforms": [
    {
      "code": "\timport copy\n\t# Initialize default Marker JSON\n\tMarker_Dict \u003d {\n\t\t\"name\": \"\",\n\t\t\"properties\": {},\n\t\t\"enabled\": True,\n\t\t\"lat\": None,\n\t\t\"lng\": None,\n\t\t\"opacity\": 1,\n\t\t\"icon\": {\n\t\t\"path\": \"material/location_on\",\n\t\t\"color\": \"#4190F7\",\n\t\t\"rotate\": 0,\n\t\t\"size\": {\n\t\t  \"width\": 36,\n\t\t  \"height\": 36\n\t\t},\n\t\t\"style\": {\n\t\t  \"classes\": \"\"\n\t\t}\n\t\t},\n\t\t\"event\": {\n\t\t\"stopPropagation\": False\n\t\t},\n\t\t\"tooltip\": {\n\t\t\"content\": {\n\t\t  \"text\": \"\",\n\t\t  \"view\": {\n\t\t    \"path\": \"\",\n\t\t    \"params\": {}\n\t\t  }\n\t\t},\n\t\t\"direction\": \"auto\",\n\t\t\"permanent\": False,\n\t\t\"sticky\": False,\n\t\t\"opacity\": 1\n\t\t},\n\t\t\"popup\": {\n\t\t\"enabled\": False,\n\t\t\"content\": {\n\t\t  \"text\": \"\",\n\t\t  \"view\": {\n\t\t    \"path\": \"\",\n\t\t    \"params\": {}\n\t\t  }\n\t\t},\n\t\t\"width\": {\n\t\t  \"max\": 300,\n\t\t  \"min\": 50\n\t\t},\n\t\t\"height\": {\n\t\t  \"max\": None\n\t\t},\n\t\t\"pan\": {\n\t\t  \"auto\": True\n\t\t},\n\t\t\"closeButton\": True,\n\t\t\"autoClose\": True,\n\t\t\"closeOnEscapeKey\": True,\n\t\t\"closeOnClick\": None\n\t\t}\n\t}\n\t\n\t# Read all the tags\n\tresults \u003d system.tag.browse(path \u003d \u0027[default]vehicles\u0027)\n\ttagPaths \u003d []\n\tfor result in results.getResults():\n\t\ttagPaths.append(str(result[\"fullPath\"]))\n\tvalues \u003d system.tag.readBlocking(tagPaths)\n\tvehicles \u003d []\n\tfor i in range(len(values)):\n\t\tvehicle_name \u003d tagPaths[i].split(\"/\")[1]\n\t\tvehicle_details \u003d values[i].value\n\t\tvehicles.append({vehicle_name:vehicle_details})\n\t# Add the custom marker instances\n\tMarker_Instances \u003d []\n\tfor vehicle in vehicles:\n\t\tname \u003d vehicle.keys()[0]\n\t\tMarker \u003d copy.deepcopy(Marker_Dict)\n\t\tMarker[\"lat\"] \u003d vehicle[name][\"Latitude\"]\n\t\tMarker[\"lng\"] \u003d vehicle[name][\"Longitude\"]\n\t\tMarker[\"tooltip\"][\"content\"][\"text\"] \u003d name\n\t\tMarker_Instances.append(Marker)\n\treturn Marker_Instances",
      "type": "script"
    }
  ]
}
1 Like

can you clarify more on this part , and how to do it in your opinion,

You are using a UDT to encapsulate all the data from a vehicle. Suggestion I have is to consolidate all the vehicles data within a dataset type tag, like the example below:

Then you can use the tag as follow.

I think it will be straightforward to build marker instances by iterating a dataset

thanks,

will try it

I've never used the Map component but was interested in your question. Try this:

Map markers 1

Figure 1.

  1. Create two custom properties on the Map component:
  • vehicleStatus and create a binding on this to your GPS data tags that you posted above.
  • sampleMarker. Create the property and paste in a sample marker or the one below.
sampleMarker
{
  "name": "marker0",
  "properties": {},
  "enabled": true,
  "lat": 25.938225,
  "lng": 49.938877,
  "opacity": 1,
  "icon": {
    "path": "material/location_on",
    "color": "#4190F7",
    "rotate": 0,
    "size": {
      "width": 36,
      "height": 36
    },
    "style": {
      "classes": ""
    }
  },
  "event": {
    "stopPropagation": false
  },
  "tooltip": {
    "content": {
      "text": "Vehicle ID",
      "view": {
        "path": "",
        "params": {}
      }
    },
    "direction": "auto",
    "permanent": false,
    "sticky": false,
    "opacity": 1
  },
  "popup": {
    "enabled": false,
    "content": {
      "text": "",
      "view": {
        "path": "",
        "params": {}
      }
    },
    "width": {
      "max": 300,
      "min": 50
    },
    "height": {
      "max": null
    },
    "pan": {
      "auto": true
    },
    "closeButton": true,
    "autoClose": true,
    "closeOnEscapeKey": true,
    "closeOnClick": null
  }
}
  1. Create a property binding on ui.marker as shown below.
    Map markers 2

Figure 2.

  1. Paste in the following code:
ui.marker binding script transform

	def recursiveCopy(original):
		# With credit to https://forum.inductiveautomation.com/u/lrose
		# https://forum.inductiveautomation.com/t/copy-dictionary-and-modify-it-without-affecting-the-original/62297/9
	    from copy import deepcopy
	    from com.inductiveautomation.ignition.common.script.abc import AbstractMutableJythonMap,AbstractMutableJythonSequence
	
	    if isinstance(original,AbstractMutableJythonMap):
	        return {key:recursiveCopy(value) for key,value in original.iteritems()}
	
	    if isinstance(original,AbstractMutableJythonSequence):
	        return [recursiveCopy(item) for item in original]
	
	    return deepcopy(original)
	
	markers = []
	for car in value['tags']:
		protoMarker = recursiveCopy(self.custom.sampleMarker)
		name = car['name']
		for dic in car['tags']:
			if dic['name'] == 'Latitude':
				lat = dic['value']
			if dic['name'] == 'Longitude':
				lng = dic['value']
		protoMarker['lat'] = lat
		protoMarker['lng'] = lng
		protoMarker['tooltip']['content']['text'] = name
		markers.append(protoMarker)
	
	return markers

How it works:

  • The initial binding brings in this.custom.vehicleStatus. That gets passed to the script transform as value.
  • We read in self.custom.sampleMarker as a template for constructing the list of markers. Due to the way Jython works we need to make a deepcopy of it (otherwise all the markers will be set to the last value). See the article referenced in the script to understand more.
  • Loop through your GPS data and find the name, lat and lng of each vehicle. Note that dictionaries are unordered so we need a loop. Since your data will be short a simple loop is adequate and readable.
  • Update the copy of the template and add it to the list of markers.

I suggest you to store vehicle information on a database then retrieve locations of the vehicle from the database via binding in to UI> Maker.
You can find the latest updated location of the vehicle via the database.

Thanks, I following instructions, I don´t understand where to past this after creating custom sampleMarker property, the only way I can do it is adding each value or object one by one

Map sample marker

Figure 3.

1 Like

Thanks

So to use this method with a simplified json as:

[
    {"name": "05-RIO-MENDA", "latitude": 42.472783, "longitude": -2.40938},
    {"name": "05-NAV-CORDO", "latitude": 42.789632, "longitude": -1.639813},
    {"name": "05-CAL-FORJA", "latitude": 41.624986, "longitude": -4.735528},
    {"name": "05-CAL-RINCO", "latitude": 40.96796, "longitude": -5.661133},

]

I would need to do a custom sampleMaker,

image

then bind it on my layers.ui.marker

where should I put the Json ?:

and your code should be a Transform script from the bindin?

def recursiveCopy(original):
		# With credit to https://forum.inductiveautomation.com/u/lrose
		# https://forum.inductiveautomation.com/t/copy-dictionary-and-modify-it-without-affecting-the-original/62297/9
	    from copy import deepcopy
	    from com.inductiveautomation.ignition.common.script.abc import AbstractMutableJythonMap,AbstractMutableJythonSequence
	
	    if isinstance(original,AbstractMutableJythonMap):
	        return {key:recursiveCopy(value) for key,value in original.iteritems()}
	
	    if isinstance(original,AbstractMutableJythonSequence):
	        return [recursiveCopy(item) for item in original]
	
	    return deepcopy(original)
	
	markers = []
	for car in value['tags']:
		protoMarker = recursiveCopy(self.custom.sampleMarker)
		name = car['name']
		for dic in car['tags']:
			if dic['name'] == 'Latitude':
				lat = dic['value']
			if dic['name'] == 'Longitude':
				lng = dic['value']
		protoMarker['lat'] = lat
		protoMarker['lng'] = lng
		protoMarker['tooltip']['content']['text'] = name
		markers.append(protoMarker)
	
	return markers
  1. So to use this method with a simplified json as:

Yes. That looks right.
Put that JSON into custom.vehicleStatus (or whatever you call it).

  1. I would need to do a custom sampleMaker,

Yes. It looks good.

  1. then bind it on my layers.ui.marker ...

No. The script reads it in the line,
protoMarker = recursiveCopy(self.custom.sampleMarker)
There is no binding on custom.sampleMarker.

  1. Where should I put the JSON?

I think I have answered this in 1 above.

  1. ... and your code should be a Transform script from the binding?

See post #8, Figure 2.

1 Like

I don´t know how to put the Json into the vehicleStatus, with Property, Expression...?

Your vehicle data structure is different to the original post. We need to modify:

Map - custom.vehicleStatus
[
  {
    "latitude": 42.472783,
    "longitude": -2.40938,
    "name": "05-RIO-MENDA"
  },
  {
    "latitude": 42.789632,
    "longitude": -1.639813,
    "name": "05-NAV-CORDO"
  },
  {
    "latitude": 41.624986,
    "longitude": -4.735528,
    "name": "05-CAL-FORJA"
  },
  {
    "latitude": 40.96796,
    "longitude": -5.661133,
    "name": "05-CAL-RINCO"
  }
]
Map - custom.sampleMarker
{
  "enabled": true,
  "event": {
    "stopPropagation": false
  },
  "icon": {
    "color": "#4190F7",
    "path": "material/location_on",
    "rotate": 0,
    "size": {
      "height": 36,
      "width": 36
    },
    "style": {
      "classes": ""
    }
  },
  "lat": 25.938225,
  "lng": 49.938877,
  "name": "marker0",
  "opacity": 1,
  "popup": {
    "autoClose": true,
    "closeButton": true,
    "closeOnClick": null,
    "closeOnEscapeKey": true,
    "content": {
      "text": "",
      "view": {
        "params": {},
        "path": ""
      }
    },
    "enabled": false,
    "height": {
      "max": null
    },
    "pan": {
      "auto": true
    },
    "width": {
      "max": 300,
      "min": 50
    }
  },
  "properties": {},
  "tooltip": {
    "content": {
      "text": "5956 XJR",
      "view": {
        "params": {},
        "path": ""
      }
    },
    "direction": "auto",
    "opacity": 1,
    "permanent": false,
    "sticky": false
  }
}
Map - script transform on ui.marker property binding
def transform(self, value, quality, timestamp):

	def recursiveCopy(original):
		# With credit to https://forum.inductiveautomation.com/u/lrose
		# https://forum.inductiveautomation.com/t/copy-dictionary-and-modify-it-without-affecting-the-original/62297/9
	    from copy import deepcopy
	    from com.inductiveautomation.ignition.common.script.abc import AbstractMutableJythonMap,AbstractMutableJythonSequence
	
	    if isinstance(original,AbstractMutableJythonMap):
	        return {key:recursiveCopy(value) for key,value in original.iteritems()}
	
	    if isinstance(original,AbstractMutableJythonSequence):
	        return [recursiveCopy(item) for item in original]
	
	    return deepcopy(original)
	
	markers = []
	for car in value:
		protoMarker = recursiveCopy(self.custom.sampleMarker)
		protoMarker['lat'] = car['latitude']
		protoMarker['lng'] = car['longitude']
		protoMarker['tooltip']['content']['text'] = car['name']
		markers.append(protoMarker)
	return markers

Figure 4.

Figure 5.

1 Like

Thanks for the explanation Transistor, one thing I´m missing is t know where to paste the Json in the custom prop. I guess is a tag but I not sure how to do so.

Which JSON and in which custom prop?
The three code blocks above Figure 4 tell you where to paste them.

this part

It tells you!

Vehicle status