Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
T
traffic-signal-platform
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
signal
traffic-signal-platform
Commits
b71a95c6
Commit
b71a95c6
authored
May 17, 2023
by
hanbing
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[add] 根据起始点获取一条最优线路
parent
255e2a9f
Changes
8
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
2166 additions
and
113 deletions
+2166
-113
SpecialServiceRouteBO.java
...src/main/java/net/wanji/web/bo/SpecialServiceRouteBO.java
+4
-4
SpecialServiceServiceImpl.java
...net/wanji/web/service/impl/SpecialServiceServiceImpl.java
+88
-109
GeometryUtil.java
...mmon/src/main/java/net/wanji/common/gts/GeometryUtil.java
+112
-0
Tools.java
wj-common/src/main/java/net/wanji/common/gts/Tools.java
+1577
-0
GtsService.java
...rc/main/java/net/wanji/common/gts/service/GtsService.java
+90
-0
GtsServiceImpl.java
...ava/net/wanji/common/gts/service/impl/GtsServiceImpl.java
+287
-0
RidInfoMapper.java
...main/java/net/wanji/databus/dao/mapper/RidInfoMapper.java
+2
-0
RidInfoMapper.xml
wj-databus/src/main/resources/mapper/RidInfoMapper.xml
+6
-0
No files found.
signal-control-service/src/main/java/net/wanji/web/bo/SpecialServiceRouteBO.java
View file @
b71a95c6
...
...
@@ -9,8 +9,8 @@ import lombok.Data;
*/
@Data
public
class
SpecialServiceRouteBO
{
@ApiModelProperty
(
value
=
"起点坐标"
)
private
double
[]
startLonLat
;
@ApiModelProperty
(
value
=
"终点坐标"
)
private
double
[]
endLonLat
;
@ApiModelProperty
(
value
=
"起点坐标
,格式:112.968523,28.179455
"
)
private
String
startLonLat
;
@ApiModelProperty
(
value
=
"终点坐标
,格式:112.968523,28.179455
"
)
private
String
endLonLat
;
}
signal-control-service/src/main/java/net/wanji/web/service/impl/SpecialServiceServiceImpl.java
View file @
b71a95c6
This diff is collapsed.
Click to expand it.
wj-common/src/main/java/net/wanji/common/gts/GeometryUtil.java
0 → 100644
View file @
b71a95c6
package
net
.
wanji
.
common
.
gts
;
import
org.locationtech.jts.geom.Geometry
;
import
org.locationtech.jts.io.ParseException
;
import
org.locationtech.jts.io.WKTReader
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
java.util.regex.Matcher
;
import
java.util.regex.Pattern
;
public
class
GeometryUtil
{
public
final
static
double
PI
=
3.1415926535898
D
;
private
static
Logger
log
=
LoggerFactory
.
getLogger
(
GeometryUtil
.
class
);
public
static
Geometry
str2Geometry
(
String
coors
)
{
Geometry
geometry
=
null
;
int
num
=
getSubNumber
(
coors
,
";"
);
try
{
if
(
num
==
0
)
{
// 点
geometry
=
getTheGeom
(
coors
,
1
);
}
else
if
(
num
>
0
)
{
String
[]
_s
=
coors
.
split
(
";"
);
if
(
_s
[
0
].
equals
(
_s
[
_s
.
length
-
1
]))
{
// 面
geometry
=
getTheGeom
(
coors
,
3
);
}
else
{
// 线
geometry
=
getTheGeom
(
coors
,
2
);
}
}
}
catch
(
Exception
e
)
{
log
.
error
(
"str2Geometry"
,
e
);
}
return
geometry
;
}
public
static
int
getSubNumber
(
String
des
,
String
reg
)
{
Pattern
p
=
Pattern
.
compile
(
reg
);
Matcher
m
=
p
.
matcher
(
des
);
int
count
=
0
;
while
(
m
.
find
())
{
count
++;
}
return
count
;
}
public
static
Geometry
getTheGeom
(
String
coors
,
int
type
)
throws
ParseException
{
coors
=
coors
.
replaceAll
(
";"
,
","
);
String
[]
xys
=
coors
.
split
(
","
);
StringBuffer
xy
=
null
;
String
endString
=
""
;
if
(
type
==
1
)
{
// 点
xy
=
new
StringBuffer
(
"POINT("
);
endString
=
")"
;
}
else
if
(
type
==
2
)
{
// 线
xy
=
new
StringBuffer
(
"LINESTRING("
);
endString
=
")"
;
}
else
if
(
type
==
3
)
{
// 面
xy
=
new
StringBuffer
(
"POLYGON(("
);
String
x
=
xys
[
0
];
String
y
=
xys
[
1
];
String
endy
=
xys
[
xys
.
length
-
1
];
if
(
y
.
equals
(
endy
))
{
endString
=
"))"
;
}
else
{
endString
=
","
+
x
+
" "
+
y
+
"))"
;
}
}
for
(
int
i
=
0
;
i
<
xys
.
length
;
i
+=
2
)
{
String
x
=
xys
[
i
];
String
y
=
xys
[
i
+
1
];
xy
.
append
(
x
);
xy
.
append
(
" "
);
xy
.
append
(
y
);
xy
.
append
(
","
);
}
String
wkt
=
xy
.
substring
(
0
,
xy
.
length
()
-
1
)
+
endString
;
Geometry
geometry
=
new
WKTReader
().
read
(
wkt
);
geometry
.
setSRID
(
4326
);
return
geometry
;
}
public
static
double
getDistance
(
double
x1
,
double
y1
,
double
x2
,
double
y2
)
{
double
latRadians1
=
y1
*
(
Math
.
PI
/
180
);
double
latRadians2
=
y2
*
(
Math
.
PI
/
180
);
double
latRadians
=
latRadians1
-
latRadians2
;
double
lngRadians
=
x1
*
(
Math
.
PI
/
180
)
-
x2
*
(
Math
.
PI
/
180
);
double
f
=
2
*
Math
.
asin
(
Math
.
sqrt
(
Math
.
pow
(
Math
.
sin
(
latRadians
/
2
),
2
)
+
Math
.
cos
(
latRadians1
)
*
Math
.
cos
(
latRadians2
)
*
Math
.
pow
(
Math
.
sin
(
lngRadians
/
2
),
2
)));
return
f
*
6378137
;
}
public
static
double
calcAngle
(
double
fStartLon
,
double
fStartLat
,
double
fEndLon
,
double
fEndLat
)
{
double
fAngle
=
0
;
if
(
fEndLon
!=
fStartLon
)
{
double
fAtan
=
(
fEndLat
-
fStartLat
)
/
(
fEndLon
-
fStartLon
);
fAngle
=
Math
.
atan
(
fAtan
);
if
(
fEndLon
-
fStartLon
<
0
)
fAngle
+=
PI
;
else
if
(
fAngle
<
0
)
fAngle
+=
2
*
PI
;
}
else
{
if
(
fEndLat
>
fStartLat
)
{
fAngle
=
PI
/
2
;
}
else
if
(
fEndLat
<
fStartLat
)
{
fAngle
=
PI
/
2
*
3
;
}
else
if
(
fEndLat
==
fStartLat
)
{
fAngle
=
0
;
}
}
return
fAngle
;
}
}
wj-common/src/main/java/net/wanji/common/gts/Tools.java
0 → 100644
View file @
b71a95c6
This diff is collapsed.
Click to expand it.
wj-common/src/main/java/net/wanji/common/gts/service/GtsService.java
0 → 100644
View file @
b71a95c6
package
net
.
wanji
.
common
.
gts
.
service
;
public
interface
GtsService
{
/**
* acoor和bcoor的交集
* @param acoor
* @param bcoor
* @return 交集坐标对
* @throws Exception
*/
public
String
intersection
(
String
acoor
,
String
bcoor
)
throws
Exception
;
/**
* acoor和bcoor的并集
* @param acoor
* @param bcoor
* @return 并集坐标对
*/
public
String
union
(
String
acoor
,
String
bcoor
);
/**
* 获取coor的指定distance距离的缓冲区
* @param coor
* @param distance
* @return 缓冲区坐标对
*/
public
String
buffer
(
String
coor
,
double
distance
);
/**
* acoor是否包含bcoor
* @param acoor
* @param bcoor
* @return
*/
public
boolean
contains
(
String
acoor
,
String
bcoor
);
/**
* acoor与bcoor是否相交
* @param acoor
* @param bcoor
* @return
*/
public
boolean
intersects
(
String
acoor
,
String
bcoor
);
/**
* 获取acoor与bcoor的差异
* @param acoor
* @param bcoor
* @return 差异集合点集
*/
public
String
different
(
String
acoor
,
String
bcoor
);
/**
* 对称差异分析
* @param acoor
* @param bcoor
* @return 对称差异坐标对。多区域用#分割
*/
public
String
symDifferent
(
String
acoor
,
String
bcoor
);
/**
* 获取acoor和bcoor的距离
* @param acoor
* @param bcoor
* @return 距离
*/
public
double
distance
(
String
acoor
,
String
bcoor
);
/**
* 计算多边形面积
* @param coor
* @return 多边形面积
*/
public
double
polygonarea
(
String
coor
);
/**
* 获取中心点
* @param coor
* @return 中心点坐标
*/
public
String
centroid
(
String
coor
);
/**
* 获取外接多边形
* @param coor
* @return
*/
public
String
bounds
(
String
coor
);
}
wj-common/src/main/java/net/wanji/common/gts/service/impl/GtsServiceImpl.java
0 → 100644
View file @
b71a95c6
package
net
.
wanji
.
common
.
gts
.
service
.
impl
;
import
net.wanji.common.gts.GeometryUtil
;
import
net.wanji.common.gts.Tools
;
import
net.wanji.common.gts.service.GtsService
;
import
org.locationtech.jts.geom.Coordinate
;
import
org.locationtech.jts.geom.Geometry
;
import
org.locationtech.jts.geom.Point
;
import
org.springframework.stereotype.Service
;
@Service
public
class
GtsServiceImpl
implements
GtsService
{
@Override
public
String
intersection
(
String
acoor
,
String
bcoor
)
throws
Exception
{
Geometry
geometryA
=
GeometryUtil
.
str2Geometry
(
acoor
);
Geometry
geometryb
=
GeometryUtil
.
str2Geometry
(
bcoor
);
if
(
geometryA
==
null
||
geometryb
==
null
)
{
return
null
;
}
else
{
Geometry
intersections
=
geometryA
.
intersection
(
geometryb
);
int
num
=
intersections
.
getNumGeometries
();
StringBuffer
sb
=
new
StringBuffer
();
Geometry
temp
=
null
;
Coordinate
[]
coors
=
null
;
for
(
int
i
=
0
;
i
<
num
;
i
++)
{
temp
=
intersections
.
getGeometryN
(
i
);
coors
=
temp
.
getCoordinates
();
for
(
int
j
=
0
;
j
<
coors
.
length
;
j
++)
{
if
(
j
==
coors
.
length
-
1
)
{
sb
.
append
(
Tools
.
getNumberFormatString
(
coors
[
j
].
x
,
6
,
6
)
+
","
+
Tools
.
getNumberFormatString
(
coors
[
j
].
y
,
6
,
6
));
}
else
{
sb
.
append
(
Tools
.
getNumberFormatString
(
coors
[
j
].
x
,
6
,
6
)
+
","
+
Tools
.
getNumberFormatString
(
coors
[
j
].
y
,
6
,
6
)+
";"
);
}
}
sb
.
append
(
"#"
);
}
return
sb
.
substring
(
0
,
sb
.
length
()
-
1
);
}
}
@Override
public
String
union
(
String
acoor
,
String
bcoor
)
{
Geometry
geometryA
=
GeometryUtil
.
str2Geometry
(
acoor
);
Geometry
geometryb
=
GeometryUtil
.
str2Geometry
(
bcoor
);
if
(
geometryA
==
null
||
geometryb
==
null
)
{
return
null
;
}
else
{
Geometry
intersections
=
geometryA
.
union
(
geometryb
);
int
num
=
intersections
.
getNumGeometries
();
StringBuffer
sb
=
new
StringBuffer
();
Geometry
temp
=
null
;
Coordinate
[]
coors
=
null
;
for
(
int
i
=
0
;
i
<
num
;
i
++)
{
temp
=
intersections
.
getGeometryN
(
i
);
coors
=
temp
.
getCoordinates
();
for
(
int
j
=
0
;
j
<
coors
.
length
;
j
++)
{
if
(
j
==
coors
.
length
-
1
)
{
sb
.
append
(
Tools
.
getNumberFormatString
(
coors
[
j
].
x
,
6
,
6
)
+
","
+
Tools
.
getNumberFormatString
(
coors
[
j
].
y
,
6
,
6
));
}
else
{
sb
.
append
(
Tools
.
getNumberFormatString
(
coors
[
j
].
x
,
6
,
6
)
+
","
+
Tools
.
getNumberFormatString
(
coors
[
j
].
y
,
6
,
6
)+
";"
);
}
}
sb
.
append
(
"#"
);
}
// bean.setResult(sb.substring(0, sb.length() - 1));
return
sb
.
substring
(
0
,
sb
.
length
()
-
1
);
}
}
@Override
public
String
buffer
(
String
coor
,
double
distance
)
{
// ResultBean<String> bean = new ResultBean<String>();
Geometry
geometry
=
GeometryUtil
.
str2Geometry
(
coor
);
if
(
geometry
==
null
)
{
return
null
;
// bean.setSuccessful(false);
// bean.setInfo("坐标格式错误");
}
else
{
Geometry
intersections
=
geometry
.
buffer
(
distance
/
111.12
);
// bean.setSuccessful(true);
int
num
=
intersections
.
getNumGeometries
();
StringBuffer
sb
=
new
StringBuffer
();
Geometry
temp
=
null
;
Coordinate
[]
coors
=
null
;
for
(
int
i
=
0
;
i
<
num
;
i
++)
{
temp
=
intersections
.
getGeometryN
(
i
);
coors
=
temp
.
getCoordinates
();
for
(
int
j
=
0
;
j
<
coors
.
length
;
j
++)
{
if
(
j
==
coors
.
length
-
1
)
{
sb
.
append
(
Tools
.
getNumberFormatString
(
coors
[
j
].
x
,
6
,
6
)
+
","
+
Tools
.
getNumberFormatString
(
coors
[
j
].
y
,
6
,
6
));
}
else
{
sb
.
append
(
Tools
.
getNumberFormatString
(
coors
[
j
].
x
,
6
,
6
)
+
","
+
Tools
.
getNumberFormatString
(
coors
[
j
].
y
,
6
,
6
)+
";"
);
}
}
sb
.
append
(
"#"
);
}
if
(
num
!=
0
)
{
return
sb
.
substring
(
0
,
sb
.
length
()
-
1
);
}
}
return
null
;
}
@Override
public
boolean
contains
(
String
acoor
,
String
bcoor
)
{
Geometry
geometryA
=
GeometryUtil
.
str2Geometry
(
acoor
);
Geometry
geometryb
=
GeometryUtil
.
str2Geometry
(
bcoor
);
if
(
geometryA
==
null
||
geometryb
==
null
)
{
return
false
;
}
else
{
Boolean
contains
=
geometryA
.
contains
(
geometryb
);
return
contains
;
}
// return bean;
}
@Override
public
boolean
intersects
(
String
acoor
,
String
bcoor
)
{
// ResultBean<String> bean = new ResultBean<String>();
Geometry
geometryA
=
GeometryUtil
.
str2Geometry
(
acoor
);
Geometry
geometryb
=
GeometryUtil
.
str2Geometry
(
bcoor
);
if
(
geometryA
==
null
||
geometryb
==
null
)
{
// bean.setSuccessful(false);
// bean.setInfo("坐标格式错误");
return
false
;
}
else
{
Boolean
contains
=
geometryA
.
intersects
(
geometryb
);
// bean.setSuccessful(true);
// bean.setResult(contains+"");
return
contains
;
}
// return bean;
}
@Override
public
String
different
(
String
acoor
,
String
bcoor
)
{
// ResultBean<String> bean = new ResultBean<String>();
Geometry
geometryA
=
GeometryUtil
.
str2Geometry
(
acoor
);
Geometry
geometryb
=
GeometryUtil
.
str2Geometry
(
bcoor
);
if
(
geometryA
==
null
||
geometryb
==
null
)
{
// bean.setSuccessful(false);
// bean.setInfo("坐标格式错误");
return
null
;
}
else
{
Geometry
intersections
=
geometryA
.
difference
(
geometryb
);
// bean.setSuccessful(true);
int
num
=
intersections
.
getNumGeometries
();
StringBuffer
sb
=
new
StringBuffer
();
Geometry
temp
=
null
;
Coordinate
[]
coors
=
null
;
for
(
int
i
=
0
;
i
<
num
;
i
++)
{
temp
=
intersections
.
getGeometryN
(
i
);
coors
=
temp
.
getCoordinates
();
for
(
int
j
=
0
;
j
<
coors
.
length
;
j
++)
{
if
(
j
==
coors
.
length
-
1
)
{
sb
.
append
(
Tools
.
getNumberFormatString
(
coors
[
j
].
x
,
6
,
6
)
+
","
+
Tools
.
getNumberFormatString
(
coors
[
j
].
y
,
6
,
6
));
}
else
{
sb
.
append
(
Tools
.
getNumberFormatString
(
coors
[
j
].
x
,
6
,
6
)
+
","
+
Tools
.
getNumberFormatString
(
coors
[
j
].
y
,
6
,
6
)+
";"
);
}
}
sb
.
append
(
"#"
);
}
return
sb
.
substring
(
0
,
sb
.
length
()
-
1
);
}
// return bean;
}
@Override
public
String
symDifferent
(
String
acoor
,
String
bcoor
)
{
// ResultBean<String> bean = new ResultBean<String>();
Geometry
geometryA
=
GeometryUtil
.
str2Geometry
(
acoor
);
Geometry
geometryb
=
GeometryUtil
.
str2Geometry
(
bcoor
);
if
(
geometryA
==
null
||
geometryb
==
null
)
{
// bean.setSuccessful(false);
// bean.setInfo("坐标格式错误");
return
null
;
}
else
{
Geometry
intersections
=
geometryA
.
symDifference
(
geometryb
);
// bean.setSuccessful(true);
int
num
=
intersections
.
getNumGeometries
();
StringBuffer
sb
=
new
StringBuffer
();
Geometry
temp
=
null
;
Coordinate
[]
coors
=
null
;
for
(
int
i
=
0
;
i
<
num
;
i
++)
{
temp
=
intersections
.
getGeometryN
(
i
);
coors
=
temp
.
getCoordinates
();
for
(
int
j
=
0
;
j
<
coors
.
length
;
j
++)
{
if
(
j
==
coors
.
length
-
1
)
{
sb
.
append
(
Tools
.
getNumberFormatString
(
coors
[
j
].
x
,
6
,
6
)
+
","
+
Tools
.
getNumberFormatString
(
coors
[
j
].
y
,
6
,
6
));
}
else
{
sb
.
append
(
Tools
.
getNumberFormatString
(
coors
[
j
].
x
,
6
,
6
)
+
","
+
Tools
.
getNumberFormatString
(
coors
[
j
].
y
,
6
,
6
)+
";"
);
}
}
sb
.
append
(
"#"
);
}
return
sb
.
substring
(
0
,
sb
.
length
()
-
1
);
}
// return bean;
}
@Override
public
double
distance
(
String
acoor
,
String
bcoor
)
{
// ResultBean<String> bean = new ResultBean<String>();
Geometry
geometryA
=
GeometryUtil
.
str2Geometry
(
acoor
);
Geometry
geometryb
=
GeometryUtil
.
str2Geometry
(
bcoor
);
if
(
geometryA
==
null
||
geometryb
==
null
)
{
// bean.setSuccessful(false);
// bean.setInfo("坐标格式错误");
return
-
1
;
}
else
{
double
distance
=
geometryA
.
distance
(
geometryb
)*
111.12
;
//不知道为什么要乘以111.12(一度是约111.12公里)
// bean.setSuccessful(true);
// bean.setResult(distance);
return
distance
;
}
// return bean;
}
@Override
public
double
polygonarea
(
String
coor
)
{
// ResultBean<String> bean = new ResultBean<String>();
Geometry
geometry
=
GeometryUtil
.
str2Geometry
(
coor
);
if
(
geometry
==
null
)
{
// bean.setSuccessful(false);
// bean.setInfo("坐标格式错误");
return
-
1
;
}
else
{
double
area
=
geometry
.
getArea
()*
10000
;
//不知道为什么要这样计算(可能一度=60海里 1海里=1852km)
// bean.setSuccessful(true);
// bean.setResult(area);
return
area
;
}
// return bean;
}
@Override
public
String
centroid
(
String
coor
)
{
// ResultBean<String> bean = new ResultBean<String>();
Geometry
geometry
=
GeometryUtil
.
str2Geometry
(
coor
);
if
(
geometry
==
null
)
{
// bean.setSuccessful(false);
// bean.setInfo("坐标格式错误");
return
null
;
}
else
{
Point
point
=
geometry
.
getCentroid
();
return
Tools
.
getNumberFormatString
(
point
.
getX
(),
6
,
6
)
+
","
+
Tools
.
getNumberFormatString
(
point
.
getY
(),
6
,
6
);
}
// return bean;
}
@Override
public
String
bounds
(
String
coor
)
{
// ResultBean<String> bean = new ResultBean<String>();
Geometry
geometry
=
GeometryUtil
.
str2Geometry
(
coor
);
if
(
geometry
==
null
)
{
// bean.setSuccessful(false);
// bean.setInfo("坐标格式错误");
return
null
;
}
else
{
Geometry
boundGeom
=
geometry
.
getBoundary
();
// bean.setSuccessful(true);
int
num
=
boundGeom
.
getNumGeometries
();
StringBuffer
sb
=
new
StringBuffer
();
Geometry
temp
=
null
;
Coordinate
[]
coors
=
null
;
for
(
int
i
=
0
;
i
<
num
;
i
++)
{
temp
=
boundGeom
.
getGeometryN
(
i
);
coors
=
temp
.
getCoordinates
();
for
(
int
j
=
0
;
j
<
coors
.
length
;
j
++)
{
if
(
j
==
coors
.
length
-
1
)
{
sb
.
append
(
Tools
.
getNumberFormatString
(
coors
[
j
].
x
,
6
,
6
)
+
","
+
Tools
.
getNumberFormatString
(
coors
[
j
].
y
,
6
,
6
));
}
else
{
sb
.
append
(
Tools
.
getNumberFormatString
(
coors
[
j
].
x
,
6
,
6
)
+
","
+
Tools
.
getNumberFormatString
(
coors
[
j
].
y
,
6
,
6
)+
";"
);
}
}
sb
.
append
(
"#"
);
}
return
sb
.
substring
(
0
,
sb
.
length
()
-
1
);
}
// return bean;
}
}
wj-databus/src/main/java/net/wanji/databus/dao/mapper/RidInfoMapper.java
View file @
b71a95c6
...
...
@@ -27,4 +27,6 @@ public interface RidInfoMapper {
RidInfoEntity
selectByEndInDir
(
String
endCrossId
,
int
spilloverDirInt
);
List
<
RidInfoEntity
>
selectAll
();
List
<
RidInfoEntity
>
selectUpCross
(
String
startCrossId
);
}
wj-databus/src/main/resources/mapper/RidInfoMapper.xml
View file @
b71a95c6
...
...
@@ -81,4 +81,10 @@
select
<include
refid=
"Base_Column_List"
/>
from t_base_rid_info
</select>
<select
id=
"selectUpCross"
resultType=
"net.wanji.databus.dao.entity.RidInfoEntity"
>
select
<include
refid=
"Base_Column_List"
/>
from t_base_rid_info
where end_cross_id = #{startCrossId}
</select>
</mapper>
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment