Hammer Projection
Nodes on left, UV map on right
A while back, sh*t like 6 years ago, I wrote a number of map projections, including Mercator and among them the Hammer, close relation to Aitoff which I've cleaned up for blender 2.8
UVMap result on default UV sphere
To use, run the script in text editor, or install as addon. Adds a Hammer projection entry to the UV mesh edit mode menu.

from numpy import sin, cos, sinc, arccos
from mathutils import Vector, Matrix
from math import degrees, pi, sqrt
import bmesh
import bpy
bl_info = {
"name": "Hammer Project",
"author": "batFINGER",
"version": (1, 0),
"blender": (2, 80, 0),
"location": "View3D > Mesh > UV UnWrap > Hammer Project",
"description": "UV Hammer Projection",
"warning": "",
"wiki_url": "",
"category": "UV",
}
class Spherical:
def __init__(self, vert, north=(0, 0, 1), long0=(0, -1)):
self.vert = vert
self.north, self.long0 = Vector(north), Vector(long0)
v = vert.co
R = v.length
lat = pi / 2 - self.north.angle(v)
is_pole = v.xy.length < 0.0001
long = 0 if is_pole else v.xy.angle_signed(long0)
self.R = R
self.lat = lat
self.long = long
def __repr__(self):
return "%.3f, %.3f, %.3f" % (self.R,
degrees(self.lat), degrees(self.long))
class HammerUV:
def x2(self, lat, long):
a = 2 * sqrt(2) * cos(lat) * sin(long / 2)
b = sqrt(1 + cos(lat) * cos(long / 2))
return a / b
def y2(self, lat, long):
a = sqrt(2) * sin(lat)
b = sqrt(1 + cos(lat) * cos(long / 2))
return a / b
# Aitoff equations (no joy)
def x(self, lat, long):
a = arccos(cos(lat) * cos(long / 2))
b = 2 * cos(lat) * sin(long / 2)
return b / sinc(a)
def y(self, lat, long):
a = arccos(cos(lat) * cos(long / 2))
b = sin(lat)
return b / sinc(a)
def uv(self, face, uv_layer):
''' Map a UV face '''
# see if face is east / west
c = face.calc_center_median()
for l in face.loops:
luv = l[uv_layer]
# apply the location of the vertex as a UV
p = self.pts[l.vert]
lat, long = p.lat, p.long # % longfix
# quick hack for now.
if l.vert.co.y > 0:
if c.x < 0 and long > 0:
long = -long
elif c.x > 0 and long < 0:
long = -long
u, v = self.x2(lat, long), self.y2(lat, long)
uv = self.translate + self.scale @ Vector((u, v))
luv.uv = uv
def calc_uv(self):
bm = self.bm
uv_layer = bm.loops.layers.uv.verify()
bm.select_mode = {'FACE', 'VERT'}
bm.select_flush_mode()
bm.select_flush(True)
# adjust UVs
for f in bm.faces:
self.uv(f, uv_layer)
def __init__(self, me, bm):
def pt(v):
s = Spherical(v)
return s
# add a new uv map
uv = me.uv_layers.new(name="Hammer")
me.uv_layers.active = uv
self.bm = bm
# spherical coords for verts
self.pts = {v: pt(v) for v in self.bm.verts}
# radius average of calc'd s.R
self.R = sum(s.R for s in self.pts.values()) / len(self.pts)
# scale UV to [0, 1] make scale matrix
scale_x = 0.5 / self.x2(0, pi)
scale_y = 0.5 / self.y2(pi / 2, 0)
self.scale = Matrix([[scale_x, 0], [0, scale_y]])
# and transform vector
self.translate = Vector((0.5, 0.5))
class UV_OT_HammerProject(bpy.types.Operator):
"""Create a Hammer Projection UV Map"""
bl_idname = "uv.hammer_project"
bl_label = "Hammer Project"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
return (context.mode == 'EDIT_MESH')
def execute(self, context):
obj = context.edit_object
me = obj.data
bm = bmesh.from_edit_mesh(me)
merc = HammerUV(me, bm)
merc.calc_uv()
bmesh.update_edit_mesh(me)
return {'FINISHED'}
def unwrapmenu(self, context):
''' menu item '''
self.layout.operator("uv.hammer_project")
def register():
# add to edit mesh > UV menu
bpy.types.VIEW3D_MT_uv_map.prepend(unwrapmenu)
bpy.utils.register_class(UV_OT_HammerProject)
def unregister():
bpy.types.VIEW3D_MT_uv_map.remove(unwrapmenu)
bpy.utils.unregister_class(UV_OT_HammerProject)
if __name__ == "__main__":
register()
Since it is a UV mapping can also use it on other spherical meshes like the icosphere

I added the code for the Aitoff projection, but have not quite managed to get that one "steady", not sure about the sinc(...) method required for that one.
Using Nodes
In https://blender.stackexchange.com/a/159492/15543 dabbling in nodes I came up with a node setup to generate latitude longitude using object texture coordinates.
Basically the equivalent node setup to the script above, generating the UV and feeding into texture node.


Tissot index hammer projection image from wiki by Justin Kunimune CC BY-SA 4.0