def create_polygon(self, poly, rgb_color, **kwargs): p = Polygon(poly, closed=True, fc=rgb2mpl(rgb_color), ec='black', **kwargs) p.set_linewidth(self.linewidth) return p
def __add_xy_values_as_polygon__(self, xy: list, color: str, closed=False, fill=False, alpha=0.2, line_width=2): xy = PlotterInterface.get_xy_from_timestamp_to_date_number(xy) polygon = Polygon(np.array(xy), closed=closed, fill=fill) polygon.set_visible(True) polygon.set_color(color) polygon.set_alpha(alpha) polygon.set_linewidth(line_width) self.axes_for_candlesticks.add_patch(polygon)
def __init__(self, xy, closed=True, plotview=None, **opts): shape = Polygon(xy, closed, **opts) if 'linewidth' not in opts: shape.set_linewidth(1.0) if 'facecolor' not in opts: shape.set_facecolor('r') super(NXpolygon, self).__init__(shape, resize=False, plotview=plotview) self.shape.set_label('Polygon') self.polygon = self.shape
def BoxDraw(box): import matplotlib from matplotlib.patches import Polygon ax = matplotlib.pyplot.subplot(111, aspect='equal') rect = Polygon([[box[0], box[2]], [box[1], box[2]], [box[1], box[3]], [box[0], box[3]]]) rect.set_fill(False) rect.set_linewidth(2) ax.add_patch(rect)
def create_polygon(self, poly, **kwargs): rgb_color = kwargs.pop('fill_color') p = Polygon(poly, closed=True, fc=rgb2mpl(rgb_color), ec='black', **kwargs) p.set_linewidth(self.linewidth) return p
def __plot_single_fibonacci_wave__(self, fib_wave: FibonacciWave, color: str, suffix: str = ''): if self.sys_config.config.fibonacci_detail_print: fib_wave.print(suffix) xy = fib_wave.get_xy_parameter() xy = PlotterInterface.get_xy_from_timestamp_to_date_number(xy) fib_polygon = Polygon(np.array(xy), closed=False, fill=False) fib_polygon.set_visible(True) fib_polygon.set_color(color) fib_polygon.set_linewidth(1) self.axes_for_candlesticks.add_patch(fib_polygon) fib_wave_patch = FibonacciWavePatch(fib_wave, fib_polygon) self.fibonacci_patch_container.add_patch(fib_wave_patch) fib_wave_patch.add_retracement_patch_list_to_axis(self.axes_for_candlesticks)
def __add_to_ranges_polygon_dic__(self, polygon: Polygon, for_main: bool, range: PatternRange): polygon.set_visible(False) polygon.set_color('r' if for_main else 'k') polygon.set_linewidth(1) self.axes_for_candlesticks.add_patch(polygon) for ticks in range.tick_list: if for_main: if ticks.f_var not in self.ranges_polygon_dic_list: self.ranges_polygon_dic_list[ticks.f_var] = [polygon] else: self.ranges_polygon_dic_list[ticks.f_var].append(polygon) else: if ticks.f_var not in self.ranges_opposite_polygon_dic_list: self.ranges_opposite_polygon_dic_list[ticks.f_var] = [polygon] else: self.ranges_opposite_polygon_dic_list[ticks.f_var].append(polygon)
class BrainLabelingGUI(QMainWindow, Ui_BrainLabelingGui): def __init__(self, parent=None, parent_labeling_name=None, stack=None, section=None): """ Initialization of BrainLabelingGUI. """ self.params_dir = '../params' # self.app = QApplication(sys.argv) QMainWindow.__init__(self, parent) self.parent_labeling_name = parent_labeling_name self.dm = DataManager(data_dir=os.environ['LOCAL_DATA_DIR'], repo_dir=os.environ['LOCAL_REPO_DIR'], result_dir=os.environ['LOCAL_RESULT_DIR'], labeling_dir=os.environ['LOCAL_LABELING_DIR']) self.gabor_params_id='blueNisslWide' self.segm_params_id='blueNisslRegular' self.vq_params_id='blueNissl' self.dm.set_gabor_params(gabor_params_id=self.gabor_params_id) self.dm.set_segmentation_params(segm_params_id=self.segm_params_id) self.dm.set_vq_params(vq_params_id=self.vq_params_id) if (stack is None or section is None) and self.parent_labeling_name is not None: stack, section_str, user, timestamp = self.parent_labeling_name[:-4].split('_') section = int(section_str) self.dm.set_image(stack, 'x5', section) self.is_placing_vertices = False self.curr_polygon_vertices = [] self.polygon_list = [] self.polygon_labels = [] self.vertex_list = [] self.highlight_polygon = None self.segm_transparent = None self.under_img = None self.textonmap_vis = None self.dirmap_vis = None self.debug_mode = False self.load_labeling() # self.data_manager.close() self.initialize_brain_labeling_gui() def paramSettings_clicked(self): pass # self.paramsForm = ParamSettingsForm() # self.paramsForm.show() # self.gabor_params_id='blueNisslWide' # self.segm_params_id='blueNisslRegular' # self.vq_params_id='blueNissl' def load_labeling(self): # self.masked_img = self.dm.image_rgb.copy() self.masked_img = gray2rgb(self.dm.image) # self.masked_img[~self.dm.mask, :] = 0 if self.parent_labeling_name is None: print 'No labeling is given. Initialize labeling.' self.parent_labeling = None self.labeling = { 'username' : None, 'parent_labeling_name' : None, 'login_time' : datetime.datetime.now().strftime("%m%d%Y%H%M%S"), 'initial_polygons': None, 'final_polygons': None, # 'init_label_circles' : [], # 'final_label_circles' : None, # 'labelnames' : self.dm.labelnames, 'labelnames' : [], # will be filled by _add_buttons() } # labelnames not including -1 ("no label") # labelnames_fn = os.path.join(self.dm.labelings_dir, 'labelnames.txt') # if os.path.isfile(labelnames_fn) and not IGNORE_EXISTING_LABELNAMES: # with open(labelnames_fn, 'r') as f: # labelnames = f.readlines() # labelnames = json.load(open(labelnames_fn, 'r')) # self.labeling['labelnames'] = # else: # # n_models = 10 # n_models = 0 else: self.parent_labeling = self.dm.load_labeling(self.parent_labeling_name) print 'Load saved labeling' # label_circles = parent_labeling['final_label_circles'] self.labeling = { 'username' : None, 'parent_labeling_name' : self.parent_labeling_name, 'login_time' : datetime.datetime.now().strftime("%m%d%Y%H%M%S"), 'initial_polygons': self.parent_labeling['final_polygons'], 'final_polygons': None, # 'init_label_circles' : label_circles, # 'final_label_circles' : None, 'labelnames' : [], # will be filled by _add_buttons() } # self.n_labels = len(self.labeling['labelnames']) # initialize GUI variables self.paint_label = -1 # color of pen self.pick_mode = False # True while you hold ctrl; used to pick a color from the image self.press = False # related to pan (press and drag) vs. select (click) self.base_scale = 1.2 # multiplication factor for zoom using scroll wheel self.moved = False # indicates whether mouse has moved while left button is pressed def openMenu(self, canvas_pos): # if self.seg_enabled: # self.growRegion_Action.setEnabled(True) # seed_sp = self.segmentation[int(canvas_pos[1]), int(canvas_pos[0])] # else: # self.growRegion_Action.setEnabled(False) self.endDraw_Action.setVisible(self.is_placing_vertices) self.deletePolygon_Action.setVisible(not self.is_placing_vertices) pos = self.cursor().pos() action = self.menu.exec_(pos) # if action == self.growRegion_Action: # self.statusBar().showMessage('Grow region from superpixel %d' % seed_sp ) # self.curr_label = self.sp_labellist[seed_sp] # for sp in self.cluster_sps[seed_sp]: # self.paint_superpixel(sp) # elif action == self.pickColor_Action: # self.pick_color(self.sp_labellist[seed_sp]) # elif action == self.eraseColor_Action: # self.pick_color(-1) # self.paint_superpixel(seed_sp) # elif action == self.eraseAllSpsCurrColor_Action: # self.pick_color(-1) # for sp in self.cluster_sps[seed_sp]: # self.paint_superpixel(sp) # elif action == self.endDraw_Action: if action == self.endDraw_Action: self.is_placing_vertices = False polygon = Polygon(self.curr_polygon_vertices, closed=True, fill=False, edgecolor=self.colors[self.curr_label + 1], linewidth=2) self.axes.add_patch(polygon) self.polygon_list.append(polygon) self.polygon_labels.append(self.curr_label) self.curr_polygon_vertices = [] for v in self.vertex_list: v.remove() self.vertex_list = [] self.statusBar().showMessage('Done labeling region using label %d (%s)' % (self.curr_label, self.labeling['labelnames'][self.curr_label])) elif action == self.endDrawOpen_Action: self.is_placing_vertices = False polygon = Polygon(self.curr_polygon_vertices, closed=False, fill=False, edgecolor=self.colors[self.curr_label + 1], linewidth=2) self.axes.add_patch(polygon) self.polygon_list.append(polygon) self.polygon_labels.append(self.curr_label) self.curr_polygon_vertices = [] for v in self.vertex_list: v.remove() self.vertex_list = [] self.statusBar().showMessage('Done labeling region using label %d (%s)' % (self.curr_label, self.labeling['labelnames'][self.curr_label])) elif action == self.deletePolygon_Action: polygon_index = self.polygon_list.index(self.selected_polygon) self.selected_polygon.remove() del self.polygon_list[polygon_index] del self.polygon_labels[polygon_index] self.remove_highlight_polygon() elif action == self.crossReference_Action: self.parent().refresh_data() self.parent().comboBoxBrowseMode.setCurrentIndex(self.curr_label + 1) self.parent().set_labelnameFilter(self.curr_label) self.parent().switch_to_labeling() def initialize_brain_labeling_gui(self): self.menu = QMenu() # self.growRegion_Action = self.menu.addAction("Label similar neighbors") # self.pickColor_Action = self.menu.addAction("Pick this label") # self.eraseColor_Action = self.menu.addAction("Remove label of this superpixel") # self.eraseAllSpsCurrColor_Action = self.menu.addAction("Clear similar neighbors") self.endDraw_Action = self.menu.addAction("End drawing region") self.endDrawOpen_Action = self.menu.addAction("End drawing open boundary") self.deletePolygon_Action = self.menu.addAction("Delete polygon") self.crossReference_Action = self.menu.addAction("Cross reference") # A set of high-contrast colors proposed by Green-Armytage self.colors = np.loadtxt('100colors.txt', skiprows=1) self.label_cmap = ListedColormap(self.colors, name='label_cmap') self.curr_label = -1 self.setupUi(self) self.fig = self.canvaswidget.fig self.canvas = self.canvaswidget.canvas self.canvas.mpl_connect('scroll_event', self.zoom_fun) self.bpe_id = self.canvas.mpl_connect('button_press_event', self.press_fun) self.bre_id = self.canvas.mpl_connect('button_release_event', self.release_fun) self.canvas.mpl_connect('motion_notify_event', self.motion_fun) # self.canvas.customContextMenuRequested.connect(self.openMenu) # self.display_buttons = [self.img_radioButton, self.imgSeg_radioButton, self.textonmap_radioButton, self.dirmap_radioButton, self.labeling_radioButton] self.display_buttons = [self.img_radioButton, self.textonmap_radioButton, self.dirmap_radioButton, self.labeling_radioButton] self.img_radioButton.setChecked(True) self.seg_enabled = False self.seg_loaded = False self.groups_loaded = False for b in self.display_buttons: b.toggled.connect(self.display_option_changed) self.spOnOffSlider.valueChanged.connect(self.display_option_changed) self.set_debug_mode() ########## Label buttons ############# self.n_labelbuttons = 0 self.labelbuttons = [] self.labeledits = [] if self.parent_labeling is not None: for n in self.parent_labeling['labelnames']: self._add_labelbutton(desc=n) else: for n in self.dm.labelnames: self._add_labelbutton(desc=n) # self.loadButton.clicked.connect(self.load_callback) self.saveButton.clicked.connect(self.save_callback) self.newLabelButton.clicked.connect(self.newlabel_callback) # self.newLabelButton.clicked.connect(self.sigboost_callback) self.quitButton.clicked.connect(self.close) self.buttonParams.clicked.connect(self.paramSettings_clicked) self.buttonDebugMode.clicked.connect(self.debugMode_toggled) # self.brushSizeSlider.valueChanged.connect(self.brushSizeSlider_valueChanged) # self.brushSizeEdit.setText('%d' % self.brushSizeSlider.value()) # help_message = 'Usage: right click to pick a color; left click to assign color to a superpixel; scroll to zoom, drag to move' # self.setWindowTitle('%s' %(help_message)) self.setWindowTitle(self.windowTitle() + ', parent_labeling = %s' %(self.parent_labeling_name)) # self.statusBar().showMessage() self.fig.clear() self.fig.set_facecolor('white') self.axes = self.fig.add_subplot(111) self.axes.axis('off') self.axes.imshow(self.masked_img, cmap=plt.cm.Greys_r,aspect='equal') # labelmap_vis = label2rgb(self.labelmap, image=self.img, colors=self.colors, alpha=0.3, image_alpha=1) # self.axes.imshow(labelmap_vis) # self.circle_list = [plt.Circle((x,y), radius=r, color=self.colors[l+1], alpha=.3) for x,y,r,l in self.labeling['init_label_circles']] # self.labelmap = self.generate_labelmap(self.circle_list) if self.labeling['initial_polygons'] is not None: for l, xys_percent in self.labeling['initial_polygons']: p = Polygon(xys_percent * np.array([self.dm.image_width, self.dm.image_height])[np.newaxis,:], closed=True, fill=False, edgecolor=self.colors[l + 1], linewidth=2) self.polygon_list.append(p) self.polygon_labels.append(l) self.axes.add_patch(p) self.labelmap = self.generate_labelmap(self.polygon_list, self.polygon_labels) self.fig.subplots_adjust(left=0, bottom=0, right=1, top=1) self.newxmin, self.newxmax = self.axes.get_xlim() self.newymin, self.newymax = self.axes.get_ylim() self.canvas.draw() self.show() ############################################ # QT button CALLBACKs ############################################ # def growThreshSlider_valueChanged(self): # self.growThreshEdit.setText('%.2f' % (self.growThreshSlider.value()/100.)) # def brushSizeSlider_valueChanged(self, new_val): # self.brushSizeEdit.setText('%d' % new_val) def set_debug_mode(self): if self.debug_mode: self.display_groupBox.show() self.buttonParams.show() else: self.display_groupBox.hide() self.buttonParams.hide() def debugMode_toggled(self): self.debug_mode = ~self.debug_mode self.set_debug_mode() def load_groups(self): self.groups = self.dm.load_pipeline_result('clusterGroups', 'pkl') self.groups_ranked, self.group_scores_ranked = zip(*self.groups) for i, g in enumerate(self.groups_ranked[:50]): self._add_labelbutton() self.pick_color(i) for sp in g: self.paint_superpixel(sp) self.groups_loaded = True def load_segmentation(self): self.segmentation = self.dm.load_pipeline_result('segmentation', 'npy') self.n_superpixels = len(np.unique(self.segmentation)) - 1 self.sp_props = self.dm.load_pipeline_result('spProps', 'npy') self.sp_labellist = -1*np.ones((self.n_superpixels, ), dtype=np.int) self.sp_rectlist = [None for _ in range(self.n_superpixels)] # self.neighbors = self.dm.load_pipeline_result('neighbors', 'npy') # self.texton_hists = self.dm.load_pipeline_result('texHist', 'npy') # self.textonmap = self.dm.load_pipeline_result('texMap', 'npy') self.clusters = self.dm.load_pipeline_result('clusters', 'pkl') # self.cluster_sps, curr_cluster_score_sps, scores_sps, nulls_sps, models_sps, added_sps = zip(*self.clusters) self.cluster_sps, self.curr_cluster_score_sps = zip(*self.clusters) # self.n_texton = 100 # from scipy.spatial.distance import cdist # overall_texton_hist = np.bincount(self.textonmap[self.mask].flat, minlength=self.n_texton) # self.overall_texton_hist_normalized = overall_texton_hist.astype(np.float) / overall_texton_hist.sum() # self.D_sp_null = np.squeeze(cdist(self.texton_hists, [self.overall_texton_hist_normalized], chi2)) self.seg_loaded = True # def display_option_changed(self, checked): def display_option_changed(self): # if checked: # self.axes.clear() if self.sender() == self.spOnOffSlider: if self.spOnOffSlider.value() == 1: if self.segm_transparent is None: self.segm_transparent = self.dm.load_pipeline_result('segmentationTransparent', 'png') self.my_cmap = plt.cm.Reds self.my_cmap.set_under(color="white", alpha="0") # self.segm_handle = self.axes.imshow(self.segm_transparent, aspect='equal', # cmap=self.my_cmap, alpha=1.) if not self.seg_loaded: self.load_segmentation() self.seg_enabled = True else: self.segm_handle.remove() self.seg_enabled = False # elif self.sender() == self.imgSeg_radioButton: # # self.seg_vis = self.dm.load_pipeline_result('segmentationWithText', 'jpg') # # self.seg_vis[~self.dm.mask] = 0 # # self.axes.imshow(self.seg_vis, aspect='equal') # self.axes.imshow(self.masked_img, aspect='equal', cmap=plt.cm.Greys_r) # self.axes.imshow(self.segm_transparent, aspect='equal', cmap=my_red_cmap, alpha=1.) # if not self.seg_loaded: # self.load_segmentation() # self.seg_enabled = True else: # if self.under_img is not None: # self.under_img.remove() self.axes.clear() if self.sender() == self.img_radioButton: # self.axes.clear() # self.axes.axis('off') # self.under_img = self.axes.imshow(self.masked_img, aspect='equal', cmap=plt.cm.Greys_r) self.axes.imshow(self.masked_img, aspect='equal', cmap=plt.cm.Greys_r) # self.seg_enabled = False elif self.sender() == self.textonmap_radioButton: # self.axes.clear() # self.axes.axis('off') if self.textonmap_vis is None: self.textonmap_vis = self.dm.load_pipeline_result('texMap', 'png') # if self.under_img is not None: # self.under_img.remove() # self.under_img = self.axes.imshow(self.textonmap_vis, cmap=plt.cm.Greys_r, aspect='equal') self.axes.imshow(self.textonmap_vis, cmap=plt.cm.Greys_r, aspect='equal') # self.seg_enabled = False elif self.sender() == self.dirmap_radioButton: # self.axes.clear() # self.axes.axis('off') if self.dirmap_vis is None: self.dirmap_vis = self.dm.load_pipeline_result('dirMap', 'jpg') self.dirmap_vis[~self.dm.mask] = 0 # self.under_img = self.axes.imshow(self.dirmap_vis, aspect='equal') self.axes.imshow(self.dirmap_vis, aspect='equal') # if not self.seg_loaded: # self.load_segmentation() # self.seg_enabled = False elif self.sender() == self.labeling_radioButton: self.axes.clear() self.axes.axis('off') if not self.seg_loaded: self.load_segmentation() # if not self.groups_loaded: # self.load_groups() # else: for rect in self.sp_rectlist: if rect is not None: self.axes.add_patch(rect) self.seg_vis = self.dm.load_pipeline_result('segmentationWithText', 'jpg') self.seg_vis[~self.dm.mask] = 0 self.axes.imshow(self.seg_vis, aspect='equal') # self.seg_enabled = True if self.seg_enabled: self.segm_handle = self.axes.imshow(self.segm_transparent, aspect='equal', cmap=self.my_cmap, alpha=1.) for i in range(len(self.sp_rectlist)): self.sp_rectlist[i] = None self.axes.axis('off') self.axes.set_xlim([self.newxmin, self.newxmax]) self.axes.set_ylim([self.newymin, self.newymax]) self.fig.subplots_adjust(left=0, bottom=0, right=1, top=1) self.canvas.draw() def _add_labelbutton(self, desc=None): self.n_labelbuttons += 1 index = self.n_labelbuttons - 1 row = (index) % 5 col = (index) / 5 btn = QPushButton('%d' % index, self) if desc is None: labelname = 'label %d'%index else: labelname = desc edt = QLineEdit(QString(labelname)) self.labeling['labelnames'].append(labelname) self.labelbuttons.append(btn) self.labeledits.append(edt) btn.clicked.connect(self.labelbutton_callback) edt.editingFinished.connect(self.labelNameChanged) r, g, b, a = self.label_cmap(index + 1) btn.setStyleSheet("background-color: rgb(%d, %d, %d)"%(int(r*255),int(g*255),int(b*255))) btn.setFixedSize(20, 20) self.labelsLayout.addWidget(btn, row, 2*col) self.labelsLayout.addWidget(edt, row, 2*col+1) def newlabel_callback(self): # self.n_labels += 1 self._add_labelbutton() # def sigboost_callback(self): # self._save_labeling() # def load_callback(self): # self.initialize_data_manager() def labelNameChanged(self): edt = self.sender() ind_onscreen = self.labeledits.index(edt) # the first one is "no label" self.labeling['labelnames'][ind_onscreen] = str(edt.text()) def _save_labeling(self, ): username, ok = QInputDialog.getText(self, "Username", "Please enter your username:"******"%m%d%Y%H%M%S") # self.labeling['labelnames'] = [str(edt.text()).strip() for edt in self.labeledits[1:]] # labelnames_fn = os.path.join(self.data_dir, 'labelnames.json') new_labeling_name = self.username + '_' + self.labeling['logout_time'] # new_labeling_fn = os.path.join(self.dm.labelings_dir, self.dm.image_name + '_' + new_labeling_name + '.pkl') # pickle.dump(self.labeling, open(new_labeling_fn, 'w')) # print 'Labeling saved to', new_labeling_fn # self.labelmap = self.generate_labelmap(self.circle_list) # self.labelmap = self.generate_labelmap(self.circle_list) # labelmap_vis = self.colors[self.labelmap] self.labelmap = self.generate_labelmap(self.polygon_list, self.polygon_labels) labelmap_vis = label2rgb(self.labelmap, image=self.masked_img, colors=self.colors[1:], bg_label=-1, bg_color=(1,1,1), alpha=0.3, image_alpha=1.) new_labeling_fn = self.dm.save_labeling(self.labeling, new_labeling_name, labelmap_vis) print 'Curr labelnames', self.labeling['labelnames'] new_labelnames = [] q = [n.lower() for n in self.dm.labelnames] for n in self.labeling['labelnames']: if n.lower() not in q: new_labelnames.append(n) print 'Global Labelnames', self.dm.labelnames + new_labelnames self.dm.set_labelnames(self.dm.labelnames + new_labelnames) self.statusBar().showMessage('Labeling saved to %s' % new_labeling_fn ) def save_callback(self): self._save_labeling() def labelbutton_callback(self): self.statusBar().showMessage('Left click to select vertices') self.is_placing_vertices = True self.pick_color(int(self.sender().text())) ############################################ # matplotlib canvas CALLBACKs ############################################ def zoom_fun(self, event): # get the current x and y limits and subplot position cur_pos = self.axes.get_position() cur_xlim = self.axes.get_xlim() cur_ylim = self.axes.get_ylim() xdata = event.xdata # get event x location ydata = event.ydata # get event y location if xdata is None or ydata is None: # mouse position outside data region return left = xdata - cur_xlim[0] right = cur_xlim[1] - xdata up = ydata - cur_ylim[0] down = cur_ylim[1] - ydata # print left, right, up, down if event.button == 'up': # deal with zoom in scale_factor = 1/self.base_scale elif event.button == 'down': # deal with zoom out scale_factor = self.base_scale self.newxmin = xdata - left*scale_factor self.newxmax = xdata + right*scale_factor self.newymin = ydata - up*scale_factor self.newymax = ydata + down*scale_factor # set new limits # inv = self.axes.transData.inverted() # lb_disp = inv.transform([self.newxmin, self.newymin]) # rt_disp = inv.transform([self.newxmax, self.newymax]) # print lb_disp, rt_disp self.axes.set_xlim([self.newxmin, self.newxmax]) self.axes.set_ylim([self.newymin, self.newymax]) self.canvas.draw() # force re-draw def press_fun(self, event): self.press_x = event.xdata self.press_y = event.ydata self.press = True self.press_time = time.time() def motion_fun(self, event): if self.press and time.time() - self.press_time > .5: # this is drag and move cur_xlim = self.axes.get_xlim() cur_ylim = self.axes.get_ylim() if (event.xdata==None) | (event.ydata==None): #print 'either event.xdata or event.ydata is None' return offset_x = self.press_x - event.xdata offset_y = self.press_y - event.ydata self.axes.set_xlim(cur_xlim + offset_x) self.axes.set_ylim(cur_ylim + offset_y) self.canvas.draw() def remove_highlight_polygon(self): # Remove the highlight polygon if it exists if self.highlight_polygon is not None: self.highlight_polygon.remove() self.highlight_polygon = None def draw_highlight_polygon(self, event): if not self.is_placing_vertices: # Check if the click is within a polygon. If so, # construct the hightlight polygon over the selected polygon containing_polygons = [] for i, (p,l) in enumerate(zip(self.polygon_list, self.polygon_labels)): contains, attrd = p.contains(event) if contains: containing_polygons.append((p,l)) if len(containing_polygons) > 0: selected_polygon, selected_polygon_label = containing_polygons[0] self.selected_polygon = selected_polygon self.seletced_polygon_label = selected_polygon_label self.statusBar().showMessage('Polygon (%s) is selected' % self.labeling['labelnames'][self.seletced_polygon_label]) self.highlight_polygon = Polygon(selected_polygon.get_xy()) self.highlight_polygon.update_from(selected_polygon) self.highlight_polygon.set_linewidth(5) self.axes.add_patch(self.highlight_polygon) self.pick_color(self.seletced_polygon_label) def release_fun(self, event): """ The release-button callback is responsible for picking a color or changing a color. """ self.press = False self.release_x = event.xdata self.release_y = event.ydata self.release_time = time.time() # Fixed panning issues by using the time difference between the press and release event # Long times refer to a press and hold if (self.release_time - self.press_time) < .21 and self.release_x > 0 and self.release_y > 0: self.remove_highlight_polygon() self.draw_highlight_polygon(event) if event.button == 1: # left click: draw if self.curr_label is None: self.statusBar().showMessage('want to paint, but no label is selected') else: if self.seg_enabled: sp_ind = self.segmentation[int(event.ydata), int(event.xdata)] if sp_ind == -1: self.statusBar().showMessage('This is the background') else: self.statusBar().showMessage('Labeled superpixel %d using label %d (%s)' % (sp_ind, self.curr_label, self.labeling['labelnames'][self.curr_label])) # self.paint_superpixel(sp_ind) self.show_region(sp_ind) else: if self.is_placing_vertices: self.curr_polygon_vertices.append([event.xdata, event.ydata]) vertex = plt.Circle((event.xdata, event.ydata), radius=10, color=self.colors[self.curr_label + 1], alpha=.8) self.axes.add_patch(vertex) self.vertex_list.append(vertex) self.statusBar().showMessage('... in the process of labeling region using label %d (%s)' % (self.curr_label, self.labeling['labelnames'][self.curr_label])) elif event.button == 3: # right click: erase canvas_pos = (event.xdata, event.ydata) self.openMenu(canvas_pos) # self.statusBar().showMessage('Erase %d (%s)' % (self.curr_label, self.labeling['labelnames'][self.curr_label + 1])) # self.erase_circles_near(event.xdata, event.ydata) self.canvas.draw() # force re-draw ############################################ # other functions ############################################ def pick_color(self, selected_label): self.curr_label = selected_label self.statusBar().showMessage('Picked label %d (%s)' % (self.curr_label, self.labeling['labelnames'][self.curr_label])) # def paint_circle(self, x, y): # if self.curr_label is None: # self.statusBar().showMessage('No label is selected') # else: # pass # brush_radius = self.brushSizeSlider.value() # circ = plt.Circle((x, y), radius=brush_radius, color=self.colors[self.curr_label + 1], alpha=.3) # self.axes.add_patch(circ) # self.circle_list.append(circ) # def erase_circles_near(self, x, y): # to_remove = [] # for c in self.circle_list: # if abs(c.center[0] - x) < 30 and abs(c.center[1] - y) < 30: # to_remove.append(c) # for c in to_remove: # self.circle_list.remove(c) # c.remove() # def circle_list_to_labeling_field(self, circle_list): # label_circles = [] # for c in circle_list: # label = np.where(np.all(self.colors == c.get_facecolor()[:3], axis=1))[0][0] - 1 # label_circles.append((int(c.center[0]), int(c.center[1]), c.radius, label)) # return label_circles def show_region(self, sp_ind): for i, r in enumerate(self.sp_rectlist): if r is not None: r.remove() self.sp_rectlist[i] = None for i in self.cluster_sps[sp_ind]: ymin, xmin, ymax, xmax = self.sp_props[i, 4:] width = xmax - xmin height = ymax - ymin rect = Rectangle((xmin, ymin), width, height, ec="none", alpha=.3, color=self.colors[self.curr_label + 1]) self.sp_rectlist[i] = rect self.axes.add_patch(rect) self.statusBar().showMessage('Sp %d, cluster score %.4f' % (sp_ind, self.curr_cluster_score_sps[sp_ind])) def paint_superpixel(self, sp_ind): if self.curr_label == self.sp_labellist[sp_ind]: self.statusBar().showMessage('Superpixel already has the selected label') elif self.curr_label != -1: self.sp_labellist[sp_ind] = self.curr_label # self.labelmap = self.sp_labellist[self.segmentation] ### Removes previous color to prevent a blending of two or more patches ### if self.sp_rectlist[sp_ind] is not None: self.sp_rectlist[sp_ind].remove() # approximate the superpixel area with a square ymin, xmin, ymax, xmax = self.sp_props[sp_ind, 4:] width = xmax - xmin height = ymax - ymin rect = Rectangle((xmin, ymin), width, height, ec="none", alpha=.3, color=self.colors[self.curr_label + 1]) self.sp_rectlist[sp_ind] = rect self.axes.add_patch(rect) else: self.statusBar().showMessage("Remove label of superpixel %d" % sp_ind) self.sp_labellist[sp_ind] = -1 self.sp_rectlist[sp_ind].remove() self.sp_rectlist[sp_ind] = None def generate_labelmap(self, polygon_list, polygon_labels): """ Generate labelmap from the list of polygons and the list of polygon labels """ labelmap_flat = -1 * np.ones((self.dm.image_height * self.dm.image_width, 1), dtype=np.int) X, Y = np.mgrid[0:self.dm.image_height, 0:self.dm.image_width] all_points = np.column_stack([Y.ravel(), X.ravel()]) # nx2 for p, l in zip(self.polygon_list, self.polygon_labels): labelmap_flat[p.get_path().contains_points(all_points)] = l labelmap = labelmap_flat.reshape((self.dm.image_height, self.dm.image_width)) # for c in zip(polygon_list): # cx, cy = c.center # for x in np.arange(cx-c.radius, cx+c.radius): # for y in np.arange(cy-c.radius, cy+c.radius): # if (cx-x)**2+(cy-y)**2 <= c.radius**2: # label = np.where(np.all(self.colors == c.get_facecolor()[:3], axis=1))[0][0] - 1 # labelmap[int(y),int(x)] = label return labelmap
ax.axis("off") titleax.axis("off") m = Basemap(projection='eck4', lon_0=160, resolution='c', ax=ax) m.drawcoastlines(linewidth=0, color="#FFFFFF") m.drawmapboundary(color="aqua") m.fillcontinents(color='#cccccc', lake_color='#FFFFFF') # Read shapefile m.readshapefile("data/ne_10m_admin_0_countries/ne_10m_admin_0_countries", "units", drawbounds=False) patches = [] for info, shape in zip(m.units_info, m.units): poly = Polygon(np.array(shape), True) poly.set_facecolor('none') poly.set_linewidth(0) poly.set_edgecolor("#000000") poly.set_zorder(1) poly = ax.add_patch(poly) patches.append(poly) x1, y1 = m(coords["lng"].values, coords["lat"].values) _c = [scalarMap.to_rgba(groups.index(i)) for i in groups] p = m.scatter(x1, y1, marker="o", alpha=1, color=_c, zorder=2, sizes=[0] * coords.shape[0]) lwidths = list(np.logspace(np.log(1), np.log(5), 300, base=np.e))
# for line in csvfile: # lon = (float(line[0]) + float(line[2]))/2 + float(line[5]) # lat = (float(line[1]) + float(line[3]))/2 + float(line[6]) # x, y = m(lon, lat) # name = line[4].replace('\\n', '\n') # plt.text(x, y, name, horizontalalignment='center', verticalalignment='center', fontsize=int(line[7])) for lakepoly in m.lakepolygons: lp = Polygon(lakepoly.boundary, zorder=2) lp.set_facecolor('0.8') lp.set_edgecolor('0.8') lp.set_linewidth(0.1) ax.add_patch(lp) # xx, yy = m(-72.0, 26.0) # plt.text(xx, yy, u'Made by zhyuey', color='yellow') # plt.title('Map of contiguous United States', fontsize=24) # # plt.savefig('usa_state_75.png', dpi=75) # # plt.savefig('usa_state_75.png', dpi=75) # plt.savefig('usa_state_300.png', dpi=300) # # plt.savefig('usa_state_600.png', dpi=600) # m.drawcoastlines(linewidth=0.3) # m.drawcountries(linewidth=0.3)
class Shape(object): """ Displays the polygon objects onto the canvas by supplying draw methods and maintaining internal information on the shape. Draws to matplotlib backend """ def __init__(self, canvas=None, tag='', color=''): self.__canvas = canvas self.__coordinates = [] self.__tag = tag self.__color = color self.__item_handler = None self.__plot = Plot.baseplot self.__hdf = None self.__attributes = [] self.__note = '' self.__id = None self.__prev_x = 1.0 self.__prev_y = 1.0 self.__lines = [] self.__saved = False self.__selected = False self.__drawing_line = None # temporary line for free draw def add_attribute(self, attr): """ Append a passed attribute onto the internal attribute list :param str attr: An attribute enum """ if attr in TAGS: self.__attributes.append(attr) self.__saved = False else: logger.error('Caught invalid attribute for adding \'%s\'' % attr) def anchor_rectangle(self, event): """ Establishes a corner of a rectangle as an anchor for when the user drags the cursor to create a rectangle. Used in 'Draw Rect' button :param event: A matplotlib backend event object """ self.__coordinates.append((event.xdata, event.ydata)) self.__prev_x = event.x self.__prev_y = self.__canvas.figure.bbox.height - event.y def clear_lines(self): """ Remove any existing lines and clear the shape data. This is called so the lines don't remain on the screen if the user unclicks the toggleable button. """ for line in self.__lines: line.remove() def clear_unfinished_data(self): """ In the event the user is plotting points and decides to switch buttons without finishing the free draw polygon, the lines must be cleared and data must be reset. this function will ensure any unfinished data is cleared for future shape drawing. """ if self.__can_draw() != -1: return for line in self.__lines: line.remove() self.__coordinates = [] def draw(self, fig, fl, plot=Plot.baseplot, fill=False): """ Draw the shape to the canvas, onto the passed figure. Only fill the object if the *fill* parameter is set to ``True`` :param fig: A ``SubplotAxes`` object from the matplotlib backend :param fl: A string representing the HDF path :param plot: ``constants.Plot`` enum specifying which plot the object belongs to :param bool fill: ``False`` for fill, ``True`` for outline """ logger.info("Drawing polygon") # Generates a random color r = lambda: random.randint(0, 255) clr = '#%02X%02X%02X' % (r(), r(), r()) self.__color = clr self.__plot = plot self.__hdf = fl self.__item_handler = \ Polygon(self.__coordinates, facecolor=clr, fill=fill, picker=5) if self.__selected: self.set_highlight(True) fig.add_patch(self.__item_handler) def fill_rectangle(self, event, plot, fl, fig, fill=False): """ Draws the rectangle and stores the coordinates of the rectangle internally. Used in 'Draw Rect' button. Forwards argument parameters to ``draw`` :param fig: Figure to draw canvas to :param bool fill: Whether to fill or no fill the shape """ try: self.lastrect except AttributeError: pass else: self.__canvas._tkcanvas.delete(self.lastrect) del self.lastrect if event.xdata is not None and event.ydata is not None: beg = self.__coordinates[0] self.__coordinates.append((event.xdata, beg[1])) self.__coordinates.append((event.xdata, event.ydata)) self.__coordinates.append((beg[0], event.ydata)) self.draw(fig, fl, plot, fill) else: self.__coordinates = [] def generate_lat_range(self): axes = self.__canvas.figure.get_axes() labels = [x.get_xlabel() for x in axes] lat = axes[labels.index(u'Latitude')] time = axes[labels.index(u'Time')] min_ = lat.transData.inverted().transform( time.transData.transform(np.array(min(self.__coordinates))))[0] max_ = lat.transData.inverted().transform( time.transData.transform(np.array(max(self.__coordinates))))[0] return '%.4f - %.4f' % (min_, max_) def get_attributes(self): """ Return attributes list maintained by shape :rtype: :py:class:`list` """ return self.__attributes def get_color(self): """ Return the hexdecimal color value :rtype: :py:class:`str` """ return self.__color def get_coordinates(self): """ Return the list of coordinates internally maintained by shape :rtype: :py:class:`list` """ return self.__coordinates def get_id(self): """ Return the database ID of shape :rtype: :py:class:`int` """ return self.__id def get_itemhandler(self): """ Return the item handler object to the actual backend base :rtype: :py:class:`matplotlib.patches.polygon` """ return self.__item_handler def get_max_lat(self): axes = self.__canvas.figure.get_axes() labels = [x.get_xlabel() for x in axes] lat = axes[labels.index(u'Latitude')] time = axes[labels.index(u'Time')] max_ = lat.transData.inverted().transform( time.transData.transform(np.array(max(self.__coordinates))))[0] return max_ def get_min_lat(self): axes = self.__canvas.figure.get_axes() labels = [x.get_xlabel() for x in axes] lat = axes[labels.index(u'Latitude')] time = axes[labels.index(u'Time')] min_ = lat.transData.inverted().transform( time.transData.transform(np.array(min(self.__coordinates))))[0] return min_ def get_notes(self): """ Return the notes string internally maintained by shape :rtype: :py:class:`str` """ return self.__note def get_plot(self): """ Return the plot type :rtype: :py:class:`int` """ return self.__plot def get_hdf(self): """ Return the file used :rtype: :py:class:`str` """ return self.__hdf def get_saved(self): """ Returns if the shape has been saved or not """ return self.__saved def get_tag(self): """ Return the program Tag of shape :rtype: :py:class:`str` """ return self.__tag def in_x_extent(self, x): time_cords = [pair[0] for pair in self.__coordinates] if min(time_cords) <= x <= max(time_cords): return True else: return False def in_y_extent(self, y): altitude_cords = [pair[1] for pair in self.__coordinates] if min(altitude_cords) <= y <= max(altitude_cords): return True else: return False def is_attribute(self, attr): """ Return ``True`` if *attr* is inside the attributes list, ``False`` otherwise. :param str attr: :rtype: :py:class:`bool` """ for item in self.__attributes: if attr == item: logger.info('Found attribute') return True return False def is_empty(self): """ Return ``True`` if empty, ``False`` otherwise """ if len(self.__coordinates) == 0: return True return False def is_selected(self): """ Return a boolean value based on whether the object is currently highlighted in the figure. Uses ``__selected`` :rtype: :py:class:`bool` """ return self.__selected def loaded_draw(self, fig, fill): """ Called in the case of panning the plot, since panning the plot invalidates the previous figure, the figures must first be cleared and the shapes are removed. Loaded draw draws the shapes back into view using a new figure. :param fig: A ``SubplotAxes`` object to add the patch to :param bool fill: Boolean value whether to have the shape filled in when drawn to or not """ self.__item_handler = \ Polygon(self.__coordinates, facecolor=self.__color, fill=fill, picker=5) if self.__selected: self.set_highlight(True) fig.add_patch(self.__item_handler) def paint(self, color): """ Changes the color of the shape and saves it internally :param color: the new color of the shape """ self.set_color(color) self.__saved = False def plot_point(self, event, plot, fl, fig, fill=False): """ Plot a single point to the shape, connect any previous existing points and fill to a shape if the current coordinate intersects the beginning point. :param event: A ``matplotlib.backend_bases.MouseEvent`` passed object :param plot: an integer indicating which plot it was draw on :param fl: A string representing the HDF it was drawn on :param fig: The figure to be drawing the canvas to :param bool fill: Whether the shape will have a solid fill or not """ self.__coordinates.append((event.xdata, event.ydata)) logger.debug("Plotted point at (%0.5f, %0.5f)", event.xdata, event.ydata) if len(self.__coordinates) > 1: logger.debug("Drawing line from plot") self.__lines.append( mlines.Line2D((self.__prev_x, event.xdata), (self.__prev_y, event.ydata), linewidth=2.0, color='#000000')) fig.add_artist(self.__lines[-1]) self.__canvas.show() if len(self.__coordinates) > 3: index = self.__can_draw() if index > -1: logger.debug("Creating polygon from points") a1 = tuple_to_nparray(self.__coordinates[index]) a2 = tuple_to_nparray(self.__coordinates[index + 1]) b1 = tuple_to_nparray(self.__coordinates[-1]) b2 = tuple_to_nparray(self.__coordinates[-2]) x = get_intersection(a1, a2, b1, b2) pair = nparray_to_tuple(x) self.__coordinates[index] = pair del self.__coordinates[:index] self.__coordinates.pop() for line in self.__lines: line.remove() self.__drawing_line.remove() self.__drawing_line = None self.__lines = [] self.draw(fig, fl, plot=plot, fill=fill) self.__plot = plot self.__hdf = fl return True self.__prev_x = event.xdata self.__prev_y = event.ydata def sketch_line(self, event, fig): if self.__drawing_line: self.__drawing_line.remove() self.__drawing_line = \ mlines.Line2D((self.__prev_x, event.xdata), (self.__prev_y, event.ydata), linewidth=2.0, color='#000000') fig.add_artist(self.__drawing_line) self.__canvas.show() return def close_polygon(self, event, plot, fl, fig, fill=False): if len(self.__coordinates) > 3: index = self.__can_draw() if index > -1: logger.debug("Creating polygon from points") a1 = tuple_to_nparray(self.__coordinates[index]) a2 = tuple_to_nparray(self.__coordinates[index + 1]) b1 = tuple_to_nparray(self.__coordinates[-1]) b2 = tuple_to_nparray(self.__coordinates[-2]) x = get_intersection(a1, a2, b1, b2) pair = nparray_to_tuple(x) self.__coordinates[index] = pair del self.__coordinates[:index] self.__coordinates.pop() for line in self.__lines: line.remove() self.__drawing_line.remove() self.__drawing_line = None self.__lines = [] self.draw(fig, plot, fill) self.__plot = plot self.__hdf = fl return True else: logger.warning('Not enough points') def redraw(self, fig, fl, fill): """ Function to draw the shape in the event the shape *may* or *may not* already be drawn. Checks if the image already exists, if not draws the image :param fig: A ``SubplotAxes`` object to add the patch to :param fl: A string representing the HDF file :param bool fill: Boolean value whether to have the shape filled in when drawn or not """ if self.__item_handler is not None and self.__item_handler.is_figure_set( ): self.__item_handler.remove() self.__item_handler = \ Polygon(self.__coordinates, facecolor=self.__color, fill=fill, picker=5) if self.__selected: self.set_highlight(True) self.__hdf = fl fig.add_patch(self.__item_handler) def remove(self): """ Wrapper function to internally call matplotlib backend to remove the shape from the figure """ if self.__item_handler is None: self.clear_lines() else: self.__item_handler.remove() def remove_attribute(self, attr): """ Remove an attribute as specified in ``constants.py`` from the internal attributes variable :param str attr: """ if attr in TAGS: self.__attributes.remove(attr) self.__saved = False else: logger.error('Caught invalid attribute for removal \'%s\'' % attr) # noinspection PyAttributeOutsideInit def rubberband(self, event): """ Draws a temporary helper rectangle that outlines the final shape of the rectangle for the user. This draws to **screen** coordiantes, so backend is not needed here. :param event: A ``matplotlib.backend_bases.MouseEvent`` forwarded object. """ try: self.lastrect except AttributeError: pass else: self.__canvas._tkcanvas.delete(self.lastrect) height = self.__canvas.figure.bbox.height y2 = height - event.y self.lastrect = self.__canvas._tkcanvas.create_rectangle( self.__prev_x, self.__prev_y, event.x, y2) def save(self): """ Marks the shape as saved """ self.__saved = True def set_attributes(self, attributes_list): """ Set the internal list of attributes to a custom passed list :param list attributes_list: """ for i in attributes_list: if i not in TAGS: logger.error('Caught invalid attribute for setting \'%s\'' % i) return self.__attributes = attributes_list def set_color(self, color): """ Set internal color variable :param str color: Valid hexadecimal color value """ self.__color = color def set_coordinates(self, coordinates): """ Pass a list of coordinates to set to the shape to. :param list coordinates: """ self.__coordinates = coordinates def set_highlight(self, highlight): """ Set the ``linewidth`` and ``linestyle`` attributes of a the internal item handler. Highlights if *highlight* is ``True``, otherwise sets to normal outline. :param bool highlight: """ if highlight: self.__selected = True self.__item_handler.set_linewidth(3.0) self.__item_handler.set_linestyle('dashed') else: self.__selected = False self.__item_handler.set_linewidth(1.0) self.__item_handler.set_linestyle('solid') def set_id(self, _id): """ Set the database ID of the shape. **unsafe** to use outside letting database call this. :param int _id: Database primary key """ self.__id = _id def set_notes(self, note): """ Pass a string containing new notes to set the shape to :param str note: New note string """ self.__note = note def set_plot(self, plot): """ Manually set the new value of the internal plot variable. **unsafe** :param constants.Plot plot: Plot value """ self.__plot = plot def set_hdf(self, fl): """ Manually set the value of the internal file variable :param fl: HDF file path """ self.__hdf = fl def set_tag(self, tag): """ Set internal tag variable :param str tag: """ self.__tag = tag def __can_draw(self): if not self.__coordinates: logger.warning('Attempting to ask to draw empty shape, probably just ' + \ 'toggling a button after using free draw? See ticket #92') return -1 b1 = tuple_to_nparray(self.__coordinates[-1]) b2 = tuple_to_nparray(self.__coordinates[-2]) for i in range(len(self.__coordinates) - 3): a1 = tuple_to_nparray(self.__coordinates[i]) a2 = tuple_to_nparray(self.__coordinates[i + 1]) if is_intersecting(a1, a2, b1, b2): logger.debug("Polygon labeled for draw") return i return -1 def __str__(self): logger.debug('Stringing %s' % self.__tag) time_cords = [ mpl.dates.num2date(x[0]).strftime('%H:%M:%S') for x in self.__coordinates ] altitude_cords = [x[1] for x in self.__coordinates] string = 'Name:\n\t%s\n' % self.__tag string += 'Time Scale:\n\t%s - %s\n' % (min(time_cords), max(time_cords)) string += 'Latitude Scale:\n\t%s\n' % self.generate_lat_range() string += 'Altitude Scale:\n\t%.4f km - %.4f km\n' % ( min(altitude_cords), max(altitude_cords)) string += 'Color:\n\t%s\n' % self.__color if len(self.__attributes) > 0: string += 'Attributes:\n' for item in self.__attributes: string += '\t%s\n' % item if self.__note != '': string += 'Notes:\n\t%s' % self.__note return string
def plot_planform(c_r, c_k, c_t, b_k, b, Lambda_le_1, Lambda_le_2, *args, **kwargs): fig = plt.subplots(figsize=(9, 9)) # optional arguments c_mac = kwargs.get('mac', None) X_le_mac = kwargs.get('X_le_mac', None) Y_mac = kwargs.get('Y_mac', None) X_ac = kwargs.get('X_ac', None) xLineWing = [0, b_k/2, b/2, b/2, b_k/2, 0] dy_k = (b_k/2)*math.tan(Lambda_le_1) dy = dy_k + (b/2 - b_k/2)*math.tan(Lambda_le_2) yLineWing = [ 0, dy_k, dy, dy + c_t, dy_k + c_k, c_r] # planform lineWing, = plt.plot(xLineWing, yLineWing, 'k-') plt.scatter(xLineWing, yLineWing, marker='o', s=40) # centerline centerLine, = plt.plot([0,0], [-0.2*c_r,2.1*c_r], 'b') centerLine.set_dashes([8, 4, 2, 4]) # c/4 line pC4r = [0, 0.25*c_r] pC4k = [b_k/2, dy_k + 0.25*c_k] pC4t = [b/2, dy + 0.25*c_t] quarterChordLine, = plt.plot([pC4r[0],pC4k[0],pC4t[0]], [pC4r[1],pC4k[1],pC4t[1]], 'k--') plt.scatter([pC4r[0],pC4k[0],pC4t[0]], [pC4r[1],pC4k[1],pC4t[1]], marker='o', s=40) if ('mac' in kwargs) and ('X_le_mac' in kwargs) and ('Y_mac' in kwargs): c_mac = kwargs['mac'] X_le_mac = kwargs['X_le_mac'] Y_mac = kwargs['Y_mac'] #print(mac) #print(X_le_mac) #print(Y_mac) lineMAC, = plt.plot([Y_mac, Y_mac], [X_le_mac, X_le_mac + c_mac], color="red", linewidth=2.5, linestyle="-") lineMAC.set_dashes([1000,1]) # HUUUUGE lineLEMAC, = plt.plot([0,b/2], [X_le_mac,X_le_mac], color="orange", linewidth=1.5, linestyle="-") lineLEMAC.set_dashes([10,2]) lineTEMAC, = plt.plot([0,b/2], [X_le_mac + c_mac, X_le_mac + c_mac], color="orange", linewidth=1.5, linestyle="-") lineTEMAC.set_dashes([10,2]) plt.scatter(Y_mac, X_le_mac, marker='o', s=40) ax = plt.gca() # gca stands for 'get current axis' ax.annotate( r'$(Y_{\bar{c}},X_{\mathrm{le},\bar{c}}) = ' +r'( {0:.3}'.format(Y_mac) + r'\,\mathrm{m}'+r',\,{0:.3}'.format(X_le_mac) + r'\,\mathrm{m} )$', xy=(Y_mac, X_le_mac), xycoords='data', xytext=(20, 30), textcoords='offset points', fontsize=12, arrowprops=dict(arrowstyle="->", connectionstyle="arc3,rad=.2")) # if ('X_le_r_eq' in kwargs) and ('c_r_eq' in kwargs): X_le_r_eq = kwargs['X_le_r_eq'] c_r_eq = kwargs['c_r_eq'] vertices = [(0, X_le_r_eq)] + [(b/2, dy)] + [(b/2, dy+c_t)] + [(0, X_le_r_eq + c_r_eq)] poly = Polygon(vertices, facecolor="yellow", alpha=0.5) poly.set_edgecolor("brown") poly.set_linewidth(2) ax0 = plt.gca() # gca stands for 'get current axis' ax0.add_patch(poly) if 'X_ac' in kwargs: X_ac = kwargs['X_ac'] #print(X_ac) plt.scatter(0, X_ac, marker='o', s=40) lineAC, = plt.plot([0,b/2], [X_ac,X_ac], color="brown", linewidth=3.5, linestyle="-") lineAC.set_dashes([10,2.5,3,2.5]) ax = plt.gca() # gca stands for 'get current axis' ax.annotate(r'$X_{\mathrm{ac,W}} = '+r'{0:.3}'.format(X_ac)+r'\,\mathrm{m} $', xy=(b/2, X_ac), xycoords='data', xytext=(20, 30), textcoords='offset points', fontsize=12, arrowprops=dict(arrowstyle="->", connectionstyle="arc3,rad=.2")) # plt.axis('equal') # xmajorLocator = MultipleLocator(2.0) # xmajorFormatter = FormatStrFormatter('%.1f') # xminorLocator = MultipleLocator(4) # ax = plt.gca() # gca stands for 'get current axis' # ax.xaxis.set_major_locator(xmajorLocator) # ax.xaxis.set_major_formatter(xmajorFormatter) # # for the minor ticks, use no labels; default NullFormatter # ax.xaxis.set_minor_locator(xminorLocator) plt.axis([-0.02*b/2, 1.1*b/2, -0.05*c_r, 1.1*(dy + c_t)]) plt.gca().invert_yaxis() plt.title('Wing planform', fontsize=16) plt.xlabel('$y$ (m)', fontsize=16) plt.ylabel('$X$ (m)', fontsize=16) # Moving spines ax = plt.gca() # gca stands for 'get current axis' ax.spines['right'].set_color('none') ax.spines['top'].set_color('none') ax.xaxis.set_ticks_position('bottom') ax.spines['bottom'].set_position(('outward',10)) # outward by 10 points ax.yaxis.set_ticks_position('left') ax.spines['left'].set_position(('data',-0.07*b/2)) plt.show()
circle.set_facecolor(None) patches = [circle] for i in range(nPolys): p = [1] + [0] * (n - 1) + [-z[i]] roots = np.roots(p) angles = np.sort([cmath.phase(root) for root in roots]) roots = np.cos(angles) + j * np.sin(angles) x = roots.real y = roots.imag coords = np.stack((x, y), axis=1) poly = Polygon(xy=coords, closed=True) poly.set_edgecolor('royalblue') poly.set_facecolor('lightsteelblue') poly.set_linewidth(1) patches.append(poly) fig, ax = plt.subplots(subplot_kw={'aspect': 'equal'}) # Set bottom and left spines as x and y axes of coordinate system ax.spines['bottom'].set_position('zero') ax.spines['left'].set_position('zero') # Remove top and right spines ax.spines['top'].set_visible(False) ax.spines['right'].set_visible(False) # Create 'x' and 'y' labels placed at the end of the axes ax.set_xlabel('x', size=14, labelpad=-24, x=1.03) ax.set_ylabel('y', size=14, labelpad=-21, y=1.02, rotation=0)
class WindroseAxes(PolarAxes): """ Makes a windrose axes """ RESOLUTION = 100 def __init__(self, *args, **kwargs): """ See Axes base class for args and kwargs documentation """ PolarAxes.__init__(self, *args, **kwargs) self.set_aspect('equal', adjustable='box', anchor='C') self.radii_angle = 67.5 self.cla() def _init_axis(self): self.xaxis = None self.yaxis = None def cla(self): """ Clear the current axes """ self._get_lines = _process_plot_var_args(self) self._get_patches_for_fill = _process_plot_var_args(self, 'fill') self._gridOn = matplotlib.rcParams['polaraxes.grid'] self.thetagridlabels = [] self.thetagridlines = [] self.rgridlabels = [] self.rgridlines = [] self.lines = [] self.images = [] self.patches = [] self.artists = [] self.collections = [] self.texts = [] # text in axis coords self.legend_ = None self.grid(self._gridOn) self.title = Text( x=0.5, y=1.05, text='', fontproperties=FontProperties( size=matplotlib.rcParams['axes.titlesize']), verticalalignment='bottom', horizontalalignment='center', ) self.title.set_transform(self.transAxes) self._set_artist_props(self.title) self.thetas = N.linspace(0, 2 * N.pi, self.RESOLUTION) verts = list(zip(self.thetas, N.ones(self.RESOLUTION))) self.axesPatch = Polygon( verts, facecolor=self._axisbg, edgecolor=matplotlib.rcParams['axes.edgecolor'], ) self.axesPatch.set_figure(self.figure) self.axesPatch.set_transform(self.transData) self.axesPatch.set_linewidth(matplotlib.rcParams['axes.linewidth']) self.axison = True self.rintv = Interval(Value(0), Value(1)) self.rintd = Interval(Value(0), Value(1)) self.rformatter = ScalarFormatter() self.rformatter.set_view_interval(self.rintv) self.rformatter.set_data_interval(self.rintd) class RadialLocator(AutoLocator): 'enforce strictly positive radial ticks' def __call__(self): ticks = AutoLocator.__call__(self) return [t for t in ticks if t > 0] self.rlocator = RadialLocator() self.rlocator.set_view_interval(self.rintv) self.rlocator.set_data_interval(self.rintd) self.theta_angles = N.arange(0, 360, 45) self.theta_labels = ['E', 'N-E', 'N', 'N-W', 'W', 'S-W', 'S', 'S-E'] self.set_thetagrids(angles=self.theta_angles, labels=self.theta_labels) self._info = {'dir': list(), 'bins': list(), 'table': list()} self.patches_list = list() def _colors(self, cmap, n): ''' Returns a list of n colors based on the colormap cmap ''' return [cmap(i) for i in N.linspace(0.0, 1.0, n)] def set_radii_angle(self, **kwargs): """ Set the radii labels angle """ null = popd(kwargs, 'labels', None) angle = popd(kwargs, 'angle', None) if angle is None: angle = self.radii_angle self.radii_angle = angle radii = N.linspace(0.1, self.get_rmax(), 6) radii_labels = ["%.1f" % r for r in radii] radii_labels[0] = "" #Removing label 0 null = self.set_rgrids(radii=radii, labels=radii_labels, angle=self.radii_angle, **kwargs) def _update(self): self.regrid(self.get_rmax()) self.set_radii_angle(angle=self.radii_angle) def legend(self, loc='lower left', **kwargs): """ Sets the legend location and her properties. The location codes are 'best' : 0, 'upper right' : 1, 'upper left' : 2, 'lower left' : 3, 'lower right' : 4, 'right' : 5, 'center left' : 6, 'center right' : 7, 'lower center' : 8, 'upper center' : 9, 'center' : 10, If none of these are suitable, loc can be a 2-tuple giving x,y in axes coords, ie, loc = (0, 1) is left top loc = (0.5, 0.5) is center, center and so on. The following kwargs are supported: isaxes=True # whether this is an axes legend prop = FontProperties(size='smaller') # the font property pad = 0.2 # the fractional whitespace inside the legend border shadow # if True, draw a shadow behind legend labelsep = 0.005 # the vertical space between the legend entries handlelen = 0.05 # the length of the legend lines handletextsep = 0.02 # the space between the legend line and legend text axespad = 0.02 # the border between the axes and legend edge """ def get_handles(): handles = list() for p in self.patches_list: if isinstance(p, matplotlib.patches.Polygon) or \ isinstance(p, matplotlib.patches.Rectangle): color = p.get_facecolor() elif isinstance(p, matplotlib.lines.Line2D): color = p.get_color() else: raise AttributeError("Can't handle patches") handles.append( Rectangle((0, 0), 0.2, 0.2, facecolor=color, edgecolor='black')) return handles def get_labels(): labels = N.copy(self._info['bins']) labels = ["[%.1f : %0.1f[" %(labels[i], labels[i+1]) \ for i in range(len(labels)-1)] return labels null = popd(kwargs, 'labels', None) null = popd(kwargs, 'handles', None) handles = get_handles() labels = get_labels() self.legend_ = matplotlib.legend.Legend(self, handles, labels, loc, **kwargs) return self.legend_ def _init_plot(self, dir, var, **kwargs): """ Internal method used by all plotting commands """ #self.cla() null = popd(kwargs, 'zorder', None) #Init of the bins array if not set bins = popd(kwargs, 'bins', None) if bins is None: bins = N.linspace(N.min(var), N.max(var), 6) if isinstance(bins, int): bins = N.linspace(N.min(var), N.max(var), bins) nbins = len(bins) #Init of the number of sectors nsector = popd(kwargs, 'nsector', None) if nsector is None: nsector = 16 #Sets the colors table based on the colormap or the "colors" argument colors = popd(kwargs, 'colors', None) cmap = popd(kwargs, 'cmap', None) if colors is not None: if isinstance(colors, str): colors = [colors] * nbins if isinstance(colors, (tuple, list)): if len(colors) != nbins: raise ValueError("colors and bins must have same length") else: if cmap is None: cmap = cm.jet colors = self._colors(cmap, nbins) #Building the list of angles angles = N.arange(0, -2 * N.pi, -2 * N.pi / nsector) + N.pi / 2 normed = popd(kwargs, 'normed', False) blowto = popd(kwargs, 'blowto', False) #Set the global information dictionnary self._info['dir'], self._info['bins'], self._info['table'] = histogram( dir, var, bins, nsector, normed, blowto) return bins, nbins, nsector, colors, angles, kwargs def contour(self, dir, var, **kwargs): """ Plot a windrose in linear mode. For each var bins, a line will be draw on the axes, a segment between each sector (center to center). Each line can be formated (color, width, ...) like with standard plot pylab command. Mandatory: * dir : 1D array - directions the wind blows from, North centred * var : 1D array - values of the variable to compute. Typically the wind speeds Optional: * nsector: integer - number of sectors used to compute the windrose table. If not set, nsectors=16, then each sector will be 360/16=22.5°, and the resulting computed table will be aligned with the cardinals points. * bins : 1D array or integer- number of bins, or a sequence of bins variable. If not set, bins=6, then bins=linspace(min(var), max(var), 6) * blowto : bool. If True, the windrose will be pi rotated, to show where the wind blow to (usefull for pollutant rose). * colors : string or tuple - one string color ('k' or 'black'), in this case all bins will be plotted in this color; a tuple of matplotlib color args (string, float, rgb, etc), different levels will be plotted in different colors in the order specified. * cmap : a cm Colormap instance from matplotlib.cm. - if cmap == None and colors == None, a default Colormap is used. others kwargs : see help(pylab.plot) """ bins, nbins, nsector, colors, angles, kwargs = self._init_plot( dir, var, **kwargs) #closing lines angles = N.hstack((angles, angles[0])) vals = N.hstack((self._info['table'], N.reshape(self._info['table'][:, 0], (self._info['table'].shape[0], 1)))) offset = 0 for i in range(nbins): val = vals[i, :] + offset offset += vals[i, :] zorder = nbins - i patch = self.plot(angles, val, color=colors[i], zorder=zorder, **kwargs) self.patches_list.extend(patch) self._update() def contourf(self, dir, var, **kwargs): """ Plot a windrose in filled mode. For each var bins, a line will be draw on the axes, a segment between each sector (center to center). Each line can be formated (color, width, ...) like with standard plot pylab command. Mandatory: * dir : 1D array - directions the wind blows from, North centred * var : 1D array - values of the variable to compute. Typically the wind speeds Optional: * nsector: integer - number of sectors used to compute the windrose table. If not set, nsectors=16, then each sector will be 360/16=22.5°, and the resulting computed table will be aligned with the cardinals points. * bins : 1D array or integer- number of bins, or a sequence of bins variable. If not set, bins=6, then bins=linspace(min(var), max(var), 6) * blowto : bool. If True, the windrose will be pi rotated, to show where the wind blow to (usefull for pollutant rose). * colors : string or tuple - one string color ('k' or 'black'), in this case all bins will be plotted in this color; a tuple of matplotlib color args (string, float, rgb, etc), different levels will be plotted in different colors in the order specified. * cmap : a cm Colormap instance from matplotlib.cm. - if cmap == None and colors == None, a default Colormap is used. others kwargs : see help(pylab.plot) """ bins, nbins, nsector, colors, angles, kwargs = self._init_plot( dir, var, **kwargs) null = popd(kwargs, 'facecolor', None) null = popd(kwargs, 'edgecolor', None) offset = 0 for i in range(nbins): val = self._info['table'][i, :] + offset offset += self._info['table'][i, :] zorder = nbins - i patch = self.fill(angles, val, facecolor=colors[i], edgecolor=colors[i], zorder=zorder, **kwargs) self.patches_list.extend(patch) def bar(self, dir, var, **kwargs): """ Plot a windrose in bar mode. For each var bins and for each sector, a colored bar will be draw on the axes. Mandatory: * dir : 1D array - directions the wind blows from, North centred * var : 1D array - values of the variable to compute. Typically the wind speeds Optional: * nsector: integer - number of sectors used to compute the windrose table. If not set, nsectors=16, then each sector will be 360/16=22.5°, and the resulting computed table will be aligned with the cardinals points. * bins : 1D array or integer- number of bins, or a sequence of bins variable. If not set, bins=6 between min(var) and max(var). * blowto : bool. If True, the windrose will be pi rotated, to show where the wind blow to (usefull for pollutant rose). * colors : string or tuple - one string color ('k' or 'black'), in this case all bins will be plotted in this color; a tuple of matplotlib color args (string, float, rgb, etc), different levels will be plotted in different colors in the order specified. * cmap : a cm Colormap instance from matplotlib.cm. - if cmap == None and colors == None, a default Colormap is used. edgecolor : string - The string color each edge bar will be plotted. Default : no edgecolor * opening : float - between 0.0 and 1.0, to control the space between each sector (1.0 for no space) """ bins, nbins, nsector, colors, angles, kwargs = self._init_plot( dir, var, **kwargs) null = popd(kwargs, 'facecolor', None) edgecolor = popd(kwargs, 'edgecolor', None) if edgecolor is not None: if not isinstance(edgecolor, str): raise ValueError('edgecolor must be a string color') opening = popd(kwargs, 'opening', None) if opening is None: opening = 0.8 dtheta = 2 * N.pi / nsector opening = dtheta * opening for j in range(nsector): offset = 0 for i in range(nbins): if i > 0: offset += self._info['table'][i - 1, j] val = self._info['table'][i, j] zorder = nbins - i patch = Rectangle((angles[j] - opening / 2, offset), opening, val, facecolor=colors[i], edgecolor=edgecolor, zorder=zorder, **kwargs) self.add_patch(patch) if j == 0: self.patches_list.append(patch) self._update() def box(self, dir, var, **kwargs): """ Plot a windrose in proportional bar mode. For each var bins and for each sector, a colored bar will be draw on the axes. Mandatory: * dir : 1D array - directions the wind blows from, North centred * var : 1D array - values of the variable to compute. Typically the wind speeds Optional: * nsector: integer - number of sectors used to compute the windrose table. If not set, nsectors=16, then each sector will be 360/16=22.5°, and the resulting computed table will be aligned with the cardinals points. * bins : 1D array or integer- number of bins, or a sequence of bins variable. If not set, bins=6 between min(var) and max(var). * blowto : bool. If True, the windrose will be pi rotated, to show where the wind blow to (usefull for pollutant rose). * colors : string or tuple - one string color ('k' or 'black'), in this case all bins will be plotted in this color; a tuple of matplotlib color args (string, float, rgb, etc), different levels will be plotted in different colors in the order specified. * cmap : a cm Colormap instance from matplotlib.cm. - if cmap == None and colors == None, a default Colormap is used. edgecolor : string - The string color each edge bar will be plotted. Default : no edgecolor """ bins, nbins, nsector, colors, angles, kwargs = self._init_plot( dir, var, **kwargs) null = popd(kwargs, 'facecolor', None) edgecolor = popd(kwargs, 'edgecolor', None) if edgecolor is not None: if not isinstance(edgecolor, str): raise ValueError('edgecolor must be a string color') opening = N.linspace(0.0, N.pi / 16, nbins) for j in range(nsector): offset = 0 for i in range(nbins): if i > 0: offset += self._info['table'][i - 1, j] val = self._info['table'][i, j] zorder = nbins - i patch = Rectangle((angles[j] - opening[i] / 2, offset), opening[i], val, facecolor=colors[i], edgecolor=edgecolor, zorder=zorder, **kwargs) self.add_patch(patch) if j == 0: self.patches_list.append(patch) self._update()
def plot_planform(c_r, c_k, c_t, b_k, b, Lambda_le_1, Lambda_le_2, *args, **kwargs): fig = plt.subplots(figsize=(9, 9)) # optional arguments c_mac = kwargs.get('mac', None) X_le_mac = kwargs.get('X_le_mac', None) Y_mac = kwargs.get('Y_mac', None) X_ac = kwargs.get('X_ac', None) xLineWing = [0, b_k / 2, b / 2, b / 2, b_k / 2, 0] dy_k = (b_k / 2) * math.tan(Lambda_le_1) dy = dy_k + (b / 2 - b_k / 2) * math.tan(Lambda_le_2) yLineWing = [0, dy_k, dy, dy + c_t, dy_k + c_k, c_r] # planform lineWing, = plt.plot(xLineWing, yLineWing, 'k-') plt.scatter(xLineWing, yLineWing, marker='o', s=40) # centerline centerLine, = plt.plot([0, 0], [-0.2 * c_r, 2.1 * c_r], 'b') centerLine.set_dashes([8, 4, 2, 4]) # c/4 line pC4r = [0, 0.25 * c_r] pC4k = [b_k / 2, dy_k + 0.25 * c_k] pC4t = [b / 2, dy + 0.25 * c_t] quarterChordLine, = plt.plot([pC4r[0], pC4k[0], pC4t[0]], [pC4r[1], pC4k[1], pC4t[1]], 'k--') plt.scatter([pC4r[0], pC4k[0], pC4t[0]], [pC4r[1], pC4k[1], pC4t[1]], marker='o', s=40) if ('mac' in kwargs) and ('X_le_mac' in kwargs) and ('Y_mac' in kwargs): c_mac = kwargs['mac'] X_le_mac = kwargs['X_le_mac'] Y_mac = kwargs['Y_mac'] #print(mac) #print(X_le_mac) #print(Y_mac) lineMAC, = plt.plot([Y_mac, Y_mac], [X_le_mac, X_le_mac + c_mac], color="red", linewidth=2.5, linestyle="-") lineMAC.set_dashes([1000, 1]) # HUUUUGE lineLEMAC, = plt.plot([0, b / 2], [X_le_mac, X_le_mac], color="orange", linewidth=1.5, linestyle="-") lineLEMAC.set_dashes([10, 2]) lineTEMAC, = plt.plot([0, b / 2], [X_le_mac + c_mac, X_le_mac + c_mac], color="orange", linewidth=1.5, linestyle="-") lineTEMAC.set_dashes([10, 2]) plt.scatter(Y_mac, X_le_mac, marker='o', s=40) ax = plt.gca() # gca stands for 'get current axis' ax.annotate(r'$(Y_{\bar{c}},X_{\mathrm{le},\bar{c}}) = ' + r'( {0:.3}'.format(Y_mac) + r'\,\mathrm{m}' + r',\,{0:.3}'.format(X_le_mac) + r'\,\mathrm{m} )$', xy=(Y_mac, X_le_mac), xycoords='data', xytext=(20, 30), textcoords='offset points', fontsize=12, arrowprops=dict(arrowstyle="->", connectionstyle="arc3,rad=.2")) # if ('X_le_r_eq' in kwargs) and ('c_r_eq' in kwargs): X_le_r_eq = kwargs['X_le_r_eq'] c_r_eq = kwargs['c_r_eq'] vertices = [(0, X_le_r_eq)] + [(b / 2, dy)] + [(b / 2, dy + c_t)] + [ (0, X_le_r_eq + c_r_eq) ] poly = Polygon(vertices, facecolor="yellow", alpha=0.5) poly.set_edgecolor("brown") poly.set_linewidth(2) ax0 = plt.gca() # gca stands for 'get current axis' ax0.add_patch(poly) if 'X_ac' in kwargs: X_ac = kwargs['X_ac'] #print(X_ac) plt.scatter(0, X_ac, marker='o', s=40) lineAC, = plt.plot([0, b / 2], [X_ac, X_ac], color="brown", linewidth=3.5, linestyle="-") lineAC.set_dashes([10, 2.5, 3, 2.5]) ax = plt.gca() # gca stands for 'get current axis' ax.annotate(r'$X_{\mathrm{ac,W}} = ' + r'{0:.3}'.format(X_ac) + r'\,\mathrm{m} $', xy=(b / 2, X_ac), xycoords='data', xytext=(20, 30), textcoords='offset points', fontsize=12, arrowprops=dict(arrowstyle="->", connectionstyle="arc3,rad=.2")) # plt.axis('equal') # xmajorLocator = MultipleLocator(2.0) # xmajorFormatter = FormatStrFormatter('%.1f') # xminorLocator = MultipleLocator(4) # ax = plt.gca() # gca stands for 'get current axis' # ax.xaxis.set_major_locator(xmajorLocator) # ax.xaxis.set_major_formatter(xmajorFormatter) # # for the minor ticks, use no labels; default NullFormatter # ax.xaxis.set_minor_locator(xminorLocator) plt.axis([-0.02 * b / 2, 1.1 * b / 2, -0.05 * c_r, 1.1 * (dy + c_t)]) plt.gca().invert_yaxis() plt.title('Wing planform', fontsize=16) plt.xlabel('$y$ (m)', fontsize=16) plt.ylabel('$X$ (m)', fontsize=16) # Moving spines ax = plt.gca() # gca stands for 'get current axis' ax.spines['right'].set_color('none') ax.spines['top'].set_color('none') ax.xaxis.set_ticks_position('bottom') ax.spines['bottom'].set_position(('outward', 10)) # outward by 10 points ax.yaxis.set_ticks_position('left') ax.spines['left'].set_position(('data', -0.07 * b / 2)) plt.show()