Skip to content

Commit fae3ead

Browse files
committed
add earth sphere utilities
1 parent 7120923 commit fae3ead

File tree

4 files changed

+118
-1
lines changed

4 files changed

+118
-1
lines changed

__init__.py

+11
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
TERRAIN_RECLASS = True
4949
BASEMAPS = True
5050
DROP = True
51+
EARTH_SPHERE = True
5152

5253
import bpy, os
5354

@@ -95,6 +96,8 @@
9596
from .operators import view3d_mapviewer
9697
if DROP:
9798
from .operators import object_drop
99+
if EARTH_SPHERE:
100+
from .operators import mesh_earth_sphere
98101

99102

100103
import bpy.utils.previews as iconsLib
@@ -144,6 +147,10 @@ def draw(self, context):
144147
if DELAUNAY:
145148
self.layout.operator("tesselation.delaunay", icon_value=icons_dict["delaunay"].icon_id, text='Delaunay')
146149
self.layout.operator("tesselation.voronoi", icon_value=icons_dict["voronoi"].icon_id, text='Voronoi')
150+
if EARTH_SPHERE:
151+
self.layout.operator("earth.sphere", icon="WORLD", text='lonlat to sphere')
152+
#self.layout.operator("earth.curvature", icon="SPHERECURVE", text='Earth curvature correction')
153+
self.layout.operator("earth.curvature", icon_value=icons_dict["curve"].icon_id, text='Earth curvature correction')
147154

148155
class VIEW3D_MT_menu_gis_object(bpy.types.Menu):
149156
bl_label = "Object"
@@ -238,6 +245,8 @@ def register():
238245
nodes_terrain_analysis_builder.register()
239246
if TERRAIN_RECLASS:
240247
nodes_terrain_analysis_reclassify.register()
248+
if EARTH_SPHERE:
249+
mesh_earth_sphere.register()
241250

242251
#menus
243252
bpy.types.VIEW3D_MT_editor_menus.append(add_gis_menu)
@@ -306,6 +315,8 @@ def unregister():
306315
nodes_terrain_analysis_builder.unregister()
307316
if TERRAIN_RECLASS:
308317
nodes_terrain_analysis_reclassify.unregister()
318+
if EARTH_SPHERE:
319+
mesh_earth_sphere.unregister()
309320

310321
if __name__ == "__main__":
311322
register()

icons/curve.png

725 Bytes
Loading

operators/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__all__ = ["add_camera_exif", "add_camera_georef", "io_export_shp", "io_get_srtm", "io_import_georaster", "io_import_osm", "io_import_shp", "io_import_asc", "mesh_delaunay_voronoi", "nodes_terrain_analysis_builder", "nodes_terrain_analysis_reclassify", "view3d_mapviewer", "object_drop"]
1+
__all__ = ["add_camera_exif", "add_camera_georef", "io_export_shp", "io_get_srtm", "io_import_georaster", "io_import_osm", "io_import_shp", "io_import_asc", "mesh_delaunay_voronoi", "nodes_terrain_analysis_builder", "nodes_terrain_analysis_reclassify", "view3d_mapviewer", "object_drop", "mesh_earth_sphere"]

operators/mesh_earth_sphere.py

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import bpy
2+
from bpy.types import Operator
3+
from bpy.props import IntProperty
4+
5+
from math import cos, sin, radians, sqrt
6+
from mathutils import Vector
7+
8+
import logging
9+
log = logging.getLogger(__name__)
10+
11+
12+
def lonlat2xyz(R, lon, lat):
13+
lon, lat = radians(lon), radians(lat)
14+
x = R * cos(lat) * cos(lon)
15+
y = R * cos(lat) * sin(lon)
16+
z = R *sin(lat)
17+
return (x, y, z)
18+
19+
20+
class OBJECT_OT_earth_sphere(Operator):
21+
bl_idname = "earth.sphere"
22+
bl_label = "lonlat to sphere"
23+
bl_description = "Transform longitude/latitude data to a sphere like earth globe"
24+
bl_options = {"REGISTER", "UNDO"}
25+
26+
radius: IntProperty(name = "Radius", default=100, description="Sphere radius", min=1)
27+
28+
def execute(self, context):
29+
scn = bpy.context.scene
30+
obj = bpy.context.view_layer.objects.active
31+
32+
if not obj:
33+
self.report({'INFO'}, "No active object")
34+
return {'CANCELLED'}
35+
36+
if obj.type != 'MESH':
37+
self.report({'INFO'}, "Selection isn't a mesh")
38+
return {'CANCELLED'}
39+
40+
w, h, thick = obj.dimensions
41+
if w > 360:
42+
self.report({'INFO'}, "Longitude exceed 360°")
43+
return {'CANCELLED'}
44+
if h > 180:
45+
self.report({'INFO'}, "Latitude exceed 360°")
46+
return {'CANCELLED'}
47+
48+
mesh = obj.data
49+
for vertex in mesh.vertices:
50+
lon, lat = vertex.co.x, vertex.co.y
51+
vertex.co = lonlat2xyz(self.radius, lon, lat)
52+
53+
return {'FINISHED'}
54+
55+
EARTH_RADIUS = 6378137 #meters
56+
def getZDelta(d):
57+
'''delta value for adjusting z across earth curvature
58+
http://webhelp.infovista.com/Planet/62/Subsystems/Raster/Content/help/analysis/viewshedanalysis.html'''
59+
return sqrt(EARTH_RADIUS**2 + d**2) - EARTH_RADIUS
60+
61+
62+
class OBJECT_OT_earth_curvature(Operator):
63+
bl_idname = "earth.curvature"
64+
bl_label = "Earth curvature correction"
65+
bl_description = "Apply earth curvature correction for viewsheed analysis"
66+
bl_options = {"REGISTER", "UNDO"}
67+
68+
def execute(self, context):
69+
scn = bpy.context.scene
70+
obj = bpy.context.view_layer.objects.active
71+
72+
if not obj:
73+
self.report({'INFO'}, "No active object")
74+
return {'CANCELLED'}
75+
76+
if obj.type != 'MESH':
77+
self.report({'INFO'}, "Selection isn't a mesh")
78+
return {'CANCELLED'}
79+
80+
mesh = obj.data
81+
viewpt = scn.cursor.location
82+
83+
for vertex in mesh.vertices:
84+
d = (viewpt.xy - vertex.co.xy).length
85+
vertex.co.z = vertex.co.z - getZDelta(d)
86+
87+
return {'FINISHED'}
88+
89+
90+
classes = [
91+
OBJECT_OT_earth_sphere,
92+
OBJECT_OT_earth_curvature
93+
]
94+
95+
def register():
96+
for cls in classes:
97+
try:
98+
bpy.utils.register_class(cls)
99+
except ValueError as e:
100+
log.warning('{} is already registered, now unregister and retry... '.format(cls))
101+
bpy.utils.unregister_class(cls)
102+
bpy.utils.register_class(cls)
103+
104+
def unregister():
105+
for cls in classes:
106+
bpy.utils.unregister_class(cls)

0 commit comments

Comments
 (0)