// internal/geospatial/geometry.gopackagegeospatialimport("encoding/json""fmt""math")// Point represents a 2D pointtypePointstruct{X,Yfloat64}// NewPoint creates a new pointfuncNewPoint(x,yfloat64)*Point{return&Point{X:x,Y:y}}// Distance calculates the distance to another pointfunc(p*Point)Distance(other*Point)float64{dx:=p.X-other.Xdy:=p.Y-other.Yreturnmath.Sqrt(dx*dx+dy*dy)}// MarshalJSON implements json.Marshalerfunc(p*Point)MarshalJSON()([]byte,error){returnjson.Marshal([2]float64{p.X,p.Y})}// UnmarshalJSON implements json.Unmarshalerfunc(p*Point)UnmarshalJSON(data[]byte)error{varcoords[2]float64iferr:=json.Unmarshal(data,&coords);err!=nil{returnerr}p.X=coords[0]p.Y=coords[1]returnnil}// LineString represents a line stringtypeLineStringstruct{Points[]Point}// NewLineString creates a new line stringfuncNewLineString(points[]Point)*LineString{return&LineString{Points:points}}// Length calculates the length of the line stringfunc(ls*LineString)Length()float64{iflen(ls.Points)<2{return0}length:=0.0fori:=1;i<len(ls.Points);i++{length+=ls.Points[i-1].Distance(&ls.Points[i])}returnlength}// Polygon represents a polygontypePolygonstruct{ExteriorRing[]PointInteriorRings[][]Point}// NewPolygon creates a new polygonfuncNewPolygon(exteriorRing[]Point,interiorRings[][]Point)*Polygon{return&Polygon{ExteriorRing:exteriorRing,InteriorRings:interiorRings,}}// Area calculates the area of the polygonfunc(p*Polygon)Area()float64{returnp.ringArea(p.ExteriorRing)-p.interiorRingsArea()}// ringArea calculates the area of a ringfunc(p*Polygon)ringArea(ring[]Point)float64{iflen(ring)<3{return0}area:=0.0n:=len(ring)fori:=0;i<n;i++{j:=(i+1)%narea+=ring[i].X*ring[j].Yarea-=ring[j].X*ring[i].Y}returnmath.Abs(area)/2.0}// interiorRingsArea calculates the area of interior ringsfunc(p*Polygon)interiorRingsArea()float64{area:=0.0for_,ring:=rangep.InteriorRings{area+=p.ringArea(ring)}returnarea}// Contains checks if the polygon contains a pointfunc(p*Polygon)Contains(point*Point)bool{if!p.ringContains(point,p.ExteriorRing,true){returnfalse}// Check if point is in any interior ringfor_,ring:=rangep.InteriorRings{ifp.ringContains(point,ring,false){returnfalse}}returntrue}// ringContains checks if a ring contains a pointfunc(p*Polygon)ringContains(point*Point,ring[]Point,exteriorbool)bool{iflen(ring)<3{returnfalse}inside:=falsen:=len(ring)fori,j:=0,n-1;i<n;j,i=i,i+1{if((ring[i].Y>point.Y)!=(ring[j].Y>point.Y))&&(point.X<(ring[j].X-ring[i].X)*(point.Y-ring[i].Y)/(ring[j].Y-ring[i].Y)+ring[i].X){inside=!inside}}returninside==exterior}// Geometry represents a generic geometrytypeGeometryinterface{Type()stringBounds()*BoundsArea()float64Length()float64Contains(otherGeometry)boolIntersects(otherGeometry)bool}// Bounds represents a bounding boxtypeBoundsstruct{MinX,MinY,MaxX,MaxYfloat64}// NewBounds creates a new boundsfuncNewBounds(minX,minY,maxX,maxYfloat64)*Bounds{return&Bounds{MinX:minX,MinY:minY,MaxX:maxX,MaxY:maxY,}}// Width returns the width of the boundsfunc(b*Bounds)Width()float64{returnb.MaxX-b.MinX}// Height returns the height of the boundsfunc(b*Bounds)Height()float64{returnb.MaxY-b.MinY}// Contains checks if the bounds contain a pointfunc(b*Bounds)Contains(point*Point)bool{returnpoint.X>=b.MinX&&point.X<=b.MaxX&&point.Y>=b.MinY&&point.Y<=b.MaxY}// Intersects checks if the bounds intersect with another boundsfunc(b*Bounds)Intersects(other*Bounds)bool{return!(b.MaxX<other.MinX||b.MinX>other.MaxX||b.MaxY<other.MinY||b.MinY>other.MaxY)}
// internal/geospatial/crs.gopackagegeospatialimport("fmt""math")// CRS represents a coordinate reference systemtypeCRSstruct{EPSGCodeintNamestringProj4string}// Common CRS definitionsvar(WGS84=CRS{EPSGCode:4326,Name:"WGS 84",Proj4:"+proj=longlat +datum=WGS84 +no_defs",}WebMercator=CRS{EPSGCode:3857,Name:"Web Mercator",Proj4:"+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs",}UTMZone10N=CRS{EPSGCode:32610,Name:"UTM Zone 10N",Proj4:"+proj=utm +zone=10 +datum=WGS84 +units=m +no_defs",})// Transform transforms coordinates between CRSfuncTransform(point*Point,from,toCRS)(*Point,error){iffrom.EPSGCode==to.EPSGCode{returnpoint,nil}// Simple transformation for common casesiffrom.EPSGCode==4326&&to.EPSGCode==3857{returntransformWGS84ToWebMercator(point),nil}iffrom.EPSGCode==3857&&to.EPSGCode==4326{returntransformWebMercatorToWGS84(point),nil}returnnil,fmt.Errorf("unsupported transformation from %d to %d",from.EPSGCode,to.EPSGCode)}// transformWGS84ToWebMercator transforms from WGS84 to Web MercatorfunctransformWGS84ToWebMercator(point*Point)*Point{x:=point.X*20037508.34/180y:=math.Log(math.Tan((90+point.Y)*math.Pi/360))/(math.Pi/180)y=y*20037508.34/180return&Point{X:x,Y:y}}// transformWebMercatorToWGS84 transforms from Web Mercator to WGS84functransformWebMercatorToWGS84(point*Point)*Point{x:=point.X/20037508.34*180y:=point.Y/20037508.34*180y=180/math.Pi*(2*math.Atan(math.Exp(y*math.Pi/180))-math.Pi/2)return&Point{X:x,Y:y}}
// internal/geospatial/rtree.gopackagegeospatialimport("fmt""math""sort")// RTreeNode represents a node in the R-treetypeRTreeNodestruct{Bounds*BoundsChildren[]*RTreeNodeData[]GeometryIsLeafboolMaxEntriesint}// NewRTreeNode creates a new R-tree nodefuncNewRTreeNode(maxEntriesint,isLeafbool)*RTreeNode{return&RTreeNode{Children:make([]*RTreeNode,0),Data:make([]Geometry,0),IsLeaf:isLeaf,MaxEntries:maxEntries,}}// Insert inserts a geometry into the R-treefunc(n*RTreeNode)Insert(geometryGeometry)error{ifn.IsLeaf{n.Data=append(n.Data,geometry)n.updateBounds()iflen(n.Data)>n.MaxEntries{returnn.split()}returnnil}// Find the best child to insert intobestChild:=n.findBestChild(geometry.Bounds())iferr:=bestChild.Insert(geometry);err!=nil{returnerr}n.updateBounds()returnnil}// Search searches for geometries that intersect with the given boundsfunc(n*RTreeNode)Search(bounds*Bounds)[]Geometry{varresults[]Geometryif!n.Bounds.Intersects(bounds){returnresults}ifn.IsLeaf{for_,geometry:=rangen.Data{ifgeometry.Bounds().Intersects(bounds){results=append(results,geometry)}}}else{for_,child:=rangen.Children{results=append(results,child.Search(bounds)...)}}returnresults}// findBestChild finds the best child to insert intofunc(n*RTreeNode)findBestChild(bounds*Bounds)*RTreeNode{iflen(n.Children)==0{returnnil}bestChild:=n.Children[0]bestArea:=n.Children[0].Bounds.union(bounds).Area()for_,child:=rangen.Children[1:]{area:=child.Bounds.union(bounds).Area()ifarea<bestArea{bestArea=areabestChild=child}}returnbestChild}// split splits a leaf node when it exceeds max entriesfunc(n*RTreeNode)split()error{if!n.IsLeaf{returnfmt.Errorf("cannot split non-leaf node")}// Simple split: divide data in halfmid:=len(n.Data)/2leftData:=n.Data[:mid]rightData:=n.Data[mid:]n.Data=leftDatarightChild:=NewRTreeNode(n.MaxEntries,true)rightChild.Data=rightDatarightChild.updateBounds()n.Children=append(n.Children,rightChild)n.IsLeaf=falsen.updateBounds()returnnil}// updateBounds updates the bounds of the nodefunc(n*RTreeNode)updateBounds(){ifn.IsLeaf{iflen(n.Data)==0{n.Bounds=nilreturn}bounds:=n.Data[0].Bounds()for_,geometry:=rangen.Data[1:]{bounds=bounds.union(geometry.Bounds())}n.Bounds=bounds}else{iflen(n.Children)==0{n.Bounds=nilreturn}bounds:=n.Children[0].Boundsfor_,child:=rangen.Children[1:]{bounds=bounds.union(child.Bounds)}n.Bounds=bounds}}// Area returns the area of the boundsfunc(b*Bounds)Area()float64{returnb.Width()*b.Height()}// union returns the union of two boundsfunc(b*Bounds)union(other*Bounds)*Bounds{ifb==nil{returnother}ifother==nil{returnb}return&Bounds{MinX:math.Min(b.MinX,other.MinX),MinY:math.Min(b.MinY,other.MinY),MaxX:math.Max(b.MaxX,other.MaxX),MaxY:math.Max(b.MaxY,other.MaxY),}}// RTree represents an R-tree spatial indextypeRTreestruct{Root*RTreeNodeMaxEntriesint}// NewRTree creates a new R-treefuncNewRTree(maxEntriesint)*RTree{return&RTree{Root:NewRTreeNode(maxEntries,true),MaxEntries:maxEntries,}}// Insert inserts a geometry into the R-treefunc(rt*RTree)Insert(geometryGeometry)error{returnrt.Root.Insert(geometry)}// Search searches for geometries that intersect with the given boundsfunc(rt*RTree)Search(bounds*Bounds)[]Geometry{returnrt.Root.Search(bounds)}// NearestNeighbor finds the nearest neighbor to a pointfunc(rt*RTree)NearestNeighbor(point*Point)Geometry{bounds:=&Bounds{MinX:point.X-0.001,MinY:point.Y-0.001,MaxX:point.X+0.001,MaxY:point.Y+0.001,}candidates:=rt.Search(bounds)iflen(candidates)==0{returnnil}nearest:=candidates[0]minDistance:=point.Distance(nearest.(*Point))for_,candidate:=rangecandidates[1:]{distance:=point.Distance(candidate.(*Point))ifdistance<minDistance{minDistance=distancenearest=candidate}}returnnearest}
// internal/geospatial/spatial_hash.gopackagegeospatialimport("fmt""math")// SpatialHash represents a spatial hash indextypeSpatialHashstruct{CellSizefloat64Cellsmap[string][]Geometry}// NewSpatialHash creates a new spatial hashfuncNewSpatialHash(cellSizefloat64)*SpatialHash{return&SpatialHash{CellSize:cellSize,Cells:make(map[string][]Geometry),}}// Insert inserts a geometry into the spatial hashfunc(sh*SpatialHash)Insert(geometryGeometry){bounds:=geometry.Bounds()minX:=math.Floor(bounds.MinX/sh.CellSize)maxX:=math.Ceil(bounds.MaxX/sh.CellSize)minY:=math.Floor(bounds.MinY/sh.CellSize)maxY:=math.Ceil(bounds.MaxY/sh.CellSize)forx:=minX;x<=maxX;x++{fory:=minY;y<=maxY;y++{key:=fmt.Sprintf("%.0f,%.0f",x,y)sh.Cells[key]=append(sh.Cells[key],geometry)}}}// Search searches for geometries that intersect with the given boundsfunc(sh*SpatialHash)Search(bounds*Bounds)[]Geometry{varresults[]Geometryseen:=make(map[Geometry]bool)minX:=math.Floor(bounds.MinX/sh.CellSize)maxX:=math.Ceil(bounds.MaxX/sh.CellSize)minY:=math.Floor(bounds.MinY/sh.CellSize)maxY:=math.Ceil(bounds.MaxY/sh.CellSize)forx:=minX;x<=maxX;x++{fory:=minY;y<=maxY;y++{key:=fmt.Sprintf("%.0f,%.0f",x,y)ifgeometries,exists:=sh.Cells[key];exists{for_,geometry:=rangegeometries{if!seen[geometry]&&geometry.Bounds().Intersects(bounds){results=append(results,geometry)seen[geometry]=true}}}}}returnresults}// Clear clears the spatial hashfunc(sh*SpatialHash)Clear(){sh.Cells=make(map[string][]Geometry)}
// internal/geospatial/spatial_analysis.gopackagegeospatialimport("math""sort")// SpatialAnalyzer provides spatial analysis functionstypeSpatialAnalyzerstruct{}// NewSpatialAnalyzer creates a new spatial analyzerfuncNewSpatialAnalyzer()*SpatialAnalyzer{return&SpatialAnalyzer{}}// ConvexHull calculates the convex hull of a set of pointsfunc(sa*SpatialAnalyzer)ConvexHull(points[]Point)[]Point{iflen(points)<3{returnpoints}// Sort points by x-coordinate, then by y-coordinatesortedPoints:=make([]Point,len(points))copy(sortedPoints,points)sort.Slice(sortedPoints,func(i,jint)bool{ifsortedPoints[i].X==sortedPoints[j].X{returnsortedPoints[i].Y<sortedPoints[j].Y}returnsortedPoints[i].X<sortedPoints[j].X})// Build lower hulllower:=make([]Point,0)for_,point:=rangesortedPoints{forlen(lower)>=2&&sa.cross(lower[len(lower)-2],lower[len(lower)-1],point)<=0{lower=lower[:len(lower)-1]}lower=append(lower,point)}// Build upper hullupper:=make([]Point,0)fori:=len(sortedPoints)-1;i>=0;i--{forlen(upper)>=2&&sa.cross(upper[len(upper)-2],upper[len(upper)-1],sortedPoints[i])<=0{upper=upper[:len(upper)-1]}upper=append(upper,sortedPoints[i])}// Remove duplicate pointshull:=make([]Point,0)hull=append(hull,lower...)hull=append(hull,upper[1:len(upper)-1]...)returnhull}// cross calculates the cross productfunc(sa*SpatialAnalyzer)cross(o,a,bPoint)float64{return(a.X-o.X)*(b.Y-o.Y)-(a.Y-o.Y)*(b.X-o.X)}// Centroid calculates the centroid of a set of pointsfunc(sa*SpatialAnalyzer)Centroid(points[]Point)Point{iflen(points)==0{returnPoint{}}varsumX,sumYfloat64for_,point:=rangepoints{sumX+=point.XsumY+=point.Y}returnPoint{X:sumX/float64(len(points)),Y:sumY/float64(len(points)),}}// BoundingBox calculates the bounding box of a set of pointsfunc(sa*SpatialAnalyzer)BoundingBox(points[]Point)*Bounds{iflen(points)==0{returnnil}minX,minY:=points[0].X,points[0].YmaxX,maxY:=points[0].X,points[0].Yfor_,point:=rangepoints[1:]{ifpoint.X<minX{minX=point.X}ifpoint.X>maxX{maxX=point.X}ifpoint.Y<minY{minY=point.Y}ifpoint.Y>maxY{maxY=point.Y}}return&Bounds{MinX:minX,MinY:minY,MaxX:maxX,MaxY:maxY,}}// DistanceMatrix calculates the distance matrix between pointsfunc(sa*SpatialAnalyzer)DistanceMatrix(points[]Point)[][]float64{n:=len(points)matrix:=make([][]float64,n)fori:=0;i<n;i++{matrix[i]=make([]float64,n)forj:=0;j<n;j++{ifi==j{matrix[i][j]=0}else{matrix[i][j]=points[i].Distance(&points[j])}}}returnmatrix}// NearestNeighbors finds the k nearest neighbors of a pointfunc(sa*SpatialAnalyzer)NearestNeighbors(point*Point,points[]Point,kint)[]Point{ifk>=len(points){returnpoints}// Calculate distancesdistances:=make([]struct{pointPointdistancefloat64},len(points))fori,p:=rangepoints{distances[i]=struct{pointPointdistancefloat64}{point:p,distance:point.Distance(&p),}}// Sort by distancesort.Slice(distances,func(i,jint)bool{returndistances[i].distance<distances[j].distance})// Return k nearest neighborsresult:=make([]Point,k)fori:=0;i<k;i++{result[i]=distances[i].point}returnresult}
// internal/geospatial/spatial_queries.gopackagegeospatialimport("context""database/sql""fmt")// SpatialQueries provides spatial query functionstypeSpatialQueriesstruct{db*sql.DB}// NewSpatialQueries creates a new spatial queries instancefuncNewSpatialQueries(db*sql.DB)*SpatialQueries{return&SpatialQueries{db:db}}// FindWithinDistance finds geometries within a distance of a pointfunc(sq*SpatialQueries)FindWithinDistance(ctxcontext.Context,tablestring,point*Point,distancefloat64)([]Geometry,error){query:=fmt.Sprintf(` SELECT id, ST_AsText(geom) as wkt FROM %s WHERE ST_DWithin(geom, ST_GeomFromText('POINT(%f %f)', 4326), %f) `,table,point.X,point.Y,distance)rows,err:=sq.db.QueryContext(ctx,query)iferr!=nil{returnnil,err}deferrows.Close()vargeometries[]Geometryforrows.Next(){varidint64varwktstringiferr:=rows.Scan(&id,&wkt);err!=nil{returnnil,err}// Parse WKT and create geometrygeometry,err:=sq.parseWKT(wkt)iferr!=nil{continue}geometries=append(geometries,geometry)}returngeometries,nil}// FindIntersecting finds geometries that intersect with a given geometryfunc(sq*SpatialQueries)FindIntersecting(ctxcontext.Context,tablestring,geometryGeometry)([]Geometry,error){wkt:=sq.geometryToWKT(geometry)query:=fmt.Sprintf(` SELECT id, ST_AsText(geom) as wkt FROM %s WHERE ST_Intersects(geom, ST_GeomFromText('%s', 4326)) `,table,wkt)rows,err:=sq.db.QueryContext(ctx,query)iferr!=nil{returnnil,err}deferrows.Close()vargeometries[]Geometryforrows.Next(){varidint64varwktstringiferr:=rows.Scan(&id,&wkt);err!=nil{returnnil,err}// Parse WKT and create geometrygeometry,err:=sq.parseWKT(wkt)iferr!=nil{continue}geometries=append(geometries,geometry)}returngeometries,nil}// FindContained finds geometries that are contained within a given geometryfunc(sq*SpatialQueries)FindContained(ctxcontext.Context,tablestring,geometryGeometry)([]Geometry,error){wkt:=sq.geometryToWKT(geometry)query:=fmt.Sprintf(` SELECT id, ST_AsText(geom) as wkt FROM %s WHERE ST_Contains(ST_GeomFromText('%s', 4326), geom) `,table,wkt)rows,err:=sq.db.QueryContext(ctx,query)iferr!=nil{returnnil,err}deferrows.Close()vargeometries[]Geometryforrows.Next(){varidint64varwktstringiferr:=rows.Scan(&id,&wkt);err!=nil{returnnil,err}// Parse WKT and create geometrygeometry,err:=sq.parseWKT(wkt)iferr!=nil{continue}geometries=append(geometries,geometry)}returngeometries,nil}// parseWKT parses a WKT string into a geometryfunc(sq*SpatialQueries)parseWKT(wktstring)(Geometry,error){// Simple WKT parser for pointsifwkt[:6]=="POINT("{// Parse point coordinatescoords:=wkt[6:len(wkt)-1]varx,yfloat64if_,err:=fmt.Sscanf(coords,"%f %f",&x,&y);err!=nil{returnnil,err}returnNewPoint(x,y),nil}returnnil,fmt.Errorf("unsupported WKT type: %s",wkt)}// geometryToWKT converts a geometry to WKTfunc(sq*SpatialQueries)geometryToWKT(geometryGeometry)string{switchg:=geometry.(type){case*Point:returnfmt.Sprintf("POINT(%f %f)",g.X,g.Y)default:return""}}
// internal/geospatial/performance.gopackagegeospatialimport("context""database/sql""fmt""time")// SpatialIndexOptimizer provides spatial index optimizationtypeSpatialIndexOptimizerstruct{db*sql.DB}// NewSpatialIndexOptimizer creates a new spatial index optimizerfuncNewSpatialIndexOptimizer(db*sql.DB)*SpatialIndexOptimizer{return&SpatialIndexOptimizer{db:db}}// CreateSpatialIndex creates a spatial index on a tablefunc(sio*SpatialIndexOptimizer)CreateSpatialIndex(ctxcontext.Context,table,column,indexNamestring)error{query:=fmt.Sprintf("CREATE INDEX %s ON %s USING GIST (%s)",indexName,table,column)_,err:=sio.db.ExecContext(ctx,query)returnerr}// AnalyzeTable analyzes a table for query optimizationfunc(sio*SpatialIndexOptimizer)AnalyzeTable(ctxcontext.Context,tablestring)error{query:=fmt.Sprintf("ANALYZE %s",table)_,err:=sio.db.ExecContext(ctx,query)returnerr}// VacuumTable vacuums a table to reclaim spacefunc(sio*SpatialIndexOptimizer)VacuumTable(ctxcontext.Context,tablestring)error{query:=fmt.Sprintf("VACUUM ANALYZE %s",table)_,err:=sio.db.ExecContext(ctx,query)returnerr}// GetTableStats gets table statisticsfunc(sio*SpatialIndexOptimizer)GetTableStats(ctxcontext.Context,tablestring)(*TableStats,error){query:=` SELECT schemaname, tablename, attname, n_distinct, correlation FROM pg_stats WHERE tablename = $1 `rows,err:=sio.db.QueryContext(ctx,query,table)iferr!=nil{returnnil,err}deferrows.Close()stats:=&TableStats{TableName:table,Columns:make(map[string]*ColumnStats),}forrows.Next(){varschemaName,tableName,attNamestringvarnDistinctfloat64varcorrelationfloat64iferr:=rows.Scan(&schemaName,&tableName,&attName,&nDistinct,&correlation);err!=nil{returnnil,err}stats.Columns[attName]=&ColumnStats{Name:attName,NDistinct:nDistinct,Correlation:correlation,}}returnstats,nil}// TableStats represents table statisticstypeTableStatsstruct{TableNamestring`json:"table_name"`Columnsmap[string]*ColumnStats`json:"columns"`}// ColumnStats represents column statisticstypeColumnStatsstruct{Namestring`json:"name"`NDistinctfloat64`json:"n_distinct"`Correlationfloat64`json:"correlation"`}// SpatialQueryProfiler provides spatial query profilingtypeSpatialQueryProfilerstruct{db*sql.DB}// NewSpatialQueryProfiler creates a new spatial query profilerfuncNewSpatialQueryProfiler(db*sql.DB)*SpatialQueryProfiler{return&SpatialQueryProfiler{db:db}}// ProfileQuery profiles a spatial queryfunc(sqp*SpatialQueryProfiler)ProfileQuery(ctxcontext.Context,querystring)(*QueryProfile,error){// Enable query profilingif_,err:=sqp.db.ExecContext(ctx,"SET enable_seqscan = off");err!=nil{returnnil,err}start:=time.Now()rows,err:=sqp.db.QueryContext(ctx,query)iferr!=nil{returnnil,err}deferrows.Close()// Count rowsrowCount:=0forrows.Next(){rowCount++}duration:=time.Since(start)// Get query planplan,err:=sqp.getQueryPlan(ctx,query)iferr!=nil{returnnil,err}return&QueryProfile{Query:query,Duration:duration,RowCount:rowCount,Plan:plan,},nil}// getQueryPlan gets the query execution planfunc(sqp*SpatialQueryProfiler)getQueryPlan(ctxcontext.Context,querystring)(string,error){explainQuery:=fmt.Sprintf("EXPLAIN ANALYZE %s",query)row:=sqp.db.QueryRowContext(ctx,explainQuery)varplanstringiferr:=row.Scan(&plan);err!=nil{return"",err}returnplan,nil}// QueryProfile represents a query profiletypeQueryProfilestruct{Querystring`json:"query"`Durationtime.Duration`json:"duration"`RowCountint`json:"row_count"`Planstring`json:"plan"`}