Browse Source

refactor: 주소로 지역좌표 조회하는 코드를 CoordUtils로 이동

feature/address-coordinate
lkd9125(이경도) 2 months ago
parent
commit
9b0451a9e9
  1. 19
      pav-server/src/main/java/com/palnet/biz/api/comn/coordinate/model/SearchAddressCoordinateRS.java
  2. 233
      pav-server/src/main/java/com/palnet/biz/api/comn/coordinate/service/ComnCoordinateService.java
  3. 269
      pav-server/src/main/java/com/palnet/comn/utils/CoordUtils.java

19
pav-server/src/main/java/com/palnet/biz/api/comn/coordinate/model/SearchAddressCoordinateRS.java

@ -1,5 +1,6 @@
package com.palnet.biz.api.comn.coordinate.model; package com.palnet.biz.api.comn.coordinate.model;
import com.palnet.comn.utils.CoordUtils;
import lombok.Data; import lombok.Data;
import java.util.List; import java.util.List;
@ -11,22 +12,6 @@ public class SearchAddressCoordinateRS {
private String admCd; // 법정동 코드 private String admCd; // 법정동 코드
private List<LocationCoordinateModel> locationCoordList; // 좌표 리스트 private List<CoordUtils.LocationCoordinateModel> locationCoordList; // 좌표 리스트
@Data
public static class LocationCoordinateModel {
private Integer index;
private List<CoordimateModel> coordinateList;
}
@Data
public static class CoordimateModel{
private Double lat; // 위도
private Double lon; // 경도
}
} }

233
pav-server/src/main/java/com/palnet/biz/api/comn/coordinate/service/ComnCoordinateService.java

