Package: hrosailing
hrosailing![Still in development. In particular we do not guarantee backwards compatibility to the versions 0.x.x.]!Thehrosailingpackage provides various tools and interfaces to
visualize, create and work with polar (performance) diagrams.The main interface being thePolarDiagraminterface for
the creation of custom polar diagrams, which is compatible with
the functionalities of this package.hrosailingalso provides some
pre-implemented classes inheriting fromPolarDiagramwhich can be used as well.The package contains a data processing framework, centered around thePolarPipelineclass, to generate polar diagrams from raw data.pipelinecomponentsprovides many out of the box parts for
the aforementioned framework as well as the possibility to easily
create own ones.The package also provides many navigational usages of polar
(performance) diagrams withcruising.You can find the documentationhere.
See also the examples below for some showcases.InstallationThe recommended way to installhrosailingis withpip:pip install hrosailingIt has the following dependencies:numpyversion 1.22.0scipyversion 1.9.1matplotlibversion 3.4.3For some features it might be necessary to also use:pynmea2version 1.18.0pandasversion 1.3.3netCDF4version 1.6.1meteostatversion 1.6.5Thehrosailingpackage might also be compatible (in large) with
other versions of Python, together with others versions of some
of the used packages. However, this has not been tested properly.ExamplesIn the following we showcase some of the capabilities ofhrosailing.
All definitions of an example code might be used in the succeeding examples.Serialization ofPolarDiagramobjectsFor a first example, lets say we obtained some table with polar
performance diagram data, like the one availablehere,
and call the file testdata.csv.importhrosailing.polardiagramaspol# the format of `testdata.csv` is a tab separated one# supported by the keyword `array`pd=pol.from_csv("testdata.csv",fmt="array")# for symmetric resultspd=pd.symmetrize()# serializes the polar diagram to a .csv file# in the style of an intern formatpd.to_csv("polar_diagram.csv")# the default format is the intern format `hro`pd2=pol.from_csv("polar_diagram.csv")Currently serialization is only supported for some csv-formats, see alsocsv-format-examplesfor example files for the currently supported formats. See alsoIssue #1for a plan
to add more serialization options.Visualizing polar diagramsimportmatplotlib.pyplotaspltimporthrosailing.plottingasplotws=[10,20,30]plt.subplot(2,2,1,projection="hro polar").plot(pd,ws=ws)plt.subplot(2,2,2,projection="hro polar").plot(pd,ws=ws,use_convex_hull=True)plt.subplot(2,2,3,projection="hro flat").plot(pd,ws=ws)plt.subplot(2,2,4,projection="hro color gradient").plot(pd)plt.show()3d visualization is also supported.plot.plot_3d(pd)plt.show()Iterate over polar diagram dataWe can also directly iterate and/or evaluate the wind angles,
wind speeds and boat speeds of the polar diagram.importnumpyasnpdefrandom_shifted_pt(pt,mul):pt=np.array(pt)rand=np.random.random(pt.shape)-0.5rand*=np.array(mul)random_pt=pt+randforiinrange(3):random_pt[i]=max(random_pt[i],0)returnrandom_ptdata=np.array([random_shifted_pt([ws,wa,pd(ws,wa)],[10,5,2])forwainpd.wind_anglesforwsinpd.wind_speedsfor_inrange(6)])data=data[np.random.choice(len(data),size=500)]Creating polar diagrams from raw dataimporthrosailing.pipelineaspipeimporthrosailing.processingasprocpol_pips=[pipe.PolarPipeline(data_handler=proc.ArrayHandler(),imputator=proc.RemoveOnlyImputator(),extension=pipe.TableExtension()),pipe.PolarPipeline(data_handler=proc.ArrayHandler(),imputator=proc.RemoveOnlyImputator(),extension=pipe.PointcloudExtension()),pipe.PolarPipeline(data_handler=proc.ArrayHandler(),imputator=proc.RemoveOnlyImputator(),extension=pipe.CurveExtension())]# here `data` is treated as some obtained measurements given as# a numpy.ndarraypds=[pol_pip([(data,["Wind speed","Wind angle","Boat speed"])]).polardiagramforpol_pipinpol_pips]#fori,pdinenumerate(pds):plt.subplot(1,3,i+1,projection="hro polar").plot(pd,ws=ws)plt.tight_layout()plt.show()If we are unhappy with the default behaviour of the pipelines,
we can customize one or more components of it.CustomizingPolarPipelineimporthrosailing.modelsasmodelsclassMyInfluenceModel(models.InfluenceModel):defremove_influence(self,data):tws=np.array(data["TWS"])twa=np.array(data["TWA"])bsp=np.array(data["BSP"])returnnp.array([tws,(twa+90)%360,bsp**2]).transpose()defadd_influence(self,pd,influence_data:dict):passclassMyFilter(proc.Filter):deffilter(self,wts):returnnp.logical_or(wts<=0.2,wts>=0.8)defmy_model_func(ws,wa,*params):returnparams[0]+params[1]*wa+params[2]*ws+params[3]*ws*wamy_regressor=proc.LeastSquareRegressor(model_func=my_model_func,init_vals=(1,2,3,4))my_extension=pipe.CurveExtension(regressor=my_regressor)defmy_norm(pt):returnnp.linalg.norm(pt,axis=1)**4my_pol_pip=pipe.PolarPipeline(data_handler=proc.ArrayHandler(),imputator=proc.RemoveOnlyImputator(),influence_model=MyInfluenceModel(),post_weigher=proc.CylindricMeanWeigher(radius=2,norm=my_norm),extension=my_extension,post_filter=MyFilter())out=my_pol_pip([(data,["Wind speed","Wind angle","Boat speed"])])my_pd=out.polardiagramThe customizations above are arbitrary and lead to comparably bad results:plot.plot_polar(my_pd,ws=ws)plt.show()Including Influences and Weather modelsFor the next example we initialize a simple influence model and
a random weather model.fromdatetimeimporttimedeltafromdatetimeimportdatetimeasdtclassMyInfluenceModel(models.InfluenceModel):defremove_influence(self,data):passdefadd_influence(self,pd,data,**kwargs):ws,wa,wave_height=np.array([data["TWS"],data["TWA"],data["WVHGT"]])twa=(wa+5)%360tws=ws+ws/wave_heightreturn[pd(ws,wa)forws,wainzip(tws,twa)]im=MyInfluenceModel()n,m,k,l=500,50,40,3data=20*(np.random.random((n,m,k,l)))wm=models.GriddedWeatherModel(data=data,times=[dt.now()+i*timedelta(hours=1)foriinrange(n)],lats=np.linspace(40,50,m),lons=np.linspace(40,50,k),attrs=["TWS","TWA","WVHGT"])Computing Isochronesimporthrosailing.cruisingascruisestart=(42.5,43.5)isochrones=[cruise.isochrone(pd=pd,start=start,start_time=dt.now(),direction=direction,wm=wm,im=im,total_time=1/3)fordirectioninrange(0,360,5)]coordinates,_=zip(*isochrones)lats,longs=zip(*coordinates)forlat,longincoordinates:plt.plot([start[0],lat],[start[1],long],color="lightgray")plt.plot(lats,longs,ls="",marker=".")plt.show()LicenseThehrosailingpackage is published under theApache 2.0 License,
see alsoLicenseCitingAlso seeCitation.TODOTodo