2026-04-14-8 auth and commercial

This commit is contained in:
Hamza-Ayed
2026-04-14 20:14:48 +03:00
parent be7dcc2652
commit f5b3f9f790
430 changed files with 6074 additions and 751 deletions
+19
View File
@@ -0,0 +1,19 @@
import { Repository } from 'typeorm';
import { AdminBoundary } from './entities/admin-boundary.entity';
export declare class AdminBoundariesService {
private adminBoundaryRepo;
private readonly logger;
constructor(adminBoundaryRepo: Repository<AdminBoundary>);
importGeoJSON(targetCountryCode: string, geoJson: any): Promise<{
success: boolean;
imported: number;
errors: number;
skipped: number;
}>;
importFromFile(countryCode: string, filePath: string): Promise<{
success: boolean;
imported: number;
errors: number;
skipped: number;
}>;
}
+137
View File
@@ -0,0 +1,137 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
var AdminBoundariesService_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.AdminBoundariesService = void 0;
const common_1 = require("@nestjs/common");
const typeorm_1 = require("@nestjs/typeorm");
const typeorm_2 = require("typeorm");
const admin_boundary_entity_1 = require("./entities/admin-boundary.entity");
let AdminBoundariesService = AdminBoundariesService_1 = class AdminBoundariesService {
adminBoundaryRepo;
logger = new common_1.Logger(AdminBoundariesService_1.name);
constructor(adminBoundaryRepo) {
this.adminBoundaryRepo = adminBoundaryRepo;
}
async importGeoJSON(targetCountryCode, geoJson) {
this.logger.log(`Importing admin boundaries for ${targetCountryCode}...`);
let imported = 0;
let errors = 0;
let skipped = 0;
if (!geoJson || !geoJson.features || !Array.isArray(geoJson.features)) {
this.logger.error('Invalid GeoJSON format');
return { success: false, imported: 0, errors: 1, skipped: 0 };
}
for (const feature of geoJson.features) {
try {
const props = feature.properties || {};
const countryCode = props.country || props.iso_3166_1_alpha2;
if (targetCountryCode && countryCode && countryCode.toUpperCase() !== targetCountryCode.toUpperCase()) {
skipped++;
continue;
}
const subtype = (props.subtype || '').toLowerCase();
let adminLevel;
switch (subtype) {
case 'country':
adminLevel = 2;
break;
case 'region':
adminLevel = 4;
break;
case 'county':
adminLevel = 6;
break;
case 'localadmin':
adminLevel = 8;
break;
case 'locality':
adminLevel = 8;
break;
case 'neighborhood':
adminLevel = 10;
break;
case 'macrohood':
adminLevel = 9;
break;
case 'microhood':
adminLevel = 11;
break;
default:
adminLevel = parseInt(props.admin_level || props.adminLevel, 10);
}
if (isNaN(adminLevel)) {
skipped++;
continue;
}
const nameAr = props['names']?.['primary'] || props['names']?.['common'] || props['name_ar'] || props['name'];
const nameEn = props['names']?.['en'] || props['name_en'];
const isArabic = (text) => /[\u0600-\u06FF]/.test(text);
if (['JO', 'SY', 'EG'].includes(targetCountryCode.toUpperCase()) && nameAr && !isArabic(nameAr)) {
const alternativeAr = Object.values(props['names'] || {}).find(v => typeof v === 'string' && isArabic(v));
if (!alternativeAr) {
skipped++;
continue;
}
}
let geom = feature.geometry;
if (geom.type === 'Polygon') {
geom = { type: 'MultiPolygon', coordinates: [geom.coordinates] };
}
else if (geom.type !== 'MultiPolygon') {
skipped++;
continue;
}
const boundary = this.adminBoundaryRepo.create({
country_code: targetCountryCode.toUpperCase(),
admin_level: adminLevel,
name_ar: nameAr,
name_en: nameEn,
geom: geom,
});
await this.adminBoundaryRepo.save(boundary);
imported++;
}
catch (err) {
this.logger.error(`Error importing feature: ${err.message}`);
errors++;
}
}
this.logger.log(`Import finished. Imported: ${imported}, Errors: ${errors}, Skipped: ${skipped}`);
return { success: true, imported, errors, skipped };
}
async importFromFile(countryCode, filePath) {
const fs = require('fs');
if (!fs.existsSync(filePath)) {
this.logger.error(`File not found: ${filePath}`);
return { success: false, imported: 0, errors: 1, skipped: 0 };
}
try {
const data = fs.readFileSync(filePath, 'utf8');
const geoJson = JSON.parse(data);
return this.importGeoJSON(countryCode, geoJson);
}
catch (err) {
this.logger.error(`Failed to read or parse GeoJSON file: ${err.message}`);
return { success: false, imported: 0, errors: 1, skipped: 0 };
}
}
};
exports.AdminBoundariesService = AdminBoundariesService;
exports.AdminBoundariesService = AdminBoundariesService = AdminBoundariesService_1 = __decorate([
(0, common_1.Injectable)(),
__param(0, (0, typeorm_1.InjectRepository)(admin_boundary_entity_1.AdminBoundary)),
__metadata("design:paramtypes", [typeorm_2.Repository])
], AdminBoundariesService);
//# sourceMappingURL=admin-boundaries.service.js.map
@@ -0,0 +1 @@
{"version":3,"file":"admin-boundaries.service.js","sourceRoot":"","sources":["../../src/geocoding/admin-boundaries.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
import { Repository, DataSource } from 'typeorm';
import { NeighborhoodPoint } from './entities/neighborhood-point.entity';
import { NeighborhoodPolygon } from './entities/neighborhood-polygon.entity';
import { AdminBoundary } from './entities/admin-boundary.entity';
export declare class AdministrativeLinkingService {
private dataSource;
private readonly neighborhoodPointRepo;
private readonly neighborhoodPolygonRepo;
private readonly adminBoundaryRepo;
private readonly logger;
constructor(dataSource: DataSource, neighborhoodPointRepo: Repository<NeighborhoodPoint>, neighborhoodPolygonRepo: Repository<NeighborhoodPolygon>, adminBoundaryRepo: Repository<AdminBoundary>);
private readonly overpassMirrors;
private fetchWithRetry;
syncOsmNeighborhoodPoints(bbox?: string, country?: string): Promise<{
detected_country: string;
osm_fetched: any;
country_points: number;
total_points_in_db: number;
linked_to_district: number;
}>;
generateVoronoiNeighborhoods(country?: string): Promise<{
status: string;
country: string;
total_polygons: number;
districts_processed: any;
}>;
linkPlaces(country: 'jordan' | 'syria' | 'egypt'): Promise<{
status: string;
country: "syria" | "jordan" | "egypt";
total_places: number;
linked_neighborhood: number;
linked_district: number;
linked_governorate: number;
sample_masoum: any;
rows_affected: any;
} | {
status: string;
error: string;
polygons: number;
places: number;
}>;
}
@@ -0,0 +1,285 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
var AdministrativeLinkingService_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.AdministrativeLinkingService = void 0;
const common_1 = require("@nestjs/common");
const typeorm_1 = require("@nestjs/typeorm");
const typeorm_2 = require("typeorm");
const neighborhood_point_entity_1 = require("./entities/neighborhood-point.entity");
const neighborhood_polygon_entity_1 = require("./entities/neighborhood-polygon.entity");
const admin_boundary_entity_1 = require("./entities/admin-boundary.entity");
const axios_1 = __importDefault(require("axios"));
let AdministrativeLinkingService = AdministrativeLinkingService_1 = class AdministrativeLinkingService {
dataSource;
neighborhoodPointRepo;
neighborhoodPolygonRepo;
adminBoundaryRepo;
logger = new common_1.Logger(AdministrativeLinkingService_1.name);
constructor(dataSource, neighborhoodPointRepo, neighborhoodPolygonRepo, adminBoundaryRepo) {
this.dataSource = dataSource;
this.neighborhoodPointRepo = neighborhoodPointRepo;
this.neighborhoodPolygonRepo = neighborhoodPolygonRepo;
this.adminBoundaryRepo = adminBoundaryRepo;
}
overpassMirrors = [
'https://overpass-api.de/api/interpreter',
'https://overpass.kumi.systems/api/interpreter',
'https://z.overpass-api.de/api/interpreter'
];
async fetchWithRetry(data, retries = 3) {
for (let i = 0; i <= retries; i++) {
const url = this.overpassMirrors[i % this.overpassMirrors.length];
try {
const response = await axios_1.default.post(url, data, {
timeout: 45000,
headers: {
'User-Agent': 'IntaleqMapBot/1.0',
'Content-Type': 'application/x-www-form-urlencoded'
}
});
return response.data;
}
catch (error) {
this.logger.warn(`Mirror ${url} failed: ${error.message}. Trying next mirror...`);
if (i === retries)
throw error;
await new Promise(res => setTimeout(res, 2000));
}
}
}
async syncOsmNeighborhoodPoints(bbox = '31.86,35.94,32.22,36.25', country) {
this.logger.log(`Syncing neighborhood points for country: ${country || 'auto'}, bbox: ${bbox}`);
const query = `[out:json][timeout:900];node["place"~"neighbourhood|suburb|town"](${bbox});out;`;
try {
const data = await this.fetchWithRetry(`data=${encodeURIComponent(query)}`);
const elements = data.elements;
this.logger.log(`Found ${elements.length} points in OSM.`);
let detectedCountry = country;
if (!detectedCountry) {
const firstLat = parseFloat(bbox.split(',')[0]);
if (firstLat > 32.3)
detectedCountry = 'syria';
else if (firstLat < 31.0)
detectedCountry = 'egypt';
else
detectedCountry = 'jordan';
}
for (const e of elements) {
const nameAr = e.tags['name:ar'] || e.tags['name'];
const nameEn = e.tags['name:en'] || e.tags['name'];
const geometry = { type: 'Point', coordinates: [e.lon, e.lat] };
await this.dataSource.query(`
INSERT INTO neighborhood_points (osm_id, name_ar, name_en, place_type, geometry, country)
VALUES ($1, $2, $3, $4, ST_SetSRID(ST_GeomFromGeoJSON($5), 4326), $6)
ON CONFLICT (osm_id) DO UPDATE SET
name_ar = EXCLUDED.name_ar,
name_en = EXCLUDED.name_en,
place_type = EXCLUDED.place_type,
geometry = EXCLUDED.geometry,
country = COALESCE(EXCLUDED.country, neighborhood_points.country);
`, [e.id, nameAr, nameEn, e.tags['place'], JSON.stringify(geometry), detectedCountry]);
}
await this.dataSource.query(`
UPDATE neighborhood_points np
SET district_id = COALESCE(
(
SELECT id FROM admin_boundaries ab
WHERE ab.admin_level IN (6, 8)
AND ST_Contains(ab.geom::geometry, np.geometry::geometry)
ORDER BY ab.admin_level DESC
LIMIT 1
),
(
SELECT id FROM admin_boundaries ab
WHERE ab.admin_level IN (6, 8)
ORDER BY ab.geom::geometry <-> np.geometry::geometry
LIMIT 1
)
)
WHERE district_id IS NULL AND (country = $1 OR $1 IS NULL);
`, [detectedCountry]);
const totalPoints = await this.dataSource.query(`SELECT count(*) as cnt FROM neighborhood_points`);
const linkedPoints = await this.dataSource.query(`SELECT count(*) as cnt FROM neighborhood_points WHERE district_id IS NOT NULL`);
const countryPoints = await this.dataSource.query(`SELECT count(*) as cnt FROM neighborhood_points WHERE country = $1`, [detectedCountry]);
const diagnostics = {
detected_country: detectedCountry,
osm_fetched: elements.length,
country_points: parseInt(countryPoints[0].cnt),
total_points_in_db: parseInt(totalPoints[0].cnt),
linked_to_district: parseInt(linkedPoints[0].cnt),
};
this.logger.log(`Step 1 diagnostics: ${JSON.stringify(diagnostics)}`);
return diagnostics;
}
catch (error) {
this.logger.error(`Failed to sync OSM points: ${error.message}`, error.stack);
throw error;
}
}
async generateVoronoiNeighborhoods(country) {
this.logger.log(`Generating Voronoi polygons for ${country || 'all'} neighborhoods...`);
const whereClause = country ? `WHERE district_id IS NOT NULL AND country = '${country}'` : `WHERE district_id IS NOT NULL`;
const prePoints = await this.dataSource.query(`SELECT count(*) as cnt FROM neighborhood_points ${whereClause}`);
const preDistricts = await this.dataSource.query(`SELECT district_id, count(*) as cnt FROM neighborhood_points ${whereClause} GROUP BY district_id`);
const deleteWhere = country ? `WHERE method = 'voronoi' AND country = '${country}'` : `WHERE method = 'voronoi'`;
await this.dataSource.query(`DELETE FROM neighborhood_polygons ${deleteWhere}`);
this.logger.log(`Deleted old voronoi polygons for ${country || 'all'}.`);
let totalInserted = 0;
for (const row of preDistricts) {
const districtId = row.district_id;
const pointCount = parseInt(row.cnt);
try {
if (pointCount >= 2) {
const result = await this.dataSource.query(`
INSERT INTO neighborhood_polygons (osm_id, name_ar, name_en, place_type, parent_id, method, country, geometry)
WITH voronoi_cells AS (
SELECT (ST_Dump(ST_VoronoiPolygons(
ST_Collect(np.geometry::geometry),
0.00001,
d.geom::geometry
))).geom as cell
FROM neighborhood_points np
JOIN admin_boundaries d ON d.id = np.district_id
WHERE np.district_id = $1
GROUP BY d.geom
),
matched AS (
SELECT
np.osm_id, np.name_ar, np.name_en, np.place_type, np.district_id, np.country,
ST_Multi(ST_Intersection(vc.cell::geometry, d.geom::geometry)) as geometry
FROM voronoi_cells vc
CROSS JOIN LATERAL (
SELECT np2.*
FROM neighborhood_points np2
WHERE np2.district_id = $1
ORDER BY np2.geometry::geometry <-> vc.cell::geometry
LIMIT 1
) np
JOIN admin_boundaries d ON d.id = $1
)
SELECT osm_id, name_ar, name_en, place_type, district_id, 'voronoi', country, geometry
FROM matched
WHERE ST_IsValid(geometry) AND NOT ST_IsEmpty(geometry)
`, [districtId]);
totalInserted += (result?.length || result?.[1] || 0);
}
else if (pointCount === 1) {
await this.dataSource.query(`
INSERT INTO neighborhood_polygons (osm_id, name_ar, name_en, place_type, parent_id, method, country, geometry)
SELECT np.osm_id, np.name_ar, np.name_en, np.place_type, np.district_id, 'voronoi', np.country, ST_Multi(d.geom::geometry)
FROM neighborhood_points np
JOIN admin_boundaries d ON d.id = np.district_id
WHERE np.district_id = $1
`, [districtId]);
totalInserted++;
}
}
catch (err) {
this.logger.error(`Voronoi FAILED for district ${districtId}: ${err.message}`);
}
}
const postCount = await this.dataSource.query(`SELECT count(*) as cnt FROM neighborhood_polygons ${country ? `WHERE country = '${country}'` : ''}`);
return {
status: 'success',
country: country || 'all',
total_polygons: parseInt(postCount[0].cnt),
districts_processed: preDistricts.length
};
}
async linkPlaces(country) {
const tableName = `places_${country}`;
const polyCount = await this.dataSource.query(`SELECT count(*) as cnt FROM neighborhood_polygons`);
const placeCount = await this.dataSource.query(`SELECT count(*) as cnt FROM ${tableName} WHERE location IS NOT NULL`);
this.logger.log(`linkPlaces pre-flight: ${polyCount[0].cnt} polygons, ${placeCount[0].cnt} places with location in ${tableName}`);
if (parseInt(polyCount[0].cnt) === 0) {
this.logger.error('ABORT: neighborhood_polygons is EMPTY. Run generate-voronoi first!');
return {
status: 'FAILED',
error: 'neighborhood_polygons table is empty. Run generate-voronoi first.',
polygons: 0,
places: parseInt(placeCount[0].cnt)
};
}
this.logger.log(`Linking administrative hierarchy for ${tableName} using KNN...`);
const updateResult = await this.dataSource.query(`
UPDATE ${tableName} p
SET
neighborhood_id = (
SELECT np.id FROM neighborhood_polygons np
WHERE (np.country = $1 OR np.country IS NULL)
ORDER BY p.location::geometry <-> np.geometry::geometry
LIMIT 1
),
sub_district_id = (
SELECT ab.id FROM admin_boundaries ab
WHERE ab.admin_level = 8
AND ST_Within(p.location::geometry, ab.geom::geometry)
LIMIT 1
),
district_id = (
SELECT ab.id FROM admin_boundaries ab
WHERE ab.admin_level = 6
AND ST_Within(p.location::geometry, ab.geom::geometry)
LIMIT 1
),
governorate_id = (
SELECT ab.id FROM admin_boundaries ab
WHERE ab.admin_level = 4
AND ST_Within(p.location::geometry, ab.geom::geometry)
LIMIT 1
)
WHERE p.location IS NOT NULL;
`, [country]);
const linkedNeighborhood = await this.dataSource.query(`SELECT count(*) as cnt FROM ${tableName} WHERE neighborhood_id IS NOT NULL`);
const linkedDistrict = await this.dataSource.query(`SELECT count(*) as cnt FROM ${tableName} WHERE district_id IS NOT NULL`);
const linkedGov = await this.dataSource.query(`SELECT count(*) as cnt FROM ${tableName} WHERE governorate_id IS NOT NULL`);
const totalPlaces = await this.dataSource.query(`SELECT count(*) as cnt FROM ${tableName}`);
const samplePlace = await this.dataSource.query(`
SELECT p.id, p.name_ar, p.neighborhood_id, p.district_id, p.governorate_id,
n.name_ar as neighborhood_name
FROM ${tableName} p
LEFT JOIN neighborhood_polygons n ON p.neighborhood_id = n.id
WHERE p.name_ar LIKE '%معصوم%'
LIMIT 3
`);
const diagnostics = {
status: 'completed',
country,
total_places: parseInt(totalPlaces[0].cnt),
linked_neighborhood: parseInt(linkedNeighborhood[0].cnt),
linked_district: parseInt(linkedDistrict[0].cnt),
linked_governorate: parseInt(linkedGov[0].cnt),
sample_masoum: samplePlace,
rows_affected: updateResult?.[1] || 'unknown'
};
this.logger.log(`Step 3 diagnostics: ${JSON.stringify(diagnostics)}`);
return diagnostics;
}
};
exports.AdministrativeLinkingService = AdministrativeLinkingService;
exports.AdministrativeLinkingService = AdministrativeLinkingService = AdministrativeLinkingService_1 = __decorate([
(0, common_1.Injectable)(),
__param(1, (0, typeorm_1.InjectRepository)(neighborhood_point_entity_1.NeighborhoodPoint)),
__param(2, (0, typeorm_1.InjectRepository)(neighborhood_polygon_entity_1.NeighborhoodPolygon)),
__param(3, (0, typeorm_1.InjectRepository)(admin_boundary_entity_1.AdminBoundary)),
__metadata("design:paramtypes", [typeorm_2.DataSource,
typeorm_2.Repository,
typeorm_2.Repository,
typeorm_2.Repository])
], AdministrativeLinkingService);
//# sourceMappingURL=administrative-linking.service.js.map
File diff suppressed because one or more lines are too long
+4
View File
@@ -0,0 +1,4 @@
export declare class ReverseGeocodeDto {
lat: number;
lng: number;
}
+39
View File
@@ -0,0 +1,39 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ReverseGeocodeDto = void 0;
const class_validator_1 = require("class-validator");
const class_transformer_1 = require("class-transformer");
const swagger_1 = require("@nestjs/swagger");
class ReverseGeocodeDto {
lat;
lng;
}
exports.ReverseGeocodeDto = ReverseGeocodeDto;
__decorate([
(0, class_validator_1.IsNotEmpty)(),
(0, class_validator_1.IsNumber)(),
(0, class_validator_1.Min)(-90),
(0, class_validator_1.Max)(90),
(0, class_transformer_1.Type)(() => Number),
(0, swagger_1.ApiProperty)({ description: 'Latitude' }),
__metadata("design:type", Number)
], ReverseGeocodeDto.prototype, "lat", void 0);
__decorate([
(0, class_validator_1.IsNotEmpty)(),
(0, class_validator_1.IsNumber)(),
(0, class_validator_1.Min)(-180),
(0, class_validator_1.Max)(180),
(0, class_transformer_1.Type)(() => Number),
(0, swagger_1.ApiProperty)({ description: 'Longitude' }),
__metadata("design:type", Number)
], ReverseGeocodeDto.prototype, "lng", void 0);
//# sourceMappingURL=reverse-geocode.dto.js.map
@@ -0,0 +1 @@
{"version":3,"file":"reverse-geocode.dto.js","sourceRoot":"","sources":["../../../src/geocoding/dto/reverse-geocode.dto.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qDAAiE;AACjE,yDAAyC;AACzC,6CAA8C;AAE9C,MAAa,iBAAiB;IAO5B,GAAG,CAAS;IAQZ,GAAG,CAAS;CACb;AAhBD,8CAgBC;AATC;IANC,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;IACV,IAAA,qBAAG,EAAC,CAAC,EAAE,CAAC;IACR,IAAA,qBAAG,EAAC,EAAE,CAAC;IACP,IAAA,wBAAI,EAAC,GAAG,EAAE,CAAC,MAAM,CAAC;IAClB,IAAA,qBAAW,EAAC,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC;;8CAC7B;AAQZ;IANC,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;IACV,IAAA,qBAAG,EAAC,CAAC,GAAG,CAAC;IACT,IAAA,qBAAG,EAAC,GAAG,CAAC;IACR,IAAA,wBAAI,EAAC,GAAG,EAAE,CAAC,MAAM,CAAC;IAClB,IAAA,qBAAW,EAAC,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC;;8CAC9B"}
+7
View File
@@ -0,0 +1,7 @@
export declare class SearchQueryDto {
q: string;
lat?: number;
lng?: number;
radius?: number;
country?: string;
}
+58
View File
@@ -0,0 +1,58 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SearchQueryDto = void 0;
const class_validator_1 = require("class-validator");
const class_transformer_1 = require("class-transformer");
const swagger_1 = require("@nestjs/swagger");
class SearchQueryDto {
q;
lat;
lng;
radius = 20000;
country;
}
exports.SearchQueryDto = SearchQueryDto;
__decorate([
(0, class_validator_1.IsString)(),
(0, swagger_1.ApiPropertyOptional)({ description: 'Search query string' }),
__metadata("design:type", String)
], SearchQueryDto.prototype, "q", void 0);
__decorate([
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsNumber)(),
(0, class_transformer_1.Type)(() => Number),
(0, swagger_1.ApiPropertyOptional)({ description: 'Latitude for proximity search' }),
__metadata("design:type", Number)
], SearchQueryDto.prototype, "lat", void 0);
__decorate([
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsNumber)(),
(0, class_transformer_1.Type)(() => Number),
(0, swagger_1.ApiPropertyOptional)({ description: 'Longitude for proximity search' }),
__metadata("design:type", Number)
], SearchQueryDto.prototype, "lng", void 0);
__decorate([
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsNumber)(),
(0, class_validator_1.Min)(0),
(0, class_validator_1.Max)(50000),
(0, class_transformer_1.Type)(() => Number),
(0, swagger_1.ApiPropertyOptional)({ description: 'Proximity radius in meters', default: 20000 }),
__metadata("design:type", Number)
], SearchQueryDto.prototype, "radius", void 0);
__decorate([
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsEnum)(['jordan', 'syria', 'egypt']),
(0, swagger_1.ApiPropertyOptional)({ description: 'Country filter', enum: ['jordan', 'syria', 'egypt'] }),
__metadata("design:type", String)
], SearchQueryDto.prototype, "country", void 0);
//# sourceMappingURL=search-query.dto.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"search-query.dto.js","sourceRoot":"","sources":["../../../src/geocoding/dto/search-query.dto.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qDAAmF;AACnF,yDAAyC;AACzC,6CAAsD;AAEtD,MAAa,cAAc;IAGzB,CAAC,CAAS;IAMV,GAAG,CAAU;IAMb,GAAG,CAAU;IAQb,MAAM,GAAY,KAAK,CAAC;IAKxB,OAAO,CAAU;CAClB;AA7BD,wCA6BC;AA1BC;IAFC,IAAA,0BAAQ,GAAE;IACV,IAAA,6BAAmB,EAAC,EAAE,WAAW,EAAE,qBAAqB,EAAE,CAAC;;yCAClD;AAMV;IAJC,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;IACV,IAAA,wBAAI,EAAC,GAAG,EAAE,CAAC,MAAM,CAAC;IAClB,IAAA,6BAAmB,EAAC,EAAE,WAAW,EAAE,+BAA+B,EAAE,CAAC;;2CACzD;AAMb;IAJC,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;IACV,IAAA,wBAAI,EAAC,GAAG,EAAE,CAAC,MAAM,CAAC;IAClB,IAAA,6BAAmB,EAAC,EAAE,WAAW,EAAE,gCAAgC,EAAE,CAAC;;2CAC1D;AAQb;IANC,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;IACV,IAAA,qBAAG,EAAC,CAAC,CAAC;IACN,IAAA,qBAAG,EAAC,KAAK,CAAC;IACV,IAAA,wBAAI,EAAC,GAAG,EAAE,CAAC,MAAM,CAAC;IAClB,IAAA,6BAAmB,EAAC,EAAE,WAAW,EAAE,4BAA4B,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;;8CAC3D;AAKxB;IAHC,IAAA,4BAAU,GAAE;IACZ,IAAA,wBAAM,EAAC,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IACpC,IAAA,6BAAmB,EAAC,EAAE,WAAW,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,CAAC;;+CAC1E"}
@@ -0,0 +1,8 @@
export declare class AdminBoundary {
id: number;
country_code: string;
admin_level: number;
name_ar: string;
name_en: string;
geom: any;
}
@@ -0,0 +1,54 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AdminBoundary = void 0;
const typeorm_1 = require("typeorm");
let AdminBoundary = class AdminBoundary {
id;
country_code;
admin_level;
name_ar;
name_en;
geom;
};
exports.AdminBoundary = AdminBoundary;
__decorate([
(0, typeorm_1.PrimaryGeneratedColumn)(),
__metadata("design:type", Number)
], AdminBoundary.prototype, "id", void 0);
__decorate([
(0, typeorm_1.Column)({ length: 3 }),
(0, typeorm_1.Index)(),
__metadata("design:type", String)
], AdminBoundary.prototype, "country_code", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'int' }),
(0, typeorm_1.Index)(),
__metadata("design:type", Number)
], AdminBoundary.prototype, "admin_level", void 0);
__decorate([
(0, typeorm_1.Column)({ nullable: true }),
(0, typeorm_1.Index)(),
__metadata("design:type", String)
], AdminBoundary.prototype, "name_ar", void 0);
__decorate([
(0, typeorm_1.Column)({ nullable: true }),
__metadata("design:type", String)
], AdminBoundary.prototype, "name_en", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'geometry', spatialFeatureType: 'MultiPolygon', srid: 4326, nullable: true }),
(0, typeorm_1.Index)({ spatial: true }),
__metadata("design:type", Object)
], AdminBoundary.prototype, "geom", void 0);
exports.AdminBoundary = AdminBoundary = __decorate([
(0, typeorm_1.Entity)('admin_boundaries')
], AdminBoundary);
//# sourceMappingURL=admin-boundary.entity.js.map
@@ -0,0 +1 @@
{"version":3,"file":"admin-boundary.entity.js","sourceRoot":"","sources":["../../../src/geocoding/entities/admin-boundary.entity.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qCAAwE;AAGjE,IAAM,aAAa,GAAnB,MAAM,aAAa;IAExB,EAAE,CAAS;IAIX,YAAY,CAAS;IAIrB,WAAW,CAAS;IAIpB,OAAO,CAAS;IAGhB,OAAO,CAAS;IAIhB,IAAI,CAAM;CACX,CAAA;AAtBY,sCAAa;AAExB;IADC,IAAA,gCAAsB,GAAE;;yCACd;AAIX;IAFC,IAAA,gBAAM,EAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;IACrB,IAAA,eAAK,GAAE;;mDACa;AAIrB;IAFC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;IACvB,IAAA,eAAK,GAAE;;kDACY;AAIpB;IAFC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1B,IAAA,eAAK,GAAE;;8CACQ;AAGhB;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;8CACX;AAIhB;IAFC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,UAAU,EAAE,kBAAkB,EAAE,cAAc,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC5F,IAAA,eAAK,EAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;;2CACf;wBArBC,aAAa;IADzB,IAAA,gBAAM,EAAC,kBAAkB,CAAC;GACd,aAAa,CAsBzB"}
+20
View File
@@ -0,0 +1,20 @@
export declare abstract class BasePlace {
id: number;
latitude: number;
longitude: number;
name: string;
name_ar: string;
name_en: string;
address: string;
category: string;
neighbourhood: string;
city: string;
description: string;
created_at: Date;
source: string;
location: any;
governorate_id: number;
district_id: number;
sub_district_id: number;
neighborhood_id: number;
}
+114
View File
@@ -0,0 +1,114 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.BasePlace = void 0;
const typeorm_1 = require("typeorm");
class BasePlace {
id;
latitude;
longitude;
name;
name_ar;
name_en;
address;
category;
neighbourhood;
city;
description;
created_at;
source;
location;
governorate_id;
district_id;
sub_district_id;
neighborhood_id;
}
exports.BasePlace = BasePlace;
__decorate([
(0, typeorm_1.PrimaryGeneratedColumn)(),
__metadata("design:type", Number)
], BasePlace.prototype, "id", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'decimal', precision: 10, scale: 8, nullable: true }),
__metadata("design:type", Number)
], BasePlace.prototype, "latitude", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'decimal', precision: 11, scale: 8, nullable: true }),
__metadata("design:type", Number)
], BasePlace.prototype, "longitude", void 0);
__decorate([
(0, typeorm_1.Column)({ nullable: true }),
(0, typeorm_1.Index)(),
__metadata("design:type", String)
], BasePlace.prototype, "name", void 0);
__decorate([
(0, typeorm_1.Column)({ nullable: true }),
(0, typeorm_1.Index)(),
__metadata("design:type", String)
], BasePlace.prototype, "name_ar", void 0);
__decorate([
(0, typeorm_1.Column)({ nullable: true }),
__metadata("design:type", String)
], BasePlace.prototype, "name_en", void 0);
__decorate([
(0, typeorm_1.Column)({ nullable: true }),
__metadata("design:type", String)
], BasePlace.prototype, "address", void 0);
__decorate([
(0, typeorm_1.Column)({ nullable: true }),
__metadata("design:type", String)
], BasePlace.prototype, "category", void 0);
__decorate([
(0, typeorm_1.Column)({ nullable: true }),
__metadata("design:type", String)
], BasePlace.prototype, "neighbourhood", void 0);
__decorate([
(0, typeorm_1.Column)({ nullable: true }),
__metadata("design:type", String)
], BasePlace.prototype, "city", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'text', nullable: true }),
__metadata("design:type", String)
], BasePlace.prototype, "description", void 0);
__decorate([
(0, typeorm_1.CreateDateColumn)(),
__metadata("design:type", Date)
], BasePlace.prototype, "created_at", void 0);
__decorate([
(0, typeorm_1.Column)({ nullable: true }),
__metadata("design:type", String)
], BasePlace.prototype, "source", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'geometry', spatialFeatureType: 'Point', srid: 4326, nullable: true }),
(0, typeorm_1.Index)({ spatial: true }),
__metadata("design:type", Object)
], BasePlace.prototype, "location", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'int', nullable: true }),
(0, typeorm_1.Index)(),
__metadata("design:type", Number)
], BasePlace.prototype, "governorate_id", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'int', nullable: true }),
(0, typeorm_1.Index)(),
__metadata("design:type", Number)
], BasePlace.prototype, "district_id", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'int', nullable: true }),
(0, typeorm_1.Index)(),
__metadata("design:type", Number)
], BasePlace.prototype, "sub_district_id", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'int', nullable: true }),
(0, typeorm_1.Index)(),
__metadata("design:type", Number)
], BasePlace.prototype, "neighborhood_id", void 0);
//# sourceMappingURL=base-place.entity.js.map
@@ -0,0 +1 @@
{"version":3,"file":"base-place.entity.js","sourceRoot":"","sources":["../../../src/geocoding/entities/base-place.entity.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qCAAkF;AAElF,MAAsB,SAAS;IAE7B,EAAE,CAAS;IAGX,QAAQ,CAAS;IAGjB,SAAS,CAAS;IAIlB,IAAI,CAAS;IAIb,OAAO,CAAS;IAGhB,OAAO,CAAS;IAGhB,OAAO,CAAS;IAGhB,QAAQ,CAAS;IAGjB,aAAa,CAAS;IAGtB,IAAI,CAAS;IAGb,WAAW,CAAS;IAGpB,UAAU,CAAO;IAGjB,MAAM,CAAS;IAIf,QAAQ,CAAM;IAId,cAAc,CAAS;IAIvB,WAAW,CAAS;IAIpB,eAAe,CAAS;IAIxB,eAAe,CAAS;CACzB;AA7DD,8BA6DC;AA3DC;IADC,IAAA,gCAAsB,GAAE;;qCACd;AAGX;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;2CACpD;AAGjB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;4CACnD;AAIlB;IAFC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1B,IAAA,eAAK,GAAE;;uCACK;AAIb;IAFC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1B,IAAA,eAAK,GAAE;;0CACQ;AAGhB;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;0CACX;AAGhB;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;0CACX;AAGhB;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;2CACV;AAGjB;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;gDACL;AAGtB;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;uCACd;AAGb;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;8CACrB;AAGpB;IADC,IAAA,0BAAgB,GAAE;8BACP,IAAI;6CAAC;AAGjB;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;yCACZ;AAIf;IAFC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,UAAU,EAAE,kBAAkB,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IACrF,IAAA,eAAK,EAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;;2CACX;AAId;IAFC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IACvC,IAAA,eAAK,GAAE;;iDACe;AAIvB;IAFC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IACvC,IAAA,eAAK,GAAE;;8CACY;AAIpB;IAFC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IACvC,IAAA,eAAK,GAAE;;kDACgB;AAIxB;IAFC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IACvC,IAAA,eAAK,GAAE;;kDACgB"}
@@ -0,0 +1,10 @@
export declare class NeighborhoodPoint {
id: number;
osm_id: number;
name_ar: string;
name_en: string;
place_type: string;
district_id: number;
country: string;
geometry: any;
}
@@ -0,0 +1,65 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.NeighborhoodPoint = void 0;
const typeorm_1 = require("typeorm");
let NeighborhoodPoint = class NeighborhoodPoint {
id;
osm_id;
name_ar;
name_en;
place_type;
district_id;
country;
geometry;
};
exports.NeighborhoodPoint = NeighborhoodPoint;
__decorate([
(0, typeorm_1.PrimaryGeneratedColumn)(),
__metadata("design:type", Number)
], NeighborhoodPoint.prototype, "id", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'bigint', unique: true, nullable: true }),
(0, typeorm_1.Index)(),
__metadata("design:type", Number)
], NeighborhoodPoint.prototype, "osm_id", void 0);
__decorate([
(0, typeorm_1.Column)({ nullable: true }),
(0, typeorm_1.Index)(),
__metadata("design:type", String)
], NeighborhoodPoint.prototype, "name_ar", void 0);
__decorate([
(0, typeorm_1.Column)({ nullable: true }),
__metadata("design:type", String)
], NeighborhoodPoint.prototype, "name_en", void 0);
__decorate([
(0, typeorm_1.Column)({ nullable: true }),
__metadata("design:type", String)
], NeighborhoodPoint.prototype, "place_type", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'int', nullable: true }),
(0, typeorm_1.Index)(),
__metadata("design:type", Number)
], NeighborhoodPoint.prototype, "district_id", void 0);
__decorate([
(0, typeorm_1.Column)({ nullable: true }),
(0, typeorm_1.Index)(),
__metadata("design:type", String)
], NeighborhoodPoint.prototype, "country", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'geometry', spatialFeatureType: 'Point', srid: 4326 }),
(0, typeorm_1.Index)({ spatial: true }),
__metadata("design:type", Object)
], NeighborhoodPoint.prototype, "geometry", void 0);
exports.NeighborhoodPoint = NeighborhoodPoint = __decorate([
(0, typeorm_1.Entity)('neighborhood_points')
], NeighborhoodPoint);
//# sourceMappingURL=neighborhood-point.entity.js.map
@@ -0,0 +1 @@
{"version":3,"file":"neighborhood-point.entity.js","sourceRoot":"","sources":["../../../src/geocoding/entities/neighborhood-point.entity.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qCAAwE;AAGjE,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;IAE5B,EAAE,CAAS;IAIX,MAAM,CAAS;IAIf,OAAO,CAAS;IAGhB,OAAO,CAAS;IAGhB,UAAU,CAAS;IAInB,WAAW,CAAS;IAIpB,OAAO,CAAS;IAIhB,QAAQ,CAAM;CACf,CAAA;AA7BY,8CAAiB;AAE5B;IADC,IAAA,gCAAsB,GAAE;;6CACd;AAIX;IAFC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IACxD,IAAA,eAAK,GAAE;;iDACO;AAIf;IAFC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1B,IAAA,eAAK,GAAE;;kDACQ;AAGhB;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;kDACX;AAGhB;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;qDACR;AAInB;IAFC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IACvC,IAAA,eAAK,GAAE;;sDACY;AAIpB;IAFC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1B,IAAA,eAAK,GAAE;;kDACQ;AAIhB;IAFC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,UAAU,EAAE,kBAAkB,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACrE,IAAA,eAAK,EAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;;mDACX;4BA5BH,iBAAiB;IAD7B,IAAA,gBAAM,EAAC,qBAAqB,CAAC;GACjB,iBAAiB,CA6B7B"}
@@ -0,0 +1,12 @@
export declare class NeighborhoodPolygon {
id: number;
osm_id: number;
name_ar: string;
name_en: string;
place_type: string;
parent_id: number;
method: string;
country: string;
confidence: number;
geometry: any;
}
@@ -0,0 +1,75 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.NeighborhoodPolygon = void 0;
const typeorm_1 = require("typeorm");
let NeighborhoodPolygon = class NeighborhoodPolygon {
id;
osm_id;
name_ar;
name_en;
place_type;
parent_id;
method;
country;
confidence;
geometry;
};
exports.NeighborhoodPolygon = NeighborhoodPolygon;
__decorate([
(0, typeorm_1.PrimaryGeneratedColumn)(),
__metadata("design:type", Number)
], NeighborhoodPolygon.prototype, "id", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'bigint', nullable: true }),
(0, typeorm_1.Index)(),
__metadata("design:type", Number)
], NeighborhoodPolygon.prototype, "osm_id", void 0);
__decorate([
(0, typeorm_1.Column)({ nullable: true }),
(0, typeorm_1.Index)(),
__metadata("design:type", String)
], NeighborhoodPolygon.prototype, "name_ar", void 0);
__decorate([
(0, typeorm_1.Column)({ nullable: true }),
__metadata("design:type", String)
], NeighborhoodPolygon.prototype, "name_en", void 0);
__decorate([
(0, typeorm_1.Column)({ nullable: true }),
__metadata("design:type", String)
], NeighborhoodPolygon.prototype, "place_type", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'int', nullable: true }),
(0, typeorm_1.Index)(),
__metadata("design:type", Number)
], NeighborhoodPolygon.prototype, "parent_id", void 0);
__decorate([
(0, typeorm_1.Column)({ default: 'voronoi' }),
__metadata("design:type", String)
], NeighborhoodPolygon.prototype, "method", void 0);
__decorate([
(0, typeorm_1.Column)({ nullable: true }),
(0, typeorm_1.Index)(),
__metadata("design:type", String)
], NeighborhoodPolygon.prototype, "country", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'float', default: 0.7 }),
__metadata("design:type", Number)
], NeighborhoodPolygon.prototype, "confidence", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'geometry', spatialFeatureType: 'MultiPolygon', srid: 4326 }),
(0, typeorm_1.Index)({ spatial: true }),
__metadata("design:type", Object)
], NeighborhoodPolygon.prototype, "geometry", void 0);
exports.NeighborhoodPolygon = NeighborhoodPolygon = __decorate([
(0, typeorm_1.Entity)('neighborhood_polygons')
], NeighborhoodPolygon);
//# sourceMappingURL=neighborhood-polygon.entity.js.map
@@ -0,0 +1 @@
{"version":3,"file":"neighborhood-polygon.entity.js","sourceRoot":"","sources":["../../../src/geocoding/entities/neighborhood-polygon.entity.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qCAAwE;AAGjE,IAAM,mBAAmB,GAAzB,MAAM,mBAAmB;IAE9B,EAAE,CAAS;IAIX,MAAM,CAAS;IAIf,OAAO,CAAS;IAGhB,OAAO,CAAS;IAGhB,UAAU,CAAS;IAInB,SAAS,CAAS;IAGlB,MAAM,CAAS;IAIf,OAAO,CAAS;IAGhB,UAAU,CAAS;IAInB,QAAQ,CAAM;CACf,CAAA;AAnCY,kDAAmB;AAE9B;IADC,IAAA,gCAAsB,GAAE;;+CACd;AAIX;IAFC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1C,IAAA,eAAK,GAAE;;mDACO;AAIf;IAFC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1B,IAAA,eAAK,GAAE;;oDACQ;AAGhB;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;oDACX;AAGhB;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;uDACR;AAInB;IAFC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IACvC,IAAA,eAAK,GAAE;;sDACU;AAGlB;IADC,IAAA,gBAAM,EAAC,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;;mDAChB;AAIf;IAFC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1B,IAAA,eAAK,GAAE;;oDACQ;AAGhB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC;;uDACrB;AAInB;IAFC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,UAAU,EAAE,kBAAkB,EAAE,cAAc,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IAC5E,IAAA,eAAK,EAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;;qDACX;8BAlCH,mBAAmB;IAD/B,IAAA,gBAAM,EAAC,uBAAuB,CAAC;GACnB,mBAAmB,CAmC/B"}
@@ -4,6 +4,7 @@ export declare class OsmPointWithArea {
latitude: number;
name: string;
name_ar: string;
name_en: string;
amenity: string;
shop: string;
addr_street: string;
@@ -17,6 +17,7 @@ let OsmPointWithArea = class OsmPointWithArea {
latitude;
name;
name_ar;
name_en;
amenity;
shop;
addr_street;
@@ -46,6 +47,10 @@ __decorate([
(0, typeorm_1.Column)({ nullable: true }),
__metadata("design:type", String)
], OsmPointWithArea.prototype, "name_ar", void 0);
__decorate([
(0, typeorm_1.Column)({ nullable: true }),
__metadata("design:type", String)
], OsmPointWithArea.prototype, "name_en", void 0);
__decorate([
(0, typeorm_1.Column)({ nullable: true }),
__metadata("design:type", String)
@@ -1 +1 @@
{"version":3,"file":"osm-point-with-area.entity.js","sourceRoot":"","sources":["../../../src/geocoding/entities/osm-point-with-area.entity.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qCAAwD;AAGjD,IAAM,gBAAgB,GAAtB,MAAM,gBAAgB;IAE3B,MAAM,CAAS;IAGf,SAAS,CAAS;IAGlB,QAAQ,CAAS;IAGjB,IAAI,CAAS;IAGb,OAAO,CAAS;IAGhB,OAAO,CAAS;IAGhB,IAAI,CAAS;IAGb,WAAW,CAAS;IAGpB,kBAAkB,CAAS;IAG3B,SAAS,CAAS;IAGlB,UAAU,CAAS;IAGnB,IAAI,CAAM;CACX,CAAA;AApCY,4CAAgB;AAE3B;IADC,IAAA,uBAAa,EAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;;gDACnB;AAGf;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;mDACnD;AAGlB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;kDACpD;AAGjB;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;8CACd;AAGb;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;iDACX;AAGhB;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;iDACX;AAGhB;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;8CACd;AAGb;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;qDACP;AAGpB;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;4DACA;AAG3B;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;mDACT;AAGlB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;oDACtB;AAGnB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,UAAU,EAAE,kBAAkB,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;8CAChE;2BAnCC,gBAAgB;IAD5B,IAAA,gBAAM,EAAC,sBAAsB,CAAC;GAClB,gBAAgB,CAoC5B"}
{"version":3,"file":"osm-point-with-area.entity.js","sourceRoot":"","sources":["../../../src/geocoding/entities/osm-point-with-area.entity.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qCAAwD;AAGjD,IAAM,gBAAgB,GAAtB,MAAM,gBAAgB;IAE3B,MAAM,CAAS;IAGf,SAAS,CAAS;IAGlB,QAAQ,CAAS;IAGjB,IAAI,CAAS;IAGb,OAAO,CAAS;IAGhB,OAAO,CAAS;IAGhB,OAAO,CAAS;IAGhB,IAAI,CAAS;IAGb,WAAW,CAAS;IAGpB,kBAAkB,CAAS;IAG3B,SAAS,CAAS;IAGlB,UAAU,CAAS;IAGnB,IAAI,CAAM;CACX,CAAA;AAvCY,4CAAgB;AAE3B;IADC,IAAA,uBAAa,EAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;;gDACnB;AAGf;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;mDACnD;AAGlB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;kDACpD;AAGjB;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;8CACd;AAGb;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;iDACX;AAGhB;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;iDACX;AAGhB;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;iDACX;AAGhB;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;8CACd;AAGb;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;qDACP;AAGpB;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;4DACA;AAG3B;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;mDACT;AAGlB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;oDACtB;AAGnB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,UAAU,EAAE,kBAAkB,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;8CAChE;2BAtCC,gBAAgB;IAD5B,IAAA,gBAAM,EAAC,sBAAsB,CAAC;GAClB,gBAAgB,CAuC5B"}
@@ -0,0 +1,3 @@
import { BasePlace } from './base-place.entity';
export declare class PlaceEgypt extends BasePlace {
}
+18
View File
@@ -0,0 +1,18 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PlaceEgypt = void 0;
const typeorm_1 = require("typeorm");
const base_place_entity_1 = require("./base-place.entity");
let PlaceEgypt = class PlaceEgypt extends base_place_entity_1.BasePlace {
};
exports.PlaceEgypt = PlaceEgypt;
exports.PlaceEgypt = PlaceEgypt = __decorate([
(0, typeorm_1.Entity)('places_egypt')
], PlaceEgypt);
//# sourceMappingURL=place-egypt.entity.js.map
@@ -0,0 +1 @@
{"version":3,"file":"place-egypt.entity.js","sourceRoot":"","sources":["../../../src/geocoding/entities/place-egypt.entity.ts"],"names":[],"mappings":";;;;;;;;;AAAA,qCAAiC;AACjC,2DAAgD;AAGzC,IAAM,UAAU,GAAhB,MAAM,UAAW,SAAQ,6BAAS;CAAG,CAAA;AAA/B,gCAAU;qBAAV,UAAU;IADtB,IAAA,gBAAM,EAAC,cAAc,CAAC;GACV,UAAU,CAAqB"}
@@ -0,0 +1,3 @@
import { BasePlace } from './base-place.entity';
export declare class PlaceJordan extends BasePlace {
}
+18
View File
@@ -0,0 +1,18 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PlaceJordan = void 0;
const typeorm_1 = require("typeorm");
const base_place_entity_1 = require("./base-place.entity");
let PlaceJordan = class PlaceJordan extends base_place_entity_1.BasePlace {
};
exports.PlaceJordan = PlaceJordan;
exports.PlaceJordan = PlaceJordan = __decorate([
(0, typeorm_1.Entity)('places_jordan')
], PlaceJordan);
//# sourceMappingURL=place-jordan.entity.js.map
@@ -0,0 +1 @@
{"version":3,"file":"place-jordan.entity.js","sourceRoot":"","sources":["../../../src/geocoding/entities/place-jordan.entity.ts"],"names":[],"mappings":";;;;;;;;;AAAA,qCAAiC;AACjC,2DAAgD;AAGzC,IAAM,WAAW,GAAjB,MAAM,WAAY,SAAQ,6BAAS;CAAG,CAAA;AAAhC,kCAAW;sBAAX,WAAW;IADvB,IAAA,gBAAM,EAAC,eAAe,CAAC;GACX,WAAW,CAAqB"}
+2 -11
View File
@@ -1,12 +1,3 @@
export declare class PlaceSyria {
id: number;
latitude: number;
longitude: number;
name: string;
name_ar: string;
name_en: string;
address: string;
category: string;
created_at: Date;
location: any;
import { BasePlace } from './base-place.entity';
export declare class PlaceSyria extends BasePlace {
}
+2 -54
View File
@@ -5,65 +5,13 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PlaceSyria = void 0;
const typeorm_1 = require("typeorm");
let PlaceSyria = class PlaceSyria {
id;
latitude;
longitude;
name;
name_ar;
name_en;
address;
category;
created_at;
location;
const base_place_entity_1 = require("./base-place.entity");
let PlaceSyria = class PlaceSyria extends base_place_entity_1.BasePlace {
};
exports.PlaceSyria = PlaceSyria;
__decorate([
(0, typeorm_1.PrimaryGeneratedColumn)(),
__metadata("design:type", Number)
], PlaceSyria.prototype, "id", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'decimal', precision: 10, scale: 8, nullable: true }),
__metadata("design:type", Number)
], PlaceSyria.prototype, "latitude", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'decimal', precision: 11, scale: 8, nullable: true }),
__metadata("design:type", Number)
], PlaceSyria.prototype, "longitude", void 0);
__decorate([
(0, typeorm_1.Column)({ nullable: true }),
__metadata("design:type", String)
], PlaceSyria.prototype, "name", void 0);
__decorate([
(0, typeorm_1.Column)({ nullable: true }),
__metadata("design:type", String)
], PlaceSyria.prototype, "name_ar", void 0);
__decorate([
(0, typeorm_1.Column)({ nullable: true }),
__metadata("design:type", String)
], PlaceSyria.prototype, "name_en", void 0);
__decorate([
(0, typeorm_1.Column)({ nullable: true }),
__metadata("design:type", String)
], PlaceSyria.prototype, "address", void 0);
__decorate([
(0, typeorm_1.Column)({ nullable: true }),
__metadata("design:type", String)
], PlaceSyria.prototype, "category", void 0);
__decorate([
(0, typeorm_1.CreateDateColumn)(),
__metadata("design:type", Date)
], PlaceSyria.prototype, "created_at", void 0);
__decorate([
(0, typeorm_1.Column)({ type: 'geometry', spatialFeatureType: 'Point', nullable: true }),
__metadata("design:type", Object)
], PlaceSyria.prototype, "location", void 0);
exports.PlaceSyria = PlaceSyria = __decorate([
(0, typeorm_1.Entity)('places_syria')
], PlaceSyria);
+1 -1
View File
@@ -1 +1 @@
{"version":3,"file":"place-syria.entity.js","sourceRoot":"","sources":["../../../src/geocoding/entities/place-syria.entity.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qCAAmF;AAG5E,IAAM,UAAU,GAAhB,MAAM,UAAU;IAErB,EAAE,CAAS;IAGX,QAAQ,CAAS;IAGjB,SAAS,CAAS;IAGlB,IAAI,CAAS;IAGb,OAAO,CAAS;IAGhB,OAAO,CAAS;IAGhB,OAAO,CAAS;IAGhB,QAAQ,CAAS;IAGjB,UAAU,CAAO;IAGjB,QAAQ,CAAM;CACf,CAAA;AA9BY,gCAAU;AAErB;IADC,IAAA,gCAAsB,GAAE;;sCACd;AAGX;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;4CACpD;AAGjB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;6CACnD;AAGlB;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;wCACd;AAGb;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;2CACX;AAGhB;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;2CACX;AAGhB;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;2CACX;AAGhB;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;4CACV;AAGjB;IADC,IAAA,0BAAgB,GAAE;8BACP,IAAI;8CAAC;AAGjB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,UAAU,EAAE,kBAAkB,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;4CAC5D;qBA7BH,UAAU;IADtB,IAAA,gBAAM,EAAC,cAAc,CAAC;GACV,UAAU,CA8BtB"}
{"version":3,"file":"place-syria.entity.js","sourceRoot":"","sources":["../../../src/geocoding/entities/place-syria.entity.ts"],"names":[],"mappings":";;;;;;;;;AAAA,qCAAiC;AACjC,2DAAgD;AAGzC,IAAM,UAAU,GAAhB,MAAM,UAAW,SAAQ,6BAAS;CAAG,CAAA;AAA/B,gCAAU;qBAAV,UAAU;IADtB,IAAA,gBAAM,EAAC,cAAc,CAAC;GACV,UAAU,CAAqB"}
+9
View File
@@ -0,0 +1,9 @@
import { OnModuleInit } from '@nestjs/common';
import { Repository } from 'typeorm';
import { PlaceSyria } from './entities/place-syria.entity';
export declare class GeocodingInitService implements OnModuleInit {
private readonly repo;
private readonly logger;
constructor(repo: Repository<PlaceSyria>);
onModuleInit(): Promise<void>;
}
+104
View File
@@ -0,0 +1,104 @@
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
var GeocodingInitService_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.GeocodingInitService = void 0;
const common_1 = require("@nestjs/common");
const typeorm_1 = require("@nestjs/typeorm");
const typeorm_2 = require("typeorm");
const place_syria_entity_1 = require("./entities/place-syria.entity");
let GeocodingInitService = GeocodingInitService_1 = class GeocodingInitService {
repo;
logger = new common_1.Logger(GeocodingInitService_1.name);
constructor(repo) {
this.repo = repo;
}
async onModuleInit() {
this.logger.log('Checking for PostGIS extensions and triggers...');
try {
await this.repo.query('CREATE EXTENSION IF NOT EXISTS postgis;');
await this.repo.query('CREATE EXTENSION IF NOT EXISTS pg_trgm;');
const tables = ['places_jordan', 'places_syria', 'places_egypt'];
const columnMapping = [
{ old: 'admin_level4_id', new: 'governorate_id' },
{ old: 'admin_level6_id', new: 'district_id' },
{ old: 'admin_level8_id', new: 'sub_district_id' },
{ old: 'admin_level10_id', new: 'neighborhood_id' }
];
for (const table of tables) {
for (const mapping of columnMapping) {
await this.repo.query(`
DO $$
BEGIN
IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = '${table}' AND column_name = '${mapping.old}') THEN
ALTER TABLE ${table} RENAME COLUMN ${mapping.old} TO ${mapping.new};
END IF;
END $$;
`);
}
}
await this.repo.query(`
CREATE OR REPLACE FUNCTION sync_place_location() RETURNS trigger AS $$
BEGIN
IF NEW.latitude IS NOT NULL AND NEW.longitude IS NOT NULL THEN
NEW.location := ST_SetSRID(ST_MakePoint(CAST(NEW.longitude AS FLOAT), CAST(NEW.latitude AS FLOAT)), 4326);
-- Keep standard admin boundary logic for higher levels
NEW.governorate_id := (SELECT id FROM admin_boundaries WHERE admin_level = 4 AND ST_Contains(geom, NEW.location) LIMIT 1);
NEW.district_id := (SELECT id FROM admin_boundaries WHERE admin_level = 6 AND ST_Contains(geom, NEW.location) LIMIT 1);
NEW.sub_district_id := (SELECT id FROM admin_boundaries WHERE admin_level = 8 AND ST_Contains(geom, NEW.location) LIMIT 1);
-- ROOT CAUSE FIX: Use the new Voronoi polygons for neighborhoods!
NEW.neighborhood_id := (SELECT id FROM neighborhood_polygons ORDER BY NEW.location::geometry <-> geometry::geometry LIMIT 1);
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
`);
await this.repo.query(`
DROP TRIGGER IF EXISTS trg_sync_place_location ON places_syria;
CREATE TRIGGER trg_sync_place_location
BEFORE INSERT OR UPDATE ON places_syria
FOR EACH ROW EXECUTE FUNCTION sync_place_location();
`);
await this.repo.query(`
DROP TRIGGER IF EXISTS trg_sync_place_location_jordan ON places_jordan;
CREATE TRIGGER trg_sync_place_location_jordan
BEFORE INSERT OR UPDATE ON places_jordan
FOR EACH ROW EXECUTE FUNCTION sync_place_location();
`);
await this.repo.query(`
DROP TRIGGER IF EXISTS trg_sync_place_location_egypt ON places_egypt;
CREATE TRIGGER trg_sync_place_location_egypt
BEFORE INSERT OR UPDATE ON places_egypt
FOR EACH ROW EXECUTE FUNCTION sync_place_location();
`);
await this.repo.query('CREATE INDEX IF NOT EXISTS idx_places_jordan_location ON places_jordan USING gist (location);');
await this.repo.query('CREATE INDEX IF NOT EXISTS idx_places_egypt_location ON places_egypt USING gist (location);');
await this.repo.query('CREATE INDEX IF NOT EXISTS idx_places_jordan_names_trgm ON places_jordan USING gist (name_ar gist_trgm_ops, name_en gist_trgm_ops);');
await this.repo.query('CREATE INDEX IF NOT EXISTS idx_places_egypt_names_trgm ON places_egypt USING gist (name_ar gist_trgm_ops, name_en gist_trgm_ops);');
this.logger.log('Geocoding database triggers and optimized indexes initialized for Syria, Jordan, and Egypt.');
}
catch (err) {
this.logger.error('Failed to initialize database geocoding triggers:', err);
}
}
};
exports.GeocodingInitService = GeocodingInitService;
exports.GeocodingInitService = GeocodingInitService = GeocodingInitService_1 = __decorate([
(0, common_1.Injectable)(),
__param(0, (0, typeorm_1.InjectRepository)(place_syria_entity_1.PlaceSyria)),
__metadata("design:paramtypes", [typeorm_2.Repository])
], GeocodingInitService);
//# sourceMappingURL=geocoding-init.service.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"geocoding-init.service.js","sourceRoot":"","sources":["../../src/geocoding/geocoding-init.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,2CAAkE;AAClE,6CAAmD;AACnD,qCAAqC;AACrC,sEAA2D;AAGpD,IAAM,oBAAoB,4BAA1B,MAAM,oBAAoB;IAKZ;IAJF,MAAM,GAAG,IAAI,eAAM,CAAC,sBAAoB,CAAC,IAAI,CAAC,CAAC;IAEhE,YAEmB,IAA4B;QAA5B,SAAI,GAAJ,IAAI,CAAwB;IAC5C,CAAC;IAEJ,KAAK,CAAC,YAAY;QAChB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iDAAiD,CAAC,CAAC;QACnE,IAAI,CAAC;YAEH,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,yCAAyC,CAAC,CAAC;YACjE,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,yCAAyC,CAAC,CAAC;YAGjE,MAAM,MAAM,GAAG,CAAC,eAAe,EAAE,cAAc,EAAE,cAAc,CAAC,CAAC;YACjE,MAAM,aAAa,GAAG;gBACpB,EAAE,GAAG,EAAE,iBAAiB,EAAE,GAAG,EAAE,gBAAgB,EAAE;gBACjD,EAAE,GAAG,EAAE,iBAAiB,EAAE,GAAG,EAAE,aAAa,EAAE;gBAC9C,EAAE,GAAG,EAAE,iBAAiB,EAAE,GAAG,EAAE,iBAAiB,EAAE;gBAClD,EAAE,GAAG,EAAE,kBAAkB,EAAE,GAAG,EAAE,iBAAiB,EAAE;aACpD,CAAC;YAEF,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;gBAC3B,KAAK,MAAM,OAAO,IAAI,aAAa,EAAE,CAAC;oBACpC,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC;;;wFAGwD,KAAK,wBAAwB,OAAO,CAAC,GAAG;8BAClG,KAAK,kBAAkB,OAAO,CAAC,GAAG,OAAO,OAAO,CAAC,GAAG;;;WAGvE,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;YAGD,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC;;;;;;;;;;;;;;;;;OAiBrB,CAAC,CAAC;YAEH,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC;;;;;OAKrB,CAAC,CAAC;YAGH,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC;;;;;OAKrB,CAAC,CAAC;YAEH,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC;;;;;OAKrB,CAAC,CAAC;YAGH,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,+FAA+F,CAAC,CAAC;YACvH,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,6FAA6F,CAAC,CAAC;YAGrH,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,qIAAqI,CAAC,CAAC;YAC7J,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,mIAAmI,CAAC,CAAC;YAE3J,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,6FAA6F,CAAC,CAAC;QACjH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,mDAAmD,EAAE,GAAG,CAAC,CAAC;QAC9E,CAAC;IACH,CAAC;CACF,CAAA;AA5FY,oDAAoB;+BAApB,oBAAoB;IADhC,IAAA,mBAAU,GAAE;IAKR,WAAA,IAAA,0BAAgB,EAAC,+BAAU,CAAC,CAAA;qCACN,oBAAU;GALxB,oBAAoB,CA4FhC"}
+104 -6
View File
@@ -1,11 +1,109 @@
import { GeocodingService } from './geocoding.service';
import { AdminBoundariesService } from './admin-boundaries.service';
import { JordanResearchService } from './jordan-research.service';
import { AdministrativeLinkingService } from './administrative-linking.service';
import { SearchQueryDto } from './dto/search-query.dto';
import { ReverseGeocodeDto } from './dto/reverse-geocode.dto';
export declare class GeocodingController {
private readonly geocodingService;
constructor(geocodingService: GeocodingService);
search(query: string): Promise<{
userPlaces: import("./entities/place-syria.entity").PlaceSyria[];
osmAreas: import("./entities/osm-area.entity").OsmArea[];
private readonly adminBoundariesService;
private readonly jordanResearchService;
private readonly adminLinkingService;
constructor(geocodingService: GeocodingService, adminBoundariesService: AdminBoundariesService, jordanResearchService: JordanResearchService, adminLinkingService: AdministrativeLinkingService);
search(queryDto: SearchQueryDto): Promise<{
results: any;
source: string;
} | {
results: any[];
source?: undefined;
}>;
reverse(reverseDto: ReverseGeocodeDto): Promise<any>;
addPlace(placeData: any): Promise<{
latitude: number;
longitude: number;
location: string;
id: number;
name: string;
name_ar: string;
name_en: string;
address: string;
category: string;
neighbourhood: string;
city: string;
description: string;
created_at: Date;
source: string;
governorate_id: number;
district_id: number;
sub_district_id: number;
neighborhood_id: number;
}>;
deletePlace(country: string, name?: string, id?: string): Promise<{
success: boolean;
affected: number | null | undefined;
}>;
upsertPlace(placeData: any): Promise<{
id: any;
action: string;
}>;
upsertBatch(body: {
places: any[];
}): Promise<{
total: number;
processed: number;
created: number;
updated: number;
}>;
getPlaces(limit?: string): Promise<(import("./entities/place-syria.entity").PlaceSyria | import("./entities/place-jordan.entity").PlaceJordan | import("./entities/place-egypt.entity").PlaceEgypt)[]>;
getGeoJSON(): Promise<{
type: string;
features: any;
}>;
importBoundaries(country: string, filePath: string): Promise<{
success: boolean;
imported: number;
errors: number;
skipped: number;
}>;
zarqaResearch(): Promise<{
area: string;
report_timestamp: string;
sources: {
osm: any;
overture: any;
wikidata: any;
static_files: {};
hdx: string;
gadm: string;
};
comparison_summary: string;
}>;
syncNeighborhoods(bbox?: string, country?: string): Promise<{
detected_country: string;
osm_fetched: any;
country_points: number;
total_points_in_db: number;
linked_to_district: number;
}>;
generateVoronoi(country?: string): Promise<{
status: string;
country: string;
total_polygons: number;
districts_processed: any;
}>;
linkPlaces(country: 'jordan' | 'syria' | 'egypt'): Promise<{
status: string;
country: "syria" | "jordan" | "egypt";
total_places: number;
linked_neighborhood: number;
linked_district: number;
linked_governorate: number;
sample_masoum: any;
rows_affected: any;
} | {
status: string;
error: string;
polygons: number;
places: number;
}>;
reverse(lat: number, lng: number): Promise<any>;
addPlace(placeData: any): Promise<import("./entities/place-syria.entity").PlaceSyria>;
}
+155 -14
View File
@@ -16,41 +16,87 @@ exports.GeocodingController = void 0;
const common_1 = require("@nestjs/common");
const swagger_1 = require("@nestjs/swagger");
const geocoding_service_1 = require("./geocoding.service");
const admin_boundaries_service_1 = require("./admin-boundaries.service");
const jordan_research_service_1 = require("./jordan-research.service");
const administrative_linking_service_1 = require("./administrative-linking.service");
const api_key_guard_1 = require("../common/guards/api-key.guard");
const rate_limiter_guard_1 = require("../common/guards/rate-limiter.guard");
const search_query_dto_1 = require("./dto/search-query.dto");
const reverse_geocode_dto_1 = require("./dto/reverse-geocode.dto");
let GeocodingController = class GeocodingController {
geocodingService;
constructor(geocodingService) {
adminBoundariesService;
jordanResearchService;
adminLinkingService;
constructor(geocodingService, adminBoundariesService, jordanResearchService, adminLinkingService) {
this.geocodingService = geocodingService;
this.adminBoundariesService = adminBoundariesService;
this.jordanResearchService = jordanResearchService;
this.adminLinkingService = adminLinkingService;
}
async search(query) {
return this.geocodingService.searchPlaces(query);
async search(queryDto) {
const { q, lat, lng, radius, country } = queryDto;
return this.geocodingService.searchPlaces(q, lat, lng, radius, country);
}
async reverse(lat, lng) {
async reverse(reverseDto) {
const { lat, lng } = reverseDto;
return this.geocodingService.reverseGeocode(lat, lng);
}
async addPlace(placeData) {
return this.geocodingService.addPlace(placeData);
}
async deletePlace(country, name, id) {
if (id) {
return this.geocodingService.deletePlaceById(Number(id), country);
}
if (name) {
return this.geocodingService.deletePlacesByName(name, country);
}
throw new common_1.HttpException('Name or ID required', common_1.HttpStatus.BAD_REQUEST);
}
async upsertPlace(placeData) {
return this.geocodingService.upsertPlace(placeData);
}
async upsertBatch(body) {
return this.geocodingService.upsertBatch(body.places);
}
async getPlaces(limit) {
return this.geocodingService.getRecentPlaces(limit ? parseInt(limit, 10) : 50);
}
async getGeoJSON() {
return this.geocodingService.getAllPlacesGeoJSON();
}
async importBoundaries(country, filePath) {
return this.adminBoundariesService.importFromFile(country, filePath);
}
async zarqaResearch() {
return this.jordanResearchService.generateZarqaReport();
}
async syncNeighborhoods(bbox, country) {
return this.adminLinkingService.syncOsmNeighborhoodPoints(bbox, country);
}
async generateVoronoi(country) {
return this.adminLinkingService.generateVoronoiNeighborhoods(country);
}
async linkPlaces(country) {
return this.adminLinkingService.linkPlaces(country);
}
};
exports.GeocodingController = GeocodingController;
__decorate([
(0, common_1.Get)('search'),
(0, swagger_1.ApiOperation)({ summary: 'Search for locations (Forward Geocoding)' }),
(0, swagger_1.ApiQuery)({ name: 'q', required: true }),
__param(0, (0, common_1.Query)('q')),
__param(0, (0, common_1.Query)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String]),
__metadata("design:paramtypes", [search_query_dto_1.SearchQueryDto]),
__metadata("design:returntype", Promise)
], GeocodingController.prototype, "search", null);
__decorate([
(0, common_1.Get)('reverse'),
(0, swagger_1.ApiOperation)({ summary: 'Reverse Geocoding (Lat/Lng to Address)' }),
(0, swagger_1.ApiQuery)({ name: 'lat', required: true }),
(0, swagger_1.ApiQuery)({ name: 'lng', required: true }),
__param(0, (0, common_1.Query)('lat')),
__param(1, (0, common_1.Query)('lng')),
__param(0, (0, common_1.Query)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Number, Number]),
__metadata("design:paramtypes", [reverse_geocode_dto_1.ReverseGeocodeDto]),
__metadata("design:returntype", Promise)
], GeocodingController.prototype, "reverse", null);
__decorate([
@@ -61,10 +107,105 @@ __decorate([
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", Promise)
], GeocodingController.prototype, "addPlace", null);
__decorate([
(0, common_1.Delete)('places'),
(0, swagger_1.ApiOperation)({ summary: 'Delete a place by name or ID' }),
(0, swagger_1.ApiQuery)({ name: 'name', required: false }),
(0, swagger_1.ApiQuery)({ name: 'id', required: false, type: Number }),
(0, swagger_1.ApiQuery)({ name: 'country', required: true, enum: ['jordan', 'syria', 'egypt'] }),
__param(0, (0, common_1.Query)('country')),
__param(1, (0, common_1.Query)('name')),
__param(2, (0, common_1.Query)('id')),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String, String, String]),
__metadata("design:returntype", Promise)
], GeocodingController.prototype, "deletePlace", null);
__decorate([
(0, common_1.Post)('upsert-place'),
(0, swagger_1.ApiOperation)({ summary: 'Add or Update a location (Automated Scraper)' }),
__param(0, (0, common_1.Body)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", Promise)
], GeocodingController.prototype, "upsertPlace", null);
__decorate([
(0, common_1.Post)('upsert-batch'),
(0, swagger_1.ApiOperation)({ summary: 'Add or Update multiple locations in bulk' }),
__param(0, (0, common_1.Body)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", Promise)
], GeocodingController.prototype, "upsertBatch", null);
__decorate([
(0, common_1.Get)('places'),
(0, swagger_1.ApiOperation)({ summary: 'Get recent user submitted places' }),
__param(0, (0, common_1.Query)('limit')),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String]),
__metadata("design:returntype", Promise)
], GeocodingController.prototype, "getPlaces", null);
__decorate([
(0, common_1.Get)('geojson'),
(0, swagger_1.ApiOperation)({ summary: 'Get all user submitted places as GeoJSON for Map Style' }),
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", Promise)
], GeocodingController.prototype, "getGeoJSON", null);
__decorate([
(0, common_1.Post)('import-boundaries'),
(0, swagger_1.ApiOperation)({ summary: 'Import administrative boundaries from a local GeoJSON file on the server' }),
(0, swagger_1.ApiQuery)({ name: 'country', required: true }),
(0, swagger_1.ApiQuery)({ name: 'filePath', required: true }),
__param(0, (0, common_1.Query)('country')),
__param(1, (0, common_1.Query)('filePath')),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String, String]),
__metadata("design:returntype", Promise)
], GeocodingController.prototype, "importBoundaries", null);
__decorate([
(0, common_1.Get)('research/zarqa'),
(0, swagger_1.ApiOperation)({ summary: 'Generate a research report for Zarqa, Jordan (Sample Data)' }),
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", Promise)
], GeocodingController.prototype, "zarqaResearch", null);
__decorate([
(0, common_1.Post)('admin/sync-neighborhoods'),
(0, swagger_1.ApiOperation)({ summary: 'Sync neighborhood points from OSM for a bbox' }),
(0, swagger_1.ApiQuery)({ name: 'bbox', required: false }),
(0, swagger_1.ApiQuery)({ name: 'country', required: false, enum: ['jordan', 'syria', 'egypt'] }),
__param(0, (0, common_1.Query)('bbox')),
__param(1, (0, common_1.Query)('country')),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String, String]),
__metadata("design:returntype", Promise)
], GeocodingController.prototype, "syncNeighborhoods", null);
__decorate([
(0, common_1.Post)('admin/generate-voronoi'),
(0, swagger_1.ApiOperation)({ summary: 'Generate Voronoi polygons for neighborhoods' }),
(0, swagger_1.ApiQuery)({ name: 'country', required: false, enum: ['jordan', 'syria', 'egypt'] }),
__param(0, (0, common_1.Query)('country')),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String]),
__metadata("design:returntype", Promise)
], GeocodingController.prototype, "generateVoronoi", null);
__decorate([
(0, common_1.Post)('admin/link-places'),
(0, swagger_1.ApiOperation)({ summary: 'Link places to administrative hierarchy' }),
(0, swagger_1.ApiQuery)({ name: 'country', required: true, enum: ['jordan', 'syria', 'egypt'] }),
__param(0, (0, common_1.Query)('country')),
__metadata("design:type", Function),
__metadata("design:paramtypes", [String]),
__metadata("design:returntype", Promise)
], GeocodingController.prototype, "linkPlaces", null);
exports.GeocodingController = GeocodingController = __decorate([
(0, swagger_1.ApiTags)('geocoding'),
(0, swagger_1.ApiHeader)({ name: 'x-api-key', description: 'Multi-tenant API Key', required: true }),
(0, common_1.Controller)('geocoding'),
(0, common_1.UseGuards)(api_key_guard_1.ApiKeyGuard),
__metadata("design:paramtypes", [geocoding_service_1.GeocodingService])
(0, common_1.UseGuards)(api_key_guard_1.ApiKeyGuard, rate_limiter_guard_1.TenantThrottlerGuard),
__metadata("design:paramtypes", [geocoding_service_1.GeocodingService,
admin_boundaries_service_1.AdminBoundariesService,
jordan_research_service_1.JordanResearchService,
administrative_linking_service_1.AdministrativeLinkingService])
], GeocodingController);
//# sourceMappingURL=geocoding.controller.js.map
File diff suppressed because one or more lines are too long
+65 -3
View File
@@ -1,19 +1,63 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.GeocodingModule = void 0;
const common_1 = require("@nestjs/common");
const typeorm_1 = require("@nestjs/typeorm");
const geocoding_service_1 = require("./geocoding.service");
const geocoding_init_service_1 = require("./geocoding-init.service");
const geocoding_controller_1 = require("./geocoding.controller");
const place_syria_entity_1 = require("./entities/place-syria.entity");
const place_jordan_entity_1 = require("./entities/place-jordan.entity");
const place_egypt_entity_1 = require("./entities/place-egypt.entity");
const osm_area_entity_1 = require("./entities/osm-area.entity");
const osm_point_with_area_entity_1 = require("./entities/osm-point-with-area.entity");
const admin_boundary_entity_1 = require("./entities/admin-boundary.entity");
const admin_boundaries_service_1 = require("./admin-boundaries.service");
const jordan_research_service_1 = require("./jordan-research.service");
const administrative_linking_service_1 = require("./administrative-linking.service");
const neighborhood_point_entity_1 = require("./entities/neighborhood-point.entity");
const neighborhood_polygon_entity_1 = require("./entities/neighborhood-polygon.entity");
const cache_manager_1 = require("@nestjs/cache-manager");
const redisStore = __importStar(require("cache-manager-redis-store"));
let GeocodingModule = class GeocodingModule {
};
exports.GeocodingModule = GeocodingModule;
@@ -22,12 +66,30 @@ exports.GeocodingModule = GeocodingModule = __decorate([
imports: [
typeorm_1.TypeOrmModule.forFeature([
place_syria_entity_1.PlaceSyria,
place_jordan_entity_1.PlaceJordan,
place_egypt_entity_1.PlaceEgypt,
osm_area_entity_1.OsmArea,
osm_point_with_area_entity_1.OsmPointWithArea
], 'mysqlConnection'),
osm_point_with_area_entity_1.OsmPointWithArea,
admin_boundary_entity_1.AdminBoundary,
neighborhood_point_entity_1.NeighborhoodPoint,
neighborhood_polygon_entity_1.NeighborhoodPolygon
]),
cache_manager_1.CacheModule.register({
store: redisStore,
host: process.env.REDIS_HOST || 'localhost',
port: parseInt(process.env.REDIS_PORT || '6379', 10),
ttl: 3600,
}),
],
controllers: [geocoding_controller_1.GeocodingController],
providers: [geocoding_service_1.GeocodingService],
providers: [
geocoding_service_1.GeocodingService,
geocoding_init_service_1.GeocodingInitService,
admin_boundaries_service_1.AdminBoundariesService,
jordan_research_service_1.JordanResearchService,
administrative_linking_service_1.AdministrativeLinkingService
],
exports: [geocoding_service_1.GeocodingService],
})
], GeocodingModule);
//# sourceMappingURL=geocoding.module.js.map
+1 -1
View File
@@ -1 +1 @@
{"version":3,"file":"geocoding.module.js","sourceRoot":"","sources":["../../src/geocoding/geocoding.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,6CAAgD;AAChD,2DAAuD;AACvD,iEAA6D;AAC7D,sEAA2D;AAC3D,gEAAqD;AACrD,sFAAyE;AAalE,IAAM,eAAe,GAArB,MAAM,eAAe;CAAG,CAAA;AAAlB,0CAAe;0BAAf,eAAe;IAX3B,IAAA,eAAM,EAAC;QACN,OAAO,EAAE;YACP,uBAAa,CAAC,UAAU,CAAC;gBACvB,+BAAU;gBACV,yBAAO;gBACP,6CAAgB;aACjB,EAAE,iBAAiB,CAAC;SACtB;QACD,WAAW,EAAE,CAAC,0CAAmB,CAAC;QAClC,SAAS,EAAE,CAAC,oCAAgB,CAAC;KAC9B,CAAC;GACW,eAAe,CAAG"}
{"version":3,"file":"geocoding.module.js","sourceRoot":"","sources":["../../src/geocoding/geocoding.module.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,2CAAwC;AACxC,6CAAgD;AAChD,2DAAuD;AACvD,qEAAgE;AAChE,iEAA6D;AAC7D,sEAA2D;AAC3D,wEAA6D;AAC7D,sEAA2D;AAC3D,gEAAqD;AACrD,sFAAyE;AACzE,4EAAiE;AACjE,yEAAoE;AACpE,uEAAkE;AAClE,qFAAgF;AAChF,oFAAyE;AACzE,wFAA6E;AAC7E,yDAAoD;AACpD,sEAAwD;AA+BjD,IAAM,eAAe,GAArB,MAAM,eAAe;CAAG,CAAA;AAAlB,0CAAe;0BAAf,eAAe;IA7B3B,IAAA,eAAM,EAAC;QACN,OAAO,EAAE;YACP,uBAAa,CAAC,UAAU,CAAC;gBACvB,+BAAU;gBACV,iCAAW;gBACX,+BAAU;gBACV,yBAAO;gBACP,6CAAgB;gBAChB,qCAAa;gBACb,6CAAiB;gBACjB,iDAAmB;aACpB,CAAC;YACF,2BAAW,CAAC,QAAQ,CAAC;gBACnB,KAAK,EAAE,UAAU;gBACjB,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,WAAW;gBAC3C,IAAI,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,MAAM,EAAE,EAAE,CAAC;gBACpD,GAAG,EAAE,IAAI;aACV,CAAC;SACH;QACD,WAAW,EAAE,CAAC,0CAAmB,CAAC;QAClC,SAAS,EAAE;YACT,oCAAgB;YAChB,6CAAoB;YACpB,iDAAsB;YACtB,+CAAqB;YACrB,6DAA4B;SAC7B;QACD,OAAO,EAAE,CAAC,oCAAgB,CAAC;KAC5B,CAAC;GACW,eAAe,CAAG"}
+65 -6
View File
@@ -1,17 +1,76 @@
import { Repository } from 'typeorm';
import type { Cache } from 'cache-manager';
import { PlaceSyria } from './entities/place-syria.entity';
import { PlaceJordan } from './entities/place-jordan.entity';
import { PlaceEgypt } from './entities/place-egypt.entity';
import { BasePlace } from './entities/base-place.entity';
import { OsmArea } from './entities/osm-area.entity';
import { OsmPointWithArea } from './entities/osm-point-with-area.entity';
export declare class GeocodingService {
private placesRepository;
private placesSyriaRepository;
private placesJordanRepository;
private placesEgyptRepository;
private osmAreasRepository;
private osmPointsRepository;
private cacheManager;
private readonly logger;
constructor(placesRepository: Repository<PlaceSyria>, osmAreasRepository: Repository<OsmArea>, osmPointsRepository: Repository<OsmPointWithArea>);
searchPlaces(query: string): Promise<{
userPlaces: PlaceSyria[];
osmAreas: OsmArea[];
private readonly DB_TIMEOUT_MS;
constructor(placesSyriaRepository: Repository<PlaceSyria>, placesJordanRepository: Repository<PlaceJordan>, placesEgyptRepository: Repository<PlaceEgypt>, osmAreasRepository: Repository<OsmArea>, osmPointsRepository: Repository<OsmPointWithArea>, cacheManager: Cache);
private getRepositoryForCoords;
private identifyRegion;
private getTableNameForRepo;
searchPlaces(query: string, lat?: number, lon?: number, radius?: number, country?: string): Promise<{
results: any;
source: string;
} | {
results: any[];
source?: undefined;
}>;
private formatResults;
private getRepoByTableName;
reverseGeocode(lat: number, lng: number): Promise<any>;
addPlace(data: Partial<PlaceSyria>): Promise<PlaceSyria>;
addPlace(data: Partial<BasePlace>): Promise<{
latitude: number;
longitude: number;
location: string;
id: number;
name: string;
name_ar: string;
name_en: string;
address: string;
category: string;
neighbourhood: string;
city: string;
description: string;
created_at: Date;
source: string;
governorate_id: number;
district_id: number;
sub_district_id: number;
neighborhood_id: number;
}>;
upsertPlace(data: Partial<BasePlace>): Promise<{
id: any;
action: string;
}>;
upsertBatch(places: Partial<BasePlace>[]): Promise<{
total: number;
processed: number;
created: number;
updated: number;
}>;
getRecentPlaces(limit?: number): Promise<(PlaceSyria | PlaceJordan | PlaceEgypt)[]>;
getAllPlacesGeoJSON(): Promise<{
type: string;
features: any;
}>;
deletePlacesByName(name: string, country: string): Promise<{
success: boolean;
affected: number;
}>;
deletePlaceById(id: number, country: string): Promise<{
success: boolean;
affected: number | null | undefined;
}>;
private getRepoByCountry;
}
+257 -39
View File
@@ -17,52 +17,188 @@ exports.GeocodingService = void 0;
const common_1 = require("@nestjs/common");
const typeorm_1 = require("@nestjs/typeorm");
const typeorm_2 = require("typeorm");
const cache_manager_1 = require("@nestjs/cache-manager");
const place_syria_entity_1 = require("./entities/place-syria.entity");
const place_jordan_entity_1 = require("./entities/place-jordan.entity");
const place_egypt_entity_1 = require("./entities/place-egypt.entity");
const osm_area_entity_1 = require("./entities/osm-area.entity");
const osm_point_with_area_entity_1 = require("./entities/osm-point-with-area.entity");
let GeocodingService = GeocodingService_1 = class GeocodingService {
placesRepository;
placesSyriaRepository;
placesJordanRepository;
placesEgyptRepository;
osmAreasRepository;
osmPointsRepository;
cacheManager;
logger = new common_1.Logger(GeocodingService_1.name);
constructor(placesRepository, osmAreasRepository, osmPointsRepository) {
this.placesRepository = placesRepository;
DB_TIMEOUT_MS = 1100;
constructor(placesSyriaRepository, placesJordanRepository, placesEgyptRepository, osmAreasRepository, osmPointsRepository, cacheManager) {
this.placesSyriaRepository = placesSyriaRepository;
this.placesJordanRepository = placesJordanRepository;
this.placesEgyptRepository = placesEgyptRepository;
this.osmAreasRepository = osmAreasRepository;
this.osmPointsRepository = osmPointsRepository;
this.cacheManager = cacheManager;
}
async searchPlaces(query) {
if (!query || query.length < 3)
return { userPlaces: [], osmAreas: [] };
const userPlaces = await this.placesRepository.find({
where: [
{ name: (0, typeorm_2.Like)(`%${query}%`) },
{ name_ar: (0, typeorm_2.Like)(`%${query}%`) },
],
take: 10,
getRepositoryForCoords(lat, lng) {
if (lat >= 29 && lat <= 37.5 && lng >= 34.5 && lng <= 42.5) {
if (lat > 32.5 && lng > 35.8)
return this.placesSyriaRepository;
return this.placesJordanRepository;
}
if (lat >= 22 && lat <= 32 && lng >= 24.5 && lng <= 37) {
return this.placesEgyptRepository;
}
return this.placesSyriaRepository;
}
identifyRegion(lat, lng) {
if (lat === undefined || lng === undefined)
return undefined;
if (lat >= 29 && lat <= 37.5 && lng >= 34.5 && lng <= 42.5) {
if (lat > 32.5 && lng > 35.8)
return 'syria';
return 'jordan';
}
if (lat >= 22 && lat <= 32 && lng >= 24.5 && lng <= 37) {
return 'egypt';
}
return undefined;
}
getTableNameForRepo(repo) {
if (repo === this.placesJordanRepository)
return 'places_jordan';
if (repo === this.placesEgyptRepository)
return 'places_egypt';
return 'places_syria';
}
async searchPlaces(query, lat, lon, radius = 20000, country) {
try {
const cleanQuery = query.trim();
if (!cleanQuery)
return { results: [] };
const hasLocation = lat !== undefined && lon !== undefined;
const geoSegment = hasLocation ? `${lat.toFixed(2)}_${lon.toFixed(2)}` : 'global';
const cacheKey = `geo_search:${country || 'auto'}:${geoSegment}:${cleanQuery.toLowerCase()}`;
const cached = await this.cacheManager.get(cacheKey);
if (cached)
return { results: cached, source: 'cache_hit' };
let targetRegion = country?.toLowerCase() || this.identifyRegion(lat, lon);
const primaryTables = targetRegion && ['syria', 'egypt', 'jordan'].includes(targetRegion)
? [`places_${targetRegion}`] : ['places_jordan', 'places_syria', 'places_egypt'];
const queryPromises = [];
primaryTables.forEach(tableName => {
const repo = this.getRepoByTableName(tableName);
queryPromises.push(repo.query(`
SELECT
p.id, p.name, p.name_ar, p.name_en, p.category,
n.name_ar as neighbourhood, d.name_ar as district, g.name_ar as governorate,
p.latitude, p.longitude, p.address, '${tableName.replace('places_', '')}' as region, 'user_place' as source,
CASE WHEN $2::float IS NOT NULL THEN ST_DistanceSphere(p.location, ST_SetSRID(ST_MakePoint($3::float, $2::float), 4326)) ELSE 0 END as distance,
GREATEST(similarity(COALESCE(p.name_ar, ''), $1), similarity(COALESCE(p.name, ''), $1)) as relevance
FROM ${tableName} p
LEFT JOIN neighborhood_polygons n ON p.neighborhood_id = n.id
LEFT JOIN admin_boundaries d ON p.district_id = d.id
LEFT JOIN admin_boundaries g ON p.governorate_id = g.id
WHERE (p.name_ar % $1 OR p.name % $1)
AND ($2::float IS NULL OR ST_DistanceSphere(p.location, ST_SetSRID(ST_MakePoint($3::float, $2::float), 4326)) <= $4)
ORDER BY (p.name_ar <-> $1) ASC LIMIT 10
`, [cleanQuery, lat || null, lon || null, radius]));
});
queryPromises.push(this.osmPointsRepository.query(`
SELECT
o.osm_id as id, o.name, o.name_ar, o.name_en, COALESCE(o.amenity, o.shop, 'place') as category,
o.latitude, o.longitude, o.addr_street as address, 'osm_global' as source,
CASE WHEN $2::float IS NOT NULL THEN ST_DistanceSphere(o.geom, ST_SetSRID(ST_MakePoint($3::float, $2::float), 4326)) ELSE 0 END as distance,
GREATEST(similarity(COALESCE(o.name, ''), $1), similarity(COALESCE(o.name_ar, ''), $1)) as relevance
FROM osm_points_with_area o
WHERE (o.name % $1 OR o.name_ar % $1)
AND ($2::float IS NULL OR ST_DistanceSphere(o.geom, ST_SetSRID(ST_MakePoint($3::float, $2::float), 4326)) <= $4)
ORDER BY (o.name <-> $1) ASC LIMIT 10
`, [cleanQuery, lat || null, lon || null, radius]));
const executionResults = await Promise.race([
Promise.allSettled(queryPromises),
new Promise((_, reject) => setTimeout(() => reject(new Error('QUERY_TIMEOUT')), this.DB_TIMEOUT_MS))
]).catch(e => {
this.logger.warn(`Search optimization threshold hit: ${e.message}`);
return [];
});
let allResults = [];
if (Array.isArray(executionResults)) {
executionResults.forEach(res => {
if (res.status === 'fulfilled' && res.value)
allResults.push(...res.value);
});
}
const formatted = this.formatResults(allResults, hasLocation);
if (formatted.length > 0) {
await this.cacheManager.set(cacheKey, formatted, 3600000);
}
return { results: formatted };
}
catch (e) {
this.logger.error('Search failed:', e);
return { results: [] };
}
}
formatResults(results, hasLocation) {
const seenStreets = new Set();
return results
.map(r => {
const proximityBonus = hasLocation ? Math.max(0, 1 - (Number(r.distance) / 10000)) : 0;
const totalScore = (Number(r.relevance) * 0.7) + (proximityBonus * 0.3);
return { ...r, totalScore };
})
.sort((a, b) => b.totalScore - a.totalScore)
.filter(r => {
if (r.category === 'street') {
const key = `${r.name_ar || r.name}_${r.district}_${r.governorate}`;
if (seenStreets.has(key))
return false;
seenStreets.add(key);
}
return true;
})
.slice(0, 4)
.map(r => {
const addressParts = [r.neighbourhood, r.district, r.governorate].filter(Boolean);
const full_address = addressParts.length > 0 ? addressParts.join('، ') : (r.address || '');
return {
...r,
latitude: parseFloat(r.latitude),
longitude: parseFloat(r.longitude),
distance_km: r.distance ? (Number(r.distance) / 1000).toFixed(2) : null,
location: { lat: parseFloat(r.latitude), lng: parseFloat(r.longitude) },
full_address,
};
});
const osmAreas = await this.osmAreasRepository.find({
where: [
{ name: (0, typeorm_2.Like)(`%${query}%`) },
{ name_ar: (0, typeorm_2.Like)(`%${query}%`) },
],
take: 10,
});
return {
userPlaces,
osmAreas,
};
}
getRepoByTableName(tableName) {
if (tableName === 'places_egypt')
return this.placesEgyptRepository;
if (tableName === 'places_jordan')
return this.placesJordanRepository;
return this.placesSyriaRepository;
}
async reverseGeocode(lat, lng) {
try {
const repo = this.getRepositoryForCoords(lat, lng);
const tableName = this.getTableNameForRepo(repo);
const query = `
SELECT *, ST_Distance_Sphere(location, POINT(?, ?)) as distance
FROM places_syria
WHERE location IS NOT NULL
ORDER BY distance ASC
LIMIT 5
SELECT
p.id, p.name, p.name_ar, p.category,
n.name_ar as neighbourhood,
d.name_ar as district,
g.name_ar as governorate,
p.latitude, p.longitude, p.address, 'user_place' as source,
ST_DistanceSphere(p.location::geometry, ST_SetSRID(ST_MakePoint($1::float, $2::float), 4326)) as distance
FROM ${tableName} p
LEFT JOIN neighborhood_polygons n ON p.neighborhood_id = n.id
LEFT JOIN admin_boundaries d ON p.sub_district_id = d.id
LEFT JOIN admin_boundaries g ON p.governorate_id = g.id
WHERE p.location IS NOT NULL
ORDER BY p.location::geometry <-> ST_SetSRID(ST_MakePoint($1::float, $2::float), 4326) ASC LIMIT 3
`;
const results = await this.placesRepository.query(query, [lng, lat]);
return results;
return await repo.query(query, [lng, lat]);
}
catch (error) {
this.logger.error('Reverse geocoding error:', error);
@@ -70,21 +206,103 @@ let GeocodingService = GeocodingService_1 = class GeocodingService {
}
}
async addPlace(data) {
const newPlace = this.placesRepository.create({
...data,
created_at: new Date(),
});
return this.placesRepository.save(newPlace);
try {
const lat = Number(data.latitude), lng = Number(data.longitude);
const repo = this.getRepositoryForCoords(lat, lng), tableName = this.getTableNameForRepo(repo);
const newPlace = repo.create({ ...data, latitude: lat, longitude: lng, created_at: new Date(), location: { type: 'Point', coordinates: [lng, lat] } });
const savedPlace = await repo.save(newPlace);
await repo.query(`UPDATE ${tableName} SET location = ST_SetSRID(ST_MakePoint($1::float, $2::float), 4326) WHERE id = $3`, [lng, lat, savedPlace.id]);
return { ...savedPlace, latitude: lat, longitude: lng, location: `POINT(${lng} ${lat})` };
}
catch (error) {
throw new common_1.HttpException(error.message, common_1.HttpStatus.INTERNAL_SERVER_ERROR);
}
}
async upsertPlace(data) {
try {
const lat = Number(data.latitude), lng = Number(data.longitude);
const repo = this.getRepositoryForCoords(lat, lng), tableName = this.getTableNameForRepo(repo);
const existing = await repo.query(`SELECT id, name FROM ${tableName} WHERE ST_DistanceSphere(location, ST_SetSRID(ST_MakePoint($1::float, $2::float), 4326)) < 15 LIMIT 1`, [lng, lat]);
if (existing && existing.length > 0) {
await repo.update(existing[0].id, { name: data.name || undefined, name_ar: data.name_ar || undefined, category: data.category || undefined });
return { id: existing[0].id, action: 'updated' };
}
const result = await this.addPlace(data);
return { id: result.id, action: 'created' };
}
catch (error) {
throw error;
}
}
async upsertBatch(places) {
const results = [];
for (const place of places) {
try {
const r = await this.addPlace(place);
results.push({ id: r.id, action: 'created' });
}
catch (e) { }
}
return { total: places.length, processed: results.length, created: results.length, updated: 0 };
}
async getRecentPlaces(limit = 50) {
const s = await this.placesSyriaRepository.find({ order: { created_at: 'DESC' }, take: limit });
const j = await this.placesJordanRepository.find({ order: { created_at: 'DESC' }, take: limit });
const e = await this.placesEgyptRepository.find({ order: { created_at: 'DESC' }, take: limit });
return [...s, ...j, ...e].sort((a, b) => b.created_at.getTime() - a.created_at.getTime()).slice(0, limit);
}
async getAllPlacesGeoJSON() {
try {
const q = `
SELECT id::text, name_ar as name, category, latitude, longitude, address, 'user' as region FROM places_syria
UNION ALL SELECT id::text, name_ar as name, category, latitude, longitude, address, 'user' as region FROM places_jordan
UNION ALL SELECT id::text, name_ar as name, category, latitude, longitude, address, 'user' as region FROM places_egypt
UNION ALL SELECT id::text, COALESCE(names->>'primary', names->>'common', 'Building') as name, 'building' as category, ST_Y(ST_Centroid(location)) as latitude, ST_X(ST_Centroid(location)) as longitude, '' as address, 'overture' as region FROM overture_building WHERE names->>'primary' IS NOT NULL LIMIT 500
UNION ALL SELECT id::text, COALESCE(names->>'primary', names->>'common', 'Street') as name, 'street' as category, ST_Y(ST_Centroid(location)) as latitude, ST_X(ST_Centroid(location)) as longitude, '' as address, 'overture' as region FROM overture_segment WHERE names->>'primary' IS NOT NULL LIMIT 500
`;
const res = await this.placesSyriaRepository.query(q);
const features = res.map(p => ({
type: 'Feature',
geometry: { type: 'Point', coordinates: [parseFloat(p.longitude), parseFloat(p.latitude)] },
properties: { id: p.id, name: p.name, category: p.category, address: p.address, region: p.region }
}));
return { type: 'FeatureCollection', features };
}
catch (e) {
return { type: 'FeatureCollection', features: [] };
}
}
async deletePlacesByName(name, country) {
const repo = this.getRepoByCountry(country);
const r1 = await repo.delete({ name_ar: name }), r2 = await repo.delete({ name_en: name }), r3 = await repo.delete({ name: name });
return { success: true, affected: (r1.affected || 0) + (r2.affected || 0) + (r3.affected || 0) };
}
async deletePlaceById(id, country) {
const repo = this.getRepoByCountry(country);
return { success: true, affected: (await repo.delete(id)).affected };
}
getRepoByCountry(country) {
switch (country?.toLowerCase()) {
case 'syria': return this.placesSyriaRepository;
case 'jordan': return this.placesJordanRepository;
case 'egypt': return this.placesEgyptRepository;
default: throw new common_1.HttpException('Invalid country: ' + country, common_1.HttpStatus.BAD_REQUEST);
}
}
};
exports.GeocodingService = GeocodingService;
exports.GeocodingService = GeocodingService = GeocodingService_1 = __decorate([
(0, common_1.Injectable)(),
__param(0, (0, typeorm_1.InjectRepository)(place_syria_entity_1.PlaceSyria, 'mysqlConnection')),
__param(1, (0, typeorm_1.InjectRepository)(osm_area_entity_1.OsmArea, 'mysqlConnection')),
__param(2, (0, typeorm_1.InjectRepository)(osm_point_with_area_entity_1.OsmPointWithArea, 'mysqlConnection')),
__param(0, (0, typeorm_1.InjectRepository)(place_syria_entity_1.PlaceSyria)),
__param(1, (0, typeorm_1.InjectRepository)(place_jordan_entity_1.PlaceJordan)),
__param(2, (0, typeorm_1.InjectRepository)(place_egypt_entity_1.PlaceEgypt)),
__param(3, (0, typeorm_1.InjectRepository)(osm_area_entity_1.OsmArea)),
__param(4, (0, typeorm_1.InjectRepository)(osm_point_with_area_entity_1.OsmPointWithArea)),
__param(5, (0, common_1.Inject)(cache_manager_1.CACHE_MANAGER)),
__metadata("design:paramtypes", [typeorm_2.Repository,
typeorm_2.Repository,
typeorm_2.Repository])
typeorm_2.Repository,
typeorm_2.Repository,
typeorm_2.Repository, Object])
], GeocodingService);
//# sourceMappingURL=geocoding.service.js.map
File diff suppressed because one or more lines are too long
+25
View File
@@ -0,0 +1,25 @@
import { Repository } from 'typeorm';
import { PlaceJordan } from './entities/place-jordan.entity';
export declare class JordanResearchService {
private readonly placeRepo;
private readonly logger;
constructor(placeRepo: Repository<PlaceJordan>);
private fetchWithRetry;
fetchOsmZarqa(): Promise<any>;
fetchOvertureZarqa(): Promise<any>;
fetchWikidataZarqa(): Promise<any>;
checkStaticFiles(): Promise<{}>;
generateZarqaReport(): Promise<{
area: string;
report_timestamp: string;
sources: {
osm: any;
overture: any;
wikidata: any;
static_files: {};
hdx: string;
gadm: string;
};
comparison_summary: string;
}>;
}
+183
View File
@@ -0,0 +1,183 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
var JordanResearchService_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.JordanResearchService = void 0;
const common_1 = require("@nestjs/common");
const typeorm_1 = require("@nestjs/typeorm");
const typeorm_2 = require("typeorm");
const place_jordan_entity_1 = require("./entities/place-jordan.entity");
const axios_1 = __importDefault(require("axios"));
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
let JordanResearchService = JordanResearchService_1 = class JordanResearchService {
placeRepo;
logger = new common_1.Logger(JordanResearchService_1.name);
constructor(placeRepo) {
this.placeRepo = placeRepo;
}
async fetchWithRetry(url, data, method = 'get', retries = 2) {
for (let i = 0; i <= retries; i++) {
try {
const config = {
timeout: 20000,
headers: {
'User-Agent': 'Mozilla/5.0 (compatible; IntaleqMapBot/1.0; +https://intaleq.xyz)',
'Accept': 'application/json, application/sparql-results+json'
}
};
const response = method === 'post'
? await axios_1.default.post(url, data, config)
: await axios_1.default.get(url, { ...config, params: data ? { query: data, format: 'json' } : {} });
return response.data;
}
catch (error) {
if (i === retries) {
this.logger.error(`Failed to fetch from ${url} after ${retries} retries: ${error.message}`);
throw error;
}
await new Promise(res => setTimeout(res, 2000));
}
}
}
async fetchOsmZarqa() {
this.logger.log('Fetching Zarqa Geometries from OSM...');
const query = `[out:json];(relation["boundary"="administrative"]["admin_level"~"6|8|10"](31.86,35.94,32.22,36.25);node["place"~"neighbourhood|suburb|town"](31.86,35.94,32.22,36.25););out geom;`;
try {
const data = await this.fetchWithRetry('https://overpass-api.de/api/interpreter', `data=${encodeURIComponent(query)}`, 'post');
return data.elements.map(e => ({
id: e.id,
name_ar: e.tags['name:ar'] || e.tags['name'],
type: e.tags['admin_level'] ? `admin_level_${e.tags['admin_level']}` : `place_${e.tags['place']}`,
geometry: e.geometry ? e.geometry : (e.lat && e.lon ? { type: 'Point', coordinates: [e.lon, e.lat] } : null)
}));
}
catch (e) {
return { error: `OSM Fetch failed: ${e.message}` };
}
}
async fetchOvertureZarqa() {
try {
const tableCheck = await this.placeRepo.query("SELECT count(*) FROM information_schema.tables WHERE table_name = 'overture_segment'");
if (parseInt(tableCheck[0].count) === 0)
return { error: 'table overture_segment does not exist in this database' };
const columns = await this.placeRepo.query("SELECT column_name FROM information_schema.columns WHERE table_name = 'overture_segment'");
const colList = columns.map(c => c.column_name);
const roadClassCol = colList.includes('road_class') ? 'road_class' : (colList.includes('class') ? 'class' : 'NULL');
return await this.placeRepo.query(`
SELECT DISTINCT COALESCE(names->>'primary', names->>'common') as name_ar, ${roadClassCol} as road_class,
ST_AsGeoJSON(ST_Centroid(location)) as centroid
FROM overture_segment
WHERE (names->>'primary' IS NOT NULL OR names->>'common' IS NOT NULL)
AND ST_Within(location, ST_MakeEnvelope(35.94, 31.86, 36.25, 32.22, 4326))
LIMIT 10
`);
}
catch (e) {
return { error: `Overture query failed: ${e.message}` };
}
}
async fetchWikidataZarqa() {
const sparql = `SELECT ?item ?itemLabel WHERE { ?item wdt:P131 wd:Q231710. SERVICE wikibase:label { bd:serviceParam wikibase:language "ar,en". } } LIMIT 20`;
try {
const data = await this.fetchWithRetry('https://query.wikidata.org/sparql', sparql, 'get');
return data.results.bindings.map(b => ({ id: b.item.value.split('/').pop(), name_ar: b.itemLabel.value }));
}
catch (e) {
return { error: `Wikidata Fetch failed: ${e.message}` };
}
}
async checkStaticFiles() {
const paths = ['/data/infrastructure/osm-data/', './data/', './infrastructure/osm-data/'];
const files = ['jor_adm2_geoboundaries.geojson', 'gadm41_JOR_2.json'];
const results = {};
for (const file of files) {
let found = false;
for (const p of paths) {
if (fs.existsSync(path.join(p, file))) {
results[file] = `Found at ${p}`;
found = true;
break;
}
}
if (!found)
results[file] = 'Not Found locally - Check if overture_ingest.sh was run for division_area';
}
return results;
}
async generateZarqaReport() {
const [osm, overture, wikidata, staticFiles] = await Promise.all([
this.fetchOsmZarqa(),
this.fetchOvertureZarqa(),
this.fetchWikidataZarqa(),
this.checkStaticFiles()
]);
return {
area: 'Zarqa Governorate, Jordan',
report_timestamp: new Date().toISOString(),
sources: {
osm,
overture,
wikidata,
static_files: staticFiles,
hdx: "Requires GeoBoundaries GeoJSON for Level 2 (Districts)",
gadm: "Requires GADM v4.1 for Level 1-2"
},
comparison_summary: "Multi-source research enabled with geometries. OSM provides precise neighborhood centroids and boundaries where available. Use the provided centroids to visualize Zarqa subunits."
};
}
};
exports.JordanResearchService = JordanResearchService;
exports.JordanResearchService = JordanResearchService = JordanResearchService_1 = __decorate([
(0, common_1.Injectable)(),
__param(0, (0, typeorm_1.InjectRepository)(place_jordan_entity_1.PlaceJordan)),
__metadata("design:paramtypes", [typeorm_2.Repository])
], JordanResearchService);
//# sourceMappingURL=jordan-research.service.js.map
@@ -0,0 +1 @@
{"version":3,"file":"jordan-research.service.js","sourceRoot":"","sources":["../../src/geocoding/jordan-research.service.ts"],"names":[],"mappings":"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"}