Python Matplotlib basemap nightshade是黑暗中的一个点

Python Matplotlib basemap nightshade是黑暗中的一个点,python,matplotlib,Python,Matplotlib,我正在地图上绘制lat、lon点,我想知道这些点是在黑暗中还是在光明中。我有运行的代码,可以使用绘制地图和着色,如果我想知道点是在黑暗中还是在光明中,我不知道该怎么做 我突然想到,nightshade返回的matplotlib.contour.QuadContourSet对象中的区域应该可以判断点是灰色还是白色,但我无法确定如何进行。任何人都有想法,下面是一个有效的例子 matplotlib.contour.QuadContourSet有一个.collections,它返回一个包含3个mcoll

我正在地图上绘制lat、lon点,我想知道这些点是在黑暗中还是在光明中。我有运行的代码,可以使用绘制地图和着色,如果我想知道点是在黑暗中还是在光明中,我不知道该怎么做

我突然想到,nightshade返回的
matplotlib.contour.QuadContourSet
对象中的区域应该可以判断点是灰色还是白色,但我无法确定如何进行。任何人都有想法,下面是一个有效的例子

matplotlib.contour.QuadContourSet
有一个
.collections
,它返回一个包含3个
mcoll.PathCollection
对象的列表,因此问题可能是如何确定点是否在
PathCollection

from mpl_toolkits.basemap import Basemap, shiftgrid, cm
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_axes([0.1,0.1,0.8,0.8])
m = Basemap(llcrnrlon=-145.5,llcrnrlat=1.,urcrnrlon=-2.566,urcrnrlat=46.352,\
            rsphere=(6378137.00,6356752.3142),\
            resolution='l',area_thresh=1000.,projection='lcc',\
            lat_1=50.,lon_0=-107.,ax=ax)
m.drawcoastlines()
m.drawcountries()
m.drawstates()
parallels = np.arange(0.,80,20.)
m.drawparallels(parallels,labels=[1,0,0,1])
meridians = np.arange(10.,360.,30.)
m.drawmeridians(meridians,labels=[1,0,0,1])
from datetime import datetime
date = datetime(2014, 4, 1, 12)
CS=m.nightshade(date)

lats = [40, 40]
lons = [-120, -85]
x,y = m(lons, lats)

m.scatter(x, y, color='r', s=50)
ax.set_title(date.isoformat())

plt.show()

很抱歉,你花了三年时间才得到答案。。。试试这个

它需要
shapely
库,但假设安装它没什么大不了的,我认为这应该可以做到

非常感谢这个网站的灵感

编辑:

据我所知,Cartopy没有我们需要的内置功能

在V0.17中,我们只需要在第107行的cartopy.feature.nightshade中添加一点更改,在定义
geom
之后,编写
self.geom=geom
,然后我们就可以像使用basemap一样使用nightshade类中的几何体

然而,由于Cartopy似乎仍在快速变化,我会建议其他人不要去改变Cartopy的代码,我只在这里复制它(但该文件的所有信用,除了一个geom行,归原始作者所有)

我还改变了投影,因为LambertConformal()让我感到悲伤,它与这个问题无关

