Skip to content

Commit

Permalink
Merge pull request #60 from ZEISS/20240919-update-osm-map-diagram
Browse files Browse the repository at this point in the history
Updated after changes in main SW
  • Loading branch information
mprinkezs authored Oct 1, 2024
2 parents 0da967b + 0bd044a commit ffb5fe0
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 92 deletions.
11 changes: 3 additions & 8 deletions AppExamples/scripted_diagrams/OSMMapDiagram/doc/Documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,10 @@

## Short description

The script `LocationScalarElement.py` generates a scripted value element, which passes location information to the diagram service `OSMMapDiagram`. The service implemented in `service.py` creates a map from [OpenStreetMap](https://www.openstreetmap.org/) (OSM) data using the Python packages [Cartopy](https://scitools.org.uk/cartopy/docs/latest/index.html) and [Matplotlib](https://matplotlib.org/). The map's center is defined by the location data. The location markers and labels (optional) are added to the map. Some map configurations can be set via Preferences ► App-Settings.
The script `LocationScalarElement.py` generates a scripted value element, which passes location information to the diagram service `OSMMapDiagram`. The service implemented in `service.py` creates a map from [OpenStreetMap](https://www.openstreetmap.org/) (OSM) data using the Python packages [Cartopy](https://scitools.org.uk/cartopy/docs/latest/index.html), [Matplotlib](https://matplotlib.org/) and [NumPy](https://numpy.org/). The map's center is defined by the location data. The location markers and labels (optional) are added to the map. Some map configurations can be set via Preferences ► App-Settings.

This example demonstrates the flexibility of scripted diagrams. It can be used for reporting of measurements acquired 'in the field', e.g. with ZEISS [T-SCAN hawk 2](https://www.handsonmetrology.com/products/t-scan-hawk-2/) or ZEISS [TRITOP](https://www.zeiss.com/metrology/en/systems/optical-3d/3d-photogrammetry/tritop.html).

> [!WARNING]
> Only one service function may be used at a time.
> All diagram services currently not in use must be stopped, otherwise no diagram is created.
Location information can be provided by
* Manual input in the GeoLocation element creation dialog
* Element keywords of any element
Expand All @@ -37,8 +33,7 @@ The location information is passed as parameters to the scripted diagram service
context.data[stage] = {
"ude_diagram_custom": 1, # mandatory, fixed
"ude_diagram_type": "SVGDiagram", # mandatory, fixed
"ude_diagram_alt": <altitude>, # altitude
"ude_diagram_alt_en": <enable_altitude_label>, # True or False
"ude_diagram_alt": <altitude>, # altitude, can be None
"ude_diagram_label": '<label>', # optional, can be empty
'ude_diagram_lat': <latitude>, # latitude in decimal degrees
'ude_diagram_lon': <longitude> # longitude in decimal degrees
Expand All @@ -47,7 +42,7 @@ context.data[stage] = {

## Managing the scripted diagram services

Use Apps->Manage Services... to start `OSMMapDiagram` and stop any diagram service currently not in use. Only one diagram service may be active!
Use Apps->Manage Services... to start `OSMMapDiagram`.

## Diagram settings

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Release Notes OSMMapDiagram

## Installation Requirements

* Software Version
* ZEISS INSPECT 2025

## Released at 2024-09-30 (v1.0.0)

* Initial release
Binary file modified AppExamples/scripted_diagrams/OSMMapDiagram/doc/Releasenotes.pdf
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
Cartopy
Cartopy
numpy < 2.0.0
147 changes: 64 additions & 83 deletions AppExamples/scripted_diagrams/OSMMapDiagram/scripts/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@
# https://zeissiqs.github.io/zeiss-inspect-addon-api/2025/python_examples/
# ---

import io
import gom

from gom import apifunction
import gom.api.settings

import gom.api.extensions.diagrams.matplotlib_tools as mpltools

import matplotlib.pyplot as plt
import numpy as np

Expand All @@ -27,9 +28,6 @@
SVG_PATH = None
#SVG_PATH = 'C:/temp/OSMMapDiagram.svg'

# Set SVG resolution in dpi
SVG_DPI = 'figure'

ALTITUDE = gom.tr("Alt.")

# helper function
Expand All @@ -42,66 +40,54 @@ def zoomlevel_from_deg(delta):
def meters_to_deg(dist):
"""Degrees (latitude) assuming spherical earth model"""
return dist * 360 / (2 * np.pi * 6400000)

@apifunction
def geolocation(*args, **kwargs)->str:
"""Create OSM map diagram with location markers and (optional) labels
Args:
*args (any): (unused)
**kwargs (dict):
{
'<uuid>': {
'ude_diagram_alt': 0.0,
'ude_diagram_alt_en': False,
'ude_diagram_custom': 1,
'ude_diagram_label': '<label>',
'ude_diagram_lat': <latitude>,
'ude_diagram_lon': <longitude>,
...
},
..
}
Returns:
string: SVG image
"""
def geolocation(view, element_data)->str:
gom.log.info('Geolocation Service')
gom.log.info(f'{kwargs=}')
gom.log.info(f'{view=}, {elements=}')
request = cimgt.OSM()

# Find bounding box
lat_min = 90
lat_max = -90
lon_min = 180
lon_max = -180
for element in kwargs.keys():
gom.log.info(f'{kwargs[element]=}')
lat = kwargs[element]['ude_diagram_lat']

gom.log.debug(f'Computing bounding box')
for e in element_data:
element = e['element']
data = e['data']

gom.log.debug(f"{data['ude_diagram_name']}")
lat = data['ude_diagram_lat']
lat_min = min(lat_min, lat)
lat_max = max(lat_max, lat)
gom.log.debug(f'{lat=}')
lon = kwargs[element]['ude_diagram_lon']
gom.log.debug(f'{lon=}')
gom.log.debug(f' {lat=}')
lon = data['ude_diagram_lon']
gom.log.debug(f' {lon=}')
lon_min = min(lon_min, lon)
lon_max = max(lon_max, lon)

gom.log.debug(f'{lat=}, {lon=}')
gom.log.debug(f'lat: {lat_min}..{lat_max} lon: {lon_min}..{lon_max}')

gom.log.debug(f'Bounding box: lat={lat_min}..{lat_max} lon={lon_min}..{lon_max}')
lat_center = (lat_min + lat_max) / 2
lon_center = (lon_min + lon_max) / 2

# Set map range and aspect ratio
range = gom.api.settings.get('range')
map_range = gom.api.settings.get('range')
aspect = gom.api.settings.get('aspect')
delta = meters_to_deg(range)

delta = meters_to_deg(map_range)
zoom = zoomlevel_from_deg(delta)-1 # 0-19
gom.log.debug(f'Zoom Level: {zoom}')

# Bounds: (lon_min, lon_max, lat_min, lat_max):
delta_lat = delta / aspect
extent = [lon_center-delta/np.cos(lat_center*np.pi/180), lon_center+delta/np.cos(lat_center*np.pi/180), lat_center-delta_lat, lat_center+delta_lat]
extent = [lon_center-delta/np.cos(lat_center*np.pi/180),
lon_center+delta/np.cos(lat_center*np.pi/180),
lat_center-delta_lat,
lat_center+delta_lat]

mpltools.setup_plot(plt, view)

# See https://scitools.org.uk/cartopy/docs/latest/reference/generated/cartopy.mpl.geoaxes.GeoAxes.html#cartopy-mpl-geoaxes-geoaxes
ax = plt.axes(projection=request.crs)
Expand All @@ -117,19 +103,22 @@ def geolocation(*args, **kwargs)->str:
marker_size = gom.api.settings.get('marker_size')

# Add location markers and (optional) labels
for element in kwargs.keys():
gom.log.debug(f'{kwargs[element]=}')
lat = kwargs[element]['ude_diagram_lat']
lon = kwargs[element]['ude_diagram_lon']
alt_en = kwargs[element]['ude_diagram_alt_en']
alt = kwargs[element]['ude_diagram_alt']
if alt_en:
alt = alt
else:
alt = None
gom.log.debug(f'{alt=}')
label = kwargs[element]['ude_diagram_label']

gom.log.debug(f'Drawing location markers')
for e in element_data:
element = e['element']
data = e['data']

gom.log.debug(f"{data['ude_diagram_name']}")
label = data['ude_diagram_label']
gom.log.debug(f' {label=}')
lat = data['ude_diagram_lat']
gom.log.debug(f' {lat=}')
lon = data['ude_diagram_lon']
gom.log.debug(f' {lon=}')
alt = data['ude_diagram_alt']
gom.log.debug(f' {alt=}')


# Add label and/or altitude as annotation
annotation = ""
if label and alt:
Expand All @@ -138,45 +127,37 @@ def geolocation(*args, **kwargs)->str:
annotation = label
elif alt:
annotation = f'{ALTITUDE}: {alt}'

# Location label offset
label_xoffset = gom.api.settings.get('label_xoffset')
label_yoffset = gom.api.settings.get('label_yoffset')


if annotation != "":
# see https://matplotlib.org/stable/gallery/text_labels_and_annotations/annotation_demo.html
# see
# https://matplotlib.org/stable/gallery/text_labels_and_annotations/annotation_demo.html
ax.annotate(
annotation,
xy=(lon, lat), transform=ccrs.PlateCarree(),
xytext=(label_xoffset, label_yoffset),
textcoords='offset points',
xytext=(50, 30), textcoords='offset points',
bbox=dict(boxstyle="round", fc="0.8"),
arrowprops=dict(arrowstyle="->", shrinkA=5, shrinkB=5)
)

# Add a marker at the current geolocation
plt.scatter(lon, lat, transform=ccrs.PlateCarree(), marker=marker_style, s=marker_size, c=marker_color)

plt.scatter(lon, lat, transform=ccrs.PlateCarree(),
marker=marker_style, s=marker_size, c=marker_color)

# Just some random points/lines:
#plt.scatter(lon, lat, transform=ccrs.PlateCarree())
#plt.plot([lon, lon+delta/2], [lat, lat-delta/2], transform=ccrs.PlateCarree())

title = gom.api.settings.get('title')
plt.title(title)
copyright_text = "Map data © OpenStreetMap contributors"
copyright_text = "© OpenStreetMap contributors"
ax.text(0, -0.03, copyright_text, transform=ax.transAxes, ha='left', fontsize=8)

svg = mpltools.create_svg (plt, view)

if SVG_PATH:
plt.savefig(SVG_PATH, format='svg', dpi=SVG_DPI)

# Create an empty file-like object
svg_output = io.StringIO()

# Save the plot to the file-like object
plt.savefig(svg_output, format='svg', dpi=SVG_DPI)
with open(SVG_PATH, "w") as f:
f.write(svg)

# Get the SVG string from the file-like object
svg_string = svg_output.getvalue()

# Close the file-like object
svg_output.close()

return svg_string
return svg

gom.run_api()

0 comments on commit ffb5fe0

Please sign in to comment.