apsg 1.3.0__py3-none-any.whl → 1.3.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- apsg/__init__.py +1 -2
- apsg/database/_alchemy.py +92 -52
- apsg/plotting/_stereogrid.py +4 -6
- apsg/plotting/_stereonet.py +2 -4
- apsg-1.3.1.dist-info/METADATA +187 -0
- {apsg-1.3.0.dist-info → apsg-1.3.1.dist-info}/RECORD +10 -13
- {apsg-1.3.0.dist-info → apsg-1.3.1.dist-info}/WHEEL +1 -1
- apsg-1.3.1.dist-info/entry_points.txt +2 -0
- apsg-1.3.1.dist-info/licenses/LICENSE +22 -0
- AUTHORS.md +0 -9
- CHANGELOG.md +0 -304
- CONTRIBUTING.md +0 -91
- apsg-1.3.0.dist-info/AUTHORS.md +0 -9
- apsg-1.3.0.dist-info/METADATA +0 -141
- apsg-1.3.0.dist-info/entry_points.txt +0 -3
apsg/__init__.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
|
|
3
|
-
import importlib.metadata
|
|
4
3
|
from apsg.math import (
|
|
5
4
|
Vector3 as vec,
|
|
6
5
|
Vector2 as vec2,
|
|
@@ -99,6 +98,6 @@ __all__ = (
|
|
|
99
98
|
"quicknet",
|
|
100
99
|
)
|
|
101
100
|
|
|
102
|
-
__version__ =
|
|
101
|
+
__version__ = "1.3.1"
|
|
103
102
|
__author__ = "Ondrej Lexa"
|
|
104
103
|
__email__ = "lexa.ondrej@gmail.com"
|
apsg/database/_alchemy.py
CHANGED
|
@@ -13,7 +13,17 @@ from apsg.feature._container import LineationSet, FoliationSet
|
|
|
13
13
|
|
|
14
14
|
from sqlalchemy import create_engine, event
|
|
15
15
|
from sqlalchemy.orm import sessionmaker
|
|
16
|
-
from sqlalchemy import
|
|
16
|
+
from sqlalchemy import (
|
|
17
|
+
Column,
|
|
18
|
+
Float,
|
|
19
|
+
ForeignKey,
|
|
20
|
+
Integer,
|
|
21
|
+
String,
|
|
22
|
+
Table,
|
|
23
|
+
Text,
|
|
24
|
+
text,
|
|
25
|
+
UniqueConstraint,
|
|
26
|
+
)
|
|
17
27
|
from sqlalchemy.orm import relationship
|
|
18
28
|
from sqlalchemy.ext.declarative import declarative_base
|
|
19
29
|
|
|
@@ -24,24 +34,31 @@ metadata = Base.metadata
|
|
|
24
34
|
class Meta(Base):
|
|
25
35
|
__tablename__ = "meta"
|
|
26
36
|
|
|
27
|
-
id = Column(Integer, primary_key=True)
|
|
37
|
+
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
28
38
|
name = Column(String(16), nullable=False)
|
|
29
39
|
value = Column(Text)
|
|
30
40
|
|
|
41
|
+
def __repr__(self):
|
|
42
|
+
return "Meta:{}={}".format(self.name, self.value)
|
|
43
|
+
|
|
31
44
|
|
|
32
45
|
class Site(Base):
|
|
33
46
|
__tablename__ = "sites"
|
|
34
47
|
|
|
35
|
-
id = Column(Integer, primary_key=True)
|
|
48
|
+
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
36
49
|
id_units = Column(ForeignKey("units.id"), nullable=False, index=True)
|
|
37
|
-
name = Column(String(16), nullable=False
|
|
50
|
+
name = Column(String(16), nullable=False)
|
|
38
51
|
x_coord = Column(Float, server_default=text("NULL"))
|
|
39
52
|
y_coord = Column(Float, server_default=text("NULL"))
|
|
40
53
|
description = Column(Text)
|
|
41
54
|
|
|
42
|
-
unit = relationship("Unit", back_populates="sites")
|
|
55
|
+
unit = relationship("Unit", back_populates="sites", cascade="save-update")
|
|
43
56
|
|
|
44
|
-
structdata = relationship(
|
|
57
|
+
structdata = relationship(
|
|
58
|
+
"Structdata", back_populates="site", cascade="all, delete-orphan"
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
__table_args__ = (UniqueConstraint("name", name="_site_name_uc"),)
|
|
45
62
|
|
|
46
63
|
def __repr__(self):
|
|
47
64
|
return "Site:{} ({})".format(self.name, self.unit.name)
|
|
@@ -50,16 +67,22 @@ class Site(Base):
|
|
|
50
67
|
tagged = Table(
|
|
51
68
|
"tagged",
|
|
52
69
|
metadata,
|
|
53
|
-
Column("id", Integer, autoincrement=True),
|
|
54
|
-
Column("id_tags", Integer, ForeignKey("tags.id"),
|
|
55
|
-
Column(
|
|
70
|
+
Column("id", Integer, primary_key=True, autoincrement=True),
|
|
71
|
+
Column("id_tags", Integer, ForeignKey("tags.id"), nullable=False, index=True),
|
|
72
|
+
Column(
|
|
73
|
+
"id_structdata",
|
|
74
|
+
Integer,
|
|
75
|
+
ForeignKey("structdata.id"),
|
|
76
|
+
nullable=False,
|
|
77
|
+
index=True,
|
|
78
|
+
),
|
|
56
79
|
)
|
|
57
80
|
|
|
58
81
|
|
|
59
82
|
class Attached(Base):
|
|
60
83
|
__tablename__ = "attach"
|
|
61
84
|
|
|
62
|
-
id = Column(Integer, primary_key=True)
|
|
85
|
+
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
63
86
|
id_structdata_planar = Column(
|
|
64
87
|
ForeignKey("structdata.id"), nullable=False, index=True
|
|
65
88
|
)
|
|
@@ -74,7 +97,7 @@ class Attached(Base):
|
|
|
74
97
|
class Structdata(Base):
|
|
75
98
|
__tablename__ = "structdata"
|
|
76
99
|
|
|
77
|
-
id = Column(Integer, primary_key=True)
|
|
100
|
+
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
78
101
|
id_sites = Column(ForeignKey("sites.id"), nullable=False, index=True)
|
|
79
102
|
id_structype = Column(ForeignKey("structype.id"), nullable=False, index=True)
|
|
80
103
|
azimuth = Column(Float, nullable=False, server_default=text("0"))
|
|
@@ -82,9 +105,13 @@ class Structdata(Base):
|
|
|
82
105
|
description = Column(Text)
|
|
83
106
|
|
|
84
107
|
site = relationship("Site", back_populates="structdata")
|
|
85
|
-
structype = relationship(
|
|
108
|
+
structype = relationship(
|
|
109
|
+
"Structype", back_populates="structdata", cascade="save-update"
|
|
110
|
+
)
|
|
86
111
|
|
|
87
|
-
tags = relationship(
|
|
112
|
+
tags = relationship(
|
|
113
|
+
"Tag", secondary=tagged, back_populates="structdata", cascade="save-update"
|
|
114
|
+
)
|
|
88
115
|
|
|
89
116
|
attach_planar = relationship(
|
|
90
117
|
"Attached", backref="planar", primaryjoin=id == Attached.id_structdata_planar
|
|
@@ -102,7 +129,7 @@ class Structdata(Base):
|
|
|
102
129
|
class Structype(Base):
|
|
103
130
|
__tablename__ = "structype"
|
|
104
131
|
|
|
105
|
-
id = Column(Integer, primary_key=True)
|
|
132
|
+
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
106
133
|
pos = Column(Integer, nullable=False, server_default=text("0"))
|
|
107
134
|
structure = Column(String(16), nullable=False)
|
|
108
135
|
description = Column(Text)
|
|
@@ -112,6 +139,8 @@ class Structype(Base):
|
|
|
112
139
|
|
|
113
140
|
structdata = relationship("Structdata", back_populates="structype")
|
|
114
141
|
|
|
142
|
+
__table_args__ = (UniqueConstraint("structure", name="_structype_structure_uc"),)
|
|
143
|
+
|
|
115
144
|
def __repr__(self):
|
|
116
145
|
return "Type:{}".format(self.structure)
|
|
117
146
|
|
|
@@ -119,13 +148,15 @@ class Structype(Base):
|
|
|
119
148
|
class Tag(Base):
|
|
120
149
|
__tablename__ = "tags"
|
|
121
150
|
|
|
122
|
-
id = Column(Integer, primary_key=True)
|
|
151
|
+
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
123
152
|
pos = Column(Integer, nullable=False, server_default=text("0"))
|
|
124
153
|
name = Column(String(16), nullable=False)
|
|
125
154
|
description = Column(Text)
|
|
126
155
|
|
|
127
156
|
structdata = relationship("Structdata", secondary=tagged, back_populates="tags")
|
|
128
157
|
|
|
158
|
+
__table_args__ = (UniqueConstraint("name", name="_tag_name_uc"),)
|
|
159
|
+
|
|
129
160
|
def __repr__(self):
|
|
130
161
|
return "Tag:{}".format(self.name)
|
|
131
162
|
|
|
@@ -133,13 +164,15 @@ class Tag(Base):
|
|
|
133
164
|
class Unit(Base):
|
|
134
165
|
__tablename__ = "units"
|
|
135
166
|
|
|
136
|
-
id = Column(Integer, primary_key=True)
|
|
167
|
+
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
137
168
|
pos = Column(Integer, nullable=False, server_default=text("0"))
|
|
138
169
|
name = Column(String(60), nullable=False)
|
|
139
170
|
description = Column(Text)
|
|
140
171
|
|
|
141
172
|
sites = relationship("Site", back_populates="unit")
|
|
142
173
|
|
|
174
|
+
__table_args__ = (UniqueConstraint("name", name="_unit_name_uc"),)
|
|
175
|
+
|
|
143
176
|
def __repr__(self):
|
|
144
177
|
return "Unit:{}".format(self.name)
|
|
145
178
|
|
|
@@ -203,7 +236,8 @@ class SDBSession:
|
|
|
203
236
|
Keyword Args:
|
|
204
237
|
create (bool): if True existing sdbfile will be deleted
|
|
205
238
|
and new database will be created
|
|
206
|
-
autocommit(bool): if True, each operation is autocommitted
|
|
239
|
+
autocommit(bool): if True, each operation is autocommitted.
|
|
240
|
+
Default False
|
|
207
241
|
|
|
208
242
|
Example:
|
|
209
243
|
>>> db = SDBSession('database.sdb', create=True)
|
|
@@ -277,7 +311,7 @@ class SDBSession:
|
|
|
277
311
|
|
|
278
312
|
def meta(self, name, **kwargs):
|
|
279
313
|
"""
|
|
280
|
-
|
|
314
|
+
Query Meta when no kwargs or insert/update when kwargs provided
|
|
281
315
|
|
|
282
316
|
Args:
|
|
283
317
|
name (str): meta name
|
|
@@ -301,7 +335,7 @@ class SDBSession:
|
|
|
301
335
|
|
|
302
336
|
def site(self, name, **kwargs):
|
|
303
337
|
"""
|
|
304
|
-
|
|
338
|
+
Query Site when no kwargs or insert/update when kwargs provided
|
|
305
339
|
|
|
306
340
|
Args:
|
|
307
341
|
name (str): site name
|
|
@@ -328,7 +362,7 @@ class SDBSession:
|
|
|
328
362
|
|
|
329
363
|
def unit(self, name, **kwargs):
|
|
330
364
|
"""
|
|
331
|
-
|
|
365
|
+
Query Unit when no kwargs or insert/update when kwargs provided
|
|
332
366
|
|
|
333
367
|
Args:
|
|
334
368
|
name (str): unit name
|
|
@@ -352,7 +386,7 @@ class SDBSession:
|
|
|
352
386
|
|
|
353
387
|
def tag(self, name, **kwargs):
|
|
354
388
|
"""
|
|
355
|
-
|
|
389
|
+
Query Tag when no kwargs or insert/update when kwargs provided
|
|
356
390
|
|
|
357
391
|
Args:
|
|
358
392
|
name (str): tag name
|
|
@@ -376,14 +410,14 @@ class SDBSession:
|
|
|
376
410
|
|
|
377
411
|
def structype(self, structure, **kwargs):
|
|
378
412
|
"""
|
|
379
|
-
|
|
413
|
+
Query Structype when no kwargs or insert/update when kwargs provided
|
|
380
414
|
|
|
381
415
|
Args:
|
|
382
|
-
structure (str): label for structure
|
|
416
|
+
structure (str): label used for structure
|
|
383
417
|
|
|
384
418
|
Keyword Args:
|
|
385
|
-
description (str):
|
|
386
|
-
planar (int): 1 for planar 0 for linear
|
|
419
|
+
description (str): Structype description
|
|
420
|
+
planar (int): 1 for planar 0 for linear Structype
|
|
387
421
|
structcode (int): structcode (optional)
|
|
388
422
|
groupcode (int): groupcode (optional)
|
|
389
423
|
|
|
@@ -405,11 +439,11 @@ class SDBSession:
|
|
|
405
439
|
|
|
406
440
|
def add_structdata(self, site, structype, azimuth, inclination, **kwargs):
|
|
407
441
|
"""
|
|
408
|
-
Add
|
|
442
|
+
Add structural measurement to site
|
|
409
443
|
|
|
410
444
|
Args:
|
|
411
|
-
site (Site):
|
|
412
|
-
structype (Structype):
|
|
445
|
+
site (Site): Site instance
|
|
446
|
+
structype (Structype): Structype instance
|
|
413
447
|
azimuth (float): dip direction or plunge direction
|
|
414
448
|
inclination (float): dip or plunge
|
|
415
449
|
|
|
@@ -437,9 +471,9 @@ class SDBSession:
|
|
|
437
471
|
Add Foliation to site
|
|
438
472
|
|
|
439
473
|
Args:
|
|
440
|
-
site (Site):
|
|
441
|
-
structype (Structype):
|
|
442
|
-
fol (Foliation):
|
|
474
|
+
site (Site): Site instance
|
|
475
|
+
structype (Structype): Structype instance
|
|
476
|
+
fol (Foliation): Foliation instance
|
|
443
477
|
|
|
444
478
|
Keyword Args:
|
|
445
479
|
description (str): structdata description
|
|
@@ -460,9 +494,9 @@ class SDBSession:
|
|
|
460
494
|
Add Lineation to site
|
|
461
495
|
|
|
462
496
|
Args:
|
|
463
|
-
site (Site):
|
|
464
|
-
structype (Structype):
|
|
465
|
-
lin (Lineation):
|
|
497
|
+
site (Site): Site instance
|
|
498
|
+
structype (Structype): Structype instance
|
|
499
|
+
lin (Lineation): Lineation instance
|
|
466
500
|
|
|
467
501
|
Keyword Args:
|
|
468
502
|
description (str): structdata description
|
|
@@ -478,47 +512,50 @@ class SDBSession:
|
|
|
478
512
|
azimuth, inclination = lin.geo
|
|
479
513
|
return self.add_structdata(site, structype, azimuth, inclination, **kwargs)
|
|
480
514
|
|
|
481
|
-
def attach(self,
|
|
515
|
+
def attach(self, planar, linear):
|
|
482
516
|
"""
|
|
483
|
-
|
|
517
|
+
Attach Foliation to Lineation
|
|
484
518
|
|
|
485
519
|
Args:
|
|
486
|
-
|
|
487
|
-
|
|
520
|
+
planar (Structdata): planar Structdata
|
|
521
|
+
linear (Structdata): linear Structdata
|
|
488
522
|
|
|
489
523
|
Returns:
|
|
490
524
|
Attached
|
|
491
525
|
|
|
492
526
|
"""
|
|
493
|
-
|
|
527
|
+
assert planar.structype.planar == 1, "First argument must be planar Structdata"
|
|
528
|
+
assert linear.structype.planar == 0, "Second argument must be linear Structdata"
|
|
529
|
+
pair = Attached(planar=planar, linear=linear)
|
|
494
530
|
self.session.add(pair)
|
|
495
531
|
if self.autocommit:
|
|
496
532
|
self.commit()
|
|
497
533
|
return pair
|
|
498
534
|
|
|
499
|
-
def add_pair(self,
|
|
535
|
+
def add_pair(self, site, foltype, lintype, pair, **kwargs):
|
|
500
536
|
"""
|
|
501
|
-
Add attached foliation and lineation to database
|
|
537
|
+
Add attached foliation and lineation to database. Note that
|
|
538
|
+
measurements of foliation and lineation are corrected.
|
|
502
539
|
|
|
503
540
|
Args:
|
|
504
|
-
|
|
541
|
+
site (Site): site instance
|
|
505
542
|
foltype (Structype): structype instance
|
|
506
543
|
lintype (Structype): structype instance
|
|
544
|
+
pair (Pair): Pair instance
|
|
507
545
|
|
|
508
546
|
Returns:
|
|
509
547
|
Attached
|
|
510
548
|
|
|
511
549
|
"""
|
|
512
550
|
assert isinstance(pair, Pair), "pair argument must be instance of Pair class"
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
kwargs["structype"] = lintype
|
|
516
|
-
lin = self.add_lin(pair.lin, **kwargs)
|
|
551
|
+
fol = self.add_fol(site, foltype, pair.fol, **kwargs)
|
|
552
|
+
lin = self.add_lin(site, lintype, pair.lin, **kwargs)
|
|
517
553
|
return self.attach(fol, lin)
|
|
518
554
|
|
|
519
555
|
def sites(self, **kwargs):
|
|
520
556
|
"""
|
|
521
|
-
Retrieve
|
|
557
|
+
Retrieve list of Site instances filtered by kwargs. If query results
|
|
558
|
+
in single site, instance of Site is returned.
|
|
522
559
|
|
|
523
560
|
Keyword arguments are passed to sqlalchemy filter_by method
|
|
524
561
|
"""
|
|
@@ -533,7 +570,8 @@ class SDBSession:
|
|
|
533
570
|
|
|
534
571
|
def units(self, **kwargs):
|
|
535
572
|
"""
|
|
536
|
-
Retrieve
|
|
573
|
+
Retrieve list of Unit instances filtered by kwargs. If query results
|
|
574
|
+
in single unit, instance of Unit is returned.
|
|
537
575
|
|
|
538
576
|
Keyword arguments are passed to sqlalchemy filter_by method
|
|
539
577
|
"""
|
|
@@ -548,7 +586,8 @@ class SDBSession:
|
|
|
548
586
|
|
|
549
587
|
def structypes(self, **kwargs):
|
|
550
588
|
"""
|
|
551
|
-
Retrieve
|
|
589
|
+
Retrieve list of Structype instances filtered by kwargs. If query results
|
|
590
|
+
in single structural type, instance of Structype is returned.
|
|
552
591
|
|
|
553
592
|
Keyword arguments are passed to sqlalchemy filter_by method
|
|
554
593
|
"""
|
|
@@ -563,7 +602,8 @@ class SDBSession:
|
|
|
563
602
|
|
|
564
603
|
def tags(self, **kwargs):
|
|
565
604
|
"""
|
|
566
|
-
Retrieve
|
|
605
|
+
Retrieve list of Tag instances filtered by kwargs. If query results
|
|
606
|
+
in single tag, instance of Tag is returned.
|
|
567
607
|
|
|
568
608
|
Keyword arguments are passed to sqlalchemy filter_by method
|
|
569
609
|
"""
|
|
@@ -589,14 +629,14 @@ class SDBSession:
|
|
|
589
629
|
structypes = (
|
|
590
630
|
self.session.query(Structype).filter_by(structure=structype).all()
|
|
591
631
|
)
|
|
592
|
-
assert len(structypes) == 1, f"There is no structure {structype} in db"
|
|
632
|
+
assert len(structypes) == 1, f"There is no structure {structype} in db."
|
|
593
633
|
structype = structypes[0]
|
|
594
634
|
data = (
|
|
595
635
|
self.session.query(Structdata)
|
|
596
636
|
.filter_by(structype=structype, **kwargs)
|
|
597
637
|
.all()
|
|
598
638
|
)
|
|
599
|
-
if structype.planar:
|
|
639
|
+
if structype.planar == 1:
|
|
600
640
|
res = FoliationSet(
|
|
601
641
|
[Foliation(v.azimuth, v.inclination) for v in data],
|
|
602
642
|
name=structype.structure,
|
apsg/plotting/_stereogrid.py
CHANGED
|
@@ -206,8 +206,7 @@ class StereoGrid:
|
|
|
206
206
|
dcgrid = np.asarray(self.grid).T
|
|
207
207
|
X, Y = self.proj.project_data(*dcgrid, clip_inside=False)
|
|
208
208
|
cf = ax.tricontourf(X, Y, self.values, **parsed)
|
|
209
|
-
|
|
210
|
-
collection.set_clip_path(primitive)
|
|
209
|
+
cf.set_clip_path(primitive)
|
|
211
210
|
ax.set_xlim(-1.05, 1.05)
|
|
212
211
|
ax.set_ylim(-1.05, 1.05)
|
|
213
212
|
if colorbar:
|
|
@@ -249,15 +248,14 @@ class StereoGrid:
|
|
|
249
248
|
radius=1,
|
|
250
249
|
edgecolor="black",
|
|
251
250
|
fill=False,
|
|
252
|
-
clip_box=
|
|
251
|
+
clip_box=None,
|
|
253
252
|
label="_nolegend_",
|
|
254
253
|
)
|
|
255
254
|
ax.add_patch(primitive)
|
|
256
255
|
dcgrid = np.asarray(self.grid).T
|
|
257
256
|
X, Y = self.proj.project_data(*dcgrid, clip_inside=False)
|
|
258
257
|
cf = ax.tricontour(X, Y, self.values, **parsed)
|
|
259
|
-
|
|
260
|
-
collection.set_clip_path(primitive)
|
|
258
|
+
cf.set_clip_path(primitive)
|
|
261
259
|
if colorbar:
|
|
262
260
|
fig.colorbar(cf, ax=ax, shrink=0.6)
|
|
263
261
|
plt.show()
|
|
@@ -281,7 +279,7 @@ class StereoGrid:
|
|
|
281
279
|
radius=1,
|
|
282
280
|
edgecolor="black",
|
|
283
281
|
fill=False,
|
|
284
|
-
clip_box=
|
|
282
|
+
clip_box=None,
|
|
285
283
|
label="_nolegend_",
|
|
286
284
|
)
|
|
287
285
|
ax.add_patch(primitive)
|
apsg/plotting/_stereonet.py
CHANGED
|
@@ -901,16 +901,14 @@ class StereoNet:
|
|
|
901
901
|
dcgrid = np.asarray(self.grid.grid).T
|
|
902
902
|
X, Y = self.proj.project_data(*dcgrid, clip_inside=False)
|
|
903
903
|
cf = self.ax.tricontourf(X, Y, self.grid.values, **kwargs)
|
|
904
|
-
|
|
905
|
-
collection.set_clip_path(self.primitive)
|
|
904
|
+
cf.set_clip_path(self.primitive)
|
|
906
905
|
if clines:
|
|
907
906
|
kwargs["cmap"] = None
|
|
908
907
|
kwargs["colors"] = "k"
|
|
909
908
|
kwargs["linewidths"] = linewidths
|
|
910
909
|
kwargs["linestyles"] = linestyles
|
|
911
910
|
cl = self.ax.tricontour(X, Y, self.grid.values, **kwargs)
|
|
912
|
-
|
|
913
|
-
collection.set_clip_path(self.primitive)
|
|
911
|
+
cl.set_clip_path(self.primitive)
|
|
914
912
|
if show_data:
|
|
915
913
|
artist = StereoNetArtistFactory.create_point(*args[0], **data_kws)
|
|
916
914
|
self._line(*artist.args, **artist.kwargs)
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: apsg
|
|
3
|
+
Version: 1.3.1
|
|
4
|
+
Summary: APSG - The package for structural geologists
|
|
5
|
+
Project-URL: Homepage, https://github.com/ondrolexa/apsg
|
|
6
|
+
Project-URL: Documentation, https://apsg.readthedocs.io
|
|
7
|
+
Project-URL: Repository, https://github.com/ondrolexa/apsg.git
|
|
8
|
+
Project-URL: Issues, https://github.com/ondrolexa/apsg/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/ondrolexa/apsg/blob/master/CHANGELOG.md
|
|
10
|
+
Author-email: Ondrej Lexa <lexa.ondrej@gmail.com>
|
|
11
|
+
Maintainer-email: Ondrej Lexa <lexa.ondrej@gmail.com>
|
|
12
|
+
License: MIT License
|
|
13
|
+
|
|
14
|
+
APSG - The package for structural geologists.
|
|
15
|
+
Copyright (c) 2015-2024, Ondrej Lexa
|
|
16
|
+
|
|
17
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
18
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
19
|
+
in the Software without restriction, including without limitation the rights
|
|
20
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
21
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
22
|
+
furnished to do so, subject to the following conditions:
|
|
23
|
+
|
|
24
|
+
The above copyright notice and this permission notice shall be included in all
|
|
25
|
+
copies or substantial portions of the Software.
|
|
26
|
+
|
|
27
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
28
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
29
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
30
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
31
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
32
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
33
|
+
SOFTWARE.
|
|
34
|
+
License-File: LICENSE
|
|
35
|
+
Keywords: orientation data,stereonet,structural geology
|
|
36
|
+
Classifier: Development Status :: 4 - Beta
|
|
37
|
+
Classifier: Intended Audience :: Science/Research
|
|
38
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
39
|
+
Classifier: Operating System :: OS Independent
|
|
40
|
+
Classifier: Programming Language :: Python :: 3
|
|
41
|
+
Requires-Python: >=3.10
|
|
42
|
+
Requires-Dist: matplotlib>=3.9
|
|
43
|
+
Requires-Dist: numpy
|
|
44
|
+
Requires-Dist: pandas
|
|
45
|
+
Requires-Dist: scipy
|
|
46
|
+
Requires-Dist: sqlalchemy
|
|
47
|
+
Provides-Extra: dev
|
|
48
|
+
Requires-Dist: autodocsumm; extra == 'dev'
|
|
49
|
+
Requires-Dist: black; extra == 'dev'
|
|
50
|
+
Requires-Dist: ipykernel; extra == 'dev'
|
|
51
|
+
Requires-Dist: nbsphinx; extra == 'dev'
|
|
52
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
53
|
+
Requires-Dist: readthedocs-sphinx-search; extra == 'dev'
|
|
54
|
+
Requires-Dist: sphinx; extra == 'dev'
|
|
55
|
+
Requires-Dist: sphinx-rtd-theme; extra == 'dev'
|
|
56
|
+
Provides-Extra: docs
|
|
57
|
+
Requires-Dist: autodocsumm; extra == 'docs'
|
|
58
|
+
Requires-Dist: ipykernel; extra == 'docs'
|
|
59
|
+
Requires-Dist: nbsphinx; extra == 'docs'
|
|
60
|
+
Requires-Dist: readthedocs-sphinx-search; extra == 'docs'
|
|
61
|
+
Requires-Dist: sphinx; extra == 'docs'
|
|
62
|
+
Requires-Dist: sphinx-rtd-theme; extra == 'docs'
|
|
63
|
+
Provides-Extra: extra
|
|
64
|
+
Requires-Dist: jupyterlab; extra == 'extra'
|
|
65
|
+
Requires-Dist: pyqt5; extra == 'extra'
|
|
66
|
+
Provides-Extra: tests
|
|
67
|
+
Requires-Dist: pytest; extra == 'tests'
|
|
68
|
+
Description-Content-Type: text/markdown
|
|
69
|
+
|
|
70
|
+
<img src="https://ondrolexa.github.io/apsg/apsg_banner.svg" alt="APSG logo" width="300px"/>
|
|
71
|
+
|
|
72
|
+
[](https://pypi.org/project/apsg)
|
|
73
|
+
[](https://anaconda.org/conda-forge/apsg)
|
|
74
|
+
[](https://apsg.readthedocs.io/en/stable/?badge=stable)
|
|
75
|
+
[](https://zenodo.org/badge/latestdoi/24879346)
|
|
76
|
+
|
|
77
|
+
## :thinking: What is APSG?
|
|
78
|
+
|
|
79
|
+
APSG is the package for structural geologists. It defines several new python classes to easily manage, analyze and visualize orientational structural geology data.
|
|
80
|
+
|
|
81
|
+
> [!IMPORTANT]
|
|
82
|
+
> APSG has been significantly refactored from version 1.0 and several changes are
|
|
83
|
+
> breaking backward compatibility. The main APSG namespace provides often-used
|
|
84
|
+
> classes in lowercase names as aliases to `PascalCase` convention used in
|
|
85
|
+
> modules to provide a simplified interface for users. The `PascalCase` names of
|
|
86
|
+
> classes use longer and plain English names instead acronyms for better readability.
|
|
87
|
+
>
|
|
88
|
+
> Check [documentation](https://apsg.readthedocs.org) for more details.
|
|
89
|
+
|
|
90
|
+
## :hammer_and_wrench: Requirements
|
|
91
|
+
|
|
92
|
+
You need Python 3.9 or later to run APSG. The package requires [NumPy](https://numpy.org/) and [SciPy](https://www.scipy.org/),
|
|
93
|
+
[Matplotlib](https://matplotlib.org/), [SciPy](https://scipy.org/), [SQLAlchemy](https://www.sqlalchemy.org/)
|
|
94
|
+
and [pandas](https://pandas.pydata.org/).
|
|
95
|
+
|
|
96
|
+
## :rocket: How to install
|
|
97
|
+
|
|
98
|
+
It is strongly suggested to install **apsg** into separate environment. You can create
|
|
99
|
+
Python virtual environment. For Linux and macOS use:
|
|
100
|
+
|
|
101
|
+
python -m venv .venv
|
|
102
|
+
source .venv/bin/activate
|
|
103
|
+
|
|
104
|
+
for Windows use Command Prompt or PowerShell:
|
|
105
|
+
|
|
106
|
+
python -m venv .venv
|
|
107
|
+
.venv\Scripts\activate
|
|
108
|
+
|
|
109
|
+
> [!NOTE]
|
|
110
|
+
> On Microsoft Windows, it may be required to set the execution policy in PowerShell for the user.
|
|
111
|
+
> You can do this by issuing the following PowerShell command:
|
|
112
|
+
> ```
|
|
113
|
+
> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
|
|
114
|
+
> ```
|
|
115
|
+
|
|
116
|
+
and install latest stable version of **apsg** using pip within the environment:
|
|
117
|
+
|
|
118
|
+
pip install apsg
|
|
119
|
+
|
|
120
|
+
To include jupyterlab and pyqt5 in installation, use `extra` option:
|
|
121
|
+
|
|
122
|
+
## I'm using conda or mamba to manage environments
|
|
123
|
+
|
|
124
|
+
pip install apsg[extra]
|
|
125
|
+
|
|
126
|
+
or install **master** with:
|
|
127
|
+
|
|
128
|
+
pip install git+https://github.com/ondrolexa/apsg.git
|
|
129
|
+
|
|
130
|
+
Alternatively, you can clone the repository and do a local install (recommended for dev):
|
|
131
|
+
git clone https://github.com/ondrolexa/apsg.git
|
|
132
|
+
cd apsg
|
|
133
|
+
pip install -e .[dev]
|
|
134
|
+
|
|
135
|
+
#### Upgrading via pip
|
|
136
|
+
|
|
137
|
+
To upgrade an existing version of APSG from PyPI, execute:
|
|
138
|
+
|
|
139
|
+
pip install apsg --upgrade --no-deps
|
|
140
|
+
|
|
141
|
+
#### Comments on system-wide instalations on Debian systems
|
|
142
|
+
|
|
143
|
+
Latest Debian-based systems does not allow to install non-debian packages system-wide.
|
|
144
|
+
However, installing all requirements allows to force install APSG system-wide without troubles.
|
|
145
|
+
|
|
146
|
+
Install requirements using apt:
|
|
147
|
+
|
|
148
|
+
sudo apt install python3-numpy python3-matplotlib python3-scipy python3-sqlalchemy python3-pandas
|
|
149
|
+
|
|
150
|
+
and then install apsg using pip:
|
|
151
|
+
|
|
152
|
+
pip install --break-system-packages apsg
|
|
153
|
+
|
|
154
|
+
### I'm using conda or mamba to manage environments
|
|
155
|
+
|
|
156
|
+
If you have already have conda or mamba installed, you can create environment with:
|
|
157
|
+
|
|
158
|
+
conda config --add channels conda-forge
|
|
159
|
+
conda create -n apsg python apsg jupyterlab pyqt
|
|
160
|
+
|
|
161
|
+
or using mamba
|
|
162
|
+
|
|
163
|
+
mamba create -n apsg python apsg jupyterlab pyqt
|
|
164
|
+
|
|
165
|
+
#### Current release info
|
|
166
|
+
|
|
167
|
+
| Name | Downloads | Version | Platforms |
|
|
168
|
+
| --- | --- | --- | --- |
|
|
169
|
+
| [](https://anaconda.org/conda-forge/apsg) | [](https://anaconda.org/conda-forge/apsg) | [](https://anaconda.org/conda-forge/apsg) | [](https://anaconda.org/conda-forge/apsg) |
|
|
170
|
+
|
|
171
|
+
## :blue_book: Documentation
|
|
172
|
+
|
|
173
|
+
Explore all the features of APSG. You can find detailed documentation [here](https://apsg.readthedocs.org).
|
|
174
|
+
|
|
175
|
+
## :computer: Contributing
|
|
176
|
+
|
|
177
|
+
Most discussion happens on [Github](https://github.com/ondrolexa/apsg). Feel free to open [an issue](https://github.com/ondrolexa/apsg/issues/new) or comment on any open issue or pull request. Check ``CONTRIBUTING.md`` for more details.
|
|
178
|
+
|
|
179
|
+
## :coin: Donate
|
|
180
|
+
|
|
181
|
+
APSG is an open-source project, available for you for free. It took a lot of time and resources to build this software. If you find this software useful and want to support its future development please consider donating me.
|
|
182
|
+
|
|
183
|
+
[](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=QTYZWVUNDUAH8&item_name=APSG+development+donation¤cy_code=EUR&source=url)
|
|
184
|
+
|
|
185
|
+
## License
|
|
186
|
+
|
|
187
|
+
APSG is free software: you can redistribute it and/or modify it under the terms of the MIT License. A copy of this license is provided in ``LICENSE`` file.
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
CHANGELOG.md,sha256=J5hRiMVHG88UP13uHGW-ljdNtFO2M4OdRRb9vNVwDrE,10217
|
|
3
|
-
CONTRIBUTING.md,sha256=pcfp26JziGDVX-drzjXjkISUmkXyw_yxarJTS4FFqbI,2975
|
|
4
|
-
apsg/__init__.py,sha256=HVGqE7LcE48TQc_mJpgKbKpfBB-gwWhvWgHxQjckVM4,2019
|
|
1
|
+
apsg/__init__.py,sha256=UMS0nMQdDAnftTSM1Upz4xe-5zlXJM3rlhP9NcC720Q,1966
|
|
5
2
|
apsg/config.py,sha256=X3_yXT96xXlVxFA94EfYFKJbrcGIHT0PvB9s8EKmYOg,4569
|
|
3
|
+
apsg/shell.py,sha256=UFIOy01KckLsOlqfB0UomyWZ1ITL36-lBUFhlxWdZLE,718
|
|
6
4
|
apsg/database/__init__.py,sha256=7Rvcf1KBBBNhoM28ZlvQ01CkScQTroFkoS4d1kD55Ws,315
|
|
7
|
-
apsg/database/_alchemy.py,sha256=
|
|
5
|
+
apsg/database/_alchemy.py,sha256=cs8un4RJwUoE62GOberiTIQ2akFXYM0FoR-f1h4_DW8,19726
|
|
8
6
|
apsg/database/_sdbread.py,sha256=Gzj0bpp0vnMvCfxIuX6Ktf01LoQWDCOcXST7HZp_XTY,10868
|
|
9
7
|
apsg/decorator/__init__.py,sha256=fZ-dxpldQIk6-2JhVnCj-Tsl8bz2nvoGOyG7uXKvBfg,160
|
|
10
8
|
apsg/decorator/_decorator.py,sha256=8TMSrcVvhU5UCbNMrnyrW3-65qo20_6N2ShtXd3bP-k,1194
|
|
@@ -30,11 +28,10 @@ apsg/plotting/_paleomagplots.py,sha256=Gw-fqYJVjNxTNXLwAiUKnFUBoOTZv2MEd3XACLmQ6
|
|
|
30
28
|
apsg/plotting/_plot_artists.py,sha256=6S0EKCqYU6rlBxcxcXALTk9PaUK6QL-BgUKmZH8tkMc,21059
|
|
31
29
|
apsg/plotting/_projection.py,sha256=qnJgTQaFW0v2Pu9ySAEPADHhLgpXmfJ6QIrydry8rXQ,11473
|
|
32
30
|
apsg/plotting/_roseplot.py,sha256=jbaUXSb3DIcXs0pWAQUTZfdlA2XcbquT0yHLYDjLirQ,12808
|
|
33
|
-
apsg/plotting/_stereogrid.py,sha256=
|
|
34
|
-
apsg/plotting/_stereonet.py,sha256
|
|
35
|
-
apsg/
|
|
36
|
-
apsg-1.3.
|
|
37
|
-
apsg-1.3.
|
|
38
|
-
apsg-1.3.
|
|
39
|
-
apsg-1.3.
|
|
40
|
-
apsg-1.3.0.dist-info/RECORD,,
|
|
31
|
+
apsg/plotting/_stereogrid.py,sha256=awh7MwN1WgszhOlr6UgR20wHQJ8u778-Tf_w1uflrV4,11869
|
|
32
|
+
apsg/plotting/_stereonet.py,sha256=-BFM9RCInI-RYs1DKz-ymNFkMFJLTWuw3JcyUs7YiGM,36596
|
|
33
|
+
apsg-1.3.1.dist-info/METADATA,sha256=VO_IH05Lz7B_VVuKzAVnjgYCB-4fbLboV8I6kVJ1L8o,8267
|
|
34
|
+
apsg-1.3.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
35
|
+
apsg-1.3.1.dist-info/entry_points.txt,sha256=SowP7_uRI0NJuzznKBXyM9BJcSwBxbXo6Iz5LUo9mEQ,42
|
|
36
|
+
apsg-1.3.1.dist-info/licenses/LICENSE,sha256=lY0kfpVRrzcgVZq7pI6rLK5WYiUMWe0bdKpDelN6hk8,1120
|
|
37
|
+
apsg-1.3.1.dist-info/RECORD,,
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
APSG - The package for structural geologists.
|
|
4
|
+
Copyright (c) 2015-2024, Ondrej Lexa
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
AUTHORS.md
DELETED
CHANGELOG.md
DELETED
|
@@ -1,304 +0,0 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
## 1.3.0 (Dec 14 2024)
|
|
4
|
-
* Python 3.10 set as minimal version
|
|
5
|
-
* Tensor3 eigenlins and eigenfols implemented as methods
|
|
6
|
-
* pandas accessors G property returns apsg FeatureSet
|
|
7
|
-
|
|
8
|
-
### 1.2.3 (Nov 18 2024)
|
|
9
|
-
* ClusterSet accepts PairSet and FaultSet
|
|
10
|
-
* quicknet label option added
|
|
11
|
-
* vector pow bug fix
|
|
12
|
-
|
|
13
|
-
### 1.2.2 (Oct 21 2024)
|
|
14
|
-
* Fault sense could be defined by str, one of 's', 'd', 'n' and 'r'
|
|
15
|
-
|
|
16
|
-
### 1.2.1 (Sep 23 2024)
|
|
17
|
-
* Fault sense could be defined by str, one of 's', 'd', 'n' and 'r'
|
|
18
|
-
|
|
19
|
-
## 1.2.0 (May 24 2024)
|
|
20
|
-
* sqlalchemy and pandas added to requirements
|
|
21
|
-
* quicknet fault bug fixed
|
|
22
|
-
|
|
23
|
-
### 1.1.5 (May 15 2024)
|
|
24
|
-
* paleomag Core .dd bug fixed
|
|
25
|
-
* fix round-off domain math error for acosd and asind
|
|
26
|
-
|
|
27
|
-
### 1.1.4 (Dec 13 2023)
|
|
28
|
-
* Ellipsoid repr bugfix
|
|
29
|
-
|
|
30
|
-
### 1.1.3 (Oct 23 2023)
|
|
31
|
-
Bugfix release
|
|
32
|
-
* slip and dilatation tendency methods added to stress
|
|
33
|
-
* proj alias of project for FeatureSet added
|
|
34
|
-
|
|
35
|
-
### 1.1.2 (Oct 09 2023)
|
|
36
|
-
* added title_kws argument for plotting routines
|
|
37
|
-
|
|
38
|
-
### 1.1.1 (Oct 06 2023)
|
|
39
|
-
* sigma estimate contour fix
|
|
40
|
-
|
|
41
|
-
## 1.1.0 (Oct 04 2023)
|
|
42
|
-
APSG offers convenient pandas integration via pandas accessors.
|
|
43
|
-
|
|
44
|
-
See documentation and Pandas interface tutorial for further details.
|
|
45
|
-
|
|
46
|
-
* StereoNet tensor method added
|
|
47
|
-
* Cluster class renamed to ClusterSet
|
|
48
|
-
|
|
49
|
-
### 1.0.3 (Apr 30 2023)
|
|
50
|
-
* lambda properties of tensors renamed to S
|
|
51
|
-
* cursor coordinates in stereonet show lin and fol
|
|
52
|
-
|
|
53
|
-
### 1.0.1 (Nov 22 2022)
|
|
54
|
-
* density_lookup method implemented for StereoNet.grid
|
|
55
|
-
* Stress tensor sigma* properties using inverted order of eigenvalues
|
|
56
|
-
* render2fig method of StereoNet implemented
|
|
57
|
-
* vector-like objects are not iterable, so properly render in pandas
|
|
58
|
-
|
|
59
|
-
## 1.0.0 (Oct 7 2022)
|
|
60
|
-
New major release
|
|
61
|
-
|
|
62
|
-
APSG has been significantly refactored from version 1.0 and several changes are
|
|
63
|
-
breaking backward compatibility. The main APSG namespace provides often-used
|
|
64
|
-
classes in lowercase names as aliases to `PascalCase` convention used in
|
|
65
|
-
modules to provide a simplified interface for users. The `PascalCase` names of
|
|
66
|
-
classes use longer and plain English names instead acronyms for better
|
|
67
|
-
readability.
|
|
68
|
-
|
|
69
|
-
See documentation for further details.
|
|
70
|
-
|
|
71
|
-
### 0.7.3 (Oct 6 2022)
|
|
72
|
-
* figure window title removed from StereoNet
|
|
73
|
-
* for future only bugfixes planned, foo further development see versions >=1.0
|
|
74
|
-
|
|
75
|
-
### 0.7.2 (Oct 6 2022)
|
|
76
|
-
* bugfix release
|
|
77
|
-
|
|
78
|
-
### 0.7.1 (Jul 13 2021)
|
|
79
|
-
* paleomag rs3 input/output improved
|
|
80
|
-
* Simple SQLAlchemy API to sdb database implemented
|
|
81
|
-
* StereoNet arc method fixed
|
|
82
|
-
* StereoNet polygon method added
|
|
83
|
-
|
|
84
|
-
## 0.7.0 (Feb 3 2021)
|
|
85
|
-
|
|
86
|
-
* Python 2 support dropped
|
|
87
|
-
* RosePlot added
|
|
88
|
-
* Ortensor has from_pairs method for Lisle tensor for orthogonal data
|
|
89
|
-
* StereoNet scatter method has labels kwarg to show hover annotations
|
|
90
|
-
|
|
91
|
-
### 0.6.3 (Dec 6 2019)
|
|
92
|
-
|
|
93
|
-
* Python 2/3 compatibility fix
|
|
94
|
-
|
|
95
|
-
### 0.6.2 (Dec 6 2019)
|
|
96
|
-
|
|
97
|
-
* few minor bugs fixed
|
|
98
|
-
* Stereogrid apply_func passes Vec3 instead numpy array
|
|
99
|
-
* Pair H method to get mutual rotation implemented
|
|
100
|
-
* velgrad method of DefGrad accepts steps kwarg
|
|
101
|
-
to generate list of DefGrad tensors
|
|
102
|
-
* Added Tensor class to work with deformation tensors
|
|
103
|
-
|
|
104
|
-
### 0.6.1 (Dec 12 2018)
|
|
105
|
-
|
|
106
|
-
* Stereogrid always use Euclidean norms as weights
|
|
107
|
-
* DefGrad properties e1, e2, e3 (natural principal strains) added
|
|
108
|
-
* DefGrad properties eoct, goct (octahedral strains) added
|
|
109
|
-
* DefGrad from_ratios class method added
|
|
110
|
-
* DefGrad properties k, d, K, D (strain symmetries and intesities) added
|
|
111
|
-
* New class Ellipsoid added to work with ellipsoids
|
|
112
|
-
* FabricPLot renamed to VollmerPlot for consistency
|
|
113
|
-
* RamsayPlot, FlinnPlot and HsuPlot implemented
|
|
114
|
-
* All fabric plots have new path method accepting list of tensors
|
|
115
|
-
|
|
116
|
-
## 0.6.0 (Nov 7 2018)
|
|
117
|
-
|
|
118
|
-
* Stress always gives eigenvalues sorted
|
|
119
|
-
* Stress I1, I2, I3 properties for invariants implemented
|
|
120
|
-
* Stress mean_stress property implemented
|
|
121
|
-
* Stress hydrostatic and deviatoric properties implemented
|
|
122
|
-
* precision added to settings to control numerical comparisms
|
|
123
|
-
* figsize added to settings to control figure size across APSG
|
|
124
|
-
* Animation examples fixed
|
|
125
|
-
* rand class method implemented for Fol, Lin, Vec3 and Pair to
|
|
126
|
-
generate random instance
|
|
127
|
-
* Group to_csv and from_csv improved
|
|
128
|
-
* SDB tags method works properly for multiple tags
|
|
129
|
-
* SDB can modify database metadata
|
|
130
|
-
* QGIS 3 plugin ReadSDB compatibility
|
|
131
|
-
|
|
132
|
-
### 0.5.4 (Oct 19 2018)
|
|
133
|
-
|
|
134
|
-
* StereoNet has cbpad keyword for colorbar padding
|
|
135
|
-
* FabricPlot bug introduced in 0.5.2 fixed.
|
|
136
|
-
|
|
137
|
-
### 0.5.3 (Oct 10 2018)
|
|
138
|
-
|
|
139
|
-
* Bugfix release
|
|
140
|
-
|
|
141
|
-
### 0.5.2 (Oct 10 2018)
|
|
142
|
-
|
|
143
|
-
* Fischer distribution sampling added
|
|
144
|
-
* transform method has norm kwarg to normalize tranformed vectors
|
|
145
|
-
* axisangle property to calculate axis and angle from rotation matrix
|
|
146
|
-
* StereoNet arc method added
|
|
147
|
-
* Vec3 and Group upper and flip properties implemented
|
|
148
|
-
* DefGrad, VelGrad and Stress rotate method accepts also rotation matrix
|
|
149
|
-
* velgrad method added to DefGrad to calculate matrix logarithm
|
|
150
|
-
* StereoGrid has new methods max, min, max_at, min_at
|
|
151
|
-
|
|
152
|
-
### 0.5.1 (Dec 5 2017)
|
|
153
|
-
|
|
154
|
-
* Kent distribution sampling added
|
|
155
|
-
* Automatic kernel density estimate for contouring
|
|
156
|
-
* UserWarnings fix
|
|
157
|
-
|
|
158
|
-
## 0.5.0 (Nov 19 2017)
|
|
159
|
-
|
|
160
|
-
* bux fix minor release
|
|
161
|
-
|
|
162
|
-
### 0.4.4 (Mar 25 2017)
|
|
163
|
-
|
|
164
|
-
* Group method centered improved
|
|
165
|
-
* Group method halfspace added to reorient all vectors towards resultant
|
|
166
|
-
halfspace
|
|
167
|
-
|
|
168
|
-
### 0.4.3 (Mar 25 2017)
|
|
169
|
-
|
|
170
|
-
* Stress tensor with few basic methods implemented
|
|
171
|
-
* StereoGrid keyword argument 'weighted' to control weighting
|
|
172
|
-
* StereoNet kwargs are passed to underlying methods for immediate plots
|
|
173
|
-
* StereoNet tensor method implemented (draw eigenlins or fols based on
|
|
174
|
-
fol_plot settings)
|
|
175
|
-
* Group totvar property and dot and proj methods implemented
|
|
176
|
-
* Fol and Lin dot method returns absolute value of dot product
|
|
177
|
-
* Vec3 H method to get mutual rotation implemented
|
|
178
|
-
* StereoNet.contourf method draw contour lines as well by default. Option
|
|
179
|
-
clines controls it.
|
|
180
|
-
* centered bug fixed
|
|
181
|
-
* StereoNet allows simple animations. Add `animate=True` kwarg to plotting
|
|
182
|
-
method and finally call StereoNet animate method.
|
|
183
|
-
|
|
184
|
-
### 0.4.1-2 (Mar 4 2017)
|
|
185
|
-
|
|
186
|
-
* bugfix
|
|
187
|
-
|
|
188
|
-
## 0.4.0 (Mar 4 2017)
|
|
189
|
-
|
|
190
|
-
* Density class renamed to StereoGrid
|
|
191
|
-
* Fault sense under rotation fixed
|
|
192
|
-
* FaultSet example provided
|
|
193
|
-
* Angelier-Mechler dihedra method implemented for FaultSet
|
|
194
|
-
* StereoNet accepts StereoGrid and Ortensor as quick plot arguments
|
|
195
|
-
* StereoNet instance has axtitle method to put text below stereonet
|
|
196
|
-
|
|
197
|
-
### 0.3.7 (Jan 5 2017)
|
|
198
|
-
|
|
199
|
-
* conda build for all platforms
|
|
200
|
-
* numpy, matplotlib and other helpres imported by default
|
|
201
|
-
* ortensor is normed by default
|
|
202
|
-
* ortensor MADp, MADo, MAD and kind properties added
|
|
203
|
-
|
|
204
|
-
### 0.3.6 (Jan 3 2017)
|
|
205
|
-
|
|
206
|
-
* shell script iapsg opens interactive console
|
|
207
|
-
|
|
208
|
-
### 0.3.5 (Nov 12 2016)
|
|
209
|
-
|
|
210
|
-
* Simple settings interface implemented in in apsg.core.seetings dictionary.
|
|
211
|
-
To change settings use:
|
|
212
|
-
```
|
|
213
|
-
from apsg.core import settings
|
|
214
|
-
setting['name']=value
|
|
215
|
-
```
|
|
216
|
-
* `notation` setting with values `dd` or `rhr` control how azimuth argument of
|
|
217
|
-
Fol is represented.
|
|
218
|
-
* `vec2dd` setting with values `True` or `False` control how `Vec3` is
|
|
219
|
-
represented.
|
|
220
|
-
* Vec3 could be instantiated by one arument (vector like), 2 arguments
|
|
221
|
-
(azimuth, inclination) or 3 arguments (azimuth, inclination, magnitude).
|
|
222
|
-
* Group and FaultSet can return array or list of user-defined attributes of
|
|
223
|
-
all elements
|
|
224
|
-
|
|
225
|
-
### 0.3.4 (Jun 20 2016)
|
|
226
|
-
|
|
227
|
-
* RTD fix
|
|
228
|
-
|
|
229
|
-
### 0.3.3 (Jun 4 2016)
|
|
230
|
-
|
|
231
|
-
* Added E1,E2,E3 properties and polar decomposition method to DefGrad object
|
|
232
|
-
* StereoNet has vector method to mimics lower and upper hemisphere plotting
|
|
233
|
-
of Lin and Vec3 objects as used in paleomagnetic plots
|
|
234
|
-
* StereoNet could be initialized with subplots
|
|
235
|
-
* rake method of Fol added to return vector defined by rake
|
|
236
|
-
* Density could be initialized without data for user-defined calculations
|
|
237
|
-
New method apply_func could be used to calculate density
|
|
238
|
-
* Contour(f) methods accept Density object as argument
|
|
239
|
-
* Added Group class methods to generate Spherical Fibonacci and Golden Section
|
|
240
|
-
based uniform distributions of Vec3, Lin and Fol
|
|
241
|
-
|
|
242
|
-
### 0.3.2 (Feb 22 2016)
|
|
243
|
-
|
|
244
|
-
* FabricPlot - triangular fabric plot added
|
|
245
|
-
* .asvec3 property has .V alias
|
|
246
|
-
* Resultant of Fol and Lin is calculated as vectorial in centered position
|
|
247
|
-
* dv property of Fol added to return dip slip vector
|
|
248
|
-
|
|
249
|
-
### 0.3.1 (Nov 20 2015)
|
|
250
|
-
|
|
251
|
-
* SDB class improved. Support basic filtering including tags
|
|
252
|
-
* StereoNet has close method to close figure and new method
|
|
253
|
-
to re-initialize figure when closed in interactive mode
|
|
254
|
-
* iapsg shell script added to invoke apsg ipython shell
|
|
255
|
-
|
|
256
|
-
## 0.3.0 (Nov 9 2015)
|
|
257
|
-
|
|
258
|
-
* Group fancy indexing implemented. Group could be indexed by sequences
|
|
259
|
-
of indexes like list, tuple or array as well as sliced.
|
|
260
|
-
* Cluster class with hierarchical clustering implemented
|
|
261
|
-
* Group to_file and from_file methods implemented to store data in file
|
|
262
|
-
* Group copy method for shallow copy implemented
|
|
263
|
-
* StereoNet now accept Vec3 and Fault object as well for instant plotting.
|
|
264
|
-
* Ortensor updated with new properties E1,E2,E3 and Vollmer(1989) indexes
|
|
265
|
-
P,G,R and C. Bug in Woodcocks's shape and strength values fixed.
|
|
266
|
-
* uniform_lin and uniform_fol improved.
|
|
267
|
-
* asvec3 method implemented for Fol and Lin
|
|
268
|
-
* fol_plot property of StereoNet allows choose poles or great circles for
|
|
269
|
-
immediate plotting
|
|
270
|
-
* bootstrap method of Group provide generator of random resampling with
|
|
271
|
-
replacements.
|
|
272
|
-
* Group examples method provide few well-known datasets.
|
|
273
|
-
* Matplotlib deprecation warnings are ignored by default
|
|
274
|
-
|
|
275
|
-
### 0.2.3 (Oct 21 2015)
|
|
276
|
-
|
|
277
|
-
* New Docstrings format
|
|
278
|
-
* StereoNet.getfols method bug fixed.
|
|
279
|
-
* Shell scripts to run interactive session improved.
|
|
280
|
-
|
|
281
|
-
### 0.2.2 (Apr 17 2015)
|
|
282
|
-
|
|
283
|
-
* FaultSet class added. Fault and Hoeppner methods of StereoNet implemented
|
|
284
|
-
* VelGrad and DefGrad classes used for transformations added
|
|
285
|
-
* G class to quickly create groups from strings added.
|
|
286
|
-
|
|
287
|
-
### 0.2.1 (Dec 9 2014)
|
|
288
|
-
|
|
289
|
-
* Quick plotting of groups fixed.
|
|
290
|
-
|
|
291
|
-
## 0.2.0 (Dec 9 2014)
|
|
292
|
-
|
|
293
|
-
* new StereoNet class for Schmidt projection
|
|
294
|
-
* Quick plot when data are passed as argument `StereoNet` class instantiation
|
|
295
|
-
* mplstereonet dependency depreceated
|
|
296
|
-
* new `Pair` and `Fault` classes to manipulate paired data (full support in future)
|
|
297
|
-
* new `uniform_lin` and `uniform_fol` `Group` methods
|
|
298
|
-
* abs for `Group` implemented to calculate euclidean norms
|
|
299
|
-
* new `Group` method normalized
|
|
300
|
-
* new `Group` properties and methods to calculate spherical statistics
|
|
301
|
-
|
|
302
|
-
## 0.1.0 (Nov 1 2014)
|
|
303
|
-
|
|
304
|
-
* First release of APSG
|
CONTRIBUTING.md
DELETED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
# Contributing
|
|
2
|
-
|
|
3
|
-
Contributions are welcome, and they are greatly appreciated! Every little bit helps, and credit will always be given. You can contribute in many ways.
|
|
4
|
-
|
|
5
|
-
## Types of Contributions
|
|
6
|
-
|
|
7
|
-
### Report Bugs
|
|
8
|
-
|
|
9
|
-
Report bugs at [https://github.com/ondrolexa/apsg/issues](https://github.com/ondrolexa/apsg/issues).
|
|
10
|
-
|
|
11
|
-
If you are reporting a bug, please include:
|
|
12
|
-
|
|
13
|
-
* Your operating system name and version.
|
|
14
|
-
* Any details about your local setup that might be helpful in troubleshooting.
|
|
15
|
-
* Detailed steps to reproduce the bug.
|
|
16
|
-
|
|
17
|
-
### Fix Bugs
|
|
18
|
-
|
|
19
|
-
Look through the GitHub issues for bugs. Anything tagged with "bug"
|
|
20
|
-
is open to whoever wants to implement it.
|
|
21
|
-
|
|
22
|
-
### Implement Features
|
|
23
|
-
|
|
24
|
-
Look through the GitHub issues for features. Anything tagged with "feature"
|
|
25
|
-
is open to whoever wants to implement it.
|
|
26
|
-
|
|
27
|
-
### Write Documentation
|
|
28
|
-
|
|
29
|
-
APSG could always use more documentation, whether as part of the
|
|
30
|
-
official APSG docs, in docstrings, or even on the web in blog posts,
|
|
31
|
-
articles, and such.
|
|
32
|
-
|
|
33
|
-
### Submit Feedback
|
|
34
|
-
|
|
35
|
-
The best way to send feedback is to file an issue at [https://github.com/ondrolexa/apsg/issues](https://github.com/ondrolexa/apsg/issues).
|
|
36
|
-
|
|
37
|
-
If you are proposing a feature:
|
|
38
|
-
|
|
39
|
-
* Explain in detail how it would work.
|
|
40
|
-
* Keep the scope as narrow as possible, to make it easier to implement.
|
|
41
|
-
* Remember that this is a volunteer-driven project, and that contributions
|
|
42
|
-
are welcome :)
|
|
43
|
-
|
|
44
|
-
## Get Started!
|
|
45
|
-
|
|
46
|
-
Ready to contribute? Here's how to set up `apsg` for local development.
|
|
47
|
-
|
|
48
|
-
1. Fork the `apsg` repo on GitHub.
|
|
49
|
-
2. Clone your fork locally with SSH or HTTPS.
|
|
50
|
-
```
|
|
51
|
-
$ git clone git@github.com:ondrolexa/apsg.git
|
|
52
|
-
$ git clone https://github.com/ondrolexa/apsg.git
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
3. Install your local copy and activate the virtual environment via `pipenv`.
|
|
56
|
-
```
|
|
57
|
-
$ cd apsg/
|
|
58
|
-
$ pipenv shell
|
|
59
|
-
$ pipenv install --dev
|
|
60
|
-
```
|
|
61
|
-
4. Create a branch for local development.
|
|
62
|
-
```
|
|
63
|
-
$ git checkout -b name-of-your-bugfix-or-feature
|
|
64
|
-
```
|
|
65
|
-
6. Now you can make your changes locally.
|
|
66
|
-
|
|
67
|
-
7. When you're done making changes, check that your changes pass flake8 and the tests, including testing other Python versions with tox.
|
|
68
|
-
```
|
|
69
|
-
$ flake8 apsg tests
|
|
70
|
-
$ python setup.py test
|
|
71
|
-
$ tox
|
|
72
|
-
```
|
|
73
|
-
8. Commit your changes and push your branch to GitHub.
|
|
74
|
-
```
|
|
75
|
-
$ git add .
|
|
76
|
-
$ git commit -m "Your detailed description of your changes."
|
|
77
|
-
$ git push origin name-of-your-bugfix-or-feature
|
|
78
|
-
```
|
|
79
|
-
9. Submit a pull request through the GitHub website.
|
|
80
|
-
|
|
81
|
-
## Pull Request Guidelines
|
|
82
|
-
|
|
83
|
-
Before you submit a pull request, check that it meets these guidelines:
|
|
84
|
-
|
|
85
|
-
1. The pull request should include tests.
|
|
86
|
-
2. If the pull request adds functionality, the docs should be updated. Put
|
|
87
|
-
your new functionality into a function with a docstring, and add the
|
|
88
|
-
feature to the list in ``README.md``.
|
|
89
|
-
3. The pull request should work for Python 2.7, 3.5, and 3.6.
|
|
90
|
-
Check [https://travis-ci.org/ondrolexa/apsg/pull_requests](https://travis-ci.org/ondrolexa/apsg/pull_requests)
|
|
91
|
-
and make sure that the tests pass for all supported Python versions.
|
apsg-1.3.0.dist-info/AUTHORS.md
DELETED
apsg-1.3.0.dist-info/METADATA
DELETED
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: apsg
|
|
3
|
-
Version: 1.3.0
|
|
4
|
-
Summary: APSG - The package for structural geologists
|
|
5
|
-
Home-page: https://github.com/ondrolexa/apsg
|
|
6
|
-
License: MIT
|
|
7
|
-
Author: Ondrej Lexa
|
|
8
|
-
Author-email: lexa.ondrej@gmail.com
|
|
9
|
-
Requires-Python: >=3.10,<4.0
|
|
10
|
-
Classifier: Development Status :: 4 - Beta
|
|
11
|
-
Classifier: Intended Audience :: Science/Research
|
|
12
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
-
Classifier: Operating System :: OS Independent
|
|
14
|
-
Classifier: Programming Language :: Python :: 3
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
-
Requires-Dist: matplotlib (>=3.9,<4.0)
|
|
20
|
-
Requires-Dist: numpy (>=2.1,<3.0)
|
|
21
|
-
Requires-Dist: pandas (>=2.1,<3.0)
|
|
22
|
-
Requires-Dist: pyqt5 (>=5.15,<6.0)
|
|
23
|
-
Requires-Dist: scipy (>=1.14,<2.0)
|
|
24
|
-
Requires-Dist: sqlalchemy (>=1.3,<2.0)
|
|
25
|
-
Project-URL: Documentation, https://apsg.readthedocs.io
|
|
26
|
-
Project-URL: Repository, https://github.com/ondrolexa/apsg
|
|
27
|
-
Description-Content-Type: text/markdown
|
|
28
|
-
|
|
29
|
-
<img src="https://ondrolexa.github.io/apsg/apsg_banner.svg" alt="APSG logo" width="300px"/>
|
|
30
|
-
|
|
31
|
-
[](https://pypi.org/project/apsg)
|
|
32
|
-
[](https://anaconda.org/conda-forge/apsg)
|
|
33
|
-
[](https://apsg.readthedocs.io/en/stable/?badge=stable)
|
|
34
|
-
[](https://zenodo.org/badge/latestdoi/24879346)
|
|
35
|
-
|
|
36
|
-
## :thinking: What is APSG?
|
|
37
|
-
|
|
38
|
-
APSG is the package for structural geologists. It defines several new python classes to easily manage, analyze and visualize orientational structural geology data.
|
|
39
|
-
|
|
40
|
-
> [!IMPORTANT]
|
|
41
|
-
> APSG has been significantly refactored from version 1.0 and several changes are
|
|
42
|
-
> breaking backward compatibility. The main APSG namespace provides often-used
|
|
43
|
-
> classes in lowercase names as aliases to `PascalCase` convention used in
|
|
44
|
-
> modules to provide a simplified interface for users. The `PascalCase` names of
|
|
45
|
-
> classes use longer and plain English names instead acronyms for better readability.
|
|
46
|
-
>
|
|
47
|
-
> Check [documentation](https://apsg.readthedocs.org) for more details.
|
|
48
|
-
|
|
49
|
-
## :hammer_and_wrench: Requirements
|
|
50
|
-
|
|
51
|
-
You need Python 3.9 or later to run APSG. The package requires [NumPy](https://numpy.org/) and [SciPy](https://www.scipy.org/),
|
|
52
|
-
[Matplotlib](https://matplotlib.org/), [SciPy](https://scipy.org/), [SQLAlchemy](https://www.sqlalchemy.org/)
|
|
53
|
-
and [pandas](https://pandas.pydata.org/).
|
|
54
|
-
|
|
55
|
-
## :rocket: Quick start
|
|
56
|
-
|
|
57
|
-
Install the **latest stable** version of APSG from PyPI:
|
|
58
|
-
```bash
|
|
59
|
-
pip install apsg
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
or install **master** with:
|
|
63
|
-
```bash
|
|
64
|
-
pip install git+https://github.com/ondrolexa/apsg.git
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
Alternatively, you can cloce the repository and do a local install (recommended for dev):
|
|
68
|
-
```bash
|
|
69
|
-
git clone https://github.com/ondrolexa/apsg.git
|
|
70
|
-
cd apsg
|
|
71
|
-
pip install -e ."
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
#### Upgrading via pip
|
|
75
|
-
|
|
76
|
-
To upgrade an existing version of APSG from PyPI, execute:
|
|
77
|
-
```bash
|
|
78
|
-
pip install apsg --upgrade --no-deps
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
#### Comments on system-wide instalations on Debian systems
|
|
82
|
-
|
|
83
|
-
Latest Debian-based systems does not allow to install non-debian packages system-wide.
|
|
84
|
-
However, installing all requirements allows to force install APSG system-wide without troubles.
|
|
85
|
-
|
|
86
|
-
Install requirements using apt:
|
|
87
|
-
```bash
|
|
88
|
-
sudo apt install python3-numpy python3-matplotlib python3-scipy python3-sqlalchemy python3-pandas
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
and then install apsg using pip:
|
|
92
|
-
```bash
|
|
93
|
-
pip install --break-system-packages apsg
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
### Conda/Mamba
|
|
97
|
-
|
|
98
|
-
The APSG package is also available on `conda-forge` channel. Installing `apsg`
|
|
99
|
-
from the `conda-forge` channel can be achieved by adding `conda-forge` to your
|
|
100
|
-
channels:
|
|
101
|
-
|
|
102
|
-
```bash
|
|
103
|
-
conda config --add channels conda-forge
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
Once the `conda-forge` channel has been enabled, `apsg` can be installed with:
|
|
107
|
-
|
|
108
|
-
```bash
|
|
109
|
-
conda install apsg
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
It is possible to list all of the versions of `apsg` available on your platform with:
|
|
113
|
-
|
|
114
|
-
```bash
|
|
115
|
-
conda search apsg --channel conda-forge
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
#### Current release info
|
|
119
|
-
|
|
120
|
-
| Name | Downloads | Version | Platforms |
|
|
121
|
-
| --- | --- | --- | --- |
|
|
122
|
-
| [](https://anaconda.org/conda-forge/apsg) | [](https://anaconda.org/conda-forge/apsg) | [](https://anaconda.org/conda-forge/apsg) | [](https://anaconda.org/conda-forge/apsg) |
|
|
123
|
-
|
|
124
|
-
## :blue_book: Documentation
|
|
125
|
-
|
|
126
|
-
Explore all the features of APSG. You can find detailed documentation [here](https://apsg.readthedocs.org).
|
|
127
|
-
|
|
128
|
-
## :computer: Contributing
|
|
129
|
-
|
|
130
|
-
Most discussion happens on [Github](https://github.com/ondrolexa/apsg). Feel free to open [an issue](https://github.com/ondrolexa/apsg/issues/new) or comment on any open issue or pull request. Check ``CONTRIBUTING.md`` for more details.
|
|
131
|
-
|
|
132
|
-
## :coin: Donate
|
|
133
|
-
|
|
134
|
-
APSG is an open-source project, available for you for free. It took a lot of time and resources to build this software. If you find this software useful and want to support its future development please consider donating me.
|
|
135
|
-
|
|
136
|
-
[](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=QTYZWVUNDUAH8&item_name=APSG+development+donation¤cy_code=EUR&source=url)
|
|
137
|
-
|
|
138
|
-
## License
|
|
139
|
-
|
|
140
|
-
APSG is free software: you can redistribute it and/or modify it under the terms of the MIT License. A copy of this license is provided in ``LICENSE`` file.
|
|
141
|
-
|