diff --git a/PyAVA.py b/PyAVA.py index 98c0d3b2d98a74934804a948e46fb0df82f42121..d1dfe3011bc8848ecd7c962d5256b5a9fa8a11cb 100644 --- a/PyAVA.py +++ b/PyAVA.py @@ -9,7 +9,7 @@ from args import fetch_inputs ##### MAIN ##### if __name__ == '__main__': # fetching inputs. - dir_explore, max_traj, new_sr, output, modify, initial_basename = fetch_inputs() + dir_explore, max_traj, new_sr, output, modify, initial_basename, parameters = fetch_inputs() if modify: with open(os.path.join(output, modify), "r") as f: @@ -21,10 +21,11 @@ if __name__ == '__main__': new_sr, output, os.path.join(dir_explore, initial_basename), - coords_to_change) + coords_to_change, + parameters) else: - # open explorer to select firt file + # open explorer to select first file groot = Tk() initial_file = FileExplorer(dir_explore).file groot.quit() @@ -37,4 +38,5 @@ if __name__ == '__main__': max_traj, new_sr, output, - initial_file) + initial_file, + overwrite_parameters=parameters) diff --git a/README.md b/README.md index 71d8638b09ee48978881a4874e8e8b6d989f2725..f7fba7b3bcaa1951730a84a177ebf6c4fb7c9bc3 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ ## Features +- [x] Same tools as matplotlib.pyplot plots. - [x] Spectrogram contour annotations. - [x] Spectrogram automatically computed from waveform. - [x] Choose custom spectrogram resolutions (fft, hop length, clipping dB value and PCEN). @@ -37,13 +38,15 @@ Run `$python PyAVA.py --help` for details. The annotations are saved in [JSON](http://www.json.org/) files. Each file contains a dictionnary with the categories annotated. For each category there is a list of points, each point is defined by a list of two elements : [time (in sec), frequency (in Hz)]. ### User actions -- Use the toolbar to interact with the plot (same as with matplotlib.pyplot). +- Use the toolbar to interact with the plot (same as with matplotlib.pyplot) +- Shortcuts "p" and "w" to activate panning, "z" to activate zoom. - Draw lines : - User must not have any toolbar item selected in order to annotate the spectrogram. - Left-click on a name in the listbox to activate annotation with it. - - Left-click to place a point from the selected category. - - Right-click to remove the nearest point from the selected category. - - Mouse wheel click on a point to move it around. + - Right-click on a name in the listbox to rename it. + - Left-click on spectrogram to place a point from the selected category. + - Right-click on spectrogram to remove the nearest point from the selected category. + - Mouse wheel click on a point on spectrogram to move it around. - Click on `Open file explorer` Button to change Wavefile used as base (will end the annotation of the current file). - Change resolution of the spectrogram with `FFT`, `Hop length` and `clipping fields`. - Click on `Update display` button to validate inputs. diff --git a/args.py b/args.py index 36389f18906445c47784a568dee672db0833c144..4c34ade6aa83816000d6417fd6c21c0e630d4640 100644 --- a/args.py +++ b/args.py @@ -56,13 +56,11 @@ def fetch_inputs(): parser.add_argument( '-max', '--max_contours', type=int, - default=15, + default=1, nargs='?', required=False, - help=("Number of contours that can be annotated at the same time." - "\nDue to restrictions with the module used," - "\nonly a fixed number of contours can be annotated at once." - "\nValue cannot exceed 50. Default value is '15'.\n\n") + help=("Default number of contours available at launcg." + "\nDue to restrictions with the module used, max number of contours is 99 per file.\n\n") ) parser.add_argument( @@ -92,11 +90,21 @@ def fetch_inputs(): type=str, nargs=2, required=False, - help=("Name of a file generated by a previous annitation," + help=("Name of a file generated by a previous annotation," "\nand name of the associated wavefile." "\nThis will open the interface with the contours from this file" "\nand enable modification of these contours.")) + parser.add_argument( + '-p', '--parameters', + type=str, + nargs="?", + required=False, + default=None, + help=("Path to a file with spectrogram parameters." + "\nMust come from a previous usage of PyAVA." + "\nParameters loaded from this file will overwrite default parameters.")) + # fetching arguments args = parser.parse_args() outputs = args.output @@ -113,6 +121,9 @@ def fetch_inputs(): try: assert (os.path.exists(outputs)), ( f"\nInputError: Could not find dir '{outputs}'.") + if isinstance(args.parameters, str): + assert (os.path.exists(args.parameters)), ( + f"\nInputError: Could not find dir '{args.parameters}'.") assert (os.path.exists(explore)), ( f"\nInputError: Could not find dir '{explore}'.") if modify_file: @@ -126,7 +137,7 @@ def fetch_inputs(): print(e) sys.exit(1) - return (explore, contour, resampl, outputs, modify_file, from_wav) + return (explore, contour, resampl, outputs, modify_file, from_wav, args.parameters) # if running `$python ARGS.py -h` for help. if __name__ == '__main__': diff --git a/interface.py b/interface.py index a6c01f1f0b86b7c2214ffa6546cb9b5cc6dff175..9ebcfa6bed8a76699f6536f4e9f904d27c289431 100644 --- a/interface.py +++ b/interface.py @@ -1,7 +1,9 @@ ##### IMPORTATIONS ##### import os +import json import numpy as np from tkinter import * +from tkinter import simpledialog as sd from tkinter import filedialog as fd from tkinter import ttk from matplotlib.backends.backend_tkagg import (NavigationToolbar2Tk, @@ -153,6 +155,8 @@ class App(object): coords_to_modify : dict Coordinates of points (from a previous annotation) that can be used as input to add modifications. + overwrite_parameters : str + Path to a file containing parameters to overwrite before launch Attributes ---------- @@ -236,8 +240,10 @@ class App(object): Loads default variables to local variables. submit(): Loads user inputs to local variables. - switch(self) + switch() Updates spectrogram displayed to PCEN (and conversely). + _rename_label(event): + A function to manually rename an item in listbox _frame_listbox_scroll(): Just a callable part of layout() _quit(): @@ -255,7 +261,8 @@ class App(object): NEW_SR, DIR_OUT, WAVEFILE, - coords_to_modify={}): + coords_to_modify={}, + overwrite_parameters=None): # init variables self.DIR = DIR @@ -265,12 +272,27 @@ class App(object): self.WAVEFILE = WAVEFILE self.NAME0 = 0 self.NAME1 = MAX_C - self.setup() - - # load audio data - self.load_audio() + self._default_pcen = False + + # check if parameters should be overwritten + if isinstance(overwrite_parameters, str): + with open(os.path.join(self.DIR_OUT, "..","last-parameters-used.json"), "r") as f: + parameter_dict = json.load(f) + + self._default_hop_length = parameter_dict["HOP_LENGTH"] + self._default_nfft = parameter_dict["NFFT"] + self._default_clipping = parameter_dict["CLIPPING"] + self.NEW_SR = parameter_dict["SR"] + self._default_pcen = parameter_dict["PCEN"] + + if self._default_pcen: + self.initial_text_pcen = "Switch to PCEN" + else: + self.initial_text_pcen = "Switch to Spectrogram" # init interface + self.setup() + self.load_audio() self.root = Tk() self.root.style = ttk.Style() self.root.style.theme_use('clam') @@ -288,9 +310,8 @@ class App(object): self.layout() self.axis.set_position(self._default_bounds) - # To avoid probles, disconnect matplotlib keypress - # and replace it with tkinter keypress. - self.figure.canvas.mpl_disconnect(self.klicker.key_press) + # To avoid problems, disconnect matplotlib keypress + self.figure.canvas.mpl_disconnect(self.klicker.key_press) self.root.bind('<Key>', self.get_key_pressed) # just to be sure @@ -390,39 +411,47 @@ class App(object): """ class EmptyObject(object): """ - Empty class that is just a hacky of creating an object that - can be used in matplotlib. + Empty class that is just a hacky way of creating an object + that can be used in matplotlib. """ pass # create key attribute and use it dummy_event = EmptyObject() dummy_event.key = event.char - self.klicker.get_key_event(dummy_event, show=False) - - # if a category is added. Update listbox and canvas. - if len(self.klicker.legend_labels) > self.listbox.size(): - self.listbox.insert( - self.listbox.size(), - self.klicker.legend_labels[-1]) - self.listbox.itemconfig( - self.listbox.size()-1, - { - 'bg': self.klicker.colors[ - (self.listbox.size()-1)%len(self.klicker.colors)], - 'selectbackground': mc.to_hex(tuple([min(0.1+x,1) - for x in mc.to_rgb( - self.klicker.colors[ - (self.listbox.size()-1)%len(self.klicker.colors)])])), - 'selectforeground': 'white'}) - self.listbox.select_clear(0, END) - self.listbox.select_set(self.listbox.size()-1) - self.listbox.see(self.listbox.size()-1) - self.listbox.activate(self.listbox.size()-1) - self.listbox.selection_anchor(self.listbox.size()-1) - self.axis.set_position(self._default_bounds) - self.figure.canvas.draw() + if dummy_event.key=="A": + self.klicker.add_category(False) + + # if a category is added. Update listbox and canvas. + if len(self.klicker.legend_labels) > self.listbox.size(): + self.listbox.insert( + self.listbox.size(), + self.klicker.legend_labels[-1]) + self.listbox.itemconfig( + self.listbox.size()-1, + { + 'bg': self.klicker.colors[ + (self.listbox.size()-1)%len(self.klicker.colors)], + 'selectbackground': mc.to_hex(tuple([min(0.1+x,1) + for x in mc.to_rgb( + self.klicker.colors[ + (self.listbox.size()-1)%len(self.klicker.colors)])])), + 'selectforeground': 'white'}) + self.listbox.select_clear(0, END) + self.listbox.select_set(self.listbox.size()-1) + self.listbox.see(self.listbox.size()-1) + self.listbox.activate(self.listbox.size()-1) + self.listbox.selection_anchor(self.listbox.size()-1) + self.axis.set_position(self._default_bounds) + self.figure.canvas.draw() + + elif dummy_event.key=="p" or dummy_event.key=="w": + self.toolbar.pan() + + elif dummy_event.key=="z": + self.toolbar.zoom() + def layout(self): """ This *long* function lays the structure of the tkinter interface @@ -450,7 +479,7 @@ class App(object): self.list_label = Label( self.root, width=self._default_left_panel_width, - text='Pick a line to draw.\n(Shift+a adds a new line)', + text='Pick a line to draw.\n(Shift+a adds a new line\nRight-click to rename item)', font=('calibre',10,'bold')) self.list_label.grid(row=2, column=0) @@ -522,7 +551,7 @@ class App(object): self.switch_view_button = Button( self.root, - text="Switch to PCEN", + text=self.initial_text_pcen, width=self._default_left_panel_width, command=self.switch) self.switch_view_button.grid(row=13, column=0) @@ -559,8 +588,6 @@ class App(object): Changes the focus to be on a new category, corresponding to the selected item in listbox widget. - ... - Parameters ---------- event : tkinter object @@ -577,7 +604,7 @@ class App(object): for legend_line in self.klicker.legend.get_lines(): legend_line.set_alpha(0.2) self.klicker.legend.get_lines()[self.klicker.current_line].set_alpha(1) - self.klicker.figure.canvas.draw() + self.klicker.figure.canvas.draw() def load_audio(self): """ @@ -720,6 +747,85 @@ class App(object): vmax=np.nanmax(self.spectrogram)) self.canvas.draw() + def _rename_label(self, event): + """ + A function that allows the user to rename a category + + Parameters + ---------- + event : tkinter object + event containing the item clicked in listbox widget. + + Returns + ------- + None : Shows a popup that ask for the name to give to the contour. + """ + self.listbox.selection_clear(0,END) + index_item = self.listbox.nearest(event.y) + save_config = self.listbox.itemconfig(index_item) + save_config = { + 'bg':save_config['background'][-1], + 'selectbackground':save_config['selectbackground'][-1], + 'selectforeground':save_config['selectforeground'][-1], + } + + # update selection + self.klicker.current_line = index_item + self.listbox.select_clear(0, END) + self.listbox.select_set(index_item) + self.listbox.see(index_item) + self.listbox.activate(index_item) + self.listbox.selection_anchor(index_item) + + # get new name from user + new_name = sd.askstring( + "Rename window", + f"Insert new name for '{self.klicker.legend_labels[index_item]}':" + ) + + if isinstance(new_name, str): + if new_name in self.klicker.legend_labels: + count=0 + for label in self.klicker.legend_labels: + if (label==new_name) or ("_".join(label.split("_")[:-1])==new_name): + count+=1 + new_name = new_name + f"_{count}" + + # replace spaces + new_name = new_name.replace(" ", "_") + + # destroy item + self.listbox.delete( + index_item) + old_name = self.klicker.legend_labels.pop(index_item) + + # handle klicker + self.klicker.legend_labels.insert(index_item, new_name) + self.klicker.set_legend() + self.klicker.current_line = index_item + if old_name in self.klicker.coords.keys(): + self.klicker.coords[new_name] = self.klicker.coords.pop(old_name) + self.klicker.update_lines() + + # insert item at the same index in listbox, with new name + self.listbox.insert( + index_item, + new_name) + self.listbox.itemconfig( + index_item, + save_config + ) + + # update selection + self.listbox.select_clear(0, END) + self.listbox.select_set(index_item) + self.listbox.see(index_item) + self.listbox.activate(index_item) + self.listbox.selection_anchor(index_item) + + self.axis.set_position(self._default_bounds) + self.figure.canvas.draw() + def _frame_listbox_scroll(self): """ Just a callable part of "layout" @@ -745,7 +851,8 @@ class App(object): self.klicker.colors[idx%len(self.klicker.colors)])])), 'selectforeground': 'white'}) self.listbox.pack(side="left", fill="y") - self.listbox.bind("<<ListboxSelect>>", self.link_select) + self.listbox.bind("<<ListboxSelect>>", self.link_select) + self.listbox.bind("<Button-3>", self._rename_label) self.listbox.select_set(0) self.scrollbar = Scrollbar(self.frame_list, orient="vertical") @@ -756,6 +863,7 @@ class App(object): def _quit(self): """ A function that saves coordinates of lines before closing app. + Also saves the last parameters in memory (for further use). ... @@ -778,8 +886,8 @@ class App(object): "HOP_LENGTH":self.HOP_LENGTH, "CLIPPING":self.CLIPPING }, - os.path.join(self.DIR_OUT, "parameters"), - os.path.basename(self.WAVEFILE)[:-4]+"-params.json" + os.path.join(self.DIR_OUT, ".."), + "last-parameters-used.json" ) # quit window diff --git a/last-parameters-used.json b/last-parameters-used.json new file mode 100644 index 0000000000000000000000000000000000000000..d56a73d059b5d7033ebfba704db2c51693738e2b --- /dev/null +++ b/last-parameters-used.json @@ -0,0 +1,7 @@ +{ + "PCEN": false, + "SR": 96000, + "NFFT": 2048, + "HOP_LENGTH": 512, + "CLIPPING": -80 +} \ No newline at end of file diff --git a/line_clicker/__pycache__/line_clicker.cpython-39.pyc b/line_clicker/__pycache__/line_clicker.cpython-39.pyc index 4fdac3d06e156ecb3508baef0ebe5e9db968a698..f63f3cb63553a38118e9935f9ef90867132fe0e9 100644 Binary files a/line_clicker/__pycache__/line_clicker.cpython-39.pyc and b/line_clicker/__pycache__/line_clicker.cpython-39.pyc differ diff --git a/outputs/SCW1807_20200713_064554-contours.json b/outputs/SCW1807_20200713_064554-contours.json index 55c8c0c2f4e1c0984a983b2fc70e77cbdce1e873..a8e371fae1d370ddd00b7930f2e39e34f9490c10 100644 --- a/outputs/SCW1807_20200713_064554-contours.json +++ b/outputs/SCW1807_20200713_064554-contours.json @@ -1,948 +1,250 @@ { - "Line1": [ + "invert_N": [ [ - 0.2556607438674337, - 16569.1151082533 + 0.25875445892991433, + 16543.49446379686 ], [ - 0.2804261968710533, - 16278.071710573218 + 0.38842553301349875, + 11307.172337705057 ], [ - 0.28339805123148765, - 15210.912585746242 + 0.4532610700552909, + 9280.8561126657 ], [ - 0.3081635042351073, - 14677.333023332758 + 0.501630438959485, + 9160.480891376234 ], [ - 0.34877884716104346, - 12712.790088992195 + 0.5633785694754776, + 9461.4189445999 ], [ - 0.39335666256755875, - 11160.558634698416 + 0.6457094101634677, + 11046.359358244545 ], [ - 0.43100015113306056, - 9656.83441335132 + 0.6971661855934617, + 12129.736349849745 ], [ - 0.4825122933805894, - 9074.74761799115 + 0.7743513487384523, + 12912.17528823128 ], [ - 0.5478930893101452, - 9341.537399197896 + 0.8268372596770459, + 13694.614226612812 ], [ - 0.6142645033598458, - 10287.428441658165 + 0.9060806938392365, + 13574.239005323345 ], [ - 0.682617153649836, - 11936.674361845304 + 0.9863532635100267, + 12771.737530060234 ], [ - 0.7252137328160617, - 12373.239458365431 + 1.0645675621636173, + 11868.923370389233 ], [ - 0.7846508200247487, - 13003.833486672276 + 1.1777724681096038, + 9361.106260192011 ], [ - 0.825266162950685, - 13610.173898505785 + 1.2899482385469903, + 8357.979416113123 ], [ - 0.856965942795318, - 13610.173898505785 + 1.3342010654167848, + 7936.66614159999 ], [ - 0.8995625219615438, - 13464.652199665743 - ], - [ - 0.926309211205453, - 13294.876884352361 - ], - [ - 0.9570183729299413, - 12931.072637252259 - ], - [ - 0.9649433178910996, - 12882.565404305577 - ], - [ - 0.9926806252551535, - 12688.536472518856 - ], - [ - 1.0115023695379044, - 12470.253924258792 - ], - [ - 1.0303241138206554, - 12276.22499247207 - ], - [ - 1.0521177124638406, - 11960.927978318647 - ], - [ - 1.1541513788387534, - 9923.624194558062 - ], - [ - 1.1729731231215044, - 9632.580796877977 - ], - [ - 1.1794070626717919, - 9479.66604006841 - ], - [ - 1.2234946472488883, - 9001.986768571129 - ], - [ - 1.267081844535259, - 8565.421672051005 - ], - [ - 1.3205752230230774, - 8104.60295905754 - ], - [ - 1.3483125303871313, - 7571.023396644054 - ], - [ - 1.3631718021893031, - 6988.936601283886 - ], - [ - 1.3810029283519092, - 6188.567257663657 - ], - [ - 1.3988340545145153, - 5752.002161143532 + 1.390803518389778, + 5830.099769034323 ] ], - "Line2": [ - [ - 1.2314195922100466, - 14095.246227972591 - ], - [ - 1.2413257734114946, - 13173.608801985662 - ], - [ - 1.262128753934535, - 11863.913512425286 - ], - [ - 1.2789692619769963, - 10893.768853491674 - ], - [ - 1.3116596599417742, - 10772.500771124971 - ], + "invert_N_1": [ [ - 1.336425112945394, - 10772.500771124971 + 0.37263093871064235, + 23846.21083835054 ], [ - 1.3483125303871313, - 10311.682058131508 + 0.44654468833897776, + 18624.0282993784 ], [ - 1.3601999478288687, - 10287.428441658165 + 0.49556911411287374, + 18211.20754530945 ], [ - 1.3740686015108956, - 11160.558634698416 + 0.6366086159546975, + 21493.13254015756 ], [ - 1.390909109553357, - 12421.74669131211 + 0.6878957075335426, + 23928.774989164325 ], [ - 1.4027965269950944, - 13513.159432612425 + 0.7271152481526594, + 24795.69857270911 ], [ - 1.4166651806771213, - 14192.260693865952 + 0.8138507706757061, + 26797.879229943486 ], [ - 1.432515070599438, - 15162.405352799564 + 0.8425112042050606, + 27375.82828564001 ], [ - 1.4413724760470132, - 16287.542250441644 + 0.8689089719294661, + 27293.26413482622 ], [ - 1.4490057835548567, - 19078.485781877196 + 0.9375431680129205, + 26446.981588984883 ], [ - 1.4515679818356721, - 19196.707818930037 + 1.0008978105514938, + 25208.519326778056 ], [ - 1.4733184911158894, - 18661.618655692724 + 1.0914044427494556, + 22463.261312219576 ], [ - 1.4937878542675378, - 17396.273242381794 + 1.154759085288029, + 19924.41367469557 ], [ - 1.507527807781656, - 16956.60405592277 + 1.1856821846223324, + 18665.31037478529 ], [ - 1.5294099559708072, - 16899.255901167242 + 1.2324439445912794, + 17715.82264042672 ] ], - "Line3": [ + "invert_N_2": [ [ - 0.37038441199132144, - 23845.111348935927 + 0.5011509232640762, + 27344.786753383632 ], [ - 0.4050533169552495, - 21890.595023197493 + 0.5487797158111636, + 27832.33775911113 ], [ - 0.4152003135300576, - 20611.713970553825 + 0.6271367616144365, + 31523.795373905064 ], [ - 0.43211197448807126, - 19139.794268454512 + 0.6755337604929286, + 34901.827342159886 ], [ - 0.4811557912663108, - 18174.60102117627 + 0.6916660934524259, + 36120.70485647864 ], [ - 0.5589494316731736, - 18681.327475997346 + 0.7139440770631604, + 36886.85643690757 ], [ - 0.5961550857808036, - 19694.780385639497 + 0.8138109001457632, + 40230.06333332471 ], [ - 0.6316695737926322, - 21287.34924364859 + 0.8460755660647579, + 41031.03998559132 ], [ - 0.6680296448523616, - 23072.956751113335 + 0.8960089776060592, + 40439.013764350784 ], [ - 0.6891692210498787, - 23965.760504845708 + 0.9735978170779275, + 38384.33452592775 ], [ - 0.7111543802952963, - 24496.61679084874 + 1.0373589425845124, + 36643.08093404381 ], [ - 0.7365218717323168, - 24930.953752123947 + 1.0872923541258137, + 34205.32590540632 ], [ - 0.7593526140256353, - 25510.06970049089 + 1.12647087702745, + 31523.795373905064 ], [ - 0.8320727561450939, - 27295.677207955632 - ], - [ - 0.8811165729233335, - 27102.638558499988 - ], - [ - 0.8980282338813471, - 26885.47007786238 - ], - [ - 0.9267780575099703, - 26692.431428406733 - ], - [ - 0.9614469624738983, - 25847.88733703827 - ], - [ - 1.0054172809647337, - 25075.73273921568 - ], - [ - 1.0510787655513707, - 23869.241180117882 - ], - [ - 1.1035049145212128, - 22011.24417910727 - ], - [ - 1.1339459042456375, - 20828.882451191428 - ], - [ - 1.1474752330120483, - 19936.07869745906 - ], - [ - 1.2108939616045995, - 18053.951865266492 - ], - [ - 1.2464084496164283, - 17450.70608571759 + 1.2117417798133647, + 27101.01125051988 ] ], - "Line4": [ - [ - 1.220195375131507, - 26813.08058431651 - ], - [ - 1.1821441379759765, - 27898.92298750453 - ], - [ - 1.177916222736473, - 28550.42842941734 - ], - [ - 1.079828589179994, - 34462.23706899656 - ], - [ - 1.024020108018549, - 37044.129005465846 - ], - [ - 0.9606013794259978, - 38853.866344112546 - ], - [ - 0.9242413083662684, - 40036.22807202839 - ], - [ - 0.9115575626477581, - 40374.04570857577 - ], - [ - 0.8946459016897446, - 40277.52638384795 - ], - [ - 0.8608225797737172, - 40904.9019945788 - ], - [ - 0.8160066782349811, - 40398.17553975773 - ], + "n": [ [ - 0.7771098580315496, - 38853.866344112546 + 0.8091567176081755, + 46866.257456874 ], [ - 0.7500512004987279, - 37840.41343447039 + 0.8199952219812153, + 47257.77728018231 ], [ - 0.720455793822204, - 37019.99917428389 + 0.8430558695834277, + 47845.05701514477 ], [ - 0.6917059701935808, - 35861.76727755 + 0.8624268135692862, + 47859.03986597721 ], [ - 0.6739487261876664, - 34703.535380816116 - ], - [ - 0.6638017296128582, - 33714.212302355925 - ], - [ - 0.6426621534153412, - 32242.29260025661 - ], - [ - 0.6265960755052282, - 31325.359015342277 - ], - [ - 0.5995374179724065, - 29829.30948206101 - ], - [ - 0.5682508452000812, - 28453.90910468952 - ], - [ - 0.5361186893798553, - 27585.235182139102 - ], - [ - 0.49553070308062247, - 27199.157883227806 - ], - [ - 0.45325155068558837, - 27826.53349395866 - ], - [ - 0.41520031353005765, - 30722.113235793382 + 0.8822589705071888, + 47481.50289350134 ] ], - "Line5": [ - [ - 1.4205985574839692, - 28502.16876705343 - ], - [ - 1.4036868965259557, - 27609.36501332106 - ], + "n_1": [ [ - 1.3960766490948493, - 26716.56125958869 + 0.7981661995571241, + 33032.82951685946 ], [ - 1.3952310660469487, - 25775.497843492405 + 0.8237910808460172, + 33858.07615751977 ], [ - 1.386775235567942, - 24906.82392094199 + 0.8464874042733224, + 34184.90651025652 ], [ - 1.3800105711847364, - 23603.813037116368 + 0.8750408434238032, + 34029.66209270656 ], [ - 1.3757826559452329, - 22590.360127474218 - ], - [ - 1.3707091576578287, - 21359.73873719446 - ], - [ - 1.3664812424183255, - 20828.882451191428 - ], - [ - 1.348723998412411, - 20273.89633400644 - ], - [ - 1.3444960831729078, - 20949.53160710121 - ], - [ - 1.3343490865980996, - 21190.82991892077 - ], - [ - 1.3089815951610793, - 21407.99839955837 - ], - [ - 1.2785406054366546, - 21673.426542559886 - ], - [ - 1.2658568597181443, - 22807.528608111817 - ], - [ - 1.256555446191237, - 24737.9151026683 - ], - [ - 1.24809961571223, - 26089.185648857834 + 0.8874872143355512, + 33866.24691633818 ] ], - "Line6": [ - [ - 2.0661243541857277, - 13285.951796092879 - ], - [ - 2.039050804089352, - 13986.16628625902 - ], - [ - 2.028385466172598, - 14382.954497353168 - ], - [ - 1.9766995978067898, - 14756.402225441776 - ], - [ - 1.960291385627168, - 15153.190436535922 - ], - [ - 1.8150787078375163, - 15573.319130635608 - ] - ], - "Line7": [ - [ - 2.234323175389529, - 7952.965023630615 - ], - [ - 2.2500756801682016, - 8003.241178682962 - ], - [ - 2.2694633783573366, - 8832.797737046665 - ], - [ - 2.2912745388201143, - 9913.735070672095 - ], - [ - 2.3385320531561318, - 11824.228962661231 - ], - [ - 2.3979068788603586, - 13759.860932176538 - ], - [ - 2.426988426144062, - 14212.346327647649 - ], - [ - 2.480304596164184, - 13910.689397333574 - ], - [ - 2.517868261405634, - 13332.513614231599 - ], - [ - 2.544526346415695, - 12628.647443498761 - ], - [ - 2.5869369362044288, - 12301.852435658513 - ], - [ - 2.6257123325826996, - 11723.676652556538 - ], - [ - 2.6669111912346124, - 10994.672404297527 - ], - [ - 2.7262860169388397, - 10215.39200098617 - ], - [ - 2.7844491115062455, - 9762.90660551506 - ], - [ - 2.8280714324318, - 9536.663907779503 - ], - [ - 2.9383389658825076, - 9184.730822413085 - ], - [ - 3.1188869052688313, - 8606.55503931111 - ], - [ - 3.2024963537094777, - 8254.62195394469 - ], - [ - 3.2255192453090755, - 7927.826946104442 - ], - [ - 3.3127638871601848, - 7450.20347310716 - ], - [ - 3.3345750476229625, - 6846.889612479012 - ], - [ - 3.370926981727591, - 6092.747286693827 - ], - [ - 3.398796797874473, - 5841.366511432098 - ], - [ - 3.403643722421757, - 6168.161519272346 - ], - [ - 3.41939622720043, - 6243.5757518508635 - ], - [ - 3.421819689474072, - 5841.366511432098 - ], - [ - 3.4618068169891636, - 6092.747286693827 - ], - [ - 3.5042174067778973, - 6520.094604638764 - ], - [ - 3.5236051049670323, - 7450.20347310716 - ], - [ - 3.5587453079348403, - 7776.998480947406 - ], - [ - 3.602367628860395, - 8053.517333735306 - ], - [ - 3.627813982733635, - 8204.345798892344 - ], - [ - 3.639931294101845, - 8355.174264049381 - ], - [ - 3.6738597659328316, - 8405.450419101726 - ], - [ - 3.7077882377638183, - 8480.864651680246 - ], - [ - 3.7368697850475217, - 8455.726574154072 - ], - [ - 3.752622289826194, - 9410.97352014864 - ], - [ - 3.7635278700575827, - 8480.864651680246 - ] - ], - "Line8": [ - [ - 2.236746637663171, - 15745.76905674419 - ], - [ - 2.257346066989127, - 16072.564064584436 - ], - [ - 2.2743103029046208, - 17932.781801521225 - ], - [ - 2.294909732230577, - 19843.27569351036 - ], - [ - 2.3191443549669963, - 21904.598050656532 - ], - [ - 2.3458024399770574, - 24368.129648221468 - ], - [ - 2.385789567492149, - 27208.732408678996 - ], - [ - 2.4112359213653893, - 28214.25550972591 - ], - [ - 2.460916897975049, - 28214.25550972591 - ], - [ - 2.4996922943533195, - 27284.146641257514 - ], - [ - 2.5360442284579485, - 25675.309679582453 - ], - [ - 2.6257123325826996, - 23387.74462470073 - ], - [ - 2.686298889423748, - 21552.66496529011 - ], - [ - 2.742038521717512, - 20119.79454629826 - ], - [ - 2.8208010456108745, - 19038.857212672832 - ], - [ - 2.892293182683311, - 18536.095662149375 - ], - [ - 2.979537824534421, - 17983.05795657357 - ], - [ - 3.065570735248709, - 17480.296406050114 - ], - [ - 3.162509226194386, - 16826.70639036962 - ], - [ - 3.2109784716672243, - 16248.530607267647 - ], - [ - 3.2303661698563597, - 15796.045211796536 - ], - [ - 3.3127638871601848, - 14840.798265801968 - ], - [ - 3.3491158212648138, - 13106.270916496043 - ], - [ - 3.3927381421903684, - 11849.367040187402 - ], - [ - 3.404855453558578, - 12301.852435658513 - ], - [ - 3.41939622720043, - 12301.852435658513 - ], - [ - 3.4230314206108927, - 11572.8481873995 - ], - [ - 3.458171623578701, - 11899.643195239749 - ], - [ - 3.498158751093792, - 12804.61398618197 - ], - [ - 3.5163347181461067, - 13910.689397333574 - ], - [ - 3.5332989540616, - 15142.455196116043 - ], - [ - 3.5732860815766916, - 15745.76905674419 - ], - [ - 3.625390520459993, - 16173.116374689129 - ], - [ - 3.6241787893231723, - 16525.049460055547 - ], - [ - 3.6993061198060717, - 17002.672933052832 - ], - [ - 3.7368697850475217, - 16851.844467895797 - ] - ], - "Line9": [ - [ - 2.3116012235318193, - 31764.84090906388 - ], - [ - 2.3559597629309463, - 37557.48611007064 - ], - [ - 2.3959906399496704, - 41342.01430806173 - ], - [ - 2.4154651206614823, - 42346.07280956957 - ], - [ - 2.4630694068459116, - 42075.74936685592 - ], - [ - 2.5031002838646357, - 40762.74978796105 - ], - [ - 2.5160832710058436, - 39449.75020906619 - ], - [ - 2.5864077846873865, - 36823.75105127645 - ], - [ - 2.5928992782579905, - 36051.39835780888 - ], - [ - 2.6123737589698024, - 35549.36910705496 - ], - [ - 2.651322720393426, - 33541.25210403929 - ], - [ - 2.6913535974121503, - 31842.076178410636 - ], - [ - 2.774661098234901, - 29370.547559314415 - ], - [ - 2.897999476076376, - 27632.753999012384 - ], - [ - 3.087334705218991, - 26010.813342730493 - ], - [ - 3.180379446397648, - 24929.5195718759 - ], - [ - 3.2258199013918754, - 23848.2258010213 - ], - [ - 3.316700811380331, - 21994.579336699135 - ], - [ - 3.3556497728039547, - 19291.34490956265 - ], - [ - 3.395680649822679, - 17630.786618607373 - ], - [ - 3.401090227798182, - 18248.66877338143 - ], - [ - 3.417318961724692, - 18325.904042728187 - ], - [ - 3.423810455295296, - 17514.93371458724 - ], - [ - 3.4692509102895235, - 17978.34533066778 - ], - [ - 3.503872209332745, - 19716.13889096981 - ], - [ - 3.535247761590664, - 22728.314395493326 - ], - [ - 3.585015878965294, - 23693.755262327788 - ], + "n_2": [ [ - 3.6218010091987165, - 24118.549243734953 + 0.8072955488925098, + 20071.56535619266 ], [ - 3.6250467559840187, - 24659.19612916225 + 0.8279188810699657, + 20403.612203459543 ], [ - 3.6845521137145547, - 25470.166457303196 + 0.849016312837708, + 20551.188580022605 ], [ - 3.739729809064688, - 25199.843014589547 + 0.876751138869459, + 20403.612203459543 ] ] } \ No newline at end of file