@ -1,23 +1,19 @@
package com.palnet.biz.api.comn.coordinate.service; package com.palnet.biz.api.comn.coordinate.service;
import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.util.ArrayList;
import java.io.InputStreamReader; import java.util.HashSet;
import java.util.*; import java.util.List;
import java.util.regex.Pattern; import java.util.Set;
import com.palnet.biz.api.comn.coordinate.model.*; import com.palnet.biz.api.comn.coordinate.model.*;
import com.palnet.biz.jpa.repository.flt.FltCptAuthAdmDistrictRelRepository; import com.palnet.biz.jpa.repository.flt.FltCptAuthAdmDistrictRelRepository;
import com.palnet.comn.code.ErrorCode; import com.palnet.comn.code.ErrorCode;
import com.palnet.comn.exception.CustomException; import com.palnet.comn.exception.CustomException;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject; import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException; import org.json.simple.parser.ParseException;
import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Coordinate;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import com.palnet.biz.jpa.entity.FltCptAuthBas; import com.palnet.biz.jpa.entity.FltCptAuthBas;
@ -57,16 +53,16 @@ public class ComnCoordinateService {
Set<FltCptAuthBas> fltCptAuthBas = new HashSet<>(); Set<FltCptAuthBas> fltCptAuthBas = new HashSet<>();
for(int i = 0; i < scope.length; i++){ for (String s : scope) {
String cdParam = this.codeParsing(cd, scope[i]); String cdParam = CoordUtils.getInstance().addAdmCd(cd, s);
List<FltCptAuthBas> authList = fltCptAuthAdminDistrictBasQueryRepository.getFltCptAuthBas(cdParam); List<FltCptAuthBas> authList = fltCptAuthAdminDistrictBasQueryRepository.getFltCptAuthBas(cdParam);
fltCptAuthBas.addAll(new HashSet<FltCptAuthBas>(authList)); fltCptAuthBas.addAll(new HashSet<>(authList));
} }
CompotentAuthorityRS result = new CompotentAuthorityRS(); CompotentAuthorityRS result = new CompotentAuthorityRS();
result.setFltCptpAuthBasList(new ArrayList<>(fltCptAuthBas)); result.setFltCptpAuthBasList(new ArrayList<>(fltCptAuthBas));
result.setDMS(dms); result.setDMS(dms);
result.setAddress(code.get("address").toString()); result.setAddress(code.get("address").toString());
@ -78,72 +74,19 @@ public class ComnCoordinateService {
String address = rq.getAddress(); String address = rq.getAddress();
String searchAdmCd = fltCptAuthAdminDistrictBasQueryRepository.findAdmCdByAddress(address); String searchAdmCd = fltCptAuthAdminDistrictBasQueryRepository.findAdmCdByAddress(address);
String scope = this.getScope(address); String scope = CoordUtils.getInstance().getScope(address);
if(searchAdmCd == null) { if(searchAdmCd == null) {
throw new CustomException(ErrorCode.DATA_NOTFIND); throw new CustomException(ErrorCode.DATA_NOTFIND);
} }
String coordinateForderPath = this.getCodeForderPath(searchAdmCd, scope); List<CoordUtils.LocationCoordinateModel> locationCoordinateModelList = CoordUtils.getInstance().getCoordinateByAddress(searchAdmCd, scope);
String admCd = this.removeAdmCd(searchAdmCd, scope);
List<SearchAddressCoordinateRS.LocationCoordinateModel> locationCoordinateModelList = new ArrayList<>();
InputStream inputStream = null;
try {
inputStream = new ClassPathResource(coordinateForderPath).getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"), 8192);
JSONParser jsonParser = new JSONParser();
JSONObject jsonObject = (JSONObject) jsonParser.parse(reader);
List<JSONObject> features = (List<JSONObject>) jsonObject.get("features");
Optional<JSONObject> resultJsonObject = features.stream()
.filter(feature -> {
JSONObject properties = (JSONObject) feature.get("properties");
String cd = (String) properties.get("CD");
return cd.equals(admCd);
})
.map(JSONObject::new)
.findFirst();
JSONObject result = resultJsonObject.get();
JSONObject geometry = (JSONObject)result.get("geometry");
JSONArray coordinates = (JSONArray) geometry.get("coordinates");
for(int i = 0; i < coordinates.size(); i ++){
JSONArray arrayNode = (JSONArray)((JSONArray)coordinates.get(i)).get(0) ;
List<SearchAddressCoordinateRS.CoordimateModel> coordinateList = new ArrayList<>();
for (Object o : arrayNode) {
JSONArray node = (JSONArray) o;
SearchAddressCoordinateRS.CoordimateModel coordimateModel = new SearchAddressCoordinateRS.CoordimateModel();
coordimateModel.setLon((Double) node.get(0));
coordimateModel.setLat((Double) node.get(1));
coordinateList.add(coordimateModel);
}
SearchAddressCoordinateRS.LocationCoordinateModel locationCoordinateModel = new SearchAddressCoordinateRS.LocationCoordinateModel();
locationCoordinateModel.setIndex(i);
locationCoordinateModel.setCoordinateList(coordinateList);
locationCoordinateModelList.add(locationCoordinateModel);
}
} catch (IOException | ParseException e) {
log.error("", e);
}
String admCd = CoordUtils.getInstance().removeAdmCd(searchAdmCd, scope);
SearchAddressCoordinateRS result = new SearchAddressCoordinateRS(); SearchAddressCoordinateRS result = new SearchAddressCoordinateRS();
result.setAdmCd(admCd); result.setAdmCd(admCd);
result.setAddress(address.toString()); result.setAddress(address);
result.setLocationCoordList(locationCoordinateModelList); result.setLocationCoordList(locationCoordinateModelList);
return result; return result;
@ -163,16 +106,14 @@ public class ComnCoordinateService {
private String getLowScopeRegexp(String address){ private String getLowScopeRegexp(String address){
String regexp = ""; String regexp;
if(address == null || address.isEmpty()){ if(address == null || address.isEmpty()){
regexp = "^[^ ]+(시|도)$"; regexp = "^[^ ]+(시|도)$";
return regexp; return regexp;
} }
String scope = this.getScope(address); String scope = CoordUtils.getInstance().getScope(address);
String[] addressNode = address.split(" ");
if(address.equals("세종특별자치시")){ if(address.equals("세종특별자치시")){
scope = "sig"; scope = "sig";
@ -198,150 +139,4 @@ public class ComnCoordinateService {
} }
private String codeParsing(String cd, String scope){
switch (scope) {
case "ctprvn":
if(cd.length() < 2) break;
cd = cd.substring(0, 2);
break;
case "sig":
if(cd.length() < 5) break;
cd = cd.substring(0, 5);
break;
case "emd":
if(cd.length() < 8) break;
cd = cd.substring(0, 8);
break;
case "li":
if(cd.length() < 10) break;
cd = cd.substring(0, 10);
break;
}
int length = cd.length();
int maxLength = 10;
int difference = maxLength - length;
StringBuilder sb = new StringBuilder();
sb.append(cd);
for(int i = 0; i < difference; i++){
sb.append("0");
}
return sb.toString();
}
private String getScope(String address) {
Map<String, String[]> patternMap = new HashMap<>();
patternMap.put("ctprvn", new String[]{
"^[^ ]+시$",
"^[^ ]+도$"
});
patternMap.put("sig", new String[]{
"^[^ ]+시 [^ ]+구$",
"^[^ ]+시 [^ ]+군$",
"^[^ ]+도 [^ ]+시$",
"^[^ ]+도 [^ ]+시 [^ ]+구$"
});
patternMap.put("emd", new String[]{
"^[^ ]+시 [^ ]+구 [^ ]+동$",
"^[^ ]+시 [^ ]+동$",
"^[^ ]+시 [^ ]+읍$",
"^[^ ]+시 [^ ]+면$",
"^[^ ]+도 [^ ]+시 [^ ]+동$",
"^[^ ]+도 [^ ]+시 [^ ]+읍$",
"^[^ ]+도 [^ ]+시 [^ ]+면$",
"^[^ ]+도 [^ ]+시 [^ ]+구 [^ ]+동$",
"^[^ ]+도 [^ ]+시 [^ ]+구 [^ ]+읍$",
"^[^ ]+도 [^ ]+시 [^ ]+구 [^ ]+면$",
"^[^ ]+시 [^ ]+군 [^ ]+읍$",
"^[^ ]+시 [^ ]+군 [^ ]+면$"
});
patternMap.put("li", new String[]{
"^[^ ]+시 [^ ]+구 [^ ]+동 [^ ]+리$",
"^[^ ]+시 [^ ]+동 [^ ]+리$",
"^[^ ]+시 [^ ]+읍 [^ ]+리$",
"^[^ ]+시 [^ ]+면 [^ ]+리$",
"^[^ ]+도 [^ ]+시 [^ ]+동 [^ ]+리$",
"^[^ ]+도 [^ ]+시 [^ ]+읍 [^ ]+리$",
"^[^ ]+도 [^ ]+시 [^ ]+면 [^ ]+리$",
"^[^ ]+도 [^ ]+시 [^ ]+구 [^ ]+동 [^ ]+리$",
"^[^ ]+도 [^ ]+시 [^ ]+구 [^ ]+읍 [^ ]+리$",
"^[^ ]+도 [^ ]+시 [^ ]+구 [^ ]+면 [^ ]+리$",
"^[^ ]+시 [^ ]+군 [^ ]+읍 [^ ]+리$",
"^[^ ]+시 [^ ]+군 [^ ]+면 [^ ]+리$"
});
for (Map.Entry<String, String[]> entry : patternMap.entrySet()) {
for (String pattern : entry.getValue()) {
if (Pattern.matches(pattern, address)) {
return entry.getKey();
}
}
}
throw new CustomException(ErrorCode.NON_VALID_PARAMETER);
}
private String getCodeForderPath(String cd, String scope){
if(cd.length() != 10) return null;
Stack<String> forderPathStack = new Stack<>();
switch (scope) {
case "li":
forderPathStack.push(cd.substring(0, 8));
case "emd":
forderPathStack.push(cd.substring(0, 5));
case "sig":
forderPathStack.push(cd.substring(0, 2));
case "ctprvn":
}
StringBuilder forderPath = new StringBuilder();
forderPath.append("air/coordinateFolder");
while(!forderPathStack.isEmpty()){
forderPath.append("/");
forderPath.append(forderPathStack.pop());
}
forderPath.append("/all_location.geojson");
return forderPath.toString();
}
private String removeAdmCd(String cd, String scope){
if(cd.length() != 10) return null;
String result = "";
switch (scope) {
case "li":
result = cd.substring(0, 10);
break;
case "emd":
result = cd.substring(0, 8);
break;
case "sig":
result = cd.substring(0, 5);
break;
case "ctprvn":
result = cd.substring(0, 2);
break;
}
return result;
}
} }

269
pav-server/src/main/java/com/palnet/comn/utils/CoordUtils.java

@ -1,19 +1,21 @@
package com.palnet.comn.utils; package com.palnet.comn.utils;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.util.ArrayList; import java.nio.charset.StandardCharsets;
import java.util.List; import java.util.*;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.regex.Pattern;
import com.palnet.comn.code.ErrorCode;
import com.palnet.comn.exception.CustomException;
import lombok.Data;
import org.json.simple.JSONArray; import org.json.simple.JSONArray;
import org.json.simple.JSONObject; import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser; import org.json.simple.parser.JSONParser;
@ -23,20 +25,17 @@ import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.Point; import org.locationtech.jts.geom.Point;
import org.locationtech.jts.geom.Polygon; import org.locationtech.jts.geom.Polygon;
import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.util.FileCopyUtils;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@Slf4j @Slf4j
public class CoordUtils { public class CoordUtils {
private String basePath = "air/coordinateFolder/"; private final String basePath = "air/coordinateFolder/";
private static final CoordUtils INSTANCE = new CoordUtils(); private static final CoordUtils INSTANCE = new CoordUtils();
private String baseFileName = "all_location.geojson"; private final String baseFileName = "all_location.geojson";
private List<JSONObject> allLocation; private List<JSONObject> allLocation;
@ -103,7 +102,7 @@ public class CoordUtils {
String path = basePath + coords + "/" + baseFileName; String path = basePath + coords + "/" + baseFileName;
try(InputStream inputStream = new ClassPathResource(path).getInputStream(); try(InputStream inputStream = new ClassPathResource(path).getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"), 8192)) { BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8), 8192)) {
JSONParser jsonParser = new JSONParser(); JSONParser jsonParser = new JSONParser();
@ -278,9 +277,6 @@ public class CoordUtils {
return this.contains(features, point); return this.contains(features, point);
} }
public JSONObject contains(List<JSONObject> features, Point point) { public JSONObject contains(List<JSONObject> features, Point point) {
JSONObject result = null; JSONObject result = null;
@ -312,7 +308,7 @@ public class CoordUtils {
Double lat = x instanceof Double ? (Double) x : Double.valueOf((Long) x); Double lat = x instanceof Double ? (Double) x : Double.valueOf((Long) x);
Coordinate areaCoord = new Coordinate(lon, lat); Coordinate areaCoord = new Coordinate(lon, lat);
polygonPaths.add(areaCoord); polygonPaths.add(areaCoord);
} }
@ -344,8 +340,251 @@ public class CoordUtils {
result.put("distance", standard); result.put("distance", standard);
return result; return result;
}
/**
* 주소로 지역 바운더리 좌표 가져오기
* @param searchAdmCd DB에서 조회한 법정동 코드[10자리]
* @param scope 지역 스코프,
* @return 지역좌표 반환
*/
public List<LocationCoordinateModel> getCoordinateByAddress (String searchAdmCd, String scope){
String coordinateForderPath = this.getCodeForderPath(searchAdmCd, scope);
String admCd = this.removeAdmCd(searchAdmCd, scope);
List<LocationCoordinateModel> locationCoordinateModelList = new ArrayList<>();
InputStream inputStream = null;
try {
inputStream = new ClassPathResource(coordinateForderPath).getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8), 8192);
JSONParser jsonParser = new JSONParser();
JSONObject jsonObject = (JSONObject) jsonParser.parse(reader);
List<JSONObject> features = (List<JSONObject>) jsonObject.get("features");
Optional<JSONObject> resultJsonObject = features.stream()
.filter(feature -> {
JSONObject properties = (JSONObject) feature.get("properties");
String cd = (String) properties.get("CD");
return cd.equals(admCd);
})
.map(JSONObject::new)
.findFirst();
JSONObject result = resultJsonObject.orElseThrow(() -> new CustomException(ErrorCode.DATA_NO));
JSONObject geometry = (JSONObject)result.get("geometry");
JSONArray coordinates = (JSONArray) geometry.get("coordinates");
for(int i = 0; i < coordinates.size(); i ++){
JSONArray arrayNode = (JSONArray)((JSONArray)coordinates.get(i)).get(0) ;
List<Coordinate> coordinateList = new ArrayList<>();
for (Object o : arrayNode) {
JSONArray node = (JSONArray) o;
Coordinate coordimateModel = new Coordinate((Double) node.get(0), (Double) node.get(1));
coordinateList.add(coordimateModel);
}
LocationCoordinateModel locationCoordinateModel = new LocationCoordinateModel();
locationCoordinateModel.setIndex(i);
locationCoordinateModel.setCoordinateList(coordinateList);
locationCoordinateModelList.add(locationCoordinateModel);
}
} catch (IOException | ParseException e) {
log.error("", e);
}
return locationCoordinateModelList;
}
/**
* 주소의 스코프 체크 기능.
* @param address 스코프 체크할 주소
* @return /: ctprvn, //: sig, //: emd, : li
*/
public String getScope(String address) {
Map<String, String[]> patternMap = new HashMap<>();
patternMap.put("ctprvn", new String[]{
"^[^ ]+시$",
"^[^ ]+도$"
});
patternMap.put("sig", new String[]{
"^[^ ]+시 [^ ]+구$",
"^[^ ]+시 [^ ]+군$",
"^[^ ]+도 [^ ]+시$",
"^[^ ]+도 [^ ]+시 [^ ]+구$"
});
patternMap.put("emd", new String[]{
"^[^ ]+시 [^ ]+구 [^ ]+동$",
"^[^ ]+시 [^ ]+동$",
"^[^ ]+시 [^ ]+읍$",
"^[^ ]+시 [^ ]+면$",
"^[^ ]+도 [^ ]+시 [^ ]+동$",
"^[^ ]+도 [^ ]+시 [^ ]+읍$",
"^[^ ]+도 [^ ]+시 [^ ]+면$",
"^[^ ]+도 [^ ]+시 [^ ]+구 [^ ]+동$",
"^[^ ]+도 [^ ]+시 [^ ]+구 [^ ]+읍$",
"^[^ ]+도 [^ ]+시 [^ ]+구 [^ ]+면$",
"^[^ ]+시 [^ ]+군 [^ ]+읍$",
"^[^ ]+시 [^ ]+군 [^ ]+면$"
});
patternMap.put("li", new String[]{
"^[^ ]+시 [^ ]+구 [^ ]+동 [^ ]+리$",
"^[^ ]+시 [^ ]+동 [^ ]+리$",
"^[^ ]+시 [^ ]+읍 [^ ]+리$",
"^[^ ]+시 [^ ]+면 [^ ]+리$",
"^[^ ]+도 [^ ]+시 [^ ]+동 [^ ]+리$",
"^[^ ]+도 [^ ]+시 [^ ]+읍 [^ ]+리$",
"^[^ ]+도 [^ ]+시 [^ ]+면 [^ ]+리$",
"^[^ ]+도 [^ ]+시 [^ ]+구 [^ ]+동 [^ ]+리$",
"^[^ ]+도 [^ ]+시 [^ ]+구 [^ ]+읍 [^ ]+리$",
"^[^ ]+도 [^ ]+시 [^ ]+구 [^ ]+면 [^ ]+리$",
"^[^ ]+시 [^ ]+군 [^ ]+읍 [^ ]+리$",
"^[^ ]+시 [^ ]+군 [^ ]+면 [^ ]+리$"
});
for (Map.Entry<String, String[]> entry : patternMap.entrySet()) {
for (String pattern : entry.getValue()) {
if (Pattern.matches(pattern, address)) {
return entry.getKey();
}
}
}
throw new CustomException(ErrorCode.NON_VALID_PARAMETER);
}
/**
* 법정동코드와 스코프에 맞춰 조회할 좌표파일 경로 가져오는 기능
* @param cd 조회할 법정동코드
* @param scope 스코프
* @return 폴더경로
*/
private String getCodeForderPath(String cd, String scope){
if(cd.length() != 10) return null;
Stack<String> forderPathStack = new Stack<>();
switch (scope) {
case "li":
forderPathStack.push(cd.substring(0, 8));
case "emd":
forderPathStack.push(cd.substring(0, 5));
case "sig":
forderPathStack.push(cd.substring(0, 2));
case "ctprvn":
}
StringBuilder forderPath = new StringBuilder();
forderPath.append(this.basePath);
while(!forderPathStack.isEmpty()){
forderPath.append("/");
forderPath.append(forderPathStack.pop());
}
forderPath.append(this.baseFileName);
return forderPath.toString();
}
/**
* 법정동코드를 10자리에 맞춰 0을 추가[DB조회를 위함]
* @param cd 10자리가 안되는 법정동코드
* @param scope 현재 법정동 코드의 스코프
* @return 10자리에 맞게 0이 추가된 법정동코드
*/
public String addAdmCd(String cd, String scope){
switch (scope) {
case "ctprvn":
if(cd.length() < 2) break;
cd = cd.substring(0, 2);
break;
case "sig":
if(cd.length() < 5) break;
cd = cd.substring(0, 5);
break;
case "emd":
if(cd.length() < 8) break;
cd = cd.substring(0, 8);
break;
case "li":
if(cd.length() < 10) break;
cd = cd.substring(0, 10);
break;
}
int length = cd.length();
int maxLength = 10;
int difference = maxLength - length;
StringBuilder sb = new StringBuilder();
sb.append(cd);
for(int i = 0; i < difference; i++){
sb.append("0");
} }
return sb.toString();
}
/**
* 스코프에 맞게 법정동코드 길이를 자르는 기능.
* @param cd 잘라낼 법정동 코드
* @param scope 스코프
* @return 잘라진 법정동코드
*/
public String removeAdmCd(String cd, String scope){
if(cd.length() != 10) throw new CustomException(ErrorCode.NON_VALID_PARAMETER);
String result = "";
switch (scope) {
case "li":
result = cd.substring(0, 10);
break;
case "emd":
result = cd.substring(0, 8);
break;
case "sig":
result = cd.substring(0, 5);
break;
case "ctprvn":
result = cd.substring(0, 2);
break;
default:
throw new CustomException(ErrorCode.NON_VALID_PARAMETER);
}
return result;
} }
@Data
public static class LocationCoordinateModel {
private Integer index;
private List<Coordinate> coordinateList;
}
}

Loading…
Cancel
Save