Skip to content

Commit

Permalink
Fix shot end coordinates for Opta deserializer
Browse files Browse the repository at this point in the history
The end coordinates of blocked and saved shots were based on the projection of the shot on the goal mouth instead of the shot's actual end location.

This commit uses the x and y coordinates of the location were the shots was blocked (qualifiers 146 and 147) and inversely projects the goalmouth z  coordinate (qualifier 103) on the location were the shot was blocked.

Fixes #244
  • Loading branch information
probberechts committed Dec 3, 2023
1 parent 096f959 commit 5b35173
Showing 1 changed file with 26 additions and 8 deletions.
34 changes: 26 additions & 8 deletions kloppy/infra/serializers/event/opta/deserializer.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import math
from typing import Tuple, Dict, List, NamedTuple, IO, Optional
import logging
from datetime import datetime
Expand Down Expand Up @@ -342,7 +343,9 @@ def _parse_shot(
result = None

qualifiers = _get_event_qualifiers(raw_qualifiers)
result_coordinates = _get_end_coordinates(raw_qualifiers)
result_coordinates = _get_end_coordinates(
raw_qualifiers, start_coordinates=coordinates
)
if result == ShotResult.OWN_GOAL:
if isinstance(result_coordinates, Point3D):
result_coordinates = Point3D(
Expand Down Expand Up @@ -489,28 +492,43 @@ def _team_from_xml_elm(team_elm, f7_root) -> Team:
return team


def _get_end_coordinates(raw_qualifiers: Dict[int, str]) -> Optional[Point]:
def _get_end_coordinates(
raw_qualifiers: Dict[int, str], start_coordinates: Optional[Point] = None
) -> Optional[Point]:
x, y, z = None, None, None

# pass
if 140 in raw_qualifiers:
if 140 in raw_qualifiers and 141 in raw_qualifiers:
x = float(raw_qualifiers[140])
if 141 in raw_qualifiers:
y = float(raw_qualifiers[141])

# blocked shot
if 146 in raw_qualifiers:
elif 146 in raw_qualifiers and 147 in raw_qualifiers:
x = float(raw_qualifiers[146])
if 147 in raw_qualifiers:
y = float(raw_qualifiers[147])
if 102 in raw_qualifiers and 103 in raw_qualifiers:
# the goal mouth z-coordinate is projected back to the location
# where the shot was blocked
assert start_coordinates is not None
x0, y0 = start_coordinates.x, start_coordinates.y
x_proj = float(100)
y_proj = float(raw_qualifiers[102])
z_proj = float(raw_qualifiers[103])
adj_proj = math.sqrt((x_proj - x0) ** 2 + (y_proj - y0) ** 2)
adj_block = math.sqrt((x - x0) ** 2 + (y - y0) ** 2)
z = z_proj / adj_proj * adj_block

# passed the goal line
if 102 in raw_qualifiers:
elif 102 in raw_qualifiers and 103 in raw_qualifiers:
x = float(100)
y = float(raw_qualifiers[102])
if 103 in raw_qualifiers:
z = float(raw_qualifiers[103])

if x is not None and y is not None and z is not None:
return Point3D(x=x, y=y, z=z)
if x is not None and y is not None:
return Point(x=x, y=y)

return None


Expand Down

0 comments on commit 5b35173

Please sign in to comment.