具体需求根据街道中的单据量过滤5公里范围内,订单用户量80%范围的街道,并画出凸多边形
算法思路
1、根据某点过滤范围内的坐标
private void filterOverLimit(List<ShopSalesStreetPoint> radiationPointList, Point2D.Double shopPoint) {
ShopSalesStreetPoint point0 = radiationPointList.get(0);
Point2D.Double aDouble = new Point2D.Double(point0.getX(), point0.getY());
Iterator<ShopSalesStreetPoint> it = radiationPointList.iterator();
while (it.hasNext()) {
ShopSalesStreetPoint streetPoint = it.next();
double distance = DiscretePointUtil.countPointDistance(aDouble, new Point2D.Double(streetPoint.getX(), streetPoint.getY()));
if (distance > 5000) {
it.remove();
}
}
}
2、递归
首先创建第一个点并将订单量返回用于统计,默认点位数组根据订单用户量从大到小排序
private BigDecimal processFirstPoint(List<ShopSalesStreetPoint> allStreetPointList, List<ShopSalesStreetPoint> pointList) {
//提前计算第一个节点
ShopSalesStreetPoint firstPoint = allStreetPointList.get(0);
pointList.add(firstPoint);
BigDecimal checkValue = firstPoint.getValue();
//移除第一个
allStreetPointList.remove(0);
return checkValue;
}
计算阈值,达到80%阈值则返回
//达到阈值则返回
if (checkThresholdValue(checkValue, allValue)) {
return selectedPoint2DList;
}
public static boolean checkThresholdValue(BigDecimal check, BigDecimal all) {
//四舍五入不进位RoundingMode.DOWN
return check.divide(all, 3, RoundingMode.DOWN).multiply(new BigDecimal(100)).compareTo(new BigDecimal(VALUE_ACCOUNTED)) >= 0;
}
//执行后续
selectedPoint2DList = process(checkValue, allValue, allStreetPointList, selectedPoint2DList);
从已选点位中选取最小凸多边形,并且输出排序好的点位
private List<ShopSalesStreetPoint> getMinimumBoundingPolygon(List<ShopSalesStreetPoint> selectedPoint2DList) {
if (selectedPoint2DList.size() > 2) {
selectedPoint2DList.forEach(point -> point.setFounded(false));
selectedPoint2DList = MinimumBoundingPolygon.findSmallestPolygon(selectedPoint2DList);
}
return selectedPoint2DList;
}
process方法为递归方法具体内容如下:
private List<ShopSalesStreetPoint> process(BigDecimal checkValue, BigDecimal allValue,
List<ShopSalesStreetPoint> restStreetPointList, List<ShopSalesStreetPoint> selectedPoint2DList) {
//达到阈值则返回
if (checkThresholdValue(checkValue, allValue)) {
return selectedPoint2DList;
}
ShopSalesStreetPoint curPoint = restStreetPointList.get(0);
checkValue = checkValue.add(curPoint.getValue());
//达到阈值则返回
if (checkThresholdValue(checkValue, allValue)) {
selectedPoint2DList = getMinimumBoundingPolygon(selectedPoint2DList);
return selectedPoint2DList;
}
selectedPoint2DList.add(curPoint);
//根据已有点位获取最大多边形
if (selectedPoint2DList.size() > 2) {
selectedPoint2DList = getMinimumBoundingPolygon(selectedPoint2DList);
}
//达到阈值则返回
if (checkThresholdValue(checkValue, allValue)) {
return selectedPoint2DList;
}
//移除第一个进入判断将范围内的所有点移除,并加入总额返回
restStreetPointList.remove(0);
//所有节点循环结束退出
if (restStreetPointList.isEmpty()) {
return selectedPoint2DList;
}
checkValue = removeRestStreetPoint(checkValue, selectedPoint2DList, restStreetPointList);
//达到阈值则返回
if (checkThresholdValue(checkValue, allValue)) {
return selectedPoint2DList;
}
if (restStreetPointList.isEmpty()) {
return selectedPoint2DList;
}
selectedPoint2DList = process(checkValue, allValue, restStreetPointList, selectedPoint2DList);
return selectedPoint2DList;
}
递归主函数中用到的工具类如下
package cn.com.gome.shop.analyze.backend.service.assistant;
import cn.com.gome.shop.analyze.backend.data.assistant.ShopSalesStreetPoint;
import org.springframework.stereotype.Service;
import java.awt.geom.GeneralPath;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@Service
public class PointService {
/**
* 是否有 横断
* 参数为四个点的坐标
*
* @param px1
* @param py1
* @param px2
* @param py2
* @param px3
* @param py3
* @param px4
* @param py4
* @return
*/
public static boolean isIntersect(double px1, double py1, double px2, double py2, double px3, double py3, double px4,
double py4) {
boolean flag = false;
double d = (px2 - px1) * (py4 - py3) - (py2 - py1) * (px4 - px3);
if (d != 0) {
double r = ((py1 - py3) * (px4 - px3) - (px1 - px3) * (py4 - py3)) / d;
double s = ((py1 - py3) * (px2 - px1) - (px1 - px3) * (py2 - py1)) / d;
if ((r >= 0) && (r <= 1) && (s >= 0) && (s <= 1)) {
flag = true;
}
}
return flag;
}
/**
* 目标点是否在目标边上边上
*
* @param px0 目标点的经度坐标
* @param py0 目标点的纬度坐标
* @param px1 目标线的起点(终点)经度坐标
* @param py1 目标线的起点(终点)纬度坐标
* @param px2 目标线的终点(起点)经度坐标
* @param py2 目标线的终点(起点)纬度坐标
* @return
*/
public static boolean isPointOnLine(double px0, double py0, double px1, double py1, double px2, double py2) {
boolean flag = false;
double ESP = 1e-9;//无限小的正数
// if ((Math.abs(Multiply(px0, py0, px1, py1, px2, py2)) < ESP) && ((px0 - px1) * (px0 - px2) <= 0)
// && ((py0 - py1) * (py0 - py2) <= 0)) {
// flag = true;
// }
if (((px0 - px1) * (py1 - py2)) == ((px1 - px2) * (py0 - py1)) && (px0 >= min(px1, px2) && px0 <= max(px1, px2))
&& ((py0 >= min(py1, py2)) && (py0 <= max(py1, py2)))) {
flag = true;
}
return flag;
}
public static Double min(Double d1, Double d2) {
if (d1 < d2) {
return d1;
}
return d2;
}
public static Double max(Double d1, Double d2) {
if (d1 > d2) {
return d1;
}
return d2;
}
public static double Multiply(double px0, double py0, double px1, double py1, double px2, double py2) {
return ((px1 - px0) * (py2 - py0) - (px2 - px0) * (py1 - py0));
}
/**
* 判断目标点是否在多边形内(由多个点组成)
* 备用方案
*
* @param px 目标点的经度坐标
* @param py 目标点的纬度坐标
* @param polygonXA 多边形的经度坐标集合
* @param polygonYA 多边形的纬度坐标集合
* @return
*/
@Deprecated
public static boolean isPointInPolygon(double px, double py, ArrayList<Double> polygonXA, ArrayList<Double> polygonYA) {
boolean isInside = false;
double ESP = 1e-9;
int count = 0;
double linePoint1x;
double linePoint1y;
double linePoint2x = 180;
double linePoint2y;
linePoint1x = px;
linePoint1y = py;
linePoint2y = py;
for (int i = 0; i < polygonXA.size() - 1; i++) {
double cx1 = polygonXA.get(i);
double cy1 = polygonYA.get(i);
double cx2 = polygonXA.get(i + 1);
double cy2 = polygonYA.get(i + 1);
//如果目标点在任何一条线上
if (isPointOnLine(px, py, cx1, cy1, cx2, cy2)) {
return true;
}
//如果线段的长度无限小(趋于零)那么这两点实际是重合的,不足以构成一条线段
if (Math.abs(cy2 - cy1) < ESP) {
continue;
}
//第一个点是否在以目标点为基础衍生的平行纬度线
if (isPointOnLine(cx1, cy1, linePoint1x, linePoint1y, linePoint2x, linePoint2y)) {
//第二个点在第一个的下方,靠近赤道纬度为零(最小纬度)
if (cy1 > cy2)
count++;
}
//第二个点是否在以目标点为基础衍生的平行纬度线
else if (isPointOnLine(cx2, cy2, linePoint1x, linePoint1y, linePoint2x, linePoint2y)) {
//第二个点在第一个的上方,靠近极点(南极或北极)纬度为90(最大纬度)
if (cy2 > cy1)
count++;
}
//由两点组成的线段是否和以目标点为基础衍生的平行纬度线相交
else if (isIntersect(cx1, cy1, cx2, cy2, linePoint1x, linePoint1y, linePoint2x, linePoint2y)) {
count++;
}
}
if (count % 2 == 1) {
isInside = true;
}
return isInside;
}
public static boolean isInOnLine(Double pointLon, Double pointLat, Double lon1, Double lon2,
Double lat1, Double lat2) {
Line2D.Double aDouble = new Line2D.Double(lon1, lat1, lon2, lat2);
double v = aDouble.ptLineDist(pointLon, pointLat);
return v > 0;
}
/**
* 判断是否在多边形区域内
*
* @param pointLon 要判断的点的纵坐标
* @param pointLat 要判断的点的横坐标
* @param lons 区域各顶点的纵坐标数组
* @param lats 区域各顶点的横坐标数组
* @return
*/
public static boolean isInPolygon(Double pointLon, Double pointLat, Double[] lons,
Double[] lats) {
return isInPolygon(pointLon, pointLat, Arrays.asList(lons), Arrays.asList(lats));
}
/**
* 判断是否在多边形区域内
*
* @param pointLon 要判断的点的纵坐标
* @param pointLat 要判断的点的横坐标
* @param lons 区域各顶点的纵坐标数组
* @param lats 区域各顶点的横坐标数组
* @return
*/
public static boolean isInPolygon(Double pointLon, Double pointLat, List<Double> lons,
List<Double> lats) {
// 将要判断的横纵坐标组成一个点
Point2D.Double point = new Point2D.Double(pointLon, pointLat);
// 将区域各顶点的横纵坐标放到一个点集合里面
List<Point2D.Double> pointList = new ArrayList<Point2D.Double>();
for (int i = 0; i < lons.size(); i++) {
Point2D.Double polygonPoint = new Point2D.Double(lons.get(i), lats.get(i));
pointList.add(polygonPoint);
}
return check(point, pointList);
}
/**
* 判断点是否在多边形区域内
*
* @param checkStreetPoint
* @param streetPointList
* @return
*/
public static boolean isInPolygon(Point2D.Double checkStreetPoint, List<ShopSalesStreetPoint> streetPointList) {
// 将区域各顶点的横纵坐标放到一个点集合里面
List<Point2D.Double> point2DList = new ArrayList<Point2D.Double>();
for (ShopSalesStreetPoint streetPoint : streetPointList) {
Point2D.Double polygonPoint = new Point2D.Double(streetPoint.getY(), streetPoint.getX());
point2DList.add(polygonPoint);
}
return check(checkStreetPoint, point2DList);
}
/**
* 一个点是否在多边形内
*
* @param point 要判断的点的横纵坐标
* @param polygon 组成的顶点坐标集合
* @return
*/
private static boolean check(Point2D.Double point, List<Point2D.Double> polygon) {
GeneralPath generalPath = new GeneralPath();
Point2D.Double first = polygon.get(0);
// 通过移动到指定坐标(以双精度指定),将一个点添加到路径中
generalPath.moveTo(first.x, first.y);
polygon.remove(0);
for (Point2D.Double d : polygon) {
// 通过绘制一条从当前坐标到新指定坐标(以双精度指定)的直线,将一个点添加到路径中。
generalPath.lineTo(d.x, d.y);
}
// 将几何多边形封闭
generalPath.lineTo(first.x, first.y);
generalPath.closePath();
// 测试指定的 Point2D 是否在 Shape 的边界内。
return generalPath.contains(point);
}
}
下列工具类出自:https://blog.csdn.net/qq_38567039/article/details/118672509
package cn.com.gome.shop.analyze.backend.service.assistant;
import cn.com.gome.shop.analyze.backend.data.assistant.Point;
import org.gavaghan.geodesy.Ellipsoid;
import org.gavaghan.geodesy.GeodeticCalculator;
import org.gavaghan.geodesy.GlobalCoordinates;
import java.awt.geom.Point2D;
import java.util.List;
/**
*
* 离散点计算工具
*
*
* 离散点计算工具
*
* y
* ↑ · ·
* │ · · ·
* │ · · · ·
* │ · ·
* —│————————————→ x
*
*/
public class DiscretePointUtil {
/**
*
* 查找离散点集中的(min_x, min_Y) (max_x, max_Y)
*
*
* 查找离散点集中的(min_x, min_Y) (max_x, max_Y)
*
*
* @param points 离散点集
* @return [(min_x, min_Y), (max_x, max_Y)]
*/
public static Point[] calMinMaxDots(final List<Point> points) {
if (null == points || points.isEmpty()) {
return null;
}
double min_x = points.get(0).getX(), max_x = points.get(0).getX();
double min_y = points.get(0).getY(), max_y = points.get(0).getY();
/* 这里存在优化空间,可以使用并行计算 */
for (Point point : points) {
if (min_x > point.getX()) {
min_x = point.getX();
}
if (max_x < point.getX()) {
max_x = point.getX();
}
if (min_y > point.getY()) {
min_y = point.getY();
}
if (max_y < point.getY()) {
max_y = point.getY();
}
}
Point ws = new Point(min_x, min_y);
Point en = new Point(max_x, max_y);
return new Point[]{ws, en};
}
/**
*
* 求矩形面积平方根
*
*
* 以两个点作为矩形的对角线上的两点,计算其面积的平方根
*
*
* @param ws 西南点
* @param en 东北点
* @return 矩形面积平方根
*/
public static double calRectAreaSquare(Point ws, Point en) {
if (null == ws || null == en) {
return .0;
}
/* 为防止计算面积时float溢出,先计算各边平方根,再相乘 */
return Math.sqrt(Math.abs(ws.getX() - en.getX()))
* Math.sqrt(Math.abs(ws.getY() - en.getY()));
}
/**
*
* 求两点之间的长度 不能用于经纬度
*
*
* 求两点之间的长度
*
*
* @param ws 西南点
* @param en 东北点
* @return 两点之间的长度
*/
public static double calLineLen(Point ws, Point en) {
if (null == ws || null == en) {
return .0;
}
if (ws.equals(en)) {
return .0;
}
double a = Math.abs(ws.getX() - en.getX()); // 直角三角形的直边a
double b = Math.abs(ws.getY() - en.getY()); // 直角三角形的直边b
double min = Math.min(a, b); // 短直边
double max = Math.max(a, b); // 长直边
/**
* 为防止计算平方时float溢出,做如下转换
* √(min²+max²) = √((min/max)²+1) * abs(max)
*/
double inner = min / max;
return Math.sqrt(inner * inner + 1.0) * max;
}
/**
*
* 求两点间的中心点
*
*
* 求两点间的中心点
*
*
* @param ws 西南点
* @param en 东北点
* @return 两点间的中心点
*/
public static Point calCerter(Point ws, Point en) {
if (null == ws || null == en) {
return null;
}
return new Point(ws.getX() + (en.getX() - ws.getX()) / 2.0, ws.getY()
+ (en.getY() - ws.getY()) / 2.0);
}
/**
*
* 计算向量角
*
*
* 计算两点组成的向量与x轴正方向的向量角
*
*
* @param s 向量起点
* @param d 向量终点
* @return 向量角
*/
public static double angleOf(Point s, Point d) {
double dist = countPointDistance(new Point2D.Double(s.getX(), s.getY()), new Point2D.Double(d.getX(), d.getY()));
if (dist <= 0) {
return .0;
}
double x = d.getX() - s.getX(); // 直角三角形的直边a
double y = d.getY() - s.getY(); // 直角三角形的直边b
if (y >= 0.) { /* 1 2 象限 */
return Math.acos(x / dist);
} else { /* 3 4 象限 */
return Math.acos(-x / dist) + Math.PI;
}
}
/**
*
* 修正角度
*
*
* 修正角度到 [0, 2PI]
*
*
* @param angle 原始角度
* @return 修正后的角度
*/
public static double reviseAngle(double angle) {
while (angle < 0.) {
angle += 2 * Math.PI;
}
while (angle >= 2 * Math.PI) {
angle -= 2 * Math.PI;
}
return angle;
}
/**
* 计算坐标点之间距离
* WGS84坐标系
*
* @param pointA
* @param pointB
* @return
*/
public static double countPointDistance(Point2D.Double pointA, Point2D.Double pointB) {
return getDistanceMeter(new GlobalCoordinates(pointA.getY(), pointA.getX()), new GlobalCoordinates(pointB.getY(), pointB.getX()), Ellipsoid.WGS84);
}
public static double getDistanceMeter(GlobalCoordinates gpsFrom, GlobalCoordinates gpsTo, Ellipsoid ellipsoid) {
//创建GeodeticCalculator,调用计算方法,传入坐标系、经纬度用于计算距离
return new GeodeticCalculator().calculateGeodeticCurve(ellipsoid, gpsFrom, gpsTo).getEllipsoidalDistance();
}
}
package cn.com.gome.shop.analyze.backend.service.assistant;
import cn.com.gome.shop.analyze.backend.data.assistant.Point;
import cn.com.gome.shop.analyze.backend.data.assistant.ShopSalesStreetPoint;
import java.util.*;
/**
*
* 最小(凸)包围边界查找
*
*
* 最小(凸)包围边界查找
*
* Minimum Bounding Polygon (Convex Hull; Smallest Enclosing A Set of Points)
* ©2009 Darel Rex Finley.
*
* y
* ↑ · ·
* │ · · ·
* │ · · · ·
* │ · ·
* —│————————————→ x
*
*
*/
public class MinimumBoundingPolygon {
public static LinkedList<ShopSalesStreetPoint> findSmallestPolygon(List<ShopSalesStreetPoint> ps) {
if (null == ps || ps.isEmpty()) {
return null;
}
//查找起始点(保证y最大的情况下、尽量使x最小的点)
ShopSalesStreetPoint corner = findStartPoint(ps);
if (null == corner) {
return null;
}
double minAngleDif, oldAngle = 2 * Math.PI;
LinkedList<ShopSalesStreetPoint> bound = new LinkedList<>();
do {
minAngleDif = 2 * Math.PI;
bound.add(corner);
ShopSalesStreetPoint nextPoint = corner;
double nextAngle = oldAngle;
for (ShopSalesStreetPoint p : ps) {
if (p.isFounded()) { // 已被加入边界链表的点
continue;
}
if (p.equals(corner)) { // 重合点
/*if (!p.equals(bound.getFirst())) {
p.founded = true;
}*/
continue;
}
double currAngle = DiscretePointUtil.angleOf(new Point(corner.getX(), corner.getY()), new Point(p.getX(), p.getY())); /* 当前向量与x轴正方向的夹角 */
double angleDif = DiscretePointUtil.reviseAngle(oldAngle - currAngle); /* 两条向量之间的夹角(顺时针旋转的夹角) */
if (angleDif < minAngleDif) {
minAngleDif = angleDif;
nextPoint = p;
nextAngle = currAngle;
}
}
oldAngle = nextAngle;
corner = nextPoint;
corner.setFounded(true);
} while (!corner.equals(bound.getFirst())); /* 判断边界是否闭合 */
return bound;
}
/**
* 查找起始点(保证y最大的情况下、尽量使x最小的点)
*/
private static ShopSalesStreetPoint findStartPoint(List<ShopSalesStreetPoint> ps) {
if (null == ps || ps.isEmpty()) {
return null;
}
ShopSalesStreetPoint p = ps.get(0);
ListIterator<ShopSalesStreetPoint> iter = ps.listIterator();
while (iter.hasNext()) {
ShopSalesStreetPoint point = iter.next();
if (point.getY() > p.getY() || (point.getY() == p.getY() && point.getX() < p.getX())) { /* 找到最靠上靠左的点 */
p = point;
}
}
return p;
}
private static double cal_cross_product(Point A, Point B, Point C) {
double AB[] = {B.getX() - A.getX(), B.getY() - A.getY()};
double AC[] = {C.getX() - A.getX(), C.getY() - A.getY()};
return AB[0] * AC[1] - AB[1] * AC[0];
}
/**
* 验证点位是否为凸多边形
*
* @param points
* @return
*/
public static boolean isConvex(List<Point> points) {
double flag = 0;
int n = points.size();
for (int i = 0; i < n; i++) {
//cur > 0 表示points是按逆时针输出的;cur< 0, 顺时针
Double cur = cal_cross_product(points.get(i), points.get((i + 1) % n), points.get((i + 2) % n));
if (cur != 0) {
//说明异号, 说明有个角大于180度
if (cur * flag < 0) {
return false;
} else {
flag = cur;
}
}
}
return true;
}
public static void main(String[] args) {
//交道口街道
Double lng = 116.400978;
Double lat = 39.93576;
//东直门
Double lng1 = 116.441826;
Double lat1 = 39.930855;
//龙潭街道
Double lng2 = 116.436783;
Double lat2 = 39.887241;
//体育馆路街道
Double lng3 = 116.422462;
Double lat3 = 39.88694;
//东四街道
Double lng4 = 116.423988;
Double lat4 = 39.930332;
List<Point> pointList = new ArrayList<>();
pointList.add(new Point(lng, lat));
pointList.add(new Point(lng1, lat1));
pointList.add(new Point(lng2, lat2));
pointList.add(new Point(lng3, lat3));
pointList.add(new Point(lng4, lat4));
long start = new Date().getTime();
boolean convex = isConvex(pointList);
long end = new Date().getTime();
System.out.println(convex);
System.out.println("执行时间:" + (start - end));
}
}
使用的对象类如下
数据源对象
package cn.com.gome.shop.analyze.backend.data.assistant;
import java.io.Serializable;
import lombok.Data;
/**
* dwr_store_town_sales_info
* @author
*/
@Data
public class DwrStoreTownSalesInfo implements Serializable {
/**
* 门店编码
*/
private String storeCode;
/**
* 一级编码
*/
private String firstCode;
/**
* 一级区域
*/
private String firstArea;
/**
* 二级编码
*/
private String secondCode;
/**
* 二级区域
*/
private String secondArea;
/**
* 三级编码
*/
private String thirdCode;
/**
* 三级区域
*/
private String thirdArea;
/**
* 四级编码
*/
private String fouthCode;
/**
* 四级区域
*/
private String fouthArea;
/**
* 是否门店内数据:1是,0否
*/
private Integer isStore;
/**
* 订单用户数
*/
private Double orderUserNum;
/**
* 销量
*/
private Double salesNum;
/**
* 销售额
*/
private Double salesAmt;
/**
* 经度
*/
private Double x;
/**
* 纬度
*/
private Double y;
/**
* 数据统计同步时间
*/
private String dt;
private static final long serialVersionUID = 1L;
}
计算所需对象
package cn.com.gome.shop.analyze.backend.data.assistant;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Objects;
/**
* @author hanhao
*/
@Data
public class ShopSalesStreetPoint {
public ShopSalesStreetPoint() {
}
public ShopSalesStreetPoint(double x, double y) {
this.x = x;
this.y = y;
}
/**
* x坐标
*/
private double x;
/**
* y坐标
*/
private double y;
/**
* 边界查找算法中 是否被找到
*/
boolean founded = false;
public double getX() {
return x;
}
public void setX(double x) {
this.x = x;
}
public double getY() {
return y;
}
public void setY(double y) {
this.y = y;
}
public boolean isFounded() {
return founded;
}
public void setFounded(boolean founded) {
this.founded = founded;
}
/** Constructor Getters & Setters */
/**
* 计算值,具体内容可配置
*/
private BigDecimal value;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ShopSalesStreetPoint that = (ShopSalesStreetPoint) o;
return Objects.equals(getX(), that.getX()) &&
Objects.equals(getY(), that.getY());
}
}
离散点
package cn.com.gome.shop.analyze.backend.data.assistant;
import lombok.Data;
import java.io.Serializable;
/**
*
* 离散点
*
*
* 离散点
*
*
*/
public class Point implements Serializable {
public Point() {
}
public Point(double x, double y) {
this.x = x;
this.y = y;
}
/**
* x坐标
*/
private double x;
/**
* y坐标
*/
private double y;
/**
* 边界查找算法中 是否被找到
*/
boolean founded = false;
public double getX() {
return x;
}
public void setX(double x) {
this.x = x;
}
public double getY() {
return y;
}
public void setY(double y) {
this.y = y;
}
public boolean isFounded() {
return founded;
}
public void setFounded(boolean founded) {
this.founded = founded;
}
/** Constructor Getters & Setters */
}