#include "fm_address.h" #include #include #include #if (USE_JP_ADDRESS_TOOL) #include #include #include #include #include #include #include #endif // TYPE 0: 국토지리원 1. 전자 국토 기본도 (지명 정보) "주거 표시 주소" (2200만건) // TYPE 1: 기존DB 1100만건 // TYPE 2: 국토교통성 지명DB https://nlftp.mlit.go.jp/cgi-bin/isj/dls/_choose_method.cgi #if (USE_JP_ADDRESS) // 비트팩->바이트 계산 inline uint32_t _ABitsPackToByte(uint8_t* bits) { return ceil((double)(bits[0] + bits[1] + bits[2] + bits[3])/8.0); } // 영역 포함 확인 inline bool _AIsInArea(JA_AREA_PACKET* area,int x, int y, int tolerance) { return (x >= (area->xmin - tolerance) && x <= (area->xmax + tolerance) && y >= (area->ymin - tolerance) && y <= (area->ymax + tolerance)); } #if (USE_JP_ADDRESS_TOOL) #define PROCESS_TEST_CITY 0 QMap FMAddress::_stringTable; #endif // USE_JP_ADDRESS_TOOL FMAddress::FMAddress() { memset(&_header,0,sizeof(_JA_HEADER)); _areas = NULL; _strings = NULL; _file = NULL; #if (USE_JP_ADDRESS_TOOL) _maxLenPref = 0; _maxLenCity = 0; _maxLenChome = 0; _maxA0 = 0; _maxA1 = 0; QString dateString = QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss"); strcpy(_header.header,"F-REVERSE ADDRESS GEOCODING TABLE API"); strcpy(_header.date,dateString.toLocal8Bit().constData()); strcpy(_header.version,"0.1.0"); qInfo() << _header.header << _header.date << _header.version; _prefs = NULL; #endif } FMAddress::~FMAddress() { if(_areas != NULL) { free(_areas); } if(_strings != NULL) { free(_strings); } if(_file != NULL) { fclose(_file); } } bool FMAddress::open(QString path) { _file = fopen(path.toLocal8Bit(),"rb"); if(_file == NULL) { return false; } fread(&_header,sizeof(_JA_HEADER),1,_file); _strings = (uint8_t*)malloc(_header.stringSize); fread(_strings,_header.stringSize,1,_file); uint32_t areaSize = sizeof(_JA_AREA_PACKET) * (_header.pref_count + _header.city_count + _header.town_count); _areas = (JA_AREA_PACKET*)malloc(areaSize); fread(_areas,areaSize,1,_file); #if (USE_JP_ADDRESS_TOOL) qInfo() << "-----------------------------------------------------"; qInfo() << "HEADER : Bound:" << _header.xmin << _header.ymin << _header.xmax << _header.ymax; qInfo() << "HEADER : Bound(Lon/Lat):" << QString().sprintf("%.6f,%.6f,%.6f,%.6f",(double)_header.xmin / 80000.0,(double)_header.ymin / 120000.0,(double)_header.xmax / 80000.0,(double)_header.ymax / 120000.0); qInfo() << "HEADER : ID/DATE/VERSION:" << _header.header << _header.date << _header.version; qInfo() << "HEADER : SIZE:" << QString().sprintf("0x%X",sizeof(_JA_HEADER)); qInfo() << "HEADER : STRING SIZE:" << _header.stringSize; qInfo() << "HEADER : PREF,COUNT:" << _header.pref_count; qInfo() << "HEADER : CITY,COUNT:" << _header.city_count; qInfo() << "HEADER : TOWN,COUNT:" << _header.town_count; qInfo() << "HEADER : JIBUN,COUNT:" << _header.jibun_count; //debugList(); #endif return true; } bool FMAddress::open() { if(!isOpened()) { QString path = QDir::cleanPath(QCoreApplication::applicationDirPath() + QDir::separator() + "jpaddr.bin"); if(!open(path)) { open("C://home//roadmovie//script//jpaddr//20210120_jp_addr.bin"); } } return isOpened(); } bool FMAddress::search(double lon, double lat,QStringList& result,int tolerance) { wchar_t* p = NULL; wchar_t* c = NULL; wchar_t* t = NULL; int a0 = 0; int a1 = 0; int dist = 0; if(search(lon,lat,&p,&c,&t,&a0,&a1,&dist,tolerance) == false) { return false; } result.append(QString::fromUtf16((ushort*)p)); result.append(QString::fromUtf16((ushort*)c)); result.append(QString::fromUtf16((ushort*)t)); result.append(QString::number(a0)); result.append(QString::number(a1)); result.append(QString::number(dist)); return true; } bool FMAddress::search(double lon,double lat,wchar_t** pref,wchar_t** city,wchar_t** town,int* a0, int*a1, int* pdist, int tolerance) { qInfo() << QString().sprintf("SEARCH START :%.6f,%.6f [%d]",lat,lon,tolerance); _found_a0 = -1; _found_a1 = -1; _found_dist_sq = 1000000; // 거리^2 int x = (int)(lon * 80000.0); int y = (int)(lat * 120000.0); bool bFound = false; for(uint32_t p=0;p<_header.pref_count;p++) { JA_AREA_PACKET* pp = &_areas[p]; //qInfo() << "PREF" << QString::fromUtf16((ushort*)&_strings[pp->nameOffset]) << pp->xmin << pp->ymin << pp->xmax << pp->ymax; // PREF 포함 if(_AIsInArea(&_areas[p],x,y,tolerance)) { //qInfo() << QString::fromUtf16((ushort*)&_strings[pp->nameOffset]); uint32_t cidx = pp->sub_offset / sizeof(_JA_AREA_PACKET); for(uint32_t c=0;csub_count;c++) { JA_AREA_PACKET* cc = &_areas[cidx + c]; // CITY 포함 //qInfo() << "\t" << QString::fromUtf16((ushort*)&_strings[cc->nameOffset]); if(_AIsInArea(cc,x,y,tolerance)) { uint32_t tidx = cc->sub_offset / sizeof(_JA_AREA_PACKET); for(uint32_t t=0;tsub_count;t++) { JA_AREA_PACKET* tt = &_areas[tidx + t]; if(_AIsInArea(tt,x,y,tolerance)) { //qInfo() << "\t\t" << QString::fromUtf16((ushort*)&_strings[tt->nameOffset]); if(_searchTown(tt,x,y,tolerance)) // 가장 가까운 지점이 발견된 경우 { bFound = true; //qInfo() << "\t\t\t" << "FOUND!!!!" << QString::fromUtf16((ushort*)&_strings[tt->nameOffset]) << (int)sqrt((double)_found_dist_sq) ; // 리턴 *pref = (wchar_t*)&_strings[pp->nameOffset]; *city = (wchar_t*)&_strings[cc->nameOffset]; *town = (wchar_t*)&_strings[tt->nameOffset]; *pdist = (int)sqrt((double)_found_dist_sq); *a0 = _found_a0; *a1 = _found_a1; } } // IN TOWN } // FOR TOWN } // IN CITY } // FOR CITY } // IN PREF } // FOR PREF return bFound;//(_found_dist_sq < tolerance); } bool FMAddress::_searchTown(JA_AREA_PACKET* town,int x, int y, int tolerance) { const int lx = x - town->xmin; const int ly = y - town->ymin; JA_AREA_PACKET box = {0,}; box.xmin = (lx - tolerance); box.ymin = (ly - tolerance); box.xmax = (lx + tolerance); box.ymax = (ly + tolerance); uint32_t bytes = _ABitsPackToByte(town->bits); // 1 지번당 바이트 const uint32_t xm = ((1 << town->bits[0])-1); // xmask const uint32_t ym = ((1 << town->bits[1])-1); // ymask const uint32_t a0m = ((1 << town->bits[2])-1); // a0mask const uint32_t a1m = ((1 << town->bits[3])-1); // a1mask // A0-A1-x-y shift const uint32_t xs = town->bits[1] + town->bits[2] + town->bits[3]; const uint32_t ys = town->bits[2] + town->bits[3]; // a0 const uint32_t a0s = town->bits[3]; // a0 const uint32_t a1s = 0; // a1 // 할당 @TODO -> MAX_TOWN_BUFFER 를 생성해서 처리 가능.. uint8_t* buffer = (uint8_t*)malloc(bytes * town->sub_count); uint64_t v; // 데이터 처리용 fseek(_file,town->sub_offset,SEEK_SET); fread(buffer,bytes * town->sub_count,1,_file); bool bFound = false; for(uint32_t j=0;jsub_count;j++) { v = 0; memcpy(&v,&buffer[bytes*j],bytes); int vx = ((v >> xs) & xm); int vy = ((v >> ys) & ym); // 영역 포함 if(_AIsInArea(&box,vx,vy,0)) { int ds2 = ((vx - lx) * (vx - lx)) + ((vy - ly) * (vy - ly)); if(ds2 < _found_dist_sq) { _found_dist_sq = ds2; _found_a0 = (v >> a0s) & a0m; _found_a1 = (v >> a1s) & a1m; _found_x = town->xmin + vx; _found_y = town->ymin + vy; //qInfo() << "FOUND:" << _found_dist_sq << "JIBUN:" << _found_a0 << _found_a1; bFound = true; } } // half binary search if(vx > box.xmax) { break; } //qInfo() << "\t\t\t" << QString().sprintf("#%d %d-%d %d,%d (%.6f,%.6f)",j+1,va0,va1,vx,vy,((double)(town->xmin + vx))/80000.0,((double)(town->ymin + vy))/120000.0); } free(buffer); return bFound; } #if (USE_JP_ADDRESS_TOOL) void FMAddress::debugList() { for(uint32_t p=0;p<_header.pref_count;p++) { JA_AREA_PACKET* pp = &_areas[p]; qInfo() << QString::fromUtf16((ushort*)&_strings[pp->nameOffset]) << QString().sprintf("cities:%d bound: %.6f,%.6f,%.6f,%.6f",pp->sub_count,((double)pp->xmin)/80000.0,((double)pp->xmax)/80000.0,((double)pp->ymin)/120000.0,((double)pp->ymax)/120000.0); for(uint32_t c=0;csub_count;c++) { // AREA 는 TOWN 을 제외하고는 offset 보다는 index 가 좋겠음.. uint32_t cidx = pp->sub_offset / sizeof(_JA_AREA_PACKET); JA_AREA_PACKET* cc = &_areas[cidx + c]; qInfo() << "\t" << QString::fromUtf16((ushort*)&_strings[cc->nameOffset]) << QString().sprintf("towns:%d bound: %.6f,%.6f,%.6f,%.6f",cc->sub_count,((double)cc->xmin)/80000.0,((double)cc->xmax)/80000.0,((double)cc->ymin)/120000.0,((double)cc->ymax)/120000.0); for(uint32_t t=0;tsub_count;t++) { uint32_t tidx = cc->sub_offset / sizeof(_JA_AREA_PACKET); JA_AREA_PACKET* tt = &_areas[tidx + t]; qInfo() << "\t\t" << QString::fromUtf16((ushort*)&_strings[tt->nameOffset]) << QString().sprintf("offset:0x%X jibuns:%d bound: %.6f,%.6f,%.6f,%.6f",tt->sub_offset,tt->sub_count,((double)tt->xmin)/80000.0,((double)tt->xmax)/80000.0,((double)tt->ymin)/120000.0,((double)tt->ymax)/120000.0); _debugTown(tt); //break; } //break; } //break; } } void FMAddress::_debugTown(JA_AREA_PACKET* town) { uint32_t bytes = _ABitsPackToByte(town->bits); // 1 지번당 바이트 const uint32_t xm = ((1 << town->bits[0])-1); // xmask const uint32_t ym = ((1 << town->bits[1])-1); // ymask const uint32_t a0m = ((1 << town->bits[2])-1); // a0mask const uint32_t a1m = ((1 << town->bits[3])-1); // a1mask // A0-A1-x-y shift const uint32_t xs = town->bits[1] + town->bits[2] + town->bits[3]; const uint32_t ys = town->bits[2] + town->bits[3]; // a0 const uint32_t a0s = town->bits[3]; // a0 const uint32_t a1s = 0; // a1 uint8_t* buffer = (uint8_t*)malloc(bytes * town->sub_count); // 할당 uint64_t v; // 데이터 처리용 fseek(_file,town->sub_offset,SEEK_SET); fread(buffer,bytes * town->sub_count,1,_file); for(uint32_t j=0;jsub_count;j++) { v = 0; memcpy(&v,&buffer[bytes*j],bytes); uint32_t vx = (v >> xs) & xm; uint32_t vy = (v >> ys) & ym; uint32_t va0 = (v >> a0s) & a0m; uint32_t va1 = (v >> a1s) & a1m; qInfo() << "\t\t\t" << QString().sprintf("#%d %d-%d %d,%d (%.6f,%.6f)",j+1,va0,va1,vx,vy,((double)(town->xmin + vx))/80000.0,((double)(town->ymin + vy))/120000.0); } free(buffer); } bool FMAddress::verify(QString src) { qInfo() << "START VERIFY:" << src << __FUNCTION__ << __LINE__; QElapsedTimer et; et.start(); QFile file(src); if (!file.open(QIODevice::ReadOnly)) { qInfo() << file.errorString(); return false; } #if (USE_ADDR_DB_TYPE == 1) // 한줄 버리기 qInfo() << QString::fromStdString(file.readLine().toStdString()); #endif int diffCount = 0; int processCount = 0; int lines = 0; while (!file.atEnd()) { QByteArray line = file.readLine(); lines++; if(lines > 0 && lines % 100000 == 0) { //qInfo() << lines; } QString str = QString::fromStdString(line.toStdString()); QStringList lst = str.split(","); #if (USE_ADDR_DB_TYPE == 0) QString pref = lst.at(1); QString city = lst.at(2); QString town = lst.at(3); int a0 = lst.at(4).toInt(); int a1 = lst.at(5).toInt(); if (a1 > 1000) { continue; } double lon = lst.at(6).toDouble(); double lat = lst.at(7).toDouble(); #elif (USE_ADDR_DB_TYPE == 1) QString pref = lst.at(0); QString city = lst.at(1); QString town = lst.at(2); // '青森県' and ADDR_2TH.SI_NAME = '三沢市' if(town.isEmpty()) { town = " "; } bool bok0, bok2; int a0 = lst.at(3).toInt(&bok0); int a1 = lst.at(4).toInt(&bok2); if(bok0 == false || bok2 == false) { continue; } double lon = lst.at(8).toDouble(); double lat = lst.at(7).toDouble(); #endif wchar_t* pname = NULL; wchar_t* cname = NULL; wchar_t* tname = NULL; int ra0 = -1; int ra1 = -1; int dist = -1; processCount += 1; if(!search(lon,lat,&pname,&cname,&tname,&ra0,&ra1,&dist,2) || sqrt((double)dist) > 20.0 || a0 != ra0 || a1 != ra1) { QString qpname = QString::fromUtf16((ushort*)pname); QString qcname = QString::fromUtf16((ushort*)cname); QString qtname = QString::fromUtf16((ushort*)tname); if(abs((int)(lon*80000.0) - _found_x) > 10 || abs((int)(lat*120000.0) - _found_y) > 10 // pref != qpname || // city != qcname || // town != qtname ) { qInfo() << "NOT FOUND!!!" << str << qpname << qcname << qtname << "DIST:" << sqrt((double)dist) << "RA0-A1" << ra0 << ra1 << "LON/LAT" << QString().sprintf("%.6f,%.6f",((double)_found_x)/80000.0,((double)_found_y)/120000.0); file.close(); return false; } double min = ((double)et.elapsed()) / 60000.0; qInfo() << "DIFF COUNT:" << a0 << ra0 << a1 << ra1 << ++diffCount << "/" << processCount << QString().sprintf("%.1f min",min); } //qInfo() << "FOUND:" << str << QString::fromUtf16((ushort*)pname) << QString::fromUtf16((ushort*)cname) << QString::fromUtf16((ushort*)tname) << "DIST:" << sqrt((double)dist) << "RA0-A1" << ra0 << ra1; } file.close(); double min = ((double)et.elapsed()) / 60000.0; qInfo() << "DONE:" << QString().sprintf("%.1f min",min) << __FUNCTION__ << __LINE__; return true; } bool FMAddress::convert(QString src, QString target) { qInfo() << "START:" << src << __FUNCTION__ << __LINE__; QElapsedTimer et; et.start(); _currentPref = NULL; _freeConvertData(); _prefs = (JA_AREA*)malloc(sizeof(_JA_AREA) * MAX_PREF_COUNT); memset(_prefs,0,sizeof(_JA_AREA) * MAX_PREF_COUNT); // CODE,PREF,CITY,CHOME,A0,A1,LON,LAT,CDATE // 253851,11469174 -> 기존 DB ADDR_1TH,ADDR_2TH // ADDR_1TH 의 경우 旭ヶ丘一丁目, 旭ヶ丘二丁目 등으로 각 丁目 별로 BUNJI 1개씩만 저장 // ADDR_2TH 의 경우 모든 BUNJI 포함하고 HO는 1개씩 저장 // 22467875 -> 신규 DB // X=LON*80000, Y:LAT*120000 QFile file(src); if (!file.open(QIODevice::ReadOnly)) { qInfo() << file.errorString(); return false; } QString maxA0 = ""; QString maxA1 = ""; // 초기화 _currentPrefName = ""; _currentCityName = ""; _currentChomeName = ""; _header.pref_count = 0; #if (PROCESS_TEST_CITY) int townCount = 0; // 테스트용 #endif #if (USE_ADDR_DB_TYPE == 1) // 한줄 버리기 qInfo() << QString::fromStdString(file.readLine().toStdString()); #endif int lines = 0; while (!file.atEnd()) { QByteArray line = file.readLine(); lines++; if(lines > 0 && lines % 100000 == 0) { //qInfo() << lines; } QString str = QString::fromStdString(line.toStdString()); QStringList lst = str.split(","); #if (USE_ADDR_DB_TYPE == 0) //QString code = lst.at(0); QString pref = lst.at(1); QString city = lst.at(2); QString chome = lst.at(3); int a0 = lst.at(4).toInt(); int a1 = lst.at(5).toInt(); if (a1 > 1000) { //qInfo() << str; continue; } #else QString pref = lst.at(0); QString city = lst.at(1); QString chome = lst.at(2); chome = chome.replace("\"",""); // '青森県' and ADDR_2TH.SI_NAME = '三沢市' if(chome.isEmpty()) { chome = " "; } bool bok0, bok2; int a0 = lst.at(3).toInt(&bok0); int a1 = lst.at(4).toInt(&bok2); if(bok0 == false || bok2 == false) { //qInfo() << "ERROR:" << str; continue; } #endif // if(lines >= 965764) { // // 宮城県 // qInfo() << str << lines; // } // 東京都 // if(pref != QString::fromUtf8("\xe6\x9d\xb1\xe4\xba\xac\xe9\x83\xbd")) // { // continue; // } // 千代田 // if(lst.at(0) != QString("13101")) // { // continue; // } // 도도부현 변경시 if(_currentPrefName != pref) { _currentPrefName = pref; qInfo() << _currentPrefName; _currentPref = &_prefs[_header.pref_count]; wcscpy((wchar_t*)&_currentPref->name[0],(wchar_t*)pref.utf16()); _currentCity = (JA_AREA*)malloc(sizeof(_JA_AREA) * MAX_CITY_COUNT); memset(_currentCity,0,sizeof(_JA_AREA) * MAX_CITY_COUNT); _currentPref->subAreas = _currentCity; _header.pref_count += 1; _currentPref->count = 0; _currentCityName = ""; //qInfo() << "pref:" << pref; } // 시구군 변경시 if(_currentCityName != city) { qInfo() << pref << city << lines; _currentCity = &_currentPref->subAreas[_currentPref->count]; _currentPref->count += 1; _currentCityName = city; wcscpy((wchar_t*)&_currentCity->name[0],(wchar_t*)city.utf16()); _currentChome = (JA_AREA*)malloc(sizeof(_JA_AREA) * MAX_CHOME_COUNT); memset(_currentChome,0,sizeof(_JA_AREA) * MAX_CHOME_COUNT); _currentCity->subAreas = _currentChome; _currentChomeName = ""; } // 동변경시 if(_currentChomeName != chome) { //qInfo() << pref << city << chome; #if (PROCESS_TEST_CITY) if(townCount >= 1) { break; } townCount +=1; #endif // 기존 CHOME 데이터 크기대로 리사이즈 if(_currentChome != NULL) { _resizeChome(_currentChome); } // current 변경 _currentChome = &_currentCity->subAreas[_currentCity->count]; _currentCity->count += 1; // 개수 추가 _currentChomeName = chome; // 명칭->저장 wcscpy((wchar_t*)&_currentChome->name[0],(wchar_t*)chome.utf16()); // 지번 생성 (개수를 모르니 그냥 처리) _currentJibun = (JA_JIBUN*)malloc(sizeof(_JA_JIBUN) * MAX_JIBUN_COUNT); memset(_currentJibun,0,sizeof(_JA_JIBUN) * MAX_JIBUN_COUNT); _currentChome->subJibuns = _currentJibun; //qInfo() << chome << _currentChome->count << __FUNCTION__ << __LINE__; } // if(_currentChome->count >= MAX_JIBUN_COUNT) { // qInfo() << str << _currentChome->count << lines; // break; // } // if(lines > 659415) { // qInfo() << lines; // } // 모든 지번 처리 // CODE,PREF,CITY,CHOME,A0,A1,LON,LAT,CDATE _currentJibun = &_currentChome->subJibuns[_currentChome->count]; _currentChome->count += 1; // 지번 _currentJibun->a0 = a0; _currentJibun->a1 = a1; // // X=LON*80000, Y:LAT*120000 #if (USE_ADDR_DB_TYPE == 0) _currentJibun->x = (int)(lst.at(6).toDouble() * 80000.0); _currentJibun->y = (int)(lst.at(7).toDouble() * 120000.0); #else _currentJibun->x = (int)(lst.at(8).toDouble() * 80000.0); _currentJibun->y = (int)(lst.at(7).toDouble() * 120000.0); #endif if(_currentJibun->x < 0 || _currentJibun->y < 0) { qInfo() << "!!!!!!" << str; } // _maxLenPref: 4 _maxLenCity: 8 _maxLenChome: 12 _maxLenPref = qMax(pref.length(),_maxLenPref); _maxLenCity = qMax(city.length(),_maxLenCity); _maxLenChome = qMax(chome.length(),_maxLenChome); bool bLog = false; if(a0 > _maxA0) { maxA0 = str; bLog = true; } if (a1 > _maxA1) { maxA1 = str; bLog = true; } _maxA0 = qMax(a0,_maxA0); _maxA1 = qMax(a1,_maxA1); //const ushort* ut = pref.utf16(); //qInfo() << pref << pref.length() << QString::fromUtf16(pref.utf16()) << wcslen((const wchar_t*)ut); if(bLog) { //qInfo() << str; } // "27220,大阪府,箕面市,箕面四丁目,16,57555,135.474490149,34.832934236,20190327\n" // OK // if(code == "27146" && // chome == QString("\xe6\x96\xb0\xe9\x87\x91\xe5\xb2\xa1\xe7\x94\xba\xe4\xba\x94\xe4\xb8\x81") && // a0 == 7) { // qInfo() << str; // } // OK // if(code == "40231" && // chome == QString("\xe4\xbb\xb2\xe4\xb8\x80\xe4\xb8\x81\xe7\x9b\xae") && // a0 == 150) { // qInfo() << str; // } // "27220,大阪府,箕面市,小野原西三丁目,9,1314,135.508661045,34.832548066,20190327\n" -> NG // if(code == "27220" && // chome == QString("\xe5\xb0\x8f\xe9\x87\x8e\xe5\x8e\x9f\xe8\xa5\xbf\xe4\xb8\x89\xe4\xb8\x81\xe7\x9b\xae") && // a0 == 9) { // qInfo() << str; // } // "15222,新潟?,上越市,安江二丁目,5,2000,138.264244772,37.170192031,20170804\n" -> NG // if(code == "15222" && // chome == QString("\xe5\xae\x89\xe6\xb1\x9f\xe4\xba\x8c\xe4\xb8\x81\xe7\x9b\xae") && // a0 == 5) { // qInfo() << str; // } } // 마지막 TOWN 처리 if(_currentChome != NULL) { _resizeChome(_currentChome); } //qInfo() << "_maxLenPref:" << _maxLenPref << "_maxLenCity:" << _maxLenCity << "_maxLenChome:" << _maxLenChome; //qInfo() << "_maxA0:" << _maxA0 << "_maxA1:" << _maxA1; //qInfo() << "maxA0 addr:" << maxA0; //qInfo() << "maxA1 addr:" << maxA1; //qInfo() << "REC NO.:" << lines; uint32_t maxChome = 0; uint32_t maxJibun = 0; for(uint32_t p=0;p<_header.pref_count;p++) { JA_AREA* pref = &_prefs[p]; //qInfo() << QString::fromUtf16(pref->name) << pref->count; JA_AREA* citys = pref->subAreas; for(uint32_t c=0;c<(uint32_t)pref->count;c++) { JA_AREA* city = &citys[c]; //qInfo() << "\t\t" << QString::fromUtf16(city->name) << city->count; maxChome = qMax(maxChome,(uint32_t)city->count); JA_AREA* chomes = city->subAreas; for(uint32_t t=0;t<(uint32_t)city->count;t++) { JA_AREA* chome = &chomes[t]; maxJibun = qMax(maxJibun,(uint32_t)chome->count); //qInfo() << "\t\t" << QString::fromUtf16(chome->name) << chome->count; } } } //qInfo() << "MAX_CHOME" << maxChome; //qInfo() << "MAX_JIBUN" << maxJibun; //qInfo() << __FUNCTION__ << __LINE__; // 데이터 확인 + MIN MAX 처리 + 문자열 테이블 처리 _createStatCSV(); //qInfo() << __FUNCTION__ << __LINE__; // 저장 _outFile = fopen(target.toLocal8Bit(),"wb"); // 데이터 저장 _saveData(); _freeConvertData(); fclose(_outFile); double min = ((double)et.elapsed()) / 60000.0; qInfo() << "DONE:" << QString().sprintf("%.1f min",min) << __FUNCTION__ << __LINE__; return true; } void FMAddress::_updateBound(_JA_AREA* dest, _JA_AREA* src) { dest->xmin = qMin(dest->xmin,src->xmin); dest->ymin = qMin(dest->ymin,src->ymin); dest->xmax = qMax(dest->xmax,src->xmax); dest->ymax = qMax(dest->ymax,src->ymax); } void FMAddress::_updateBoundP(_JA_AREA* dest, _JA_JIBUN* src) { dest->xmin = qMin(dest->xmin,src->x); dest->ymin = qMin(dest->ymin,src->y); dest->xmax = qMax(dest->xmax,src->x); dest->ymax = qMax(dest->ymax,src->y); dest->a0min = qMin(dest->a0min,(uint16_t)src->a0); dest->a0max = qMax(dest->a0max,(uint16_t)src->a0); dest->a1min = qMin(dest->a1min,(uint16_t)src->a1); dest->a1max = qMax(dest->a1max,(uint16_t)src->a1); } void FMAddress::_initBound(_JA_AREA* dest) { dest->xmin = 160 * 80000; dest->ymin = 50 * 120000; dest->xmax = 0; dest->ymax = 0; // min,max dest->a0min = 10000; dest->a0max = 0; dest->a1min = 10000; dest->a1max = 0; } void FMAddress::_calculateBitPack(_JA_AREA * src) // , uint8_t* byte { // 32의 경우 5bit 만 사용하면 31까지만 표현가능하여 무조건 1씩 추가 int a0Bits = ceil(log((double)(src->a0max+1)) / log( 2.0 )); int a1Bits = ceil(log((double)(src->a1max+1)) / log( 2.0 )); int w = src->xmax - src->xmin; int h = src->ymax - src->ymin; int xBits = 0; if(w > 0) { xBits = ceil(log((double)(w+1)) / log( 2.0 )); } int yBits = 0; if(h > 0) { yBits = ceil(log((double)(h+1)) / log( 2.0 )); } src->bits[0] = xBits = qMax(xBits,4); src->bits[1] =yBits = qMax(yBits,4); src->bits[2] =a0Bits = qMax(a0Bits,4); src->bits[3] =a1Bits = qMax(a1Bits,4); //int bytes = ceil((double)(xBits + yBits + a0Bits + a1Bits)/8.0); //*byte = (uint8_t)bytes; //return QString().sprintf("%02d,%02d,%02d,%02d,%02d",bytes,xBits,yBits,a0Bits,a1Bits); } int _compareJIBUN(const void *j1, const void *j2) { if(((JA_JIBUN*)j1)->x == ((JA_JIBUN*)j2)->x) { return ((JA_JIBUN*)j1)->y - ((JA_JIBUN*)j2)->y; } return ((JA_JIBUN*)j1)->x - ((JA_JIBUN*)j2)->x; } // 지번 X 순으로 정렬 void FMAddress::_sortJIBUN(JA_AREA* chome) { qsort(chome->subJibuns,chome->count,sizeof(_JA_JIBUN),_compareJIBUN); } void FMAddress::_saveData() { // 헤더 저장 _writeOffset = 0; fwrite(&_header,sizeof(_JA_HEADER),1,_outFile); _writeOffset += sizeof(_JA_HEADER); // 문자열 테이블 처리+저장 _saveStringTable(); //_header.prefOffset = _writeOffset; // 도도부현 저장 _savePref(); qInfo() << "-----------------------------------------------------"; qInfo() << "HEADER : Bound:" << _header.xmin << _header.ymin << _header.xmax << _header.ymax; qInfo() << "HEADER : Bound(Lon/Lat):" << QString().sprintf("%.6f,%.6f,%.6f,%.6f",(double)_header.xmin / 80000.0,(double)_header.ymin / 120000.0,(double)_header.xmax / 80000.0,(double)_header.ymax / 120000.0); qInfo() << "HEADER : ID/DATE/VERSION:" << _header.header << _header.date << _header.version; qInfo() << "HEADER : STRING SIZE:" << _header.stringSize; qInfo() << "HEADER : PREF,COUNT:" << _header.pref_count; qInfo() << "HEADER : CITY,COUNT:" << _header.city_count; qInfo() << "HEADER : TOWN,COUNT:" << _header.town_count; qInfo() << "HEADER : JIBUN,COUNT:" << _header.jibun_count; uint32_t area_size = (_header.pref_count + _header.city_count + _header.town_count) * sizeof(_JA_AREA_PACKET); qInfo() << "HEADER SIZE:" << sizeof(_JA_HEADER); qInfo() << "STRING TABLE SIZE:" << _header.stringSize; qInfo() << "AREA TABLE SIZE:" << area_size; qInfo() << "JIBUN TOTAL BYTE:" << _totalJibunBytes; qInfo() << "CURRENT FILE SIZE:" << sizeof(_JA_HEADER) + _header.stringSize + area_size + _totalJibunBytes; // 2,455,848, 2455850 } bool _compareStringTable(const QString &k1, const QString &k2) { return FMAddress::_stringTable.value(k1) < FMAddress::_stringTable.value(k2); } void FMAddress::_saveStringTable() { QList ordered = _stringTable.keys(); qSort(ordered.begin(),ordered.end(),_compareStringTable); uint32_t offset = 0; // 정렬하고 foreach (QString key, ordered) { offset = _stringTable.value(key); //qInfo() << key << offset; fseek(_outFile, sizeof(_JA_HEADER) + offset,SEEK_SET); fwrite((wchar_t*)key.utf16(),key.length() * 2,1,_outFile); } wchar_t end = {0,}; fwrite(&end,2,1,_outFile); // 마지막 종료 문자 _writeOffset = sizeof(_JA_HEADER) + _header.stringSize; // 문자열 테이블 크기까지 } void FMAddress::_copyPacket(JA_AREA_PACKET* dest, JA_AREA* src) { dest->nameOffset = src->nameOffset; dest->sub_count = src->count; dest->xmin = src->xmin; dest->xmax = src->xmax; dest->ymin = src->ymin; dest->ymax = src->ymax; dest->bits[0] = src->bits[0]; dest->bits[1] = src->bits[1]; dest->bits[2] = src->bits[2]; dest->bits[3] = src->bits[3]; } bool FMAddress::_packJibun(JA_AREA* town,uint8_t* buffer,uint32_t* offset) { const uint32_t packetSize = sizeof(_JA_AREA_PACKET); // 지번 파일 시작 옵셋 const uint32_t jStartOffset = sizeof(_JA_HEADER) + _header.stringSize + (_header.pref_count * packetSize) + (_header.city_count * packetSize) + (_header.town_count * packetSize); uint32_t byte = _ABitsPackToByte(town->bits);// _bitPackToByte(town); // 레코드당 바이트 크기 if(byte > 8) { qInfo() << "TOO MANY BYTES!!!!!!!!!!!!!!!!"; return false; } uint32_t off = *offset; town->offset = (jStartOffset + off); // mask const uint32_t xm = ((1 << town->bits[0])-1); // xmask const uint32_t ym = ((1 << town->bits[1])-1); // ymask const uint32_t a0m = ((1 << town->bits[2])-1); // a0mask const uint32_t a1m = ((1 << town->bits[3])-1); // a1mask // A0-A1-x-y shift const uint32_t xs = town->bits[1] + town->bits[2] + town->bits[3]; const uint32_t ys = town->bits[2] + town->bits[3]; // a0 const uint32_t a0s = town->bits[3]; // a0 const uint32_t a1s = 0; // a1 //qInfo() << "---------------------------------------------------"; //qInfo() << "PACK:" << QString::fromUtf16(town->name) << "BYTE:" << byte << "BITS:" << town->bits[0] << town->bits[1] << town->bits[2] << town->bits[3]; //qInfo() << "mask:" << xm << ym << a0m << a1m; //qInfo() << "shifts:" << xs << ys << a0s << a1s; for(int j = 0;jcount;j++) { uint8_t *pj = &buffer[off]; JA_JIBUN* item = &town->subJibuns[j]; uint32_t x = (uint32_t)(item->x - town->xmin); uint32_t y = (uint32_t)(item->y - town->ymin); uint32_t a0 = (uint32_t)item->a0; uint32_t a1 = (uint32_t)item->a1; uint64_t pp = ((uint64_t)x << xs) | ((uint64_t)y << ys) | (a0 << a0s) | (a1 << a1s); uint8_t* pt = (uint8_t*)&pp; // 1244345 -> 235 252 27 0 0 0 0 0 //qInfo() << "PTB:" << pt[0] << pt[1] << pt[2] << pt[3] << pt[4] << pt[5] << pt[6] << pt[7]; memcpy(pj,pt,byte); off += byte; //qInfo() << "XY:" << x << y << "J:" << a0 << a1 << "P64:" << pp << "OFFSET:" << off; // unpack/verify uint8_t vb[8] = {0,}; memcpy(vb,pt,byte); uint64_t *vp = (uint64_t*)vb; uint32_t vx = ((*vp) >> xs) & xm; uint32_t vy = ((*vp) >> ys) & ym; uint32_t va0 = ((*vp) >> a0s) & a0m; uint32_t va1 = ((*vp) >> a1s) & a1m; if(vx != x || vy != y || va0 != a0 || va1 != a1) { qInfo() << "PACK:" << QString::fromUtf16(town->name) << "BYTE:" << byte << "BITS:" << town->bits[0] << town->bits[1] << town->bits[2] << town->bits[3]; qInfo() << "mask:" << xm << ym << a0m << a1m; qInfo() << "shifts:" << xs << ys << a0s << a1s; qInfo() << "PTB:" << pt[0] << pt[1] << pt[2] << pt[3] << pt[4] << pt[5] << pt[6] << pt[7]; qInfo() << "XY:" << x << y << "J:" << a0 << a1 << "P64:" << pp << "OFFSET:" << off; qInfo() << "!!!!!!! ERROR VXY:" << vx << vy << "J:" << va0 << va1; return false; } } *offset = off; return true; } // OFFSET 생성 + 데이터 저장 void FMAddress::_savePref() { const uint32_t packetSize = sizeof(_JA_AREA_PACKET); // 별도로 로딩하여 PREF PACKET = 0 부터 시작해야함 const uint32_t cityStartOffset = (_header.pref_count * packetSize); const uint32_t townStartOffset = cityStartOffset + (_header.city_count * packetSize); // 지번은 파일에서 로딩하여 실제 파일옵셋 저장 uint8_t *pJibun = (uint8_t*)malloc(_totalJibunBytes); memset(pJibun,0,_totalJibunBytes); //uint32_t totalArea = _header.pref_count + countCity + countTown; JA_AREA_PACKET* plist = (JA_AREA_PACKET*)malloc(_header.pref_count * packetSize); memset(plist,0,_header.pref_count * packetSize); JA_AREA_PACKET* clist = (JA_AREA_PACKET*)malloc(_header.city_count * packetSize); memset(clist,0,_header.city_count * packetSize); JA_AREA_PACKET* tlist = (JA_AREA_PACKET*)malloc(_header.town_count * packetSize); memset(tlist,0,_header.town_count * packetSize); //int pIndex = 0; == p int cIndex = 0; int tIndex = 0; uint32_t jOffset = 0; // HEADER + STRING TABLE + PREFS.... + CITYS.... + TOWNS.... + JIBUNS.... for(uint32_t p=0;p<_header.pref_count;p++) { // 도도부현 JA_AREA* pp = &_prefs[p]; JA_AREA_PACKET* ppp = &plist[p]; _copyPacket(ppp,pp); ppp->sub_offset = cityStartOffset + (cIndex * packetSize); for(uint32_t c=0;c<(uint32_t)pp->count;c++) { // 시군구 JA_AREA* cc = &pp->subAreas[c]; JA_AREA_PACKET* ccc = &clist[cIndex++]; _copyPacket(ccc,cc); ccc->sub_offset = townStartOffset + (tIndex * packetSize); for(uint32_t t=0;t<(uint32_t)cc->count;t++) { // 동읍면 JA_AREA* tt = &cc->subAreas[t]; JA_AREA_PACKET* ttt = &tlist[tIndex++]; //ttt->sub_offset = 0;//jibunStartOffset; _copyPacket(ttt,tt); // 데이터 저장 if(_packJibun(tt,pJibun,&jOffset) == false) { qInfo() << "PACK ERROR:" << QString::fromUtf16((ushort*)pp->name) << QString::fromUtf16((ushort*)cc->name) << QString::fromUtf16((ushort*)tt->name); free(plist); free(clist); free(tlist); free(pJibun); return; } ttt->sub_offset = tt->offset; // for(uint32_t j=0;j<(uint32_t)tt->count;j++) { // 번지 BYTE PACKET 생성 // //JA_JIBUN* jj = &((JA_JIBUN*)tt->offset)[j]; // // jOffset // } } //tIndex += cc->count; } //cIndex += pp->count; } //uint32_t offset = _writeOffset; // 현재 WRITE OFFSET 에서 시작 (HEADER + STRING TABLE) fwrite(plist,packetSize,_header.pref_count,_outFile); fwrite(clist,packetSize,_header.city_count,_outFile); fwrite(tlist,packetSize,_header.town_count,_outFile); fwrite(pJibun,_totalJibunBytes,1,_outFile); free(plist); free(clist); free(tlist); free(pJibun); } //int FMAddress::_bitPackToByte(_JA_AREA * src) //{ // return ceil((double)(src->bits[0] + src->bits[1] + src->bits[2] + src->bits[3])/8.0); //} // 중복 문자 필터 + 옵셋 저장 void FMAddress::_setStringOffset(JA_AREA* area,QString name) { _totalStringBufferLength += (MAX_JA_AREA_NAME_LEN * 2); _totalStringLength += (name.length() * 2); if(!_stringTable.contains(name)) { area->nameOffset = _currentStringOffset; _stringTable.insert(name,area->nameOffset); // HEADER + _currentStringOffset += ((name.length()+1) * 2); } else { area->nameOffset = _stringTable.value(name,0); } } void FMAddress::_createStatCSV() { _totalStringBufferLength = 0; _totalStringLength = 0; _currentStringOffset = 0; _header.xmin = 160 * 80000; _header.ymin = 50 * 120000; _header.xmax = 0; _header.ymax = 0; _header.city_count = 0; _header.town_count = 0; _header.jibun_count = 0; // LOOP for(uint32_t p=0;p<_header.pref_count;p++) { // 도도부현 JA_AREA* pp = &_prefs[p]; // qInfo() << QString::fromUtf16(pp->name); _initBound(pp); _setStringOffset(pp,QString::fromUtf16(pp->name)); _header.city_count += pp->count; // 전체 시구군 개수 for(uint32_t c=0;c<(uint32_t)pp->count;c++) { // 시구군 JA_AREA* cc = &pp->subAreas[c]; //qInfo() << QString::fromUtf16(cc->name) << cc->count << __FUNCTION__ << __LINE__;; _setStringOffset(cc,QString::fromUtf16(cc->name)); _header.town_count += cc->count; // 전체 TOWN 개수 _initBound(cc); for(uint32_t t=0;t<(uint32_t)cc->count;t++) { // 동읍면 JA_AREA* tt = &cc->subAreas[t]; _setStringOffset(tt,QString::fromUtf16(tt->name)); _header.jibun_count += tt->count; // 전체 지번 개수 // qInfo() << QString::fromUtf16(tt->name) << tt->count << __FUNCTION__ << __LINE__; // 丁目 로 끝나지 않는 TOWN 도 많음.. _initBound(tt); for(uint32_t j=0;j<(uint32_t)tt->count;j++) { // 번지 JA_JIBUN* jj = &tt->subJibuns[j]; _updateBoundP(tt,jj); } _updateBound(cc,tt); _sortJIBUN(tt); // 지번을 X순으로 정렬 _calculateBitPack(tt); // 지번 데이터 BIT PACK 계산 } _updateBound(pp,cc); } _header.xmin = qMin(_header.xmin,pp->xmin); _header.ymin = qMin(_header.ymin,pp->ymin); _header.xmax = qMax(_header.xmax,pp->xmax); _header.ymax = qMax(_header.ymax,pp->ymax); } //qInfo() << __FUNCTION__ << __LINE__; // LOOP 2 (CSV 생성) QString dest = "C://home//roadmovie//script//jpaddr//report.csv"; QFile outFile( dest ); outFile.open( QIODevice::WriteOnly | QIODevice::Text); QTextStream str( &outFile ); str.setCodec("UTF-16LE"); str.setGenerateByteOrderMark(true); str << "DO,SI,DONG,COUND,XMIN,YMIN,XMAX,YMAX,BYTE,XB,YB,A0B,A1B\n"; _totalJibunBytes = 0; for(uint32_t p=0;p<_header.pref_count;p++) { // 도도부현 JA_AREA* pp = &_prefs[p]; str << QString::fromUtf16(pp->name) << ",__,__," << QString::number(pp->count) << ","; str << QString::number(pp->xmin) << "," << QString::number(pp->ymin) << "," << QString::number(pp->xmax) << "," << QString::number(pp->ymax) << "\n"; for(uint32_t c=0;c<(uint32_t)pp->count;c++) { // 시군구 JA_AREA* cc = &pp->subAreas[c]; str << QString::fromUtf16(pp->name) << "," << QString::fromUtf16(cc->name) << ",__," << QString::number(cc->count) << ","; str << QString::number(cc->xmin) << "," << QString::number(cc->ymin) << "," << QString::number(cc->xmax) << "," << QString::number(cc->ymax) << "\n"; for(uint32_t t=0;t<(uint32_t)cc->count;t++) { // 동읍면 JA_AREA* tt = &cc->subAreas[t]; str << QString::fromUtf16(pp->name) << "," << QString::fromUtf16(cc->name) << "," << QString::fromUtf16(tt->name) << "," << QString::number(tt->count) << "," ; str << QString::number(tt->xmin) << "," << QString::number(tt->ymin) << "," << QString::number(tt->xmax) << "," << QString::number(tt->ymax) << ","; // _bitPackToByte(tt) _totalJibunBytes += ( _ABitsPackToByte(tt->bits) * tt->count); //uint8_t bt = 0; //str << _calculateBitPack(tt,&bt) << "\n"; //totalByte += ((tt->bits[0] + tt->bits[1] + tt->bits[2] + tt->bits[0]) * tt->count); // for(int j=0;jcount;j++) { // 번지 // JA_JIBUN* jj = &((JA_JIBUN*)tt->offset)[j]; // } } } } //qInfo() << __FUNCTION__ << __LINE__; // 현,개수 // -> 시,개수 // -> 동,개수,xmin,ymin,xmax,ymax outFile.close(); qInfo() << "TOTAL JIBUN BYTES:" << _totalJibunBytes; qInfo() << "STRINGS:" << _totalStringLength << _currentStringOffset << _totalStringBufferLength; _header.stringSize = _currentStringOffset; } void FMAddress::_resizeChome(JA_AREA* chome) { //qInfo() << QString::fromUtf16(chome->name) << chome->count << __FUNCTION__ << __LINE__; if(chome->count > 0) { JA_JIBUN* nj = (JA_JIBUN*)malloc(sizeof(_JA_JIBUN) * chome->count); memcpy(nj,chome->subJibuns,sizeof(_JA_JIBUN) * chome->count); free(chome->subJibuns); chome->subJibuns = nj; } } void FMAddress::_freeConvertData() { if(_prefs != NULL) { for(uint32_t p=0;p<_header.pref_count;p++) { JA_AREA* pref = &_prefs[p]; if (pref->count > 0) { JA_AREA* citys = pref->subAreas; for(uint32_t c=0;c<(uint32_t)pref->count;c++) { JA_AREA* city = &citys[c]; if (city->count > 0) { JA_AREA* chomes = city->subAreas; for(uint32_t t=0;t<(uint32_t)city->count;t++) { JA_AREA* chome = &chomes[t]; if(chome->count > 0) { free(chome->subJibuns); } } free(chomes); } } free(citys); } } free(_prefs); _prefs = NULL; } _currentJibun = NULL; _currentPref = NULL; _currentCity = NULL; _currentChome = NULL; } #endif // USE_JP_ADDRESS_TOOL #endif // USE_JP_ADDRESS