# HG changeset patch # User Paul Boddie # Date 1302559288 -7200 # Node ID 7f4475b7302583481ca7fa0eb7c13037575cb97d # Parent 299fbb742884ef9dc6af149f71990ea73dfe6ed5 Added support for aggregating events by location, obtaining map references, calculating map positions, and marking locations on the map complete with pop-up elements containing the appropriate events. diff -r 299fbb742884 -r 7f4475b73025 EventAggregatorSupport.py --- a/EventAggregatorSupport.py Sun Apr 10 23:52:35 2011 +0200 +++ b/EventAggregatorSupport.py Tue Apr 12 00:01:28 2011 +0200 @@ -105,6 +105,12 @@ else: return cmp(x, y) +def sign(x): + if x < 0: + return -1 + else: + return 1 + # Utility classes and associated functions. class Form: @@ -1782,6 +1788,61 @@ today = datetime.date.today() return today.year +# Location-related functions. + +class Reference: + + "A map reference." + + def __init__(self, degrees, minutes=0, seconds=0): + self.degrees = degrees + self.minutes = minutes + self.seconds = seconds + + def __repr__(self): + return "Reference(%d, %d, %d)" % (self.degrees, self.minutes, self.seconds) + + def __add__(self, other): + if not isinstance(other, Reference): + return NotImplemented + else: + s = sign(self.degrees) + o = sign(other.degrees) + carry, seconds = adc(s * self.seconds, o * other.seconds) + carry, minutes = adc(s * self.minutes, o * other.minutes + carry) + return Reference(self.degrees + other.degrees + carry, minutes, seconds) + + def __sub__(self, other): + if not isinstance(other, Reference): + return NotImplemented + else: + return self.__add__(Reference(-other.degrees, other.minutes, other.seconds)) + + def to_degrees(self): + return sign(self.degrees) * (abs(self.degrees) + self.minutes / 60.0 + self.seconds / 3600.0) + + def to_pixels(self, scale): + return self.to_degrees() * scale + +def adc(x, y): + result = x + y + return divmod(result, 60) + +def getPositionForReference(latitude, longitude, map_y, map_x, map_x_scale, map_y_scale): + return (longitude - map_x).to_pixels(map_x_scale), (latitude - map_y).to_pixels(map_y_scale) + +def getPositionForCentrePoint(position, map_x_scale, map_y_scale): + x, y = position + return x - map_x_scale / 2.0, y - map_y_scale / 2.0 + +def getMapReference(value): + + "Return a map reference by parsing the given 'value'." + + return Reference(*map(float, value.split(":"))) + +# vim: tabstop=4 expandtab shiftwidth=4 + # User interface functions. def getParameter(request, name, default=None): diff -r 299fbb742884 -r 7f4475b73025 css/event-aggregator.css --- a/css/event-aggregator.css Sun Apr 10 23:52:35 2011 +0200 +++ b/css/event-aggregator.css Tue Apr 12 00:01:28 2011 +0200 @@ -426,5 +426,20 @@ background-color: #fff; } +.event-map-description p { + font-weight: bold; + font-size: larger; +} + +ul.event-map-description-events { + list-style-type: none; + padding: 0.25em; +} + +ul.event-map-description-events li { + text-align: left; + padding: 0.25em 0 0.25em 0; +} + /* vim: tabstop=4 expandtab shiftwidth=4 */ diff -r 299fbb742884 -r 7f4475b73025 macros/EventAggregator.py --- a/macros/EventAggregator.py Sun Apr 10 23:52:35 2011 +0200 +++ b/macros/EventAggregator.py Tue Apr 12 00:01:28 2011 +0200 @@ -1342,7 +1342,16 @@ if maps is not None and map_name is not None: try: - map_bottom_left, map_top_right, map_width, map_height, map_image = maps[map_name].split() + map_details = maps[map_name].split() + + map_bottom_left_latitude, map_bottom_left_longitude, map_top_right_latitude, map_top_right_longitude = \ + map(getMapReference, map_details[:4]) + map_width, map_height = map(int, map_details[4:6]) + map_image = map_details[6] + + map_x_scale = map_width / (map_top_right_longitude - map_bottom_left_longitude).to_degrees() + map_y_scale = map_height / (map_top_right_latitude - map_bottom_left_latitude).to_degrees() + except (KeyError, ValueError): pass @@ -1368,10 +1377,22 @@ _("You do not have read access to the locations page:"), locations_page)) - # Attempt to show the image. + # Attempt to show the map. else: + # Get events by position. + + events_by_location = {} + + for event in all_shown_events: + event_details = event.getDetails() + + location = event_details.get("location") + if not events_by_location.has_key(location): + events_by_location[location] = [] + events_by_location[location].append(event) + # Get the map image URL. map_image_url = AttachFile.getAttachUrl(maps_page, map_image, request) @@ -1385,6 +1406,65 @@ escattr(map_width), escattr(map_height), map_image_url) )) + # Show the events in the map. + + for location, events in events_by_location.items(): + + # Look up the position of a location using the locations page. + + latitude, longitude = None, None + + if location is not None: + try: + latitude, longitude = map(getMapReference, locations[location].split()) + except (KeyError, ValueError): + pass + + # Skip unpositioned locations or events. + + if latitude is None or longitude is None: + continue + + # Get the position and dimensions of the map marker. + # NOTE: Use one degree as the marker size. + + marker_x, marker_y = getPositionForCentrePoint( + getPositionForReference(latitude, longitude, map_bottom_left_latitude, map_bottom_left_longitude, + map_x_scale, map_y_scale), + map_x_scale, map_y_scale) + + # Put a marker on the map. + + output.append(fmt.div(on=1, css_class="event-map-label", + style="left:%dpx; bottom:%dpx; min-width:%dpx; min-height:%dpx") % ( + marker_x, marker_y, map_x_scale, map_y_scale)) + output.append(fmt.div(on=1, css_class="event-map-details")) + output.append(fmt.div(on=1, css_class="event-map-shadow")) + output.append(fmt.div(on=1, css_class="event-map-description")) + + output.append(fmt.paragraph(on=1)) + output.append(fmt.text(location)) + output.append(fmt.paragraph(on=0)) + + output.append(fmt.bullet_list(on=1, attr={"class" : "event-map-description-events"})) + + for event in events: + event_page = event.getPage() + event_summary = event.getSummary(parent_name) + + # Link to the page using the summary. + + output.append(fmt.listitem(on=1)) + output.append(event_page.linkToPage(request, event_summary)) + output.append(fmt.listitem(on=0)) + + output.append(fmt.bullet_list(on=0)) + + output.append(fmt.div(on=0)) + output.append(fmt.div(on=0)) + output.append(fmt.div(on=0)) + output.append(fmt.div(on=0)) + # End of map view output. output.append(fmt.div(on=0))