Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updated after changes in main SW #60

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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()