Skip to content
This repository has been archived by the owner on Dec 16, 2024. It is now read-only.

Add _repr_svg_() #111

Closed
wants to merge 21 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
89fd53c
add _repr_svg_() to BaseCZMLObject()'s that have Position() attribute
omnidaniel25 May 19, 2023
76f8478
BaseCZMLObject._repr_svg_() creates SVG string instead of BaseCZMLObj…
omnidaniel25 May 20, 2023
62d8d5b
BaseCZMLObject._repr_svg_() calculates SVG frame instead of BaseCZMLO…
omnidaniel25 May 20, 2023
70c13b4
fix test_packet_svg_no_color() - point colour now not inputted
omnidaniel25 May 20, 2023
7f54740
flake8 lint
omnidaniel25 May 20, 2023
bb15d78
Packet._svg() won't create SVG of Position() or PositionList()
omnidaniel25 May 21, 2023
af86427
refactored Packet._svg() bounds calculation
omnidaniel25 May 21, 2023
9b7a1b2
add test_packet_svg_no_elements()
omnidaniel25 May 21, 2023
d42b0f9
isort and black lint
omnidaniel25 May 21, 2023
268921f
add test_packet_svg_with_path() and rename test_packet_svg() -> test_…
omnidaniel25 May 21, 2023
cc7bf40
move bounds calc to Position() and PositionList()
omnidaniel25 May 21, 2023
f2716db
add BOUND_DEFAULT in properties.py
omnidaniel25 May 21, 2023
914596d
remove coordinates existence check in BaseCZMLObject()'s with defined…
omnidaniel25 May 21, 2023
5818660
remove coordinates existence check in BaseCZMLObject()'s with defined…
omnidaniel25 May 21, 2023
958f86e
factor of BaseCZMLObject()'s with _get_xy_coords() is of type float
omnidaniel25 May 21, 2023
431204f
remove default bound value
omnidaniel25 May 21, 2023
dea4596
fix mypy issues
omnidaniel25 May 21, 2023
de91fa4
add test_rectangle_svg()
omnidaniel25 May 22, 2023
f80e505
add test_wall_svg()
omnidaniel25 May 22, 2023
ec25ae2
scale circle radius with size of SVG
omnidaniel25 May 22, 2023
c5968a7
output message and SVG for BaseCZMLObject() with no position property…
omnidaniel25 May 22, 2023
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
154 changes: 95 additions & 59 deletions src/czml3/_version.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

# This file helps to compute a version number in source trees obtained from
# git-archive tarball (such as those provided by githubs download-from-tag
# feature). Distribution tarballs (built by setup.py sdist) and build
Expand Down Expand Up @@ -58,28 +57,32 @@ class NotThisMethod(Exception):

def register_vcs_handler(vcs, method): # decorator
"""Decorator to mark a method as the handler for a particular VCS."""

def decorate(f):
"""Store f in HANDLERS[vcs][method]."""
if vcs not in HANDLERS:
HANDLERS[vcs] = {}
HANDLERS[vcs][method] = f
return f

return decorate


def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False,
env=None):
def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, env=None):
"""Call the given command(s)."""
assert isinstance(commands, list)
p = None
for c in commands:
try:
dispcmd = str([c] + args)
# remember shell=False, so use git.cmd on windows, not just git
p = subprocess.Popen([c] + args, cwd=cwd, env=env,
stdout=subprocess.PIPE,
stderr=(subprocess.PIPE if hide_stderr
else None))
p = subprocess.Popen(
[c] + args,
cwd=cwd,
env=env,
stdout=subprocess.PIPE,
stderr=(subprocess.PIPE if hide_stderr else None),
)
break
except EnvironmentError:
e = sys.exc_info()[1]
Expand Down Expand Up @@ -116,16 +119,22 @@ def versions_from_parentdir(parentdir_prefix, root, verbose):
for i in range(3):
dirname = os.path.basename(root)
if dirname.startswith(parentdir_prefix):
return {"version": dirname[len(parentdir_prefix):],
"full-revisionid": None,
"dirty": False, "error": None, "date": None}
return {
"version": dirname[len(parentdir_prefix) :],
"full-revisionid": None,
"dirty": False,
"error": None,
"date": None,
}
else:
rootdirs.append(root)
root = os.path.dirname(root) # up a level