`

#---启动nightshade.py---
来自未来导入(绝对导入、分割、打印功能)
导入日期时间
将numpy作为np导入
将shapely.geometry作为sgeom导入
从cartopy.feature导入ShapelyFeature
从cartopy进口crs作为CCR
茄类植物(形状特征):
定义初始值(self,date=None,delta=0.1,折射率=-0.83,
color=“k”,alpha=0.5,**kwargs):
"""
遮蔽地球的黑暗面,考虑折射。
参数
----------
日期:日期时间
用于计算太阳位置的UTC日期时间对象。
默认值:datetime.datetime.utcnow()
三角洲:浮动
步长(以度为单位),用于确定图像的分辨率
夜间多边形特征(`npts=180/三角形`)。
折射:浮动
由折射引起的以度为单位的调整,
太阳圆盘的厚度、高度等。。。
注
----
绘制要素时,可以使用Matplotlib关键字参数。
这允许标准Matplotlib控制以下方面:
“颜色”、“字母”等。
"""
如果日期为无:
date=datetime.datetime.utcnow()
#确保日期是UTC,或者相对于时区而言是朴素的
如果date.utcoffset():
raise VALUERROR('日期时间实例必须是UTC,而不是{0}'。格式(
date.tzname()))
#返回格林威治小时角,
#需要经度(相反方向)
纬度、经度=太阳位置(日期)
极长=极长
如果纬度>0:
极纬度=-90+纬度
中心长度=180
其他:
磁极高度=90+高度
中环长度=0
旋转磁极=ccrs。旋转磁极(磁极纬度=磁极纬度,
极点经度=极点经度,
中心经度=中心经度)
净现值=整数(180/增量)
x=np.空(npts*2)
y=np.空(npts*2)
#求解日出/日落方程:
# https://en.wikipedia.org/wiki/Sunrise_equation#Generalized_equation
#注:在维基百科上的广义方程中,
#delta==0。在旋转的极点坐标系中。
#因此,最大/最小纬度为+/-(90+折射)
#向上填充纬度,然后向下填充纬度
y[:npts]=np.linspace(-(90+折射),90+折射,npts)
y[npts:]=y[:npts][::-1]
#求解omega0的广义方程,它是
#从太阳正午到日出/日落的角度
omega0=np.rad2deg(np.arccos(np.sin)(np.deg2rad(折射))/
np.cos(np.deg2rad(y)))
#从午夜偏移量填充经度值。
#这需要是一个闭合的循环来填充多边形。
#负经度
x[:npts]=-(180-omega0[:npts])
#正经度
x[npts:=180-ω0[npts:]
kwargs.setdefault('facecolor',color)
kwargs.setdefault('alpha',alpha)
geom=sgeom.Polygon(np.column_stack((x,y)))
self.geom=geom
返回超级(夜色,自我)。\uu初始化__(
[geom],旋转电杆,**kwargs)
定义朱利安日(日期):
"""
从输入日期时间计算儒略日。
参数
----------
日期
UTC日期时间对象。
注
----
算法实现了第3章(算法14)中的以下等式:
David Vallado的《天体动力学和应用基础》,(2007年)
朱利安日纪元是:公元前4713年1月1日中午(前朱利安)
公元前4714年11月24日中午(前格里高利)
"""
年份=日期。年份
月=date.month
day=日期.day
小时=日期。小时
分钟=日期。分钟
秒=日期。秒
#1月/2月分别对应于13/14月
#使常数正确计算
如果月份<3:
月份+=12
年份-=1
B=2-整数(100年)+整数(整数(100年)/4)

    from datetime import datetime
    import numpy as np
    import matplotlib.pyplot as plt
    from mpl_toolkits.basemap import Basemap, shiftgrid, cm
    from shapely.geometry import Polygon, Point

    # set up basemap
    fig = plt.figure()
    ax = fig.add_axes([0.1, 0.1, 0.8, 0.8])
    m = Basemap(llcrnrlon=-145.5, llcrnrlat=1., urcrnrlon=-2.566, urcrnrlat=46.352,
                rsphere=(6378137.00, 6356752.3142),
                resolution='l', area_thresh=1000., projection='lcc',
                lat_1=50., lon_0=-107., ax=ax)
    m.drawcoastlines()
    m.drawcountries()
    m.drawstates()
    parallels = np.arange(0., 80, 20.)
    m.drawparallels(parallels, labels=[1, 0, 0, 1])
    meridians = np.arange(10., 360., 30.)
    m.drawmeridians(meridians, labels=[1, 0, 0, 1])
    date = datetime(2014, 4, 1, 12)
    ax.set_title(date.isoformat())

    # get the contour set of the nightshade
    cs = m.nightshade(date)    

    # get each polygon from the contour collection
    polys = []
    for i in range(len(cs.collections)):
        p = cs.collections[i]
        if p.get_paths() != []:
            p = p.get_paths()[0]
            v = p.vertices
            x = v[:, 0]
            y = v[:, 1]
            poly = Polygon([(i[0], i[1]) for i in zip(x, y)])
            polys.append(poly)

    # define your points
    lats = [40, 40]
    lons = [-120, -85]
    x, y = m(lons, lats)
    points = [Point(xi, yi) for xi, yi in zip(x, y)]

    # plot your points, red for dark green for light
    for point in points:
        in_shade = any([poly.contains(point) for poly in polys])
        if in_shade:
            m.scatter([point.x], [point.y], color='r', s=50)
        else:
            m.scatter([point.x], [point.y], color='g', s=50)

    plt.show()

# --- START nightshade.py ---
from __future__ import (absolute_import, division, print_function)

import datetime

import numpy as np
import shapely.geometry as sgeom

from cartopy.feature import ShapelyFeature
from cartopy import crs as ccrs


class Nightshade(ShapelyFeature):
    def __init__(self, date=None, delta=0.1, refraction=-0.83,
                 color="k", alpha=0.5, **kwargs):
        """
        Shade the darkside of the Earth, accounting for refraction.

        Parameters
        ----------
        date : datetime
            A UTC datetime object used to calculate the position of the sun.
            Default: datetime.datetime.utcnow()
        delta : float
            Stepsize in degrees to determine the resolution of the
            night polygon feature (``npts = 180 / delta``).
        refraction : float
            The adjustment in degrees due to refraction,
            thickness of the solar disc, elevation etc...

        Note
        ----
            Matplotlib keyword arguments can be used when drawing the feature.
            This allows standard Matplotlib control over aspects such as
            'color', 'alpha', etc.

        """
        if date is None:
            date = datetime.datetime.utcnow()

        # make sure date is UTC, or naive with respect to time zones
        if date.utcoffset():
            raise ValueError('datetime instance must be UTC, not {0}'.format(
                             date.tzname()))

        # Returns the Greenwich hour angle,
        # need longitude (opposite direction)
        lat, lon = _solar_position(date)
        pole_lon = lon
        if lat > 0:
            pole_lat = -90 + lat
            central_lon = 180
        else:
            pole_lat = 90 + lat
            central_lon = 0

        rotated_pole = ccrs.RotatedPole(pole_latitude=pole_lat,
                                        pole_longitude=pole_lon,
                                        central_rotated_longitude=central_lon)

        npts = int(180/delta)
        x = np.empty(npts*2)
        y = np.empty(npts*2)

        # Solve the equation for sunrise/sunset:
        # https://en.wikipedia.org/wiki/Sunrise_equation#Generalized_equation
        # NOTE: In the generalized equation on Wikipedia,
        #       delta == 0. in the rotated pole coordinate system.
        #       Therefore, the max/min latitude is +/- (90+refraction)

        # Fill latitudes up and then down
        y[:npts] = np.linspace(-(90+refraction), 90+refraction, npts)
        y[npts:] = y[:npts][::-1]

        # Solve the generalized equation for omega0, which is the
        # angle of sunrise/sunset from solar noon
        omega0 = np.rad2deg(np.arccos(np.sin(np.deg2rad(refraction)) /
                                      np.cos(np.deg2rad(y))))

        # Fill the longitude values from the offset for midnight.
        # This needs to be a closed loop to fill the polygon.
        # Negative longitudes
        x[:npts] = -(180 - omega0[:npts])
        # Positive longitudes
        x[npts:] = 180 - omega0[npts:]

        kwargs.setdefault('facecolor', color)
        kwargs.setdefault('alpha', alpha)

        geom = sgeom.Polygon(np.column_stack((x, y)))
        self.geom = geom
        return super(Nightshade, self).__init__(
            [geom], rotated_pole, **kwargs)


def _julian_day(date):
    """
    Calculate the Julian day from an input datetime.

    Parameters
    ----------
    date
        A UTC datetime object.

    Note
    ----
    Algorithm implemented following equations from Chapter 3 (Algorithm 14):
    Vallado, David 'Fundamentals of Astrodynamics and Applications', (2007)

    Julian day epoch is: noon on January 1, 4713 BC (proleptic Julian)
                         noon on November 24, 4714 BC (proleptic Gregorian)

    """
    year = date.year
    month = date.month
    day = date.day
    hour = date.hour
    minute = date.minute
    second = date.second

    # January/February correspond to months 13/14 respectively
    # for the constants to work out properly
    if month < 3:
        month += 12
        year -= 1

    B = 2 - int(year/100) + int(int(year/100)/4)
    C = ((second/60 + minute)/60 + hour)/24

    JD = (int(365.25*(year + 4716)) + int(30.6001*(month+1)) +
          day + B - 1524.5 + C)
    return JD


def _solar_position(date):
    """
    Calculate the latitude and longitude point where the sun is
    directly overhead for the given date.

    Parameters
    ----------
    date
        A UTC datetime object.

    Returns
    -------
    (latitude, longitude) in degrees

    Note
    ----
    Algorithm implemented following equations from Chapter 5 (Algorithm 29):
    Vallado, David 'Fundamentals of Astrodynamics and Applications', (2007)

    """
    # NOTE: Constants are in degrees in the textbook,
    #       so we need to convert the values from deg2rad when taking sin/cos

    # Centuries from J2000
    T_UT1 = (_julian_day(date) - 2451545.0)/36525

    # solar longitude (deg)
    lambda_M_sun = (280.460 + 36000.771*T_UT1) % 360

    # solar anomaly (deg)
    M_sun = (357.5277233 + 35999.05034*T_UT1) % 360

    # ecliptic longitude
    lambda_ecliptic = (lambda_M_sun + 1.914666471*np.sin(np.deg2rad(M_sun)) +
                       0.019994643*np.sin(np.deg2rad(2*M_sun)))

    # obliquity of the ecliptic (epsilon in Vallado's notation)
    epsilon = 23.439291 - 0.0130042*T_UT1

    # declination of the sun
    delta_sun = np.rad2deg(np.arcsin(np.sin(np.deg2rad(epsilon)) *
                                     np.sin(np.deg2rad(lambda_ecliptic))))

    # Greenwich mean sidereal time (seconds)
    theta_GMST = (67310.54841 +
                  (876600*3600 + 8640184.812866)*T_UT1 +
                  0.093104*T_UT1**2 -
                  6.2e-6*T_UT1**3)
    # Convert to degrees
    theta_GMST = (theta_GMST % 86400)/240

    # Right ascension calculations
    numerator = (np.cos(np.deg2rad(epsilon)) *
                 np.sin(np.deg2rad(lambda_ecliptic)) /
                 np.cos(np.deg2rad(delta_sun)))
    denominator = (np.cos(np.deg2rad(lambda_ecliptic)) /
                   np.cos(np.deg2rad(delta_sun)))

    alpha_sun = np.rad2deg(np.arctan2(numerator, denominator))

    # longitude is opposite of Greenwich Hour Angle (GHA)
    # GHA == theta_GMST - alpha_sun
    lon = -(theta_GMST-alpha_sun)
    if lon < -180:
        lon += 360

    return (delta_sun, lon)

# --- END COPIED CODE FROM nightshade.py ---

"""our code, formatted so you could use it as a separate module
if you include an "import nightshade" line"""
import datetime
import numpy as np
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
from shapely.geometry import Point

import cartopy.crs as ccrs
import cartopy.feature as cf

# pick a date
date = datetime.datetime(2014, 4, 1, 12)

# set up map
proj = ccrs.PlateCarree()
ax = plt.axes(projection=proj)
ax.set_extent([-145, -60, 0, 90], crs=proj)
ax.stock_img()
ax.gridlines()
ax.coastlines()
ax.set_title(date.isoformat())
# I would add states and other features here but Cartopy's links to
# naturalearth data seem to be broken for me... That's another issue though!
ns = Nightshade(date, alpha=0.6)
ax.add_feature(ns)
# This is only one polygon to deal with, nicer than basemap's version!
ns_poly = ns.geom  # this is the polygon that is the lit-up area

# define your points
lats = [40, 40]
lons = [-120, -85]

# plot your points, red for dark green for light
for lon, lat in zip(lons, lats):
    in_sun = ns_poly.contains(Point(lon, lat))
    if in_sun:
        ax.scatter([lon], [lat], color='g', s=50, transform=proj)
    else:
        ax.scatter([lon], [lat], color='r', s=50, transform=proj)

plt.show()