def measure_graph(request, area_ident, filter='all'): """ visualizes scores or measures in a graph identifier_list: [{'waterbody_slug': ...}, ...] start_end_dates: 2-tuple dates each row is an area """ if filter == 'measure': measures = Measure.objects.filter( Q(pk=area_ident)|Q(parent__id=area_ident)).order_by('title') else: area = get_object_or_404(Area, ident=area_ident) if filter == 'focus': measures = [m for m in _sorted_measures(area) if m.is_indicator == True] else: measures = _sorted_measures(area) start_date = iso8601.parse_date(request.GET.get('dt_start', '2008-1-1T00:00:00')).date() end_date = iso8601.parse_date(request.GET.get('dt_end', '2013-1-1T00:00:00')).date() width = int(request.GET.get('width', 380)) height = int(request.GET.get('height', 170)) legend_location = int(request.GET.get('legend-location', -1)) wide_left_ticks = request.GET.get('wide_left_ticks', 'false') == 'true' format = request.GET.get('format', None) graph = DateGridGraph(width=width, height=height) _image_measures(graph, measures, start_date, end_date, legend_location=legend_location, wide_left_ticks=wide_left_ticks) graph.set_margins() if format == 'ps': return graph.render( response=HttpResponse(content_type='application/postscript'), format='ps') else: return graph.png_response( response=HttpResponse(content_type='image/png'))
def get(self, request, *args, **kwargs): """ Draw the EKR graph """ dt_start, dt_end = self._dt_from_request() graph_items, graph_settings = self._graph_items_from_request() graph = DateGridGraph( width=int(graph_settings['width']), height=int(graph_settings['height'])) # # Legend. Must do this before using graph location calculations # legend_handles = [ # Line2D([], [], color=value_to_html_color(0.8), lw=10), # Line2D([], [], color=value_to_html_color(0.6), lw=10), # Line2D([], [], color=value_to_html_color(0.4), lw=10), # Line2D([], [], color=value_to_html_color(0.2), lw=10), # Line2D([], [], color=value_to_html_color(0.0), lw=10), # ] # legend_labels = [ # 'Zeer goed', 'Goed', 'Matig', 'Ontoereikend', 'Slecht'] # graph.legend(legend_handles, legend_labels, legend_location=6) yticklabels = [] block_width = (date2num(dt_end) - date2num(dt_start)) / 50 # Legend #graph.margin_right_extra += 90 # Room for legend. See also nens_graph. legend_handles = [ Line2D([], [], color=COLOR_1, lw=10), Line2D([], [], color=COLOR_2, lw=10), Line2D([], [], color=COLOR_3, lw=10), Line2D([], [], color=COLOR_4, lw=10), Line2D([], [], color=COLOR_5, lw=10), ] legend_labels = [ 'slecht', 'ontoereikend', 'matig', 'goed', 'zeer goed', ] graph.legend(legend_handles, legend_labels, legend_location=7) for index, graph_item in enumerate(graph_items): if not graph_item.location: graph_item.location = graph_settings['location'] # Find the corresponding Score. score = Score.from_graph_item(graph_item) if score.id is None: graph_item.label = '(%s)' % graph_item.label yticklabels.append(graph_item.label) # We want to draw a shadow past the end of the last # event. That's why we ignore dt_start. try: ts = graph_item.time_series(dt_end=dt_end, with_comments=True) except: logger.exception( 'HorizontalBarView crashed on graph_item.time_series of %s' % graph_item) ts = {} if len(ts) != 1: logger.warn('Warning: drawing %d timeseries on a single bar ' 'HorizontalBarView', len(ts)) # We assume there is only one timeseries. for (loc, par, unit), single_ts in ts.items(): dates, values, comments, flag_dates, flag_values, flag_comments = ( dates_values_comments(single_ts)) if not dates: logger.warning('Tried to draw empty timeseries %s %s', loc, par) continue block_dates = [] block_dates_shadow = [] for date_index in range(len(dates) - 1): dist_to_next = (date2num(dates[date_index + 1]) - date2num(dates[date_index])) this_block_width = min(block_width, dist_to_next) block_dates.append( (date2num(dates[date_index]), this_block_width)) block_dates_shadow.append( (date2num(dates[date_index]), dist_to_next)) block_dates.append( (date2num(dates[-1]), block_width)) # Ignoring tzinfo, otherwise we can't compare. last_date = max(dt_start.replace(tzinfo=None), dates[-1]) block_dates_shadow.append( (date2num(last_date), (date2num(dt_end) - date2num(dt_start)))) a, b, c, d = score.borders block_colors = [comment_to_html_color(comment) for comment in comments] # Block shadow graph.axes.broken_barh( block_dates_shadow, (index - 0.2, 0.4), facecolors=block_colors, edgecolors=block_colors, alpha=0.2) # The 'real' block graph.axes.broken_barh( block_dates, (index - 0.4, 0.8), facecolors=block_colors, edgecolors='grey') # for goal in graph_item.goals.all(): # collected_goal_timestamps.update([goal.timestamp, ]) # For each unique bar goal timestamp, generate a mini # graph. The graphs are ordered by timestamp. goal_timestamps = [ datetime.datetime(2015, 1, 1, 0, 0), datetime.datetime(2027, 1, 1, 0, 0), ] subplot_numbers = [312, 313] for index, goal_timestamp in enumerate(goal_timestamps): axes_goal = graph.figure.add_subplot(subplot_numbers[index]) axes_goal.set_yticks(range(len(yticklabels))) axes_goal.set_yticklabels('') axes_goal.set_xticks([0, ]) axes_goal.set_xticklabels([goal_timestamp.year, ]) for graph_item_index, graph_item in enumerate(graph_items): # TODO: make more efficient; score is retrieved twice # in this function. score = Score.from_graph_item(graph_item) #print 'score: %s' % score #print 'doel scores: %s' % str(score.targets) #a, b, c, d = score.borders goal = score.targets[index] if goal is not None: axes_goal.broken_barh( [(-0.5, 1)], (graph_item_index - 0.4, 0.8), facecolors=comment_to_html_color(goal), edgecolors='grey') # # 0 or 1 items # goals = graph_item.goals.filter(timestamp=goal_timestamp) # for goal in goals: # axes_goal.broken_barh( # [(-0.5, 1)], (graph_item_index - 0.4, 0.8), # facecolors=value_to_html_color(goal.value), # edgecolors='grey') axes_goal.set_xlim((-0.5, 0.5)) axes_goal.set_ylim(-0.5, len(yticklabels) - 0.5) # Coordinates are related to the graph size - not graph 311 bar_width_px = 12 axes_x = float(graph.width - (graph.MARGIN_RIGHT + graph.margin_right_extra) + bar_width_px + 2 * bar_width_px * index ) / graph.width axes_y = float(graph.MARGIN_BOTTOM + graph.margin_bottom_extra) / graph.height axes_width = float(bar_width_px) / graph.width axes_height = float(graph.graph_height()) / graph.height axes_goal.set_position((axes_x, axes_y, axes_width, axes_height)) graph.axes.set_yticks(range(len(yticklabels))) graph.axes.set_yticklabels(yticklabels) graph.axes.set_xlim(date2num((dt_start, dt_end))) graph.axes.set_ylim(-0.5, len(yticklabels) - 0.5) # Set the margins, including legend. graph.set_margins() return graph.png_response( response=HttpResponse(content_type='image/png'))
def get(self, request, *args, **kwargs): """ Input: - dt_start - dt_end - width - height - item={fews_norm_source_slug:.., location_id:.., parameter_id:.., module_id:.., type:.., arguments:..} - type can be 'line', 'stacked-bar', 'vertical-line', 'horizontal-line', 'stacked-line' (see README). - items are processed in order. """ default_colors = ['green', 'blue', 'yellow', 'magenta', ] dt_start, dt_end = self._dt_from_request() # Get all graph items from request. graph_items, graph_settings = self._graph_items_from_request() graph = DateGridGraph( width=int(graph_settings['width']), height=int(graph_settings['height'])) if ('y-range-min' not in graph_settings and 'y-range-max' not in graph_settings): graph.axes.set_ymargin(0.1) bar_width = GraphView.BAR_WIDTHS[PredefinedGraph.PERIOD_REVERSE[ graph_settings['aggregation-period']]] graph.figure.suptitle( graph_settings.get('title', ''), x=0.5, y=1, horizontalalignment='center', verticalalignment='top') # Disappears somewhere, but not so important now... # graph.axes.set_xlabel(graph_settings.get('x-label', '')) # Can be overruled later by unit_as_y_label graph.axes.set_ylabel(graph_settings.get('y-label', '')) color_index = 0 unit_from_graph = '(no unit)' ts_stacked_sum = { 'bar-positive': 0, 'bar-negative': 0, 'line-cum': 0, 'line': 0} matplotlib_legend_index = 0 # To be filled using matplotlib_legend_index reversed_legend_items = [] # Let's draw these graph items. for graph_item in graph_items: graph_type = graph_item.graph_type try: if graph_type == GraphItem.GRAPH_TYPE_LINE: ts = cached_time_series_from_graph_item( graph_item, dt_start, dt_end) for (loc, par, unit), single_ts in ts.items(): if unit: unit_from_graph = unit matplotlib_legend_index += graph.line_from_single_ts( single_ts, graph_item, default_color=default_colors[color_index], flags=graph_settings['flags']) color_index = (color_index + 1) % len(default_colors) elif (graph_type == GraphItem.GRAPH_TYPE_STACKED_LINE_CUMULATIVE or graph_type == GraphItem.GRAPH_TYPE_STACKED_LINE): ts = cached_time_series_from_graph_item( graph_item, dt_start, dt_end) for (loc, par, unit), single_ts in ts.items(): if unit: unit_from_graph = unit if (graph_type == GraphItem.GRAPH_TYPE_STACKED_LINE): current_ts = single_ts stacked_key = 'line' else: current_ts = time_series_cumulative( single_ts, graph_settings['reset-period']) stacked_key = 'line-cum' # cum_ts is bottom_ts + cumulative single_ts # Make last observation carried forward current_ts.is_locf = True ts_stacked_sum[stacked_key] = ( current_ts + ts_stacked_sum[stacked_key]) added = graph.line_from_single_ts( ts_stacked_sum[stacked_key], graph_item, default_color=default_colors[color_index], flags=False) # Mark these items to be reversed in the legend. reversed_legend_items.extend( range(matplotlib_legend_index, matplotlib_legend_index + added)) matplotlib_legend_index += added color_index = (color_index + 1) % len(default_colors) elif graph_type == GraphItem.GRAPH_TYPE_HORIZONTAL_LINE: matplotlib_legend_index += graph.horizontal_line( graph_item.value, graph_item.layout_dict(), default_color=default_colors[color_index]) color_index = (color_index + 1) % len(default_colors) elif graph_type == GraphItem.GRAPH_TYPE_VERTICAL_LINE: matplotlib_legend_index += graph.vertical_line( graph_item.value, graph_item.layout_dict(), default_color=default_colors[color_index]) color_index = (color_index + 1) % len(default_colors) elif graph_type == GraphItem.GRAPH_TYPE_STACKED_BAR: ts = cached_time_series_aggregated( graph_item, dt_start, dt_end, aggregation=graph_settings['aggregation'], aggregation_period=graph_settings[ 'aggregation-period']) if graph_item.value == 'negative': stacked_key = 'bar-negative' polarity = -1 else: stacked_key = 'bar-positive' polarity = 1 for (loc, par, option), single_ts in ts.items(): if option == TIME_SERIES_ALL: # Make sure all timestamps are present. ts_stacked_sum[stacked_key] += single_ts * 0 abs_single_ts = polarity * abs(single_ts) added = graph.bar_from_single_ts( abs_single_ts, graph_item, bar_width, default_color=default_colors[color_index], bottom_ts=ts_stacked_sum[stacked_key]) # Mark these items to be reversed in the legend. if polarity == 1: reversed_legend_items.extend( range(matplotlib_legend_index, matplotlib_legend_index + added)) matplotlib_legend_index += added ts_stacked_sum[stacked_key] += abs_single_ts color_index = (color_index + 1) % len( default_colors) elif graph_type == GraphItem.GRAPH_TYPE_STACKED_BAR_SIGN: ts = cached_time_series_aggregated( graph_item, dt_start, dt_end, aggregation=graph_settings['aggregation'], aggregation_period=graph_settings[ 'aggregation-period']) if graph_item.value == 'negative': stacked_key_positive = 'bar-negative' stacked_key_negative = 'bar-positive' polarity = { TIME_SERIES_POSITIVE: -1, TIME_SERIES_NEGATIVE: 1} else: stacked_key_positive = 'bar-positive' stacked_key_negative = 'bar-negative' polarity = { TIME_SERIES_POSITIVE: 1, TIME_SERIES_NEGATIVE: -1} for (loc, par, option), single_ts in ts.items(): # Make sure all timestamps are present. if option == TIME_SERIES_POSITIVE: stacked_key = stacked_key_positive elif option == TIME_SERIES_NEGATIVE: stacked_key = stacked_key_negative if (option == TIME_SERIES_POSITIVE or option == TIME_SERIES_NEGATIVE): ts_stacked_sum[stacked_key] += single_ts * 0 abs_single_ts = polarity[option] * abs(single_ts) matplotlib_legend_index += graph.bar_from_single_ts( abs_single_ts, graph_item, bar_width, default_color=default_colors[color_index], bottom_ts=ts_stacked_sum[stacked_key]) ts_stacked_sum[stacked_key] += abs_single_ts color_index = (color_index + 1) % len( default_colors) except: # You never know if there is a bug somewhere logger.exception("Unknown error while drawing graph item.") if graph_settings['legend-location'] >= 0: graph.legend( legend_location=graph_settings['legend-location'], reversed_legend_items=reversed_legend_items, remove_duplicates=True) if graph_settings.get('unit_as_y_label', False): graph.axes.set_ylabel(unit_from_graph) graph.axes.set_xlim(date2num((dt_start, dt_end))) # Set ylim y_min = graph_settings.get('y-range-min', graph.axes.get_ylim()[0]) y_max = graph_settings.get('y-range-max', graph.axes.get_ylim()[1]) graph.axes.set_ylim(y_min, y_max) # Now line? if graph_settings.get('now-line', False): today = datetime.datetime.now() graph.axes.axvline(today, color='orange', lw=2, ls='--') # Set the margins, including legend. graph.set_margins() response_format = graph_settings['format'] if response_format == 'csv': response = HttpResponse(mimetype='text/csv') response['Content-Disposition'] = ( 'attachment; filename="%s.csv"' % graph_settings.get('title', 'export')) graph.timeseries_csv(response) return response elif response_format == 'bmp': return graph.render( response=HttpResponse(content_type='image/bmp'), format='bmp') elif response_format == 'emf': return graph.render( response=HttpResponse(content_type='image/x-emf'), format='emf') elif response_format == 'eps': return graph.render( response=HttpResponse(content_type='image/x-eps'), format='eps') # elif response_format == 'jpg': # return graph.render( # response=HttpResponse(content_type='image/jpg'), # format='jpg') elif response_format == 'pdf': return graph.render( response=HttpResponse(content_type='application/pdf'), format='pdf') # elif response_format == 'png2': # return graph.render( # response=HttpResponse(content_type='image/png'), # format='png2') elif response_format == 'ps': return graph.render( response=HttpResponse(content_type='application/postscript'), format='ps') elif response_format == 'raw': return graph.render( response=HttpResponse(content_type='test/plain'), format='raw') elif response_format == 'rgb': return graph.render( response=HttpResponse(content_type='image/x-rgb'), format='rgb') elif response_format == 'rgba': return graph.render( response=HttpResponse(content_type='image/x-rgba'), format='rgba') elif response_format == 'svg': return graph.render( response=HttpResponse(content_type='image/svg+xml'), format='svg') elif response_format == 'svgz': return graph.render( response=HttpResponse(content_type='image/svg+xml'), format='svgz') elif response_format == 'html': # Display html table return render_to_response( 'lizard_graph/table.html', {'data': graph.timeseries_as_list()}) elif response_format == 'xls': xls = data_as_xls(data=graph.timeseries_as_list()) response = HttpResponse(xls.read(), mimetype='application/xls') response['Content-Disposition'] = ('attachment; ' 'filename="export.xls"') return response elif response_format == 'png_attach': response = HttpResponse(mimetype='image/png') response['Content-Disposition'] = ( 'attachment; filename="%s.png"' % graph_settings.get('title', 'export')) graph.render(response=response) return response else: return graph.render( response=HttpResponse(content_type='image/png'))