if verbose:
print("Tried directories %s but none started with prefix %s" %
(str(rootdirs), parentdir_prefix))
print(
"Tried directories %s but none started with prefix %s"
% (str(rootdirs), parentdir_prefix)
)
raise NotThisMethod("rootdir doesn't start with parentdir_prefix")


Expand Down Expand Up @@ -181,7 +190,7 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose):
# starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of
# just "foo-1.0". If we see a "tag: " prefix, prefer those.
TAG = "tag: "
tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)])
tags = set([r[len(TAG) :] for r in refs if r.startswith(TAG)])
if not tags:
# Either we're using git < 1.8.3, or there really are no tags. We use
# a heuristic: assume all version tags have a digit. The old git %d
Expand All @@ -190,27 +199,34 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose):
# between branches and tags. By ignoring refnames without digits, we
# filter out many common branch names like "release" and
# "stabilization", as well as "HEAD" and "master".
tags = set([r for r in refs if re.search(r'\d', r)])
tags = set([r for r in refs if re.search(r"\d", r)])
if verbose:
print("discarding '%s', no digits" % ",".join(refs - tags))
if verbose:
print("likely tags: %s" % ",".join(sorted(tags)))
for ref in sorted(tags):
# sorting will prefer e.g. "2.0" over "2.0rc1"
if ref.startswith(tag_prefix):
r = ref[len(tag_prefix):]
r = ref[len(tag_prefix) :]
if verbose:
print("picking %s" % r)
return {"version": r,
"full-revisionid": keywords["full"].strip(),
"dirty": False, "error": None,
"date": date}
return {
"version": r,
"full-revisionid": keywords["full"].strip(),
"dirty": False,
"error": None,
"date": date,
}
# no suitable tags, so version is "0+unknown", but full hex is still there
if verbose:
print("no suitable tags, using unknown + full revision id")
return {"version": "0+unknown",
"full-revisionid": keywords["full"].strip(),
"dirty": False, "error": "no suitable tags", "date": None}
return {
"version": "0+unknown",
"full-revisionid": keywords["full"].strip(),
"dirty": False,
"error": "no suitable tags",
"date": None,
}


@register_vcs_handler("git", "pieces_from_vcs")
Expand All @@ -225,19 +241,27 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command):
if sys.platform == "win32":
GITS = ["git.cmd", "git.exe"]

out, rc = run_command(GITS, ["rev-parse", "--git-dir"], cwd=root,
hide_stderr=True)
out, rc = run_command(GITS, ["rev-parse", "--git-dir"], cwd=root, hide_stderr=True)
if rc != 0:
if verbose:
print("Directory %s not under git control" % root)
raise NotThisMethod("'git rev-parse --git-dir' returned error")

# if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty]
# if there isn't one, this yields HEX[-dirty] (no NUM)
describe_out, rc = run_command(GITS, ["describe", "--tags", "--dirty",
"--always", "--long",
"--match", "%s*" % tag_prefix],
cwd=root)
describe_out, rc = run_command(
GITS,
[
"describe",
"--tags",
"--dirty",
"--always",
"--long",
"--match",
"%s*" % tag_prefix,
],
cwd=root,
)
# --long was added in git-1.5.5
if describe_out is None:
raise NotThisMethod("'git describe' failed")
Expand All @@ -260,17 +284,16 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command):
dirty = git_describe.endswith("-dirty")
pieces["dirty"] = dirty
if dirty:
git_describe = git_describe[:git_describe.rindex("-dirty")]
git_describe = git_describe[: git_describe.rindex("-dirty")]

# now we have TAG-NUM-gHEX or HEX

if "-" in git_describe:
# TAG-NUM-gHEX
mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe)
mo = re.search(r"^(.+)-(\d+)-g([0-9a-f]+)$", git_describe)
if not mo:
# unparseable. Maybe git-describe is misbehaving?
pieces["error"] = ("unable to parse git-describe output: '%s'"
% describe_out)
pieces["error"] = "unable to parse git-describe output: '%s'" % describe_out
return pieces

# tag
Expand All @@ -279,10 +302,12 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command):
if verbose:
fmt = "tag '%s' doesn't start with prefix '%s'"
print(fmt % (full_tag, tag_prefix))
pieces["error"] = ("tag '%s' doesn't start with prefix '%s'"
% (full_tag, tag_prefix))
pieces["error"] = "tag '%s' doesn't start with prefix '%s'" % (
full_tag,
tag_prefix,
)
return pieces
pieces["closest-tag"] = full_tag[len(tag_prefix):]
pieces["closest-tag"] = full_tag[len(tag_prefix) :]

# distance: number of commits since tag
pieces["distance"] = int(mo.group(2))
Expand All @@ -293,13 +318,13 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command):
else:
# HEX: no tags
pieces["closest-tag"] = None
count_out, rc = run_command(GITS, ["rev-list", "HEAD", "--count"],
cwd=root)
count_out, rc = run_command(GITS, ["rev-list", "HEAD", "--count"], cwd=root)
pieces["distance"] = int(count_out) # total number of commits

# commit date: see ISO-8601 comment in git_versions_from_keywords()
date = run_command(GITS, ["show", "-s", "--format=%ci", "HEAD"],
cwd=root)[0].strip()
date = run_command(GITS, ["show", "-s", "--format=%ci", "HEAD"], cwd=root)[
0
].strip()
pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1)

return pieces
Expand Down Expand Up @@ -330,8 +355,7 @@ def render_pep440(pieces):
rendered += ".dirty"
else:
# exception #1
rendered = "0+untagged.%d.g%s" % (pieces["distance"],
pieces["short"])
rendered = "0+untagged.%d.g%s" % (pieces["distance"], pieces["short"])
if pieces["dirty"]:
rendered += ".dirty"
return rendered
Expand Down Expand Up @@ -445,11 +469,13 @@ def render_git_describe_long(pieces):
def render(pieces, style):
"""Render the given version pieces into the requested style."""
if pieces["error"]:
return {"version": "unknown",
"full-revisionid": pieces.get("long"),
"dirty": None,
"error": pieces["error"],
"date": None}
return {
"version": "unknown",
"full-revisionid": pieces.get("long"),
"dirty": None,
"error": pieces["error"],
"date": None,
}

if not style or style == "default":
style = "pep440" # the default
Expand All @@ -469,9 +495,13 @@ def render(pieces, style):
else:
raise ValueError("unknown style '%s'" % style)

return {"version": rendered, "full-revisionid": pieces["long"],
"dirty": pieces["dirty"], "error": None,
"date": pieces.get("date")}
return {
"version": rendered,
"full-revisionid": pieces["long"],
"dirty": pieces["dirty"],
"error": None,
"date": pieces.get("date"),
}


def get_versions():
Expand All @@ -485,8 +515,7 @@ def get_versions():
verbose = cfg.verbose

try:
return git_versions_from_keywords(get_keywords(), cfg.tag_prefix,
verbose)
return git_versions_from_keywords(get_keywords(), cfg.tag_prefix, verbose)
except NotThisMethod:
pass

Expand All @@ -495,13 +524,16 @@ def get_versions():
# versionfile_source is the relative path from the top of the source
# tree (where the .git directory might live) to this file. Invert
# this to find the root from __file__.
for i in cfg.versionfile_source.split('/'):
for i in cfg.versionfile_source.split("/"):
root = os.path.dirname(root)
except NameError:
return {"version": "0+unknown", "full-revisionid": None,
"dirty": None,
"error": "unable to find root of source tree",
"date": None}
return {
"version": "0+unknown",
"full-revisionid": None,
"dirty": None,
"error": "unable to find root of source tree",
"date": None,
}

try:
pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose)
Expand All @@ -515,6 +547,10 @@ def get_versions():
except NotThisMethod:
pass

return {"version": "0+unknown", "full-revisionid": None,
"dirty": None,
"error": "unable to compute version", "date": None}
return {
"version": "0+unknown",
"full-revisionid": None,
"dirty": None,
"error": "unable to compute version",
"date": None,
}
70 changes: 70 additions & 0 deletions src/czml3/base.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import datetime as dt
import json
import re
import warnings
from enum import Enum
from json import JSONEncoder
Expand Down Expand Up @@ -53,3 +54,72 @@ def to_json(self):
obj_dict[property_name] = getattr(self, property_name)

return obj_dict

def _svg(self):
raise NotImplementedError

def _repr_svg_(self, min_dim_size: float = 100.0):
try:
svg_elements, x_min, x_max, y_min, y_max = self._svg()

# adjust SVG frame
if None in (x_min, x_max, y_min, y_max): # frame undefined
raise ValueError("No coordinates found.")
elif x_min == x_max and y_min == y_max:
x_min *= 0.99
y_min *= 0.99
x_max *= 1.01
y_max *= 1.01
else:
expand = 0.04
widest_part = max([x_max - x_min, y_max - y_min])
expand_amount = widest_part * expand
x_min -= expand_amount
y_min -= expand_amount
x_max += expand_amount
y_max += expand_amount

# create SVG
dx = x_max - x_min
dy = y_max - y_min
width = min([max([min_dim_size, dx]), 300.0])
height = min([max([min_dim_size, dy]), 300.0])
circle_radius = 0.02 * (
max([dx - min_dim_size, dy - min_dim_size, min_dim_size])
)
str_svg_elements = re.sub(
"CIRCLE_RADIUS",
f"{circle_radius}",
svg_elements,
) # scale point radius
svg_start = f'<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet" width="{width}" height="{height}" viewBox="{x_min} {y_min} {dx} {dy}"><g transform="matrix(1,0,0,-1,0,{y_min + y_max})">'
svg_end = "</g></svg>"
return "".join((svg_start, str_svg_elements, svg_end))
except NotImplementedError:
print(
f"{self.__class__.__name__} class does not support the positions property."
)
return """<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="50%" viewBox="0 0 1000 1000">
<path d="M512 85.333333C277.333333 85.333333 85.333333 277.333333 85.333333 512s192 426.666667 426.666667 426.666667 426.666667-192 426.666667-426.666667S746.666667 85.333333 512 85.333333z" fill="#7CB342" /><path d="M960 512c0 249.6-202.666667 448-448 448S64 761.6 64 512 262.4 64 512 64s448 198.4 448 448z m-452.266667 206.933333c0-8.533333-4.266667-12.8-12.8-17.066666-27.733333-8.533333-53.333333-8.533333-76.8-32-4.266667-8.533333-4.266667-17.066667-8.533333-27.733334-8.533333-8.533333-32-12.8-44.8-17.066666h-89.6c-12.8-4.266667-23.466667-23.466667-32-36.266667 0-4.266667 0-12.8-8.533333-12.8-8.533333-4.266667-17.066667 4.266667-27.733334 0-4.266667-4.266667-4.266667-8.533333-4.266666-12.8 0-12.8 8.533333-27.733333 17.066666-36.266667 12.8-8.533333 27.733333 4.266667 40.533334 4.266667 4.266667 0 4.266667 0 8.533333 4.266667 12.8 4.266667 17.066667 21.333333 17.066667 36.266666v8.533334c0 4.266667 4.266667 4.266667 8.533333 4.266666 4.266667-23.466667 4.266667-44.8 8.533333-68.266666 0-27.733333 27.733333-53.333333 49.066667-61.866667 8.533333-4.266667 12.8 4.266667 23.466667 0 27.733333-8.533333 93.866667-36.266667 81.066666-72.533333-8.533333-32-36.266667-61.866667-72.533333-57.6-8.533333 4.266667-12.8 8.533333-21.333333 12.8-12.8 8.533333-40.533333 36.266667-53.333334 36.266666-23.466667-4.266667-23.466667-36.266667-17.066666-49.066666 4.266667-17.066667 44.8-76.8 72.533333-66.133334l17.066667 17.066667c8.533333 4.266667 23.466667 4.266667 36.266666 4.266667 4.266667 0 8.533333 0 12.8-4.266667 4.266667-4.266667 4.266667-4.266667 4.266667-8.533333 0-12.8-12.8-27.733333-21.333333-36.266667-8.533333-8.533333-23.466667-17.066667-36.266667-23.466667-44.8-12.8-117.333333 4.266667-151.466667 36.266667s-61.866667 85.333333-81.066666 130.133333c-8.533333 27.733333-17.066667 61.866667-21.333334 93.866667-4.266667 21.333333-8.533333 40.533333 4.266667 61.866667 12.8 27.733333 40.533333 53.333333 68.266667 72.533333 17.066667 12.8 53.333333 12.8 72.533333 36.266667 12.8 17.066667 8.533333 40.533333 8.533333 61.866666 0 27.733333 17.066667 49.066667 27.733334 72.533334 4.266667 12.8 8.533333 32 12.8 44.8 0 4.266667 4.266667 32 4.266666 36.266666 27.733333 12.8 49.066667 27.733333 81.066667 36.266667 4.266667 0 21.333333-27.733333 21.333333-32 12.8-12.8 23.466667-32 36.266667-40.533333 8.533333-4.266667 17.066667-8.533333 27.733333-17.066667 8.533333-8.533333 12.8-27.733333 17.066667-40.533333 2.133333-10.666667 6.4-27.733333 2.133333-40.533334z m8.533334-413.866666c4.266667 0 8.533333-4.266667 17.066666-8.533334 12.8-8.533333 27.733333-23.466667 40.533334-32 12.8-8.533333 27.733333-23.466667 36.266666-32 12.8-8.533333 23.466667-27.733333 27.733334-40.533333 4.266667-8.533333 17.066667-27.733333 12.8-40.533333-4.266667-8.533333-27.733333-12.8-36.266667-17.066667-36.266667-8.533333-66.133333-12.8-102.4-12.8-12.8 0-32 4.266667-36.266667 17.066667-4.266667 23.466667 12.8 17.066667 32 23.466666 0 0 4.266667 36.266667 4.266667 40.533334 4.266667 21.333333-8.533333 36.266667-8.533333 57.6 0 12.8 0 36.266667 8.533333 44.8h4.266667zM891.733333 618.666667c4.266667-8.533333 4.266667-23.466667 8.533334-32 4.266667-21.333333 4.266667-44.8 4.266666-66.133334 0-44.8-4.266667-89.6-17.066666-130.133333-8.533333-12.8-12.8-27.733333-17.066667-40.533333-8.533333-23.466667-21.333333-44.8-40.533333-61.866667-17.066667-23.466667-40.533333-85.333333-81.066667-66.133333-12.8 4.266667-21.333333 21.333333-32 32-8.533333 12.8-17.066667 27.733333-27.733333 40.533333-4.266667 4.266667-8.533333 12.8-4.266667 17.066667 0 4.266667 4.266667 4.266667 8.533333 4.266666 8.533333 4.266667 12.8 4.266667 21.333334 8.533334 4.266667 0 8.533333 4.266667 4.266666 8.533333 0 0 0 4.266667-4.266666 4.266667-21.333333 23.466667-44.8 40.533333-66.133334 61.866666-4.266667 4.266667-8.533333 12.8-8.533333 17.066667 0 4.266667 4.266667 4.266667 4.266667 8.533333s-4.266667 4.266667-8.533334 8.533334c-8.533333 4.266667-17.066667 8.533333-23.466666 12.8-4.266667 8.533333 0 23.466667-4.266667 32-4.266667 23.466667-17.066667 40.533333-27.733333 61.866666-8.533333 12.8-12.8 27.733333-21.333334 40.533334 0 17.066667-4.266667 32 4.266667 44.8 21.333333 32 61.866667 12.8 93.866667 27.733333 8.533333 4.266667 17.066667 4.266667 23.466666 12.8 12.8 12.8 12.8 36.266667 17.066667 49.066667 4.266667 17.066667 8.533333 36.266667 17.066667 53.333333 4.266667 21.333333 12.8 44.8 17.066666 61.866667 40.533333-32 76.8-66.133333 102.4-110.933334 32-27.733333 44.8-64 57.6-100.266666z" fill="#0277BD" />
<text x="200" y="-790" fill="red" transform="rotate(90 0 0)" style="font-family:ariel;font-size:300">czml3</text>
<defs>
<path id="curve1" d="M 10 100 C 200 30 300 250 350 50"
stroke="black" fill="none" stroke-width="5" />
<path id="curve2" d="M 100 300 C 300 -50 600 20 800 125"
stroke="black" fill="none" stroke-width="5" transform="translate(0,20)"/>
</defs>
<!-- <text id="T" style="font-family:ariel;font-size:16">
<textPath xlink:href="#curve1" startOffset ="10" fill="red">
<animate attributeName="startOffset" dur="7s" from="0" to="320"
repeatCount="1" />
Property has no position!
</textPath>
</text> -->
<text id="T" fill="black" style="font-family:ariel;font-size:90">
<textPath xlink:href="#curve2">
No position found.
</textPath>
</text>
</svg>"""
Loading