<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>/home/adeel</title><link href="https://adl1995.github.io/" rel="alternate"/><link href="https://adl1995.github.io/feeds/all.atom.xml" rel="self"/><id>https://adl1995.github.io/</id><updated>2024-01-08T08:42:00+01:00</updated><subtitle>Notes on software, astronomy, machine learning, and infrastructure.</subtitle><entry><title>The Two-Factor Authentication System at CERN</title><link href="https://adl1995.github.io/the-two-factor-authentication-system-at-cern.html" rel="alternate"/><published>2024-01-08T08:42:00+01:00</published><updated>2024-01-08T08:42:00+01:00</updated><author><name>Adeel Ahmad</name></author><id>tag:adl1995.github.io,2024-01-08:/the-two-factor-authentication-system-at-cern.html</id><summary type="html">&lt;p&gt;This blog post is a summary of my project at the European Organization for Nuclear Research (&lt;span class="caps"&gt;CERN&lt;/span&gt;) where I worked in their Identity and Access Management (&lt;span class="caps"&gt;IAM&lt;/span&gt;)&amp;nbsp;team.&lt;/p&gt;
&lt;p&gt;My project was to introduce Two-Factor Authentication (&lt;span class="caps"&gt;2FA&lt;/span&gt;) in the Keycloak system. &lt;span class="caps"&gt;CERN&lt;/span&gt; started migration to the Keycloak Identity Provider (IdP) as part of the &lt;span class="caps"&gt;MALT&lt;/span&gt; project which aimed to move away from Microsoft products. The project was &lt;a href="https://home.cern/news/news/computing/three-year-malt-project-comes-close"&gt;cancelled&lt;/a&gt; in late 2021, however, some services were allowed to continue development. The &lt;span class="caps"&gt;IAM&lt;/span&gt; team was allowed to continue development for the &lt;span class="caps"&gt;CERN&lt;/span&gt; &lt;span class="caps"&gt;SSO&lt;/span&gt;, which was based on&amp;nbsp;Keycloak.&lt;/p&gt;
&lt;p&gt;We developed a custom &lt;span class="caps"&gt;2FA&lt;/span&gt; implementation with Keycloak which allowed users to optionally login from the &lt;span class="caps"&gt;SSO&lt;/span&gt; login page. Internally, this setup posed a few …&lt;/p&gt;</summary><content type="html">&lt;p&gt;This blog post is a summary of my project at the European Organization for Nuclear Research (&lt;span class="caps"&gt;CERN&lt;/span&gt;) where I worked in their Identity and Access Management (&lt;span class="caps"&gt;IAM&lt;/span&gt;)&amp;nbsp;team.&lt;/p&gt;
&lt;p&gt;My project was to introduce Two-Factor Authentication (&lt;span class="caps"&gt;2FA&lt;/span&gt;) in the Keycloak system. &lt;span class="caps"&gt;CERN&lt;/span&gt; started migration to the Keycloak Identity Provider (IdP) as part of the &lt;span class="caps"&gt;MALT&lt;/span&gt; project which aimed to move away from Microsoft products. The project was &lt;a href="https://home.cern/news/news/computing/three-year-malt-project-comes-close"&gt;cancelled&lt;/a&gt; in late 2021, however, some services were allowed to continue development. The &lt;span class="caps"&gt;IAM&lt;/span&gt; team was allowed to continue development for the &lt;span class="caps"&gt;CERN&lt;/span&gt; &lt;span class="caps"&gt;SSO&lt;/span&gt;, which was based on&amp;nbsp;Keycloak.&lt;/p&gt;
&lt;p&gt;We developed a custom &lt;span class="caps"&gt;2FA&lt;/span&gt; implementation with Keycloak which allowed users to optionally login from the &lt;span class="caps"&gt;SSO&lt;/span&gt; login page. Internally, this setup posed a few drawbacks as it was provided using a separate realm in Keycloak. Firstly, it did not enforce users to use &lt;span class="caps"&gt;2FA&lt;/span&gt;, and only 10% of total &lt;span class="caps"&gt;CERN&lt;/span&gt; accounts were using &lt;span class="caps"&gt;2FA&lt;/span&gt; in this old approach. Secondly, this approach required creating a separate realm in Keycloak, thus duplicating all users, which added an overhead on the&amp;nbsp;software.&lt;/p&gt;
&lt;p&gt;This flow allowed users to login with &lt;span class="caps"&gt;2FA&lt;/span&gt; using a separate button in the &lt;span class="caps"&gt;SSO&lt;/span&gt; login portal, as shown&amp;nbsp;below.&lt;/p&gt;
&lt;p align="center"&gt;
    &lt;img src="/images/2fa/2fa-old-login-page.png" alt="2fa-old-login-page" style="width:400px;"/&gt;
&lt;/p&gt;

&lt;p&gt;In the new approach, &lt;span class="caps"&gt;2FA&lt;/span&gt; is tied to the user&amp;#8217;s account, thus making it mandatory for each login. The &lt;a href="https://home.cern/news/news/computing/computer-security-multifactor-masses"&gt;motivation&lt;/a&gt; behind this approach came from the &lt;span class="caps"&gt;CERN&lt;/span&gt; annual (simulated) phishing campaign, where each year the &lt;span class="caps"&gt;CERN&lt;/span&gt; Computer Security team sends thousands of emails containing phishing links, which redirects users to a web form asking for their password. Although no actual data is collected, this gives us an indication of how many users will get compromised in a real-world scenario. In the 2022 &lt;span class="caps"&gt;CERN&lt;/span&gt; phishing campaign, around 2000 users gave away their passwords. In a real phishing incident this would have meant 2000 compromised accounts, unless they were protected with Two-Factor Authentication (&lt;span class="caps"&gt;2FA&lt;/span&gt;).&lt;/p&gt;
&lt;h3&gt;Always-on &lt;span class="caps"&gt;2FA&lt;/span&gt;&amp;nbsp;Approach&lt;/h3&gt;
&lt;p&gt;In late 2020, the &lt;span class="caps"&gt;CERN&lt;/span&gt; Computer Security team took a mandate to make &lt;span class="caps"&gt;2FA&lt;/span&gt; mandatory and couple it with the user account. This new flow requires users to provide their &lt;span class="caps"&gt;2FA&lt;/span&gt; token for each login, if enabled for their account. In addition to improving security, since the account became protected for all &lt;span class="caps"&gt;SSO&lt;/span&gt; access, this solved two main&amp;nbsp;problems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Users were no longer required to re-login when they wanted to access a &lt;span class="caps"&gt;2FA&lt;/span&gt;-protected&amp;nbsp;application.&lt;/li&gt;
&lt;li&gt;The complex Keycloak setup and login flow could be&amp;nbsp;simplified.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The new &lt;span class="caps"&gt;2FA&lt;/span&gt; flow is tied to the user&amp;#8217;s account rather than with the end application in a single realm. A realm in Keycloak is a space which includes users, applications, roles, and groups. This is made possible by providing &lt;span class="caps"&gt;2FA&lt;/span&gt; from&amp;nbsp;the &lt;code&gt;cern&lt;/code&gt; realm in Keycloak, contrary to the &lt;span class="caps"&gt;2FA&lt;/span&gt; provided&amp;nbsp;in &lt;code&gt;mfa&lt;/code&gt; realm, as done previously. A migration&amp;nbsp;script &lt;code&gt;migrate-users-2fa&lt;/code&gt; is responsible for migrating users from the old to the new &lt;span class="caps"&gt;2FA&lt;/span&gt; flow. A comparison of the &lt;span class="caps"&gt;2FA&lt;/span&gt; systems is shown&amp;nbsp;below.&lt;/p&gt;
&lt;p align="center"&gt;
    &lt;img src="/images/2fa/2fa-new-flow.png" alt="new_flow" style="width:350px;"/&gt;
    &lt;img src="/images/2fa/2fa-old-flow.png" alt="old_flow" style="width:350px;"/&gt;
    &lt;p align="center"&gt;New vs. Old &lt;span class="caps"&gt;2FA&lt;/span&gt;&amp;nbsp;Flow&lt;/p&gt;
&lt;/p&gt;

&lt;h3&gt;Supported &lt;span class="caps"&gt;MFA&lt;/span&gt;&amp;nbsp;Methods&lt;/h3&gt;
&lt;p&gt;Keycloak natively supports Time-based one-time password (&lt;span class="caps"&gt;TOTP&lt;/span&gt;) and WebAuthn protocols. For WebAuthn we distribute Yubikeys throughout the &lt;span class="caps"&gt;CERN&lt;/span&gt; campus. The &lt;span class="caps"&gt;IAM&lt;/span&gt; team has developed a custom web portal where users can register their &lt;span class="caps"&gt;2FA&lt;/span&gt; token. In the future we also plan to add an option to register multiple &lt;span class="caps"&gt;2FA&lt;/span&gt;&amp;nbsp;tokens.&lt;/p&gt;
&lt;p align="center"&gt;
    &lt;img src="/images/2fa/users-portal.jpg" alt="users-portal" style="width:800px;"/&gt;
    &lt;p align="center"&gt;&lt;span class="caps"&gt;OTP&lt;/span&gt; and WebAuthn &lt;span class="caps"&gt;MFA&lt;/span&gt;&lt;/p&gt;
&lt;/p&gt;

&lt;h3&gt;Keycloak&amp;nbsp;Configuration&lt;/h3&gt;
&lt;p&gt;We introduced several customisations in Keycloak, which were provided by implementing the Keycloak Service Provider Interfaces (SPIs). This included adding a custom role in the user token if they logged in with &lt;span class="caps"&gt;2FA&lt;/span&gt;. We also changed the default Keycloak authentication flow to check for a specific role in the user profile and to prompt for a &lt;span class="caps"&gt;2FA&lt;/span&gt; based on its presence. A check was also added for the user password reset flag in our database and to display a form to reset the user&amp;nbsp;password.&lt;/p&gt;
&lt;h4&gt;Custom Extensions&amp;nbsp;(SPIs)&lt;/h4&gt;
&lt;p&gt;Keycloak allows modifying the default authentication flow and add a custom check before the user logs in. In our custom &lt;span class="caps"&gt;2FA&lt;/span&gt; login flow, we check if a user has the &lt;span class="caps"&gt;2FA&lt;/span&gt; migrated role present in their token. We present the &lt;span class="caps"&gt;2FA&lt;/span&gt; login screen only if this role is present. Other SPIs we developed include adding a custom role in the user token and checking if the user password has been&amp;nbsp;compromised.&lt;/p&gt;
&lt;p&gt;In the old login flow, &lt;span class="caps"&gt;2FA&lt;/span&gt; login is provided using a separate button which redirects users to login using a different login screen. The new system incorporates &lt;span class="caps"&gt;2FA&lt;/span&gt; and &lt;span class="caps"&gt;1FA&lt;/span&gt; logins within the same login&amp;nbsp;flow.&lt;/p&gt;
&lt;p&gt;Note that in the figure below there is the addition of&amp;nbsp;a &lt;code&gt;2FA Migrated Role&lt;/code&gt; which indicates whether a user is using the new &lt;span class="caps"&gt;2FA&lt;/span&gt; flow or not. This is a temporary solution which allows both optional and Always-on &lt;span class="caps"&gt;2FA&lt;/span&gt; systems to co-exist while we run the migration&amp;nbsp;campaign.&lt;/p&gt;
&lt;p align="center"&gt;
    &lt;img src="/images/2fa/2fa-login-flow-mfa.png" alt="2fa-login-flow-mfa" style="width:800px;"/&gt;
    &lt;p align="center"&gt;&lt;span class="caps"&gt;2FA&lt;/span&gt; Authentication&amp;nbsp;Flow&lt;/p&gt;
&lt;/p&gt;

&lt;!-- #### Role Check

The role check in Keycloak is done within the login flow. This role is added to the user profile in both the `cern` and `mfa` realm. We make use of the 2FA CLI tool to add this role to the user. --&gt;

&lt;h4&gt;Migration&amp;nbsp;Strategy&lt;/h4&gt;
&lt;p&gt;The &lt;span class="caps"&gt;CERN&lt;/span&gt; Computer Security team started rolling out the new &lt;span class="caps"&gt;2FA&lt;/span&gt; flow in February, 2021. Initially, users from the &lt;span class="caps"&gt;IT&lt;/span&gt; department were migrated to the new flow and were asked for their feedback. Gradually, we asked for volunteers from other departments and currently have around 3,000 users in the new &lt;span class="caps"&gt;2FA&lt;/span&gt; flow, while the total user count is above 10,000. The majority of unmigrated users are externally affiliated university employees at &lt;span class="caps"&gt;CERN&lt;/span&gt; and can be migrated at&amp;nbsp;once.&lt;/p&gt;
&lt;p&gt;The Computer Security team also maintains a list of critical accounts which are obliged to use &lt;span class="caps"&gt;2FA&lt;/span&gt;, and do not have the ability to opt-out (i.e. disable both their &lt;span class="caps"&gt;2FA&lt;/span&gt; tokens). The migration is controlled using an internal group which is open for subscription from any &lt;span class="caps"&gt;CERN&lt;/span&gt; account. A migration script is responsible for reading users from this group and &lt;strong&gt;1)&lt;/strong&gt; adding the required roles in their Keycloak profile and &lt;strong&gt;2)&lt;/strong&gt; copying their &lt;span class="caps"&gt;2FA&lt;/span&gt; tokens from the old to the new realm. The chart below shows a detailed overview of migrated users in&amp;nbsp;Keycloak.&lt;/p&gt;
&lt;p align="center"&gt;
    &lt;img src="/images/2fa/2fa-migrated-timeline.png" alt="2fa-migrated-timeline" style="width:800px;"/&gt;
    &lt;p align="center"&gt;&lt;span class="caps"&gt;2FA&lt;/span&gt; Migration&amp;nbsp;Timeline&lt;/p&gt;
&lt;/p&gt;

&lt;p&gt;The diagram below shows which departments are fully migrated, partially migrated, and pending&amp;nbsp;migration.&lt;/p&gt;
&lt;p align="center"&gt;
    &lt;img src="/images/2fa/2fa-migrated-diagram.png" alt="2fa-migrated-diagram" style="width:800px;"/&gt;
    &lt;p align="center"&gt;&lt;span class="caps"&gt;2FA&lt;/span&gt; Migration&amp;nbsp;Progress&lt;/p&gt;
&lt;/p&gt;

&lt;h4&gt;Command-Line&amp;nbsp;Tool&lt;/h4&gt;
&lt;p&gt;The authentication team has developed a &lt;span class="caps"&gt;CLI&lt;/span&gt; tool in Python to migrate users and generate statistics on the migration. This tool runs as a cron job to periodically synchronize users in the &lt;span class="caps"&gt;2FA&lt;/span&gt; group and adds the migrated role to their Keycloak profile. We currently have the following cron jobs in&amp;nbsp;place:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;migrate-users-2fa&lt;/code&gt;: Moves users to the new &lt;span class="caps"&gt;2FA&lt;/span&gt; flow and copies their &lt;span class="caps"&gt;2FA&lt;/span&gt;&amp;nbsp;credentials.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;remove-users-from-mfa-role&lt;/code&gt;: Removes users from the new &lt;span class="caps"&gt;2FA&lt;/span&gt; flow which are in the bypass&amp;nbsp;list.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;get-users-2fa&lt;/code&gt;: Returns a list of users pending migration to the new &lt;span class="caps"&gt;2FA&lt;/span&gt;&amp;nbsp;flow.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This tool is also installed on a &lt;span class="caps"&gt;VM&lt;/span&gt; which is accessible by the &lt;span class="caps"&gt;CERN&lt;/span&gt; Computer Security team to generate their own&amp;nbsp;statistics.&lt;/p&gt;
&lt;h4&gt;Critical&amp;nbsp;Users&lt;/h4&gt;
&lt;p&gt;The Computer Security team has defined a list of critical users which are obliged to use &lt;span class="caps"&gt;2FA&lt;/span&gt; for each login. These users do not have the ability to disable both their &lt;span class="caps"&gt;2FA&lt;/span&gt; methods. This is made possible by adding these users to a special authorization group. In the &lt;span class="caps"&gt;API&lt;/span&gt;, we check if the user is part of this authorization group and deny their request to disable both their &lt;span class="caps"&gt;2FA&lt;/span&gt;&amp;nbsp;methods.&lt;/p&gt;
&lt;h4&gt;Kerberos&amp;nbsp;Configuration&lt;/h4&gt;
&lt;p&gt;To simplify &lt;span class="caps"&gt;SSO&lt;/span&gt; access we also offer logins with Kerberos. All &lt;span class="caps"&gt;CERN&lt;/span&gt;-managed Windows machines get a valid Kerberos ticket on login and can login without re-entering their username and password. We provide Kerberos logins using a separate Identity Provider in both&amp;nbsp;the &lt;code&gt;cern&lt;/code&gt; and &lt;code&gt;mfa&lt;/code&gt; realm. The reason to create a separate IdP was to avoid automatic logins if the user already had a valid Kerberos ticket. With this flow, users can choose to authenticate with Kerberos by pressing a separate button on the &lt;span class="caps"&gt;SSO&lt;/span&gt; login page, as shown&amp;nbsp;below.&lt;/p&gt;
&lt;p align="center"&gt;
    &lt;img src="/images/2fa/kerberos-login.png" alt="kerberos-login" style="width:400px;"/&gt;
    &lt;p align="center"&gt;&lt;span class="caps"&gt;2FA&lt;/span&gt;&amp;nbsp;Kerberos&lt;/p&gt;
&lt;/p&gt;

&lt;h3&gt;Compromised Password&amp;nbsp;Check&lt;/h3&gt;
&lt;p&gt;We added a check in the login page to search for the user&amp;#8217;s password hash in a large database of compromised passwords. This database was obtained from the &lt;a href="https://haveibeenpwned.com/"&gt;Have I Been Pwned&lt;/a&gt; website, and is hosted internally at &lt;span class="caps"&gt;CERN&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;To achieve this check we developed a custom &lt;span class="caps"&gt;SPI&lt;/span&gt; in Keycloak which overrides the default authentication flow. This allowed us to intercept the user password and search it in our database. In case a match is found, we flag the user account and immediately notify them via email. The user has a grace period of 14 days to change their password, after which their account is blocked and a password reset is mandatory to access their&amp;nbsp;account. &lt;/p&gt;
&lt;h3&gt;Presentations&lt;/h3&gt;
&lt;p&gt;The &lt;span class="caps"&gt;2FA&lt;/span&gt; migration project at &lt;span class="caps"&gt;CERN&lt;/span&gt; was presented at these&amp;nbsp;conferences.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://indico.cern.ch/event/1200682/contributions/5090608/"&gt;HEPiX&amp;nbsp;presentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://indico.jlab.org/event/459/contributions/11497/"&gt;&lt;span class="caps"&gt;CHEP&lt;/span&gt; 2023&amp;nbsp;presentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;We plan to finish the &lt;span class="caps"&gt;2FA&lt;/span&gt; migration and remove all logic relating with optional &lt;span class="caps"&gt;2FA&lt;/span&gt; flow. There have been numerous requests from users to add new features to our system; these include the ability to register multiple &lt;span class="caps"&gt;2FA&lt;/span&gt; tokens and add support for step-up authentication for applications requiring &lt;span class="caps"&gt;2FA&lt;/span&gt;. These features are now natively supported in newer versions of Keycloak and thus will be made available to our users. The timeline for providing this new functionality has yet to be&amp;nbsp;determined.&lt;/p&gt;</content><category term="security"/></entry><entry><title>Rate limiting in HAProxy and Nginx</title><link href="https://adl1995.github.io/rate-limiting-in-haproxy-and-nginx.html" rel="alternate"/><published>2023-05-29T18:00:00+02:00</published><updated>2023-05-29T18:00:00+02:00</updated><author><name>Adeel Ahmad</name></author><id>tag:adl1995.github.io,2023-05-29:/rate-limiting-in-haproxy-and-nginx.html</id><summary type="html">&lt;p&gt;Rate-limiting is a common strategy for safe guarding a server from potential DDoS attacks or sudden peaks in network traffic. Rate-limiting instructs the server to block requests from certain &lt;span class="caps"&gt;IP&lt;/span&gt; addresses that are sending an unusual number of requests to the&amp;nbsp;system.&lt;/p&gt;
&lt;p&gt;We can apply rate-limiting to both Nginx and HAProxy. Nginx runs on each end node hosting the service, while HAProxy serves as the load-balancer and distributes incoming requests among available nodes. This post describes how to rate-limit requests on both Nginx and HAProxy and shows how to whitelist IPs and rate-limit a single &lt;span class="caps"&gt;URL&lt;/span&gt;. The final section shows how to apply this configuration in&amp;nbsp;Puppet.&lt;/p&gt;
&lt;h2&gt;1. Rate-limiting in&amp;nbsp;HAProxy&lt;/h2&gt;
&lt;p&gt;This section describes how to configure HAProxy to rate-limit …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Rate-limiting is a common strategy for safe guarding a server from potential DDoS attacks or sudden peaks in network traffic. Rate-limiting instructs the server to block requests from certain &lt;span class="caps"&gt;IP&lt;/span&gt; addresses that are sending an unusual number of requests to the&amp;nbsp;system.&lt;/p&gt;
&lt;p&gt;We can apply rate-limiting to both Nginx and HAProxy. Nginx runs on each end node hosting the service, while HAProxy serves as the load-balancer and distributes incoming requests among available nodes. This post describes how to rate-limit requests on both Nginx and HAProxy and shows how to whitelist IPs and rate-limit a single &lt;span class="caps"&gt;URL&lt;/span&gt;. The final section shows how to apply this configuration in&amp;nbsp;Puppet.&lt;/p&gt;
&lt;h2&gt;1. Rate-limiting in&amp;nbsp;HAProxy&lt;/h2&gt;
&lt;p&gt;This section describes how to configure HAProxy to rate-limit incoming requests and block requests that cross a certain threshold. HAProxy provides rate-limiting in the following&amp;nbsp;contexts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Rate queuing:&lt;/strong&gt; The simplest form of rate-limiting in which requests are queued if they cross a certain threshold. Subsequent requests are served in &lt;span class="caps"&gt;FIFO&lt;/span&gt;&amp;nbsp;order.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sliding window:&lt;/strong&gt; Requests are stored in a &lt;em&gt;stick table&lt;/em&gt; that keeps a record of incoming &lt;span class="caps"&gt;IP&lt;/span&gt; addresses. A threshold is defined, allowing the user to make a certain number of requests in a given time period. Subsequent requests are denied with&amp;nbsp;a &lt;code&gt;429&lt;/code&gt; status&amp;nbsp;code.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fixed window:&lt;/strong&gt; Similar to the sliding window, we define an interval as before; however, instead of storing the request rate, we keep a request counter. When requests for a certain user reach their limit, subsequent requests get blocked, usually for a period of 24 hours (can be modified). This is mainly used for APIs where user requests are limited to, for example, 1000 per&amp;nbsp;day.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;1.1. HAProxy&amp;nbsp;configuration&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;This &lt;a href="https://www.haproxy.com/blog/four-examples-of-haproxy-rate-limiting"&gt;HAProxy blog&lt;/a&gt; covers the basics of applying rate-limiting on the server&amp;nbsp;side.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;HAProxy configuration is divided into four&amp;nbsp;sections: &lt;code&gt;frontend&lt;/code&gt;, &lt;code&gt;backend&lt;/code&gt;, &lt;code&gt;defaults&lt;/code&gt;,&amp;nbsp;and &lt;code&gt;global&lt;/code&gt;.&amp;nbsp;The &lt;code&gt;frontend&lt;/code&gt; handles incoming requests from clients, and&amp;nbsp;the &lt;code&gt;backend&lt;/code&gt; is expected to fulfill the request. Note that all these steps can be performed in a combined config section, but for maintainability and readability, it is divided into separate&amp;nbsp;sections.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;frontend&lt;/code&gt;: Handles all incoming requests from&amp;nbsp;clients.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;backend&lt;/code&gt;: Expected to fulfill the&amp;nbsp;request.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;defaults&lt;/code&gt;: Contains default settings for the server. Can be used to avoid&amp;nbsp;duplication.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;global&lt;/code&gt;: Global settings for the&amp;nbsp;server.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;To rate-limit in HAProxy, we first need to decrypt the &lt;span class="caps"&gt;HTTPS&lt;/span&gt; traffic using &lt;span class="caps"&gt;SSL&lt;/span&gt; offloading. This feature is only available in HAProxy versions 1.x and&amp;nbsp;onwards.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We will only focus on&amp;nbsp;the &lt;code&gt;frontend&lt;/code&gt; and &lt;code&gt;backend&lt;/code&gt; configuration sections, as the other sections contain default configuration and will not be used for setting up load&amp;nbsp;balancing.&lt;/p&gt;
&lt;h3&gt;1.2. Defining the frontend&amp;nbsp;configuration&lt;/h3&gt;
&lt;p&gt;In&amp;nbsp;the &lt;code&gt;frontend&lt;/code&gt; section, we specify the port and &lt;span class="caps"&gt;IP&lt;/span&gt; address where our site listens for traffic. If we have configured &lt;span class="caps"&gt;SSL&lt;/span&gt; for our site, we also need to specify the location of the certificate&amp;nbsp;file.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;View&amp;nbsp;the &lt;code&gt;haproxy&lt;/code&gt; systemd service&amp;nbsp;(&lt;code&gt;$ systemctl status haproxy&lt;/code&gt;) to check where the configuration file is&amp;nbsp;defined.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;frontend my_example_site.com&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# The keyword following &amp;#39;frontend&amp;#39; is the label.&lt;/span&gt;
&lt;span class="l l-Scalar l-Scalar-Plain"&gt;bind *:443 ssl crt /etc/ssl/cert1.pem&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;
&lt;span class="l l-Scalar l-Scalar-Plain"&gt;bind *:80&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# Bind all IP addresses to listen on port 80.&lt;/span&gt;
&lt;span class="l l-Scalar l-Scalar-Plain"&gt;default_backend example_site_backend&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# Afterwards, traffic will get redirected to this backend.&lt;/span&gt;
&lt;span class="l l-Scalar l-Scalar-Plain"&gt;http-request track-sc0 src&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# (Described in section 1.4. Stick tables).&lt;/span&gt;
&lt;span class="l l-Scalar l-Scalar-Plain"&gt;http-request deny deny_status 429 if { sc_http_req_rate(0) gt 50 } !white_list&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# (Described in section 1.5. Setting the Request Rate Limit).&lt;/span&gt;
&lt;span class="l l-Scalar l-Scalar-Plain"&gt;mode http&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# HTTP mode instructs the server to inspect the traffic before passing it to the backend.&lt;/span&gt;
&lt;span class="c1"&gt;# Other options include &amp;#39;tcp,&amp;#39; which means the traffic is encrypted and will be passed on to the backend as is.&lt;/span&gt;
&lt;span class="l l-Scalar l-Scalar-Plain"&gt;redirect scheme https code 301 if !{ ssl_fc }&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# Instructs the server to redirect traffic to HTTPS with a 301 status code if they try accessing from an unencrypted site.&lt;/span&gt;
&lt;span class="l l-Scalar l-Scalar-Plain"&gt;stick-table type ipv6 size 100k expire 30s store http_req_rate(1s)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# See the section below for stick tables.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;1.3. Defining the backend&amp;nbsp;configuration&lt;/h3&gt;
&lt;p&gt;The backend section defines a pool of servers where requests are actually handled. Below is an example backend&amp;nbsp;configuration:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;backend my_example_site.com_backend&lt;/span&gt;
&lt;span class="l l-Scalar l-Scalar-Plain"&gt;balance roundrobin&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# Select the load balancing algorithm.&lt;/span&gt;
&lt;span class="l l-Scalar l-Scalar-Plain"&gt;default-server inter 2s fall 2 rise 2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# See below:&lt;/span&gt;
&lt;span class="c1"&gt;# inter: Specifies the inter-check delay for health checks. In this case, &amp;#39;inter 2s&amp;#39; indicates that the interval between two consecutive health checks for a server is 2 seconds.&lt;/span&gt;
&lt;span class="c1"&gt;# fall: Specifies the number of consecutive failed health checks after which a server is considered down. With &amp;#39;fall 2&amp;#39;, if two consecutive health checks fail, the server will be marked as down.&lt;/span&gt;
&lt;span class="c1"&gt;# rise: Specifies the number of consecutive successful health checks required for a server to be marked as up. With &amp;#39;rise 2&amp;#39;, after two consecutive successful health checks, the server will be marked as up.&lt;/span&gt;
&lt;span class="l l-Scalar l-Scalar-Plain"&gt;mode http&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# Instruct backend servers to communicate using the HTTP protocol.&lt;/span&gt;
&lt;span class="l l-Scalar l-Scalar-Plain"&gt;option httpchk HEAD /status HTTP/1.1\r\nHost:\ www.example.com&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# See below:&lt;/span&gt;
&lt;span class="c1"&gt;# Sends a HEAD request to the /status path of the backend servers, specifying the Host header as www.example.com, to perform health checks and determine the availability and health status of the servers.&lt;/span&gt;
&lt;span class="l l-Scalar l-Scalar-Plain"&gt;stick on src&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# Enable session stickiness based on the source IP address of the client.&lt;/span&gt;
&lt;span class="l l-Scalar l-Scalar-Plain"&gt;stick-table type ip size 20k peers sct_my_example_site&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# Define a stick table.&lt;/span&gt;
&lt;span class="l l-Scalar l-Scalar-Plain"&gt;server server1 192.168.1.25:80&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# Define a list of servers; each on a separate line.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;1.4. Stick&amp;nbsp;tables&lt;/h3&gt;
&lt;p&gt;Stick tables are what make it possible to rate-limit servers. They are a key-value store that holds an incoming &lt;span class="caps"&gt;IP&lt;/span&gt; address as the key with its counter. The counter is incremented whenever a new request is made to the server. Using this information, we can define rules to block requests if they cross a certain&amp;nbsp;threshold.&lt;/p&gt;
&lt;p&gt;We can tweak how long a stick table can hold information before erasing its buffer. A stick table can be defined as&amp;nbsp;follows.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;backend st_src_global&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# The backend for which we are defining this.&lt;/span&gt;
&lt;span class="l l-Scalar l-Scalar-Plain"&gt;stick-table type ip size 1m expire 10s store http_req_rate(10s)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# A stick table that can hold 1m&lt;/span&gt;
&lt;span class="c1"&gt;# (1048576) IPs and expires after 10 seconds unless it is accessed during that time.&lt;/span&gt;
&lt;span class="c1"&gt;# The HTTP request rate is calculated in an interval of 10 seconds.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;HAProxy provides up to 12 stick table counter tracks, labeled&amp;nbsp;from &lt;code&gt;sc0&lt;/code&gt; up&amp;nbsp;to &lt;code&gt;sc11&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;1.5. Setting the request rate&amp;nbsp;limit&lt;/h3&gt;
&lt;p&gt;We can use the HAProxy&amp;nbsp;built-in &lt;code&gt;http_req_rate&lt;/code&gt; directive to measure the request rate. In this example, we will return&amp;nbsp;a &lt;code&gt;429&lt;/code&gt; if a user makes more than 50 requests in an interval of 5&amp;nbsp;seconds.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;stick-table type ip size 1m expire 5s store http_req_rate(5s)&lt;/span&gt;
&lt;span class="l l-Scalar l-Scalar-Plain"&gt;http-request deny deny_status 429 if { sc_http_req_rate(0) gt 50 } !white_list&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;The&amp;nbsp;parameter &lt;code&gt;0&lt;/code&gt; for &lt;code&gt;sc_http_req_rate&lt;/code&gt; refers to the stick counter&amp;nbsp;number.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;1.6. Whitelisting an &lt;span class="caps"&gt;IP&lt;/span&gt;&amp;nbsp;address&lt;/h3&gt;
&lt;p&gt;To define a whitelist, we use Access Control Lists (ACLs). In HAProxy, they allow us to test various conditions and perform a given action based on those tests. They can be defined as&amp;nbsp;follows.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;acl white_list src 192.168.1.1 ...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# List of IP addresses to whitelist.&lt;/span&gt;
&lt;span class="l l-Scalar l-Scalar-Plain"&gt;http-request track-sc0 src&lt;/span&gt;
&lt;span class="l l-Scalar l-Scalar-Plain"&gt;http-request deny deny_status 429 if { sc_http_req_rate(0) gt 25 } !white_list&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# This applies to all incoming IPs, except for those defined in the whitelist.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;1.7. Limiting the number of open&amp;nbsp;connections&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;conn_cur&lt;/code&gt; option can be used to count the number of open connections from an &lt;span class="caps"&gt;IP&lt;/span&gt; address. If a user has too many connections open, we can deny their further connections. The syntax remains similar as&amp;nbsp;before.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;stick-table type ip size 1m expire 10s store conn_cur&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# Define a stick table.&lt;/span&gt;
&lt;span class="l l-Scalar l-Scalar-Plain"&gt;tcp-request content track-sc0 src&lt;/span&gt;
&lt;span class="l l-Scalar l-Scalar-Plain"&gt;tcp-request content reject if { sc_conn_cur(0) gt 10 }&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# The parameter `0` in sc_conn_cur refers to the stick counter number.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;By&amp;nbsp;using &lt;code&gt;tcp-request&lt;/code&gt; instead&amp;nbsp;of &lt;code&gt;http-request&lt;/code&gt;, we do not evaluate &lt;span class="caps"&gt;HTTP&lt;/span&gt; headers in the packet, making the processing more&amp;nbsp;efficient.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;HAProxy provides additional counters for measuring the error rate of a site (&lt;span class="caps"&gt;HTTP&lt;/span&gt; requests that have&amp;nbsp;a &lt;code&gt;4xx&lt;/code&gt; status code).&amp;nbsp;The &lt;code&gt;bytes_out_rate&lt;/code&gt; counter can be used to track content that is generating the most traffic for your site. It is also possible to create custom statistics using the general-purpose&amp;nbsp;counter &lt;code&gt;sc_inc_gpc0&lt;/code&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;HAProxy Enterprise has features that allow individual increments across all peer nodes. This approach is better for detecting DDoS&amp;nbsp;attacks.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;2. Rate-limiting in&amp;nbsp;Nginx&lt;/h2&gt;
&lt;p&gt;Nginx supports various rate-limiting schemes. In the example below, we&amp;#8217;ll set up two-stage&amp;nbsp;rate-limiting.&lt;/p&gt;
&lt;p&gt;Two-stage rate-limiting throttles a request before blocking it. To implement this, we first define&amp;nbsp;the &lt;code&gt;limit_req_zone&lt;/code&gt; in the Nginx configuration&amp;nbsp;file &lt;code&gt;nginx.conf&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;limit_req_zone zone=two_stage_limit_store:10m rate=5r/s;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To limit certain types of requests, we can use&amp;nbsp;the &lt;code&gt;$limit&lt;/code&gt; variable. This can be used for rate-limiting a specific request method. For example, to rate-limit &lt;span class="caps"&gt;POST&lt;/span&gt; calls, we can&amp;nbsp;write:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;map $request_method $limit {&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;default         &amp;#39;&amp;#39;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;POST            &amp;lt;IP of your server&amp;gt;;&lt;/span&gt;
&lt;span class="l l-Scalar l-Scalar-Plain"&gt;}&lt;/span&gt;

&lt;span class="l l-Scalar l-Scalar-Plain"&gt;limit_req_zone $limit zone=two_stage_limit_store:10m rate=5r/s;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Next, we reference the above limit in our server configuration. If the server hostname is &amp;#8220;example.com,&amp;#8221; this file will be located&amp;nbsp;at &lt;code&gt;/etc/nginx/sites-enabled/example.com.conf&lt;/code&gt;. We first specify the response code for requests exceeding our rate-limit in&amp;nbsp;the &lt;code&gt;server&lt;/code&gt; section.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;server {&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;...&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;limit_req_status 429;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;...&lt;/span&gt;
&lt;span class="l l-Scalar l-Scalar-Plain"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;The error&amp;nbsp;code &lt;code&gt;429&lt;/code&gt; corresponds to &amp;#8220;Too Many&amp;nbsp;Requests.&amp;#8221;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Next, we define&amp;nbsp;the &lt;code&gt;location&lt;/code&gt;(s) we would like to rate-limit (within&amp;nbsp;the &lt;code&gt;server&lt;/code&gt; section). To rate-limit a specific endpoint, we can&amp;nbsp;add:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;location /token/abc {&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;limit_req zone=two_stage_limit_store burst=25 delay=20;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;...&lt;/span&gt;
&lt;span class="l l-Scalar l-Scalar-Plain"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: If we&amp;nbsp;replace &lt;code&gt;/token/abc&lt;/code&gt; with &lt;code&gt;/&lt;/code&gt;, we will rate-limit all endpoints on our&amp;nbsp;server.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Here, we reference the limit zone defined earlier in&amp;nbsp;the &lt;code&gt;nginx.conf&lt;/code&gt; file. The next two parameters are our rate-limit&amp;nbsp;parameters:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;burst&lt;/code&gt; specifies the number of requests to allow within 1&amp;nbsp;second.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;delay&lt;/code&gt; indicates the request count after which subsequent requests are&amp;nbsp;throttled.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To summarize, in the above config, we allow up to 25 requests per second. Requests 1 to 20 will reach the server without any delay. Requests 21 to 25 will be throttled, and any subsequent requests will be rejected with&amp;nbsp;a &lt;code&gt;429&lt;/code&gt; status&amp;nbsp;code.&lt;/p&gt;
&lt;p&gt;Upon reviewing the text, I have identified a few minor typos and suggestions for&amp;nbsp;improvement:&lt;/p&gt;
&lt;h2&gt;3. Configuration in&amp;nbsp;Puppet&lt;/h2&gt;
&lt;p&gt;The configurations below are an exact replica of what is shown above, but in Puppet. It is assumed that the required services are already installed on the&amp;nbsp;system.&lt;/p&gt;
&lt;h3&gt;3.1. HAProxy&amp;nbsp;configuration&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# HAProxy Configuration&lt;/span&gt;

&lt;span class="l l-Scalar l-Scalar-Plain"&gt;$ipv4s_list = ...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# List of IPs.&lt;/span&gt;

&lt;span class="l l-Scalar l-Scalar-Plain"&gt;haproxy::frontend { &amp;quot;my_example_site.com&amp;quot;&lt;/span&gt;&lt;span class="p p-Indicator"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;bind    =&amp;gt; {&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;&amp;#39;*:80&amp;#39;   =&amp;gt; [],&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;&amp;#39;:::80&amp;#39;  =&amp;gt; [],&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;# SSL termination for rate-limiting.&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;&amp;#39;*:443&amp;#39;  =&amp;gt; &amp;quot;ssl crt ${certificate_path}&amp;quot;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;&amp;#39;:::443&amp;#39; =&amp;gt; &amp;quot;ssl crt ${certificate_path}&amp;quot;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;options =&amp;gt; {&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;&amp;#39;mode&amp;#39;            =&amp;gt; &amp;#39;http&amp;#39;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;&amp;#39;redirect&amp;#39;        =&amp;gt; &amp;#39;scheme https code 301 if !{ ssl_fc }&amp;#39;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;&amp;#39;stick-table&amp;#39;     =&amp;gt; &amp;#39;type ipv6 size 100k expire 30s store http_req_rate(1s)&amp;#39;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;&amp;#39;acl&amp;#39;             =&amp;gt; &amp;quot;white_list src ${join($ipv4s_list, &amp;#39; &amp;#39;)}&amp;quot;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;&amp;#39;http-request&amp;#39;    =&amp;gt; [&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;&amp;#39;track-sc0 src&amp;#39;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;# This setting allows 45 requests per second.&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;&amp;#39;deny deny_status 429 if { sc_http_req_rate(0) gt 45 } !white_list&amp;#39;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;],&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;&amp;#39;default_backend&amp;#39; =&amp;gt; &amp;quot;my_example_site.com_backend&amp;quot;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;},&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;

&lt;span class="l l-Scalar l-Scalar-Plain"&gt;haproxy::backend { &amp;quot;my_example_site.com_backend&amp;quot;&lt;/span&gt;&lt;span class="p p-Indicator"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;options          =&amp;gt; {&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;&amp;#39;balance&amp;#39;        =&amp;gt; &amp;#39;roundrobin&amp;#39;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;&amp;#39;mode&amp;#39;           =&amp;gt; &amp;#39;http&amp;#39;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;&amp;#39;default-server&amp;#39; =&amp;gt; &amp;#39;inter 2s fall 2 rise 2&amp;#39;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;&amp;#39;option&amp;#39;         =&amp;gt; [&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;&amp;quot;httpchk HEAD /status HTTP/1.1\r\nHost:\ www.example.com&amp;quot;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;],&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;&amp;#39;stick&amp;#39;          =&amp;gt; &amp;#39;on src&amp;#39;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;&amp;#39;stick-table&amp;#39;    =&amp;gt; &amp;quot;type ip size 20k peers sct_my_example_site&amp;quot;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;},&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;3.2. Nginx&amp;nbsp;configuration&lt;/h3&gt;
&lt;p&gt;Similarly, for Nginx, we define the same configuration as&amp;nbsp;above.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Two-stage rate limiting for all nodes, see: https://www.nginx.com/blog/rate-limiting-nginx/#Two-Stage-Rate-Limiting&lt;/span&gt;
&lt;span class="l l-Scalar l-Scalar-Plain"&gt;nginx::resource::location { &amp;#39;rate limit&amp;#39;&lt;/span&gt;&lt;span class="p p-Indicator"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;ensure                =&amp;gt; present,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;ssl                   =&amp;gt; true,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;ssl_only              =&amp;gt; true,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;location              =&amp;gt; &amp;#39;/&amp;#39;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;server                =&amp;gt; $facts[&amp;#39;networking&amp;#39;][&amp;#39;fqdn&amp;#39;],&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;limit_zone            =&amp;gt; &amp;#39;two_stage_limit_store burst=25 delay=20&amp;#39;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;proxy                 =&amp;gt; &amp;quot;http://my_example_site.com:8080&amp;quot;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;proxy_read_timeout    =&amp;gt; &amp;#39;90s&amp;#39;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;proxy_connect_timeout =&amp;gt; &amp;#39;90s&amp;#39;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;proxy_send_timeout    =&amp;gt; &amp;#39;90s&amp;#39;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;proxy_set_header      =&amp;gt; $proxy_set_header,&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;

&lt;span class="l l-Scalar l-Scalar-Plain"&gt;class { &amp;#39;nginx&amp;#39;&lt;/span&gt;&lt;span class="p p-Indicator"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;# Override the default Nginx log format.&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;log_format              =&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;&amp;#39;json_combined&amp;#39; =&amp;gt; &amp;#39;escape=json&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;&amp;#39;{&amp;quot;data&amp;quot;&lt;/span&gt;&lt;span class="p p-Indicator"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&amp;quot;time_local&amp;quot;&lt;/span&gt;&lt;span class="p p-Indicator"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;${time_local}&amp;quot;&lt;/span&gt;&lt;span class="p p-Indicator"&gt;,&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&amp;quot;remote_addr&amp;quot;&lt;/span&gt;&lt;span class="p p-Indicator"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;${remote_addr}&amp;quot;&lt;/span&gt;&lt;span class="p p-Indicator"&gt;,&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&amp;quot;remote_user&amp;quot;&lt;/span&gt;&lt;span class="p p-Indicator"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;${remote_user}&amp;quot;&lt;/span&gt;&lt;span class="p p-Indicator"&gt;,&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&amp;quot;request&amp;quot;&lt;/span&gt;&lt;span class="p p-Indicator"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;${request}&amp;quot;&lt;/span&gt;&lt;span class="p p-Indicator"&gt;,&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&amp;quot;status&amp;quot;&lt;/span&gt;&lt;span class="p p-Indicator"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;${status}&amp;quot;&lt;/span&gt;&lt;span class="p p-Indicator"&gt;,&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&amp;quot;body_bytes_sent&amp;quot;&lt;/span&gt;&lt;span class="p p-Indicator"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;${body_bytes_sent}&amp;quot;&lt;/span&gt;&lt;span class="p p-Indicator"&gt;,&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&amp;quot;request_time&amp;quot;&lt;/span&gt;&lt;span class="p p-Indicator"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;${request_time}&amp;quot;&lt;/span&gt;&lt;span class="p p-Indicator"&gt;,&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&amp;quot;http_referrer&amp;quot;&lt;/span&gt;&lt;span class="p p-Indicator"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;${http_referer}&amp;quot;&lt;/span&gt;&lt;span class="p p-Indicator"&gt;,&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&amp;quot;http_user_agent&amp;quot;&lt;/span&gt;&lt;span class="p p-Indicator"&gt;:&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;${http_user_agent}&amp;quot;&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p p-Indicator"&gt;}&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="s"&gt;&amp;#39;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;},&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;limit_req_zone&lt;/span&gt;&lt;span class="nv"&gt;          &lt;/span&gt;&lt;span class="s"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;${facts[&amp;#39;&lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;networking&amp;#39;][&amp;#39;ip&amp;#39;]} zone=two_stage_limit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Further&amp;nbsp;reading&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.haproxy.com/blog/haproxy-ssl-termination/"&gt;https://www.haproxy.com/blog/haproxy-ssl-termination/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.haproxy.com/blog/introduction-to-haproxy-stick-tables/"&gt;https://www.haproxy.com/blog/introduction-to-haproxy-stick-tables/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.haproxy.com/blog/four-examples-of-haproxy-rate-limiting/"&gt;https://www.haproxy.com/blog/four-examples-of-haproxy-rate-limiting/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.haproxy.com/blog/introduction-to-haproxy-acls/"&gt;https://www.haproxy.com/blog/introduction-to-haproxy-acls/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://uldissturms.github.io/2014/03/03/request-rate-limiting-with-haproxy-vs-nginx-using-chef-solo/"&gt;http://uldissturms.github.io/2014/03/03/request-rate-limiting-with-haproxy-vs-nginx-using-chef-solo/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.sysbee.net/blog/sysbee-blog/haproxy-sysadmins-swiss-army-knife/"&gt;https://www.sysbee.net/blog/sysbee-blog/haproxy-sysadmins-swiss-army-knife/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><category term="Security"/></entry><entry><title>Creating a JSON logger for Flask</title><link href="https://adl1995.github.io/creating-a-json-logger-for-flask.html" rel="alternate"/><published>2022-08-09T21:30:00+02:00</published><updated>2022-08-09T21:30:00+02:00</updated><author><name>Adeel Ahmad</name></author><id>tag:adl1995.github.io,2022-08-09:/creating-a-json-logger-for-flask.html</id><summary type="html">&lt;p&gt;By default Flask writes logs to the console in plain-text format. This can be limiting if you intend to store your logs in a text file and periodically send them to a central monitoring service. For example, &lt;a href="https://www.elastic.co/kibana/"&gt;Kibana&lt;/a&gt;, only accepts &lt;span class="caps"&gt;JSON&lt;/span&gt; logs by&amp;nbsp;default.&lt;/p&gt;
&lt;p&gt;You might also want to enrich your logs with additional metadata, e.g. timestamps, method names, log type (Warn, Debug, etc.). In this post we will use the Python &lt;a href="https://docs.python.org/3/library/logging.html"&gt;logging&lt;/a&gt; library to modify Flask&amp;#8217;s logging format and write them to a text file. In the end we will see how to periodically send these logs to an external service using &lt;a href="https://flume.apache.org/"&gt;Flume&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In our app we would like to setup two types of loggers. One for …&lt;/p&gt;</summary><content type="html">&lt;p&gt;By default Flask writes logs to the console in plain-text format. This can be limiting if you intend to store your logs in a text file and periodically send them to a central monitoring service. For example, &lt;a href="https://www.elastic.co/kibana/"&gt;Kibana&lt;/a&gt;, only accepts &lt;span class="caps"&gt;JSON&lt;/span&gt; logs by&amp;nbsp;default.&lt;/p&gt;
&lt;p&gt;You might also want to enrich your logs with additional metadata, e.g. timestamps, method names, log type (Warn, Debug, etc.). In this post we will use the Python &lt;a href="https://docs.python.org/3/library/logging.html"&gt;logging&lt;/a&gt; library to modify Flask&amp;#8217;s logging format and write them to a text file. In the end we will see how to periodically send these logs to an external service using &lt;a href="https://flume.apache.org/"&gt;Flume&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In our app we would like to setup two types of loggers. One for writing logs to the console, and the other for writing them to a file. &lt;span class="caps"&gt;CLI&lt;/span&gt; logs can be brief and only contain essential data, while file logs can be more extensive with full exception&amp;nbsp;details.&lt;/p&gt;
&lt;h2&gt;Creating a file log&amp;nbsp;handler&lt;/h2&gt;
&lt;p&gt;We can use&amp;nbsp;the &lt;code&gt;logging.handlers.TimedRotatingFileHandler&lt;/code&gt; method to create the file handler. Over time this file can grow, so we will instruct the handler to create a new file every 12&amp;nbsp;hours. &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;For a full list of file log handlers, see here: &lt;a href="https://docs.python.org/3/library/logging.handlers.html#filehandler"&gt;https://docs.python.org/3/library/logging.handlers.html#filehandler&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;json_handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;handlers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TimedRotatingFileHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;LOG_DIR&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/project-abc.log&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;interval&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;H&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;backupCount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;when&lt;/code&gt; parameter specifies&amp;nbsp;the &lt;code&gt;interval&lt;/code&gt; rate. In our case a new file will get created every 12 hours.&amp;nbsp;Setting &lt;code&gt;backupCount&lt;/code&gt; to &lt;code&gt;1&lt;/code&gt; keeps a single backup of the log file. Afterwards it will get&amp;nbsp;deleted.&lt;/p&gt;
&lt;p&gt;In the next step we create our custom &lt;span class="caps"&gt;JSON&lt;/span&gt; formatter to update the log formatting to our&amp;nbsp;needs.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;JsonFormatter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Formatter&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;formatException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exc_info&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JsonFormatter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;formatException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exc_info&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;json_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;&amp;quot;timestamp&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;&amp;quot;level&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;ERROR&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;&amp;quot;logger&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;app&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s2"&gt;&amp;quot;message&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json_result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Next we instantiate this class and pass it our desired &lt;span class="caps"&gt;JSON&lt;/span&gt;&amp;nbsp;format.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;json_formatter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JsonFormatter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="s1"&gt;&amp;#39;{&amp;quot;timestamp&amp;quot;:&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;%(asctime)s&lt;/span&gt;&lt;span class="s1"&gt;&amp;quot;, &amp;quot;level&amp;quot;:&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;%(levelname)s&lt;/span&gt;&lt;span class="s1"&gt;&amp;quot;, &amp;quot;logger&amp;quot;:&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;%(module)s&lt;/span&gt;&lt;span class="s1"&gt;&amp;quot;, &amp;quot;message&amp;quot;:&amp;quot;&lt;/span&gt;&lt;span class="si"&gt;%(message)s&lt;/span&gt;&lt;span class="s1"&gt;&amp;quot;}&amp;#39;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;All logs will be written in the format given above, including exception logs, since we override&amp;nbsp;the &lt;code&gt;formatException&lt;/code&gt; method in our&amp;nbsp;class.&lt;/p&gt;
&lt;p&gt;Creating a&amp;nbsp;custom &lt;code&gt;JsonFormatter&lt;/code&gt; is necessary as we intend to override&amp;nbsp;its &lt;code&gt;formatException&lt;/code&gt; method.  By default exceptions are written in plain-text format, but this is incompatible with our Kibana setup, which expects data to be in &lt;span class="caps"&gt;JSON&lt;/span&gt;&amp;nbsp;format.&lt;/p&gt;
&lt;p&gt;Next we register this formatter class with our Flask app. This is usually done in the&amp;nbsp;project&amp;#8217;s &lt;code&gt;app.py&lt;/code&gt; file.&amp;nbsp;The &lt;code&gt;root.addHandler&lt;/code&gt; call will register this formatter with our Flask&amp;nbsp;app. &lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;json_handler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setFormatter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json_formatter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json_handler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Configuring the &lt;span class="caps"&gt;CLI&lt;/span&gt;&amp;nbsp;logs&lt;/h2&gt;
&lt;p&gt;So far we only tinkered with file logs. To create a custom log format for &lt;span class="caps"&gt;CLI&lt;/span&gt; logs we must use the &lt;a href="https://docs.python.org/3/library/logging.handlers.html#streamhandler"&gt;logging.StreamHandler&lt;/a&gt; class. After instantiating it we can set our desired log&amp;nbsp;format.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;console_handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StreamHandler&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;console_formatter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Formatter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="s2"&gt;&amp;quot;[&lt;/span&gt;&lt;span class="si"&gt;%(asctime)s&lt;/span&gt;&lt;span class="s2"&gt;] - &lt;/span&gt;&lt;span class="si"&gt;%(name)s&lt;/span&gt;&lt;span class="s2"&gt; - &lt;/span&gt;&lt;span class="si"&gt;%(levelname)s&lt;/span&gt;&lt;span class="s2"&gt; - &lt;/span&gt;&lt;span class="si"&gt;%(message)s&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As we did for the file log handler, we need to register this formatter in&amp;nbsp;our &lt;code&gt;app.py&lt;/code&gt; file.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;console_handler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setFormatter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;console_formatter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;console_handler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Result&lt;/h2&gt;
&lt;p&gt;Once everything is configured your Flask project should produce &lt;span class="caps"&gt;JSON&lt;/span&gt; logs, like&amp;nbsp;below.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;timestamp&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;2022-08-08 20:12:40,326&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;level&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;INFO&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;logger&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;_internal&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;message&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;127.0.0.1 - - [08/Aug/2022 20:12:40] &amp;quot;&lt;/span&gt;&lt;span class="err"&gt;ESC&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="err"&gt;mGET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;HTTP/&lt;/span&gt;&lt;span class="mf"&gt;1.1E&lt;/span&gt;&lt;span class="err"&gt;SC&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;m&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot; 302 -&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;timestamp&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;2022-08-08 20:12:40,368&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;level&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;INFO&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;logger&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;_internal&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;message&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;127.0.0.1 - - [08/Aug/2022 20:12:40] &amp;quot;&lt;/span&gt;&lt;span class="err"&gt;ESC&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;37&lt;/span&gt;&lt;span class="err"&gt;mGET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;/swagger&lt;/span&gt;&lt;span class="mi"&gt;-&lt;/span&gt;&lt;span class="err"&gt;ui&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;HTTP/&lt;/span&gt;&lt;span class="mf"&gt;1.1E&lt;/span&gt;&lt;span class="err"&gt;SC&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;m&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot; 200 -&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;timestamp&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;2022-08-08 20:12:40,431&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;level&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;INFO&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;logger&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;_internal&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;message&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;127.0.0.1 - - [08/Aug/2022 20:12:40] &amp;quot;&lt;/span&gt;&lt;span class="err"&gt;ESC&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;37&lt;/span&gt;&lt;span class="err"&gt;mGET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;/swaggerui/droid&lt;/span&gt;&lt;span class="mi"&gt;-&lt;/span&gt;&lt;span class="err"&gt;sa&lt;/span&gt;&lt;span class="kc"&gt;ns&lt;/span&gt;&lt;span class="err"&gt;.css&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;HTTP/&lt;/span&gt;&lt;span class="mf"&gt;1.1E&lt;/span&gt;&lt;span class="err"&gt;SC&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;m&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot; 200 -&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;timestamp&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;2022-08-08 20:12:40,433&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;level&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;INFO&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;logger&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;_internal&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;message&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;127.0.0.1 - - [08/Aug/2022 20:12:40] &amp;quot;&lt;/span&gt;&lt;span class="err"&gt;ESC&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;37&lt;/span&gt;&lt;span class="err"&gt;mGET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;/swaggerui/swagger&lt;/span&gt;&lt;span class="mi"&gt;-&lt;/span&gt;&lt;span class="err"&gt;ui.css&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;HTTP/&lt;/span&gt;&lt;span class="mf"&gt;1.1E&lt;/span&gt;&lt;span class="err"&gt;SC&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;m&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot; 200 -&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;timestamp&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;2022-08-08 20:12:40,433&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;level&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;INFO&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;logger&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;_internal&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;message&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;127.0.0.1 - - [08/Aug/2022 20:12:40] &amp;quot;&lt;/span&gt;&lt;span class="err"&gt;ESC&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;37&lt;/span&gt;&lt;span class="err"&gt;mGET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;/swaggerui/swagger&lt;/span&gt;&lt;span class="mi"&gt;-&lt;/span&gt;&lt;span class="err"&gt;ui&lt;/span&gt;&lt;span class="mi"&gt;-&lt;/span&gt;&lt;span class="err"&gt;bu&lt;/span&gt;&lt;span class="kc"&gt;n&lt;/span&gt;&lt;span class="err"&gt;dle.js&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;HTTP/&lt;/span&gt;&lt;span class="mf"&gt;1.1E&lt;/span&gt;&lt;span class="err"&gt;SC&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="err"&gt;m&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot; 200 -&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h1&gt;Sending logs with&amp;nbsp;Flume&lt;/h1&gt;
&lt;p&gt;Setting up Flume is outside the scope of this post, but we show the configuration needed to send these logs to your monitoring service using the log file we created&amp;nbsp;above.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# # ##################### #&lt;/span&gt;
&lt;span class="c1"&gt;# # Configure the sources #&lt;/span&gt;
&lt;span class="c1"&gt;# # ##################### #&lt;/span&gt;
&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sources&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;r0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filegroups&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="o"&gt;/.*&lt;/span&gt;
&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sources&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;r0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;abc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sources&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;r0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;abc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;

&lt;span class="c1"&gt;# ################### #&lt;/span&gt;
&lt;span class="c1"&gt;# Configure the sinks #&lt;/span&gt;
&lt;span class="c1"&gt;# ################### #&lt;/span&gt;
&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sinks&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;k0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hostname&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;xyz&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt;
&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sinks&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;k0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10001&lt;/span&gt;
&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sinks&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;k0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;batch&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;em&gt;source&lt;/em&gt; is the project where the logs are being created and the &lt;em&gt;sink&lt;/em&gt; is our destination. In our configuration we installed Flume alongside our main project in OpenShift which periodically sends logs to our monitoring&amp;nbsp;service.&lt;/p&gt;</content><category term="automation"/></entry><entry><title>My course portfolio for Computational Photography</title><link href="https://adl1995.github.io/my-course-portfolio-for-computational-photography.html" rel="alternate"/><published>2022-04-15T14:22:00+02:00</published><updated>2022-04-15T14:22:00+02:00</updated><author><name>Adeel Ahmad</name></author><id>tag:adl1995.github.io,2022-04-15:/my-course-portfolio-for-computational-photography.html</id><summary type="html">&lt;p&gt;This semester I took Georgia Tech&amp;#8217;s &lt;a href="https://omscs.gatech.edu/cs-6475-computational-photography"&gt;Computational Photography&lt;/a&gt; course. It was a very hands-on course, mostly comprising of assignments and projects. This post includes the results for all its assignments and&amp;nbsp;projects.&lt;/p&gt;
&lt;h2&gt;1. Pyramid&amp;nbsp;blending&lt;/h2&gt;
&lt;p&gt;The goal of this assignment was to combine two separate images into a seamlessly blended image, using a mask. The input took a left image, a right image, and a mask, which was a binary image used to overlap the two&amp;nbsp;inputs.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Pyramid blending" src="https://adl1995.github.io/images/portfolio/pyramid_blending.png"&gt;&lt;/p&gt;
&lt;h2&gt;2.&amp;nbsp;Panoramas&lt;/h2&gt;
&lt;p&gt;For this assignment we wrote code to align &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt; stitch together a series of images into a panorama. We followed the text from &lt;a href="http://szeliski.org/Book/1stEdition.htm"&gt;Computer Vision: Algorithms and Applications&lt;/a&gt; book. The assignment followed homography techniques to create the output&amp;nbsp;picture.&lt;/p&gt;
&lt;p align="center"&gt;
    &lt;img src="https://adl1995.github.io/images/portfolio/panorama_1.jpg" alt="panorama_1" style="width:350px;"/&gt;
    &lt;img src="https://adl1995.github.io/images/portfolio/panorama_2.jpg" alt="panorama_2" style="width:350px;"/&gt;
    &lt;img src="https://adl1995.github.io/images/portfolio/panorama_3.jpg" alt="panorama_3" style="width:350px;"/&gt;
&lt;/p&gt;

&lt;p&gt;After stitching …&lt;/p&gt;</summary><content type="html">&lt;p&gt;This semester I took Georgia Tech&amp;#8217;s &lt;a href="https://omscs.gatech.edu/cs-6475-computational-photography"&gt;Computational Photography&lt;/a&gt; course. It was a very hands-on course, mostly comprising of assignments and projects. This post includes the results for all its assignments and&amp;nbsp;projects.&lt;/p&gt;
&lt;h2&gt;1. Pyramid&amp;nbsp;blending&lt;/h2&gt;
&lt;p&gt;The goal of this assignment was to combine two separate images into a seamlessly blended image, using a mask. The input took a left image, a right image, and a mask, which was a binary image used to overlap the two&amp;nbsp;inputs.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Pyramid blending" src="https://adl1995.github.io/images/portfolio/pyramid_blending.png"&gt;&lt;/p&gt;
&lt;h2&gt;2.&amp;nbsp;Panoramas&lt;/h2&gt;
&lt;p&gt;For this assignment we wrote code to align &lt;span class="amp"&gt;&amp;amp;&lt;/span&gt; stitch together a series of images into a panorama. We followed the text from &lt;a href="http://szeliski.org/Book/1stEdition.htm"&gt;Computer Vision: Algorithms and Applications&lt;/a&gt; book. The assignment followed homography techniques to create the output&amp;nbsp;picture.&lt;/p&gt;
&lt;p align="center"&gt;
    &lt;img src="https://adl1995.github.io/images/portfolio/panorama_1.jpg" alt="panorama_1" style="width:350px;"/&gt;
    &lt;img src="https://adl1995.github.io/images/portfolio/panorama_2.jpg" alt="panorama_2" style="width:350px;"/&gt;
    &lt;img src="https://adl1995.github.io/images/portfolio/panorama_3.jpg" alt="panorama_3" style="width:350px;"/&gt;
&lt;/p&gt;

&lt;p&gt;After stitching these all up, I got this&amp;nbsp;result.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Panorama" src="https://adl1995.github.io/images/portfolio/panorama_result.jpg"&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class="caps"&gt;PS&lt;/span&gt;. I took these pictures from the Salève mountain in&amp;nbsp;France.&lt;/p&gt;
&lt;h2&gt;3. &lt;span class="caps"&gt;HDR&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;This one required us to produce &lt;span class="caps"&gt;HDR&lt;/span&gt; images, using input images captured at various exposure&amp;nbsp;levels.&lt;/p&gt;
&lt;p align="center"&gt;
    &lt;img src="https://adl1995.github.io/images/portfolio/bamboo_all.jpg" alt="bamboo_all"/&gt;
    &lt;p align = "center"&gt;(Input&amp;nbsp;images)&lt;/p&gt;
&lt;/p&gt;

&lt;p&gt;For the output we produced a basic &lt;span class="caps"&gt;HDR&lt;/span&gt; image and it&amp;#8217;s best version, using histogram&amp;nbsp;equalization.&lt;/p&gt;
&lt;p align="center"&gt;
    &lt;img src="https://adl1995.github.io/images/portfolio/basic_hdr.png" alt="basic_hdr" style="width:350px;"/&gt;
    &lt;!-- &lt;p align = "center"&gt;(Basic&amp;nbsp;HDR)&lt;/p&gt; --&gt;
    &lt;img src="https://adl1995.github.io/images/portfolio/best_hdr.png" alt="best_hdr" style="width:350px;"/&gt;
    &lt;!-- &lt;p align = "center"&gt;(Best&amp;nbsp;HDR)&lt;/p&gt; --&gt;
&lt;/p&gt;

&lt;p&gt;Finally, we also created a color-enchanged version using Gamma&amp;nbsp;correction.&lt;/p&gt;
&lt;p&gt;&lt;img alt="HDR" src="https://adl1995.github.io/images/portfolio/best_hdr_color_enhanced.png"&gt;&lt;/p&gt;
&lt;h2&gt;4. Video&amp;nbsp;Textures&lt;/h2&gt;
&lt;p&gt;In this assignment we infinitely looped pieces of video. The output was a &lt;span class="caps"&gt;GIF&lt;/span&gt; with very smooth&amp;nbsp;transitions.&lt;/p&gt;
&lt;p align="center"&gt;
    &lt;img src="https://adl1995.github.io/images/portfolio/texture_frames.jpg" alt="texture_frames" style="width:400px;"/&gt;
&lt;/p&gt;

&lt;p&gt;This was the final result. (Not totally smooth, but&amp;nbsp;almost.)&lt;/p&gt;
&lt;p align="center"&gt;
    &lt;img src="https://adl1995.github.io/images/portfolio/salt_texture.gif" alt="salt_texture" style="width:300px;"/&gt;
&lt;/p&gt;

&lt;h2&gt;5. Image&amp;nbsp;Inpainting&lt;/h2&gt;
&lt;p&gt;This was our first project which required us to implement an image inpainting technique to remove objects from pictures. This is similar to the &lt;a href="https://androidgadget.org/google-pixel-6-magic-eraser-si-vous-pensez-que-cela-ne-fonctionne-pas-vous-lutilisez-mal/"&gt;Magic Eraser&lt;/a&gt; tool available on the Pixel 6. We replicated results from this &lt;a href="https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/criminisi_tip2004.pdf"&gt;research paper&lt;/a&gt;.&lt;/p&gt;
&lt;p align="center"&gt;
    &lt;img src="https://adl1995.github.io/images/portfolio/inpaint_input_2.jpg" alt="inpaint_input_2" style="width:350px;"/&gt;
    &lt;img src="https://adl1995.github.io/images/portfolio/inpaint_result_2.png" alt="inpaint_result_2" style="width:350px;"/&gt;
&lt;/p&gt;

&lt;p align="center"&gt;
    &lt;img src="https://adl1995.github.io/images/portfolio/inpaint_input_1.jpg" alt="inpaint_input_1" style="width:350px;"/&gt;
    &lt;img src="https://adl1995.github.io/images/portfolio/inpaint_result_1.png" alt="inpaint_result_1" style="width:350px;"/&gt;
&lt;/p&gt;

&lt;h2&gt;6. Seam&amp;nbsp;Carving&lt;/h2&gt;
&lt;p&gt;The final project was based on seam carving to shrink or expand images. We implemented the backward and forward energy methods from these two research&amp;nbsp;papers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.faculty.idc.ac.il/arik/SCWeb/imret/index.html"&gt;Seam Carving for Content-Aware Image Resizing.&amp;nbsp;(2007)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.faculty.idc.ac.il/arik/SCWeb/vidret/index.html"&gt;Improved Seam Carving for Video Retargeting.&amp;nbsp;(2008)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p align="center"&gt;
    &lt;img src="https://adl1995.github.io/images/portfolio/beach.png" alt="beach" style="width:450px;"/&gt;
    &lt;img src="https://adl1995.github.io/images/portfolio/beach_output.png" alt="beach_output" style="width:250px;"/&gt;
&lt;/p&gt;

&lt;p align="center"&gt;
    &lt;img src="https://adl1995.github.io/images/portfolio/bench.png" alt="bench" style="width:430px;"/&gt;
    &lt;img src="https://adl1995.github.io/images/portfolio/bench_output.png" alt="bench_output" style="width:250px;"/&gt;
&lt;/p&gt;

&lt;p&gt;The red lines in this image show where the pixels were removed in the output image. These are points where the removal would have the least effect in the output&amp;nbsp;image.&lt;/p&gt;
&lt;p align="center"&gt;
    &lt;img src="https://adl1995.github.io/images/portfolio/bench_output_red.png" alt="bench_output_red" style="width:500px;"/&gt;
&lt;/p&gt;</content><category term="image-processing"/></entry><entry><title>Building a Camera Obscura</title><link href="https://adl1995.github.io/building-a-camera-obscura.html" rel="alternate"/><published>2022-01-29T11:02:00+01:00</published><updated>2022-01-29T11:02:00+01:00</updated><author><name>Adeel Ahmad</name></author><id>tag:adl1995.github.io,2022-01-29:/building-a-camera-obscura.html</id><summary type="html">&lt;p&gt;A &lt;a href="https://en.wikipedia.org/wiki/Camera_obscura"&gt;camera obscura&lt;/a&gt; is the predecessor of modern day cameras. It works by letting light in through a small pinhole and projects it onto a surface (e.g. a&amp;nbsp;wall).&lt;/p&gt;
&lt;p&gt;To build a room obscura we need to choose a room which gets plenty of sunlight. I chose my bedroom for&amp;nbsp;this.&lt;/p&gt;
&lt;p&gt;&lt;img alt="scene" src="https://adl1995.github.io/images/obscura/scene.jpg"&gt;&lt;/p&gt;
&lt;p&gt;Next we need to identify all the sources of light and completely seal them. In my case I used garbage bags to cover my two&amp;nbsp;windows.&lt;/p&gt;
&lt;p align="center"&gt;
    &lt;img src="https://adl1995.github.io/images/obscura/setup_2.jpg" alt="setup_2" style="width:350px;"/&gt;
    &lt;img src="https://adl1995.github.io/images/obscura/setup_3.jpg" alt="setup_3" style="width:350px;"/&gt;
&lt;/p&gt;

&lt;p&gt;I used packing tape instead of duct tape as it can tear off paint from the wood. However, this led to some light bleeding in from the edges. I fixed this using aluminum&amp;nbsp;foil.&lt;/p&gt;
&lt;p&gt;&lt;img alt="failed" src="https://adl1995.github.io/images/obscura/failed.jpg"&gt;&lt;/p&gt;
&lt;p&gt;I experimented with three pinholes of various&amp;nbsp;sizes …&lt;/p&gt;</summary><content type="html">&lt;p&gt;A &lt;a href="https://en.wikipedia.org/wiki/Camera_obscura"&gt;camera obscura&lt;/a&gt; is the predecessor of modern day cameras. It works by letting light in through a small pinhole and projects it onto a surface (e.g. a&amp;nbsp;wall).&lt;/p&gt;
&lt;p&gt;To build a room obscura we need to choose a room which gets plenty of sunlight. I chose my bedroom for&amp;nbsp;this.&lt;/p&gt;
&lt;p&gt;&lt;img alt="scene" src="https://adl1995.github.io/images/obscura/scene.jpg"&gt;&lt;/p&gt;
&lt;p&gt;Next we need to identify all the sources of light and completely seal them. In my case I used garbage bags to cover my two&amp;nbsp;windows.&lt;/p&gt;
&lt;p align="center"&gt;
    &lt;img src="https://adl1995.github.io/images/obscura/setup_2.jpg" alt="setup_2" style="width:350px;"/&gt;
    &lt;img src="https://adl1995.github.io/images/obscura/setup_3.jpg" alt="setup_3" style="width:350px;"/&gt;
&lt;/p&gt;

&lt;p&gt;I used packing tape instead of duct tape as it can tear off paint from the wood. However, this led to some light bleeding in from the edges. I fixed this using aluminum&amp;nbsp;foil.&lt;/p&gt;
&lt;p&gt;&lt;img alt="failed" src="https://adl1995.github.io/images/obscura/failed.jpg"&gt;&lt;/p&gt;
&lt;p&gt;I experimented with three pinholes of various&amp;nbsp;sizes.&lt;/p&gt;
&lt;p&gt;&lt;img alt="pinhole" src="https://adl1995.github.io/images/obscura/pinhole.jpg"&gt;&lt;/p&gt;
&lt;p&gt;After trapping out all the light I mounted my camera next to my pinhole and captured the&amp;nbsp;image.&lt;/p&gt;
&lt;p&gt;&lt;img alt="setup" src="https://adl1995.github.io/images/obscura/setup.jpg"&gt;&lt;/p&gt;
&lt;p&gt;This was the best result I could get using a pinhole of 0.4&amp;nbsp;inches.&lt;/p&gt;
&lt;p&gt;&lt;img alt="original_image_1" src="https://adl1995.github.io/images/obscura/original_image_1.jpg"&gt;&lt;/p&gt;
&lt;h3&gt;Lessons&amp;nbsp;learned&lt;/h3&gt;
&lt;p&gt;It is extremely important to perform this experiment on a sunny day, otherwise you will not get a clear&amp;nbsp;image.&lt;/p&gt;
&lt;p&gt;You should also experiment with different focal lengths, i.e. adjust the distance between your pinhole and the screen. This can produce a sharper&amp;nbsp;image.&lt;/p&gt;</content><category term="image-processing"/></entry><entry><title>Passwordless logins with Yubikey</title><link href="https://adl1995.github.io/passwordless-logins-with-yubikey.html" rel="alternate"/><published>2020-11-29T07:50:00+01:00</published><updated>2020-11-29T07:50:00+01:00</updated><author><name>Adeel Ahmad</name></author><id>tag:adl1995.github.io,2020-11-29:/passwordless-logins-with-yubikey.html</id><summary type="html">&lt;p&gt;Yubikey is currently the de facto device for &lt;span class="caps"&gt;U2F&lt;/span&gt; authentication. It enables adding an extra layer of security on top of &lt;span class="caps"&gt;SSH&lt;/span&gt;, system login, signing &lt;span class="caps"&gt;GPG&lt;/span&gt; keys, and so on. It is also compatible with several other authentication methods, such as WebAuthn and &lt;span class="caps"&gt;PAM&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;This post will show how to leverage your Yubikey for unlocking the system lock-screen, both with and without using a password. It will then delve into how to automatically lock the screen when the Yubikey is&amp;nbsp;unplugged.&lt;/p&gt;
&lt;p&gt;To achieve logins with Yubikeys we require a &lt;span class="caps"&gt;PAM&lt;/span&gt; configuration. &lt;span class="caps"&gt;PAM&lt;/span&gt; or Pluggable Authentication Modules define the authentication flow for common Linux utilities, such&amp;nbsp;as &lt;code&gt;sudo&lt;/code&gt;, &lt;code&gt;su&lt;/code&gt;,&amp;nbsp;and &lt;code&gt;passwd&lt;/code&gt;. We will override the default authentication flow for the &lt;a href="https://linux.die.net/man/1/xlock"&gt;xlock …&lt;/a&gt;&lt;/p&gt;</summary><content type="html">&lt;p&gt;Yubikey is currently the de facto device for &lt;span class="caps"&gt;U2F&lt;/span&gt; authentication. It enables adding an extra layer of security on top of &lt;span class="caps"&gt;SSH&lt;/span&gt;, system login, signing &lt;span class="caps"&gt;GPG&lt;/span&gt; keys, and so on. It is also compatible with several other authentication methods, such as WebAuthn and &lt;span class="caps"&gt;PAM&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;This post will show how to leverage your Yubikey for unlocking the system lock-screen, both with and without using a password. It will then delve into how to automatically lock the screen when the Yubikey is&amp;nbsp;unplugged.&lt;/p&gt;
&lt;p&gt;To achieve logins with Yubikeys we require a &lt;span class="caps"&gt;PAM&lt;/span&gt; configuration. &lt;span class="caps"&gt;PAM&lt;/span&gt; or Pluggable Authentication Modules define the authentication flow for common Linux utilities, such&amp;nbsp;as &lt;code&gt;sudo&lt;/code&gt;, &lt;code&gt;su&lt;/code&gt;,&amp;nbsp;and &lt;code&gt;passwd&lt;/code&gt;. We will override the default authentication flow for the &lt;a href="https://linux.die.net/man/1/xlock"&gt;xlock&lt;/a&gt; lock manager to allow logins with&amp;nbsp;Yubikey.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: The above process should be similar across most lock managers, such&amp;nbsp;as &lt;code&gt;i3lock&lt;/code&gt; or &lt;code&gt;xscreensaver&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Creating a &lt;span class="caps"&gt;PAM&lt;/span&gt;&amp;nbsp;configuration&lt;/h2&gt;
&lt;p&gt;We shall first replicate the default authentication provided with xlock using &lt;span class="caps"&gt;PAM&lt;/span&gt;. With this configuration the user should only be able to log in with their&amp;nbsp;password.&lt;/p&gt;
&lt;p&gt;All &lt;span class="caps"&gt;PAM&lt;/span&gt; configuration files lie under&amp;nbsp;the &lt;code&gt;/etc/pam.d/&lt;/code&gt; directory. We create a file&amp;nbsp;named &lt;code&gt;xlock&lt;/code&gt; which replicates the default&amp;nbsp;authentication:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;/etc/pam.d/xlock
&lt;span class="c1"&gt;#%PAM-1.0&lt;/span&gt;
auth&lt;span class="w"&gt;            &lt;/span&gt;include&lt;span class="w"&gt;         &lt;/span&gt;system-auth
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: For the above configuration file to take effect the tool&amp;nbsp;(&lt;code&gt;xlock&lt;/code&gt;) must be &lt;span class="caps"&gt;PAM&lt;/span&gt; compatible. We can confirm&amp;nbsp;that &lt;code&gt;xlock&lt;/code&gt; is &lt;span class="caps"&gt;PAM&lt;/span&gt; compatible by inspecting the output&amp;nbsp;of &lt;code&gt;ldd /usr/bin/xlock | grep libpam.so&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The first comment line indicates the &lt;span class="caps"&gt;PAM&lt;/span&gt; version. The lines that follow define the authentication&amp;nbsp;flow:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;auth&lt;/code&gt; is the module interface responsible for verifying the user&amp;#8217;s&amp;nbsp;password.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;include&lt;/code&gt; is the &lt;span class="caps"&gt;PAM&lt;/span&gt; control flag which &lt;em&gt;includes&lt;/em&gt;&amp;nbsp;the &lt;code&gt;system-auth&lt;/code&gt; configuration file (this file defines the default authentication flow). This flag can also be used to load modules, as we shall see&amp;nbsp;later.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: There is an excellent &lt;a href="https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/managing_smart_cards/pam_configuration_files"&gt;documentation&lt;/a&gt; provided by RedHat on &lt;span class="caps"&gt;PAM&lt;/span&gt; configuration&amp;nbsp;files.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Supporting Yubikey&amp;nbsp;logins&lt;/h3&gt;
&lt;p&gt;We shall now add Yubikey login functionality to our &lt;span class="caps"&gt;PAM&lt;/span&gt; configuration, but we first need to install the Yubico module for &lt;span class="caps"&gt;PAM&lt;/span&gt; and set it&amp;nbsp;up.&lt;/p&gt;
&lt;p&gt;Yubico, the company behind Yubikeys, exposes the &lt;a href="https://developers.yubico.com/yubico-pam/"&gt;pam_yubico.so&lt;/a&gt; module which can be used for Yubikey&amp;nbsp;authentication.&lt;/p&gt;
&lt;p&gt;It provides two authentication mechanisms,&amp;nbsp;the &lt;code&gt;client&lt;/code&gt; mode and&amp;nbsp;the &lt;code&gt;challenge-response&lt;/code&gt; mode.&amp;nbsp;The &lt;code&gt;client&lt;/code&gt; mode sends a request to the Yubico server for verifying the user&amp;#8217;s &lt;span class="caps"&gt;OTP&lt;/span&gt;, and requires an active Internet connection for the user to login. As this is inconvenient we shall only explore&amp;nbsp;the &lt;code&gt;challenge-response&lt;/code&gt; mode in this&amp;nbsp;post.&lt;/p&gt;
&lt;p&gt;Before proceeding with the configuration&amp;nbsp;the &lt;code&gt;pam_yubico&lt;/code&gt; package must be installed manually. This package is easily available across most Linux distributions. On Arch Linux it can be installed&amp;nbsp;with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;pacman&lt;span class="w"&gt; &lt;/span&gt;-S&lt;span class="w"&gt; &lt;/span&gt;yubico-pam
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We next add Yubikey mappings before setting the challenge-response&amp;nbsp;credential.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Warning: It is recommended that you use a secondary account to perform the next steps as there is a risk of permanently locking your account (in case of &lt;span class="caps"&gt;PAM&lt;/span&gt;&amp;nbsp;misconfiguration).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;Adding&amp;nbsp;mappings&lt;/h4&gt;
&lt;p&gt;Each Yubikey must be paired with a unique public &lt;span class="caps"&gt;ID&lt;/span&gt; which&amp;nbsp;the &lt;code&gt;pam_yubico&lt;/code&gt; module uses to uniquely identify the user. The public &lt;span class="caps"&gt;ID&lt;/span&gt; consists of the first 12 characters extracted from the &lt;span class="caps"&gt;OTP&lt;/span&gt;&amp;nbsp;token.&lt;/p&gt;
&lt;p&gt;To obtain your Yubikey&amp;#8217;s public &lt;span class="caps"&gt;ID&lt;/span&gt; open up your shell and press the Yubikey button. You will see a similar output as&amp;nbsp;below:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;vvctffbvkhdnliklfhbbfiecudthfvrvuhnhtirehidr
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now copy take the first 12 characters&amp;nbsp;(&lt;code&gt;vvctffbvkhdn&lt;/code&gt;) and add them to a file&amp;nbsp;named &lt;code&gt;yubikey_mappings&lt;/code&gt; in&amp;nbsp;the &lt;code&gt;/etc/&lt;/code&gt; directory, along with your username. In our case this will&amp;nbsp;be:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;/etc/yubikey_mappings
adeel:vvctffbvkhdn
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: This file also allows specifying multiple Yubikey mappings, each separated by a new&amp;nbsp;line.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;Setting the challenge response&amp;nbsp;credential&lt;/h4&gt;
&lt;p&gt;Yubikey needs to somehow verify the generated &lt;span class="caps"&gt;OTP&lt;/span&gt; (One Time Password) when it tries to authenticate the user. It does so by using&amp;nbsp;the &lt;code&gt;challenge-response&lt;/code&gt; mode.&lt;/p&gt;
&lt;p&gt;To set up&amp;nbsp;the &lt;code&gt;challenge-response&lt;/code&gt; mode, we first need to install the Yubikey manager tool&amp;nbsp;called &lt;code&gt;ykman&lt;/code&gt;. On Arch Linux it can be installed&amp;nbsp;with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;pacman&lt;span class="w"&gt; &lt;/span&gt;-S&lt;span class="w"&gt; &lt;/span&gt;yubikey-manger
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;ykman&lt;/code&gt; tool will generate a secret credential and store it in a local file. Whenever the user tries to login&amp;nbsp;with &lt;code&gt;xlock&lt;/code&gt;,&amp;nbsp;the &lt;code&gt;pam_yubico&lt;/code&gt; module will verify the generated &lt;span class="caps"&gt;OTP&lt;/span&gt; against the stored&amp;nbsp;credential.&lt;/p&gt;
&lt;p&gt;The challenge response credential can be set on slot 2 of the Yubikey&amp;nbsp;with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;ykman&lt;span class="w"&gt; &lt;/span&gt;otp&lt;span class="w"&gt; &lt;/span&gt;chalresp&lt;span class="w"&gt; &lt;/span&gt;--generate&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;
Using&lt;span class="w"&gt; &lt;/span&gt;a&lt;span class="w"&gt; &lt;/span&gt;randomly&lt;span class="w"&gt; &lt;/span&gt;generated&lt;span class="w"&gt; &lt;/span&gt;key:&lt;span class="w"&gt; &lt;/span&gt;29eb38b6f50b246c46f954af9710a77c78792114
Program&lt;span class="w"&gt; &lt;/span&gt;a&lt;span class="w"&gt; &lt;/span&gt;challenge-response&lt;span class="w"&gt; &lt;/span&gt;credential&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;slot&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;?&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;y/N&lt;span class="o"&gt;]&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;y
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Warning: Ensure that the slot you&amp;#8217;re writing the data to doesn&amp;#8217;t already contain any credential, as it might not be&amp;nbsp;recoverable!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;After&amp;nbsp;the &lt;code&gt;challenge-response&lt;/code&gt; credential is set it needs to be written to a local file which will be later read&amp;nbsp;by &lt;code&gt;pam_yubico&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Yubico provides another tool&amp;nbsp;called &lt;code&gt;ykpamcfg&lt;/code&gt; (which should be bundled with&amp;nbsp;the &lt;code&gt;yubikey-manger&lt;/code&gt; package) to write this file to disk. It takes the Yubikey slot number as its parameter and writes the secret to a&amp;nbsp;file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;ykpamcfg&lt;span class="w"&gt; &lt;/span&gt;-2
Stored&lt;span class="w"&gt; &lt;/span&gt;initial&lt;span class="w"&gt; &lt;/span&gt;challenge&lt;span class="w"&gt; &lt;/span&gt;and&lt;span class="w"&gt; &lt;/span&gt;expected&lt;span class="w"&gt; &lt;/span&gt;response&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/home/adeel/.yubico/challenge-&amp;lt;Serial ID&amp;gt;&amp;#39;&lt;/span&gt;.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Updating the Linux &lt;span class="caps"&gt;PAM&lt;/span&gt;&amp;nbsp;configuration&lt;/h3&gt;
&lt;p&gt;We shall now update&amp;nbsp;the &lt;code&gt;/etc/pam.d/xlock&lt;/code&gt; file and add the Yubico &lt;span class="caps"&gt;PAM&lt;/span&gt; at the very&amp;nbsp;beginning.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;/etc/pam.d/xlock
&lt;span class="c1"&gt;#%PAM-1.0&lt;/span&gt;

auth&lt;span class="w"&gt;  &lt;/span&gt;sufficient&lt;span class="w"&gt;  &lt;/span&gt;pam_yubico.so&lt;span class="w"&gt; &lt;/span&gt;debug&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;challenge-response&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;authfile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/etc/yubikey_mappings
auth&lt;span class="w"&gt;  &lt;/span&gt;include&lt;span class="w"&gt;     &lt;/span&gt;system-auth
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We pass three parameters to&amp;nbsp;the &lt;code&gt;pam_yubico.so&lt;/code&gt; module:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;debug&lt;/code&gt; prints all the authentication steps to the console when the &amp;#8216;Enter&amp;#8217; key is&amp;nbsp;pressed.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mode&lt;/code&gt; specifies which mode the module will use for authentication&amp;nbsp;(&lt;code&gt;challenge-response&lt;/code&gt; or &lt;code&gt;client&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;authfile&lt;/code&gt; points to the credential file written by&amp;nbsp;the &lt;code&gt;ykpamcfg&lt;/code&gt; tool. &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Setting the module type&amp;nbsp;to &lt;code&gt;sufficient&lt;/code&gt; means that if Yubikey authentication succeeds, no further steps will be processed and the user will get logged in. This is the key point which enables passwordless logins. However, in the event of authentication failure, remaining authentication steps will still be applied, i.e. the user can still log in with their password if the Yubikey is not plugged&amp;nbsp;in.&lt;/p&gt;
&lt;p&gt;If the module type is set&amp;nbsp;to &lt;code&gt;required&lt;/code&gt; instead&amp;nbsp;of &lt;code&gt;sufficient&lt;/code&gt; it will enable Two-Factor Authentication (&lt;span class="caps"&gt;2FA&lt;/span&gt;) which will require the user to plug in their Yubikey &lt;em&gt;and&lt;/em&gt; enter their password to&amp;nbsp;login.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: For passwordless logins the user will need to press&amp;nbsp;the &lt;code&gt;Enter&lt;/code&gt; with their Yubikey plugged in to unlock their&amp;nbsp;screen.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;At this stage you should be able unlock your screen with they&amp;nbsp;Yubikey.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: You may need to replug your Yubikey for the changes to take&amp;nbsp;effect.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Automatically locking the screen when Yubikey is&amp;nbsp;unplugged&lt;/h2&gt;
&lt;p&gt;Up till locking the screen still requires manually invoking&amp;nbsp;the &lt;code&gt;xlock&lt;/code&gt; command. It would be nice if we can somehow automatically lock the screen whenever our Yubikey is unplugged. We can achieve this with&amp;nbsp;Udev.&lt;/p&gt;
&lt;p&gt;Udev is the device manager used in Linux which can be used for a myriad of tasks. It tracks the state changes for all external devices, for example, it can be used to identify when a &lt;span class="caps"&gt;USB&lt;/span&gt; device is plugged or unplugged. Each device outputs a series of attributes which can be used to uniquely identify&amp;nbsp;it.&lt;/p&gt;
&lt;p&gt;We shall use these attributes to create a Udev rule which triggers&amp;nbsp;an &lt;code&gt;xlock.service&lt;/code&gt; Systemd service when the Yubikey is&amp;nbsp;unplugged.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: We can also achieve this with a Shell script instead of Systemd, but Udev discourages executing long-running programs using scripts as it terminates them after a certain time&amp;nbsp;period.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Creating the Systemd&amp;nbsp;service&lt;/h3&gt;
&lt;p&gt;Systemd is the Linux service manager which can be used to launch user processes. We create a file&amp;nbsp;named &lt;code&gt;xlock.service&lt;/code&gt; in&amp;nbsp;the &lt;code&gt;/etc/systemd/system/&lt;/code&gt; directory:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;/etc/systemd/system/xlock.service
&lt;span class="o"&gt;[&lt;/span&gt;Unit&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;Description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;xlock

&lt;span class="o"&gt;[&lt;/span&gt;Service&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;User&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;adeel
&lt;span class="nv"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;simple
&lt;span class="nv"&gt;Environment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;DISPLAY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;:0
&lt;span class="nv"&gt;ExecStart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/bin/xlock&lt;span class="w"&gt; &lt;/span&gt;

&lt;span class="o"&gt;[&lt;/span&gt;Install&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;WantedBy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;multi-user.target
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;Type=simple&lt;/code&gt; implies that this service does not exit after&amp;nbsp;execution.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;Environment&lt;/code&gt; tag specifies which display should be locked&amp;nbsp;(&lt;code&gt;0&lt;/code&gt; is the default&amp;nbsp;display).&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;ExecStart&lt;/code&gt; tag takes a path of the binary or script it will&amp;nbsp;execute.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: Consult the &lt;a href="https://www.freedesktop.org/software/systemd/man/systemd.service.html"&gt;official docs&lt;/a&gt; to explore Systemd in&amp;nbsp;detail.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Creating the Udev&amp;nbsp;rule&lt;/h3&gt;
&lt;p&gt;We first need to identify a set of unique attributes for our device (Yubikey).&amp;nbsp;The &lt;code&gt;udevadm&lt;/code&gt; tool allows monitoring Udev output whenever a device state changes. We shall invoke the following command and then remove our&amp;nbsp;Yubikey:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;udevadm&lt;span class="w"&gt; &lt;/span&gt;monitor&lt;span class="w"&gt; &lt;/span&gt;--environment&lt;span class="w"&gt; &lt;/span&gt;--udev&lt;span class="w"&gt; &lt;/span&gt;
monitor&lt;span class="w"&gt; &lt;/span&gt;will&lt;span class="w"&gt; &lt;/span&gt;print&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;received&lt;span class="w"&gt; &lt;/span&gt;events&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;:
UDEV&lt;span class="w"&gt; &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;event&lt;span class="w"&gt; &lt;/span&gt;which&lt;span class="w"&gt; &lt;/span&gt;udev&lt;span class="w"&gt; &lt;/span&gt;sends&lt;span class="w"&gt; &lt;/span&gt;out&lt;span class="w"&gt; &lt;/span&gt;after&lt;span class="w"&gt; &lt;/span&gt;rule&lt;span class="w"&gt; &lt;/span&gt;processing

UDEV&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="m"&gt;461872&lt;/span&gt;.738673&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;remove&lt;span class="w"&gt;   &lt;/span&gt;/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2.1/1-2.1:1.0/0003:1050:0407.0157/input/input294/event18&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;input&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;ACTION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;remove
&lt;span class="nv"&gt;ID_VENDOR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Yubico
&lt;span class="nv"&gt;SUBSYSTEM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;input
&lt;span class="nv"&gt;DEVNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/input/event18
&lt;span class="nv"&gt;ID_INPUT_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We only show a truncated output above, but once you have identified the attributes you would like to use, create a file&amp;nbsp;named &lt;code&gt;yubikey-actions.rules&lt;/code&gt; in&amp;nbsp;the &lt;code&gt;/etc/udev/rules.d/&lt;/code&gt; directory:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;/etc/udev/rules.d/yubikey-actions.rules&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="nv"&gt;ACTION&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;remove&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;ENV&lt;span class="o"&gt;{&lt;/span&gt;ID_MODEL_ID&lt;span class="o"&gt;}==&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;0407&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;ENV&lt;span class="o"&gt;{&lt;/span&gt;ID_VENDOR_ID&lt;span class="o"&gt;}==&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;1050&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;RUN&lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;/usr/bin/systemctl start xlock.service&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It might be worthwhile to reload the configuration for both Systemd and&amp;nbsp;Udev:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;systemctl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;daemon&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;reload&lt;/span&gt;
&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;udevadm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;control&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;reload&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If everything worked out fine your screen should now get locked whenever you remove your&amp;nbsp;Yubikey.&lt;/p&gt;
&lt;h2&gt;References:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://fedoraproject.org/wiki/Using_Yubikeys_with_Fedora#Using_a_Yubikey_to_authenticate_to_a_machine_running_Fedora"&gt;Using a Yubikey to authenticate to a machine running&amp;nbsp;Fedora&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ocramius.github.io/blog/yubikey-for-ssh-gpg-git-and-local-login/"&gt;YubiKey for &lt;span class="caps"&gt;SSH&lt;/span&gt;, Login, &lt;span class="caps"&gt;2FA&lt;/span&gt;, &lt;span class="caps"&gt;GPG&lt;/span&gt; and Git&amp;nbsp;Signing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://blog.fraggod.net/2015/01/12/starting-systemd-service-instance-for-device-from-udev.html"&gt;Starting systemd service instance for device from&amp;nbsp;udev&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><category term="automation"/></entry><entry><title>Plotting graphical data using RRDtool and a Python Collectd plugin</title><link href="https://adl1995.github.io/plotting-graphical-data-using-rrdtool-and-a-python-collectd-plugin.html" rel="alternate"/><published>2020-05-11T11:02:00+02:00</published><updated>2020-05-11T11:02:00+02:00</updated><author><name>Adeel Ahmad</name></author><id>tag:adl1995.github.io,2020-05-11:/plotting-graphical-data-using-rrdtool-and-a-python-collectd-plugin.html</id><summary type="html">&lt;!-- Date: 2019-05-30 07:57 --&gt;
&lt;!-- Title: Using RRDtool and a Python Collectd plugin to graph weather data --&gt;
&lt;!-- Title: Plotting graphs data from a Python Collectd plugin using RRDtool --&gt;

&lt;p&gt;Collectd is Unix daemon used for periodically collecting system usage statistics, which can include identifying &lt;span class="caps"&gt;CPU&lt;/span&gt; or memory bottleneck issues. The collected data can then be transformed to graphs using &lt;a href="https://oss.oetiker.ch/rrdtool"&gt;RRDtool&lt;/a&gt; or a &lt;a href="https://en.wikipedia.org/wiki/Grafana"&gt;Grafana&lt;/a&gt; dashboard (Grafana provides real time graphs and complex search&amp;nbsp;queries).&lt;/p&gt;
&lt;p&gt;The daemon itself is modular and functions through external plugins with each plugin performing a distinct function. This post will explore a plugin which &lt;em&gt;collects weather information of a given city&lt;/em&gt;. The first section will explain how the plugin configuration works and how to plot a graph of the output data using RRDtool. Finally, we will delve into the plugin internals and see how it is&amp;nbsp;written.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: For an intro on how to setup Collectd …&lt;/p&gt;&lt;/blockquote&gt;</summary><content type="html">&lt;!-- Date: 2019-05-30 07:57 --&gt;
&lt;!-- Title: Using RRDtool and a Python Collectd plugin to graph weather data --&gt;
&lt;!-- Title: Plotting graphs data from a Python Collectd plugin using RRDtool --&gt;

&lt;p&gt;Collectd is Unix daemon used for periodically collecting system usage statistics, which can include identifying &lt;span class="caps"&gt;CPU&lt;/span&gt; or memory bottleneck issues. The collected data can then be transformed to graphs using &lt;a href="https://oss.oetiker.ch/rrdtool"&gt;RRDtool&lt;/a&gt; or a &lt;a href="https://en.wikipedia.org/wiki/Grafana"&gt;Grafana&lt;/a&gt; dashboard (Grafana provides real time graphs and complex search&amp;nbsp;queries).&lt;/p&gt;
&lt;p&gt;The daemon itself is modular and functions through external plugins with each plugin performing a distinct function. This post will explore a plugin which &lt;em&gt;collects weather information of a given city&lt;/em&gt;. The first section will explain how the plugin configuration works and how to plot a graph of the output data using RRDtool. Finally, we will delve into the plugin internals and see how it is&amp;nbsp;written.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: For an intro on how to setup Collectd, refer to this &lt;a href="https://collectd.org/wiki/index.php/First_steps"&gt;wiki&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Plugin&amp;nbsp;configuration&lt;/h2&gt;
&lt;p&gt;All enabled Collectd plugins must be listed in&amp;nbsp;the &lt;code&gt;collectd.conf&lt;/code&gt; file. The default file paths used by Collectd can be viewed&amp;nbsp;with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;collectd&lt;span class="w"&gt; &lt;/span&gt;-h

Builtin&lt;span class="w"&gt; &lt;/span&gt;defaults:
&lt;span class="w"&gt;  &lt;/span&gt;Config&lt;span class="w"&gt; &lt;/span&gt;file&lt;span class="w"&gt;       &lt;/span&gt;/etc/collectd.conf
&lt;span class="w"&gt;  &lt;/span&gt;PID&lt;span class="w"&gt; &lt;/span&gt;file&lt;span class="w"&gt;          &lt;/span&gt;/var/run/collectd.pid
&lt;span class="w"&gt;  &lt;/span&gt;Plugin&lt;span class="w"&gt; &lt;/span&gt;directory&lt;span class="w"&gt;  &lt;/span&gt;/usr/lib/collectd
&lt;span class="w"&gt;  &lt;/span&gt;Data&lt;span class="w"&gt; &lt;/span&gt;directory&lt;span class="w"&gt;    &lt;/span&gt;/var/lib/collectd
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The plugins lie under&amp;nbsp;the &lt;code&gt;/usr/lib/collectd&lt;/code&gt; directory, by&amp;nbsp;default.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: Python plugins might not work when placed under the default plugin directory, as this causes anomalies when importing external&amp;nbsp;modules.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Invoking the&amp;nbsp;plugin&lt;/h3&gt;
&lt;p&gt;Let&amp;#8217;s say we have a script&amp;nbsp;named &lt;code&gt;log-weather.py&lt;/code&gt; which we want to periodically call using Collectd. We first need to change its path to our custom plugins&amp;nbsp;directory:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;cp&lt;span class="w"&gt; &lt;/span&gt;/path/to/log-weather.py&lt;span class="w"&gt; &lt;/span&gt;/usr/lib/collectd/python/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Next, we will add this plugin to&amp;nbsp;our &lt;code&gt;/etc/collectd.conf&lt;/code&gt; config file. We also need to load the Python plugin (this exposes&amp;nbsp;the &lt;code&gt;collectd&lt;/code&gt; Python module which we then import in our&amp;nbsp;script):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cat&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;collectd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;
&lt;span class="n"&gt;LoadPlugin&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;python&lt;/span&gt;
&lt;span class="na"&gt;&amp;lt;Plugin python&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;ModulePath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;/usr/lib/collectd/python/&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;Import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;log-weather&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;Module&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;weather_logger&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;CITY&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;lt;city&amp;gt;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;API_KEY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;lt;api_key&amp;gt;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;Module&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="na"&gt;&amp;lt;/Plugin&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The import structure remains similar for all Python plugins, except for the module path, the module name, and its configuration&amp;nbsp;parameters.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Import&lt;/code&gt; directive: This takes the script name as its value. Notice how this differs from the module name in the following line. We&amp;#8217;ll see this in detail in a later &lt;a href="#weather-logger-plugin"&gt;section&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Module&lt;/code&gt; tag: This module requires two configuration&amp;nbsp;parameters, &lt;code&gt;CITY&lt;/code&gt; and &lt;code&gt;API_KEY&lt;/code&gt;. It calls an external &lt;span class="caps"&gt;API&lt;/span&gt; with the given key to collect the weather&amp;nbsp;information.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Enabling&amp;nbsp;RRDtool&lt;/h4&gt;
&lt;p&gt;As we intend to plot the collected data at the end, we will enable the RRDtool plugin. This plugin enables writing the data dispatched by our script to an &lt;span class="caps"&gt;RRD&lt;/span&gt; (Round Robin Database)&amp;nbsp;file.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;LoadPlugin&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rrdtool&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;Setting the call&amp;nbsp;interval&lt;/h4&gt;
&lt;p&gt;In&amp;nbsp;our &lt;code&gt;/etc/collectd.conf&lt;/code&gt; file we can tweak the frequency by which our plugin is called with&amp;nbsp;the &lt;code&gt;Interval&lt;/code&gt; directive. This expects a value in&amp;nbsp;seconds:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Interval 1800
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Our script will now collect the weather information every 1800 seconds, or every half an&amp;nbsp;hour.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: It is also possible to set the interval for each plugin separately. This requires setting the interval in&amp;nbsp;the &lt;code&gt;register_read&lt;/code&gt; function, which we will see&amp;nbsp;later.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Starting the Collectd&amp;nbsp;service&lt;/h3&gt;
&lt;p&gt;Once the configuration is done, we must (re)start the Collectd service&amp;nbsp;using &lt;code&gt;systemd&lt;/code&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: this step might be different based on your Linux distribution. Consult the &lt;a href="https://collectd.org/wiki/index.php/First_steps"&gt;Collectd Wiki&lt;/a&gt; for more&amp;nbsp;details.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Enabling the&amp;nbsp;Collectd &lt;code&gt;systemd&lt;/code&gt; service will start Collectd when the user logs in on their&amp;nbsp;computer.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;systemctl&lt;span class="w"&gt; &lt;/span&gt;start&lt;span class="w"&gt; &lt;/span&gt;collectd
$&lt;span class="w"&gt; &lt;/span&gt;systemctl&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;enable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;collectd
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Plotting the&amp;nbsp;data&lt;/h3&gt;
&lt;p&gt;As soon as the Collectd service is running, data will be written to the &lt;span class="caps"&gt;RRD&lt;/span&gt; file using the RRDtool plugin which we enabled earlier. This data is stored inside the data directory, which in our case&amp;nbsp;is &lt;code&gt;/var/lib/collectd/home/weather_alert/temperature.rrd&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To plot this data we will use&amp;nbsp;the &lt;code&gt;rrdtool&lt;/code&gt; Linux utility. (This tool is different from the one used by Collectd, which uses&amp;nbsp;the &lt;code&gt;librrd&lt;/code&gt; &lt;span class="caps"&gt;API&lt;/span&gt; for writing the data.) We will first view the information written to&amp;nbsp;the &lt;code&gt;temperature.rrd&lt;/code&gt; file&amp;nbsp;with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rrdtool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rrd&lt;/span&gt;
&lt;span class="n"&gt;rrd_version&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;0003&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="n"&gt;last_update&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1589090986&lt;/span&gt;
&lt;span class="n"&gt;header_size&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3496&lt;/span&gt;
&lt;span class="n"&gt;ds&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;index&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;ds&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;&amp;quot;GAUGE&amp;quot;&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And then proceed with creating the&amp;nbsp;graph:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;rrdtool&lt;span class="w"&gt; &lt;/span&gt;graph&lt;span class="w"&gt; &lt;/span&gt;weather.png&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="c1"&gt;# (1)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;--title&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Weather (Athens)&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="c1"&gt;# (2)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;--vertical-label&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Temperature (°C)&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="c1"&gt;# (3)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;--start&lt;span class="w"&gt; &lt;/span&gt;now-8000s&lt;span class="w"&gt; &lt;/span&gt;--end&lt;span class="w"&gt; &lt;/span&gt;now&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;# (4)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;DEF:a&lt;span class="o"&gt;=&lt;/span&gt;temperature.rrd:value:AVERAGE&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="c1"&gt;# (5)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It produces this&amp;nbsp;result:&lt;/p&gt;
&lt;p align="center"&gt;
    &lt;img src="https://adl1995.github.io/images/weather-rrdtool.png" style="width:600px;"/&gt;
&lt;/p&gt;

&lt;p&gt;Let&amp;#8217;s go over the above command in&amp;nbsp;detail:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We specify the name for the output&amp;nbsp;graph.&lt;/li&gt;
&lt;li&gt;This title appears at the top of the&amp;nbsp;graph.&lt;/li&gt;
&lt;li&gt;The vertical label appears to the left of our&amp;nbsp;x-axis.&lt;/li&gt;
&lt;li&gt;Each graph has a start and end time which can be provided either as a relative or absolute timestamp&amp;nbsp;value. &lt;code&gt;now-7200s&lt;/code&gt; means that we will view weather statistics from the past 2 hours up till&amp;nbsp;now.&lt;/li&gt;
&lt;li&gt;The &lt;span class="caps"&gt;DEF&lt;/span&gt; directive defines the graph&amp;nbsp;variable &lt;code&gt;a&lt;/code&gt;, which is followed by this&amp;nbsp;pattern &lt;code&gt;&amp;lt;source file&amp;gt;:&amp;lt;key&amp;gt;:&amp;lt;metric&amp;gt;&lt;/code&gt;.&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;source file&amp;gt;&lt;/code&gt; contains the path to the &lt;span class="caps"&gt;RRD&lt;/span&gt;&amp;nbsp;file.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;key&amp;gt;&lt;/code&gt; is what we saw in&amp;nbsp;the &lt;code&gt;rrdtool info&lt;/code&gt; command&amp;nbsp;as &lt;code&gt;ds[value]&lt;/code&gt;. In this case we will plot&amp;nbsp;the &lt;code&gt;value&lt;/code&gt; key.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;metric&amp;gt;&lt;/code&gt; is usually&amp;nbsp;either &lt;code&gt;MIN&lt;/code&gt;, &lt;code&gt;MAX&lt;/code&gt;,&amp;nbsp;or &lt;code&gt;AVERAGE&lt;/code&gt;. It is also visible in&amp;nbsp;the &lt;code&gt;info&lt;/code&gt; output.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: this tool can produce some &lt;a href="https://oss.oetiker.ch/rrdtool/gallery/index.en.html"&gt;very complex graphs&lt;/a&gt;, but for our use case we will just stick with a simple line&amp;nbsp;graph.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Having configured the script we will now go over the plugin structure and see how the weather collection script is&amp;nbsp;written.&lt;/p&gt;
&lt;h2&gt;&lt;a name="weather-logger-plugin"&gt;&lt;/a&gt; Weather Logger&amp;nbsp;plugin&lt;/h2&gt;
&lt;p&gt;The Python Collectd plugin begins by importing&amp;nbsp;the &lt;code&gt;collectd&lt;/code&gt; module. It then defines a series of callback functions. For our use case we will only define&amp;nbsp;a &lt;code&gt;config&lt;/code&gt; and&amp;nbsp;a &lt;code&gt;read&lt;/code&gt; callback&amp;nbsp;function.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;collectd&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;PLUGIN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;weather_alert&amp;#39;&lt;/span&gt;
&lt;span class="n"&gt;CITY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;API_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt; &lt;span class="c1"&gt;# placeholders&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;config_callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;global&lt;/span&gt; &lt;span class="n"&gt;CITY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;API_KEY&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;city&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;CITY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;api_key&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;API_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;CITY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;collectd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="s1"&gt; plugin: [Error] The city name must be provided&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PLUGIN&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;API_KEY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;collectd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="s1"&gt; plugin: [Error] The API key must be provided&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PLUGIN&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The configuration function above sets&amp;nbsp;the &lt;code&gt;CITY&lt;/code&gt; and &lt;code&gt;API_KEY&lt;/code&gt; variables that we passed over from the config file. Later on we will see how to view Collectd&amp;nbsp;logs.&lt;/p&gt;
&lt;p&gt;We now define&amp;nbsp;the &lt;code&gt;read_callback&lt;/code&gt; function:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;read_callback&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;weather&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;http://api.openweathermap.org/data/2.5/forecast?q=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;CITY&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;&amp;amp;units=metric&amp;amp;appid=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;API_KEY&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;weather_metric&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;collectd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plugin&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PLUGIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;temperature&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;weather_metric&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;weather&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;list&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;main&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;temp&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;weather_metric&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;collectd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="s1"&gt; plugin: [Error] Unable to run &lt;/span&gt;&lt;span class="si"&gt;{}&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We call the OpenWeatherMap &lt;span class="caps"&gt;API&lt;/span&gt; for collecting the data.&amp;nbsp;The &lt;code&gt;collectd.Values()&lt;/code&gt; function registers the plugin name which we later reference in the config file.&amp;nbsp;The &lt;code&gt;weather_metric.dispatch()&lt;/code&gt; function is what writes the data to the &lt;span class="caps"&gt;RRD&lt;/span&gt;&amp;nbsp;file.&lt;/p&gt;
&lt;p&gt;The final step is to register these two functions with&amp;nbsp;the &lt;code&gt;collectd&lt;/code&gt; module:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;collectd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register_config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config_callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;collectd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register_read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;read_callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;Diagnosing plugin&amp;nbsp;issues&lt;/h2&gt;
&lt;p&gt;Let&amp;#8217;s try removing&amp;nbsp;the &lt;code&gt;CITY&lt;/code&gt; from&amp;nbsp;our &lt;code&gt;/etc/collectd.conf&lt;/code&gt; configuration file and see if it raises any errors. We restart the Collectd service and check its&amp;nbsp;status:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;systemctl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;restart&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;collectd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;
&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;systemctl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;collectd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;
&lt;span class="err"&gt;●&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;collectd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;System&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;statistics&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;daemon&lt;/span&gt;
&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="nl"&gt;Loaded&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;loaded&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;systemd&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;system&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;collectd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;vendor&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;preset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="nl"&gt;Active&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;active&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;running&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;May&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;07&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;collectd&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;29390&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;weather_alert&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;plugin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;be&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;provided&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It can also be helpful to view the full logs for more details. By default, these are present in&amp;nbsp;the &lt;code&gt;/var/log/collectd.log&lt;/code&gt; file.&lt;/p&gt;
&lt;h3&gt;References&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.dbrgn.ch/2017/3/10/write-a-collectd-python-plugin"&gt;https://blog.dbrgn.ch/2017/3/10/write-a-collectd-python-plugin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://frontier.town/2017/10/collectd-and-rrdtool"&gt;https://frontier.town/2017/10/collectd-and-rrdtool&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gist.github.com/twolfson/ad3bb967d37dd140382d"&gt;https://gist.github.com/twolfson/ad3bb967d37dd140382d&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><category term="automation"/></entry><entry><title>The Kerberos Authentication System for Single Sign-On (SSO)</title><link href="https://adl1995.github.io/the-kerberos-authentication-system-for-single-sign-on-sso.html" rel="alternate"/><published>2019-07-21T21:43:00+02:00</published><updated>2019-07-21T21:43:00+02:00</updated><author><name>Adeel Ahmad</name></author><id>tag:adl1995.github.io,2019-07-21:/the-kerberos-authentication-system-for-single-sign-on-sso.html</id><summary type="html">&lt;p&gt;When working with authentication protocols the commonly used technique in the past was known as &lt;em&gt;authentication by assertion&lt;/em&gt;. In this scheme a user logs in to their machine which then authenticates their request to a remote server. Once the authentication is finished the user can then communicate with other services. This provides a very low level of security, which has led to numerous vulnerabilities in the early versions of the &lt;a href="https://en.wikipedia.org/wiki/Berkeley_r-commands#Security"&gt;rlogin&lt;/a&gt; Unix login&amp;nbsp;utility.&lt;/p&gt;
&lt;p&gt;An alternative solution is for the user to repeatedly provide their password each time they wish to use a service. This however requires the user to send their plain text password over the network, which could potentially be intercepted by a third-party user and can get …&lt;/p&gt;</summary><content type="html">&lt;p&gt;When working with authentication protocols the commonly used technique in the past was known as &lt;em&gt;authentication by assertion&lt;/em&gt;. In this scheme a user logs in to their machine which then authenticates their request to a remote server. Once the authentication is finished the user can then communicate with other services. This provides a very low level of security, which has led to numerous vulnerabilities in the early versions of the &lt;a href="https://en.wikipedia.org/wiki/Berkeley_r-commands#Security"&gt;rlogin&lt;/a&gt; Unix login&amp;nbsp;utility.&lt;/p&gt;
&lt;p&gt;An alternative solution is for the user to repeatedly provide their password each time they wish to use a service. This however requires the user to send their plain text password over the network, which could potentially be intercepted by a third-party user and can get&amp;nbsp;compromised.&lt;/p&gt;
&lt;p&gt;Kerberos aims to solve this issue by introducing a mechanism where a user only logs-in to their local machine once, also known as Single-Sign On or &lt;span class="caps"&gt;SSO&lt;/span&gt;. The user&amp;#8217;s information is transferred securely over the network (and is deprived of plaintext passwords). This removes the risk of user&amp;#8217;s password being sniffed by an eavesdropper as the protocol only uses secret keys for communicating with the outside&amp;nbsp;service.&lt;/p&gt;
&lt;h3&gt;The Kerberos Authentication&amp;nbsp;System&lt;/h3&gt;
&lt;p&gt;The Kerberos Authentication System was first introduced in 1988 (&lt;a href="https://www3.nd.edu/~dthain/courses/cse66771/summer2014/papers/kerberos.pdf"&gt;paper&lt;/a&gt;). Its motivation was to authenticate a client to a server without sharing the user&amp;#8217;s password across a network. Normal authentication protocols are prone to outside attackers who can sniff the network traffic and potentially gain access to user&amp;nbsp;credentials.&lt;/p&gt;
&lt;p&gt;This led Kerberos to gain popularity during the past few decades and it is now being used in various organisations as their main authentication system, including &lt;a href="http://information-technology.web.cern.ch/services/fe/afs/howto/authenticate-processes"&gt;&lt;span class="caps"&gt;CERN&lt;/span&gt;&lt;/a&gt;. Kerberos is also the default authentication system used by the Windows Operating&amp;nbsp;System.&lt;/p&gt;
&lt;p&gt;Before explaining how this protocol works, let&amp;#8217;s review the terminology used in Kerberos. In Kerberos the client and user are referred to as a &amp;#8220;process&amp;#8221; and a &amp;#8220;principal&amp;#8221;, respectively. Apart from this it also has a naming convention for the entity used for authentication, which consists of its primary name, the instance, and a realm, and is expressed&amp;nbsp;as &lt;code&gt;name.instance@realm&lt;/code&gt;. The primary name is the name of the user, and the instance allows the system to distinguish variations among the primary name. A realm refers to the administrative entity, for&amp;nbsp;example, &lt;code&gt;CERN.CH&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Kerberos exchanges a series of encrypted messages with the verifier (server) to prove that the process is running on behalf of the intended user. These messages include an encryption key, which is unique to each user, and is derived from their password. When an application communicates with the server it encrypts the data using this key and the server verifies its confidentiality through its&amp;nbsp;checksum.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: Although Kerberos ensures that the user&amp;#8217;s password is not shared with the service, there is still a risk of the user&amp;#8217;s machine being compromised, for example, through a Trojan horse&amp;nbsp;malware.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Obtaining the encryption&amp;nbsp;key&lt;/h3&gt;
&lt;p&gt;Before a client sends an authentication request to the server, the authentication key is only known by the server, which it regenerates each time the client tries to authenticate itself. Kerberos then issues a certificate which is encrypted with the newly generated key and distributes it to the client. This certificate includes, among other information, a session key along with its &lt;span class="caps"&gt;TTL&lt;/span&gt; timestamp (after which the key will&amp;nbsp;expire).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The encryption algorithm used by Kerberos is called &lt;span class="caps"&gt;DES&lt;/span&gt; or &lt;a href="https://en.wikipedia.org/wiki/Data_Encryption_Standard"&gt;Data Encryption Scheme&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;The Kerberos Database Management Service (&lt;span class="caps"&gt;KDMS&lt;/span&gt;)&lt;/h3&gt;
&lt;p&gt;The &lt;span class="caps"&gt;KDMS&lt;/span&gt; is responsible for performing write operations to the Kerberos database. The current version of Kerberos states that the management service should only run on the server node, as shown in the figure&amp;nbsp;below.&lt;/p&gt;
&lt;figure style="text-align:center;" class="image"&gt;
  &lt;img src="https://adl1995.github.io/images/kerberos/kdms.png" alt="kdms"&gt;
  &lt;figcaption&gt;&lt;span class="caps"&gt;KDMS&lt;/span&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;h4&gt;&lt;span class="caps"&gt;KDMS&lt;/span&gt;&amp;nbsp;utilities&lt;/h4&gt;
&lt;p&gt;The client side of Kerberos provides two utilities for communicating with the server. The first&amp;nbsp;utility, &lt;code&gt;kpasswd&lt;/code&gt;, allows the principal to update their password. The second&amp;nbsp;utility, &lt;code&gt;kadmin&lt;/code&gt;, allows the administrator to access the Kerberos database. It provides numerous commands for modifying, updating, and deleting a principal. As an example, the password for the principal user can be updated&amp;nbsp;by:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ kpasswd adeel
Password for adeel@CERN.CH: &amp;lt;current password&amp;gt;
Enter new password: &amp;lt;new password&amp;gt;
Enter it again: &amp;lt;new password&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: It is not necessary to specify the username in the above command if that user is only registered with a single&amp;nbsp;realm.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;An important point to note here is that &lt;span class="caps"&gt;KDMS&lt;/span&gt; does not rely on the standard ticket-granting service, but rather requires the principal (user) to use the authentication service each time they interact with the database. Because of this feature, a logged-in user is not at risk of getting their password modified by a malicious user if their workstation is left&amp;nbsp;unattended.&lt;/p&gt;
&lt;figure style="text-align:center;" class="image"&gt;
  &lt;img src="https://adl1995.github.io/images/kerberos/kpasswd-kadmin-auth.png" alt="kpasswd-kadmin-auth"&gt;
  &lt;figcaption&gt;Authentication process for kpasswd and kadmin&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;h4&gt;Kerberos database&amp;nbsp;replication&lt;/h4&gt;
&lt;p&gt;To achieve better performance and high availability, Kerberos clients posses an exact replica of the Kerberos server database. The database needs to be in sync with the server and must contain the latest changes. For this, Kerberos provides two tools&amp;nbsp;called &lt;code&gt;kprop&lt;/code&gt; and &lt;code&gt;kpropd&lt;/code&gt;.&amp;nbsp;The &lt;code&gt;kprop&lt;/code&gt; tool runs on the server side and periodically creates a dump of the database (every 1 hour by default)  and sends it to the&amp;nbsp;client.&lt;/p&gt;
&lt;figure style="text-align:center;" class="image"&gt;
  &lt;img src="https://adl1995.github.io/images/kerberos/database-replication.png" alt="database-replication"&gt;
  &lt;figcaption&gt;Database replication in Kerberos&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;&lt;br&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: Some additional steps are not mentioned here for brevity. These include the database checksum, which is used for verifying the data&amp;nbsp;authenticity.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Kerberos in&amp;nbsp;practice&lt;/h3&gt;
&lt;p&gt;In some implementations of Kerberos the ticket is obtained as part of the system login process. Thus the user is often oblivious that Kerberos is running in the&amp;nbsp;background.&lt;/p&gt;
&lt;p&gt;In other cases the user can manually login using&amp;nbsp;the &lt;code&gt;kinit&lt;/code&gt; tool:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ kinit adeel
Password for adeel@CERN.CH: &amp;lt;password&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Limitations of&amp;nbsp;Kerberos&lt;/h3&gt;
&lt;p&gt;Although Kerberos takes away much of the hassle involved in the authentication process, it nevertheless has some limitations entailed with it. Firstly, if the user has chosen a weak password, it is relatively easy for an attacker to impersonate themselves as the real user. Secondly, the ticket expiration time is always a trade-off between security and convenience. A longer expiration time raises the risk of the session key being stolen and thus allowing an attacker to gain authority over the services, and a short expiration time requires the user to regularly re-enter their plaintext password into the shell, which could be infected by a&amp;nbsp;malware.&lt;/p&gt;
&lt;p&gt;Kerberos also does not support multifactor authentication by default, which is nowadays becoming the norm for most modern-day&amp;nbsp;applications.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Disclaimer: The images in this blog post are taken from the &lt;a href="https://www3.nd.edu/~dthain/courses/cse66771/summer2014/papers/kerberos.pdf"&gt;Kerberos &lt;span class="caps"&gt;MIT&lt;/span&gt; paper&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;</content><category term="Security"/></entry><entry><title>Programmatically organising your backpacking trip using Google My Maps</title><link href="https://adl1995.github.io/programmatically-organising-your-backpacking-trip-using-google-my-maps.html" rel="alternate"/><published>2019-05-30T09:14:00+02:00</published><updated>2019-05-30T09:14:00+02:00</updated><author><name>Adeel Ahmad</name></author><id>tag:adl1995.github.io,2019-05-30:/programmatically-organising-your-backpacking-trip-using-google-my-maps.html</id><summary type="html">&lt;blockquote&gt;
&lt;p&gt;This blog post has been converted from a &lt;a href="https://docs.google.com/presentation/d/10NlBmnUk7qsh0nPso3Lqg8OvoP6CZg8sHJx1gllHem4/edit#slide=id.g577465d654_0_0"&gt;presentation&lt;/a&gt; I gave during the &lt;a href="https://indico.cern.ch/event/771113/overview"&gt;Thematic &lt;span class="caps"&gt;CERN&lt;/span&gt; School of Computing 2019&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;When planning a journey to a new country or a city it helps to mark down all the places you would like to visit and eventually create a travel plan for each day. I personally use Google Maps for finding places of interest including historical buildings, museums, and libraries. As an example, if I was to visit say Split, Croatia I could search for &amp;#8220;places to visit split&amp;#8221; on Google Maps. It will then list all the attractions based on features such as reviews and&amp;nbsp;popularity.&lt;/p&gt;
&lt;p align="center"&gt;
    &lt;img src="https://adl1995.github.io/images/google-my-maps/things-to-do-split.png" alt="Things to do in Split" style="width:300px;"/&gt;
&lt;/p&gt;

&lt;p&gt;Although it is possible to individually &amp;#8220;Save&amp;#8221; each place in Google Maps, it does not …&lt;/p&gt;</summary><content type="html">&lt;blockquote&gt;
&lt;p&gt;This blog post has been converted from a &lt;a href="https://docs.google.com/presentation/d/10NlBmnUk7qsh0nPso3Lqg8OvoP6CZg8sHJx1gllHem4/edit#slide=id.g577465d654_0_0"&gt;presentation&lt;/a&gt; I gave during the &lt;a href="https://indico.cern.ch/event/771113/overview"&gt;Thematic &lt;span class="caps"&gt;CERN&lt;/span&gt; School of Computing 2019&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;When planning a journey to a new country or a city it helps to mark down all the places you would like to visit and eventually create a travel plan for each day. I personally use Google Maps for finding places of interest including historical buildings, museums, and libraries. As an example, if I was to visit say Split, Croatia I could search for &amp;#8220;places to visit split&amp;#8221; on Google Maps. It will then list all the attractions based on features such as reviews and&amp;nbsp;popularity.&lt;/p&gt;
&lt;p align="center"&gt;
    &lt;img src="https://adl1995.github.io/images/google-my-maps/things-to-do-split.png" alt="Things to do in Split" style="width:300px;"/&gt;
&lt;/p&gt;

&lt;p&gt;Although it is possible to individually &amp;#8220;Save&amp;#8221; each place in Google Maps, it does not allow customisability options including categorisation of places and marking places based on personal&amp;nbsp;interests.&lt;/p&gt;
&lt;h3&gt;Google My&amp;nbsp;Maps&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://www.google.com/mymaps"&gt;Google My Maps&lt;/a&gt; is a tool which allows creating custom maps and is useful for planning trips, marking hiking routes, and other sports activities. It also allows sharing the map either publicly or privately. Some of its features&amp;nbsp;include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Updating the base map to satellite or street&amp;nbsp;view&lt;/li&gt;
&lt;li&gt;Personalising the map by styling the icons and changing their&amp;nbsp;color&lt;/li&gt;
&lt;li&gt;Importing data from a spreadsheet including &lt;span class="caps"&gt;CSV&lt;/span&gt;, &lt;span class="caps"&gt;XML&lt;/span&gt;, and &lt;span class="caps"&gt;KML&lt;/span&gt;&amp;nbsp;files&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Adding a new&amp;nbsp;entry&lt;/h4&gt;
&lt;p&gt;The manual process for adding a new entry involves finding a location using the search bar and then adding it to the map layer which places a waypoint icon on the base&amp;nbsp;map.&lt;/p&gt;
&lt;p align="center"&gt;
    &lt;img src="https://adl1995.github.io/images/google-my-maps/adding-entry.png" alt="Adding entry" style="width:500px;"/&gt;
&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A &lt;em&gt;layer&lt;/em&gt; is Google My Map&amp;#8217;s way of categorising different topics, for example, there can be a separate layer for different&amp;nbsp;cities.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This, however, is a tedious and repetitive task, which can be done programmatically using&amp;nbsp;Python.&lt;/p&gt;
&lt;h4&gt;Adding entries using Google Maps &lt;span class="caps"&gt;API&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;In principal, if we have a spreadsheet with a list of places, the only task left is to import these places into our custom map. Thus we require a way of automating the process of creating this&amp;nbsp;spreadsheet.&lt;/p&gt;
&lt;p&gt;The Google Maps &lt;span class="caps"&gt;API&lt;/span&gt; provides an elegant way of querying the data using their Places &lt;span class="caps"&gt;API&lt;/span&gt;. For example, to get a list of places to visit in Split, the request can be formatted&amp;nbsp;as:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;https://maps.googleapis.com/maps/api/place/textsearch/json?query=things+to+do+split&amp;amp;language=en&amp;amp;key=API_KEY
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This returns a &lt;span class="caps"&gt;JSON&lt;/span&gt; object containing a list of places. Each record contains the place name, its latitude, longitude, address, icon,&amp;nbsp;etc. &lt;/p&gt;
&lt;p&gt;The code block below sends a &lt;span class="caps"&gt;GET&lt;/span&gt; request to the server, and then performs four major steps including getting&amp;nbsp;the &lt;code&gt;result&lt;/code&gt; object, enumerating through the places list and then writing the appropriate entries to a &lt;span class="caps"&gt;CSV&lt;/span&gt;&amp;nbsp;file.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;# Fetch the data.&lt;/span&gt;
&lt;span class="n"&gt;places&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;https://maps.googleapis.com/maps/api/place/textsearch/json?query=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;&amp;amp;language=en&amp;amp;key=API_KEY&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Convert the response to a JSON object.&lt;/span&gt;
&lt;span class="n"&gt;places&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;places&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;results&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# Note: in this case we only keep the these three columns, but we can further populate the CSV file with the place description, its rating, and so on...&lt;/span&gt;
&lt;span class="n"&gt;columns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;names&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;coordinates&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;icon&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# Write the data to a CSV file.&lt;/span&gt;
&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;places/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;.csv&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;w&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;out_file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;writer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;out_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;delimiter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;,&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;writerow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;place&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;places&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;place&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;icon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;place&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;icon&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lng&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;place&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;geometry&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;location&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;lat&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;place&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;geometry&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;location&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;lng&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;static&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt; -&amp;gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;writerow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;Invoking the&amp;nbsp;script&lt;/h4&gt;
&lt;p&gt;To get a list of all the places to visit in&amp;nbsp;Split:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;python&lt;span class="w"&gt; &lt;/span&gt;fetch-google-maps.py&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;things to do split&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will generate a &lt;span class="caps"&gt;CSV&lt;/span&gt; file&amp;nbsp;called &lt;code&gt;Things To Do Split.csv&lt;/code&gt; in&amp;nbsp;the &lt;code&gt;places&lt;/code&gt; sub-directory.&lt;/p&gt;
&lt;h4&gt;Importing the&amp;nbsp;file&lt;/h4&gt;
&lt;p&gt;Google My Maps supports populates the map from &lt;span class="caps"&gt;CSV&lt;/span&gt;, &lt;span class="caps"&gt;XML&lt;/span&gt;, &lt;span class="caps"&gt;KML&lt;/span&gt;, and &lt;span class="caps"&gt;GPL&lt;/span&gt; files. Instead of using coordinates (as we do in this case) we can also use street&amp;nbsp;addresses.&lt;/p&gt;
&lt;p&gt;To import the file, create a new layer in your custom map and use the &amp;#8220;Import&amp;#8221; utility. A window will pop up prompting for the file upload. Note that you will need to guide Google My Maps by marking the columns that correspond to the place name and its&amp;nbsp;coordinates.&lt;/p&gt;
&lt;p align="center"&gt;
    &lt;img src="https://adl1995.github.io/images/google-my-maps/lat-lon-prompt.png" alt="Coordinates" style="width:350px;"/&gt;
    &lt;img src="https://adl1995.github.io/images/google-my-maps/title-prompt.png" alt="Title" style="width:350px;"/&gt;
&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: By default, if there are more than 10 items in the &lt;span class="caps"&gt;CSV&lt;/span&gt; file Google My Maps will group them all under a single entry. To view each item separately select the &amp;#8220;Uniform style&amp;#8221; option and update &amp;#8220;group places by&amp;#8221; dropdown to &amp;#8220;Individual&amp;nbsp;styles&amp;#8221;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Limitations&lt;/h3&gt;
&lt;p&gt;Although our map is now populated with the landmark icons at their correct positions, they are not yet styled and all have the same icon. They are also not color coded which makes it difficult to keep track of all the places to visit on a particular day (changing the icon also allows quick identification, e.g. a museum or library should be clearly distinguishable). A fully styled map may look&amp;nbsp;like:&lt;/p&gt;
&lt;p align="center"&gt;
    &lt;img src="https://adl1995.github.io/images/google-my-maps/map-simple.png" alt="Map simple" style="width:350px;"/&gt;
    &lt;img src="https://adl1995.github.io/images/google-my-maps/map-styled.png" alt="Map styled" style="width:400px;"/&gt;
&lt;/p&gt;

&lt;p&gt;I could not find a way to embed this information within the &lt;span class="caps"&gt;CSV&lt;/span&gt; file, so this step still has to be done manually. Another limitation with Google My Maps is that it is not possible to import multiple &lt;span class="caps"&gt;CSV&lt;/span&gt; files into a single layer (thus requiring us to merge the files before importing&amp;nbsp;them).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A styled map from one of my recent trips can be seen &lt;a href="https://drive.google.com/open?id=1OdZq0vA46Oi8sny75zG0Z-MNVURjCSh6&amp;amp;usp=sharing"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;References&lt;/h3&gt;
&lt;p&gt;Source code: &lt;a href="https://gist.github.com/adl1995/a66fa3532364ff87aaaec72be332abe7"&gt;https://gist.github.com/adl1995/a66fa3532364ff87aaaec72be332abe7&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Blog: &lt;a href="https://www.google.com/earth/outreach/learn/visualize-your-data-on-a-custom-map-using-google-my-maps"&gt;https://www.google.com/earth/outreach/learn/visualize-your-data-on-a-custom-map-using-google-my-maps&lt;/a&gt;&lt;/p&gt;</content><category term="Automation"/></entry><entry><title>Building RPM packages with rpmbuild, Koji, and GitLab-CI</title><link href="https://adl1995.github.io/building-rpm-packages-with-rpmbuild-koji-and-gitlab-ci.html" rel="alternate"/><published>2019-05-06T08:24:00+02:00</published><updated>2019-05-06T08:24:00+02:00</updated><author><name>Adeel Ahmad</name></author><id>tag:adl1995.github.io,2019-05-06:/building-rpm-packages-with-rpmbuild-koji-and-gitlab-ci.html</id><summary type="html">&lt;p&gt;The &lt;span class="caps"&gt;RPM&lt;/span&gt; system facilitates the user to query and update a software package. It also allows examining package interdependencies, and verifying package file permissions. This blog post will describe the process of building an &lt;span class="caps"&gt;RPM&lt;/span&gt; package using&amp;nbsp;the &lt;code&gt;rpmbuild&lt;/code&gt; utility and will then explain how to schedule build tasks using Koji. Finally, it will describe how to automate the build pipeline using continuous integration in&amp;nbsp;GitLab.&lt;/p&gt;
&lt;h3&gt;1. &lt;span class="caps"&gt;RPM&lt;/span&gt; Package&amp;nbsp;Manager&lt;/h3&gt;
&lt;p&gt;&lt;span class="caps"&gt;RPM&lt;/span&gt; Package Manager is an open-source package management system which was originally designed for Red Hat Linux, but it is now supported on most Linux distributions. &lt;span class="caps"&gt;RPM&lt;/span&gt; packages can generally be of two&amp;nbsp;types:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Binary &lt;span class="caps"&gt;RPM&lt;/span&gt;: A binary &lt;span class="caps"&gt;RPM&lt;/span&gt; contains the compiled binary of a complete application (or a library …&lt;/li&gt;&lt;/ul&gt;</summary><content type="html">&lt;p&gt;The &lt;span class="caps"&gt;RPM&lt;/span&gt; system facilitates the user to query and update a software package. It also allows examining package interdependencies, and verifying package file permissions. This blog post will describe the process of building an &lt;span class="caps"&gt;RPM&lt;/span&gt; package using&amp;nbsp;the &lt;code&gt;rpmbuild&lt;/code&gt; utility and will then explain how to schedule build tasks using Koji. Finally, it will describe how to automate the build pipeline using continuous integration in&amp;nbsp;GitLab.&lt;/p&gt;
&lt;h3&gt;1. &lt;span class="caps"&gt;RPM&lt;/span&gt; Package&amp;nbsp;Manager&lt;/h3&gt;
&lt;p&gt;&lt;span class="caps"&gt;RPM&lt;/span&gt; Package Manager is an open-source package management system which was originally designed for Red Hat Linux, but it is now supported on most Linux distributions. &lt;span class="caps"&gt;RPM&lt;/span&gt; packages can generally be of two&amp;nbsp;types:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Binary &lt;span class="caps"&gt;RPM&lt;/span&gt;: A binary &lt;span class="caps"&gt;RPM&lt;/span&gt; contains the compiled binary of a complete application (or a library), and targets a particular architecture and may not be installable on all platforms. For example, an &lt;span class="caps"&gt;RPM&lt;/span&gt; compiled for the x86 architecture will not be compatible with &lt;span class="caps"&gt;ARM&lt;/span&gt; architecture, and vice-versa. It should be noted however that some binary RPMs can target multiple architectures, for example, when the underlying application is written in a platform-independent language, such as Python or&amp;nbsp;Java. &lt;/li&gt;
&lt;li&gt;Source &lt;span class="caps"&gt;RPM&lt;/span&gt;: A source &lt;span class="caps"&gt;RPM&lt;/span&gt; contains the source code of an application, and a set of commands for creating its binary &lt;span class="caps"&gt;RPM&lt;/span&gt; on the target Linux environment. As the code present in an &lt;span class="caps"&gt;SRPM&lt;/span&gt; is not compiled, the created binary RPMs are termed as &amp;#8220;noarch&amp;#8221; as they are platform-independent and are generally more flexible, i.e. they allow modifying compile options, which can allow additional&amp;nbsp;functionality.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: The Fedora project hosts an extensive &lt;a href="https://docs.fedoraproject.org/en-US/quick-docs/creating-rpm-packages/index.html"&gt;documentation&lt;/a&gt; on how to build&amp;nbsp;RPMs.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;1.1. &lt;span class="caps"&gt;RPM&lt;/span&gt; file&amp;nbsp;components&lt;/h3&gt;
&lt;p&gt;There are four sections in an &lt;span class="caps"&gt;RPM&lt;/span&gt; file which contain information regarding the&amp;nbsp;package.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Name: The package&amp;nbsp;name.&lt;/li&gt;
&lt;li&gt;Signature: Used to verify the authenticity of the package. It does so by applying a mathematical function on the header and archive sections of the file. There are numerous options available for the encryption method, such as &lt;span class="caps"&gt;PGP&lt;/span&gt; or &lt;span class="caps"&gt;MD5&lt;/span&gt;.&lt;/li&gt;
&lt;li&gt;Header: The copyright information, package version numbers, package description,&amp;nbsp;etc.&lt;/li&gt;
&lt;li&gt;Payload: The actual contents of the package, which are decompressed when the package is installed. The data in this section is compressed&amp;nbsp;using &lt;code&gt;gzip&lt;/code&gt;. Upon decompression, the data is&amp;nbsp;in &lt;code&gt;cpio&lt;/code&gt; format.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;1.2. &lt;span class="caps"&gt;RPM&lt;/span&gt; build&amp;nbsp;procedure&lt;/h3&gt;
&lt;p&gt;Building an &lt;span class="caps"&gt;RPM&lt;/span&gt; can be accomplished using&amp;nbsp;the &lt;code&gt;rpmbuild&lt;/code&gt; utility. In most cases, we only require the package source code and a spec file. The spec file is tailored for each package and contains the recipe for building the&amp;nbsp;package.&lt;/p&gt;
&lt;p&gt;Listed below are the directories with a description of what they contain. These directories are created when building an &lt;span class="caps"&gt;RPM&lt;/span&gt;&amp;nbsp;package:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span class="caps"&gt;BUILD&lt;/span&gt;: The software build storage&amp;nbsp;directory.&lt;/li&gt;
&lt;li&gt;&lt;span class="caps"&gt;RPMS&lt;/span&gt;: Binary &lt;span class="caps"&gt;RPM&lt;/span&gt; storage&amp;nbsp;directory.&lt;/li&gt;
&lt;li&gt;&lt;span class="caps"&gt;SOURCES&lt;/span&gt;: The package source code (e.g. as a&amp;nbsp;tarball).&lt;/li&gt;
&lt;li&gt;&lt;span class="caps"&gt;SPECS&lt;/span&gt;: The spec file(s) for one or more&amp;nbsp;RPMs.&lt;/li&gt;
&lt;li&gt;&lt;span class="caps"&gt;SRPMS&lt;/span&gt;: Source &lt;span class="caps"&gt;RPM&lt;/span&gt; storage&amp;nbsp;directory.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: It is possible to place the package source code outside of the &lt;span class="caps"&gt;SOURCES&lt;/span&gt; directory and then providing the path in&amp;nbsp;the &lt;code&gt;Source0&lt;/code&gt; tag.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The essence of the &lt;span class="caps"&gt;RPM&lt;/span&gt; build process lies in the spec file, which contains information regarding the package, its version (and build) number, its changelog, etc. This information can later be queried using&amp;nbsp;the &lt;code&gt;rpm&lt;/code&gt; command. In addition to this meta-information it also contains the instructions to build, install, and delete the package. The sections that are involved in the spec file are explained&amp;nbsp;below.&lt;/p&gt;
&lt;h4&gt;1.2.1. Preamble&amp;nbsp;section&lt;/h4&gt;
&lt;p&gt;This section contains the metadata of the package, which can later be queried using&amp;nbsp;the &lt;code&gt;rpm -qi &amp;lt;package-name&amp;gt;&lt;/code&gt; command. An example section is shown&amp;nbsp;below:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;!?&lt;/span&gt;&lt;span class="n"&gt;dist&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;%define&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dist&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;el7&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cern&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nl"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;           &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nl"&gt;Version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nl"&gt;Release&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;release&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nl"&gt;Summary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;summary&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nl"&gt;Group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;which&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;belongs&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nl"&gt;License&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;license&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;policy&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nl"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;homepage&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nl"&gt;Vendor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;vendor&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nl"&gt;Requires&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dependencies&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nl"&gt;BuildRoot&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;directory&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nl"&gt;BuildArch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;architecture&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;noarch&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nl"&gt;Source0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nl"&gt;Source1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="nl"&gt;Patch0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;patch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nl"&gt;Patch1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="nf"&gt;%description&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: The &lt;span class="caps"&gt;RPM&lt;/span&gt; spec file provides numerous system and user-defined macros. User-defined macros follow&amp;nbsp;the &lt;code&gt;%{&amp;lt;macro name&amp;gt;}&lt;/code&gt; syntax, whereas system macros are written without the curly braces, for&amp;nbsp;example &lt;code&gt;%define&lt;/code&gt;. The macros section provides a detailed overview of the available&amp;nbsp;macros.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;1.2.2. Prep&amp;nbsp;section&lt;/h4&gt;
&lt;p&gt;The prep or prepare section precedes the build section and defines the commands necessary for the build. If the source is specified using a tarball, the prep section is responsible for extracting&amp;nbsp;it.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;%prep&lt;/span&gt;

&lt;span class="cp"&gt;# This step extracts the tarball.&lt;/span&gt;
&lt;span class="nf"&gt;%setup&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;cq&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;%setup&lt;/code&gt; macro is used for unpacking the original sources in preparation for the build and can take the following&amp;nbsp;options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-n &amp;lt;name&amp;gt;&lt;/code&gt;: Name of the software build&amp;nbsp;directory.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-q&lt;/code&gt;: Suppress the displaying of files when unpacking&amp;nbsp;sources.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-c&lt;/code&gt;: Create the top-level build directory before unpacking the&amp;nbsp;sources.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-D&lt;/code&gt;: Do not delete the build directory prior to unpacking the&amp;nbsp;sources.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-T&lt;/code&gt;: Override the default unpacking scheme, used in combination&amp;nbsp;with &lt;code&gt;-a&lt;/code&gt; (after)&amp;nbsp;or &lt;code&gt;-b&lt;/code&gt; (before) option, for&amp;nbsp;example:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;%setup -D -T -b 2&lt;/code&gt;&lt;/p&gt;
&lt;h4&gt;1.2.3. Build&amp;nbsp;section&lt;/h4&gt;
&lt;p&gt;This section contains the commands to build the application. Since the build instructions are usually present in a separate file, this section is mostly&amp;nbsp;empty.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;%build&lt;/span&gt;

&lt;span class="cp"&gt;# Commands to build the application.&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;1.2.4. Install&amp;nbsp;section&lt;/h4&gt;
&lt;p&gt;The install section is responsible for installing the application. In principal, this section should delete the build directory, i.e. remove previously installed files. The example below removes the build directory and then copies the build files of the package&amp;nbsp;to &lt;code&gt;%{buildroot}&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;%install&lt;/span&gt;

&lt;span class="cp"&gt;# Remove the build directory. &lt;/span&gt;
&lt;span class="n"&gt;rm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;rf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;buildroot&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cp"&gt;# Create the build directory.&lt;/span&gt;
&lt;span class="n"&gt;mkdir&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;buildroot&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;/%&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;_unitdir&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cp"&gt;# Move the &amp;lt;package&amp;gt;.service file.&lt;/span&gt;
&lt;span class="n"&gt;mv&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="o"&gt;/%&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;package&lt;/span&gt;&lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;buildroot&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;/%&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;_unitdir&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cp"&gt;# Move the configuration files.&lt;/span&gt;
&lt;span class="n"&gt;mkdir&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;buildroot&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;package&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="n"&gt;mv&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;conf&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sample&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;buildroot&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;package&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="cp"&gt;# Copy the build files.&lt;/span&gt;
&lt;span class="n"&gt;mkdir&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;buildroot&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="o"&gt;/&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;package&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="n"&gt;cp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;R&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;buildroot&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="o"&gt;/&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;package&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;1.2.5. Clean&amp;nbsp;section&lt;/h4&gt;
&lt;p&gt;This section removes the build files created in the previous&amp;nbsp;sections.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;%clean&lt;/span&gt;

&lt;span class="n"&gt;rm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;rf&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;buildroot&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;1.2.6. Files&amp;nbsp;section&lt;/h4&gt;
&lt;p&gt;The files section is used for marking configuration and documentation files, and to ensure file permissions and&amp;nbsp;ownerships.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;%files&lt;/span&gt;

&lt;span class="cp"&gt;# Mark the file as documentation.&lt;/span&gt;
&lt;span class="nf"&gt;%doc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;README&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;md&lt;/span&gt;

&lt;span class="cp"&gt;# Set the attributes for the following directories and file.&lt;/span&gt;
&lt;span class="nf"&gt;%defattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;opt&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;cortex&lt;/span&gt;
&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;_unitdir&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;/%&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;
&lt;span class="nf"&gt;%config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;noreplace&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;cortex&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The following directives can be used in this&amp;nbsp;section:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;%doc&lt;/code&gt;: Flags the filename(s) that follow as part of the&amp;nbsp;documentation.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;%config&lt;/code&gt;: Flags the specified file as being a configuration&amp;nbsp;file.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;%attr&lt;/code&gt;: Sets the file permissions, its owner, and its group. It has the following&amp;nbsp;syntax:
  &lt;code&gt;%attr(&amp;lt;mode&amp;gt;, &amp;lt;user&amp;gt;, &amp;lt;group&amp;gt;) &amp;lt;filename&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;%defattr&lt;/code&gt;: Sets the default attributes for files and directories (it has a similar syntax&amp;nbsp;as &lt;code&gt;%attr&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;%ghost&lt;/code&gt;: Marks file(s) to not be installed as part of the package (such as log&amp;nbsp;files).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;%verify&lt;/code&gt;: Verifies the integrity of installed files. It can take up to nine&amp;nbsp;attributes:
  &lt;code&gt;%verify(mode md5 size maj min symlink mtime) &amp;lt;directory&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: There is another&amp;nbsp;section &lt;code&gt;%changelog&lt;/code&gt; which is not mentioned here. It is used for keeping track of the changes made to the package, similar to Git&amp;nbsp;history.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;1.3. Spec file&amp;nbsp;macros&lt;/h3&gt;
&lt;p&gt;The &lt;span class="caps"&gt;RPM&lt;/span&gt; system provides numerous built-in macros for working with spec files. These macros allow generalizing the spec file, by allowing users to not hard-code directory paths, but instead allows providing them using&amp;nbsp;macros.&lt;/p&gt;
&lt;p&gt;In addition to this, there also exist macros for debugging spec files. Some of these are listed&amp;nbsp;below:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;%dump&lt;/code&gt;: Prints out the macro&amp;nbsp;values.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;%{echo:message}&lt;/code&gt;: Prints message to&amp;nbsp;stderr.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;%{error:message}&lt;/code&gt;: Prints message to stderr and returns &lt;span class="caps"&gt;BADSPEC&lt;/span&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: It is possible to reference tags as macros in various section of the spec file. For example, the &amp;#8220;Name&amp;#8221; tag can be referenced&amp;nbsp;as &lt;code&gt;%{name}&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;1.3.1. Defining new&amp;nbsp;macros&lt;/h4&gt;
&lt;p&gt;To make the package management process easier, &lt;span class="caps"&gt;RPM&lt;/span&gt; allows creating custom macros using the following&amp;nbsp;syntax:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;%define&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;macro_name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It also allows expanding the result of shell commands using&amp;nbsp;the &lt;code&gt;%(&amp;lt;command&amp;gt;)&lt;/code&gt; syntax. So to create a macro which holds the list of files present in the current directory, one could&amp;nbsp;do:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;%define&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;list_files&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ls&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Custom-defined macros can later be referenced using the curly braces syntax. For example,&amp;nbsp;the &lt;code&gt;list_files&lt;/code&gt; macro will be referenced&amp;nbsp;as &lt;code&gt;%{list_files}&lt;/code&gt;.&lt;/p&gt;
&lt;h4&gt;1.3.2. Passing parameters to&amp;nbsp;macros&lt;/h4&gt;
&lt;p&gt;The macros can take one or more parameters, with the following&amp;nbsp;syntax:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nf"&gt;%define&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;macro_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;These parameters can be accessed using the following&amp;nbsp;directives:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;%0&lt;/code&gt;: The macro&amp;nbsp;name.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;%*&lt;/code&gt;: All parameters to the&amp;nbsp;macro.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;%#&lt;/code&gt;: The number of passed&amp;nbsp;parameters.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;%1&lt;/code&gt;, &lt;code&gt;%2&lt;/code&gt;, [&amp;#8230;]: The first, second, and remaining&amp;nbsp;parameters.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. Managing &lt;span class="caps"&gt;RPM&lt;/span&gt; builds with&amp;nbsp;Koji&lt;/h3&gt;
&lt;p&gt;Koji is an &lt;span class="caps"&gt;RPM&lt;/span&gt; build system which was originally developed and is still used by the Fedora project as their main build system. It allows scheduling build tasks, provides build reproducibility, and versions the data. Under the hood, Koji uses &lt;a href="https://en.wikipedia.org/wiki/Yum_(software)"&gt;Yum&lt;/a&gt; and &lt;a href="https://github.com/rpm-software-management/mock/wiki"&gt;Mock&lt;/a&gt; to create the builds and provides a web interface for numerous tasks, such as viewing and cancelling&amp;nbsp;builds.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: The web server can be configured to create a repository with each new tag, so whenever a build is completed and tagged, a new repository is&amp;nbsp;created.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The command line&amp;nbsp;tool &lt;code&gt;koji&lt;/code&gt;  allows initiating the package build. It has the following&amp;nbsp;syntax:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;koji&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;srpm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;scm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The example below shows how to initiate a build with some additional&amp;nbsp;options:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;koji&lt;span class="w"&gt; &lt;/span&gt;--config&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;config&lt;span class="w"&gt; &lt;/span&gt;file&lt;span class="w"&gt; &lt;/span&gt;path&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;build&lt;span class="w"&gt; &lt;/span&gt;--scratch&lt;span class="w"&gt; &lt;/span&gt;--wait&lt;span class="w"&gt; &lt;/span&gt;target&lt;span class="w"&gt; &lt;/span&gt;rpmbuild/SRPMS/&amp;lt;package&lt;span class="w"&gt; &lt;/span&gt;name&amp;gt;.src.rpm
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The options passed to this command are described&amp;nbsp;below:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;--config&lt;/code&gt;: Koji config file path (a Mock config can be created&amp;nbsp;with &lt;code&gt;koji mock-config&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--scratch&lt;/code&gt;: Builds the package without including it in the&amp;nbsp;release.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--wait&lt;/code&gt;: Waits for the build to&amp;nbsp;complete.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This command will create a new Koji task for this build, which can be tracked using the web interface. Another important task to perform is to tag the package (which allows organizing and filtering&amp;nbsp;packages):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$&lt;span class="w"&gt; &lt;/span&gt;koji&lt;span class="w"&gt; &lt;/span&gt;tag-pkg&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;package&lt;span class="w"&gt; &lt;/span&gt;name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: Koji is made up of several components and provides numerous other features which are explained in the &lt;a href="https://docs.pagure.org/koji"&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;3. Automating the build pipeline with continuous integration and&amp;nbsp;deployment&lt;/h3&gt;
&lt;p&gt;Continuous integration (&lt;span class="caps"&gt;CI&lt;/span&gt;) is the process of automating the build, test, and deployment (this step is part of continuous deployment) process of an application. In case of GitLab, this is done using&amp;nbsp;the &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; script file which follows the &lt;span class="caps"&gt;YAML&lt;/span&gt; format and is present in the repository&amp;#8217;s root directory. It is triggered each time an update is made to the code&amp;nbsp;base.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: Runners are isolated virtual machines which run the code defined&amp;nbsp;in &lt;code&gt;.gitlab-ci.yml&lt;/code&gt;. The GitLab &lt;a href="https://docs.gitlab.com/ee/ci/runners/README.html"&gt;documentation&lt;/a&gt; explains how a runner can be&amp;nbsp;configured.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;3.1. Defining variables and the base&amp;nbsp;image&lt;/h4&gt;
&lt;p&gt;Variables allow generalizing the file, and can be used later on.&amp;nbsp;The &lt;code&gt;image&lt;/code&gt; tag defines the base image to be used for running the&amp;nbsp;pipeline.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;&amp;lt;base image url&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;variables&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;NAME&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#39;value&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;3.2. Defining&amp;nbsp;stages&lt;/h4&gt;
&lt;p&gt;Before defining the jobs, all stages must be explicitly defined. A stage can run commands in sequence or in parallel. In lieu of building RPMs, the stages can be defined as&amp;nbsp;below:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;stages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;build&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;package&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;test&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;koji&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;deploy&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;3.3. Defining&amp;nbsp;jobs&lt;/h4&gt;
&lt;p&gt;A job defines a series of actions that must be performed when it is invoked. It can take numerous tags as options, such as the job stage, which allows sequencing the job&amp;nbsp;order.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;rpm_build_binary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# Name of the job.&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;*rpmbuild_deps&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# References a different job.&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# The scripts to execute.&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;rpmbuild -bb $SPEC_FILE --define &amp;quot;dist $DIST&amp;quot; --define &amp;quot;_topdir $(pwd)/rpmbuild&amp;quot; --define &amp;quot;_sourcedir $(pwd)&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;rpm -qpl rpmbuild/RPMS/noarch/*&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;except&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# Do not execute the job for the following.&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;tags&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# This job will not be executed when a new tag is created.&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;artifacts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# Lists the artifacts available to the job.&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;paths&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# Paths available to the job.&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;rpmbuild/RPMS/noarch/*&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;expire_in&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# The time period after which the job should expire.&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;1 week&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: By default, all jobs run independently of each other, but dependency can be injected by introducing&amp;nbsp;the &lt;code&gt;dependencies&lt;/code&gt; tag.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;3.4. Cross-referencing&amp;nbsp;jobs&lt;/h4&gt;
&lt;p&gt;The &lt;span class="caps"&gt;YAML&lt;/span&gt; format allows assigning an anchor to each job which can later be referenced in successive jobs, thus avoiding code&amp;nbsp;duplication.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;.rpmbuild_deps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;&amp;amp;rpmbuild_deps&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;before_script&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;yum install -y rpm-build rpmdevtools redhat-rpm-config&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;rpmbuild_deps&lt;/code&gt; job can now be expanded at a later stage&amp;nbsp;using &lt;code&gt;&amp;lt;&amp;lt;: *rpmbuild_deps&lt;/code&gt; as shown in the previous&amp;nbsp;section.&lt;/p&gt;
&lt;h4&gt;3.5. Hidden&amp;nbsp;jobs&lt;/h4&gt;
&lt;p&gt;To hide a job so that it&amp;#8217;s not processed by the GitLab-&lt;span class="caps"&gt;CI&lt;/span&gt; runner, the job&amp;#8217;s name must be preceded with a dot&amp;nbsp;(&lt;code&gt;.&lt;/code&gt;). This tells the runner to skip processing that&amp;nbsp;job.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: The &lt;a href="https://docs.gitlab.com/ee/ci/introduction/index.html"&gt;documentation&lt;/a&gt; on GitLab provides a good overview on the benefits and workflow of continuous integration and&amp;nbsp;deployment.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;This blog post explained the build process of an &lt;span class="caps"&gt;RPM&lt;/span&gt; package using&amp;nbsp;the &lt;code&gt;rpmbuild&lt;/code&gt; utility and provided a sample spec file which can be used for building the package. Koji was then introduced which serves as the task scheduler for building RPMs and for keeping a track history of all the running and completed jobs. Finally, it was shown how to automate the entire process using GitLab continuous&amp;nbsp;integration.&lt;/p&gt;
&lt;p&gt;The next step, once the GitLab-&lt;span class="caps"&gt;CI&lt;/span&gt; pipeline is finished, can be to deploy the built &lt;span class="caps"&gt;RPM&lt;/span&gt; on a remote machine (or the cloud) using a configuration management tool, like &lt;a href="https://puppet.com"&gt;Puppet&lt;/a&gt;.&lt;/p&gt;</content><category term="Automation"/></entry><entry><title>Google Summer of Code 2018 final evaluation report</title><link href="https://adl1995.github.io/google-summer-of-code-2018-final-evaluation-report.html" rel="alternate"/><published>2018-08-09T09:28:00+02:00</published><updated>2018-08-09T09:28:00+02:00</updated><author><name>Adeel Ahmad</name></author><id>tag:adl1995.github.io,2018-08-09:/google-summer-of-code-2018-final-evaluation-report.html</id><summary type="html">&lt;h4&gt;Link to GitHub repository: &lt;a href="https://github.com/BoostGSoC18/geometry"&gt;&lt;code&gt;https://github.com/BoostGSoC18/geometry&lt;/code&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The work is present under the following&amp;nbsp;branches:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/tree/feature/geodesic_direct"&gt;feature/geodesic_direct&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/tree/feature/karney_inverse"&gt;feature/karney_inverse&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/tree/example/distance_formula"&gt;example/distance_formula&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Summary&lt;/h3&gt;
&lt;p&gt;The goal of this project was to implement the direct and inverse geodesic algorithms in the &lt;a href="https://github.com/boostorg/geometry"&gt;Boost Geometry&lt;/a&gt; library. These methods were proposed by Charles Karney in his &lt;a href="https://arxiv.org/abs/1109.4448"&gt;paper&lt;/a&gt; in&amp;nbsp;2011.&lt;/p&gt;
&lt;p&gt;In a previous &lt;a href="https://adl1995.github.io/inaccuracy-in-boost-geometry-geodesic-algorithms-for-nearly-antipodal-points.html"&gt;blog post&lt;/a&gt;, the inaccuracy of the existing methods was discussed, which provided inconsistent results for nearly antipodal points. To monitor the progress, a weekly &lt;a href="https://github.com/BoostGSoC18/geometry/issues/1"&gt;report&lt;/a&gt; was provided through GitHub, which summarized the work done. Finally, &lt;a href="https://github.com/BoostGSoC18/geometry/issues/3"&gt;benchmarks&lt;/a&gt; were performed against existing methods in Boost Geometry. The performance metric used was execution time and&amp;nbsp;accuracy.&lt;/p&gt;
&lt;p&gt;Additional material, such as utility scripts for parsing the …&lt;/p&gt;</summary><content type="html">&lt;h4&gt;Link to GitHub repository: &lt;a href="https://github.com/BoostGSoC18/geometry"&gt;&lt;code&gt;https://github.com/BoostGSoC18/geometry&lt;/code&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The work is present under the following&amp;nbsp;branches:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/tree/feature/geodesic_direct"&gt;feature/geodesic_direct&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/tree/feature/karney_inverse"&gt;feature/karney_inverse&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/tree/example/distance_formula"&gt;example/distance_formula&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Summary&lt;/h3&gt;
&lt;p&gt;The goal of this project was to implement the direct and inverse geodesic algorithms in the &lt;a href="https://github.com/boostorg/geometry"&gt;Boost Geometry&lt;/a&gt; library. These methods were proposed by Charles Karney in his &lt;a href="https://arxiv.org/abs/1109.4448"&gt;paper&lt;/a&gt; in&amp;nbsp;2011.&lt;/p&gt;
&lt;p&gt;In a previous &lt;a href="https://adl1995.github.io/inaccuracy-in-boost-geometry-geodesic-algorithms-for-nearly-antipodal-points.html"&gt;blog post&lt;/a&gt;, the inaccuracy of the existing methods was discussed, which provided inconsistent results for nearly antipodal points. To monitor the progress, a weekly &lt;a href="https://github.com/BoostGSoC18/geometry/issues/1"&gt;report&lt;/a&gt; was provided through GitHub, which summarized the work done. Finally, &lt;a href="https://github.com/BoostGSoC18/geometry/issues/3"&gt;benchmarks&lt;/a&gt; were performed against existing methods in Boost Geometry. The performance metric used was execution time and&amp;nbsp;accuracy.&lt;/p&gt;
&lt;p&gt;Additional material, such as utility scripts for parsing the test data is present at: &lt;a href="https://github.com/adl1995/boost-geometry-extra"&gt;https://github.com/adl1995/boost-geometry-extra&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The programming competency task for this project is present at: &lt;a href="https://github.com/adl1995/geolib"&gt;https://github.com/adl1995/geolib&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;List of pull&amp;nbsp;requests&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/boostorg/geometry/pull/486"&gt;Introduce formula for Karney&amp;#8217;s direct geodesic&amp;nbsp;method&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/boostorg/geometry/pull/500"&gt;Introduce formula for Karney&amp;#8217;s inverse geodesic&amp;nbsp;method&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/boostorg/geometry/pull/502"&gt;Example: usage of distance&amp;nbsp;formula&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;List of&amp;nbsp;commits&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/5718b62405773a7b901dc30e06d734a9481761dc"&gt;[example] Add example on distance&amp;nbsp;formula&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/1bfeba16aa7c7e3db6a753ecf74a299244ab7014"&gt;[strategies] Add distance strategy for Karney&amp;#8217;s inverse&amp;nbsp;formula&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/2ff18246838d2b6a4fe2db549f26581ec3e5cb6b"&gt;[formulas][test] Update copyright&amp;nbsp;information&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/b44e3aed599061be89f8b314446fe02b9345fb2f"&gt;[formulas] Remove unused variable a12 in&amp;nbsp;karney_inverse&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/55c1691ddc0852613bc37bedc935be308913e3f7"&gt;[formulas] Fix incorrect argument to meridian_length()&amp;nbsp;function&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/daf03b488898f4aca65379e8982833f9c8cf1ba3"&gt;[formulas] Use namespace alias se for series_expansion in&amp;nbsp;karney_inverse&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/6432dfe96c0141dca0c619dba130c94cd6dc9304"&gt;[formulas][util] Reformat code in karney_inverse to use coefficient&amp;nbsp;containers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/73a2e2b8a4f47ea87fd63dc4de03b4e3dd797835"&gt;Merge branch &amp;#8216;feature/geodesic_direct&amp;#8217; into&amp;nbsp;feature/karney_inverse&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/1a52eaca9c35791984d2d3ba4ed0f5df2c963a23"&gt;[formulas] Resolve inaccuracy in starting point for Newton&amp;#8217;s&amp;nbsp;method&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/535ae96a8682fcc068016db212585e2c5994827a"&gt;[test] Add nearly antipodal points dataset for inverse geodesic&amp;nbsp;problem&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/4f0431085902aac5623fbf7db0909162a138a3f7"&gt;[formulas][test] Add Karney&amp;#8217;s inverse method in inverse test&amp;nbsp;cases&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/12bd41fd5db58d0f60d6544dafa981087f077aa5"&gt;[formulas] Store values from Karney&amp;#8217;s inverse method in result_inverse&amp;nbsp;structure&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/687df8e18a907b28e69ea67e76e0ad2271ad5d37"&gt;Merge branch &amp;#8216;develop&amp;#8217; into&amp;nbsp;feature/geodesic_direct&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/621950329ac5fe633b742403af1136d79c2c30a5"&gt;[util] Pass range into math::polyval() instead of&amp;nbsp;std::vector&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/1fe3b3bd75d01ec6795f53dcde8eb7cdba2955a4"&gt;[util] Pass SeriesOrder as template parameter in evaluate_coeffs_C3x()&amp;nbsp;function&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/b86a93bc5a81c066a9f93a986943874bca055cf2"&gt;[util] Rename math::normalize_values to&amp;nbsp;math::normalize_unit_vector&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/9ff4fbe94a9195411005d2a3a6c803b8736fb74b"&gt;[util] Use functions from math namespace instead of&amp;nbsp;std&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/9699b4909891d2bbba7a6dd84bda2bf2ec8ef511"&gt;[util][formulas] Rename normalize_angle function to&amp;nbsp;normalize_azimuth&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/c26483b9fcaed93a38811031f207b0d647dd704c"&gt;[util] Change static inline to inline in series_expansion.hpp&amp;nbsp;file&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/4d39eae2b688cadb0ec9581d667568d0fe2bb0e4"&gt;[util] Add BOOST_GEOMETRY_ASSERT in series expansion and normalization&amp;nbsp;function&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/9c96bec2bf8521e720a97674b3b93c626769e702"&gt;[util] Avoid passing array size using&amp;nbsp;std::vector&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/06eb057fa99c99587b549e0d507e6f9765e169a9"&gt;[formulas] Use assignment operator on the same line for&amp;nbsp;consistency&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/19f06fa04ec36a3388ab9ae8e5ccffb86d4776a6"&gt;[util] Reverse template argument order for series expansion&amp;nbsp;functions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/cc7e9e05c15410035dafc4d8daabc4efe3e25f5c"&gt;[formulas] Use namespace alias se for&amp;nbsp;series_expansion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/dedccdbdaeaddc2fb0d27568bbe2dc00b74da316"&gt;[formaulas][util] Define coefficient containers for computing series&amp;nbsp;expansions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/1972bcda3e5c204f2a0d0bda66239725828f1306"&gt;[formula][util] Pass boost::array to series expansion&amp;nbsp;functions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/6d0720b5ae1419469577229dbb0d7efb763e9b96"&gt;[formulas] Ensure reduced length and geodesic scale are computed in canonical&amp;nbsp;form&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/02577bda556df3cd3e596cc8eb1304ecac032b20"&gt;[formulas] Use midpoint of bracket when value lies outside of&amp;nbsp;range&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/ead0b188f915bd987eb2c44770e3dcf29e94473c"&gt;[formulas] Update bracketing values in Newton&amp;#8217;s&amp;nbsp;method&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/2ddad45616ec884589235b07fe81d4043a785f3a"&gt;[util] Move difference_angle function to&amp;nbsp;normalize_spheroidal_coordinates.hpp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/490b4df9434d45afb49ed081e3bf3eb251e55e07"&gt;Merge branch &amp;#8216;feature/geodesic_direct&amp;#8217; into&amp;nbsp;feature/karney_inverse&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/78411f63c8d552189c34bb3c5589696c9c4f6c8a"&gt;[doc][util][formulas][test] Add copyright information in updated&amp;nbsp;files&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/2c7a29e7dcfe71ec5a5a04f2d442477860e798d1"&gt;[formulas][util] Improve code formatting to conform with&amp;nbsp;guidelines&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/318a61dce0a1fc9256275ff97254d6264fba10c1"&gt;[formulas] Use updated functions for&amp;nbsp;normalization&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/d591d7a7bb7aa5d921f53e14e1198fb698cd2558"&gt;[util] Use existing normalize_spheroidal_coordinates class for normalizing an&amp;nbsp;angle&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/06fc06dc7faeb9008721a8b6b5120c47e0eaf312"&gt;[formulas] Add function lambda12 to regulate bracketing range in Karney&amp;nbsp;inverse&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/642a84c551d78eb9973070d23fb7109694093741"&gt;Merge branch &amp;#8216;feature/geodesic_direct&amp;#8217; into&amp;nbsp;feature/karney_inverse&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/943672bd3a7b2f6422cfa995ac94a04c73027c0e"&gt;[formulas] Use template argument &lt;span class="caps"&gt;CT&lt;/span&gt; instead of&amp;nbsp;double&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/2aac4027aa514f3f9ea5ad4c883c12404d85d351"&gt;Merge branch &amp;#8216;feature/geodesic_direct&amp;#8217; into&amp;nbsp;feature/karney_inverse&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/d11b7160b2460f78830844c6892af2a94f6771a6"&gt;[formulas] Fix calculation of t for finding the geodesic scale&amp;nbsp;(M12)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/15d5cd628100ef4f605b5b9ae5ee7aa484f33af8"&gt;[formulas] Perform normalization on starting guess if it passes the sanity&amp;nbsp;check&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/2bde1c119a3dc04ce3c24261712e894824ff3d05"&gt;[formulas] Solve the astroid equation for inverse&amp;nbsp;problem&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/5bb581c932295f0243386cb9504c8b7409bbab2b"&gt;[formulas] Flip sign of&amp;nbsp;cos_lam12&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/71fbc86f44ec2d8ebf261808f233ec10cabf3260"&gt;Merge branch &amp;#8216;feature/geodesic_direct&amp;#8217; into&amp;nbsp;feature/karney_inverse&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/2cba2fa83fe62c52efd5fc2d7fcb1401264701cf"&gt;[test] Calculate geodesic scale (M12) using high precision&amp;nbsp;arithmetic&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/485a2f9a0f6d245ae3b2fb3762e664ced7754ea9"&gt;[formulas] Find starting point for inverse problem (short&amp;nbsp;lines)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/0344ba5c1a3e596a4558eeb677324bcf7c7d2c30"&gt;[formulas] Handle case for equatorial points in inverse&amp;nbsp;problem&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/fe1b9ff8ba3b4f7ed799ae4e5a9c5c69bd795f5f"&gt;[test] Update geodesic scale (M12) in GeographicLib&amp;nbsp;dataset&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/96074e2ab2e9f3bddd81f56c5d23438574c0c040"&gt;[test] Use series order 2 for Karney&amp;#8217;s direct&amp;nbsp;method&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/043f401fbe22a2fbf8c3cb3e852a16c00e6fd942"&gt;[util] Use Maxima generated function for computing C3x&amp;nbsp;coefficients&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/276e8e6d7a673a5e9676c4135f23805697a80eee"&gt;[formulas] Add function for computing the length at the&amp;nbsp;meridians&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/efd30c8ccc581a6669c836675f5d87ad0f26e733"&gt;Merge branch &amp;#8216;feature/geodesic_direct&amp;#8217; into&amp;nbsp;feature/karney_inverse&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/8a2dd63ccf85f80134d9d88e049ac6d272531f0f"&gt;[util] Move sin_cos_series function to&amp;nbsp;series_expansion.hpp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/2e064c0f0d1cb0e9fde38643413a76257d11346e"&gt;[formulas] Arrange points in canonical form for inverse geodesic&amp;nbsp;problem&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/7561d68c086d85ce990e32196dc95874f11ac4c4"&gt;[util] Add function for returning NaN (not a&amp;nbsp;number)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/49e0a4fdaee2a78b5691c3c0db616fbb9ecd4f28"&gt;Merge branch &amp;#8216;feature/geodesic_direct&amp;#8217; into&amp;nbsp;feature/karney_inverse&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/df0cafdd190d1ce51e75dab32f9e302527acea49"&gt;[doc] Move Maxima scripts for geodesics to&amp;nbsp;doc/other/maxima/geod.mac&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/b9b0f85560e719278e8d4e9fdc7da998dd5c7857"&gt;[util] Add functions to normalize / sum two given values&amp;nbsp;(angles)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/a10815366a58d1dd058dd0447843fbac89327b37"&gt;[test] Add comment providing dataset source and how it is&amp;nbsp;parsed&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/cc19342b4eac30183fd6f40d4d4d0a8b36fed885"&gt;[util] Remove duplicated Maxima code from&amp;nbsp;series_expansion.hpp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/831873752dd7875e485a87a70e6ce2e75803a672"&gt;[formulas] Use constant type variables for&amp;nbsp;comparison&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/1ed5f103f48356f8c3b300905107117f3eca76f5"&gt;[formulas] Move SeriesOrder to the end of template parameter&amp;nbsp;list&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/4fa4a8206a65ddd63c136989de026c0bb0d3e64f"&gt;[util] Update series expansion for&amp;nbsp;C3x&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/6ff97a69910529d31f880d0674a4015723156864"&gt;[test] Test Karney&amp;#8217;s method on antipodal points&amp;nbsp;dataset&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/3c21b13c9161f0d7bd3169c44a23ebdd87846bbb"&gt;[test] Add geodesic length to antipodal points&amp;nbsp;dataset&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/fdbb3886d3ddfac617f6df0efe83d31e360f1d77"&gt;[test] Add nearly antipodal points dataset for direct geodesic&amp;nbsp;problem&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/b8a225e1cfa057b15aded81a9822219fee3323db"&gt;[formulas] Fix direct geodesic method by performing&amp;nbsp;normalization&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/3dd6bce7202e3050a8f5d1c26b7111d0689a591a"&gt;[util] Add functions for normalizing and evaluating&amp;nbsp;polynomial&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/1e31876e9446d359d4b389d916e6d61486ccc057"&gt;[util] Modify function for evaluting C3x&amp;nbsp;coefficient&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/afe7bc679c7e90852e6e4441259f12dda4db7962"&gt;[test] Add direct Karney&amp;#8217;s method to direct.cpp test&amp;nbsp;cases&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/6a2897db25844d1bbcc8dc59bf88328e342cb5a9"&gt;[formulas][util] Add missing import and function return&amp;nbsp;type&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/0c2b8cdbab3fd6e5b525617641b260cba26ee3b9"&gt;[formulas] Make variable declarations&amp;nbsp;constant&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/ac4e483c9271507aa84b30aadaba32a221efab5f"&gt;[formulas] Compute the reduced length and geodesic scale using Karney&amp;#8217;s direct&amp;nbsp;method&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/a0a55f361f6d3f4931369ec2d60ae28722a2d718"&gt;[util] Evaluate series and coefficients for A2 and C2 using series&amp;nbsp;expansion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/9e2c74afc8523374cc9801817b48e40bcc6f78c3"&gt;[formulas] Compute the longitude for second point using the longitudinal&amp;nbsp;difference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/915219198602320924af1c88f6db02b669233beb"&gt;[util] Add functions to evaluate coefficients for A3 and C3 using series&amp;nbsp;expansion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/881c73c90b131525deb03a59a4350868be8ffbe2"&gt;[formulas] Compute the latitude for second point following Karney&amp;#8217;s&amp;nbsp;method&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/6ce1b099cb33e94d5577a089460af4310962a964"&gt;[util] Evaluate coefficients for C1p using series&amp;nbsp;expansion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/3700b4fafa16959d3b64233ff4a3611988eabccb"&gt;[util] Move series expansion functions to&amp;nbsp;util/series_expansion.hpp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/b6fcee8f303d37016449f7014c31cfcf00b926ae"&gt;[formulas] Compute sin cos series using Clenshaw&amp;nbsp;summation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/320891caefdfbb920b9e81259c14d19889faf70c"&gt;[utils] Add function for normalizing and rounding off an&amp;nbsp;angle&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/c73ea927c4f88bd3da590757ffa3fec77ba4aa72"&gt;[formulas] Add function for evaluating coefficients for&amp;nbsp;C1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/8d5d3bc8a96b819830a25d96bd1200d8389f68da"&gt;[formulas] Add draft of direct geodesic problem from Karney&amp;nbsp;(2011)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BoostGSoC18/geometry/commit/302e24066776301b5fdae795d43e3497a137678e"&gt;[utils] Add function sin_cos_degrees for evaluating sine and cosine&amp;nbsp;function&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;List of commit&amp;nbsp;diffs&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;commit 5718b62405773a7b901dc30e06d734a9481761dc
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Thu Jul 26 16:27:59 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [example] Add example on distance formula

&lt;span class="w"&gt; &lt;/span&gt;   The formula used is Karney&amp;#39;s direct method.

&lt;span class="gh"&gt;diff --git a/example/ml03_distance_formula.cpp b/example/ml03_distance_formula.cpp&lt;/span&gt;
new file mode 100644
&lt;span class="gh"&gt;index 0000000..6ddd9fc&lt;/span&gt;
&lt;span class="gd"&gt;--- /dev/null&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/example/ml03_distance_formula.cpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -0,0 +1,37 @@&lt;/span&gt;
&lt;span class="gi"&gt;+// Boost.Geometry&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+// Copyright (c) 2018 Adeel Ahmad, Islamabad, Pakistan.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+// Contributed and/or modified by Adeel Ahmad, as part of Google Summer of Code 2018 program.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+// Use, modification and distribution is subject to the Boost Software License,&lt;/span&gt;
&lt;span class="gi"&gt;+// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at&lt;/span&gt;
&lt;span class="gi"&gt;+// http://www.boost.org/LICENSE_1_0.txt)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+// Formula example - Show how to use Karney&amp;#39;s direct method.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+#include &amp;lt;boost/geometry.hpp&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+#include &amp;lt;boost/geometry/formulas/karney_direct.hpp&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+using namespace boost::geometry;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+int main()&lt;/span&gt;
&lt;span class="gi"&gt;+{&lt;/span&gt;
&lt;span class="gi"&gt;+    double lon1_deg = 0.;&lt;/span&gt;
&lt;span class="gi"&gt;+    double lat1_deg = 73.114273316483;&lt;/span&gt;
&lt;span class="gi"&gt;+    double distance_m = 19992866.6147806;&lt;/span&gt;
&lt;span class="gi"&gt;+    double azi12_deg = 78.154765899661;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    // Create an alias of the formula.&lt;/span&gt;
&lt;span class="gi"&gt;+    typedef formula::karney_direct&amp;lt;double, true, true, true, true, 8&amp;gt; karney_direct;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    // Structure to hold the resulting values.&lt;/span&gt;
&lt;span class="gi"&gt;+    formula::result_direct&amp;lt;double&amp;gt; result;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    // WGS-84 spheroid.&lt;/span&gt;
&lt;span class="gi"&gt;+    srs::spheroid&amp;lt;double&amp;gt; spheroid(6378137.0, 6356752.3142451793);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    result = karney_direct::apply(lon1_deg, lat1_deg, distance_m, azi12_deg, spheroid);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    return 0;&lt;/span&gt;
&lt;span class="gi"&gt;+}&lt;/span&gt;

commit 1bfeba16aa7c7e3db6a753ecf74a299244ab7014
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Jul 20 13:52:20 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [strategies] Add distance strategy for Karney&amp;#39;s inverse formula

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/strategies/geographic/distance_karney.hpp b/include/boost/geometry/strategies/geographic/distance_karney.hpp&lt;/span&gt;
new file mode 100644
&lt;span class="gh"&gt;index 0000000..2391496&lt;/span&gt;
&lt;span class="gd"&gt;--- /dev/null&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/strategies/geographic/distance_karney.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -0,0 +1,116 @@&lt;/span&gt;
&lt;span class="gi"&gt;+// Boost.Geometry&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+// Copyright (c) 2018 Adeel Ahmad, Islamabad, Pakistan.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+// Contributed and/or modified by Adeel Ahmad, as part of Google Summer of Code 2018 program.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+// Use, modification and distribution is subject to the Boost Software License,&lt;/span&gt;
&lt;span class="gi"&gt;+// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at&lt;/span&gt;
&lt;span class="gi"&gt;+// http://www.boost.org/LICENSE_1_0.txt)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+#ifndef BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_KARNEY_HPP&lt;/span&gt;
&lt;span class="gi"&gt;+#define BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_KARNEY_HPP&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+#include &amp;lt;boost/geometry/strategies/geographic/distance.hpp&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+#include &amp;lt;boost/geometry/strategies/geographic/parameters.hpp&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+namespace boost { namespace geometry&lt;/span&gt;
&lt;span class="gi"&gt;+{&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+namespace strategy { namespace distance&lt;/span&gt;
&lt;span class="gi"&gt;+{&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+/*!&lt;/span&gt;
&lt;span class="gi"&gt;+\brief The solution of the inverse problem of geodesics on latlong coordinates,&lt;/span&gt;
&lt;span class="gi"&gt;+       after Karney (2011).&lt;/span&gt;
&lt;span class="gi"&gt;+\ingroup distance&lt;/span&gt;
&lt;span class="gi"&gt;+\tparam Spheroid The reference spheroid model&lt;/span&gt;
&lt;span class="gi"&gt;+\tparam CalculationType \tparam_calculation&lt;/span&gt;
&lt;span class="gi"&gt;+\author See&lt;/span&gt;
&lt;span class="gi"&gt;+- Charles F.F Karney, Algorithms for geodesics, 2011&lt;/span&gt;
&lt;span class="gi"&gt;+https://arxiv.org/pdf/1109.4448.pdf&lt;/span&gt;
&lt;span class="gi"&gt;+*/&lt;/span&gt;
&lt;span class="gi"&gt;+template&lt;/span&gt;
&lt;span class="gi"&gt;+&amp;lt;&lt;/span&gt;
&lt;span class="gi"&gt;+    typename Spheroid = srs::spheroid&amp;lt;double&amp;gt;,&lt;/span&gt;
&lt;span class="gi"&gt;+    typename CalculationType = void&lt;/span&gt;
&lt;span class="gi"&gt;+&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+class karney&lt;/span&gt;
&lt;span class="gi"&gt;+    : public strategy::distance::geographic&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;lt;&lt;/span&gt;
&lt;span class="gi"&gt;+            strategy::karney, Spheroid, CalculationType&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+{&lt;/span&gt;
&lt;span class="gi"&gt;+    typedef strategy::distance::geographic&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;lt;&lt;/span&gt;
&lt;span class="gi"&gt;+            strategy::karney, Spheroid, CalculationType&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt; base_type;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+public:&lt;/span&gt;
&lt;span class="gi"&gt;+    inline karney()&lt;/span&gt;
&lt;span class="gi"&gt;+        : base_type()&lt;/span&gt;
&lt;span class="gi"&gt;+    {}&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    explicit inline karney(Spheroid const&amp;amp; spheroid)&lt;/span&gt;
&lt;span class="gi"&gt;+        : base_type(spheroid)&lt;/span&gt;
&lt;span class="gi"&gt;+    {}&lt;/span&gt;
&lt;span class="gi"&gt;+};&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS&lt;/span&gt;
&lt;span class="gi"&gt;+namespace services&lt;/span&gt;
&lt;span class="gi"&gt;+{&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+template &amp;lt;typename Spheroid, typename CalculationType&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+struct tag&amp;lt;karney&amp;lt;Spheroid, CalculationType&amp;gt; &amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+{&lt;/span&gt;
&lt;span class="gi"&gt;+    typedef strategy_tag_distance_point_point type;&lt;/span&gt;
&lt;span class="gi"&gt;+};&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+template &amp;lt;typename Spheroid, typename CalculationType, typename P1, typename P2&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+struct return_type&amp;lt;karney&amp;lt;Spheroid, CalculationType&amp;gt;, P1, P2&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    : karney&amp;lt;Spheroid, CalculationType&amp;gt;::template calculation_type&amp;lt;P1, P2&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+{};&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+template &amp;lt;typename Spheroid, typename CalculationType&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+struct comparable_type&amp;lt;karney&amp;lt;Spheroid, CalculationType&amp;gt; &amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+{&lt;/span&gt;
&lt;span class="gi"&gt;+    typedef karney&amp;lt;Spheroid, CalculationType&amp;gt; type;&lt;/span&gt;
&lt;span class="gi"&gt;+};&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+template &amp;lt;typename Spheroid, typename CalculationType&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+struct get_comparable&amp;lt;karney&amp;lt;Spheroid, CalculationType&amp;gt; &amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+{&lt;/span&gt;
&lt;span class="gi"&gt;+    static inline karney&amp;lt;Spheroid, CalculationType&amp;gt; apply(karney&amp;lt;Spheroid, CalculationType&amp;gt; const&amp;amp; input)&lt;/span&gt;
&lt;span class="gi"&gt;+    {&lt;/span&gt;
&lt;span class="gi"&gt;+        return input;&lt;/span&gt;
&lt;span class="gi"&gt;+    }&lt;/span&gt;
&lt;span class="gi"&gt;+};&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+template &amp;lt;typename Spheroid, typename CalculationType, typename P1, typename P2&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+struct result_from_distance&amp;lt;karney&amp;lt;Spheroid, CalculationType&amp;gt;, P1, P2 &amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+{&lt;/span&gt;
&lt;span class="gi"&gt;+    template &amp;lt;typename T&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    static inline typename return_type&amp;lt;karney&amp;lt;Spheroid, CalculationType&amp;gt;, P1, P2&amp;gt;::type&lt;/span&gt;
&lt;span class="gi"&gt;+        apply(karney&amp;lt;Spheroid, CalculationType&amp;gt; const&amp;amp; , T const&amp;amp; value)&lt;/span&gt;
&lt;span class="gi"&gt;+    {&lt;/span&gt;
&lt;span class="gi"&gt;+        return value;&lt;/span&gt;
&lt;span class="gi"&gt;+    }&lt;/span&gt;
&lt;span class="gi"&gt;+};&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+} // namespace services&lt;/span&gt;
&lt;span class="gi"&gt;+#endif // DOXYGEN_NO_STRATEGY_SPECIALIZATIONS&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+}} // namespace strategy::distance&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+}} // namespace boost::geometry&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+#endif // BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_KARNEY_HPP&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/include/boost/geometry/strategies/geographic/parameters.hpp b/include/boost/geometry/strategies/geographic/parameters.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 92ebe08..7c82902 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/strategies/geographic/parameters.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/strategies/geographic/parameters.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -15,6 +15,8 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;#include &amp;lt;boost/geometry/formulas/thomas_inverse.hpp&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;#include &amp;lt;boost/geometry/formulas/vincenty_direct.hpp&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;#include &amp;lt;boost/geometry/formulas/vincenty_inverse.hpp&amp;gt;
&lt;span class="gi"&gt;+#include &amp;lt;boost/geometry/formulas/karney_direct.hpp&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+#include &amp;lt;boost/geometry/formulas/karney_inverse.hpp&amp;gt;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;#include &amp;lt;boost/mpl/assert.hpp&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;#include &amp;lt;boost/mpl/integral_c.hpp&amp;gt;
&lt;span class="gu"&gt;@@ -136,6 +138,46 @@ struct vincenty&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    {};
&lt;span class="w"&gt; &lt;/span&gt;};

&lt;span class="gi"&gt;+struct karney&lt;/span&gt;
&lt;span class="gi"&gt;+{&lt;/span&gt;
&lt;span class="gi"&gt;+    template&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;lt;&lt;/span&gt;
&lt;span class="gi"&gt;+        typename CT,&lt;/span&gt;
&lt;span class="gi"&gt;+        bool EnableCoordinates = true,&lt;/span&gt;
&lt;span class="gi"&gt;+        bool EnableReverseAzimuth = false,&lt;/span&gt;
&lt;span class="gi"&gt;+        bool EnableReducedLength = false,&lt;/span&gt;
&lt;span class="gi"&gt;+        bool EnableGeodesicScale = false,&lt;/span&gt;
&lt;span class="gi"&gt;+        size_t SeriesOrder = 8&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    struct direct&lt;/span&gt;
&lt;span class="gi"&gt;+            : formula::karney_direct&lt;/span&gt;
&lt;span class="gi"&gt;+              &amp;lt;&lt;/span&gt;
&lt;span class="gi"&gt;+                  CT, EnableCoordinates, EnableReverseAzimuth,&lt;/span&gt;
&lt;span class="gi"&gt;+                  EnableReducedLength, EnableGeodesicScale,&lt;/span&gt;
&lt;span class="gi"&gt;+                  SeriesOrder&lt;/span&gt;
&lt;span class="gi"&gt;+              &amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    {};&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    template&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;lt;&lt;/span&gt;
&lt;span class="gi"&gt;+        typename CT,&lt;/span&gt;
&lt;span class="gi"&gt;+        bool EnableDistance,&lt;/span&gt;
&lt;span class="gi"&gt;+        bool EnableAzimuth,&lt;/span&gt;
&lt;span class="gi"&gt;+        bool EnableReverseAzimuth = false,&lt;/span&gt;
&lt;span class="gi"&gt;+        bool EnableReducedLength = false,&lt;/span&gt;
&lt;span class="gi"&gt;+        bool EnableGeodesicScale = false,&lt;/span&gt;
&lt;span class="gi"&gt;+        size_t SeriesOrder = 8&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    struct inverse&lt;/span&gt;
&lt;span class="gi"&gt;+        : formula::karney_inverse&lt;/span&gt;
&lt;span class="gi"&gt;+            &amp;lt;&lt;/span&gt;
&lt;span class="gi"&gt;+                CT, EnableDistance,&lt;/span&gt;
&lt;span class="gi"&gt;+                EnableAzimuth, EnableReverseAzimuth,&lt;/span&gt;
&lt;span class="gi"&gt;+                EnableReducedLength, EnableGeodesicScale,&lt;/span&gt;
&lt;span class="gi"&gt;+                SeriesOrder&lt;/span&gt;
&lt;span class="gi"&gt;+            &amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    {};&lt;/span&gt;
&lt;span class="gi"&gt;+};&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;template &amp;lt;typename FormulaPolicy&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;struct default_order
&lt;span class="gu"&gt;@@ -162,6 +204,11 @@ struct default_order&amp;lt;vincenty&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    : boost::mpl::integral_c&amp;lt;unsigned int, 4&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;{};

&lt;span class="gi"&gt;+template&amp;lt;&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+struct default_order&amp;lt;karney&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    : boost::mpl::integral_c&amp;lt;unsigned int, 8&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+{};&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;}}} // namespace boost::geometry::strategy


&lt;span class="gh"&gt;diff --git a/include/boost/geometry/strategies/strategies.hpp b/include/boost/geometry/strategies/strategies.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 7d6cb61..11019bd 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/strategies/strategies.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/strategies/strategies.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -96,6 +96,7 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;#include &amp;lt;boost/geometry/strategies/geographic/distance_cross_track_point_box.hpp&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;#include &amp;lt;boost/geometry/strategies/geographic/distance_thomas.hpp&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;#include &amp;lt;boost/geometry/strategies/geographic/distance_vincenty.hpp&amp;gt;
&lt;span class="gi"&gt;+#include &amp;lt;boost/geometry/strategies/geographic/distance_karney.hpp&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;#include &amp;lt;boost/geometry/strategies/geographic/envelope_segment.hpp&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;#include &amp;lt;boost/geometry/strategies/geographic/intersection.hpp&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;//#include &amp;lt;boost/geometry/strategies/geographic/intersection_elliptic.hpp&amp;gt;

commit 2ff18246838d2b6a4fe2db549f26581ec3e5cb6b
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Mon Jul 16 19:06:20 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [formulas][test] Update copyright information

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/formulas/karney_inverse.hpp b/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 28cdae7..01fea66 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,11 +1,26 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;// Boost.Geometry

&lt;span class="gd"&gt;-// Contributed and/or modified by Adeel Ahmad.&lt;/span&gt;
&lt;span class="gi"&gt;+// Copyright (c) 2018 Adeel Ahmad, Islamabad, Pakistan.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+// Contributed and/or modified by Adeel Ahmad, as part of Google Summer of Code 2018 program.&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;// Use, modification and distribution is subject to the Boost Software License,
&lt;span class="w"&gt; &lt;/span&gt;// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
&lt;span class="w"&gt; &lt;/span&gt;// http://www.boost.org/LICENSE_1_0.txt)

&lt;span class="gi"&gt;+// This file is converted from GeographicLib, https://geographiclib.sourceforge.io&lt;/span&gt;
&lt;span class="gi"&gt;+// GeographicLib is originally written by Charles Karney.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+// Author: Charles Karney (2008-2017)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+// Last updated version of GeographicLib: 1.49&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+// Original copyright notice:&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+// Copyright (c) Charles Karney (2008-2017) &amp;lt;charles@karney.com&amp;gt; and licensed&lt;/span&gt;
&lt;span class="gi"&gt;+// under the MIT/X11 License. For more information, see&lt;/span&gt;
&lt;span class="gi"&gt;+// https://geographiclib.sourceforge.io&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;#ifndef BOOST_GEOMETRY_FORMULAS_KARNEY_INVERSE_HPP
&lt;span class="w"&gt; &lt;/span&gt;#define BOOST_GEOMETRY_FORMULAS_KARNEY_INVERSE_HPP

&lt;span class="gh"&gt;diff --git a/test/formulas/inverse.cpp b/test/formulas/inverse.cpp&lt;/span&gt;
&lt;span class="gh"&gt;index 07c0fbc..d9617b5 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/test/formulas/inverse.cpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/test/formulas/inverse.cpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -4,7 +4,7 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;// Copyright (c) 2016-2017 Oracle and/or its affiliates.

&lt;span class="w"&gt; &lt;/span&gt;// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
&lt;span class="gd"&gt;-// Contributed and/or modified by Adeel Ahmad, as part of Google Summer of Code 2018 program.&lt;/span&gt;
&lt;span class="gi"&gt;+// Contributed and/or modified by Adeel Ahmad, as part of Google Summer of Code 2018 program&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;// Use, modification and distribution is subject to the Boost Software License,
&lt;span class="w"&gt; &lt;/span&gt;// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at

commit b44e3aed599061be89f8b314446fe02b9345fb2f
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Jul 13 12:15:35 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [formulas] Remove unused variable a12 in karney_inverse

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/formulas/karney_inverse.hpp b/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 8c509af..28cdae7 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -196,7 +196,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT const dn1 = sqrt(c1 + ep2 * math::sqr(sin_beta1));
&lt;span class="w"&gt; &lt;/span&gt;        CT const dn2 = sqrt(c1 + ep2 * math::sqr(sin_beta2));

&lt;span class="gd"&gt;-        CT a12, sigma12;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT sigma12;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT m12x, s12x, M21;

&lt;span class="w"&gt; &lt;/span&gt;        // Index zero element of coeffs_C1 is unused.
&lt;span class="gu"&gt;@@ -244,7 +244,6 @@ public:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;                m12x *= b;
&lt;span class="w"&gt; &lt;/span&gt;                s12x *= b;
&lt;span class="gd"&gt;-                a12 = sigma12 / math::d2r&amp;lt;CT&amp;gt;();&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            }
&lt;span class="w"&gt; &lt;/span&gt;            else
&lt;span class="w"&gt; &lt;/span&gt;            {
&lt;span class="gu"&gt;@@ -270,7 +269,6 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            {
&lt;span class="w"&gt; &lt;/span&gt;                result.geodesic_scale = cos(sigma12);
&lt;span class="w"&gt; &lt;/span&gt;            }
&lt;span class="gd"&gt;-            a12 = lon12 / one_minus_f;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="w"&gt; &lt;/span&gt;        else if (!meridian)
&lt;span class="w"&gt; &lt;/span&gt;        {
&lt;span class="gu"&gt;@@ -299,7 +297,6 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                }

&lt;span class="w"&gt; &lt;/span&gt;                // Convert to radians.
&lt;span class="gd"&gt;-                a12 = sigma12 / math::d2r&amp;lt;CT&amp;gt;();&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                omega12 = lam12 / (one_minus_f * dnm);
&lt;span class="w"&gt; &lt;/span&gt;            }
&lt;span class="w"&gt; &lt;/span&gt;            else
&lt;span class="gu"&gt;@@ -403,7 +400,6 @@ public:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;                m12x *= b;
&lt;span class="w"&gt; &lt;/span&gt;                s12x *= b;
&lt;span class="gd"&gt;-                a12 = sigma12 / math::d2r&amp;lt;CT&amp;gt;();&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            }
&lt;span class="w"&gt; &lt;/span&gt;        }


commit 55c1691ddc0852613bc37bedc935be308913e3f7
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Jul 13 12:08:00 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [formulas] Fix incorrect argument to meridian_length() function

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/formulas/karney_inverse.hpp b/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index ae0a368..8c509af 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -235,7 +235,6 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                                             m12x, dummy, result.geodesic_scale,
&lt;span class="w"&gt; &lt;/span&gt;                                             M21, coeffs_C1);

&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            if (sigma12 &amp;lt; c1 || m12x &amp;gt;= c0)
&lt;span class="w"&gt; &lt;/span&gt;            {
&lt;span class="w"&gt; &lt;/span&gt;                if (sigma12 &amp;lt; c3 * tiny)
&lt;span class="gu"&gt;@@ -254,7 +253,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            }
&lt;span class="w"&gt; &lt;/span&gt;        }

&lt;span class="gd"&gt;-        CT omega12, sin_omega12, cos_omega12;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT omega12;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        if (!meridian &amp;amp;&amp;amp; sin_beta1 == c0 &amp;amp;&amp;amp;
&lt;span class="w"&gt; &lt;/span&gt;            (f &amp;lt;= c0 || lon12_error &amp;gt;= f * c180))
&lt;span class="gu"&gt;@@ -273,7 +272,6 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            }
&lt;span class="w"&gt; &lt;/span&gt;            a12 = lon12 / one_minus_f;
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        else if (!meridian)
&lt;span class="w"&gt; &lt;/span&gt;        {
&lt;span class="w"&gt; &lt;/span&gt;            // If point1 and point2 belong within a hemisphere bounded by a
&lt;span class="gu"&gt;@@ -352,6 +350,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                        sin_alpha1a = sin_alpha1;
&lt;span class="w"&gt; &lt;/span&gt;                        cos_alpha1a = cos_alpha1;
&lt;span class="w"&gt; &lt;/span&gt;                    }
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                    if (iteration &amp;lt; max_iterations &amp;amp;&amp;amp; dv &amp;gt; c0)
&lt;span class="w"&gt; &lt;/span&gt;                    {
&lt;span class="w"&gt; &lt;/span&gt;                        CT diff_alpha1 = -v / dv;
&lt;span class="gu"&gt;@@ -390,17 +389,17 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                    tripn = false;
&lt;span class="w"&gt; &lt;/span&gt;                    tripb = (std::abs(sin_alpha1a - sin_alpha1) + (cos_alpha1a - cos_alpha1) &amp;lt; tol_bisection ||
&lt;span class="w"&gt; &lt;/span&gt;                             std::abs(sin_alpha1 - sin_alpha1b) + (cos_alpha1 - cos_alpha1b) &amp;lt; tol_bisection);
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                }

&lt;span class="w"&gt; &lt;/span&gt;                CT dummy;
&lt;span class="gi"&gt;+                se::coeffs_C1&amp;lt;SeriesOrder, CT&amp;gt; const coeffs_C1_eps(eps);&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                // Ensure that the reduced length and geodesic scale are computed in
&lt;span class="w"&gt; &lt;/span&gt;                // a &amp;quot;canonical&amp;quot; way, with the I2 integral.
&lt;span class="gd"&gt;-                meridian_length(n, ep2, sigma12, sin_sigma1, cos_sigma1, dn1,&lt;/span&gt;
&lt;span class="gd"&gt;-                                                 sin_sigma2, cos_sigma2, dn2,&lt;/span&gt;
&lt;span class="gd"&gt;-                                                 cos_beta1, cos_beta2, s12x,&lt;/span&gt;
&lt;span class="gd"&gt;-                                                 m12x, dummy, result.geodesic_scale,&lt;/span&gt;
&lt;span class="gd"&gt;-                                                 M21, coeffs_C1);&lt;/span&gt;
&lt;span class="gi"&gt;+                meridian_length(eps, ep2, sigma12, sin_sigma1, cos_sigma1, dn1,&lt;/span&gt;
&lt;span class="gi"&gt;+                                                   sin_sigma2, cos_sigma2, dn2,&lt;/span&gt;
&lt;span class="gi"&gt;+                                                   cos_beta1, cos_beta2, s12x,&lt;/span&gt;
&lt;span class="gi"&gt;+                                                   m12x, dummy, result.geodesic_scale,&lt;/span&gt;
&lt;span class="gi"&gt;+                                                   M21, coeffs_C1_eps);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;                m12x *= b;
&lt;span class="w"&gt; &lt;/span&gt;                s12x *= b;
&lt;span class="gu"&gt;@@ -874,11 +873,11 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            else
&lt;span class="w"&gt; &lt;/span&gt;            {
&lt;span class="w"&gt; &lt;/span&gt;                CT dummy;
&lt;span class="gd"&gt;-                meridian_length(n, eps, sigma12, sin_sigma1, cos_sigma1, dn1,&lt;/span&gt;
&lt;span class="gd"&gt;-                                                 sin_sigma2, cos_sigma2, dn2,&lt;/span&gt;
&lt;span class="gd"&gt;-                                                 cos_beta1, cos_beta2, dummy,&lt;/span&gt;
&lt;span class="gd"&gt;-                                                 diff_lam12, dummy, dummy,&lt;/span&gt;
&lt;span class="gd"&gt;-                                                 dummy, coeffs_C1);&lt;/span&gt;
&lt;span class="gi"&gt;+                meridian_length(eps, ep2, sigma12, sin_sigma1, cos_sigma1, dn1,&lt;/span&gt;
&lt;span class="gi"&gt;+                                                   sin_sigma2, cos_sigma2, dn2,&lt;/span&gt;
&lt;span class="gi"&gt;+                                                   cos_beta1, cos_beta2, dummy,&lt;/span&gt;
&lt;span class="gi"&gt;+                                                   diff_lam12, dummy, dummy,&lt;/span&gt;
&lt;span class="gi"&gt;+                                                   dummy, coeffs_C1);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;                diff_lam12 *= one_minus_f / (cos_alpha2 * cos_beta2);
&lt;span class="w"&gt; &lt;/span&gt;            }

commit daf03b488898f4aca65379e8982833f9c8cf1ba3
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Tue Jul 10 11:52:20 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [formulas] Use namespace alias se for series_expansion in karney_inverse

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/formulas/karney_inverse.hpp b/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 6b6a3b5..ae0a368 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -24,6 +24,7 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;namespace boost { namespace geometry { namespace formula
&lt;span class="w"&gt; &lt;/span&gt;{

&lt;span class="gi"&gt;+namespace se = series_expansion;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;/*!
&lt;span class="w"&gt; &lt;/span&gt;\brief The solution of the inverse problem of geodesics on latlong coordinates,
&lt;span class="gu"&gt;@@ -199,7 +200,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT m12x, s12x, M21;

&lt;span class="w"&gt; &lt;/span&gt;        // Index zero element of coeffs_C1 is unused.
&lt;span class="gd"&gt;-        series_expansion::coeffs_C1&amp;lt;SeriesOrder, CT&amp;gt; const coeffs_C1(n);&lt;/span&gt;
&lt;span class="gi"&gt;+        se::coeffs_C1&amp;lt;SeriesOrder, CT&amp;gt; const coeffs_C1(n);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        bool meridian = lat1 == -90 || sin_lam12 == 0;

&lt;span class="gu"&gt;@@ -459,7 +460,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT expansion_A1, expansion_A2;

&lt;span class="w"&gt; &lt;/span&gt;        // Evaluate the coefficients for C2.
&lt;span class="gd"&gt;-        series_expansion::coeffs_C2&amp;lt;SeriesOrder, CT&amp;gt; coeffs_C2(epsilon);&lt;/span&gt;
&lt;span class="gi"&gt;+        se::coeffs_C2&amp;lt;SeriesOrder, CT&amp;gt; coeffs_C2(epsilon);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        if (BOOST_GEOMETRY_CONDITION(EnableDistance) ||
&lt;span class="w"&gt; &lt;/span&gt;            BOOST_GEOMETRY_CONDITION(EnableReducedLength) ||
&lt;span class="gu"&gt;@@ -467,14 +468,14 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        {
&lt;span class="w"&gt; &lt;/span&gt;            // Find the coefficients for A1 by computing the
&lt;span class="w"&gt; &lt;/span&gt;            // series expansion using Horner scehme.
&lt;span class="gd"&gt;-            expansion_A1 = series_expansion::evaluate_A1&amp;lt;SeriesOrder&amp;gt;(epsilon);&lt;/span&gt;
&lt;span class="gi"&gt;+            expansion_A1 = se::evaluate_A1&amp;lt;SeriesOrder&amp;gt;(epsilon);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            if (BOOST_GEOMETRY_CONDITION(EnableReducedLength) ||
&lt;span class="w"&gt; &lt;/span&gt;                BOOST_GEOMETRY_CONDITION(EnableGeodesicScale))
&lt;span class="w"&gt; &lt;/span&gt;            {
&lt;span class="w"&gt; &lt;/span&gt;                // Find the coefficients for A2 by computing the
&lt;span class="w"&gt; &lt;/span&gt;                // series expansion using Horner scehme.
&lt;span class="gd"&gt;-                expansion_A2 = series_expansion::evaluate_A2&amp;lt;SeriesOrder&amp;gt;(epsilon);&lt;/span&gt;
&lt;span class="gi"&gt;+                expansion_A2 = se::evaluate_A2&amp;lt;SeriesOrder&amp;gt;(epsilon);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;                A12x = expansion_A1 - expansion_A2;
&lt;span class="w"&gt; &lt;/span&gt;                expansion_A2 += c1;
&lt;span class="gu"&gt;@@ -484,16 +485,16 @@ public:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        if (BOOST_GEOMETRY_CONDITION(EnableDistance))
&lt;span class="w"&gt; &lt;/span&gt;        {
&lt;span class="gd"&gt;-            CT B1 = series_expansion::sin_cos_series(sin_sigma2, cos_sigma2, coeffs_C1)&lt;/span&gt;
&lt;span class="gd"&gt;-                  - series_expansion::sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C1);&lt;/span&gt;
&lt;span class="gi"&gt;+            CT B1 = se::sin_cos_series(sin_sigma2, cos_sigma2, coeffs_C1)&lt;/span&gt;
&lt;span class="gi"&gt;+                  - se::sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C1);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            s12x = expansion_A1 * (sigma12 + B1);

&lt;span class="w"&gt; &lt;/span&gt;            if (BOOST_GEOMETRY_CONDITION(EnableReducedLength) ||
&lt;span class="w"&gt; &lt;/span&gt;                BOOST_GEOMETRY_CONDITION(EnableGeodesicScale))
&lt;span class="w"&gt; &lt;/span&gt;            {
&lt;span class="gd"&gt;-                CT B2 = series_expansion::sin_cos_series(sin_sigma2, cos_sigma2, coeffs_C2)&lt;/span&gt;
&lt;span class="gd"&gt;-                      - series_expansion::sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C2);&lt;/span&gt;
&lt;span class="gi"&gt;+                CT B2 = se::sin_cos_series(sin_sigma2, cos_sigma2, coeffs_C2)&lt;/span&gt;
&lt;span class="gi"&gt;+                      - se::sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C2);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;                J12 = A12x * sigma12 + (expansion_A1 * B1 - expansion_A2 * B2);
&lt;span class="w"&gt; &lt;/span&gt;            }
&lt;span class="gu"&gt;@@ -508,8 +509,8 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            }

&lt;span class="w"&gt; &lt;/span&gt;            J12 = A12x * sigma12 +
&lt;span class="gd"&gt;-                   (series_expansion::sin_cos_series(sin_sigma2, cos_sigma2, coeffs_C2)&lt;/span&gt;
&lt;span class="gd"&gt;-                  - series_expansion::sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C2));&lt;/span&gt;
&lt;span class="gi"&gt;+                   (se::sin_cos_series(sin_sigma2, cos_sigma2, coeffs_C2)&lt;/span&gt;
&lt;span class="gi"&gt;+                  - se::sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C2));&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        }

&lt;span class="w"&gt; &lt;/span&gt;        if (BOOST_GEOMETRY_CONDITION(EnableReducedLength))
&lt;span class="gu"&gt;@@ -627,7 +628,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                CT k2 = math::sqr(sin_beta1) * ep2;
&lt;span class="w"&gt; &lt;/span&gt;                CT eps = k2 / (c2 * (c1 + sqrt(c1 + k2)) + k2);

&lt;span class="gd"&gt;-                series_expansion::coeffs_A3&amp;lt;SeriesOrder, CT&amp;gt; const coeffs_A3(n);&lt;/span&gt;
&lt;span class="gi"&gt;+                se::coeffs_A3&amp;lt;SeriesOrder, CT&amp;gt; const coeffs_A3(n);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;                CT const A3 = math::horner_evaluate(eps, coeffs_A3.begin(), coeffs_A3.end());

&lt;span class="gu"&gt;@@ -852,12 +853,12 @@ public:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        eps = k2 / (c2 * (c1 + std::sqrt(c1 + k2)) + k2);

&lt;span class="gd"&gt;-        series_expansion::coeffs_C3&amp;lt;SeriesOrder, CT&amp;gt; const coeffs_C3(n, eps);&lt;/span&gt;
&lt;span class="gi"&gt;+        se::coeffs_C3&amp;lt;SeriesOrder, CT&amp;gt; const coeffs_C3(n, eps);&lt;/span&gt;

&lt;span class="gd"&gt;-        B312 = series_expansion::sin_cos_series(sin_sigma2, cos_sigma2, coeffs_C3)&lt;/span&gt;
&lt;span class="gd"&gt;-             - series_expansion::sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C3);&lt;/span&gt;
&lt;span class="gi"&gt;+        B312 = se::sin_cos_series(sin_sigma2, cos_sigma2, coeffs_C3)&lt;/span&gt;
&lt;span class="gi"&gt;+             - se::sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C3);&lt;/span&gt;

&lt;span class="gd"&gt;-        series_expansion::coeffs_A3&amp;lt;SeriesOrder, CT&amp;gt; const coeffs_A3(n);&lt;/span&gt;
&lt;span class="gi"&gt;+        se::coeffs_A3&amp;lt;SeriesOrder, CT&amp;gt; const coeffs_A3(n);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        CT const A3 = math::horner_evaluate(eps, coeffs_A3.begin(), coeffs_A3.end());


commit 6432dfe96c0141dca0c619dba130c94cd6dc9304
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Tue Jul 10 11:36:33 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [formulas][util] Reformat code in karney_inverse to use coefficient containers

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/formulas/karney_inverse.hpp b/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 691c111..6b6a3b5 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -161,14 +161,14 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        math::sin_cos_degrees(lat1, sin_beta1, cos_beta1);
&lt;span class="w"&gt; &lt;/span&gt;        sin_beta1 *= one_minus_f;

&lt;span class="gd"&gt;-        math::normalize_values&amp;lt;CT&amp;gt;(sin_beta1, cos_beta1);&lt;/span&gt;
&lt;span class="gi"&gt;+        math::normalize_unit_vector&amp;lt;CT&amp;gt;(sin_beta1, cos_beta1);&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        cos_beta1 = std::max(tiny, cos_beta1);

&lt;span class="w"&gt; &lt;/span&gt;        CT sin_beta2, cos_beta2;
&lt;span class="w"&gt; &lt;/span&gt;        math::sin_cos_degrees(lat2, sin_beta2, cos_beta2);
&lt;span class="w"&gt; &lt;/span&gt;        sin_beta2 *= one_minus_f;

&lt;span class="gd"&gt;-        math::normalize_values&amp;lt;CT&amp;gt;(sin_beta2, cos_beta2);&lt;/span&gt;
&lt;span class="gi"&gt;+        math::normalize_unit_vector&amp;lt;CT&amp;gt;(sin_beta2, cos_beta2);&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        cos_beta2 = std::max(tiny, cos_beta2);

&lt;span class="w"&gt; &lt;/span&gt;        // If cos_beta1 &amp;lt; -sin_beta1, then cos_beta2 - cos_beta1 is a
&lt;span class="gu"&gt;@@ -181,8 +181,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        {
&lt;span class="w"&gt; &lt;/span&gt;            if (cos_beta1 == cos_beta2)
&lt;span class="w"&gt; &lt;/span&gt;            {
&lt;span class="gd"&gt;-                sin_beta2 = sin_beta2 &amp;lt; 0 ? sin_beta1 :&lt;/span&gt;
&lt;span class="gd"&gt;-                                            -sin_beta1;&lt;/span&gt;
&lt;span class="gi"&gt;+                sin_beta2 = sin_beta2 &amp;lt; 0 ? sin_beta1 : -sin_beta1;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            }
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="w"&gt; &lt;/span&gt;        else
&lt;span class="gu"&gt;@@ -200,7 +199,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT m12x, s12x, M21;

&lt;span class="w"&gt; &lt;/span&gt;        // Index zero element of coeffs_C1 is unused.
&lt;span class="gd"&gt;-        CT coeffs_C1[SeriesOrder + 1];&lt;/span&gt;
&lt;span class="gi"&gt;+        series_expansion::coeffs_C1&amp;lt;SeriesOrder, CT&amp;gt; const coeffs_C1(n);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        bool meridian = lat1 == -90 || sin_lam12 == 0;

&lt;span class="gu"&gt;@@ -292,7 +291,7 @@ public:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            if (sigma12 &amp;gt;= c0)
&lt;span class="w"&gt; &lt;/span&gt;            {
&lt;span class="gd"&gt;-                // Short lines case (newton_start sets sin_alpha2, cos_alpha2, dnm)&lt;/span&gt;
&lt;span class="gi"&gt;+                // Short lines case (newton_start sets sin_alpha2, cos_alpha2, dnm).&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                s12x = sigma12 * b * dnm;
&lt;span class="w"&gt; &lt;/span&gt;                m12x = math::sqr(dnm) * b * sin(sigma12 / dnm);
&lt;span class="w"&gt; &lt;/span&gt;                if (BOOST_GEOMETRY_CONDITION(EnableGeodesicScale))
&lt;span class="gu"&gt;@@ -366,7 +365,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                        {
&lt;span class="w"&gt; &lt;/span&gt;                            cos_alpha1 = cos_alpha1 * cos_diff_alpha1 - sin_alpha1 * sin_diff_alpha1;
&lt;span class="w"&gt; &lt;/span&gt;                            sin_alpha1 = nsin_alpha1;
&lt;span class="gd"&gt;-                            math::normalize_values&amp;lt;CT&amp;gt;(sin_alpha1, cos_alpha1);&lt;/span&gt;
&lt;span class="gi"&gt;+                            math::normalize_unit_vector&amp;lt;CT&amp;gt;(sin_alpha1, cos_alpha1);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;                            // In some regimes we don&amp;#39;t get quadratic convergence because
&lt;span class="w"&gt; &lt;/span&gt;                            // slope -&amp;gt; 0. So use convergence conditions based on epsilon
&lt;span class="gu"&gt;@@ -386,7 +385,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                    // WGS84 and random input: mean = 4.74, sd = 0.99
&lt;span class="w"&gt; &lt;/span&gt;                    sin_alpha1 = (sin_alpha1a + sin_alpha1b) / c2;
&lt;span class="w"&gt; &lt;/span&gt;                    cos_alpha1 = (cos_alpha1a + cos_alpha1b) / c2;
&lt;span class="gd"&gt;-                    math::normalize_values&amp;lt;CT&amp;gt;(sin_alpha1, cos_alpha1);&lt;/span&gt;
&lt;span class="gi"&gt;+                    math::normalize_unit_vector&amp;lt;CT&amp;gt;(sin_alpha1, cos_alpha1);&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                    tripn = false;
&lt;span class="w"&gt; &lt;/span&gt;                    tripb = (std::abs(sin_alpha1a - sin_alpha1) + (cos_alpha1a - cos_alpha1) &amp;lt; tol_bisection ||
&lt;span class="w"&gt; &lt;/span&gt;                             std::abs(sin_alpha1 - sin_alpha1b) + (cos_alpha1 - cos_alpha1b) &amp;lt; tol_bisection);
&lt;span class="gu"&gt;@@ -447,18 +446,20 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        return result;
&lt;span class="w"&gt; &lt;/span&gt;    }

&lt;span class="gi"&gt;+    template &amp;lt;typename CoeffsC1&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    static inline void meridian_length(CT epsilon, CT ep2, CT sigma12,
&lt;span class="w"&gt; &lt;/span&gt;                                       CT sin_sigma1, CT cos_sigma1, CT dn1,
&lt;span class="w"&gt; &lt;/span&gt;                                       CT sin_sigma2, CT cos_sigma2, CT dn2,
&lt;span class="w"&gt; &lt;/span&gt;                                       CT cos_beta1, CT cos_beta2,
&lt;span class="w"&gt; &lt;/span&gt;                                       CT&amp;amp; s12x, CT&amp;amp; m12x, CT&amp;amp; m0,
&lt;span class="gd"&gt;-                                       CT&amp;amp; M12, CT&amp;amp; M21, CT coeffs_C1[])&lt;/span&gt;
&lt;span class="gi"&gt;+                                       CT&amp;amp; M12, CT&amp;amp; M21,&lt;/span&gt;
&lt;span class="gi"&gt;+                                       CoeffsC1 coeffs_C1)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    {
&lt;span class="w"&gt; &lt;/span&gt;        CT A12x = 0, J12 = 0;
&lt;span class="w"&gt; &lt;/span&gt;        CT expansion_A1, expansion_A2;

&lt;span class="gd"&gt;-        // Index zero element of coeffs_C2 is unused.&lt;/span&gt;
&lt;span class="gd"&gt;-        CT coeffs_C2[SeriesOrder + 1];&lt;/span&gt;
&lt;span class="gi"&gt;+        // Evaluate the coefficients for C2.&lt;/span&gt;
&lt;span class="gi"&gt;+        series_expansion::coeffs_C2&amp;lt;SeriesOrder, CT&amp;gt; coeffs_C2(epsilon);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        if (BOOST_GEOMETRY_CONDITION(EnableDistance) ||
&lt;span class="w"&gt; &lt;/span&gt;            BOOST_GEOMETRY_CONDITION(EnableReducedLength) ||
&lt;span class="gu"&gt;@@ -466,20 +467,14 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        {
&lt;span class="w"&gt; &lt;/span&gt;            // Find the coefficients for A1 by computing the
&lt;span class="w"&gt; &lt;/span&gt;            // series expansion using Horner scehme.
&lt;span class="gd"&gt;-            expansion_A1 = series_expansion::evaluate_series_A1&amp;lt;CT, SeriesOrder&amp;gt;(epsilon);&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-            // Evaluate the coefficients for C1.&lt;/span&gt;
&lt;span class="gd"&gt;-            series_expansion::evaluate_coeffs_C1&amp;lt;CT, SeriesOrder&amp;gt;(epsilon, coeffs_C1);&lt;/span&gt;
&lt;span class="gi"&gt;+            expansion_A1 = series_expansion::evaluate_A1&amp;lt;SeriesOrder&amp;gt;(epsilon);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            if (BOOST_GEOMETRY_CONDITION(EnableReducedLength) ||
&lt;span class="w"&gt; &lt;/span&gt;                BOOST_GEOMETRY_CONDITION(EnableGeodesicScale))
&lt;span class="w"&gt; &lt;/span&gt;            {
&lt;span class="w"&gt; &lt;/span&gt;                // Find the coefficients for A2 by computing the
&lt;span class="w"&gt; &lt;/span&gt;                // series expansion using Horner scehme.
&lt;span class="gd"&gt;-                expansion_A2 = series_expansion::evaluate_series_A2&amp;lt;CT, SeriesOrder&amp;gt;(epsilon);&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-                // Evaluate the coefficients for C2.&lt;/span&gt;
&lt;span class="gd"&gt;-                series_expansion::evaluate_coeffs_C2&amp;lt;CT, SeriesOrder&amp;gt;(epsilon, coeffs_C2);&lt;/span&gt;
&lt;span class="gi"&gt;+                expansion_A2 = series_expansion::evaluate_A2&amp;lt;SeriesOrder&amp;gt;(epsilon);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;                A12x = expansion_A1 - expansion_A2;
&lt;span class="w"&gt; &lt;/span&gt;                expansion_A2 += c1;
&lt;span class="gu"&gt;@@ -489,23 +484,18 @@ public:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        if (BOOST_GEOMETRY_CONDITION(EnableDistance))
&lt;span class="w"&gt; &lt;/span&gt;        {
&lt;span class="gd"&gt;-            CT B1 = series_expansion::sin_cos_series&amp;lt;CT, SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gd"&gt;-                                      (sin_sigma2, cos_sigma2, coeffs_C1)&lt;/span&gt;
&lt;span class="gd"&gt;-                  - series_expansion::sin_cos_series&amp;lt;CT, SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gd"&gt;-                                      (sin_sigma1, cos_sigma1, coeffs_C1);&lt;/span&gt;
&lt;span class="gi"&gt;+            CT B1 = series_expansion::sin_cos_series(sin_sigma2, cos_sigma2, coeffs_C1)&lt;/span&gt;
&lt;span class="gi"&gt;+                  - series_expansion::sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C1);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            s12x = expansion_A1 * (sigma12 + B1);

&lt;span class="w"&gt; &lt;/span&gt;            if (BOOST_GEOMETRY_CONDITION(EnableReducedLength) ||
&lt;span class="w"&gt; &lt;/span&gt;                BOOST_GEOMETRY_CONDITION(EnableGeodesicScale))
&lt;span class="w"&gt; &lt;/span&gt;            {
&lt;span class="gd"&gt;-                CT B2 = series_expansion::sin_cos_series&amp;lt;CT, SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gd"&gt;-                                          (sin_sigma2, cos_sigma2, coeffs_C2)&lt;/span&gt;
&lt;span class="gd"&gt;-                      - series_expansion::sin_cos_series&amp;lt;CT, SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gd"&gt;-                                          (sin_sigma1, cos_sigma1, coeffs_C2);&lt;/span&gt;
&lt;span class="gi"&gt;+                CT B2 = series_expansion::sin_cos_series(sin_sigma2, cos_sigma2, coeffs_C2)&lt;/span&gt;
&lt;span class="gi"&gt;+                      - series_expansion::sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C2);&lt;/span&gt;

&lt;span class="gd"&gt;-                J12 = A12x * sigma12 + (expansion_A1 * B1 -&lt;/span&gt;
&lt;span class="gd"&gt;-                                        expansion_A2 * B2);&lt;/span&gt;
&lt;span class="gi"&gt;+                J12 = A12x * sigma12 + (expansion_A1 * B1 - expansion_A2 * B2);&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            }
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="w"&gt; &lt;/span&gt;        else if (BOOST_GEOMETRY_CONDITION(EnableReducedLength) ||
&lt;span class="gu"&gt;@@ -518,14 +508,8 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            }

&lt;span class="w"&gt; &lt;/span&gt;            J12 = A12x * sigma12 +
&lt;span class="gd"&gt;-                   (series_expansion::sin_cos_series&amp;lt;CT, SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gd"&gt;-                                      (sin_sigma2,&lt;/span&gt;
&lt;span class="gd"&gt;-                                       cos_sigma2,&lt;/span&gt;
&lt;span class="gd"&gt;-                                       coeffs_C2)&lt;/span&gt;
&lt;span class="gd"&gt;-                  - series_expansion::sin_cos_series&amp;lt;CT, SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gd"&gt;-                                      (sin_sigma1,&lt;/span&gt;
&lt;span class="gd"&gt;-                                       cos_sigma1,&lt;/span&gt;
&lt;span class="gd"&gt;-                                       coeffs_C2));&lt;/span&gt;
&lt;span class="gi"&gt;+                   (series_expansion::sin_cos_series(sin_sigma2, cos_sigma2, coeffs_C2)&lt;/span&gt;
&lt;span class="gi"&gt;+                  - series_expansion::sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C2));&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        }

&lt;span class="w"&gt; &lt;/span&gt;        if (BOOST_GEOMETRY_CONDITION(EnableReducedLength))
&lt;span class="gu"&gt;@@ -554,12 +538,13 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;     doesn&amp;#39;t need to be used, return also sin_alpha2 and
&lt;span class="w"&gt; &lt;/span&gt;     cos_alpha2 and function value is sig12.
&lt;span class="w"&gt; &lt;/span&gt;    */
&lt;span class="gi"&gt;+    template &amp;lt;typename CoeffsC1&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    static inline CT newton_start(CT sin_beta1, CT cos_beta1, CT dn1,
&lt;span class="w"&gt; &lt;/span&gt;                                  CT sin_beta2, CT cos_beta2, CT dn2,
&lt;span class="w"&gt; &lt;/span&gt;                                  CT lam12, CT sin_lam12, CT cos_lam12,
&lt;span class="w"&gt; &lt;/span&gt;                                  CT&amp;amp; sin_alpha1, CT&amp;amp; cos_alpha1,
&lt;span class="w"&gt; &lt;/span&gt;                                  CT&amp;amp; sin_alpha2, CT&amp;amp; cos_alpha2,
&lt;span class="gd"&gt;-                                  CT&amp;amp; dnm, CT coeffs_C1[], CT ep2,&lt;/span&gt;
&lt;span class="gi"&gt;+                                  CT&amp;amp; dnm, CoeffsC1 coeffs_C1, CT ep2,&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                                  CT tol1, CT tol2, CT etol2, CT n, CT f)
&lt;span class="w"&gt; &lt;/span&gt;    {
&lt;span class="w"&gt; &lt;/span&gt;        CT const one_minus_f = c1 - f;
&lt;span class="gu"&gt;@@ -615,7 +600,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                (cos_omega12 &amp;gt;= c0 ? math::sqr(sin_omega12) /
&lt;span class="w"&gt; &lt;/span&gt;                (c1 + cos_omega12) : c1 - cos_omega12);

&lt;span class="gd"&gt;-            math::normalize_values&amp;lt;CT&amp;gt;(sin_alpha2, cos_alpha2);&lt;/span&gt;
&lt;span class="gi"&gt;+            math::normalize_unit_vector&amp;lt;CT&amp;gt;(sin_alpha2, cos_alpha2);&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            // Set return value.
&lt;span class="w"&gt; &lt;/span&gt;            sig12 = atan2(sin_sigma12, cos_sigma12);
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="gu"&gt;@@ -642,10 +627,9 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                CT k2 = math::sqr(sin_beta1) * ep2;
&lt;span class="w"&gt; &lt;/span&gt;                CT eps = k2 / (c2 * (c1 + sqrt(c1 + k2)) + k2);

&lt;span class="gd"&gt;-                CT coeffs_A3[SeriesOrder];&lt;/span&gt;
&lt;span class="gd"&gt;-                series_expansion::evaluate_coeffs_A3&amp;lt;double, SeriesOrder&amp;gt;(n, coeffs_A3);&lt;/span&gt;
&lt;span class="gi"&gt;+                series_expansion::coeffs_A3&amp;lt;SeriesOrder, CT&amp;gt; const coeffs_A3(n);&lt;/span&gt;

&lt;span class="gd"&gt;-                CT const A3 = math::horner_evaluate(eps, coeffs_A3, coeffs_A3 + SeriesOrder);&lt;/span&gt;
&lt;span class="gi"&gt;+                CT const A3 = math::horner_evaluate(eps, coeffs_A3.begin(), coeffs_A3.end());&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;                lambda_scale = f * cos_beta1 * A3 * math::pi&amp;lt;CT&amp;gt;();
&lt;span class="w"&gt; &lt;/span&gt;                beta_scale = lambda_scale * cos_beta1;
&lt;span class="gu"&gt;@@ -708,7 +692,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        // Sanity check on starting guess. Backwards check allows NaN through.
&lt;span class="w"&gt; &lt;/span&gt;        if (!(sin_alpha1 &amp;lt;= c0))
&lt;span class="w"&gt; &lt;/span&gt;        {
&lt;span class="gd"&gt;-            math::normalize_values&amp;lt;CT&amp;gt;(sin_alpha1, cos_alpha1);&lt;/span&gt;
&lt;span class="gi"&gt;+            math::normalize_unit_vector&amp;lt;CT&amp;gt;(sin_alpha1, cos_alpha1);&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="w"&gt; &lt;/span&gt;        else
&lt;span class="w"&gt; &lt;/span&gt;        {
&lt;span class="gu"&gt;@@ -791,6 +775,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        return k;
&lt;span class="w"&gt; &lt;/span&gt;    }

&lt;span class="gi"&gt;+    template &amp;lt;typename CoeffsC1&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    static inline CT lambda12(CT sin_beta1, CT cos_beta1, CT dn1,
&lt;span class="w"&gt; &lt;/span&gt;                              CT sin_beta2, CT cos_beta2, CT dn2,
&lt;span class="w"&gt; &lt;/span&gt;                              CT sin_alpha1, CT cos_alpha1,
&lt;span class="gu"&gt;@@ -802,7 +787,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                              CT&amp;amp; eps, CT&amp;amp; diff_omega12,
&lt;span class="w"&gt; &lt;/span&gt;                              bool diffp, CT&amp;amp; diff_lam12,
&lt;span class="w"&gt; &lt;/span&gt;                              CT f, CT n, CT ep2, CT tiny,
&lt;span class="gd"&gt;-                              CT coeffs_C1[])&lt;/span&gt;
&lt;span class="gi"&gt;+                              CoeffsC1 coeffs_C1)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    {
&lt;span class="w"&gt; &lt;/span&gt;        CT const one_minus_f = c1 - f;

&lt;span class="gu"&gt;@@ -826,7 +811,7 @@ public:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        cos_sigma1 = cos_omega1 = cos_alpha1 * cos_beta1;

&lt;span class="gd"&gt;-        math::normalize_values&amp;lt;CT&amp;gt;(sin_sigma1, cos_sigma1);&lt;/span&gt;
&lt;span class="gi"&gt;+        math::normalize_unit_vector&amp;lt;CT&amp;gt;(sin_sigma1, cos_sigma1);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        // Enforce symmetries in the case abs(beta2) = -beta1.
&lt;span class="w"&gt; &lt;/span&gt;        // Otherwise, this can yield singularities in the Newton iteration.
&lt;span class="gu"&gt;@@ -848,7 +833,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        cos_sigma2 = cos_omega2 =
&lt;span class="w"&gt; &lt;/span&gt;            cos_alpha2 * cos_beta2;

&lt;span class="gd"&gt;-        math::normalize_values&amp;lt;CT&amp;gt;(sin_sigma2, cos_sigma2);&lt;/span&gt;
&lt;span class="gi"&gt;+        math::normalize_unit_vector&amp;lt;CT&amp;gt;(sin_sigma2, cos_sigma2);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        // sig12 = sig2 - sig1, limit to [0, pi].
&lt;span class="w"&gt; &lt;/span&gt;        sigma12 = atan2(std::max(CT(0), cos_sigma1 * sin_sigma2 - sin_sigma1 * cos_sigma2),
&lt;span class="gu"&gt;@@ -867,24 +852,14 @@ public:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        eps = k2 / (c2 * (c1 + std::sqrt(c1 + k2)) + k2);

&lt;span class="gd"&gt;-        // Compute the size of coefficient array.&lt;/span&gt;
&lt;span class="gd"&gt;-        size_t const coeffs_C3_size = (SeriesOrder * (SeriesOrder - 1)) / 2;&lt;/span&gt;
&lt;span class="gd"&gt;-        CT coeffs_C3x[coeffs_C3_size];&lt;/span&gt;
&lt;span class="gd"&gt;-        series_expansion::evaluate_coeffs_C3x&amp;lt;CT, SeriesOrder&amp;gt;(n, coeffs_C3x);&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        // Evaluate C3 coefficients.&lt;/span&gt;
&lt;span class="gd"&gt;-        CT coeffs_C3[SeriesOrder];&lt;/span&gt;
&lt;span class="gd"&gt;-        series_expansion::evaluate_coeffs_C3&amp;lt;CT, SeriesOrder&amp;gt;(eps, coeffs_C3, coeffs_C3x);&lt;/span&gt;
&lt;span class="gi"&gt;+        series_expansion::coeffs_C3&amp;lt;SeriesOrder, CT&amp;gt; const coeffs_C3(n, eps);&lt;/span&gt;

&lt;span class="gd"&gt;-        B312 = series_expansion::sin_cos_series&amp;lt;CT, SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gd"&gt;-                   (sin_sigma2, cos_sigma2, coeffs_C3) -&lt;/span&gt;
&lt;span class="gd"&gt;-               series_expansion::sin_cos_series&amp;lt;CT, SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gd"&gt;-                   (sin_sigma1, cos_sigma1, coeffs_C3);&lt;/span&gt;
&lt;span class="gi"&gt;+        B312 = series_expansion::sin_cos_series(sin_sigma2, cos_sigma2, coeffs_C3)&lt;/span&gt;
&lt;span class="gi"&gt;+             - series_expansion::sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C3);&lt;/span&gt;

&lt;span class="gd"&gt;-        CT coeffs_A3[SeriesOrder];&lt;/span&gt;
&lt;span class="gd"&gt;-        series_expansion::evaluate_coeffs_A3&amp;lt;double, SeriesOrder&amp;gt;(n, coeffs_A3);&lt;/span&gt;
&lt;span class="gi"&gt;+        series_expansion::coeffs_A3&amp;lt;SeriesOrder, CT&amp;gt; const coeffs_A3(n);&lt;/span&gt;

&lt;span class="gd"&gt;-        CT const A3 = math::horner_evaluate(eps, coeffs_A3, coeffs_A3 + SeriesOrder);&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const A3 = math::horner_evaluate(eps, coeffs_A3.begin(), coeffs_A3.end());&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        diff_omega12 = -f * A3 * sin_alpha0 * (sigma12 + B312);
&lt;span class="w"&gt; &lt;/span&gt;        lam12 = eta + diff_omega12;
&lt;span class="gh"&gt;diff --git a/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp b/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 175b96f..6304484 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -387,7 +387,7 @@ inline T difference_angle(T x, T y, T&amp;amp; e)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;{
&lt;span class="w"&gt; &lt;/span&gt;    T t, d = math::sum_error(std::remainder(-x, T(360)), std::remainder(y, T(360)), t);

&lt;span class="gd"&gt;-    normalize_angle&amp;lt;degree, T&amp;gt;(d);&lt;/span&gt;
&lt;span class="gi"&gt;+    normalize_azimuth&amp;lt;degree, T&amp;gt;(d);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    // Here y - x = d + t (mod 360), exactly, where d is in (-180,180] and
&lt;span class="w"&gt; &lt;/span&gt;    // abs(t) &amp;lt;= eps (eps = 2^-45 for doubles).  The only case where the

commit 73a2e2b8a4f47ea87fd63dc4de03b4e3dd797835
Merge: 1a52eac 687df8e
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Mon Jul 9 16:15:41 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Merge branch &amp;#39;feature/geodesic_direct&amp;#39; into feature/karney_inverse

commit 1a52eaca9c35791984d2d3ba4ed0f5df2c963a23
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Jul 6 18:27:49 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [formulas] Resolve inaccuracy in starting point for Newton&amp;#39;s method

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/formulas/karney_inverse.hpp b/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 3864fb1..691c111 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -102,7 +102,6 @@ public:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        CT tiny = std::sqrt(std::numeric_limits&amp;lt;CT&amp;gt;::min());

&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT const n = f / two_minus_f;
&lt;span class="w"&gt; &lt;/span&gt;        CT const e2 = f * two_minus_f;
&lt;span class="w"&gt; &lt;/span&gt;        CT const ep2 = e2 / math::sqr(one_minus_f);
&lt;span class="gu"&gt;@@ -293,7 +292,7 @@ public:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            if (sigma12 &amp;gt;= c0)
&lt;span class="w"&gt; &lt;/span&gt;            {
&lt;span class="gd"&gt;-                // Short lines case (newton_start sets salp2, calp2, dnm)&lt;/span&gt;
&lt;span class="gi"&gt;+                // Short lines case (newton_start sets sin_alpha2, cos_alpha2, dnm)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                s12x = sigma12 * b * dnm;
&lt;span class="w"&gt; &lt;/span&gt;                m12x = math::sqr(dnm) * b * sin(sigma12 / dnm);
&lt;span class="w"&gt; &lt;/span&gt;                if (BOOST_GEOMETRY_CONDITION(EnableGeodesicScale))
&lt;span class="gu"&gt;@@ -361,7 +360,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                        CT cos_diff_alpha1 = cos(diff_alpha1);

&lt;span class="w"&gt; &lt;/span&gt;                        CT nsin_alpha1 = sin_alpha1 * cos_diff_alpha1 +
&lt;span class="gd"&gt;-                                        cos_alpha1 * sin_diff_alpha1;&lt;/span&gt;
&lt;span class="gi"&gt;+                            cos_alpha1 * sin_diff_alpha1;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;                        if (nsin_alpha1 &amp;gt; c0 &amp;amp;&amp;amp; std::abs(diff_alpha1) &amp;lt; math::pi&amp;lt;CT&amp;gt;())
&lt;span class="w"&gt; &lt;/span&gt;                        {
&lt;span class="gu"&gt;@@ -565,15 +564,21 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    {
&lt;span class="w"&gt; &lt;/span&gt;        CT const one_minus_f = c1 - f;
&lt;span class="w"&gt; &lt;/span&gt;        CT const x_thresh = c1000 * tol2;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        // Return a starting point for Newton&amp;#39;s method in sin_alpha1&lt;/span&gt;
&lt;span class="gi"&gt;+        // and cos_alpha1 (function value is -1). If Newton&amp;#39;s method&lt;/span&gt;
&lt;span class="gi"&gt;+        // doesn&amp;#39;t need to be used, return also sin_alpha2 and&lt;/span&gt;
&lt;span class="gi"&gt;+        // cos_alpha2 and function value is sig12.&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT sig12 = -c1;

&lt;span class="gi"&gt;+        // bet12 = bet2 - bet1 in [0, pi); beta12a = bet2 + bet1 in (-pi, 0]&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT sin_beta12 = sin_beta2 * cos_beta1 - cos_beta2 * sin_beta1;
&lt;span class="w"&gt; &lt;/span&gt;        CT cos_beta12 = cos_beta2 * cos_beta1 + sin_beta2 * sin_beta1;

&lt;span class="w"&gt; &lt;/span&gt;        CT sin_beta12a = sin_beta2 * cos_beta1 + cos_beta2 * sin_beta1;

&lt;span class="w"&gt; &lt;/span&gt;        bool shortline = cos_beta12 &amp;gt;= c0 &amp;amp;&amp;amp; sin_beta12 &amp;lt; c0_5 &amp;amp;&amp;amp;
&lt;span class="gd"&gt;-                         cos_beta2 * lam12 &amp;lt; c0_5;&lt;/span&gt;
&lt;span class="gi"&gt;+            cos_beta2 * lam12 &amp;lt; c0_5;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        CT sin_omega12, cos_omega12;

&lt;span class="gu"&gt;@@ -609,6 +614,10 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            cos_alpha2 = sin_beta12 - cos_beta1 * sin_beta2 *
&lt;span class="w"&gt; &lt;/span&gt;                (cos_omega12 &amp;gt;= c0 ? math::sqr(sin_omega12) /
&lt;span class="w"&gt; &lt;/span&gt;                (c1 + cos_omega12) : c1 - cos_omega12);
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            math::normalize_values&amp;lt;CT&amp;gt;(sin_alpha2, cos_alpha2);&lt;/span&gt;
&lt;span class="gi"&gt;+            // Set return value.&lt;/span&gt;
&lt;span class="gi"&gt;+            sig12 = atan2(sin_sigma12, cos_sigma12);&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="w"&gt; &lt;/span&gt;        // Skip astroid calculation if too eccentric.
&lt;span class="w"&gt; &lt;/span&gt;        else if (std::abs(n) &amp;gt; c0_1 ||
&lt;span class="gu"&gt;@@ -616,11 +625,11 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                 sin_sigma12 &amp;gt;= c6 * std::abs(n) * math::pi&amp;lt;CT&amp;gt;() *
&lt;span class="w"&gt; &lt;/span&gt;                 math::sqr(cos_beta1))
&lt;span class="w"&gt; &lt;/span&gt;        {
&lt;span class="gd"&gt;-            // Nothing to do.&lt;/span&gt;
&lt;span class="gi"&gt;+            // Nothing to do, zeroth order spherical approximation will do.&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="w"&gt; &lt;/span&gt;        else
&lt;span class="w"&gt; &lt;/span&gt;        {
&lt;span class="gd"&gt;-            // Scale lam12 and beta2 to x, y coordinate system where antipodal&lt;/span&gt;
&lt;span class="gi"&gt;+            // Scale lam12 and bet2 to x, y coordinate system where antipodal&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            // point is at origin and singular point is at y = 0, x = -1.
&lt;span class="w"&gt; &lt;/span&gt;            CT lambda_scale, beta_scale;

&lt;span class="gu"&gt;@@ -628,18 +637,17 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            volatile CT x;

&lt;span class="w"&gt; &lt;/span&gt;            CT lam12x = atan2(-sin_lam12, -cos_lam12);
&lt;span class="gd"&gt;-            if (f &amp;gt;= 0)&lt;/span&gt;
&lt;span class="gi"&gt;+            if (f &amp;gt;= c0)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            {
&lt;span class="w"&gt; &lt;/span&gt;                CT k2 = math::sqr(sin_beta1) * ep2;
&lt;span class="gd"&gt;-                CT epsilon = k2 / (c2 * (c1 * sqrt(c1 + k2)) + k2);&lt;/span&gt;
&lt;span class="gi"&gt;+                CT eps = k2 / (c2 * (c1 + sqrt(c1 + k2)) + k2);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;                CT coeffs_A3[SeriesOrder];
&lt;span class="w"&gt; &lt;/span&gt;                series_expansion::evaluate_coeffs_A3&amp;lt;double, SeriesOrder&amp;gt;(n, coeffs_A3);

&lt;span class="gd"&gt;-                CT const A3 = math::horner_evaluate(epsilon, coeffs_A3, coeffs_A3 + SeriesOrder);&lt;/span&gt;
&lt;span class="gi"&gt;+                CT const A3 = math::horner_evaluate(eps, coeffs_A3, coeffs_A3 + SeriesOrder);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;                lambda_scale = f * cos_beta1 * A3 * math::pi&amp;lt;CT&amp;gt;();
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                beta_scale = lambda_scale * cos_beta1;

&lt;span class="w"&gt; &lt;/span&gt;                x = lam12x / lambda_scale;
&lt;span class="gu"&gt;@@ -671,12 +679,12 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                if (f &amp;gt;= c0)
&lt;span class="w"&gt; &lt;/span&gt;                {
&lt;span class="w"&gt; &lt;/span&gt;                    sin_alpha1 = std::min(CT(1), -CT(x));
&lt;span class="gd"&gt;-                    cos_alpha1 = - std::sqrt(CT(1) - math::sqr(sin_alpha1));&lt;/span&gt;
&lt;span class="gi"&gt;+                    cos_alpha1 = - math::sqrt(c1 - math::sqr(sin_alpha1));&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                }
&lt;span class="w"&gt; &lt;/span&gt;                else
&lt;span class="w"&gt; &lt;/span&gt;                {
&lt;span class="gd"&gt;-                    cos_alpha1 = std::max(x &amp;gt; -tol1 ? c0 : -c1, CT(x));&lt;/span&gt;
&lt;span class="gd"&gt;-                    sin_alpha1 = std::sqrt(c1 - math::sqr(cos_alpha1));&lt;/span&gt;
&lt;span class="gi"&gt;+                    cos_alpha1 = std::max(CT(x &amp;gt; -tol1 ? c0 : -c1), CT(x));&lt;/span&gt;
&lt;span class="gi"&gt;+                    sin_alpha1 = math::sqrt(c1 - math::sqr(cos_alpha1));&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                }
&lt;span class="w"&gt; &lt;/span&gt;            }
&lt;span class="w"&gt; &lt;/span&gt;            else
&lt;span class="gu"&gt;@@ -687,8 +695,8 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                CT omega12a = lambda_scale * (f &amp;gt;= c0 ? -x * k /
&lt;span class="w"&gt; &lt;/span&gt;                    (c1 + k) : -y * (c1 + k) / k);

&lt;span class="gd"&gt;-                CT sin_omega12 = sin(omega12a);&lt;/span&gt;
&lt;span class="gd"&gt;-                CT cos_omega12 = -cos(omega12a);&lt;/span&gt;
&lt;span class="gi"&gt;+                sin_omega12 = sin(omega12a);&lt;/span&gt;
&lt;span class="gi"&gt;+                cos_omega12 = -cos(omega12a);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;                // Update spherical estimate of alpha1 using omgega12 instead of lam12.
&lt;span class="w"&gt; &lt;/span&gt;                sin_alpha1 = cos_beta2 * sin_omega12;
&lt;span class="gu"&gt;@@ -697,15 +705,15 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            }
&lt;span class="w"&gt; &lt;/span&gt;        }

&lt;span class="gd"&gt;-        // Apply sanity check on starting guess. Backwards check allows NaN through.&lt;/span&gt;
&lt;span class="gi"&gt;+        // Sanity check on starting guess. Backwards check allows NaN through.&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        if (!(sin_alpha1 &amp;lt;= c0))
&lt;span class="w"&gt; &lt;/span&gt;        {
&lt;span class="gd"&gt;-          math::normalize_values&amp;lt;CT&amp;gt;(sin_alpha1, cos_alpha1);&lt;/span&gt;
&lt;span class="gi"&gt;+            math::normalize_values&amp;lt;CT&amp;gt;(sin_alpha1, cos_alpha1);&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="w"&gt; &lt;/span&gt;        else
&lt;span class="w"&gt; &lt;/span&gt;        {
&lt;span class="gd"&gt;-          sin_alpha1 = c1;&lt;/span&gt;
&lt;span class="gd"&gt;-          cos_alpha1 = c0;&lt;/span&gt;
&lt;span class="gi"&gt;+            sin_alpha1 = c1;&lt;/span&gt;
&lt;span class="gi"&gt;+            cos_alpha1 = c0;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        }

&lt;span class="w"&gt; &lt;/span&gt;        return sig12;

commit 535ae96a8682fcc068016db212585e2c5994827a
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Tue Jul 3 11:11:41 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [test] Add nearly antipodal points dataset for inverse geodesic problem

&lt;span class="w"&gt; &lt;/span&gt;   These values are collected from GeodTest which is associated
&lt;span class="w"&gt; &lt;/span&gt;   with GeographicLib:
&lt;span class="w"&gt; &lt;/span&gt;   https://zenodo.org/record/32156

&lt;span class="w"&gt; &lt;/span&gt;   The conversion to C++ array format is done using this Python script:
&lt;span class="w"&gt; &lt;/span&gt;   https://github.com/adl1995/boost-geometry-extra/blob/master/geographiclib-dataset-parse-inverse.py

&lt;span class="w"&gt; &lt;/span&gt;   Geodesic scale (M12) is absent from the GeodTest dataset, so it is
&lt;span class="w"&gt; &lt;/span&gt;   manually generated using GeographicLib using this C++ script:
&lt;span class="w"&gt; &lt;/span&gt;   https://github.com/adl1995/boost-geometry-extra/blob/master/geographicLib-direct-antipodal.cpp

&lt;span class="gh"&gt;diff --git a/test/formulas/inverse.cpp b/test/formulas/inverse.cpp&lt;/span&gt;
&lt;span class="gh"&gt;index 8e7ad03..07c0fbc 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/test/formulas/inverse.cpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/test/formulas/inverse.cpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -4,6 +4,7 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;// Copyright (c) 2016-2017 Oracle and/or its affiliates.

&lt;span class="w"&gt; &lt;/span&gt;// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
&lt;span class="gi"&gt;+// Contributed and/or modified by Adeel Ahmad, as part of Google Summer of Code 2018 program.&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;// Use, modification and distribution is subject to the Boost Software License,
&lt;span class="w"&gt; &lt;/span&gt;// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
&lt;span class="gu"&gt;@@ -14,6 +15,7 @@&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;#include &amp;quot;test_formula.hpp&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;#include &amp;quot;inverse_cases.hpp&amp;quot;
&lt;span class="gi"&gt;+#include &amp;quot;inverse_cases_antipodal.hpp&amp;quot;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;#include &amp;lt;boost/geometry/formulas/vincenty_inverse.hpp&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;#include &amp;lt;boost/geometry/formulas/thomas_inverse.hpp&amp;gt;
&lt;span class="gu"&gt;@@ -22,8 +24,9 @@&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;#include &amp;lt;boost/geometry/srs/spheroid.hpp&amp;gt;

&lt;span class="gi"&gt;+template &amp;lt;typename Result&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;void check_inverse(std::string const&amp;amp; name,
&lt;span class="gd"&gt;-                   expected_results const&amp;amp; results,&lt;/span&gt;
&lt;span class="gi"&gt;+                   Result const&amp;amp; results,&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                   bg::formula::result_inverse&amp;lt;double&amp;gt; const&amp;amp; result,
&lt;span class="w"&gt; &lt;/span&gt;                   expected_result const&amp;amp; expected,
&lt;span class="w"&gt; &lt;/span&gt;                   expected_result const&amp;amp; reference,
&lt;span class="gu"&gt;@@ -87,6 +90,23 @@ void test_all(expected_results const&amp;amp; results)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    check_inverse(&amp;quot;karney&amp;quot;, results, result_k, results.vincenty, results.reference, 0.0000001);
&lt;span class="w"&gt; &lt;/span&gt;}

&lt;span class="gi"&gt;+void test_karney_antipodal(expected_results_antipodal const&amp;amp; results)&lt;/span&gt;
&lt;span class="gi"&gt;+{&lt;/span&gt;
&lt;span class="gi"&gt;+    double lon1d = results.p1.lon;&lt;/span&gt;
&lt;span class="gi"&gt;+    double lat1d = results.p1.lat;&lt;/span&gt;
&lt;span class="gi"&gt;+    double lon2d = results.p2.lon;&lt;/span&gt;
&lt;span class="gi"&gt;+    double lat2d = results.p2.lat;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    // WGS84&lt;/span&gt;
&lt;span class="gi"&gt;+    bg::srs::spheroid&amp;lt;double&amp;gt; spheroid(6378137.0, 6356752.3142451793);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    bg::formula::result_inverse&amp;lt;double&amp;gt; result;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    typedef bg::formula::karney_inverse&amp;lt;double, true, true, true, true, true, 8&amp;gt; ka_t;&lt;/span&gt;
&lt;span class="gi"&gt;+    result = ka_t::apply(lon1d, lat1d, lon2d, lat2d, spheroid);&lt;/span&gt;
&lt;span class="gi"&gt;+    check_inverse(&amp;quot;karney&amp;quot;, results, result, results.karney, results.karney, 0.0000001);&lt;/span&gt;
&lt;span class="gi"&gt;+}&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;int test_main(int, char*[])
&lt;span class="w"&gt; &lt;/span&gt;{
&lt;span class="w"&gt; &lt;/span&gt;    for (size_t i = 0; i &amp;lt; expected_size; ++i)
&lt;span class="gu"&gt;@@ -94,5 +114,10 @@ int test_main(int, char*[])&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        test_all(expected[i]);
&lt;span class="w"&gt; &lt;/span&gt;    }

&lt;span class="gi"&gt;+    for (size_t i = 0; i &amp;lt; expected_size_antipodal; ++i)&lt;/span&gt;
&lt;span class="gi"&gt;+    {&lt;/span&gt;
&lt;span class="gi"&gt;+        test_karney_antipodal(expected_antipodal[i]);&lt;/span&gt;
&lt;span class="gi"&gt;+    }&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    return 0;
&lt;span class="w"&gt; &lt;/span&gt;}
&lt;span class="gh"&gt;diff --git a/test/formulas/inverse_cases_antipodal.hpp b/test/formulas/inverse_cases_antipodal.hpp&lt;/span&gt;
new file mode 100644
&lt;span class="gh"&gt;index 0000000..e66046d&lt;/span&gt;
&lt;span class="gd"&gt;--- /dev/null&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/test/formulas/inverse_cases_antipodal.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -0,0 +1,342 @@&lt;/span&gt;
&lt;span class="gi"&gt;+// Boost.Geometry&lt;/span&gt;
&lt;span class="gi"&gt;+// Unit Test&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+// Copyright (c) 2018 Adeel Ahmad, Islamabad, Pakistan.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+// Contributed and/or modified by Adeel Ahmad, as part of Google Summer of Code 2018 program.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+// Use, modification and distribution is subject to the Boost Software License,&lt;/span&gt;
&lt;span class="gi"&gt;+// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at&lt;/span&gt;
&lt;span class="gi"&gt;+// http://www.boost.org/LICENSE_1_0.txt)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+#ifndef BOOST_GEOMETRY_TEST_INVERSE_CASES_ANTIPODAL_HPP&lt;/span&gt;
&lt;span class="gi"&gt;+#define BOOST_GEOMETRY_TEST_INVERSE_CASES_ANTIPODAL_HPP&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+#include &amp;quot;inverse_cases.hpp&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+struct expected_results_antipodal&lt;/span&gt;
&lt;span class="gi"&gt;+{&lt;/span&gt;
&lt;span class="gi"&gt;+    coordinates p1;&lt;/span&gt;
&lt;span class="gi"&gt;+    coordinates p2;&lt;/span&gt;
&lt;span class="gi"&gt;+    expected_result karney;&lt;/span&gt;
&lt;span class="gi"&gt;+};&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+/*&lt;/span&gt;
&lt;span class="gi"&gt;+ These values are collected from GeodTest which is associated with GeographicLib:&lt;/span&gt;
&lt;span class="gi"&gt;+     https://zenodo.org/record/32156&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+ The conversion to C++ array format is done using this Python script:&lt;/span&gt;
&lt;span class="gi"&gt;+     https://github.com/adl1995/boost-geometry-extra/blob/master/geographiclib-dataset-parse-inverse.py&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+ Geodesic scale (M12) is absent from the GeodTest dataset, so it is manually generated&lt;/span&gt;
&lt;span class="gi"&gt;+ using GeographicLib using this C++ script:&lt;/span&gt;
&lt;span class="gi"&gt;+     https://github.com/adl1995/boost-geometry-extra/blob/master/geographicLib-direct-antipodal.cpp&lt;/span&gt;
&lt;span class="gi"&gt;+*/&lt;/span&gt;
&lt;span class="gi"&gt;+expected_results_antipodal expected_antipodal[] =&lt;/span&gt;
&lt;span class="gi"&gt;+{&lt;/span&gt;
&lt;span class="gi"&gt;+    {&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 31.394417440639 }, { 179.615601631202912322, -31.275540610835465807 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19980218.4055399, 34.266322930672, 145.782701113414306756, 49490.8807994496209, -0.996116451012525883079717914370121434 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 29.788792273749 }, { 178.569451327813675741, -29.558013672069422725 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19887224.5407334, 74.302205994192, 106.156240654579267308, 97043.7545600593058, -0.998624031147844926081802441331092268 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 46.471843094141 }, { 179.083144618009561276, -46.284166405924629853 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19944337.8863917, 63.693680310665, 116.699978859005570535, 53139.140576552365, -0.997597309645591900917338534782174975 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 63.016506345929 }, { 179.862869954071637855, -63.02943882703369735 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 20000925.7533636, 153.393656073038, 26.619056019474552953, 12713.9284725111772, -1.00381317792143387457315384381217882 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 19.796231412719 }, { 179.546498474461283862, -19.470586923091672503 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19956338.1330537, 28.272934411318, 151.789094611690988249, 87191.1749625132931, -0.997015409027664833985227232915349305 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 6.373459459035 }, { 179.240009269347556917, -6.204887833274217382 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19946581.6983394, 56.859050230583, 123.169200847008284851, 53958.8698005263939, -0.999349049081101004077254401636309922 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 66.380766469414 }, { 179.632633596894388233, -66.27177494016956425 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19986277.7696849, 38.646950203356, 141.550919825824399405, 22198.215635049214, -0.996949176054954366854587988200364634 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 16.483421185231 }, { 179.731567273052604726, -16.818424446748042212 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19962737.9842573, 163.431254767325, 16.598399455529231288, 95318.4104529881431, -1.00272210232979741562076014815829694 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 4.215702155486 }, { 179.093771177769992874, -4.051917290690976764 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19932517.393764, 65.543168480886, 114.482669479963380006, 55205.4553703842317, -0.999655858425056553784315838129259646 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 40.71372085907 }, { 179.404612926861498984, -41.047052242159400671 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19951133.3595356, 143.672151631634, 36.54002600969304553, 70931.1530155553621, -1.00414169574077272173440178448799998 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 15.465481491654 }, { 179.020726605204181801, -14.622355549425900341 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19877383.8879911, 36.289185640976, 143.875673907461159912, 156419.0806764376957, -0.997639074397169589580869342171354219 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 17.586197343531 }, { 179.722490735835379144, -17.731394230364437075 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19982280.4639115, 157.929615091529, 22.089021105298661023, 69727.5357849255557, -1.00280451698301242835498214844847098 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 5.7442768247 }, { 178.85894724576868462, -6.039853564481335581 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19902873.7431814, 116.146983678305, 63.91482549951374061, 87149.6188944111673, -1.00039332893096744037109147029696032 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 32.002904282111 }, { 179.744925422107715439, -32.297934520693132807 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19967670.3104795, 163.052160078191, 17.004175883388454943, 78311.3164829640582, -1.00449903445302446414189034840092063 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 55.902716926362 }, { 179.300685189522463007, -55.934320218634018206 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19970525.337607, 98.927641063414, 81.374264168520557301, 23554.0093185709067, -1.00072788779083454713259015989024192 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 22.69939784398 }, { 179.294173474584020749, -22.654875407651067149 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19959286.1903172, 74.253870776761, 105.811588890213155275, 22369.7179951557679, -0.998972181419003457669703038845909759 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 41.312328471121 }, { 179.817186837717804928, -40.954523601529804886 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19962690.5721867, 11.277616109847, 168.784288786443902199, 77252.6121237260201, -0.994825151471527391322524636052548885 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 27.927415327453 }, { 179.636508875679110143, -27.607314264234172721 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19961296.8828333, 23.166421459647, 156.905194492817275222, 83096.5801709291101, -0.995959692767656723511038308060960844 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 41.567228741451 }, { 179.931812964300204608, -42.103039532074194347 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19944253.4454809, 176.66609526064, 3.361859685835349219, 96859.08180779197, -1.00513607140487626345759508694754913 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 37.384208978567 }, { 179.225180174670992261, -36.916085670712060029 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19928705.5911445, 39.072534864532, 141.212743814390850106, 92667.7834060578402, -0.995955516859159284415170532156480476 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 59.011868682852 }, { 179.424923485514312807, -58.82705468054708336 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19970442.3788306, 44.970301291063, 135.333817989802309531, 38071.1136293083857, -0.996658942892707400140750451100757346 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 35.515406087737 }, { 179.50369572149476218, -35.119747127350258822 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19948918.9139751, 28.528972431952, 151.622257906284404073, 84564.0387217601751, -0.995562861799169418475230486365035176 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 58.170252463184 }, { 179.254737571455023977, -58.372261836268550805 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19961407.0813807, 128.021116291844, 52.399129705193347143, 43715.3070711393309, -1.00285273713280753682397516968194395 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 34.012183807959 }, { 179.83713352180447672, -34.29640782899529639 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19970955.843065, 168.944519134772, 11.093048811826875835, 76493.5814538538151, -1.0047652354558671561335359001532197 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 45.510762948553 }, { 178.981682578823726535, -45.582753595227824235 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19940248.3450143, 99.886784003837, 80.542330522982505877, 48555.1946627894972, -1.00083807750906350619857221317943186 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 4.19841765451 }, { 179.398024428225540172, -4.198416896099783242 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19970496.5132933, 89.561550657928, 90.438456568689151881, 14.8790480103109, -0.999994104810285944218151144013972953 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 40.890119148103 }, { 179.6557148951668192, -41.553556264538302258 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19926563.5817492, 165.437641169967, 14.713597527941311478, 111805.7305227545923, -1.00492294933406567380984597548376769 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 28.096672787686 }, { 178.606868012231657724, -28.472055035513955205 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19883901.8482359, 115.174366374632, 65.257367020445564176, 107880.4353518862363, -1.00170803073331593502359737613005564 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 6.50572154271 }, { 178.926013840891647541, -6.411745140559297675 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19917276.4101551, 79.069492719523, 100.985091481519557845, 57073.3242952680707, -0.999736666933808471036115861352300271 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, .468835109567 }, { 178.325942223692180692, -.281751687044281805 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19849380.7342734, 80.234636214474, 99.77243368342786593, 123845.4568822078908, -0.999801437209140719808431185811059549 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 1.682746325049 }, { 179.717131561406935483, -.677647430701204515 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19890026.0274781, 10.076182752451, 169.927471515299313238, 177917.2104306563981, -0.999538055691262194990542866435134783 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 10.711305126218 }, { 179.874050163405229937, -10.349315378531556046 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19962987.2134077, 7.528253696796, 172.480576051850009046, 104175.1095378254456, -0.998071853755238880268052525934763253 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 53.374321544652 }, { 179.729445806011012057, -53.196257519024042184 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19980478.1457438, 23.324715976877, 156.777734080146664812, 41907.8869272231053, -0.995333596277707566279957518418086693 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 39.680221664519 }, { 179.87506206720154785, -39.256187213040660911 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19956191.7841809, 7.075406493429, 172.967624741991546131, 86943.8110669895148, -0.994801087909667924868983845954062417 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 1.377666714083 }, { 178.994542525209058878, -1.415358715570225495 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19925401.4931301, 95.29199069739, 84.7178724483824156, 45800.9140624827059, -0.99999803170512457928253979844157584 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 48.751426624188 }, { 179.661697715070846977, -48.688146707479475147 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19988599.1160495, 40.252328570137, 139.808452951157199824, 26322.3790862461568, -0.995999245724129789181233718409202993 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 59.443039048494 }, { 179.247605418616998285, -59.454371825393424121 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19969935.9534732, 93.052184108221, 87.331416513795326158, 25342.4691896499534, -1.00020727848897084122370415570912883 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 4.122408476235 }, { 179.749430572914989772, -4.689124208743755363 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19938291.6332293, 167.73479753304, 12.274635577599782826, 127855.6475863583497, -1.00068600902837667732114823593292385 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 46.422470082432 }, { 178.857408435141563774, -46.390934261324541952 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19931980.7029341, 86.67365350297, 93.852683224054943377, 56114.680046867064, -0.999607096116300386512421027873642743 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 32.614423729024 }, { 179.460593512880455451, -32.01874745886238612 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19926887.3785175, 24.943814520557, 155.229917137448282531, 112355.3319340873104, -0.995562150676871926435751447570510209 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 3.242895277973 }, { 179.556428318080663113, -3.001106476068264917 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19964490.4789049, 30.247458779683, 149.760178923092147784, 80929.0418317066044, -0.999474184270344845337774586369050667 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 6.29069210113 }, { 178.556859259685624933, -6.354208910915346725 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19877160.8505733, 94.34299459284, 85.750059038253282986, 94127.1566760840083, -0.999976397350904933070125935046235099 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 18.232086569498 }, { 179.658073278238477245, -18.87394850776853555 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19927978.7462175, 164.41905055334, 15.640779355822506503, 129771.1882449660559, -1.00293460439063886191490837518358603 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 12.049849333181 }, { 179.761046682699610657, -11.201990279782499264 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19908004.4552909, 9.418096768309, 170.610608272305604585, 157761.5040571466343, -0.997761474497510958414636661473196 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 40.289465276136 }, { 179.644208494155329095, -40.370034926441385999 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19985674.936106, 143.092606818963, 36.958610382613096419, 36200.8933724688593, -1.00414965876091266672176516294712201 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 2.197784650379 }, { 179.961199531084784854, -1.353440827124394777 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19910509.7517973, 1.542117609437, 178.458582198505846426, 160403.6285079348996, -0.999488724639301051588802238256903365 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 1.966575272177 }, { 179.699817324905962184, -3.101125282483752618 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19875595.6267266, 170.112968791865, 9.89572776349855838, 192355.7206665719908, -1.00015463589804554089823795948177576 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 25.078832492684 }, { 178.600804840925824646, -24.897833702325682511 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19887997.7953866, 77.264585323781, 103.101167809583406892, 92442.9124509225839, -0.998981189838600847075156252685701475 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 31.740361941314 }, { 179.553485210731879874, -31.909206787477701871 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19972325.3556069, 143.930820896999, 36.145242998351638503, 54883.4113710054145, -1.00379461628115951299378139083273709 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, .05479250563 }, { 178.822647462220726609, .836079031223269324 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19858049.4780499, 41.349430623518, 138.645259065012502544, 169078.442370111714, -0.9997793696948588104689292777038645 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 36.685139871608 }, { 179.366667224014334712, -36.6833040833258687 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19968965.6773632, 89.167975517493, 90.921025521408327068, 13327.2156799476918, -0.999916537946348604748436628142371774 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 3.451199399671 }, { 179.107509334399258305, -3.459003521120242021 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19938203.3838544, 91.541212417048, 88.476282464773035164, 32316.1747698810781, -1.00000397484395819880376166111091152 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 27.692898794247 }, { 178.512356615673144314, -27.666009301228316555 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19883493.6699045, 88.406440883665, 92.036345087713397961, 94128.7880896190836, -0.999736458322951659916100197733612731 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 17.363238291869 }, { 179.567921315455829491, -17.288872648596950413 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19980749.7638027, 39.697196316589, 140.321938237586060826, 46975.9359427664379, -0.997687691981715030209443284547887743 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 37.006775102539 }, { 179.191103068859169842, -37.156365616364686838 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19949309.9180043, 116.455543532607, 63.771817992036617793, 45856.1961421018701, -1.00221962858918423044940482213860378 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 45.572883540957 }, { 179.224707765088686272, -45.94675931323086696 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19940027.8586414, 137.627256708444, 42.723991162977357301, 74208.4359612889496, -1.00380887786447159371050474874209613 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 43.63393981955 }, { 178.878236417027994157, -43.642335115130514773 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19931045.2914508, 91.203625101465, 89.268780774643462256, 55253.5406349861764, -1.00002974153150514524668324156664312 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 38.4995307019 }, { 179.143856004445269342, -39.042223438550921467 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19918391.2222193, 141.232864609445, 39.117947060740562295, 102217.2563106863077, -1.00388164115732947401227193040540442 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 27.55015339382 }, { 179.596220103573824099, -27.587412128122249651 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19986004.7358853, 137.025135713548, 42.992898351962011956, 33938.7346646670654, -1.00316044390281167153489150223322213 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 1.54507498314 }, { 179.567115633151308577, -1.448861185025252004 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19978593.3191777, 36.816106412092, 143.185763012309022403, 56320.5800276739168, -0.999770499462467210349814195069484413 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 45.217063644222 }, { 179.807382581661125, -45.086424050571516283 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19987042.0782465, 18.114645812265, 161.928120141429818658, 45544.2915061261936, -0.994974179414854997816064496873877943 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 13.473522450751 }, { 179.726941062277208626, -13.570372758027936877 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19987364.078382, 156.839609002403, 23.170293747820406391, 65329.9068132034472, -1.00219093189506569530067281448282301 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 6.287741997374 }, { 179.071252372259552052, -6.743450924917895817 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19912159.8245954, 132.954797451112, 47.100789519677419746, 104772.4027498097375, -1.00071252411103017720961361192166805 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 7.639709001531 }, { 179.616156296978583335, -7.48702643786017917 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19976374.3699535, 29.731916588299, 150.279582966919438164, 69224.6591757209539, -0.998789792086741234911073661351110786 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 5.893688050348 }, { 179.586212000450856399, -4.888408917114795625 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19886907.2520668, 14.653438882877, 165.371181401863458848, 177183.5330818593022, -0.998794647031120752522781458537792787 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 61.997076235476 }, { 179.605779116829636081, -62.19593758437129915 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19976288.2901729, 149.562797049254, 30.65850204223272625, 36696.2853801462176, -1.00373071432437144245852778112748638 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 50.507637741656 }, { 179.893569206021038536, -50.721890799900161112 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19979542.5263293, 171.564028344478, 8.4746613464253591, 50644.5234828162697, -1.00508881632281776852266830246662721 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 7.484475238477 }, { 178.638400003000590878, -6.926155588124333461 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19867425.2906303, 57.020570370985, 123.087267812322270238, 132929.2775641349633, -0.999097042677338120775232255255104974 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 56.851165323215 }, { 179.587046628550073045, -56.875248360744638525 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19988235.9960515, 112.345749045605, 67.744017057185404441, 9971.0934553515518, -1.00182859249871403228837607457535341 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 10.692273150738 }, { 178.709520715733071393, -10.851727623036704339 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19893210.3050033, 102.824601316946, 77.308514969817191459, 83032.7122948051111, -1.00034345584508432835946223349310458 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 46.694739303788 }, { 179.926838145841924189, -46.948618153686522669 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19975447.9283188, 174.663684259477, 5.361568174833475454, 59614.5876209460645, -1.00520484875201732144489596976200119 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 15.804386137005 }, { 178.367587635209819128, -15.522042847777054984 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19855850.8800526, 74.932089158884, 105.357235560913450667, 123350.4326645237628, -0.999091578546475345135036150168161839 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 4.371450175299 }, { 179.780887420199549421, -4.566109732313098407 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19979071.1035552, 164.163592252794, 15.840695025950408814, 84137.2115482558728, -1.00076323969894742660358133434783667 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 30.894388279688 }, { 179.375426183521944524, -30.871308884744172663 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19968681.8321577, 77.35154610481, 102.709506078439532936, 14048.0277985734058, -0.998975176336422854284080585784977302 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 9.541166838639 }, { 178.432934555386452839, -10.09982228112793472 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19848553.7844137, 118.441353539081, 61.736686215549403663, 144831.1911566651614, -1.00060548620110489892454097571317106 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 8.489292700054 }, { 179.906698338023119097, -8.559237750032113623 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19995477.1669578, 171.963952699866, 8.037517851139094467, 72192.60793572974, -1.00152068486306466965629624610301107 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 19.562401114224 }, { 178.838724116996037606, -20.05038360490599475 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19893208.1788508, 126.362762598128, 53.875560227496658204, 112181.7524188837615, -1.00185202668802775249901060306001455 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 42.260350252749 }, { 179.807860448877064601, -42.79985897702184353 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19942715.0054774, 170.703419847646, 9.377654670896439828, 96336.3477142010769, -1.00508642406443549077721399953588843 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 24.511403144656 }, { 178.957598444862223515, -24.616808725039883945 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19924809.5184876, 102.913211410163, 77.297538210434837096, 55403.453072179318, -1.0008408309188838725134473861544393 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 20.844284170708 }, { 179.069258863637226633, -20.321320573298341477 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19909084.6340808, 44.172784008084, 136.01627115731728436, 111009.0987238994608, -0.997389183621778974142557672166731209 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 2.426010809098 }, { 178.236397468862000784, -2.513715200833756776 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19840940.6924189, 94.315194952561, 85.734896842737189557, 130002.6104886615638, -0.999825249844991659209370027383556589 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 6.600682554664 }, { 179.646475458013797028, -7.699164822656561787 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19878412.28273, 168.167678684515, 11.861035812918738552, 187426.3958525886692, -1.00098284856064978498579876031726599 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 23.372339802326 }, { 179.499422665106094027, -24.239465200482591299 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19899498.4582543, 161.197647943542, 18.932355367478826536, 151863.2545535951091, -1.00347666868431395492677893344080076 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 16.194668264095 }, { 179.115193814080201851, -17.129419031459576897 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19874825.6683239, 148.942349959054, 31.225656401221968078, 166033.3161394594622, -1.00222032222233647935638600756647065 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 1.528726471528 }, { 178.791047180477802091, -1.282203000582034597 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19897803.9939987, 69.212891442493, 110.802928803578167132, 85252.8333849204133, -0.999827144228156883265512533398577943 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 6.297249676078 }, { 178.623258703845895437, -5.709470001196540278 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19864042.0495193, 56.274639904925, 123.817184177744186806, 137475.1283083659258, -0.999190450178399580671850799262756482 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 17.393540327984 }, { 179.330156510680163326, -17.431100690958209424 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19962624.6302607, 107.855062015266, 72.181322855288535245, 19320.5501845044839, -1.00091841779689127989172447996679693 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 46.284685151236 }, { 179.852534804091121255, -46.176234945675219984 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19990422.3478916, 14.758013867151, 165.271681964991897184, 42614.1796365710104, -0.994894592261839960656288894824683666 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 14.924320176299 }, { 179.195663739713760883, -14.125476432252858442 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19891861.8615337, 31.446544793174, 148.678916887199611191, 149419.6596309045804, -0.997620142585332936313591289945179597 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 23.668824656069 }, { 179.409875478773990359, -24.107855233601412399 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19938736.4442268, 148.091483667618, 32.02919257641173958, 97771.7687385830819, -1.00323262872000595891108787327539176 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 46.986276695896 }, { 179.92040916864362177, -47.301644191214905832 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19968596.0414782, 174.796708941456, 5.234240076649939638, 66113.7417494369769, -1.00519095452608087093437916337279603 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 65.946144289524 }, { 179.808282612725835525, -65.871840130833632868 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19993734.5109736, 25.375428509648, 154.703163938350061652, 18355.2254271672769, -0.996436935914610577569305860379245132 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 10.950298933293 }, { 179.624609619829763098, -10.787771536605316781 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19975919.5586889, 28.779018914489, 151.238005588662201946, 70291.1998404303581, -0.998272071834115148902810688014142215 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 13.609869340778 }, { 179.035623147420893383, -14.023624108675206222 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19913213.8514358, 129.616021271129, 50.506400999466711623, 97596.7664002074776, -1.00146664642314031645753402699483559 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 48.701427557433 }, { 179.385565054218238481, -48.735316652259656533 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19972955.2699173, 102.875149183407, 77.294384444682547869, 18461.7742226227697, -1.00114676855429074464609584538266063 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 31.519172055785 }, { 179.555251675378549409, -31.140142027808697534 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19952318.3772514, 26.247105619999, 153.865822276646938125, 86354.7117605101002, -0.995739948399825047786748655198607594 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 31.863784754278 }, { 179.722489476483407524, -31.826935359797657785 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19993324.8682601, 29.572313410211, 150.440607907359037187, 41427.6181613499234, -0.995888009001147267440501309465616941 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 76.434608546092 }, { 179.918287057674124459, -76.48787937532808951 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19997750.023578, 167.428385412814, 12.621032110142724567, 9619.5267710862108, -1.00233963893091582164629471662919968 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 73.114273316483 }, { 179.576736605988553624, -73.098788070892914568 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19992866.6147806, 78.154765899661, 102.085693546950923465, 8580.6475692800946, -0.999384143308475469957841141876997426 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 1.125639056292 }, { 178.426819580880619395, -.694775021853292564 },&lt;/span&gt;
&lt;span class="gi"&gt;+        { 19852573.5442848, 67.184842289382, 112.831314850896246589, 132932.8743502563937, -0.999732957962833457266071945923613384 }&lt;/span&gt;
&lt;span class="gi"&gt;+    }&lt;/span&gt;
&lt;span class="gi"&gt;+};&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+size_t const expected_size_antipodal = sizeof(expected_antipodal) / sizeof(expected_results_antipodal);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+#endif // BOOST_GEOMETRY_TEST_INVERSE_CASES_ANTIPODAL_HPP&lt;/span&gt;

commit 4f0431085902aac5623fbf7db0909162a138a3f7
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Tue Jul 3 09:55:06 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [formulas][test] Add Karney&amp;#39;s inverse method in inverse test cases

&lt;span class="w"&gt; &lt;/span&gt;   The compilation is successful with gcc version (7.2.0),
&lt;span class="w"&gt; &lt;/span&gt;   but not with version (5.4.1). The accepted tolerance
&lt;span class="w"&gt; &lt;/span&gt;   is set to (0.0000001). Currently, all tests are not
&lt;span class="w"&gt; &lt;/span&gt;   passing, which indicates an error in the calculation.

&lt;span class="w"&gt; &lt;/span&gt;   Additionally, some changes have been made in
&lt;span class="w"&gt; &lt;/span&gt;   karney_inverse.hpp

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/formulas/karney_inverse.hpp b/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 517b966..3864fb1 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -98,7 +98,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT const tol_bisection = tol0 * tol2;

&lt;span class="w"&gt; &lt;/span&gt;        CT const etol2 = c0_1 * tol2 /
&lt;span class="gd"&gt;-            sqrt(std::max(c0_001, std::abs(f)) * std::min(c1, c1 - f / c2) / c2);&lt;/span&gt;
&lt;span class="gi"&gt;+            sqrt(std::max(CT(0.001), std::abs(f)) * std::min(CT(1), CT(1) - f / CT(2)) / c2);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        CT tiny = std::sqrt(std::numeric_limits&amp;lt;CT&amp;gt;::min());

&lt;span class="gu"&gt;@@ -226,8 +226,8 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            CT sin_sigma2 = sin_beta2;
&lt;span class="w"&gt; &lt;/span&gt;            CT cos_sigma2 = cos_alpha2 * cos_beta2;

&lt;span class="gd"&gt;-            CT sigma12 = std::atan2(std::max(c0, cos_sigma1 * sin_sigma2 - sin_sigma1 * cos_sigma2),&lt;/span&gt;
&lt;span class="gd"&gt;-                                                 cos_sigma1 * cos_sigma2 + sin_sigma1 * sin_sigma2);&lt;/span&gt;
&lt;span class="gi"&gt;+            CT sigma12 = std::atan2(std::max(CT(0), cos_sigma1 * sin_sigma2 - sin_sigma1 * cos_sigma2),&lt;/span&gt;
&lt;span class="gi"&gt;+                                                    cos_sigma1 * cos_sigma2 + sin_sigma1 * sin_sigma2);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            CT dummy;
&lt;span class="w"&gt; &lt;/span&gt;            meridian_length(n, ep2, sigma12, sin_sigma1, cos_sigma1, dn1,
&lt;span class="gu"&gt;@@ -670,8 +670,8 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                // Strip near cut.
&lt;span class="w"&gt; &lt;/span&gt;                if (f &amp;gt;= c0)
&lt;span class="w"&gt; &lt;/span&gt;                {
&lt;span class="gd"&gt;-                    sin_alpha1 = std::min(c1, -CT(x));&lt;/span&gt;
&lt;span class="gd"&gt;-                    cos_alpha1 = - std::sqrt(c1 - math::sqr(sin_alpha1));&lt;/span&gt;
&lt;span class="gi"&gt;+                    sin_alpha1 = std::min(CT(1), -CT(x));&lt;/span&gt;
&lt;span class="gi"&gt;+                    cos_alpha1 = - std::sqrt(CT(1) - math::sqr(sin_alpha1));&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                }
&lt;span class="w"&gt; &lt;/span&gt;                else
&lt;span class="w"&gt; &lt;/span&gt;                {
&lt;span class="gu"&gt;@@ -780,7 +780,6 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            // For y small, positive root is k = abs(y)/sqrt(1-x^2).
&lt;span class="w"&gt; &lt;/span&gt;            k = c0;
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        return k;
&lt;span class="w"&gt; &lt;/span&gt;    }

&lt;span class="gu"&gt;@@ -844,11 +843,11 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        math::normalize_values&amp;lt;CT&amp;gt;(sin_sigma2, cos_sigma2);

&lt;span class="w"&gt; &lt;/span&gt;        // sig12 = sig2 - sig1, limit to [0, pi].
&lt;span class="gd"&gt;-        sigma12 = atan2(std::max(c0, cos_sigma1 * sin_sigma2 - sin_sigma1 * cos_sigma2),&lt;/span&gt;
&lt;span class="gd"&gt;-                                     cos_sigma1 * cos_sigma2 + sin_sigma1 * sin_sigma2);&lt;/span&gt;
&lt;span class="gi"&gt;+        sigma12 = atan2(std::max(CT(0), cos_sigma1 * sin_sigma2 - sin_sigma1 * cos_sigma2),&lt;/span&gt;
&lt;span class="gi"&gt;+                                        cos_sigma1 * cos_sigma2 + sin_sigma1 * sin_sigma2);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        // omg12 = omg2 - omg1, limit to [0, pi].
&lt;span class="gd"&gt;-        sin_omega12 = std::max(c0, cos_omega1 * sin_omega2 - sin_omega1 * cos_omega2);&lt;/span&gt;
&lt;span class="gi"&gt;+        sin_omega12 = std::max(CT(0), cos_omega1 * sin_omega2 - sin_omega1 * cos_omega2);&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        cos_omega12 = cos_omega1 * cos_omega2 + sin_omega1 * sin_omega2;

&lt;span class="w"&gt; &lt;/span&gt;        // eta = omg12 - lam120.
&lt;span class="gu"&gt;@@ -900,7 +899,6 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                diff_lam12 *= one_minus_f / (cos_alpha2 * cos_beta2);
&lt;span class="w"&gt; &lt;/span&gt;            }
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        return lam12;
&lt;span class="w"&gt; &lt;/span&gt;    }

&lt;span class="gh"&gt;diff --git a/test/formulas/inverse.cpp b/test/formulas/inverse.cpp&lt;/span&gt;
&lt;span class="gh"&gt;index 59bb8b6..8e7ad03 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/test/formulas/inverse.cpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/test/formulas/inverse.cpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -18,6 +18,7 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;#include &amp;lt;boost/geometry/formulas/vincenty_inverse.hpp&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;#include &amp;lt;boost/geometry/formulas/thomas_inverse.hpp&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;#include &amp;lt;boost/geometry/formulas/andoyer_inverse.hpp&amp;gt;
&lt;span class="gi"&gt;+#include &amp;lt;boost/geometry/formulas/karney_inverse.hpp&amp;gt;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;#include &amp;lt;boost/geometry/srs/spheroid.hpp&amp;gt;

&lt;span class="gu"&gt;@@ -53,10 +54,15 @@ void test_all(expected_results const&amp;amp; results)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    double lon2r = results.p2.lon * d2r;
&lt;span class="w"&gt; &lt;/span&gt;    double lat2r = results.p2.lat * d2r;

&lt;span class="gi"&gt;+    double lon1d = results.p1.lon;&lt;/span&gt;
&lt;span class="gi"&gt;+    double lat1d = results.p1.lat;&lt;/span&gt;
&lt;span class="gi"&gt;+    double lon2d = results.p2.lon;&lt;/span&gt;
&lt;span class="gi"&gt;+    double lat2d = results.p2.lat;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    // WGS84
&lt;span class="w"&gt; &lt;/span&gt;    bg::srs::spheroid&amp;lt;double&amp;gt; spheroid(6378137.0, 6356752.3142451793);

&lt;span class="gd"&gt;-    bg::formula::result_inverse&amp;lt;double&amp;gt; result_v, result_t, result_a;&lt;/span&gt;
&lt;span class="gi"&gt;+    bg::formula::result_inverse&amp;lt;double&amp;gt; result_v, result_t, result_a, result_k;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    typedef bg::formula::vincenty_inverse&amp;lt;double, true, true, true, true, true&amp;gt; vi_t;
&lt;span class="w"&gt; &lt;/span&gt;    result_v = vi_t::apply(lon1r, lat1r, lon2r, lat2r, spheroid);
&lt;span class="gu"&gt;@@ -75,6 +81,10 @@ void test_all(expected_results const&amp;amp; results)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    result_a.azimuth *= r2d;
&lt;span class="w"&gt; &lt;/span&gt;    result_a.reverse_azimuth *= r2d;
&lt;span class="w"&gt; &lt;/span&gt;    check_inverse(&amp;quot;andoyer&amp;quot;, results, result_a, results.andoyer, results.reference, 0.001);
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    typedef bg::formula::karney_inverse&amp;lt;double, true, true, true, true, true, 8&amp;gt; ka_t;&lt;/span&gt;
&lt;span class="gi"&gt;+    result_k = ka_t::apply(lon1d, lat1d, lon2d, lat2d, spheroid);&lt;/span&gt;
&lt;span class="gi"&gt;+    check_inverse(&amp;quot;karney&amp;quot;, results, result_k, results.vincenty, results.reference, 0.0000001);&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;}

&lt;span class="w"&gt; &lt;/span&gt;int test_main(int, char*[])

commit 12bd41fd5db58d0f60d6544dafa981087f077aa5
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Mon Jul 2 13:47:58 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [formulas] Store values from Karney&amp;#39;s inverse method in result_inverse structure

&lt;span class="w"&gt; &lt;/span&gt;   The computed values from inverse method include distance, azimuth,
&lt;span class="w"&gt; &lt;/span&gt;   reverse_azimuth, reduced_length, and geodesic_scale.

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/formulas/karney_inverse.hpp b/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 5b6de69..517b966 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -133,8 +133,8 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        }

&lt;span class="w"&gt; &lt;/span&gt;        // Make points close to the equator to lie on it.
&lt;span class="gd"&gt;-        lat1 = std::abs(lat1) &amp;gt; 90 ? math::NaN&amp;lt;CT&amp;gt;() : lat1;&lt;/span&gt;
&lt;span class="gd"&gt;-        lat2 = std::abs(lat2) &amp;gt; 90 ? math::NaN&amp;lt;CT&amp;gt;() : lat2;&lt;/span&gt;
&lt;span class="gi"&gt;+        lat1 = std::abs(lat1) &amp;gt; c90 ? math::NaN&amp;lt;CT&amp;gt;() : lat1;&lt;/span&gt;
&lt;span class="gi"&gt;+        lat2 = std::abs(lat2) &amp;gt; c90 ? math::NaN&amp;lt;CT&amp;gt;() : lat2;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        lat1 = math::round_angle(lat1);
&lt;span class="w"&gt; &lt;/span&gt;        lat2 = math::round_angle(lat2);
&lt;span class="gu"&gt;@@ -217,8 +217,8 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            sin_alpha1 = sin_lam12;

&lt;span class="w"&gt; &lt;/span&gt;            // Heading north at the target.
&lt;span class="gd"&gt;-            cos_alpha2 = 1;&lt;/span&gt;
&lt;span class="gd"&gt;-            sin_alpha2 = 0;&lt;/span&gt;
&lt;span class="gi"&gt;+            cos_alpha2 = c1;&lt;/span&gt;
&lt;span class="gi"&gt;+            sin_alpha2 = c0;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            CT sin_sigma1 = sin_beta1;
&lt;span class="w"&gt; &lt;/span&gt;            CT cos_sigma1 = cos_alpha1 * cos_beta1;
&lt;span class="gu"&gt;@@ -227,7 +227,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            CT cos_sigma2 = cos_alpha2 * cos_beta2;

&lt;span class="w"&gt; &lt;/span&gt;            CT sigma12 = std::atan2(std::max(c0, cos_sigma1 * sin_sigma2 - sin_sigma1 * cos_sigma2),
&lt;span class="gd"&gt;-                                             cos_sigma1 * cos_sigma2 + sin_sigma1 * sin_sigma2);&lt;/span&gt;
&lt;span class="gi"&gt;+                                                 cos_sigma1 * cos_sigma2 + sin_sigma1 * sin_sigma2);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            CT dummy;
&lt;span class="w"&gt; &lt;/span&gt;            meridian_length(n, ep2, sigma12, sin_sigma1, cos_sigma1, dn1,
&lt;span class="gu"&gt;@@ -257,8 +257,8 @@ public:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        CT omega12, sin_omega12, cos_omega12;

&lt;span class="gd"&gt;-        if (!meridian &amp;amp;&amp;amp; sin_beta1 == 0 &amp;amp;&amp;amp;&lt;/span&gt;
&lt;span class="gd"&gt;-            (f &amp;lt;= 0 || lon12_error &amp;gt;= f * c180))&lt;/span&gt;
&lt;span class="gi"&gt;+        if (!meridian &amp;amp;&amp;amp; sin_beta1 == c0 &amp;amp;&amp;amp;&lt;/span&gt;
&lt;span class="gi"&gt;+            (f &amp;lt;= c0 || lon12_error &amp;gt;= f * c180))&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        {
&lt;span class="w"&gt; &lt;/span&gt;            // Points lie on the equator.
&lt;span class="w"&gt; &lt;/span&gt;            cos_alpha1 = cos_alpha2 = c0;
&lt;span class="gu"&gt;@@ -324,7 +324,6 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                     ++iteration)
&lt;span class="w"&gt; &lt;/span&gt;                {
&lt;span class="w"&gt; &lt;/span&gt;                    CT dv;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                    CT v = lambda12(sin_beta1, cos_beta1, dn1,
&lt;span class="w"&gt; &lt;/span&gt;                                    sin_beta2, cos_beta2, dn2,
&lt;span class="w"&gt; &lt;/span&gt;                                    sin_alpha1, cos_alpha1,
&lt;span class="gu"&gt;@@ -361,13 +360,13 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                        CT sin_diff_alpha1 = sin(diff_alpha1);
&lt;span class="w"&gt; &lt;/span&gt;                        CT cos_diff_alpha1 = cos(diff_alpha1);

&lt;span class="gd"&gt;-                        CT nsin_alpa1 = sin_alpha1 * cos_diff_alpha1 +&lt;/span&gt;
&lt;span class="gi"&gt;+                        CT nsin_alpha1 = sin_alpha1 * cos_diff_alpha1 +&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                                        cos_alpha1 * sin_diff_alpha1;

&lt;span class="gd"&gt;-                        if (nsin_alpa1 &amp;gt; c0 &amp;amp;&amp;amp; std::abs(diff_alpha1) &amp;lt; math::pi&amp;lt;CT&amp;gt;())&lt;/span&gt;
&lt;span class="gi"&gt;+                        if (nsin_alpha1 &amp;gt; c0 &amp;amp;&amp;amp; std::abs(diff_alpha1) &amp;lt; math::pi&amp;lt;CT&amp;gt;())&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                        {
&lt;span class="w"&gt; &lt;/span&gt;                            cos_alpha1 = cos_alpha1 * cos_diff_alpha1 - sin_alpha1 * sin_diff_alpha1;
&lt;span class="gd"&gt;-                            sin_alpha1 = nsin_alpa1;&lt;/span&gt;
&lt;span class="gi"&gt;+                            sin_alpha1 = nsin_alpha1;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                            math::normalize_values&amp;lt;CT&amp;gt;(sin_alpha1, cos_alpha1);

&lt;span class="w"&gt; &lt;/span&gt;                            // In some regimes we don&amp;#39;t get quadratic convergence because
&lt;span class="gu"&gt;@@ -394,12 +393,6 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                             std::abs(sin_alpha1 - sin_alpha1b) + (cos_alpha1 - cos_alpha1b) &amp;lt; tol_bisection);

&lt;span class="w"&gt; &lt;/span&gt;                }
&lt;span class="gd"&gt;-                // Store values in temporary vairables.&lt;/span&gt;
&lt;span class="gd"&gt;-                // bool enable_reduced_length = EnableReducedLength;&lt;/span&gt;
&lt;span class="gd"&gt;-                // bool enable_geodesic_scale = EnableGeodesicScale;&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-                // EnableReducedLength = false;&lt;/span&gt;
&lt;span class="gd"&gt;-                // EnableGeodesicScale = false;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;                CT dummy;
&lt;span class="w"&gt; &lt;/span&gt;                // Ensure that the reduced length and geodesic scale are computed in
&lt;span class="gu"&gt;@@ -410,11 +403,49 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                                                 m12x, dummy, result.geodesic_scale,
&lt;span class="w"&gt; &lt;/span&gt;                                                 M21, coeffs_C1);

&lt;span class="gd"&gt;-                // Restore values to their previous state.&lt;/span&gt;
&lt;span class="gd"&gt;-                // EnableReducedLength = enable_reduced_length;&lt;/span&gt;
&lt;span class="gd"&gt;-                // EnableGeodesicScale = enable_geodesic_scale;&lt;/span&gt;
&lt;span class="gi"&gt;+                m12x *= b;&lt;/span&gt;
&lt;span class="gi"&gt;+                s12x *= b;&lt;/span&gt;
&lt;span class="gi"&gt;+                a12 = sigma12 / math::d2r&amp;lt;CT&amp;gt;();&lt;/span&gt;
&lt;span class="gi"&gt;+            }&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        if (swap_point &amp;lt; 0)&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            swap(sin_alpha1, sin_alpha2);&lt;/span&gt;
&lt;span class="gi"&gt;+            swap(cos_alpha1, cos_alpha2);&lt;/span&gt;
&lt;span class="gi"&gt;+            swap(result.geodesic_scale, M21);&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        sin_alpha1 *= swap_point * lon12_sign;&lt;/span&gt;
&lt;span class="gi"&gt;+        cos_alpha1 *= swap_point * lat_sign;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        sin_alpha2 *= swap_point * lon12_sign;&lt;/span&gt;
&lt;span class="gi"&gt;+        cos_alpha2 *= swap_point * lat_sign;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        if (BOOST_GEOMETRY_CONDITION(EnableReducedLength))&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            result.reduced_length = m12x;&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        if (BOOST_GEOMETRY_CONDITION(CalcAzimuths))&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            if (BOOST_GEOMETRY_CONDITION(CalcFwdAzimuth))&lt;/span&gt;
&lt;span class="gi"&gt;+            {&lt;/span&gt;
&lt;span class="gi"&gt;+                result.azimuth = atan2(sin_alpha1, cos_alpha1) * math::r2d&amp;lt;CT&amp;gt;();&lt;/span&gt;
&lt;span class="gi"&gt;+            }&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            if (BOOST_GEOMETRY_CONDITION(CalcRevAzimuth))&lt;/span&gt;
&lt;span class="gi"&gt;+            {&lt;/span&gt;
&lt;span class="gi"&gt;+                result.reverse_azimuth = atan2(sin_alpha2, cos_alpha2) * math::r2d&amp;lt;CT&amp;gt;();&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            }
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        if (BOOST_GEOMETRY_CONDITION(EnableDistance))&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            result.distance = s12x;&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        return result;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    }

&lt;span class="w"&gt; &lt;/span&gt;    static inline void meridian_length(CT epsilon, CT ep2, CT sigma12,
&lt;span class="gu"&gt;@@ -436,8 +467,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        {
&lt;span class="w"&gt; &lt;/span&gt;            // Find the coefficients for A1 by computing the
&lt;span class="w"&gt; &lt;/span&gt;            // series expansion using Horner scehme.
&lt;span class="gd"&gt;-            expansion_A1&lt;/span&gt;
&lt;span class="gd"&gt;-                = series_expansion::evaluate_series_A1&amp;lt;CT, SeriesOrder&amp;gt;(epsilon);&lt;/span&gt;
&lt;span class="gi"&gt;+            expansion_A1 = series_expansion::evaluate_series_A1&amp;lt;CT, SeriesOrder&amp;gt;(epsilon);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            // Evaluate the coefficients for C1.
&lt;span class="w"&gt; &lt;/span&gt;            series_expansion::evaluate_coeffs_C1&amp;lt;CT, SeriesOrder&amp;gt;(epsilon, coeffs_C1);
&lt;span class="gu"&gt;@@ -447,8 +477,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            {
&lt;span class="w"&gt; &lt;/span&gt;                // Find the coefficients for A2 by computing the
&lt;span class="w"&gt; &lt;/span&gt;                // series expansion using Horner scehme.
&lt;span class="gd"&gt;-                expansion_A2&lt;/span&gt;
&lt;span class="gd"&gt;-                    = series_expansion::evaluate_series_A2&amp;lt;CT, SeriesOrder&amp;gt;(epsilon);&lt;/span&gt;
&lt;span class="gi"&gt;+                expansion_A2 = series_expansion::evaluate_series_A2&amp;lt;CT, SeriesOrder&amp;gt;(epsilon);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;                // Evaluate the coefficients for C2.
&lt;span class="w"&gt; &lt;/span&gt;                series_expansion::evaluate_coeffs_C2&amp;lt;CT, SeriesOrder&amp;gt;(epsilon, coeffs_C2);
&lt;span class="gu"&gt;@@ -466,7 +495,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                  - series_expansion::sin_cos_series&amp;lt;CT, SeriesOrder&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;                                      (sin_sigma1, cos_sigma1, coeffs_C1);

&lt;span class="gd"&gt;-            m12x = expansion_A1 * (sigma12 + B1);&lt;/span&gt;
&lt;span class="gi"&gt;+            s12x = expansion_A1 * (sigma12 + B1);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            if (BOOST_GEOMETRY_CONDITION(EnableReducedLength) ||
&lt;span class="w"&gt; &lt;/span&gt;                BOOST_GEOMETRY_CONDITION(EnableGeodesicScale))
&lt;span class="gu"&gt;@@ -587,7 +616,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                 sin_sigma12 &amp;gt;= c6 * std::abs(n) * math::pi&amp;lt;CT&amp;gt;() *
&lt;span class="w"&gt; &lt;/span&gt;                 math::sqr(cos_beta1))
&lt;span class="w"&gt; &lt;/span&gt;        {
&lt;span class="gd"&gt;-            // Nothing to do (?).&lt;/span&gt;
&lt;span class="gi"&gt;+            // Nothing to do.&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="w"&gt; &lt;/span&gt;        else
&lt;span class="w"&gt; &lt;/span&gt;        {
&lt;span class="gu"&gt;@@ -751,6 +780,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            // For y small, positive root is k = abs(y)/sqrt(1-x^2).
&lt;span class="w"&gt; &lt;/span&gt;            k = c0;
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        return k;
&lt;span class="w"&gt; &lt;/span&gt;    }

&lt;span class="gu"&gt;@@ -787,8 +817,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        sin_sigma1 = sin_beta1;
&lt;span class="w"&gt; &lt;/span&gt;        sin_omega1 = sin_alpha0 * sin_beta1;

&lt;span class="gd"&gt;-        cos_sigma1 = cos_omega1 =&lt;/span&gt;
&lt;span class="gd"&gt;-            cos_alpha1 * cos_beta1;&lt;/span&gt;
&lt;span class="gi"&gt;+        cos_sigma1 = cos_omega1 = cos_alpha1 * cos_beta1;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        math::normalize_values&amp;lt;CT&amp;gt;(sin_sigma1, cos_sigma1);

&lt;span class="gu"&gt;@@ -816,7 +845,7 @@ public:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        // sig12 = sig2 - sig1, limit to [0, pi].
&lt;span class="w"&gt; &lt;/span&gt;        sigma12 = atan2(std::max(c0, cos_sigma1 * sin_sigma2 - sin_sigma1 * cos_sigma2),
&lt;span class="gd"&gt;-                                cos_sigma1 * cos_sigma2 + sin_sigma1 * sin_sigma2);&lt;/span&gt;
&lt;span class="gi"&gt;+                                     cos_sigma1 * cos_sigma2 + sin_sigma1 * sin_sigma2);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        // omg12 = omg2 - omg1, limit to [0, pi].
&lt;span class="w"&gt; &lt;/span&gt;        sin_omega12 = std::max(c0, cos_omega1 * sin_omega2 - sin_omega1 * cos_omega2);
&lt;span class="gu"&gt;@@ -871,6 +900,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                diff_lam12 *= one_minus_f / (cos_alpha2 * cos_beta2);
&lt;span class="w"&gt; &lt;/span&gt;            }
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        return lam12;
&lt;span class="w"&gt; &lt;/span&gt;    }


commit 687df8e18a907b28e69ea67e76e0ad2271ad5d37
Merge: 6219503 048b1f7
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Jun 29 14:31:15 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Merge branch &amp;#39;develop&amp;#39; into feature/geodesic_direct

&lt;span class="w"&gt; &lt;/span&gt;   Conflicts:
&lt;span class="w"&gt; &lt;/span&gt;       include/boost/geometry/util/math.hpp
&lt;span class="w"&gt; &lt;/span&gt;       test/formulas/direct.cpp

&lt;span class="w"&gt; &lt;/span&gt;   The conflicting files have been updated.

commit 621950329ac5fe633b742403af1136d79c2c30a5
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Thu Jun 28 10:57:21 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [util] Pass range into math::polyval() instead of std::vector

&lt;span class="w"&gt; &lt;/span&gt;   This is done to avoid creating a separate container in each
&lt;span class="w"&gt; &lt;/span&gt;   iteration.

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/util/math.hpp b/include/boost/geometry/util/math.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 3c03c46..3b67359 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/util/math.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/util/math.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -855,18 +855,19 @@ inline NT horner_evaluate(NT x,&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;/*
&lt;span class="w"&gt; &lt;/span&gt;\brief Evaluate the polynomial.
&lt;span class="w"&gt; &lt;/span&gt;*/
&lt;span class="gd"&gt;-template&amp;lt;typename CT&amp;gt;&lt;/span&gt;
&lt;span class="gd"&gt;-inline CT polyval(std::vector&amp;lt;CT&amp;gt; coeff,&lt;/span&gt;
&lt;span class="gi"&gt;+template&amp;lt;typename IteratorType, typename CT&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+inline CT polyval(IteratorType first,&lt;/span&gt;
&lt;span class="gi"&gt;+                  IteratorType last,&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                  const CT eps)
&lt;span class="w"&gt; &lt;/span&gt;{
&lt;span class="gd"&gt;-    int N = boost::size(coeff) - 1;&lt;/span&gt;
&lt;span class="gi"&gt;+    int N = std::distance(first, last) - 1;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    int index = 0;

&lt;span class="gd"&gt;-    CT y = N &amp;lt; 0 ? 0 : coeff[index++];&lt;/span&gt;
&lt;span class="gi"&gt;+    CT y = N &amp;lt; 0 ? 0 : *(first + (index++));&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    while (--N &amp;gt;= 0)
&lt;span class="w"&gt; &lt;/span&gt;    {
&lt;span class="gd"&gt;-        y = y * eps + coeff[index++];&lt;/span&gt;
&lt;span class="gi"&gt;+        y = y * eps + *(first + (index++));&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    }

&lt;span class="w"&gt; &lt;/span&gt;    return y;
&lt;span class="gh"&gt;diff --git a/include/boost/geometry/util/series_expansion.hpp b/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 4ee6624..d0b5e36 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -642,8 +642,7 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            int m = Coeffs1::static_size - l - 1;
&lt;span class="w"&gt; &lt;/span&gt;            mult *= eps;

&lt;span class="gd"&gt;-            std::vector&amp;lt;CT&amp;gt; coeffs2_slice(coeffs2.begin(), coeffs2.begin() + offset);&lt;/span&gt;
&lt;span class="gd"&gt;-            coeffs1[l] = mult * math::polyval(coeffs2_slice, eps);&lt;/span&gt;
&lt;span class="gi"&gt;+            coeffs1[l] = mult * math::polyval(coeffs2.begin(), coeffs2.begin() + offset, eps);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            offset += m + 1;
&lt;span class="w"&gt; &lt;/span&gt;        }

commit 1fe3b3bd75d01ec6795f53dcde8eb7cdba2955a4
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Thu Jun 28 10:56:11 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [util] Pass SeriesOrder as template parameter in evaluate_coeffs_C3x() function

&lt;span class="w"&gt; &lt;/span&gt;   The coefficient container structs are moved to the
&lt;span class="w"&gt; &lt;/span&gt;   bottom of the file.

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/util/series_expansion.hpp b/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 17546db..4ee6624 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -30,66 +30,6 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;namespace boost { namespace geometry { namespace series_expansion {

&lt;span class="w"&gt; &lt;/span&gt;    /*
&lt;span class="gd"&gt;-     The coefficient containers for the series expansions.&lt;/span&gt;
&lt;span class="gd"&gt;-     These structs allow the caller to only know the series order.&lt;/span&gt;
&lt;span class="gd"&gt;-    */&lt;/span&gt;
&lt;span class="gd"&gt;-    template &amp;lt;size_t SeriesOrder, typename CT&amp;gt;&lt;/span&gt;
&lt;span class="gd"&gt;-    struct coeffs_C1 : boost::array&amp;lt;CT, SeriesOrder + 1&amp;gt;&lt;/span&gt;
&lt;span class="gd"&gt;-    {&lt;/span&gt;
&lt;span class="gd"&gt;-        coeffs_C1(CT const&amp;amp; epsilon)&lt;/span&gt;
&lt;span class="gd"&gt;-        {&lt;/span&gt;
&lt;span class="gd"&gt;-            evaluate_coeffs_C1(*this, epsilon);&lt;/span&gt;
&lt;span class="gd"&gt;-        }&lt;/span&gt;
&lt;span class="gd"&gt;-    };&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    template &amp;lt;size_t SeriesOrder, typename CT&amp;gt;&lt;/span&gt;
&lt;span class="gd"&gt;-    struct coeffs_C1p : boost::array&amp;lt;CT, SeriesOrder + 1&amp;gt;&lt;/span&gt;
&lt;span class="gd"&gt;-    {&lt;/span&gt;
&lt;span class="gd"&gt;-        coeffs_C1p(CT const&amp;amp; epsilon)&lt;/span&gt;
&lt;span class="gd"&gt;-        {&lt;/span&gt;
&lt;span class="gd"&gt;-            evaluate_coeffs_C1p(*this, epsilon);&lt;/span&gt;
&lt;span class="gd"&gt;-        }&lt;/span&gt;
&lt;span class="gd"&gt;-    };&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    template &amp;lt;size_t SeriesOrder, typename CT&amp;gt;&lt;/span&gt;
&lt;span class="gd"&gt;-    struct coeffs_C2 : boost::array&amp;lt;CT, SeriesOrder + 1&amp;gt;&lt;/span&gt;
&lt;span class="gd"&gt;-    {&lt;/span&gt;
&lt;span class="gd"&gt;-        coeffs_C2(CT const&amp;amp; epsilon)&lt;/span&gt;
&lt;span class="gd"&gt;-        {&lt;/span&gt;
&lt;span class="gd"&gt;-            evaluate_coeffs_C2(*this, epsilon);&lt;/span&gt;
&lt;span class="gd"&gt;-        }&lt;/span&gt;
&lt;span class="gd"&gt;-    };&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    template &amp;lt;size_t SeriesOrder, typename CT&amp;gt;&lt;/span&gt;
&lt;span class="gd"&gt;-    struct coeffs_C3x : boost::array&amp;lt;CT, (SeriesOrder * (SeriesOrder - 1)) / 2&amp;gt;&lt;/span&gt;
&lt;span class="gd"&gt;-    {&lt;/span&gt;
&lt;span class="gd"&gt;-        coeffs_C3x(CT const&amp;amp; n)&lt;/span&gt;
&lt;span class="gd"&gt;-        {&lt;/span&gt;
&lt;span class="gd"&gt;-            evaluate_coeffs_C3x(*this, SeriesOrder, n);&lt;/span&gt;
&lt;span class="gd"&gt;-        }&lt;/span&gt;
&lt;span class="gd"&gt;-    };&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    template &amp;lt;size_t SeriesOrder, typename CT&amp;gt;&lt;/span&gt;
&lt;span class="gd"&gt;-    struct coeffs_C3 : boost::array&amp;lt;CT, SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gd"&gt;-    {&lt;/span&gt;
&lt;span class="gd"&gt;-        coeffs_C3(CT const&amp;amp; n, CT const&amp;amp; epsilon)&lt;/span&gt;
&lt;span class="gd"&gt;-        {&lt;/span&gt;
&lt;span class="gd"&gt;-            coeffs_C3x&amp;lt;SeriesOrder, CT&amp;gt; coeffs_C3x(n);&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-            evaluate_coeffs_C3(*this, coeffs_C3x, epsilon);&lt;/span&gt;
&lt;span class="gd"&gt;-        }&lt;/span&gt;
&lt;span class="gd"&gt;-    };&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    template &amp;lt;size_t SeriesOrder, typename CT&amp;gt;&lt;/span&gt;
&lt;span class="gd"&gt;-    struct coeffs_A3 : boost::array&amp;lt;CT, SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gd"&gt;-    {&lt;/span&gt;
&lt;span class="gd"&gt;-        coeffs_A3(CT const&amp;amp; n)&lt;/span&gt;
&lt;span class="gd"&gt;-        {&lt;/span&gt;
&lt;span class="gd"&gt;-            evaluate_coeffs_A3(*this, n);&lt;/span&gt;
&lt;span class="gd"&gt;-        }&lt;/span&gt;
&lt;span class="gd"&gt;-    };&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    /*&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;     Generate and evaluate the series expansion of the following integral

&lt;span class="w"&gt; &lt;/span&gt;     I1 = integrate( sqrt(1+k2*sin(sigma1)^2), sigma1, 0, sigma )
&lt;span class="gu"&gt;@@ -570,10 +510,11 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;     generated by the following Maxima script:
&lt;span class="w"&gt; &lt;/span&gt;     geometry/doc/other/maxima/geod.mac
&lt;span class="w"&gt; &lt;/span&gt;    */
&lt;span class="gd"&gt;-    template &amp;lt;typename Coeffs, typename CT&amp;gt;&lt;/span&gt;
&lt;span class="gd"&gt;-    inline void evaluate_coeffs_C3x(Coeffs &amp;amp;c, size_t const&amp;amp; SeriesOrder, CT const&amp;amp; n) {&lt;/span&gt;
&lt;span class="gi"&gt;+    template &amp;lt;size_t SeriesOrder, typename Coeffs, typename CT&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    inline void evaluate_coeffs_C3x(Coeffs &amp;amp;c, CT const&amp;amp; n) {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        size_t const coeff_size = Coeffs::static_size;
&lt;span class="gd"&gt;-        BOOST_GEOMETRY_ASSERT(coeff_size == (SeriesOrder * (SeriesOrder - 1)) / 2);&lt;/span&gt;
&lt;span class="gi"&gt;+        size_t const expected_size = (SeriesOrder * (SeriesOrder - 1)) / 2;&lt;/span&gt;
&lt;span class="gi"&gt;+        BOOST_GEOMETRY_ASSERT((coeff_size == expected_size));&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        const CT n2 = math::sqr(n);
&lt;span class="w"&gt; &lt;/span&gt;        switch (SeriesOrder) {
&lt;span class="gu"&gt;@@ -741,6 +682,66 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        return 2 * sinx * cosx * k0;
&lt;span class="w"&gt; &lt;/span&gt;    }

&lt;span class="gi"&gt;+    /*&lt;/span&gt;
&lt;span class="gi"&gt;+     The coefficient containers for the series expansions.&lt;/span&gt;
&lt;span class="gi"&gt;+     These structs allow the caller to only know the series order.&lt;/span&gt;
&lt;span class="gi"&gt;+    */&lt;/span&gt;
&lt;span class="gi"&gt;+    template &amp;lt;size_t SeriesOrder, typename CT&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    struct coeffs_C1 : boost::array&amp;lt;CT, SeriesOrder + 1&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    {&lt;/span&gt;
&lt;span class="gi"&gt;+        coeffs_C1(CT const&amp;amp; epsilon)&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            evaluate_coeffs_C1(*this, epsilon);&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+    };&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    template &amp;lt;size_t SeriesOrder, typename CT&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    struct coeffs_C1p : boost::array&amp;lt;CT, SeriesOrder + 1&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    {&lt;/span&gt;
&lt;span class="gi"&gt;+        coeffs_C1p(CT const&amp;amp; epsilon)&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            evaluate_coeffs_C1p(*this, epsilon);&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+    };&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    template &amp;lt;size_t SeriesOrder, typename CT&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    struct coeffs_C2 : boost::array&amp;lt;CT, SeriesOrder + 1&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    {&lt;/span&gt;
&lt;span class="gi"&gt;+        coeffs_C2(CT const&amp;amp; epsilon)&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            evaluate_coeffs_C2(*this, epsilon);&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+    };&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    template &amp;lt;size_t SeriesOrder, typename CT&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    struct coeffs_C3x : boost::array&amp;lt;CT, (SeriesOrder * (SeriesOrder - 1)) / 2&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    {&lt;/span&gt;
&lt;span class="gi"&gt;+        coeffs_C3x(CT const&amp;amp; n)&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            evaluate_coeffs_C3x&amp;lt;SeriesOrder&amp;gt;(*this, n);&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+    };&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    template &amp;lt;size_t SeriesOrder, typename CT&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    struct coeffs_C3 : boost::array&amp;lt;CT, SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    {&lt;/span&gt;
&lt;span class="gi"&gt;+        coeffs_C3(CT const&amp;amp; n, CT const&amp;amp; epsilon)&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            coeffs_C3x&amp;lt;SeriesOrder, CT&amp;gt; coeffs_C3x(n);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            evaluate_coeffs_C3(*this, coeffs_C3x, epsilon);&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+    };&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    template &amp;lt;size_t SeriesOrder, typename CT&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    struct coeffs_A3 : boost::array&amp;lt;CT, SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    {&lt;/span&gt;
&lt;span class="gi"&gt;+        coeffs_A3(CT const&amp;amp; n)&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            evaluate_coeffs_A3(*this, n);&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+    };&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;}}} // namespace boost::geometry::series_expansion

&lt;span class="w"&gt; &lt;/span&gt;#endif // BOOST_GEOMETRY_UTIL_SERIES_EXPANSION_HPP

commit b86a93bc5a81c066a9f93a986943874bca055cf2
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Thu Jun 28 09:51:25 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [util] Rename math::normalize_values to math::normalize_unit_vector

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/formulas/karney_direct.hpp b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 95e4040..1e6add7 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -112,7 +112,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        math::sin_cos_degrees&amp;lt;CT&amp;gt;(math::round_angle&amp;lt;CT&amp;gt;(lat1), sin_beta1, cos_beta1);
&lt;span class="w"&gt; &lt;/span&gt;        sin_beta1 *= one_minus_f;

&lt;span class="gd"&gt;-        math::normalize_values&amp;lt;CT&amp;gt;(sin_beta1, cos_beta1);&lt;/span&gt;
&lt;span class="gi"&gt;+        math::normalize_unit_vector&amp;lt;CT&amp;gt;(sin_beta1, cos_beta1);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        cos_beta1 = (std::max)(c0, cos_beta1);

&lt;span class="gu"&gt;@@ -142,7 +142,7 @@ public:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        CT cos_sigma1, cos_omega1;
&lt;span class="w"&gt; &lt;/span&gt;        cos_sigma1 = cos_omega1 = sin_beta1 != c0 || cos_alpha1 != c0 ? cos_beta1 * cos_alpha1 : c1;
&lt;span class="gd"&gt;-        math::normalize_values&amp;lt;CT&amp;gt;(sin_sigma1, cos_sigma1);&lt;/span&gt;
&lt;span class="gi"&gt;+        math::normalize_unit_vector&amp;lt;CT&amp;gt;(sin_sigma1, cos_sigma1);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        CT const B11 = se::sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C1);
&lt;span class="w"&gt; &lt;/span&gt;        CT const sin_B11 = sin(B11);
&lt;span class="gh"&gt;diff --git a/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp b/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 866509d..ac1e447 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -387,7 +387,7 @@ TODO: adl1995 - Merge this function with&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;formulas/vertex_longitude.hpp
&lt;span class="w"&gt; &lt;/span&gt;*/
&lt;span class="w"&gt; &lt;/span&gt;template&amp;lt;typename ValueType&amp;gt;
&lt;span class="gd"&gt;-inline void normalize_values(ValueType&amp;amp; x, ValueType&amp;amp; y)&lt;/span&gt;
&lt;span class="gi"&gt;+inline void normalize_unit_vector(ValueType&amp;amp; x, ValueType&amp;amp; y)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;{
&lt;span class="w"&gt; &lt;/span&gt;    ValueType h = boost::math::hypot(x, y);


commit 9ff4fbe94a9195411005d2a3a6c803b8736fb74b
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Wed Jun 27 10:46:27 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [util] Use functions from math namespace instead of std

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/util/math.hpp b/include/boost/geometry/util/math.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 74afb49..3c03c46 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/util/math.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/util/math.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -788,14 +788,14 @@ inline void sin_cos_degrees(T const&amp;amp; x,&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    // the argument to the range [-45, 45] before converting it to radians.
&lt;span class="w"&gt; &lt;/span&gt;    T remainder; int quotient;

&lt;span class="gd"&gt;-    remainder = std::fmod(x, T(360));&lt;/span&gt;
&lt;span class="gd"&gt;-    quotient = int(std::floor(remainder / 90 + T(0.5)));&lt;/span&gt;
&lt;span class="gi"&gt;+    remainder = math::mod(x, T(360));&lt;/span&gt;
&lt;span class="gi"&gt;+    quotient = floor(remainder / 90 + T(0.5));&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    remainder -= 90 * quotient;

&lt;span class="w"&gt; &lt;/span&gt;    // Convert to radians.
&lt;span class="w"&gt; &lt;/span&gt;    remainder *= d2r&amp;lt;T&amp;gt;();

&lt;span class="gd"&gt;-    T s = std::sin(remainder), c = std::cos(remainder);&lt;/span&gt;
&lt;span class="gi"&gt;+    T s = sin(remainder), c = cos(remainder);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    switch (unsigned(quotient) &amp;amp; 3U)
&lt;span class="w"&gt; &lt;/span&gt;    {
&lt;span class="gu"&gt;@@ -824,7 +824,7 @@ inline T round_angle(T x) {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        return 0;
&lt;span class="w"&gt; &lt;/span&gt;    }

&lt;span class="gd"&gt;-    T y = std::abs(x);&lt;/span&gt;
&lt;span class="gi"&gt;+    T y = math::abs(x);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    // z - (z - y) must not be simplified to y.
&lt;span class="w"&gt; &lt;/span&gt;    y = y &amp;lt; z ? z - (z - y) : y;

commit 9699b4909891d2bbba7a6dd84bda2bf2ec8ef511
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Wed Jun 27 10:34:18 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [util][formulas] Rename normalize_angle function to normalize_azimuth

&lt;span class="w"&gt; &lt;/span&gt;   For normalizing longitudes, the normalize_longitude function is
&lt;span class="w"&gt; &lt;/span&gt;   used instead.

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/formulas/karney_direct.hpp b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 6de8a26..95e4040 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -80,7 +80,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT const lat1 = la1;

&lt;span class="w"&gt; &lt;/span&gt;        Azi azi12 = azimuth12;
&lt;span class="gd"&gt;-        math::normalize_angle&amp;lt;degree, Azi&amp;gt;(azi12);&lt;/span&gt;
&lt;span class="gi"&gt;+        math::normalize_azimuth&amp;lt;degree, Azi&amp;gt;(azi12);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        Dist const dist_c0 = 0;

&lt;span class="gu"&gt;@@ -114,7 +114,7 @@ public:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        math::normalize_values&amp;lt;CT&amp;gt;(sin_beta1, cos_beta1);

&lt;span class="gd"&gt;-        cos_beta1 = std::max(math::sqrt(c0), cos_beta1);&lt;/span&gt;
&lt;span class="gi"&gt;+        cos_beta1 = (std::max)(c0, cos_beta1);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        // Obtain alpha 0 by solving the spherical triangle.
&lt;span class="w"&gt; &lt;/span&gt;        CT const sin_alpha0 = sin_alpha1 * cos_beta1;
&lt;span class="gu"&gt;@@ -216,8 +216,8 @@ public:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            // Add the longitude at first point to the longitudinal
&lt;span class="w"&gt; &lt;/span&gt;            // difference and normalize the result.
&lt;span class="gd"&gt;-            math::normalize_angle&amp;lt;degree, CT&amp;gt;(lon1);&lt;/span&gt;
&lt;span class="gd"&gt;-            math::normalize_angle&amp;lt;degree, CT&amp;gt;(lon12);&lt;/span&gt;
&lt;span class="gi"&gt;+            math::normalize_longitude&amp;lt;degree, CT&amp;gt;(lon1);&lt;/span&gt;
&lt;span class="gi"&gt;+            math::normalize_longitude&amp;lt;degree, CT&amp;gt;(lon12);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            result.lon2 = lon1 + lon12;
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="gh"&gt;diff --git a/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp b/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 516bdb3..866509d 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -365,15 +365,15 @@ inline void normalize_longitude(CoordinateType&amp;amp; longitude)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;}

&lt;span class="w"&gt; &lt;/span&gt;/*!
&lt;span class="gd"&gt;-\brief Short utility to normalize an angle on a spheroid&lt;/span&gt;
&lt;span class="gd"&gt;-       normalized in range (-180, 180].&lt;/span&gt;
&lt;span class="gi"&gt;+\brief Short utility to normalize the azimuth on a spheroid&lt;/span&gt;
&lt;span class="gi"&gt;+       in the range (-180, 180].&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;\tparam Units The units of the coordindate system in the spheroid
&lt;span class="w"&gt; &lt;/span&gt;\tparam CoordinateType The type of the coordinates
&lt;span class="w"&gt; &lt;/span&gt;\param angle Angle
&lt;span class="w"&gt; &lt;/span&gt;\ingroup utility
&lt;span class="w"&gt; &lt;/span&gt;*/
&lt;span class="w"&gt; &lt;/span&gt;template &amp;lt;typename Units, typename CoordinateType&amp;gt;
&lt;span class="gd"&gt;-inline void normalize_angle(CoordinateType&amp;amp; angle)&lt;/span&gt;
&lt;span class="gi"&gt;+inline void normalize_azimuth(CoordinateType&amp;amp; angle)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;{
&lt;span class="w"&gt; &lt;/span&gt;    normalize_longitude&amp;lt;Units, CoordinateType&amp;gt;(angle);
&lt;span class="w"&gt; &lt;/span&gt;}

commit c26483b9fcaed93a38811031f207b0d647dd704c
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Tue Jun 26 17:11:35 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [util] Change static inline to inline in series_expansion.hpp file

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/util/series_expansion.hpp b/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index a679ede..17546db 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -115,7 +115,7 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;               s/case\sCT(/case /g; s/):/:/g; s/epsCT(2)/eps2/g;&amp;#39;
&lt;span class="w"&gt; &lt;/span&gt;    */
&lt;span class="w"&gt; &lt;/span&gt;    template &amp;lt;size_t SeriesOrder, typename CT&amp;gt;
&lt;span class="gd"&gt;-    static inline CT evaluate_A1(CT eps)&lt;/span&gt;
&lt;span class="gi"&gt;+    inline CT evaluate_A1(CT eps)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    {
&lt;span class="w"&gt; &lt;/span&gt;        CT eps2 = math::sqr(eps);
&lt;span class="w"&gt; &lt;/span&gt;        CT t;
&lt;span class="gu"&gt;@@ -205,7 +205,7 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;     geometry/doc/other/maxima/geod.mac
&lt;span class="w"&gt; &lt;/span&gt;    */
&lt;span class="w"&gt; &lt;/span&gt;    template &amp;lt;typename Coeffs, typename CT&amp;gt;
&lt;span class="gd"&gt;-    static inline void evaluate_coeffs_A3(Coeffs &amp;amp;c, CT const&amp;amp; n)&lt;/span&gt;
&lt;span class="gi"&gt;+    inline void evaluate_coeffs_A3(Coeffs &amp;amp;c, CT const&amp;amp; n)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    {
&lt;span class="w"&gt; &lt;/span&gt;        switch (int(Coeffs::static_size)) {
&lt;span class="w"&gt; &lt;/span&gt;        case 0:
&lt;span class="gu"&gt;@@ -274,7 +274,7 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;     geometry/doc/other/maxima/geod.mac
&lt;span class="w"&gt; &lt;/span&gt;    */
&lt;span class="w"&gt; &lt;/span&gt;    template &amp;lt;typename Coeffs, typename CT&amp;gt;
&lt;span class="gd"&gt;-    static inline void evaluate_coeffs_C1(Coeffs &amp;amp;c, CT const&amp;amp; eps)&lt;/span&gt;
&lt;span class="gi"&gt;+    inline void evaluate_coeffs_C1(Coeffs &amp;amp;c, CT const&amp;amp; eps)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    {
&lt;span class="w"&gt; &lt;/span&gt;        CT eps2 = math::sqr(eps);
&lt;span class="w"&gt; &lt;/span&gt;        CT d = eps;
&lt;span class="gu"&gt;@@ -373,7 +373,7 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;     geometry/doc/other/maxima/geod.mac
&lt;span class="w"&gt; &lt;/span&gt;    */
&lt;span class="w"&gt; &lt;/span&gt;    template &amp;lt;typename Coeffs, typename CT&amp;gt;
&lt;span class="gd"&gt;-    static inline void evaluate_coeffs_C1p(Coeffs&amp;amp; c, CT const&amp;amp; eps)&lt;/span&gt;
&lt;span class="gi"&gt;+    inline void evaluate_coeffs_C1p(Coeffs&amp;amp; c, CT const&amp;amp; eps)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    {
&lt;span class="w"&gt; &lt;/span&gt;        CT const eps2 = math::sqr(eps);
&lt;span class="w"&gt; &lt;/span&gt;        CT d = eps;
&lt;span class="gu"&gt;@@ -472,7 +472,7 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;     geometry/doc/other/maxima/geod.mac
&lt;span class="w"&gt; &lt;/span&gt;    */
&lt;span class="w"&gt; &lt;/span&gt;    template &amp;lt;typename Coeffs, typename CT&amp;gt;
&lt;span class="gd"&gt;-    static inline void evaluate_coeffs_C2(Coeffs&amp;amp; c, CT const&amp;amp; eps)&lt;/span&gt;
&lt;span class="gi"&gt;+    inline void evaluate_coeffs_C2(Coeffs&amp;amp; c, CT const&amp;amp; eps)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    {
&lt;span class="w"&gt; &lt;/span&gt;        CT const eps2 = math::sqr(eps);
&lt;span class="w"&gt; &lt;/span&gt;        CT d = eps;
&lt;span class="gu"&gt;@@ -571,7 +571,7 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;     geometry/doc/other/maxima/geod.mac
&lt;span class="w"&gt; &lt;/span&gt;    */
&lt;span class="w"&gt; &lt;/span&gt;    template &amp;lt;typename Coeffs, typename CT&amp;gt;
&lt;span class="gd"&gt;-    static inline void evaluate_coeffs_C3x(Coeffs &amp;amp;c, size_t const&amp;amp; SeriesOrder, CT const&amp;amp; n) {&lt;/span&gt;
&lt;span class="gi"&gt;+    inline void evaluate_coeffs_C3x(Coeffs &amp;amp;c, size_t const&amp;amp; SeriesOrder, CT const&amp;amp; n) {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        size_t const coeff_size = Coeffs::static_size;
&lt;span class="w"&gt; &lt;/span&gt;        BOOST_GEOMETRY_ASSERT(coeff_size == (SeriesOrder * (SeriesOrder - 1)) / 2);

&lt;span class="gu"&gt;@@ -689,7 +689,7 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;      Elements coeffs1[1] through coeffs1[SeriesOrder - 1] are set.
&lt;span class="w"&gt; &lt;/span&gt;    */
&lt;span class="w"&gt; &lt;/span&gt;    template &amp;lt;typename Coeffs1, typename Coeffs2, typename CT&amp;gt;
&lt;span class="gd"&gt;-    static inline void evaluate_coeffs_C3(Coeffs1 &amp;amp;coeffs1, Coeffs2 &amp;amp;coeffs2, CT const&amp;amp; eps)&lt;/span&gt;
&lt;span class="gi"&gt;+    inline void evaluate_coeffs_C3(Coeffs1 &amp;amp;coeffs1, Coeffs2 &amp;amp;coeffs2, CT const&amp;amp; eps)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    {
&lt;span class="w"&gt; &lt;/span&gt;        CT mult = 1;
&lt;span class="w"&gt; &lt;/span&gt;        int offset = 1;
&lt;span class="gu"&gt;@@ -717,7 +717,7 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;     using Clenshaw summation.
&lt;span class="w"&gt; &lt;/span&gt;    */
&lt;span class="w"&gt; &lt;/span&gt;    template &amp;lt;typename CT, typename Coeffs&amp;gt;
&lt;span class="gd"&gt;-    static inline CT sin_cos_series(CT const&amp;amp; sinx, CT const&amp;amp; cosx, Coeffs const&amp;amp; coeffs)&lt;/span&gt;
&lt;span class="gi"&gt;+    inline CT sin_cos_series(CT const&amp;amp; sinx, CT const&amp;amp; cosx, Coeffs const&amp;amp; coeffs)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    {
&lt;span class="w"&gt; &lt;/span&gt;        size_t n = Coeffs::static_size - 1;
&lt;span class="w"&gt; &lt;/span&gt;        size_t index = 0;

commit 4d39eae2b688cadb0ec9581d667568d0fe2bb0e4
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Tue Jun 26 16:53:46 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [util] Add BOOST_GEOMETRY_ASSERT in series expansion and normalization function

&lt;span class="w"&gt; &lt;/span&gt;   Modified functions are:
&lt;span class="w"&gt; &lt;/span&gt;   - evaluate_coeffs_C3x
&lt;span class="w"&gt; &lt;/span&gt;   - normalize_values

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp b/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 4c432a8..516bdb3 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -391,6 +391,8 @@ inline void normalize_values(ValueType&amp;amp; x, ValueType&amp;amp; y)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;{
&lt;span class="w"&gt; &lt;/span&gt;    ValueType h = boost::math::hypot(x, y);

&lt;span class="gi"&gt;+    BOOST_GEOMETRY_ASSERT(h &amp;gt; 0);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    x /= h; y /= h;
&lt;span class="w"&gt; &lt;/span&gt;}

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/util/series_expansion.hpp b/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 8fb69e7..a679ede 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -24,6 +24,7 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;#ifndef BOOST_GEOMETRY_UTIL_SERIES_EXPANSION_HPP
&lt;span class="w"&gt; &lt;/span&gt;#define BOOST_GEOMETRY_UTIL_SERIES_EXPANSION_HPP

&lt;span class="gi"&gt;+#include &amp;lt;boost/geometry/core/assert.hpp&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;#include &amp;lt;boost/geometry/util/math.hpp&amp;gt;

&lt;span class="w"&gt; &lt;/span&gt;namespace boost { namespace geometry { namespace series_expansion {
&lt;span class="gu"&gt;@@ -571,6 +572,9 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    */
&lt;span class="w"&gt; &lt;/span&gt;    template &amp;lt;typename Coeffs, typename CT&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;    static inline void evaluate_coeffs_C3x(Coeffs &amp;amp;c, size_t const&amp;amp; SeriesOrder, CT const&amp;amp; n) {
&lt;span class="gi"&gt;+        size_t const coeff_size = Coeffs::static_size;&lt;/span&gt;
&lt;span class="gi"&gt;+        BOOST_GEOMETRY_ASSERT(coeff_size == (SeriesOrder * (SeriesOrder - 1)) / 2);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        const CT n2 = math::sqr(n);
&lt;span class="w"&gt; &lt;/span&gt;        switch (SeriesOrder) {
&lt;span class="w"&gt; &lt;/span&gt;        case 0:

commit 9c96bec2bf8521e720a97674b3b93c626769e702
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Tue Jun 26 13:20:12 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [util] Avoid passing array size using std::vector

&lt;span class="w"&gt; &lt;/span&gt;   Previously, the array size was passed in as a
&lt;span class="w"&gt; &lt;/span&gt;   separate parameter.

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/formulas/karney_direct.hpp b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 891def0..6de8a26 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -114,7 +114,7 @@ public:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        math::normalize_values&amp;lt;CT&amp;gt;(sin_beta1, cos_beta1);

&lt;span class="gd"&gt;-        cos_beta1 = std::max(sqrt(std::numeric_limits&amp;lt;CT&amp;gt;::min()), cos_beta1);&lt;/span&gt;
&lt;span class="gi"&gt;+        cos_beta1 = std::max(math::sqrt(c0), cos_beta1);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        // Obtain alpha 0 by solving the spherical triangle.
&lt;span class="w"&gt; &lt;/span&gt;        CT const sin_alpha0 = sin_alpha1 * cos_beta1;
&lt;span class="gu"&gt;@@ -122,7 +122,7 @@ public:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        CT const k2 = math::sqr(cos_alpha0) * ep2;

&lt;span class="gd"&gt;-        CT const epsilon = k2 / (c2 * (c1 + sqrt(c1 + k2)) + k2);&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const epsilon = k2 / (c2 * (c1 + math::sqrt(c1 + k2)) + k2);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        // Find the coefficients for A1 by computing the
&lt;span class="w"&gt; &lt;/span&gt;        // series expansion using Horner scehme.
&lt;span class="gh"&gt;diff --git a/include/boost/geometry/util/math.hpp b/include/boost/geometry/util/math.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 6559910..74afb49 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/util/math.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/util/math.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -856,15 +856,17 @@ inline NT horner_evaluate(NT x,&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;\brief Evaluate the polynomial.
&lt;span class="w"&gt; &lt;/span&gt;*/
&lt;span class="w"&gt; &lt;/span&gt;template&amp;lt;typename CT&amp;gt;
&lt;span class="gd"&gt;-inline CT polyval(int N,&lt;/span&gt;
&lt;span class="gd"&gt;-                  const CT coeff[],&lt;/span&gt;
&lt;span class="gi"&gt;+inline CT polyval(std::vector&amp;lt;CT&amp;gt; coeff,&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                  const CT eps)
&lt;span class="w"&gt; &lt;/span&gt;{
&lt;span class="gd"&gt;-    CT y = N &amp;lt; 0 ? 0 : *coeff++;&lt;/span&gt;
&lt;span class="gi"&gt;+    int N = boost::size(coeff) - 1;&lt;/span&gt;
&lt;span class="gi"&gt;+    int index = 0;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    CT y = N &amp;lt; 0 ? 0 : coeff[index++];&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    while (--N &amp;gt;= 0)
&lt;span class="w"&gt; &lt;/span&gt;    {
&lt;span class="gd"&gt;-        y = y * eps + *coeff++;&lt;/span&gt;
&lt;span class="gi"&gt;+        y = y * eps + coeff[index++];&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    }

&lt;span class="w"&gt; &lt;/span&gt;    return y;
&lt;span class="gh"&gt;diff --git a/include/boost/geometry/util/series_expansion.hpp b/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 31decf6..8fb69e7 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -688,7 +688,7 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    static inline void evaluate_coeffs_C3(Coeffs1 &amp;amp;coeffs1, Coeffs2 &amp;amp;coeffs2, CT const&amp;amp; eps)
&lt;span class="w"&gt; &lt;/span&gt;    {
&lt;span class="w"&gt; &lt;/span&gt;        CT mult = 1;
&lt;span class="gd"&gt;-        int offset = 0;&lt;/span&gt;
&lt;span class="gi"&gt;+        int offset = 1;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        // l is the index of C3[l].
&lt;span class="w"&gt; &lt;/span&gt;        for (size_t l = 1; l &amp;lt; Coeffs1::static_size; ++l)
&lt;span class="gu"&gt;@@ -696,7 +696,10 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            // Order of polynomial in eps.
&lt;span class="w"&gt; &lt;/span&gt;            int m = Coeffs1::static_size - l - 1;
&lt;span class="w"&gt; &lt;/span&gt;            mult *= eps;
&lt;span class="gd"&gt;-            coeffs1[l] = mult * math::polyval(m, coeffs2.begin() + offset, eps);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            std::vector&amp;lt;CT&amp;gt; coeffs2_slice(coeffs2.begin(), coeffs2.begin() + offset);&lt;/span&gt;
&lt;span class="gi"&gt;+            coeffs1[l] = mult * math::polyval(coeffs2_slice, eps);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            offset += m + 1;
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="w"&gt; &lt;/span&gt;        // Post condition: offset == coeffs_C3_size
&lt;span class="gu"&gt;@@ -719,15 +722,16 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        index += (n + 1);
&lt;span class="w"&gt; &lt;/span&gt;        CT ar = 2 * (cosx - sinx) * (cosx + sinx);

&lt;span class="gi"&gt;+        // If n is odd, get the last element.&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT k0 = n &amp;amp; 1 ? coeffs[--index] : 0;
&lt;span class="w"&gt; &lt;/span&gt;        CT k1 = 0;

&lt;span class="w"&gt; &lt;/span&gt;        // Make n even.
&lt;span class="w"&gt; &lt;/span&gt;        n /= 2;
&lt;span class="w"&gt; &lt;/span&gt;        while (n--) {
&lt;span class="gd"&gt;-          // Unroll loop x 2, so accumulators return to their original role.&lt;/span&gt;
&lt;span class="gd"&gt;-          k1 = ar * k0 - k1 + coeffs[--index];&lt;/span&gt;
&lt;span class="gd"&gt;-          k0 = ar * k1 - k0 + coeffs[--index];&lt;/span&gt;
&lt;span class="gi"&gt;+            // Unroll loop x 2, so accumulators return to their original role.&lt;/span&gt;
&lt;span class="gi"&gt;+            k1 = ar * k0 - k1 + coeffs[--index];&lt;/span&gt;
&lt;span class="gi"&gt;+            k0 = ar * k1 - k0 + coeffs[--index];&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        }

&lt;span class="w"&gt; &lt;/span&gt;        return 2 * sinx * cosx * k0;

commit 06eb057fa99c99587b549e0d507e6f9765e169a9
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Mon Jun 25 18:44:37 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [formulas] Use assignment operator on the same line for consistency

&lt;span class="w"&gt; &lt;/span&gt;   Other changes include the update of series expansion function
&lt;span class="w"&gt; &lt;/span&gt;   calls, as the template arguments are reversed.

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/formulas/karney_direct.hpp b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 7a4f623..891def0 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -117,10 +117,8 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        cos_beta1 = std::max(sqrt(std::numeric_limits&amp;lt;CT&amp;gt;::min()), cos_beta1);

&lt;span class="w"&gt; &lt;/span&gt;        // Obtain alpha 0 by solving the spherical triangle.
&lt;span class="gd"&gt;-        CT const sin_alpha0&lt;/span&gt;
&lt;span class="gd"&gt;-            = sin_alpha1 * cos_beta1;&lt;/span&gt;
&lt;span class="gd"&gt;-        CT const cos_alpha0&lt;/span&gt;
&lt;span class="gd"&gt;-            = boost::math::hypot(cos_alpha1, sin_alpha1 * sin_beta1);&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const sin_alpha0 = sin_alpha1 * cos_beta1;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const cos_alpha0 = boost::math::hypot(cos_alpha1, sin_alpha1 * sin_beta1);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        CT const k2 = math::sqr(cos_alpha0) * ep2;

&lt;span class="gu"&gt;@@ -128,11 +126,10 @@ public:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        // Find the coefficients for A1 by computing the
&lt;span class="w"&gt; &lt;/span&gt;        // series expansion using Horner scehme.
&lt;span class="gd"&gt;-        CT const expansion_A1&lt;/span&gt;
&lt;span class="gd"&gt;-            = se::evaluate_series_A1&amp;lt;CT, SeriesOrder&amp;gt;(epsilon);&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const expansion_A1 = se::evaluate_A1&amp;lt;SeriesOrder&amp;gt;(epsilon);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        // Index zero element of coeffs_C1 is unused.
&lt;span class="gd"&gt;-        se::coeffs_C1&amp;lt;CT, SeriesOrder&amp;gt; const coeffs_C1(epsilon);&lt;/span&gt;
&lt;span class="gi"&gt;+        se::coeffs_C1&amp;lt;SeriesOrder, CT&amp;gt; const coeffs_C1(epsilon);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        // Tau is an integration variable.
&lt;span class="w"&gt; &lt;/span&gt;        CT const tau12 = distance / (b * (c1 + expansion_A1));
&lt;span class="gu"&gt;@@ -147,33 +144,27 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        cos_sigma1 = cos_omega1 = sin_beta1 != c0 || cos_alpha1 != c0 ? cos_beta1 * cos_alpha1 : c1;
&lt;span class="w"&gt; &lt;/span&gt;        math::normalize_values&amp;lt;CT&amp;gt;(sin_sigma1, cos_sigma1);

&lt;span class="gd"&gt;-        CT const B11 =&lt;/span&gt;
&lt;span class="gd"&gt;-            se::sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C1);&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const B11 = se::sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C1);&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT const sin_B11 = sin(B11);
&lt;span class="w"&gt; &lt;/span&gt;        CT const cos_B11 = cos(B11);

&lt;span class="gd"&gt;-        CT const sin_tau1&lt;/span&gt;
&lt;span class="gd"&gt;-            = sin_sigma1 * cos_B11 + cos_sigma1 * sin_B11;&lt;/span&gt;
&lt;span class="gd"&gt;-        CT const cos_tau1&lt;/span&gt;
&lt;span class="gd"&gt;-            = cos_sigma1 * cos_B11 - sin_sigma1 * sin_B11;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const sin_tau1 = sin_sigma1 * cos_B11 + cos_sigma1 * sin_B11;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const cos_tau1 = cos_sigma1 * cos_B11 - sin_sigma1 * sin_B11;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        // Index zero element of coeffs_C1p is unused.
&lt;span class="gd"&gt;-        se::coeffs_C1p&amp;lt;CT, SeriesOrder&amp;gt; const coeffs_C1p(epsilon);&lt;/span&gt;
&lt;span class="gi"&gt;+        se::coeffs_C1p&amp;lt;SeriesOrder, CT&amp;gt; const coeffs_C1p(epsilon);&lt;/span&gt;

&lt;span class="gd"&gt;-        CT const B12 =&lt;/span&gt;
&lt;span class="gd"&gt;-            - se::sin_cos_series&lt;/span&gt;
&lt;span class="gd"&gt;-                                (sin_tau1 * cos_tau12 + cos_tau1 * sin_tau12,&lt;/span&gt;
&lt;span class="gd"&gt;-                                 cos_tau1 * cos_tau12 - sin_tau1 * sin_tau12,&lt;/span&gt;
&lt;span class="gd"&gt;-                                 coeffs_C1p);&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const B12 = - se::sin_cos_series&lt;/span&gt;
&lt;span class="gi"&gt;+                             (sin_tau1 * cos_tau12 + cos_tau1 * sin_tau12,&lt;/span&gt;
&lt;span class="gi"&gt;+                              cos_tau1 * cos_tau12 - sin_tau1 * sin_tau12,&lt;/span&gt;
&lt;span class="gi"&gt;+                              coeffs_C1p);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        CT const sigma12 = tau12 - (B12 - B11);
&lt;span class="w"&gt; &lt;/span&gt;        CT const sin_sigma12 = sin(sigma12);
&lt;span class="w"&gt; &lt;/span&gt;        CT const cos_sigma12 = cos(sigma12);

&lt;span class="gd"&gt;-        CT const sin_sigma2&lt;/span&gt;
&lt;span class="gd"&gt;-            = sin_sigma1 * cos_sigma12 + cos_sigma1 * sin_sigma12;&lt;/span&gt;
&lt;span class="gd"&gt;-        CT const cos_sigma2&lt;/span&gt;
&lt;span class="gd"&gt;-            = cos_sigma1 * cos_sigma12 - sin_sigma1 * sin_sigma12;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const sin_sigma2 = sin_sigma1 * cos_sigma12 + cos_sigma1 * sin_sigma12;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const cos_sigma2 = cos_sigma1 * cos_sigma12 - sin_sigma1 * sin_sigma12;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        if (BOOST_GEOMETRY_CONDITION(CalcRevAzimuth))
&lt;span class="w"&gt; &lt;/span&gt;        {
&lt;span class="gu"&gt;@@ -183,21 +174,19 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            result.reverse_azimuth = atan2(sin_alpha2, cos_alpha2);

&lt;span class="w"&gt; &lt;/span&gt;            // Convert the angle to radians.
&lt;span class="gd"&gt;-            result.reverse_azimuth /= math::d2r&amp;lt;T&amp;gt;();&lt;/span&gt;
&lt;span class="gi"&gt;+            result.reverse_azimuth /= math::d2r&amp;lt;CT&amp;gt;();&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        }

&lt;span class="w"&gt; &lt;/span&gt;        if (BOOST_GEOMETRY_CONDITION(CalcCoordinates))
&lt;span class="w"&gt; &lt;/span&gt;        {
&lt;span class="w"&gt; &lt;/span&gt;            // Find the latitude at the second point.
&lt;span class="gd"&gt;-            CT const sin_beta2&lt;/span&gt;
&lt;span class="gd"&gt;-                = cos_alpha0 * sin_sigma2;&lt;/span&gt;
&lt;span class="gd"&gt;-            CT const cos_beta2&lt;/span&gt;
&lt;span class="gd"&gt;-                = boost::math::hypot(sin_alpha0, cos_alpha0 * cos_sigma2);&lt;/span&gt;
&lt;span class="gi"&gt;+            CT const sin_beta2 = cos_alpha0 * sin_sigma2;&lt;/span&gt;
&lt;span class="gi"&gt;+            CT const cos_beta2 = boost::math::hypot(sin_alpha0, cos_alpha0 * cos_sigma2);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            result.lat2 = atan2(sin_beta2, one_minus_f * cos_beta2);

&lt;span class="w"&gt; &lt;/span&gt;            // Convert the coordinate to radians.
&lt;span class="gd"&gt;-            result.lat2 /= math::d2r&amp;lt;T&amp;gt;();&lt;/span&gt;
&lt;span class="gi"&gt;+            result.lat2 /= math::d2r&amp;lt;CT&amp;gt;();&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            // Find the longitude at the second point.
&lt;span class="w"&gt; &lt;/span&gt;            CT const sin_omega2 = sin_alpha0 * sin_sigma2;
&lt;span class="gu"&gt;@@ -206,29 +195,27 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            CT const omega12 = atan2(sin_omega2 * cos_omega1 - cos_omega2 * sin_omega1,
&lt;span class="w"&gt; &lt;/span&gt;                                     cos_omega2 * cos_omega1 + sin_omega2 * sin_omega1);

&lt;span class="gd"&gt;-            se::coeffs_A3&amp;lt;CT, SeriesOrder&amp;gt; coeffs_A3(n);&lt;/span&gt;
&lt;span class="gi"&gt;+            se::coeffs_A3&amp;lt;SeriesOrder, CT&amp;gt; const coeffs_A3(n);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            CT const A3 = math::horner_evaluate(epsilon, coeffs_A3.begin(), coeffs_A3.end());
&lt;span class="w"&gt; &lt;/span&gt;            CT const A3c = -f * sin_alpha0 * A3;

&lt;span class="gd"&gt;-            se::coeffs_C3&amp;lt;CT, SeriesOrder&amp;gt; coeffs_C3(n, epsilon);&lt;/span&gt;
&lt;span class="gi"&gt;+            se::coeffs_C3&amp;lt;SeriesOrder, CT&amp;gt; const coeffs_C3(n, epsilon);&lt;/span&gt;

&lt;span class="gd"&gt;-            CT const B31 =&lt;/span&gt;
&lt;span class="gd"&gt;-                se::sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C3);&lt;/span&gt;
&lt;span class="gi"&gt;+            CT const B31 = se::sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C3);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            CT const lam12 = omega12 + A3c *
&lt;span class="w"&gt; &lt;/span&gt;                             (sigma12 + (se::sin_cos_series
&lt;span class="gd"&gt;-                                                           (sin_sigma2,&lt;/span&gt;
&lt;span class="gd"&gt;-                                                            cos_sigma2,&lt;/span&gt;
&lt;span class="gd"&gt;-                                                            coeffs_C3) - B31));&lt;/span&gt;
&lt;span class="gi"&gt;+                                             (sin_sigma2,&lt;/span&gt;
&lt;span class="gi"&gt;+                                              cos_sigma2,&lt;/span&gt;
&lt;span class="gi"&gt;+                                              coeffs_C3) - B31));&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            // Convert to radians to get the
&lt;span class="w"&gt; &lt;/span&gt;            // longitudinal difference.
&lt;span class="gd"&gt;-            CT lon12 = lam12 / math::d2r&amp;lt;T&amp;gt;();&lt;/span&gt;
&lt;span class="gi"&gt;+            CT lon12 = lam12 / math::d2r&amp;lt;CT&amp;gt;();&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            // Add the longitude at first point to the longitudinal
&lt;span class="w"&gt; &lt;/span&gt;            // difference and normalize the result.
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            math::normalize_angle&amp;lt;degree, CT&amp;gt;(lon1);
&lt;span class="w"&gt; &lt;/span&gt;            math::normalize_angle&amp;lt;degree, CT&amp;gt;(lon12);

&lt;span class="gu"&gt;@@ -239,17 +226,14 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        {
&lt;span class="w"&gt; &lt;/span&gt;            // Evaluate the coefficients for C2.
&lt;span class="w"&gt; &lt;/span&gt;            // Index zero element of coeffs_C2 is unused.
&lt;span class="gd"&gt;-            se::coeffs_C2&amp;lt;CT, SeriesOrder&amp;gt; coeffs_C2(epsilon);&lt;/span&gt;
&lt;span class="gi"&gt;+            se::coeffs_C2&amp;lt;SeriesOrder, CT&amp;gt; const coeffs_C2(epsilon);&lt;/span&gt;

&lt;span class="gd"&gt;-            CT const B21 =&lt;/span&gt;
&lt;span class="gd"&gt;-                se::sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C2);&lt;/span&gt;
&lt;span class="gd"&gt;-            CT const B22 =&lt;/span&gt;
&lt;span class="gd"&gt;-                se::sin_cos_series(sin_sigma2, cos_sigma2, coeffs_C2);&lt;/span&gt;
&lt;span class="gi"&gt;+            CT const B21 = se::sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C2);&lt;/span&gt;
&lt;span class="gi"&gt;+            CT const B22 = se::sin_cos_series(sin_sigma2, cos_sigma2, coeffs_C2);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            // Find the coefficients for A2 by computing the
&lt;span class="w"&gt; &lt;/span&gt;            // series expansion using Horner scehme.
&lt;span class="gd"&gt;-            CT const expansion_A2&lt;/span&gt;
&lt;span class="gd"&gt;-                = se::evaluate_series_A2&amp;lt;CT, SeriesOrder&amp;gt;(epsilon);&lt;/span&gt;
&lt;span class="gi"&gt;+            CT const expansion_A2 = se::evaluate_A2&amp;lt;SeriesOrder&amp;gt;(epsilon);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            CT const AB1 = (c1 + expansion_A1) * (B12 - B11);
&lt;span class="w"&gt; &lt;/span&gt;            CT const AB2 = (c1 + expansion_A2) * (B22 - B21);

commit 19f06fa04ec36a3388ab9ae8e5ccffb86d4776a6
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Mon Jun 25 18:39:43 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [util] Reverse template argument order for series expansion functions

&lt;span class="w"&gt; &lt;/span&gt;   In some cases, this allows the caller to ignore the CT template
&lt;span class="w"&gt; &lt;/span&gt;   argument, as it is deduced from the argument list.

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/util/series_expansion.hpp b/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index f9a063c..31decf6 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -32,7 +32,7 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;     The coefficient containers for the series expansions.
&lt;span class="w"&gt; &lt;/span&gt;     These structs allow the caller to only know the series order.
&lt;span class="w"&gt; &lt;/span&gt;    */
&lt;span class="gd"&gt;-    template &amp;lt;typename CT, size_t SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    template &amp;lt;size_t SeriesOrder, typename CT&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    struct coeffs_C1 : boost::array&amp;lt;CT, SeriesOrder + 1&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;    {
&lt;span class="w"&gt; &lt;/span&gt;        coeffs_C1(CT const&amp;amp; epsilon)
&lt;span class="gu"&gt;@@ -41,7 +41,7 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="w"&gt; &lt;/span&gt;    };

&lt;span class="gd"&gt;-    template &amp;lt;typename CT, size_t SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    template &amp;lt;size_t SeriesOrder, typename CT&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    struct coeffs_C1p : boost::array&amp;lt;CT, SeriesOrder + 1&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;    {
&lt;span class="w"&gt; &lt;/span&gt;        coeffs_C1p(CT const&amp;amp; epsilon)
&lt;span class="gu"&gt;@@ -50,7 +50,7 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="w"&gt; &lt;/span&gt;    };

&lt;span class="gd"&gt;-    template &amp;lt;typename CT, size_t SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    template &amp;lt;size_t SeriesOrder, typename CT&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    struct coeffs_C2 : boost::array&amp;lt;CT, SeriesOrder + 1&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;    {
&lt;span class="w"&gt; &lt;/span&gt;        coeffs_C2(CT const&amp;amp; epsilon)
&lt;span class="gu"&gt;@@ -59,7 +59,7 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="w"&gt; &lt;/span&gt;    };

&lt;span class="gd"&gt;-    template &amp;lt;typename CT, size_t SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    template &amp;lt;size_t SeriesOrder, typename CT&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    struct coeffs_C3x : boost::array&amp;lt;CT, (SeriesOrder * (SeriesOrder - 1)) / 2&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;    {
&lt;span class="w"&gt; &lt;/span&gt;        coeffs_C3x(CT const&amp;amp; n)
&lt;span class="gu"&gt;@@ -68,18 +68,18 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="w"&gt; &lt;/span&gt;    };

&lt;span class="gd"&gt;-    template &amp;lt;typename T, size_t SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gd"&gt;-    struct coeffs_C3 : boost::array&amp;lt;T, SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    template &amp;lt;size_t SeriesOrder, typename CT&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    struct coeffs_C3 : boost::array&amp;lt;CT, SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    {
&lt;span class="gd"&gt;-        coeffs_C3(T const&amp;amp; n, T const&amp;amp; epsilon)&lt;/span&gt;
&lt;span class="gi"&gt;+        coeffs_C3(CT const&amp;amp; n, CT const&amp;amp; epsilon)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        {
&lt;span class="gd"&gt;-            coeffs_C3x&amp;lt;T, SeriesOrder&amp;gt; coeffs_C3x(n);&lt;/span&gt;
&lt;span class="gi"&gt;+            coeffs_C3x&amp;lt;SeriesOrder, CT&amp;gt; coeffs_C3x(n);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            evaluate_coeffs_C3(*this, coeffs_C3x, epsilon);
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="w"&gt; &lt;/span&gt;    };

&lt;span class="gd"&gt;-    template &amp;lt;typename CT, size_t SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    template &amp;lt;size_t SeriesOrder, typename CT&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    struct coeffs_A3 : boost::array&amp;lt;CT, SeriesOrder&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;    {
&lt;span class="w"&gt; &lt;/span&gt;        coeffs_A3(CT const&amp;amp; n)
&lt;span class="gu"&gt;@@ -104,7 +104,7 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;     The scale factor A1-1 = mean value of (d/dsigma)I1 - 1

&lt;span class="w"&gt; &lt;/span&gt;     The expansion above is performed in Maxima, a Computer Algebra System.
&lt;span class="gd"&gt;-     The C++ code (that yields the function evaluate_series_A1 below) is&lt;/span&gt;
&lt;span class="gi"&gt;+     The C++ code (that yields the function evaluate_A1 below) is&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;     generated by the following Maxima script:
&lt;span class="w"&gt; &lt;/span&gt;     geometry/doc/other/maxima/geod.mac

&lt;span class="gu"&gt;@@ -113,8 +113,8 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;       sed -e &amp;#39;s/[0-9]\+/CT(&amp;amp;)/g; s/\[CT/\[/g; s/)\]/\]/g;
&lt;span class="w"&gt; &lt;/span&gt;               s/case\sCT(/case /g; s/):/:/g; s/epsCT(2)/eps2/g;&amp;#39;
&lt;span class="w"&gt; &lt;/span&gt;    */
&lt;span class="gd"&gt;-    template &amp;lt;typename CT, size_t SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gd"&gt;-    static inline CT evaluate_series_A1(CT eps)&lt;/span&gt;
&lt;span class="gi"&gt;+    template &amp;lt;size_t SeriesOrder, typename CT&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    static inline CT evaluate_A1(CT eps)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    {
&lt;span class="w"&gt; &lt;/span&gt;        CT eps2 = math::sqr(eps);
&lt;span class="w"&gt; &lt;/span&gt;        CT t;
&lt;span class="gu"&gt;@@ -154,12 +154,12 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;     The scale factor A2-1 = mean value of (d/dsigma)2 - 1

&lt;span class="w"&gt; &lt;/span&gt;     The expansion above is performed in Maxima, a Computer Algebra System.
&lt;span class="gd"&gt;-     The C++ code (that yields the function evaluate_series_A2 below) is&lt;/span&gt;
&lt;span class="gi"&gt;+     The C++ code (that yields the function evaluate_A2 below) is&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;     generated by the following Maxima script:
&lt;span class="w"&gt; &lt;/span&gt;     geometry/doc/other/maxima/geod.mac
&lt;span class="w"&gt; &lt;/span&gt;    */
&lt;span class="gd"&gt;-    template &amp;lt;typename CT, size_t SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gd"&gt;-    CT evaluate_series_A2(CT const&amp;amp; eps)&lt;/span&gt;
&lt;span class="gi"&gt;+    template &amp;lt;size_t SeriesOrder, typename CT&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    inline CT evaluate_A2(CT const&amp;amp; eps)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    {
&lt;span class="w"&gt; &lt;/span&gt;        CT const eps2 = math::sqr(eps);
&lt;span class="w"&gt; &lt;/span&gt;        CT t;
&lt;span class="gu"&gt;@@ -363,7 +363,6 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="w"&gt; &lt;/span&gt;    }

&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    /*
&lt;span class="w"&gt; &lt;/span&gt;     The coefficients C1p[l] in the Fourier expansion of B1p.


commit cc7e9e05c15410035dafc4d8daabc4efe3e25f5c
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Mon Jun 25 17:59:32 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [formulas] Use namespace alias se for series_expansion

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/formulas/karney_direct.hpp b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index df9aaab..7a4f623 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -41,6 +41,7 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;namespace boost { namespace geometry { namespace formula
&lt;span class="w"&gt; &lt;/span&gt;{

&lt;span class="gi"&gt;+namespace se = series_expansion;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;/*!
&lt;span class="w"&gt; &lt;/span&gt;\brief The solution of the direct problem of geodesics on latlong coordinates,
&lt;span class="gu"&gt;@@ -128,10 +129,10 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        // Find the coefficients for A1 by computing the
&lt;span class="w"&gt; &lt;/span&gt;        // series expansion using Horner scehme.
&lt;span class="w"&gt; &lt;/span&gt;        CT const expansion_A1
&lt;span class="gd"&gt;-            = series_expansion::evaluate_series_A1&amp;lt;CT, SeriesOrder&amp;gt;(epsilon);&lt;/span&gt;
&lt;span class="gi"&gt;+            = se::evaluate_series_A1&amp;lt;CT, SeriesOrder&amp;gt;(epsilon);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        // Index zero element of coeffs_C1 is unused.
&lt;span class="gd"&gt;-        series_expansion::coeffs_C1&amp;lt;CT, SeriesOrder&amp;gt; const coeffs_C1(epsilon);&lt;/span&gt;
&lt;span class="gi"&gt;+        se::coeffs_C1&amp;lt;CT, SeriesOrder&amp;gt; const coeffs_C1(epsilon);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        // Tau is an integration variable.
&lt;span class="w"&gt; &lt;/span&gt;        CT const tau12 = distance / (b * (c1 + expansion_A1));
&lt;span class="gu"&gt;@@ -147,7 +148,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        math::normalize_values&amp;lt;CT&amp;gt;(sin_sigma1, cos_sigma1);

&lt;span class="w"&gt; &lt;/span&gt;        CT const B11 =
&lt;span class="gd"&gt;-            series_expansion::sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C1);&lt;/span&gt;
&lt;span class="gi"&gt;+            se::sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C1);&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT const sin_B11 = sin(B11);
&lt;span class="w"&gt; &lt;/span&gt;        CT const cos_B11 = cos(B11);

&lt;span class="gu"&gt;@@ -157,10 +158,10 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            = cos_sigma1 * cos_B11 - sin_sigma1 * sin_B11;

&lt;span class="w"&gt; &lt;/span&gt;        // Index zero element of coeffs_C1p is unused.
&lt;span class="gd"&gt;-        series_expansion::coeffs_C1p&amp;lt;CT, SeriesOrder&amp;gt; const coeffs_C1p(epsilon);&lt;/span&gt;
&lt;span class="gi"&gt;+        se::coeffs_C1p&amp;lt;CT, SeriesOrder&amp;gt; const coeffs_C1p(epsilon);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        CT const B12 =
&lt;span class="gd"&gt;-            - series_expansion::sin_cos_series&lt;/span&gt;
&lt;span class="gi"&gt;+            - se::sin_cos_series&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                                (sin_tau1 * cos_tau12 + cos_tau1 * sin_tau12,
&lt;span class="w"&gt; &lt;/span&gt;                                 cos_tau1 * cos_tau12 - sin_tau1 * sin_tau12,
&lt;span class="w"&gt; &lt;/span&gt;                                 coeffs_C1p);
&lt;span class="gu"&gt;@@ -205,18 +206,18 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            CT const omega12 = atan2(sin_omega2 * cos_omega1 - cos_omega2 * sin_omega1,
&lt;span class="w"&gt; &lt;/span&gt;                                     cos_omega2 * cos_omega1 + sin_omega2 * sin_omega1);

&lt;span class="gd"&gt;-            series_expansion::coeffs_A3&amp;lt;CT, SeriesOrder&amp;gt; coeffs_A3(n);&lt;/span&gt;
&lt;span class="gi"&gt;+            se::coeffs_A3&amp;lt;CT, SeriesOrder&amp;gt; coeffs_A3(n);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            CT const A3 = math::horner_evaluate(epsilon, coeffs_A3.begin(), coeffs_A3.end());
&lt;span class="w"&gt; &lt;/span&gt;            CT const A3c = -f * sin_alpha0 * A3;

&lt;span class="gd"&gt;-            series_expansion::coeffs_C3&amp;lt;CT, SeriesOrder&amp;gt; coeffs_C3(n, epsilon);&lt;/span&gt;
&lt;span class="gi"&gt;+            se::coeffs_C3&amp;lt;CT, SeriesOrder&amp;gt; coeffs_C3(n, epsilon);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            CT const B31 =
&lt;span class="gd"&gt;-                series_expansion::sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C3);&lt;/span&gt;
&lt;span class="gi"&gt;+                se::sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C3);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            CT const lam12 = omega12 + A3c *
&lt;span class="gd"&gt;-                             (sigma12 + (series_expansion::sin_cos_series&lt;/span&gt;
&lt;span class="gi"&gt;+                             (sigma12 + (se::sin_cos_series&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                                                           (sin_sigma2,
&lt;span class="w"&gt; &lt;/span&gt;                                                            cos_sigma2,
&lt;span class="w"&gt; &lt;/span&gt;                                                            coeffs_C3) - B31));
&lt;span class="gu"&gt;@@ -238,17 +239,17 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        {
&lt;span class="w"&gt; &lt;/span&gt;            // Evaluate the coefficients for C2.
&lt;span class="w"&gt; &lt;/span&gt;            // Index zero element of coeffs_C2 is unused.
&lt;span class="gd"&gt;-            series_expansion::coeffs_C2&amp;lt;CT, SeriesOrder&amp;gt; coeffs_C2(epsilon);&lt;/span&gt;
&lt;span class="gi"&gt;+            se::coeffs_C2&amp;lt;CT, SeriesOrder&amp;gt; coeffs_C2(epsilon);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            CT const B21 =
&lt;span class="gd"&gt;-                series_expansion::sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C2);&lt;/span&gt;
&lt;span class="gi"&gt;+                se::sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C2);&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            CT const B22 =
&lt;span class="gd"&gt;-                series_expansion::sin_cos_series(sin_sigma2, cos_sigma2, coeffs_C2);&lt;/span&gt;
&lt;span class="gi"&gt;+                se::sin_cos_series(sin_sigma2, cos_sigma2, coeffs_C2);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            // Find the coefficients for A2 by computing the
&lt;span class="w"&gt; &lt;/span&gt;            // series expansion using Horner scehme.
&lt;span class="w"&gt; &lt;/span&gt;            CT const expansion_A2
&lt;span class="gd"&gt;-                = series_expansion::evaluate_series_A2&amp;lt;CT, SeriesOrder&amp;gt;(epsilon);&lt;/span&gt;
&lt;span class="gi"&gt;+                = se::evaluate_series_A2&amp;lt;CT, SeriesOrder&amp;gt;(epsilon);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            CT const AB1 = (c1 + expansion_A1) * (B12 - B11);
&lt;span class="w"&gt; &lt;/span&gt;            CT const AB2 = (c1 + expansion_A2) * (B22 - B21);

commit dedccdbdaeaddc2fb0d27568bbe2dc00b74da316
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Mon Jun 25 17:19:44 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [formaulas][util] Define coefficient containers for computing series expansions

&lt;span class="w"&gt; &lt;/span&gt;   The coefficient containers are defined as structs in
&lt;span class="w"&gt; &lt;/span&gt;   series_expansion.hpp file. They allow the caller to
&lt;span class="w"&gt; &lt;/span&gt;   compute expansions without specifying the size
&lt;span class="w"&gt; &lt;/span&gt;   for the output array.

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/formulas/karney_direct.hpp b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index c844f97..df9aaab 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -131,8 +131,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            = series_expansion::evaluate_series_A1&amp;lt;CT, SeriesOrder&amp;gt;(epsilon);

&lt;span class="w"&gt; &lt;/span&gt;        // Index zero element of coeffs_C1 is unused.
&lt;span class="gd"&gt;-        boost::array&amp;lt;CT, SeriesOrder + 1&amp;gt; coeffs_C1;&lt;/span&gt;
&lt;span class="gd"&gt;-        series_expansion::evaluate_coeffs_C1(epsilon, coeffs_C1);&lt;/span&gt;
&lt;span class="gi"&gt;+        series_expansion::coeffs_C1&amp;lt;CT, SeriesOrder&amp;gt; const coeffs_C1(epsilon);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        // Tau is an integration variable.
&lt;span class="w"&gt; &lt;/span&gt;        CT const tau12 = distance / (b * (c1 + expansion_A1));
&lt;span class="gu"&gt;@@ -148,7 +147,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        math::normalize_values&amp;lt;CT&amp;gt;(sin_sigma1, cos_sigma1);

&lt;span class="w"&gt; &lt;/span&gt;        CT const B11 =
&lt;span class="gd"&gt;-            series_expansion::sin_cos_series&amp;lt;CT, SeriesOrder + 1&amp;gt;(sin_sigma1, cos_sigma1, coeffs_C1);&lt;/span&gt;
&lt;span class="gi"&gt;+            series_expansion::sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C1);&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT const sin_B11 = sin(B11);
&lt;span class="w"&gt; &lt;/span&gt;        CT const cos_B11 = cos(B11);

&lt;span class="gu"&gt;@@ -158,11 +157,10 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            = cos_sigma1 * cos_B11 - sin_sigma1 * sin_B11;

&lt;span class="w"&gt; &lt;/span&gt;        // Index zero element of coeffs_C1p is unused.
&lt;span class="gd"&gt;-        boost::array&amp;lt;CT, SeriesOrder + 1&amp;gt; coeffs_C1p;&lt;/span&gt;
&lt;span class="gd"&gt;-        series_expansion::evaluate_coeffs_C1p(epsilon, coeffs_C1p);&lt;/span&gt;
&lt;span class="gi"&gt;+        series_expansion::coeffs_C1p&amp;lt;CT, SeriesOrder&amp;gt; const coeffs_C1p(epsilon);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        CT const B12 =
&lt;span class="gd"&gt;-            - series_expansion::sin_cos_series&amp;lt;CT, SeriesOrder + 1&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+            - series_expansion::sin_cos_series&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                                (sin_tau1 * cos_tau12 + cos_tau1 * sin_tau12,
&lt;span class="w"&gt; &lt;/span&gt;                                 cos_tau1 * cos_tau12 - sin_tau1 * sin_tau12,
&lt;span class="w"&gt; &lt;/span&gt;                                 coeffs_C1p);
&lt;span class="gu"&gt;@@ -207,26 +205,18 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            CT const omega12 = atan2(sin_omega2 * cos_omega1 - cos_omega2 * sin_omega1,
&lt;span class="w"&gt; &lt;/span&gt;                                     cos_omega2 * cos_omega1 + sin_omega2 * sin_omega1);

&lt;span class="gd"&gt;-            CT coeffs_A3[SeriesOrder];&lt;/span&gt;
&lt;span class="gd"&gt;-            series_expansion::evaluate_coeffs_A3&amp;lt;CT, SeriesOrder&amp;gt;(n, coeffs_A3);&lt;/span&gt;
&lt;span class="gi"&gt;+            series_expansion::coeffs_A3&amp;lt;CT, SeriesOrder&amp;gt; coeffs_A3(n);&lt;/span&gt;

&lt;span class="gd"&gt;-            CT const A3 = math::horner_evaluate(epsilon, coeffs_A3, coeffs_A3 + SeriesOrder);&lt;/span&gt;
&lt;span class="gi"&gt;+            CT const A3 = math::horner_evaluate(epsilon, coeffs_A3.begin(), coeffs_A3.end());&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            CT const A3c = -f * sin_alpha0 * A3;

&lt;span class="gd"&gt;-            // Compute the size of coefficient array.&lt;/span&gt;
&lt;span class="gd"&gt;-            size_t const coeffs_C3_size = (SeriesOrder * (SeriesOrder - 1)) / 2;&lt;/span&gt;
&lt;span class="gd"&gt;-            CT coeffs_C3x[coeffs_C3_size];&lt;/span&gt;
&lt;span class="gd"&gt;-            series_expansion::evaluate_coeffs_C3x&amp;lt;CT, SeriesOrder&amp;gt;(n, coeffs_C3x);&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-            // Evaluate C3 coefficients.&lt;/span&gt;
&lt;span class="gd"&gt;-            CT coeffs_C3[SeriesOrder];&lt;/span&gt;
&lt;span class="gd"&gt;-            series_expansion::evaluate_coeffs_C3&amp;lt;CT, SeriesOrder&amp;gt;(epsilon, coeffs_C3, coeffs_C3x);&lt;/span&gt;
&lt;span class="gi"&gt;+            series_expansion::coeffs_C3&amp;lt;CT, SeriesOrder&amp;gt; coeffs_C3(n, epsilon);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            CT const B31 =
&lt;span class="gd"&gt;-                series_expansion::sin_cos_series&amp;lt;CT, SeriesOrder&amp;gt;(sin_sigma1, cos_sigma1, coeffs_C3);&lt;/span&gt;
&lt;span class="gi"&gt;+                series_expansion::sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C3);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            CT const lam12 = omega12 + A3c *
&lt;span class="gd"&gt;-                             (sigma12 + (series_expansion::sin_cos_series&amp;lt;CT, SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+                             (sigma12 + (series_expansion::sin_cos_series&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                                                           (sin_sigma2,
&lt;span class="w"&gt; &lt;/span&gt;                                                            cos_sigma2,
&lt;span class="w"&gt; &lt;/span&gt;                                                            coeffs_C3) - B31));
&lt;span class="gu"&gt;@@ -248,13 +238,12 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        {
&lt;span class="w"&gt; &lt;/span&gt;            // Evaluate the coefficients for C2.
&lt;span class="w"&gt; &lt;/span&gt;            // Index zero element of coeffs_C2 is unused.
&lt;span class="gd"&gt;-            boost::array&amp;lt;CT, SeriesOrder + 1&amp;gt; coeffs_C2;&lt;/span&gt;
&lt;span class="gd"&gt;-            series_expansion::evaluate_coeffs_C2(epsilon, coeffs_C2);&lt;/span&gt;
&lt;span class="gi"&gt;+            series_expansion::coeffs_C2&amp;lt;CT, SeriesOrder&amp;gt; coeffs_C2(epsilon);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            CT const B21 =
&lt;span class="gd"&gt;-                series_expansion::sin_cos_series&amp;lt;CT, SeriesOrder + 1&amp;gt;(sin_sigma1, cos_sigma1, coeffs_C2);&lt;/span&gt;
&lt;span class="gi"&gt;+                series_expansion::sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C2);&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            CT const B22 =
&lt;span class="gd"&gt;-                series_expansion::sin_cos_series&amp;lt;CT, SeriesOrder + 1&amp;gt;(sin_sigma2, cos_sigma2, coeffs_C2);&lt;/span&gt;
&lt;span class="gi"&gt;+                series_expansion::sin_cos_series(sin_sigma2, cos_sigma2, coeffs_C2);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            // Find the coefficients for A2 by computing the
&lt;span class="w"&gt; &lt;/span&gt;            // series expansion using Horner scehme.
&lt;span class="gh"&gt;diff --git a/include/boost/geometry/util/series_expansion.hpp b/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 492393e..f9a063c 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -29,6 +29,66 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;namespace boost { namespace geometry { namespace series_expansion {

&lt;span class="w"&gt; &lt;/span&gt;    /*
&lt;span class="gi"&gt;+     The coefficient containers for the series expansions.&lt;/span&gt;
&lt;span class="gi"&gt;+     These structs allow the caller to only know the series order.&lt;/span&gt;
&lt;span class="gi"&gt;+    */&lt;/span&gt;
&lt;span class="gi"&gt;+    template &amp;lt;typename CT, size_t SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    struct coeffs_C1 : boost::array&amp;lt;CT, SeriesOrder + 1&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    {&lt;/span&gt;
&lt;span class="gi"&gt;+        coeffs_C1(CT const&amp;amp; epsilon)&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            evaluate_coeffs_C1(*this, epsilon);&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+    };&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    template &amp;lt;typename CT, size_t SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    struct coeffs_C1p : boost::array&amp;lt;CT, SeriesOrder + 1&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    {&lt;/span&gt;
&lt;span class="gi"&gt;+        coeffs_C1p(CT const&amp;amp; epsilon)&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            evaluate_coeffs_C1p(*this, epsilon);&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+    };&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    template &amp;lt;typename CT, size_t SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    struct coeffs_C2 : boost::array&amp;lt;CT, SeriesOrder + 1&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    {&lt;/span&gt;
&lt;span class="gi"&gt;+        coeffs_C2(CT const&amp;amp; epsilon)&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            evaluate_coeffs_C2(*this, epsilon);&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+    };&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    template &amp;lt;typename CT, size_t SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    struct coeffs_C3x : boost::array&amp;lt;CT, (SeriesOrder * (SeriesOrder - 1)) / 2&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    {&lt;/span&gt;
&lt;span class="gi"&gt;+        coeffs_C3x(CT const&amp;amp; n)&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            evaluate_coeffs_C3x(*this, SeriesOrder, n);&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+    };&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    template &amp;lt;typename T, size_t SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    struct coeffs_C3 : boost::array&amp;lt;T, SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    {&lt;/span&gt;
&lt;span class="gi"&gt;+        coeffs_C3(T const&amp;amp; n, T const&amp;amp; epsilon)&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            coeffs_C3x&amp;lt;T, SeriesOrder&amp;gt; coeffs_C3x(n);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            evaluate_coeffs_C3(*this, coeffs_C3x, epsilon);&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+    };&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    template &amp;lt;typename CT, size_t SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    struct coeffs_A3 : boost::array&amp;lt;CT, SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    {&lt;/span&gt;
&lt;span class="gi"&gt;+        coeffs_A3(CT const&amp;amp; n)&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            evaluate_coeffs_A3(*this, n);&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+    };&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    /*&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;     Generate and evaluate the series expansion of the following integral

&lt;span class="w"&gt; &lt;/span&gt;     I1 = integrate( sqrt(1+k2*sin(sigma1)^2), sigma1, 0, sigma )
&lt;span class="gu"&gt;@@ -53,7 +113,7 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;       sed -e &amp;#39;s/[0-9]\+/CT(&amp;amp;)/g; s/\[CT/\[/g; s/)\]/\]/g;
&lt;span class="w"&gt; &lt;/span&gt;               s/case\sCT(/case /g; s/):/:/g; s/epsCT(2)/eps2/g;&amp;#39;
&lt;span class="w"&gt; &lt;/span&gt;    */
&lt;span class="gd"&gt;-    template &amp;lt;typename CT, std::size_t SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    template &amp;lt;typename CT, size_t SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    static inline CT evaluate_series_A1(CT eps)
&lt;span class="w"&gt; &lt;/span&gt;    {
&lt;span class="w"&gt; &lt;/span&gt;        CT eps2 = math::sqr(eps);
&lt;span class="gu"&gt;@@ -98,7 +158,7 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;     generated by the following Maxima script:
&lt;span class="w"&gt; &lt;/span&gt;     geometry/doc/other/maxima/geod.mac
&lt;span class="w"&gt; &lt;/span&gt;    */
&lt;span class="gd"&gt;-    template &amp;lt;typename CT, std::size_t SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    template &amp;lt;typename CT, size_t SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    CT evaluate_series_A2(CT const&amp;amp; eps)
&lt;span class="w"&gt; &lt;/span&gt;    {
&lt;span class="w"&gt; &lt;/span&gt;        CT const eps2 = math::sqr(eps);
&lt;span class="gu"&gt;@@ -116,7 +176,7 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        case 3:
&lt;span class="w"&gt; &lt;/span&gt;            t = eps2*((-CT(11)*eps2-CT(28))*eps2-CT(192))/CT(256);
&lt;span class="w"&gt; &lt;/span&gt;            break;
&lt;span class="gd"&gt;-        case 4:&lt;/span&gt;
&lt;span class="gi"&gt;+        default:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            t = eps2*(eps2*((-CT(375)*eps2-CT(704))*eps2-CT(1792))-CT(12288))/CT(16384);
&lt;span class="w"&gt; &lt;/span&gt;            break;
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="gu"&gt;@@ -143,11 +203,10 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;     generated by the following Maxima script:
&lt;span class="w"&gt; &lt;/span&gt;     geometry/doc/other/maxima/geod.mac
&lt;span class="w"&gt; &lt;/span&gt;    */
&lt;span class="gd"&gt;-    // TODO: this produces different results that geographiclib&lt;/span&gt;
&lt;span class="gd"&gt;-    template &amp;lt;typename CT, std::size_t SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gd"&gt;-    static inline void evaluate_coeffs_A3(CT const&amp;amp; n, CT c[])&lt;/span&gt;
&lt;span class="gi"&gt;+    template &amp;lt;typename Coeffs, typename CT&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    static inline void evaluate_coeffs_A3(Coeffs &amp;amp;c, CT const&amp;amp; n)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    {
&lt;span class="gd"&gt;-        switch (SeriesOrder) {&lt;/span&gt;
&lt;span class="gi"&gt;+        switch (int(Coeffs::static_size)) {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        case 0:
&lt;span class="w"&gt; &lt;/span&gt;            break;
&lt;span class="w"&gt; &lt;/span&gt;        case 1:
&lt;span class="gu"&gt;@@ -192,7 +251,7 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            c[5] = (-CT(5)*n-CT(3))/CT(128);
&lt;span class="w"&gt; &lt;/span&gt;            c[6] = -CT(5)/CT(256);
&lt;span class="w"&gt; &lt;/span&gt;            break;
&lt;span class="gd"&gt;-        case 8:&lt;/span&gt;
&lt;span class="gi"&gt;+        default:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            c[0] = CT(1);
&lt;span class="w"&gt; &lt;/span&gt;            c[1] = (n-CT(1))/CT(2);
&lt;span class="w"&gt; &lt;/span&gt;            c[2] = (n*(CT(3)*n-CT(1))-CT(2))/CT(8);
&lt;span class="gu"&gt;@@ -213,12 +272,12 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;     generated by the following Maxima script:
&lt;span class="w"&gt; &lt;/span&gt;     geometry/doc/other/maxima/geod.mac
&lt;span class="w"&gt; &lt;/span&gt;    */
&lt;span class="gd"&gt;-    template &amp;lt;typename CT, std::size_t SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gd"&gt;-    static inline void evaluate_coeffs_C1(CT eps, boost::array&amp;lt;CT, SeriesOrder&amp;gt;&amp;amp; c)&lt;/span&gt;
&lt;span class="gi"&gt;+    template &amp;lt;typename Coeffs, typename CT&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    static inline void evaluate_coeffs_C1(Coeffs &amp;amp;c, CT const&amp;amp; eps)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    {
&lt;span class="w"&gt; &lt;/span&gt;        CT eps2 = math::sqr(eps);
&lt;span class="w"&gt; &lt;/span&gt;        CT d = eps;
&lt;span class="gd"&gt;-        switch (c.size() - 1) {&lt;/span&gt;
&lt;span class="gi"&gt;+        switch (int(Coeffs::static_size) - 1) {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        case 0:
&lt;span class="w"&gt; &lt;/span&gt;            break;
&lt;span class="w"&gt; &lt;/span&gt;        case 1:
&lt;span class="gu"&gt;@@ -284,7 +343,7 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            d *= eps;
&lt;span class="w"&gt; &lt;/span&gt;            c[7] = -CT(33)*d/CT(14336);
&lt;span class="w"&gt; &lt;/span&gt;            break;
&lt;span class="gd"&gt;-        case 8:&lt;/span&gt;
&lt;span class="gi"&gt;+        default:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            c[1] = d*(eps2*(eps2*(CT(19)*eps2-CT(64))+CT(384))-CT(1024))/CT(2048);
&lt;span class="w"&gt; &lt;/span&gt;            d *= eps;
&lt;span class="w"&gt; &lt;/span&gt;            c[2] = d*(eps2*(eps2*(CT(7)*eps2-CT(18))+CT(128))-CT(256))/CT(4096);
&lt;span class="gu"&gt;@@ -304,6 +363,7 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="w"&gt; &lt;/span&gt;    }

&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    /*
&lt;span class="w"&gt; &lt;/span&gt;     The coefficients C1p[l] in the Fourier expansion of B1p.

&lt;span class="gu"&gt;@@ -312,12 +372,12 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;     generated by the following Maxima script:
&lt;span class="w"&gt; &lt;/span&gt;     geometry/doc/other/maxima/geod.mac
&lt;span class="w"&gt; &lt;/span&gt;    */
&lt;span class="gd"&gt;-    template &amp;lt;typename CT, std::size_t SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gd"&gt;-    static inline void evaluate_coeffs_C1p(CT eps, boost::array&amp;lt;CT, SeriesOrder&amp;gt;&amp;amp; c)&lt;/span&gt;
&lt;span class="gi"&gt;+    template &amp;lt;typename Coeffs, typename CT&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    static inline void evaluate_coeffs_C1p(Coeffs&amp;amp; c, CT const&amp;amp; eps)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    {
&lt;span class="w"&gt; &lt;/span&gt;        CT const eps2 = math::sqr(eps);
&lt;span class="w"&gt; &lt;/span&gt;        CT d = eps;
&lt;span class="gd"&gt;-        switch (c.size() - 1) {&lt;/span&gt;
&lt;span class="gi"&gt;+        switch (int(Coeffs::static_size) - 1) {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        case 0:
&lt;span class="w"&gt; &lt;/span&gt;            break;
&lt;span class="w"&gt; &lt;/span&gt;        case 1:
&lt;span class="gu"&gt;@@ -383,7 +443,7 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            d *= eps;
&lt;span class="w"&gt; &lt;/span&gt;            c[7] = CT(459485)*d/CT(516096);
&lt;span class="w"&gt; &lt;/span&gt;            break;
&lt;span class="gd"&gt;-        case 8:&lt;/span&gt;
&lt;span class="gi"&gt;+        default:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            c[1] = d*(eps2*((CT(9840)-CT(4879)*eps2)*eps2-CT(20736))+CT(36864))/CT(73728);
&lt;span class="w"&gt; &lt;/span&gt;            d *= eps;
&lt;span class="w"&gt; &lt;/span&gt;            c[2] = d*(eps2*((CT(120150)-CT(86171)*eps2)*eps2-CT(142080))+CT(115200))/CT(368640);
&lt;span class="gu"&gt;@@ -411,12 +471,12 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;     generated by the following Maxima script:
&lt;span class="w"&gt; &lt;/span&gt;     geometry/doc/other/maxima/geod.mac
&lt;span class="w"&gt; &lt;/span&gt;    */
&lt;span class="gd"&gt;-    template &amp;lt;typename CT, std::size_t SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gd"&gt;-    static inline void evaluate_coeffs_C2(CT const&amp;amp; eps, boost::array&amp;lt;CT, SeriesOrder&amp;gt;&amp;amp; c)&lt;/span&gt;
&lt;span class="gi"&gt;+    template &amp;lt;typename Coeffs, typename CT&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    static inline void evaluate_coeffs_C2(Coeffs&amp;amp; c, CT const&amp;amp; eps)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    {
&lt;span class="w"&gt; &lt;/span&gt;        CT const eps2 = math::sqr(eps);
&lt;span class="w"&gt; &lt;/span&gt;        CT d = eps;
&lt;span class="gd"&gt;-        switch (c.size() - 1) {&lt;/span&gt;
&lt;span class="gi"&gt;+        switch (int(Coeffs::static_size) - 1) {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        case 0:
&lt;span class="w"&gt; &lt;/span&gt;            break;
&lt;span class="w"&gt; &lt;/span&gt;        case 1:
&lt;span class="gu"&gt;@@ -482,7 +542,7 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            d *= eps;
&lt;span class="w"&gt; &lt;/span&gt;            c[7] = CT(429)*d/CT(14336);
&lt;span class="w"&gt; &lt;/span&gt;            break;
&lt;span class="gd"&gt;-        case 8:&lt;/span&gt;
&lt;span class="gi"&gt;+        default:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            c[1] = d*(eps2*(eps2*(CT(41)*eps2+CT(64))+CT(128))+CT(1024))/CT(2048);
&lt;span class="w"&gt; &lt;/span&gt;            d *= eps;
&lt;span class="w"&gt; &lt;/span&gt;            c[2] = d*(eps2*(eps2*(CT(47)*eps2+CT(70))+CT(128))+CT(768))/CT(4096);
&lt;span class="gu"&gt;@@ -510,8 +570,8 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;     generated by the following Maxima script:
&lt;span class="w"&gt; &lt;/span&gt;     geometry/doc/other/maxima/geod.mac
&lt;span class="w"&gt; &lt;/span&gt;    */
&lt;span class="gd"&gt;-    template &amp;lt;typename CT, size_t SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gd"&gt;-    static inline void evaluate_coeffs_C3x(CT const&amp;amp; n, CT c[]) {&lt;/span&gt;
&lt;span class="gi"&gt;+    template &amp;lt;typename Coeffs, typename CT&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    static inline void evaluate_coeffs_C3x(Coeffs &amp;amp;c, size_t const&amp;amp; SeriesOrder, CT const&amp;amp; n) {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        const CT n2 = math::sqr(n);
&lt;span class="w"&gt; &lt;/span&gt;        switch (SeriesOrder) {
&lt;span class="w"&gt; &lt;/span&gt;        case 0:
&lt;span class="gu"&gt;@@ -586,7 +646,7 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            c[19] = (CT(9)-CT(15)*n)/CT(1024);
&lt;span class="w"&gt; &lt;/span&gt;            c[20] = (CT(44)-CT(99)*n)/CT(8192);
&lt;span class="w"&gt; &lt;/span&gt;            break;
&lt;span class="gd"&gt;-        case 8:&lt;/span&gt;
&lt;span class="gi"&gt;+        default:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            c[0] = (CT(1)-n)/CT(4);
&lt;span class="w"&gt; &lt;/span&gt;            c[1] = (CT(1)-n2)/CT(8);
&lt;span class="w"&gt; &lt;/span&gt;            c[2] = (n*((-CT(5)*n-CT(1))*n+CT(3))+CT(3))/CT(64);
&lt;span class="gu"&gt;@@ -625,19 +685,19 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;      Elements coeffs1[1] through coeffs1[SeriesOrder - 1] are set.
&lt;span class="w"&gt; &lt;/span&gt;    */
&lt;span class="gd"&gt;-    template &amp;lt;typename CT, size_t SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gd"&gt;-    static inline void evaluate_coeffs_C3(CT eps, CT coeffs1[], CT coeffs2[])&lt;/span&gt;
&lt;span class="gi"&gt;+    template &amp;lt;typename Coeffs1, typename Coeffs2, typename CT&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    static inline void evaluate_coeffs_C3(Coeffs1 &amp;amp;coeffs1, Coeffs2 &amp;amp;coeffs2, CT const&amp;amp; eps)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    {
&lt;span class="w"&gt; &lt;/span&gt;        CT mult = 1;
&lt;span class="w"&gt; &lt;/span&gt;        int offset = 0;

&lt;span class="w"&gt; &lt;/span&gt;        // l is the index of C3[l].
&lt;span class="gd"&gt;-        for (size_t l = 1; l &amp;lt; SeriesOrder; ++l)&lt;/span&gt;
&lt;span class="gi"&gt;+        for (size_t l = 1; l &amp;lt; Coeffs1::static_size; ++l)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        {
&lt;span class="w"&gt; &lt;/span&gt;            // Order of polynomial in eps.
&lt;span class="gd"&gt;-            int m = SeriesOrder - l - 1;&lt;/span&gt;
&lt;span class="gi"&gt;+            int m = Coeffs1::static_size - l - 1;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            mult *= eps;
&lt;span class="gd"&gt;-            coeffs1[l] = mult * math::polyval(m, coeffs2 + offset, eps);&lt;/span&gt;
&lt;span class="gi"&gt;+            coeffs1[l] = mult * math::polyval(m, coeffs2.begin() + offset, eps);&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            offset += m + 1;
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="w"&gt; &lt;/span&gt;        // Post condition: offset == coeffs_C3_size
&lt;span class="gu"&gt;@@ -650,33 +710,10 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;     using Clenshaw summation.
&lt;span class="w"&gt; &lt;/span&gt;    */
&lt;span class="gd"&gt;-    template &amp;lt;typename CT, size_t SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gd"&gt;-    static inline CT sin_cos_series(CT sinx, CT cosx, const CT coeffs[])&lt;/span&gt;
&lt;span class="gi"&gt;+    template &amp;lt;typename CT, typename Coeffs&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    static inline CT sin_cos_series(CT const&amp;amp; sinx, CT const&amp;amp; cosx, Coeffs const&amp;amp; coeffs)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    {
&lt;span class="gd"&gt;-        size_t n = SeriesOrder;&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        // Point to one beyond last element.&lt;/span&gt;
&lt;span class="gd"&gt;-        coeffs += (n + 1);&lt;/span&gt;
&lt;span class="gd"&gt;-        CT ar = 2 * (cosx - sinx) * (cosx + sinx);&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        CT k0 = n &amp;amp; 1 ? *--coeffs : 0;&lt;/span&gt;
&lt;span class="gd"&gt;-        CT k1 = 0;&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        // Make n even.&lt;/span&gt;
&lt;span class="gd"&gt;-        n /= 2;&lt;/span&gt;
&lt;span class="gd"&gt;-        while (n--) {&lt;/span&gt;
&lt;span class="gd"&gt;-          // Unroll loop x 2, so accumulators return to their original role.&lt;/span&gt;
&lt;span class="gd"&gt;-          k1 = ar * k0 - k1 + *--coeffs;&lt;/span&gt;
&lt;span class="gd"&gt;-          k0 = ar * k1 - k0 + *--coeffs;&lt;/span&gt;
&lt;span class="gd"&gt;-        }&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        return 2 * sinx * cosx * k0;&lt;/span&gt;
&lt;span class="gd"&gt;-    }&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    template &amp;lt;typename CT, size_t SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gd"&gt;-    static inline CT sin_cos_series(CT sinx, CT cosx, const boost::array&amp;lt;CT, SeriesOrder&amp;gt; coeffs)&lt;/span&gt;
&lt;span class="gd"&gt;-    {&lt;/span&gt;
&lt;span class="gd"&gt;-        size_t n = SeriesOrder - 1;&lt;/span&gt;
&lt;span class="gi"&gt;+        size_t n = Coeffs::static_size - 1;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        size_t index = 0;

&lt;span class="w"&gt; &lt;/span&gt;        // Point to one beyond last element.
&lt;span class="gu"&gt;@@ -697,7 +734,6 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        return 2 * sinx * cosx * k0;
&lt;span class="w"&gt; &lt;/span&gt;    }

&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;}}} // namespace boost::geometry::series_expansion

&lt;span class="w"&gt; &lt;/span&gt;#endif // BOOST_GEOMETRY_UTIL_SERIES_EXPANSION_HPP

commit 1972bcda3e5c204f2a0d0bda66239725828f1306
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Jun 22 19:26:27 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [formula][util] Pass boost::array to series expansion functions

&lt;span class="w"&gt; &lt;/span&gt;   Using boost::array we don&amp;#39;t have to explicitly provide
&lt;span class="w"&gt; &lt;/span&gt;   the template parameters.

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/formulas/karney_direct.hpp b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 327d9e1..c844f97 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -25,6 +25,8 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;#define BOOST_GEOMETRY_FORMULAS_KARNEY_DIRECT_HPP


&lt;span class="gi"&gt;+#include &amp;lt;boost/array.hpp&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;#include &amp;lt;boost/math/constants/constants.hpp&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;#include &amp;lt;boost/math/special_functions/hypot.hpp&amp;gt;

&lt;span class="gu"&gt;@@ -129,8 +131,8 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            = series_expansion::evaluate_series_A1&amp;lt;CT, SeriesOrder&amp;gt;(epsilon);

&lt;span class="w"&gt; &lt;/span&gt;        // Index zero element of coeffs_C1 is unused.
&lt;span class="gd"&gt;-        CT coeffs_C1[SeriesOrder + 1];&lt;/span&gt;
&lt;span class="gd"&gt;-        series_expansion::evaluate_coeffs_C1&amp;lt;CT, SeriesOrder&amp;gt;(epsilon, coeffs_C1);&lt;/span&gt;
&lt;span class="gi"&gt;+        boost::array&amp;lt;CT, SeriesOrder + 1&amp;gt; coeffs_C1;&lt;/span&gt;
&lt;span class="gi"&gt;+        series_expansion::evaluate_coeffs_C1(epsilon, coeffs_C1);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        // Tau is an integration variable.
&lt;span class="w"&gt; &lt;/span&gt;        CT const tau12 = distance / (b * (c1 + expansion_A1));
&lt;span class="gu"&gt;@@ -146,7 +148,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        math::normalize_values&amp;lt;CT&amp;gt;(sin_sigma1, cos_sigma1);

&lt;span class="w"&gt; &lt;/span&gt;        CT const B11 =
&lt;span class="gd"&gt;-            series_expansion::sin_cos_series&amp;lt;CT, SeriesOrder&amp;gt;(sin_sigma1, cos_sigma1, coeffs_C1);&lt;/span&gt;
&lt;span class="gi"&gt;+            series_expansion::sin_cos_series&amp;lt;CT, SeriesOrder + 1&amp;gt;(sin_sigma1, cos_sigma1, coeffs_C1);&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT const sin_B11 = sin(B11);
&lt;span class="w"&gt; &lt;/span&gt;        CT const cos_B11 = cos(B11);

&lt;span class="gu"&gt;@@ -156,11 +158,11 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            = cos_sigma1 * cos_B11 - sin_sigma1 * sin_B11;

&lt;span class="w"&gt; &lt;/span&gt;        // Index zero element of coeffs_C1p is unused.
&lt;span class="gd"&gt;-        CT coeffs_C1p[SeriesOrder + 1];&lt;/span&gt;
&lt;span class="gd"&gt;-        series_expansion::evaluate_coeffs_C1p&amp;lt;CT, SeriesOrder&amp;gt;(epsilon, coeffs_C1p);&lt;/span&gt;
&lt;span class="gi"&gt;+        boost::array&amp;lt;CT, SeriesOrder + 1&amp;gt; coeffs_C1p;&lt;/span&gt;
&lt;span class="gi"&gt;+        series_expansion::evaluate_coeffs_C1p(epsilon, coeffs_C1p);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        CT const B12 =
&lt;span class="gd"&gt;-            - series_expansion::sin_cos_series&amp;lt;CT, SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+            - series_expansion::sin_cos_series&amp;lt;CT, SeriesOrder + 1&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                                (sin_tau1 * cos_tau12 + cos_tau1 * sin_tau12,
&lt;span class="w"&gt; &lt;/span&gt;                                 cos_tau1 * cos_tau12 - sin_tau1 * sin_tau12,
&lt;span class="w"&gt; &lt;/span&gt;                                 coeffs_C1p);
&lt;span class="gu"&gt;@@ -246,13 +248,13 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        {
&lt;span class="w"&gt; &lt;/span&gt;            // Evaluate the coefficients for C2.
&lt;span class="w"&gt; &lt;/span&gt;            // Index zero element of coeffs_C2 is unused.
&lt;span class="gd"&gt;-            CT coeffs_C2[SeriesOrder + 1];&lt;/span&gt;
&lt;span class="gd"&gt;-            series_expansion::evaluate_coeffs_C2&amp;lt;CT, SeriesOrder&amp;gt;(epsilon, coeffs_C2);&lt;/span&gt;
&lt;span class="gi"&gt;+            boost::array&amp;lt;CT, SeriesOrder + 1&amp;gt; coeffs_C2;&lt;/span&gt;
&lt;span class="gi"&gt;+            series_expansion::evaluate_coeffs_C2(epsilon, coeffs_C2);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            CT const B21 =
&lt;span class="gd"&gt;-                series_expansion::sin_cos_series&amp;lt;CT, SeriesOrder&amp;gt;(sin_sigma1, cos_sigma1, coeffs_C2);&lt;/span&gt;
&lt;span class="gi"&gt;+                series_expansion::sin_cos_series&amp;lt;CT, SeriesOrder + 1&amp;gt;(sin_sigma1, cos_sigma1, coeffs_C2);&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            CT const B22 =
&lt;span class="gd"&gt;-                series_expansion::sin_cos_series&amp;lt;CT, SeriesOrder&amp;gt;(sin_sigma2, cos_sigma2, coeffs_C2);&lt;/span&gt;
&lt;span class="gi"&gt;+                series_expansion::sin_cos_series&amp;lt;CT, SeriesOrder + 1&amp;gt;(sin_sigma2, cos_sigma2, coeffs_C2);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            // Find the coefficients for A2 by computing the
&lt;span class="w"&gt; &lt;/span&gt;            // series expansion using Horner scehme.
&lt;span class="gu"&gt;@@ -263,8 +265,8 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            CT const AB2 = (c1 + expansion_A2) * (B22 - B21);
&lt;span class="w"&gt; &lt;/span&gt;            CT const J12 = (expansion_A1 - expansion_A2) * sigma12 + (AB1 - AB2);

&lt;span class="gd"&gt;-            CT const dn1 = sqrt(c1 + ep2 * math::sqr(sin_beta1));&lt;/span&gt;
&lt;span class="gd"&gt;-            CT const dn2 = sqrt(c1 + k2 * math::sqr(sin_sigma2));&lt;/span&gt;
&lt;span class="gi"&gt;+            CT const dn1 = math::sqrt(c1 + ep2 * math::sqr(sin_beta1));&lt;/span&gt;
&lt;span class="gi"&gt;+            CT const dn2 = math::sqrt(c1 + k2 * math::sqr(sin_sigma2));&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            // Find the reduced length.
&lt;span class="w"&gt; &lt;/span&gt;            result.reduced_length = b * ((dn2 * (cos_sigma1 * sin_sigma2) -
&lt;span class="gh"&gt;diff --git a/include/boost/geometry/util/series_expansion.hpp b/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index c3e28f1..492393e 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -214,11 +214,11 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;     geometry/doc/other/maxima/geod.mac
&lt;span class="w"&gt; &lt;/span&gt;    */
&lt;span class="w"&gt; &lt;/span&gt;    template &amp;lt;typename CT, std::size_t SeriesOrder&amp;gt;
&lt;span class="gd"&gt;-    static inline void evaluate_coeffs_C1(CT eps, CT c[])&lt;/span&gt;
&lt;span class="gi"&gt;+    static inline void evaluate_coeffs_C1(CT eps, boost::array&amp;lt;CT, SeriesOrder&amp;gt;&amp;amp; c)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    {
&lt;span class="w"&gt; &lt;/span&gt;        CT eps2 = math::sqr(eps);
&lt;span class="w"&gt; &lt;/span&gt;        CT d = eps;
&lt;span class="gd"&gt;-        switch (SeriesOrder) {&lt;/span&gt;
&lt;span class="gi"&gt;+        switch (c.size() - 1) {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        case 0:
&lt;span class="w"&gt; &lt;/span&gt;            break;
&lt;span class="w"&gt; &lt;/span&gt;        case 1:
&lt;span class="gu"&gt;@@ -313,11 +313,11 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;     geometry/doc/other/maxima/geod.mac
&lt;span class="w"&gt; &lt;/span&gt;    */
&lt;span class="w"&gt; &lt;/span&gt;    template &amp;lt;typename CT, std::size_t SeriesOrder&amp;gt;
&lt;span class="gd"&gt;-    static inline void evaluate_coeffs_C1p(CT eps, CT c[])&lt;/span&gt;
&lt;span class="gi"&gt;+    static inline void evaluate_coeffs_C1p(CT eps, boost::array&amp;lt;CT, SeriesOrder&amp;gt;&amp;amp; c)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    {
&lt;span class="w"&gt; &lt;/span&gt;        CT const eps2 = math::sqr(eps);
&lt;span class="w"&gt; &lt;/span&gt;        CT d = eps;
&lt;span class="gd"&gt;-        switch (SeriesOrder) {&lt;/span&gt;
&lt;span class="gi"&gt;+        switch (c.size() - 1) {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        case 0:
&lt;span class="w"&gt; &lt;/span&gt;            break;
&lt;span class="w"&gt; &lt;/span&gt;        case 1:
&lt;span class="gu"&gt;@@ -412,11 +412,11 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;     geometry/doc/other/maxima/geod.mac
&lt;span class="w"&gt; &lt;/span&gt;    */
&lt;span class="w"&gt; &lt;/span&gt;    template &amp;lt;typename CT, std::size_t SeriesOrder&amp;gt;
&lt;span class="gd"&gt;-    static inline void evaluate_coeffs_C2(CT const&amp;amp; eps, CT c[])&lt;/span&gt;
&lt;span class="gi"&gt;+    static inline void evaluate_coeffs_C2(CT const&amp;amp; eps, boost::array&amp;lt;CT, SeriesOrder&amp;gt;&amp;amp; c)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    {
&lt;span class="w"&gt; &lt;/span&gt;        CT const eps2 = math::sqr(eps);
&lt;span class="w"&gt; &lt;/span&gt;        CT d = eps;
&lt;span class="gd"&gt;-        switch (SeriesOrder) {&lt;/span&gt;
&lt;span class="gi"&gt;+        switch (c.size() - 1) {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        case 0:
&lt;span class="w"&gt; &lt;/span&gt;            break;
&lt;span class="w"&gt; &lt;/span&gt;        case 1:
&lt;span class="gu"&gt;@@ -510,7 +510,7 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;     generated by the following Maxima script:
&lt;span class="w"&gt; &lt;/span&gt;     geometry/doc/other/maxima/geod.mac
&lt;span class="w"&gt; &lt;/span&gt;    */
&lt;span class="gd"&gt;-    template &amp;lt;typename CT, int SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    template &amp;lt;typename CT, size_t SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    static inline void evaluate_coeffs_C3x(CT const&amp;amp; n, CT c[]) {
&lt;span class="w"&gt; &lt;/span&gt;        const CT n2 = math::sqr(n);
&lt;span class="w"&gt; &lt;/span&gt;        switch (SeriesOrder) {
&lt;span class="gu"&gt;@@ -673,6 +673,30 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        return 2 * sinx * cosx * k0;
&lt;span class="w"&gt; &lt;/span&gt;    }

&lt;span class="gi"&gt;+    template &amp;lt;typename CT, size_t SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    static inline CT sin_cos_series(CT sinx, CT cosx, const boost::array&amp;lt;CT, SeriesOrder&amp;gt; coeffs)&lt;/span&gt;
&lt;span class="gi"&gt;+    {&lt;/span&gt;
&lt;span class="gi"&gt;+        size_t n = SeriesOrder - 1;&lt;/span&gt;
&lt;span class="gi"&gt;+        size_t index = 0;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        // Point to one beyond last element.&lt;/span&gt;
&lt;span class="gi"&gt;+        index += (n + 1);&lt;/span&gt;
&lt;span class="gi"&gt;+        CT ar = 2 * (cosx - sinx) * (cosx + sinx);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        CT k0 = n &amp;amp; 1 ? coeffs[--index] : 0;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT k1 = 0;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        // Make n even.&lt;/span&gt;
&lt;span class="gi"&gt;+        n /= 2;&lt;/span&gt;
&lt;span class="gi"&gt;+        while (n--) {&lt;/span&gt;
&lt;span class="gi"&gt;+          // Unroll loop x 2, so accumulators return to their original role.&lt;/span&gt;
&lt;span class="gi"&gt;+          k1 = ar * k0 - k1 + coeffs[--index];&lt;/span&gt;
&lt;span class="gi"&gt;+          k0 = ar * k1 - k0 + coeffs[--index];&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        return 2 * sinx * cosx * k0;&lt;/span&gt;
&lt;span class="gi"&gt;+    }&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;}}} // namespace boost::geometry::series_expansion


commit 6d0720b5ae1419469577229dbb0d7efb763e9b96
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Wed Jun 20 21:32:41 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [formulas] Ensure reduced length and geodesic scale are computed in canonical form

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/formulas/karney_inverse.hpp b/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 4573641..5b6de69 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -394,6 +394,25 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                             std::abs(sin_alpha1 - sin_alpha1b) + (cos_alpha1 - cos_alpha1b) &amp;lt; tol_bisection);

&lt;span class="w"&gt; &lt;/span&gt;                }
&lt;span class="gi"&gt;+                // Store values in temporary vairables.&lt;/span&gt;
&lt;span class="gi"&gt;+                // bool enable_reduced_length = EnableReducedLength;&lt;/span&gt;
&lt;span class="gi"&gt;+                // bool enable_geodesic_scale = EnableGeodesicScale;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+                // EnableReducedLength = false;&lt;/span&gt;
&lt;span class="gi"&gt;+                // EnableGeodesicScale = false;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+                CT dummy;&lt;/span&gt;
&lt;span class="gi"&gt;+                // Ensure that the reduced length and geodesic scale are computed in&lt;/span&gt;
&lt;span class="gi"&gt;+                // a &amp;quot;canonical&amp;quot; way, with the I2 integral.&lt;/span&gt;
&lt;span class="gi"&gt;+                meridian_length(n, ep2, sigma12, sin_sigma1, cos_sigma1, dn1,&lt;/span&gt;
&lt;span class="gi"&gt;+                                                 sin_sigma2, cos_sigma2, dn2,&lt;/span&gt;
&lt;span class="gi"&gt;+                                                 cos_beta1, cos_beta2, s12x,&lt;/span&gt;
&lt;span class="gi"&gt;+                                                 m12x, dummy, result.geodesic_scale,&lt;/span&gt;
&lt;span class="gi"&gt;+                                                 M21, coeffs_C1);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+                // Restore values to their previous state.&lt;/span&gt;
&lt;span class="gi"&gt;+                // EnableReducedLength = enable_reduced_length;&lt;/span&gt;
&lt;span class="gi"&gt;+                // EnableGeodesicScale = enable_geodesic_scale;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            }
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="w"&gt; &lt;/span&gt;    }

commit 02577bda556df3cd3e596cc8eb1304ecac032b20
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Wed Jun 20 21:31:18 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [formulas] Use midpoint of bracket when value lies outside of range

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/formulas/karney_inverse.hpp b/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index f8e5478..4573641 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -93,6 +93,10 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT const tol0 = std::numeric_limits&amp;lt;CT&amp;gt;::epsilon();
&lt;span class="w"&gt; &lt;/span&gt;        CT const tol1 = c200 * tol0;
&lt;span class="w"&gt; &lt;/span&gt;        CT const tol2 = sqrt(tol0);
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        // Check on bisection interval.&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const tol_bisection = tol0 * tol2;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT const etol2 = c0_1 * tol2 /
&lt;span class="w"&gt; &lt;/span&gt;            sqrt(std::max(c0_001, std::abs(f)) * std::min(c1, c1 - f / c2) / c2);

&lt;span class="gu"&gt;@@ -373,6 +377,22 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                            continue;
&lt;span class="w"&gt; &lt;/span&gt;                        }
&lt;span class="w"&gt; &lt;/span&gt;                    }
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+                    // Either dv was not positive or updated value was outside legal&lt;/span&gt;
&lt;span class="gi"&gt;+                    // range. Use the midpoint of the bracket as the next estimate.&lt;/span&gt;
&lt;span class="gi"&gt;+                    // This mechanism is not needed for the WGS84 ellipsoid, but it does&lt;/span&gt;
&lt;span class="gi"&gt;+                    // catch problems with more eeccentric ellipsoids. Its efficacy is&lt;/span&gt;
&lt;span class="gi"&gt;+                    // such for the WGS84 test set with the starting guess set to alp1 =&lt;/span&gt;
&lt;span class="gi"&gt;+                    // 90deg:&lt;/span&gt;
&lt;span class="gi"&gt;+                    // the WGS84 test set: mean = 5.21, sd = 3.93, max = 24&lt;/span&gt;
&lt;span class="gi"&gt;+                    // WGS84 and random input: mean = 4.74, sd = 0.99&lt;/span&gt;
&lt;span class="gi"&gt;+                    sin_alpha1 = (sin_alpha1a + sin_alpha1b) / c2;&lt;/span&gt;
&lt;span class="gi"&gt;+                    cos_alpha1 = (cos_alpha1a + cos_alpha1b) / c2;&lt;/span&gt;
&lt;span class="gi"&gt;+                    math::normalize_values&amp;lt;CT&amp;gt;(sin_alpha1, cos_alpha1);&lt;/span&gt;
&lt;span class="gi"&gt;+                    tripn = false;&lt;/span&gt;
&lt;span class="gi"&gt;+                    tripb = (std::abs(sin_alpha1a - sin_alpha1) + (cos_alpha1a - cos_alpha1) &amp;lt; tol_bisection ||&lt;/span&gt;
&lt;span class="gi"&gt;+                             std::abs(sin_alpha1 - sin_alpha1b) + (cos_alpha1 - cos_alpha1b) &amp;lt; tol_bisection);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                }
&lt;span class="w"&gt; &lt;/span&gt;            }
&lt;span class="w"&gt; &lt;/span&gt;        }

commit ead0b188f915bd987eb2c44770e3dcf29e94473c
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Tue Jun 19 20:21:05 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [formulas] Update bracketing values in Newton&amp;#39;s method

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/formulas/karney_inverse.hpp b/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 6d459ab..f8e5478 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -60,7 +60,9 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    static CT constexpr c3 = 3;
&lt;span class="w"&gt; &lt;/span&gt;    static CT constexpr c4 = 4;
&lt;span class="w"&gt; &lt;/span&gt;    static CT constexpr c6 = 6;
&lt;span class="gi"&gt;+    static CT constexpr c8 = 8;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    static CT constexpr c10 = 10;
&lt;span class="gi"&gt;+    static CT constexpr c16 = 16;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    static CT constexpr c20 = 20;
&lt;span class="w"&gt; &lt;/span&gt;    static CT constexpr c90 = 90;
&lt;span class="w"&gt; &lt;/span&gt;    static CT constexpr c180 = 180;
&lt;span class="gu"&gt;@@ -331,6 +333,46 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                                    iteration &amp;lt; max_iterations,
&lt;span class="w"&gt; &lt;/span&gt;                                    dv, f, n, ep2, tiny, coeffs_C1);

&lt;span class="gi"&gt;+                    // Reversed test to allow escape with NaNs.&lt;/span&gt;
&lt;span class="gi"&gt;+                    if (tripb || !(std::abs(v) &amp;gt;= (tripn ? c8 : c1) * tol0))&lt;/span&gt;
&lt;span class="gi"&gt;+                        break;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+                    // Update bracketing values.&lt;/span&gt;
&lt;span class="gi"&gt;+                    if (v &amp;gt; c0 &amp;amp;&amp;amp; (iteration &amp;gt; max_iterations ||&lt;/span&gt;
&lt;span class="gi"&gt;+                        cos_alpha1 / sin_alpha1 &amp;gt; cos_alpha1b / sin_alpha1b))&lt;/span&gt;
&lt;span class="gi"&gt;+                    {&lt;/span&gt;
&lt;span class="gi"&gt;+                        sin_alpha1b = sin_alpha1;&lt;/span&gt;
&lt;span class="gi"&gt;+                        cos_alpha1b = cos_alpha1;   &lt;/span&gt;
&lt;span class="gi"&gt;+                    }&lt;/span&gt;
&lt;span class="gi"&gt;+                    else if (v &amp;lt; c0 &amp;amp;&amp;amp; (iteration &amp;gt; max_iterations ||&lt;/span&gt;
&lt;span class="gi"&gt;+                             cos_alpha1 / sin_alpha1 &amp;lt; cos_alpha1a / sin_alpha1a))&lt;/span&gt;
&lt;span class="gi"&gt;+                    {&lt;/span&gt;
&lt;span class="gi"&gt;+                        sin_alpha1a = sin_alpha1;&lt;/span&gt;
&lt;span class="gi"&gt;+                        cos_alpha1a = cos_alpha1;&lt;/span&gt;
&lt;span class="gi"&gt;+                    }&lt;/span&gt;
&lt;span class="gi"&gt;+                    if (iteration &amp;lt; max_iterations &amp;amp;&amp;amp; dv &amp;gt; c0)&lt;/span&gt;
&lt;span class="gi"&gt;+                    {&lt;/span&gt;
&lt;span class="gi"&gt;+                        CT diff_alpha1 = -v / dv;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+                        CT sin_diff_alpha1 = sin(diff_alpha1);&lt;/span&gt;
&lt;span class="gi"&gt;+                        CT cos_diff_alpha1 = cos(diff_alpha1);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+                        CT nsin_alpa1 = sin_alpha1 * cos_diff_alpha1 +&lt;/span&gt;
&lt;span class="gi"&gt;+                                        cos_alpha1 * sin_diff_alpha1;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+                        if (nsin_alpa1 &amp;gt; c0 &amp;amp;&amp;amp; std::abs(diff_alpha1) &amp;lt; math::pi&amp;lt;CT&amp;gt;())&lt;/span&gt;
&lt;span class="gi"&gt;+                        {&lt;/span&gt;
&lt;span class="gi"&gt;+                            cos_alpha1 = cos_alpha1 * cos_diff_alpha1 - sin_alpha1 * sin_diff_alpha1;&lt;/span&gt;
&lt;span class="gi"&gt;+                            sin_alpha1 = nsin_alpa1;&lt;/span&gt;
&lt;span class="gi"&gt;+                            math::normalize_values&amp;lt;CT&amp;gt;(sin_alpha1, cos_alpha1);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+                            // In some regimes we don&amp;#39;t get quadratic convergence because&lt;/span&gt;
&lt;span class="gi"&gt;+                            // slope -&amp;gt; 0. So use convergence conditions based on epsilon&lt;/span&gt;
&lt;span class="gi"&gt;+                            // instead of sqrt(epsilon).&lt;/span&gt;
&lt;span class="gi"&gt;+                            tripn = std::abs(v) &amp;lt;= c16 * tol0;&lt;/span&gt;
&lt;span class="gi"&gt;+                            continue;&lt;/span&gt;
&lt;span class="gi"&gt;+                        }&lt;/span&gt;
&lt;span class="gi"&gt;+                    }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                }
&lt;span class="w"&gt; &lt;/span&gt;            }
&lt;span class="w"&gt; &lt;/span&gt;        }

commit 2ddad45616ec884589235b07fe81d4043a785f3a
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Tue Jun 19 18:08:23 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [util] Move difference_angle function to normalize_spheroidal_coordinates.hpp

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/formulas/karney_inverse.hpp b/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index e4ebb65..6d459ab 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -15,6 +15,7 @@&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;#include &amp;lt;boost/geometry/util/math.hpp&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;#include &amp;lt;boost/geometry/util/series_expansion.hpp&amp;gt;
&lt;span class="gi"&gt;+#include &amp;lt;boost/geometry/util/normalize_spheroidal_coordinates.hpp&amp;gt;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;#include &amp;lt;boost/geometry/formulas/flattening.hpp&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;#include &amp;lt;boost/geometry/formulas/result_inverse.hpp&amp;gt;
&lt;span class="gu"&gt;@@ -155,14 +156,14 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        math::sin_cos_degrees(lat1, sin_beta1, cos_beta1);
&lt;span class="w"&gt; &lt;/span&gt;        sin_beta1 *= one_minus_f;

&lt;span class="gd"&gt;-        math::normalize(sin_beta1, cos_beta1);&lt;/span&gt;
&lt;span class="gi"&gt;+        math::normalize_values&amp;lt;CT&amp;gt;(sin_beta1, cos_beta1);&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        cos_beta1 = std::max(tiny, cos_beta1);

&lt;span class="w"&gt; &lt;/span&gt;        CT sin_beta2, cos_beta2;
&lt;span class="w"&gt; &lt;/span&gt;        math::sin_cos_degrees(lat2, sin_beta2, cos_beta2);
&lt;span class="w"&gt; &lt;/span&gt;        sin_beta2 *= one_minus_f;

&lt;span class="gd"&gt;-        math::normalize(sin_beta2, cos_beta2);&lt;/span&gt;
&lt;span class="gi"&gt;+        math::normalize_values&amp;lt;CT&amp;gt;(sin_beta2, cos_beta2);&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        cos_beta2 = std::max(tiny, cos_beta2);

&lt;span class="w"&gt; &lt;/span&gt;        // If cos_beta1 &amp;lt; -sin_beta1, then cos_beta2 - cos_beta1 is a
&lt;span class="gu"&gt;@@ -329,6 +330,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                                    eps, diff_omega12,
&lt;span class="w"&gt; &lt;/span&gt;                                    iteration &amp;lt; max_iterations,
&lt;span class="w"&gt; &lt;/span&gt;                                    dv, f, n, ep2, tiny, coeffs_C1);
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                }
&lt;span class="w"&gt; &lt;/span&gt;            }
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="gu"&gt;@@ -588,7 +590,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        // Apply sanity check on starting guess. Backwards check allows NaN through.
&lt;span class="w"&gt; &lt;/span&gt;        if (!(sin_alpha1 &amp;lt;= c0))
&lt;span class="w"&gt; &lt;/span&gt;        {
&lt;span class="gd"&gt;-          math::normalize&amp;lt;CT&amp;gt;(sin_alpha1, cos_alpha1);&lt;/span&gt;
&lt;span class="gi"&gt;+          math::normalize_values&amp;lt;CT&amp;gt;(sin_alpha1, cos_alpha1);&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="w"&gt; &lt;/span&gt;        else
&lt;span class="w"&gt; &lt;/span&gt;        {
&lt;span class="gu"&gt;@@ -707,7 +709,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        cos_sigma1 = cos_omega1 =
&lt;span class="w"&gt; &lt;/span&gt;            cos_alpha1 * cos_beta1;

&lt;span class="gd"&gt;-        math::normalize(sin_sigma1, cos_sigma1);&lt;/span&gt;
&lt;span class="gi"&gt;+        math::normalize_values&amp;lt;CT&amp;gt;(sin_sigma1, cos_sigma1);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        // Enforce symmetries in the case abs(beta2) = -beta1.
&lt;span class="w"&gt; &lt;/span&gt;        // Otherwise, this can yield singularities in the Newton iteration.
&lt;span class="gu"&gt;@@ -729,7 +731,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        cos_sigma2 = cos_omega2 =
&lt;span class="w"&gt; &lt;/span&gt;            cos_alpha2 * cos_beta2;

&lt;span class="gd"&gt;-        math::normalize(sin_sigma2, cos_sigma2);&lt;/span&gt;
&lt;span class="gi"&gt;+        math::normalize_values&amp;lt;CT&amp;gt;(sin_sigma2, cos_sigma2);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        // sig12 = sig2 - sig1, limit to [0, pi].
&lt;span class="w"&gt; &lt;/span&gt;        sigma12 = atan2(std::max(c0, cos_sigma1 * sin_sigma2 - sin_sigma1 * cos_sigma2),
&lt;span class="gh"&gt;diff --git a/include/boost/geometry/util/math.hpp b/include/boost/geometry/util/math.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 42d910f..110fbd7 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/util/math.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/util/math.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -867,24 +867,6 @@ inline T sum_error(T u, T v, T&amp;amp; t)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;}

&lt;span class="w"&gt; &lt;/span&gt;/*!
&lt;span class="gd"&gt;-\brief The exact difference of two angles reduced to&lt;/span&gt;
&lt;span class="gd"&gt;-       (&amp;amp;minus;180&amp;amp;deg;, 180&amp;amp;deg;].&lt;/span&gt;
&lt;span class="gd"&gt;-*/&lt;/span&gt;
&lt;span class="gd"&gt;-template&amp;lt;typename T&amp;gt;&lt;/span&gt;
&lt;span class="gd"&gt;-inline T difference_angle(T x, T y, T&amp;amp; e)&lt;/span&gt;
&lt;span class="gd"&gt;-{&lt;/span&gt;
&lt;span class="gd"&gt;-    T t, d = normalize_angle(sum_error(std::remainder(-x, T(360)),&lt;/span&gt;
&lt;span class="gd"&gt;-                                       std::remainder(y, T(360)), t));&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    // Here y - x = d + t (mod 360), exactly, where d is in (-180,180] and&lt;/span&gt;
&lt;span class="gd"&gt;-    // abs(t) &amp;lt;= eps (eps = 2^-45 for doubles).  The only case where the&lt;/span&gt;
&lt;span class="gd"&gt;-    // addition of t takes the result outside the range (-180,180] is d = 180&lt;/span&gt;
&lt;span class="gd"&gt;-    // and t &amp;gt; 0.  The case, d = -180 + eps, t = -eps, can&amp;#39;t happen, since&lt;/span&gt;
&lt;span class="gd"&gt;-    // sum_error would have returned the exact result in such a case (i.e., given t = 0).&lt;/span&gt;
&lt;span class="gd"&gt;-    return sum_error(d == 180 &amp;amp;&amp;amp; t &amp;gt; 0 ? -180 : d, t, e);&lt;/span&gt;
&lt;span class="gd"&gt;-}&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-/*!&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;\brief Evaluate the polynomial in x using Horner&amp;#39;s method.
&lt;span class="w"&gt; &lt;/span&gt;*/
&lt;span class="w"&gt; &lt;/span&gt;// TODO: adl1995 - Merge these functions with formulas/area_formulas.hpp
&lt;span class="gh"&gt;diff --git a/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp b/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 71215b3..77c8b19 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -385,8 +385,9 @@ inline void normalize_angle(CoordinateType&amp;amp; angle)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;template&amp;lt;typename T&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;inline T difference_angle(T x, T y, T&amp;amp; e)
&lt;span class="w"&gt; &lt;/span&gt;{
&lt;span class="gd"&gt;-    T t, d = normalize_angle(math::sum_error(std::remainder(-x, T(360)),&lt;/span&gt;
&lt;span class="gd"&gt;-                                             std::remainder(y, T(360)), t));&lt;/span&gt;
&lt;span class="gi"&gt;+    T t, d = math::sum_error(std::remainder(-x, T(360)), std::remainder(y, T(360)), t);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    normalize_angle&amp;lt;degree, T&amp;gt;(d);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    // Here y - x = d + t (mod 360), exactly, where d is in (-180,180] and
&lt;span class="w"&gt; &lt;/span&gt;    // abs(t) &amp;lt;= eps (eps = 2^-45 for doubles).  The only case where the

commit 490b4df9434d45afb49ed081e3bf3eb251e55e07
Merge: 06fc06d 78411f6
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Tue Jun 19 10:05:16 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Merge branch &amp;#39;feature/geodesic_direct&amp;#39; into feature/karney_inverse

&lt;span class="w"&gt; &lt;/span&gt;   Conflicts (resolved):
&lt;span class="w"&gt; &lt;/span&gt;       include/boost/geometry/util/math.hpp

commit 78411f63c8d552189c34bb3c5589696c9c4f6c8a
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Jun 15 21:42:56 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [doc][util][formulas][test] Add copyright information in updated files

&lt;span class="gh"&gt;diff --git a/doc/other/maxima/geod.mac b/doc/other/maxima/geod.mac&lt;/span&gt;
&lt;span class="gh"&gt;index 229e949..b0b052b 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/doc/other/maxima/geod.mac&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/doc/other/maxima/geod.mac&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,10 +1,27 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;/*
&lt;span class="gd"&gt;-Compute the series expansions for the ellipsoidal geodesic problem.&lt;/span&gt;
&lt;span class="gi"&gt;+Copyright (c) 2018 Adeel Ahmad, Islamabad, Pakistan.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+Contributed and/or modified by Adeel Ahmad, as part of Google Summer of Code 2018 program.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+Use, modification and distribution is subject to the Boost Software License,&lt;/span&gt;
&lt;span class="gi"&gt;+Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at&lt;/span&gt;
&lt;span class="gi"&gt;+http://www.boost.org/LICENSE_1_0.txt)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+This file is converted from GeographicLib, https://geographiclib.sourceforge.io&lt;/span&gt;
&lt;span class="gi"&gt;+GeographicLib is originally written by Charles Karney.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+Author: Charles Karney (2008-2017)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+Last updated version of GeographicLib: 1.49&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+Original copyright notice:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;Copyright (c) Charles Karney (2009-2015) &amp;lt;charles@karney.com&amp;gt; and
&lt;span class="gd"&gt;-licensed under the MIT/X11 License.  For more information, see&lt;/span&gt;
&lt;span class="gi"&gt;+licensed under the MIT/X11 License. For more information, see&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;https://geographiclib.sourceforge.io

&lt;span class="gi"&gt;+Compute the series expansions for the ellipsoidal geodesic problem.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;References:

&lt;span class="w"&gt; &lt;/span&gt;   Charles F. F. Karney,
&lt;span class="gh"&gt;diff --git a/include/boost/geometry/formulas/karney_direct.hpp b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 5e0f2f5..327d9e1 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,11 +1,26 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;// Boost.Geometry

&lt;span class="gd"&gt;-// Contributed and/or modified by Adeel Ahmad.&lt;/span&gt;
&lt;span class="gi"&gt;+// Copyright (c) 2018 Adeel Ahmad, Islamabad, Pakistan.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+// Contributed and/or modified by Adeel Ahmad, as part of Google Summer of Code 2018 program.&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;// Use, modification and distribution is subject to the Boost Software License,
&lt;span class="w"&gt; &lt;/span&gt;// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
&lt;span class="w"&gt; &lt;/span&gt;// http://www.boost.org/LICENSE_1_0.txt)

&lt;span class="gi"&gt;+// This file is converted from GeographicLib, https://geographiclib.sourceforge.io&lt;/span&gt;
&lt;span class="gi"&gt;+// GeographicLib is originally written by Charles Karney.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+// Author: Charles Karney (2008-2017)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+// Last updated version of GeographicLib: 1.49&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+// Original copyright notice:&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+// Copyright (c) Charles Karney (2008-2017) &amp;lt;charles@karney.com&amp;gt; and licensed&lt;/span&gt;
&lt;span class="gi"&gt;+// under the MIT/X11 License. For more information, see&lt;/span&gt;
&lt;span class="gi"&gt;+// https://geographiclib.sourceforge.io&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;#ifndef BOOST_GEOMETRY_FORMULAS_KARNEY_DIRECT_HPP
&lt;span class="w"&gt; &lt;/span&gt;#define BOOST_GEOMETRY_FORMULAS_KARNEY_DIRECT_HPP

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/util/math.hpp b/include/boost/geometry/util/math.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 2ae2793..6559910 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/util/math.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/util/math.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -9,6 +9,7 @@&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
&lt;span class="w"&gt; &lt;/span&gt;// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
&lt;span class="gi"&gt;+// Contributed and/or modified by Adeel Ahmad, as part of Google Summer of Code 2018 program&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;// Parts of Boost.Geometry are redesigned from Geodan&amp;#39;s Geographic Library
&lt;span class="w"&gt; &lt;/span&gt;// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.
&lt;span class="gh"&gt;diff --git a/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp b/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 04de04d..4c432a8 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -6,6 +6,7 @@&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
&lt;span class="w"&gt; &lt;/span&gt;// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
&lt;span class="gi"&gt;+// Contributed and/or modified by Adeel Ahmad, as part of Google Summer of Code 2018 program&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;// Licensed under the Boost Software License version 1.0.
&lt;span class="w"&gt; &lt;/span&gt;// http://www.boost.org/users/license.html
&lt;span class="gh"&gt;diff --git a/include/boost/geometry/util/series_expansion.hpp b/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 4bd09a1..c3e28f1 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,11 +1,26 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;// Boost.Geometry

&lt;span class="gd"&gt;-// Contributed and/or modified by Adeel Ahmad.&lt;/span&gt;
&lt;span class="gi"&gt;+// Copyright (c) 2018 Adeel Ahmad, Islamabad, Pakistan.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+// Contributed and/or modified by Adeel Ahmad, as part of Google Summer of Code 2018 program.&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;// Use, modification and distribution is subject to the Boost Software License,
&lt;span class="w"&gt; &lt;/span&gt;// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
&lt;span class="w"&gt; &lt;/span&gt;// http://www.boost.org/LICENSE_1_0.txt)

&lt;span class="gi"&gt;+// This file is converted from GeographicLib, https://geographiclib.sourceforge.io&lt;/span&gt;
&lt;span class="gi"&gt;+// GeographicLib is originally written by Charles Karney.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+// Author: Charles Karney (2008-2017)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+// Last updated version of GeographicLib: 1.49&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+// Original copyright notice:&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+// Copyright (c) Charles Karney (2008-2017) &amp;lt;charles@karney.com&amp;gt; and licensed&lt;/span&gt;
&lt;span class="gi"&gt;+// under the MIT/X11 License. For more information, see&lt;/span&gt;
&lt;span class="gi"&gt;+// https://geographiclib.sourceforge.io&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;#ifndef BOOST_GEOMETRY_UTIL_SERIES_EXPANSION_HPP
&lt;span class="w"&gt; &lt;/span&gt;#define BOOST_GEOMETRY_UTIL_SERIES_EXPANSION_HPP

&lt;span class="gh"&gt;diff --git a/test/formulas/direct.cpp b/test/formulas/direct.cpp&lt;/span&gt;
&lt;span class="gh"&gt;index 9cd4bdf..5a7e5d9 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/test/formulas/direct.cpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/test/formulas/direct.cpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -5,6 +5,7 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;// Copyright (c) 2016 Oracle and/or its affiliates.

&lt;span class="w"&gt; &lt;/span&gt;// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
&lt;span class="gi"&gt;+// Contributed and/or modified by Adeel Ahmad, as part of Google Summer of Code 2018 program.&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;// Use, modification and distribution is subject to the Boost Software License,
&lt;span class="w"&gt; &lt;/span&gt;// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
&lt;span class="gh"&gt;diff --git a/test/formulas/direct_cases_antipodal.hpp b/test/formulas/direct_cases_antipodal.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index ad5db5d..c6ba852 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/test/formulas/direct_cases_antipodal.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/test/formulas/direct_cases_antipodal.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,7 +1,9 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;// Boost.Geometry
&lt;span class="w"&gt; &lt;/span&gt;// Unit Test

&lt;span class="gd"&gt;-// Contributed and/or modified by Adeel Ahmad.&lt;/span&gt;
&lt;span class="gi"&gt;+// Copyright (c) 2018 Adeel Ahmad, Islamabad, Pakistan.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+// Contributed and/or modified by Adeel Ahmad, as part of Google Summer of Code 2018 program.&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;// Use, modification and distribution is subject to the Boost Software License,
&lt;span class="w"&gt; &lt;/span&gt;// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at

commit 2c7a29e7dcfe71ec5a5a04f2d442477860e798d1
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Jun 15 18:52:30 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [formulas][util] Improve code formatting to conform with guidelines

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/formulas/karney_direct.hpp b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 5e382f0..5e0f2f5 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -64,7 +64,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        Azi azi12 = azimuth12;
&lt;span class="w"&gt; &lt;/span&gt;        math::normalize_angle&amp;lt;degree, Azi&amp;gt;(azi12);

&lt;span class="gd"&gt;-        CT const dist_c0 = 0;&lt;/span&gt;
&lt;span class="gi"&gt;+        Dist const dist_c0 = 0;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        if (math::equals(distance, dist_c0) || distance &amp;lt; dist_c0)
&lt;span class="w"&gt; &lt;/span&gt;        {
&lt;span class="gu"&gt;@@ -73,6 +73,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            return result;
&lt;span class="w"&gt; &lt;/span&gt;        }

&lt;span class="gi"&gt;+        CT const c0 = 0;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT const c1 = 1;
&lt;span class="w"&gt; &lt;/span&gt;        CT const c2 = 2;

&lt;span class="gu"&gt;@@ -126,7 +127,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT sin_omega1 = sin_alpha0 * sin_beta1;

&lt;span class="w"&gt; &lt;/span&gt;        CT cos_sigma1, cos_omega1;
&lt;span class="gd"&gt;-        cos_sigma1 = cos_omega1 = sin_beta1 != 0 || cos_alpha1 != 0 ? cos_beta1 * cos_alpha1 : 1;&lt;/span&gt;
&lt;span class="gi"&gt;+        cos_sigma1 = cos_omega1 = sin_beta1 != c0 || cos_alpha1 != c0 ? cos_beta1 * cos_alpha1 : c1;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        math::normalize_values&amp;lt;CT&amp;gt;(sin_sigma1, cos_sigma1);

&lt;span class="w"&gt; &lt;/span&gt;        CT const B11 =
&lt;span class="gh"&gt;diff --git a/include/boost/geometry/util/math.hpp b/include/boost/geometry/util/math.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 6ba51b2..2ae2793 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/util/math.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/util/math.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -779,7 +779,9 @@ inline Result rounding_cast(T const&amp;amp; v)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;      &amp;amp;minus;0 is returned.
&lt;span class="w"&gt; &lt;/span&gt;*/
&lt;span class="w"&gt; &lt;/span&gt;template&amp;lt;typename T&amp;gt;
&lt;span class="gd"&gt;-inline void sin_cos_degrees(T const&amp;amp; x, T &amp;amp; sinx, T &amp;amp; cosx)&lt;/span&gt;
&lt;span class="gi"&gt;+inline void sin_cos_degrees(T const&amp;amp; x,&lt;/span&gt;
&lt;span class="gi"&gt;+                            T &amp;amp; sinx,&lt;/span&gt;
&lt;span class="gi"&gt;+                            T &amp;amp; cosx)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;{
&lt;span class="w"&gt; &lt;/span&gt;    // In order to minimize round-off errors, this function exactly reduces
&lt;span class="w"&gt; &lt;/span&gt;    // the argument to the range [-45, 45] before converting it to radians.
&lt;span class="gu"&gt;@@ -836,8 +838,8 @@ inline T round_angle(T x) {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;// i.e. place them in one file.
&lt;span class="w"&gt; &lt;/span&gt;template &amp;lt;typename NT, typename IteratorType&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;inline NT horner_evaluate(NT x,
&lt;span class="gd"&gt;-                                 IteratorType begin,&lt;/span&gt;
&lt;span class="gd"&gt;-                                 IteratorType end)&lt;/span&gt;
&lt;span class="gi"&gt;+                          IteratorType begin,&lt;/span&gt;
&lt;span class="gi"&gt;+                          IteratorType end)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;{
&lt;span class="w"&gt; &lt;/span&gt;    NT result(0);
&lt;span class="w"&gt; &lt;/span&gt;    IteratorType it = end;
&lt;span class="gu"&gt;@@ -854,8 +856,8 @@ inline NT horner_evaluate(NT x,&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;*/
&lt;span class="w"&gt; &lt;/span&gt;template&amp;lt;typename CT&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;inline CT polyval(int N,
&lt;span class="gd"&gt;-                         const CT coeff[],&lt;/span&gt;
&lt;span class="gd"&gt;-                         const CT eps)&lt;/span&gt;
&lt;span class="gi"&gt;+                  const CT coeff[],&lt;/span&gt;
&lt;span class="gi"&gt;+                  const CT eps)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;{
&lt;span class="w"&gt; &lt;/span&gt;    CT y = N &amp;lt; 0 ? 0 : *coeff++;

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/util/series_expansion.hpp b/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index e3dc826..4bd09a1 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -636,9 +636,7 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;     using Clenshaw summation.
&lt;span class="w"&gt; &lt;/span&gt;    */
&lt;span class="w"&gt; &lt;/span&gt;    template &amp;lt;typename CT, size_t SeriesOrder&amp;gt;
&lt;span class="gd"&gt;-    static inline CT sin_cos_series(CT sinx,&lt;/span&gt;
&lt;span class="gd"&gt;-                                    CT cosx,&lt;/span&gt;
&lt;span class="gd"&gt;-                                    const CT coeffs[])&lt;/span&gt;
&lt;span class="gi"&gt;+    static inline CT sin_cos_series(CT sinx, CT cosx, const CT coeffs[])&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    {
&lt;span class="w"&gt; &lt;/span&gt;        size_t n = SeriesOrder;


commit 318a61dce0a1fc9256275ff97254d6264fba10c1
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Jun 15 18:15:03 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [formulas] Use updated functions for normalization

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/formulas/karney_direct.hpp b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index e64e1da..5e382f0 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -15,6 +15,7 @@&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;#include &amp;lt;boost/geometry/util/math.hpp&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;#include &amp;lt;boost/geometry/util/series_expansion.hpp&amp;gt;
&lt;span class="gi"&gt;+#include &amp;lt;boost/geometry/util/normalize_spheroidal_coordinates.hpp&amp;gt;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;#include &amp;lt;boost/geometry/formulas/flattening.hpp&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;#include &amp;lt;boost/geometry/formulas/result_direct.hpp&amp;gt;
&lt;span class="gu"&gt;@@ -57,9 +58,11 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    {
&lt;span class="w"&gt; &lt;/span&gt;        result_type result;

&lt;span class="gd"&gt;-        CT const lon1 = lo1;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT lon1 = lo1;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT const lat1 = la1;
&lt;span class="gd"&gt;-        Azi const azi12 = math::normalize_angle&amp;lt;CT&amp;gt;(azimuth12);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        Azi azi12 = azimuth12;&lt;/span&gt;
&lt;span class="gi"&gt;+        math::normalize_angle&amp;lt;degree, Azi&amp;gt;(azi12);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        CT const dist_c0 = 0;

&lt;span class="gu"&gt;@@ -90,7 +93,8 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        math::sin_cos_degrees&amp;lt;CT&amp;gt;(math::round_angle&amp;lt;CT&amp;gt;(lat1), sin_beta1, cos_beta1);
&lt;span class="w"&gt; &lt;/span&gt;        sin_beta1 *= one_minus_f;

&lt;span class="gd"&gt;-        math::normalize&amp;lt;CT&amp;gt;(sin_beta1, cos_beta1);&lt;/span&gt;
&lt;span class="gi"&gt;+        math::normalize_values&amp;lt;CT&amp;gt;(sin_beta1, cos_beta1);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        cos_beta1 = std::max(sqrt(std::numeric_limits&amp;lt;CT&amp;gt;::min()), cos_beta1);

&lt;span class="w"&gt; &lt;/span&gt;        // Obtain alpha 0 by solving the spherical triangle.
&lt;span class="gu"&gt;@@ -123,7 +127,7 @@ public:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        CT cos_sigma1, cos_omega1;
&lt;span class="w"&gt; &lt;/span&gt;        cos_sigma1 = cos_omega1 = sin_beta1 != 0 || cos_alpha1 != 0 ? cos_beta1 * cos_alpha1 : 1;
&lt;span class="gd"&gt;-        math::normalize&amp;lt;CT&amp;gt;(sin_sigma1, cos_sigma1);&lt;/span&gt;
&lt;span class="gi"&gt;+        math::normalize_values&amp;lt;CT&amp;gt;(sin_sigma1, cos_sigma1);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        CT const B11 =
&lt;span class="w"&gt; &lt;/span&gt;            series_expansion::sin_cos_series&amp;lt;CT, SeriesOrder&amp;gt;(sin_sigma1, cos_sigma1, coeffs_C1);
&lt;span class="gu"&gt;@@ -211,12 +215,15 @@ public:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            // Convert to radians to get the
&lt;span class="w"&gt; &lt;/span&gt;            // longitudinal difference.
&lt;span class="gd"&gt;-            CT const lon12 = lam12 / math::d2r&amp;lt;T&amp;gt;();&lt;/span&gt;
&lt;span class="gi"&gt;+            CT lon12 = lam12 / math::d2r&amp;lt;T&amp;gt;();&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            // Add the longitude at first point to the longitudinal
&lt;span class="w"&gt; &lt;/span&gt;            // difference and normalize the result.
&lt;span class="gd"&gt;-            result.lon2 = math::normalize_angle(math::normalize_angle(lon1) +&lt;/span&gt;
&lt;span class="gd"&gt;-                                                math::normalize_angle(lon12));&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            math::normalize_angle&amp;lt;degree, CT&amp;gt;(lon1);&lt;/span&gt;
&lt;span class="gi"&gt;+            math::normalize_angle&amp;lt;degree, CT&amp;gt;(lon12);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            result.lon2 = lon1 + lon12;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        }

&lt;span class="w"&gt; &lt;/span&gt;        if (BOOST_GEOMETRY_CONDITION(CalcQuantities))
&lt;span class="gh"&gt;diff --git a/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp b/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 7a0e8e4..04de04d 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -383,7 +383,7 @@ inline void normalize_angle(CoordinateType&amp;amp; angle)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;\param x Value x
&lt;span class="w"&gt; &lt;/span&gt;\param y Value y
&lt;span class="w"&gt; &lt;/span&gt;TODO: adl1995 - Merge this function with
&lt;span class="gd"&gt;-formulas/vertex_latitude.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+formulas/vertex_longitude.hpp&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;*/
&lt;span class="w"&gt; &lt;/span&gt;template&amp;lt;typename ValueType&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;inline void normalize_values(ValueType&amp;amp; x, ValueType&amp;amp; y)

commit d591d7a7bb7aa5d921f53e14e1198fb698cd2558
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Jun 15 18:08:39 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [util] Use existing normalize_spheroidal_coordinates class for normalizing an angle

&lt;span class="w"&gt; &lt;/span&gt;   - This should normalize the given angle in range (-180, 180].
&lt;span class="w"&gt; &lt;/span&gt;   - The function normalize is also moved from math.hpp to
&lt;span class="w"&gt; &lt;/span&gt;   normalize_spheroidal_coordinates.hpp where it is renamed
&lt;span class="w"&gt; &lt;/span&gt;   to normalize_values

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/util/math.hpp b/include/boost/geometry/util/math.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index e0ab1c8..6ba51b2 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/util/math.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/util/math.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -829,29 +829,6 @@ inline T round_angle(T x) {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    return x &amp;lt; 0 ? -y : y;
&lt;span class="w"&gt; &lt;/span&gt;}

&lt;span class="gd"&gt;-/*!&lt;/span&gt;
&lt;span class="gd"&gt;-\brief Normalize the given values.&lt;/span&gt;
&lt;span class="gd"&gt;-*/&lt;/span&gt;
&lt;span class="gd"&gt;-template&amp;lt;typename T&amp;gt;&lt;/span&gt;
&lt;span class="gd"&gt;-inline void normalize(T&amp;amp; x, T&amp;amp; y)&lt;/span&gt;
&lt;span class="gd"&gt;-{&lt;/span&gt;
&lt;span class="gd"&gt;-    T h = boost::math::hypot(x, y);&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    x /= h; y /= h;&lt;/span&gt;
&lt;span class="gd"&gt;-}&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-/*!&lt;/span&gt;
&lt;span class="gd"&gt;-\brief Normalize a given angle.&lt;/span&gt;
&lt;span class="gd"&gt;-*/&lt;/span&gt;
&lt;span class="gd"&gt;-template&amp;lt;typename T&amp;gt;&lt;/span&gt;
&lt;span class="gd"&gt;-    inline T normalize_angle(T x)&lt;/span&gt;
&lt;span class="gd"&gt;-{&lt;/span&gt;
&lt;span class="gd"&gt;-    T y = std::fmod(x, T(360));&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    return y &amp;lt;= -180 ? y + 360 : (y &amp;lt;= 180 ? y : y - 360);&lt;/span&gt;
&lt;span class="gd"&gt;-}&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;/*
&lt;span class="w"&gt; &lt;/span&gt;\brief Evaluate the polynomial in x using Horner&amp;#39;s method.
&lt;span class="w"&gt; &lt;/span&gt;*/
&lt;span class="gh"&gt;diff --git a/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp b/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 785e52a..7a0e8e4 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -363,6 +363,35 @@ inline void normalize_longitude(CoordinateType&amp;amp; longitude)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;gt;::apply(longitude);
&lt;span class="w"&gt; &lt;/span&gt;}

&lt;span class="gi"&gt;+/*!&lt;/span&gt;
&lt;span class="gi"&gt;+\brief Short utility to normalize an angle on a spheroid&lt;/span&gt;
&lt;span class="gi"&gt;+       normalized in range (-180, 180].&lt;/span&gt;
&lt;span class="gi"&gt;+\tparam Units The units of the coordindate system in the spheroid&lt;/span&gt;
&lt;span class="gi"&gt;+\tparam CoordinateType The type of the coordinates&lt;/span&gt;
&lt;span class="gi"&gt;+\param angle Angle&lt;/span&gt;
&lt;span class="gi"&gt;+\ingroup utility&lt;/span&gt;
&lt;span class="gi"&gt;+*/&lt;/span&gt;
&lt;span class="gi"&gt;+template &amp;lt;typename Units, typename CoordinateType&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+inline void normalize_angle(CoordinateType&amp;amp; angle)&lt;/span&gt;
&lt;span class="gi"&gt;+{&lt;/span&gt;
&lt;span class="gi"&gt;+    normalize_longitude&amp;lt;Units, CoordinateType&amp;gt;(angle);&lt;/span&gt;
&lt;span class="gi"&gt;+}&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+/*!&lt;/span&gt;
&lt;span class="gi"&gt;+\brief Normalize the given values.&lt;/span&gt;
&lt;span class="gi"&gt;+\tparam ValueType The type of the values&lt;/span&gt;
&lt;span class="gi"&gt;+\param x Value x&lt;/span&gt;
&lt;span class="gi"&gt;+\param y Value y&lt;/span&gt;
&lt;span class="gi"&gt;+TODO: adl1995 - Merge this function with&lt;/span&gt;
&lt;span class="gi"&gt;+formulas/vertex_latitude.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+*/&lt;/span&gt;
&lt;span class="gi"&gt;+template&amp;lt;typename ValueType&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+inline void normalize_values(ValueType&amp;amp; x, ValueType&amp;amp; y)&lt;/span&gt;
&lt;span class="gi"&gt;+{&lt;/span&gt;
&lt;span class="gi"&gt;+    ValueType h = boost::math::hypot(x, y);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    x /= h; y /= h;&lt;/span&gt;
&lt;span class="gi"&gt;+}&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;/*!
&lt;span class="w"&gt; &lt;/span&gt;\brief Short utility to calculate difference between two longitudes

commit 06fc06dc7faeb9008721a8b6b5120c47e0eaf312
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Thu Jun 14 13:42:44 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [formulas] Add function lambda12 to regulate bracketing range in Karney inverse

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/formulas/karney_inverse.hpp b/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 4e988b3..e4ebb65 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -59,6 +59,8 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    static CT constexpr c3 = 3;
&lt;span class="w"&gt; &lt;/span&gt;    static CT constexpr c4 = 4;
&lt;span class="w"&gt; &lt;/span&gt;    static CT constexpr c6 = 6;
&lt;span class="gi"&gt;+    static CT constexpr c10 = 10;&lt;/span&gt;
&lt;span class="gi"&gt;+    static CT constexpr c20 = 20;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    static CT constexpr c90 = 90;
&lt;span class="w"&gt; &lt;/span&gt;    static CT constexpr c180 = 180;
&lt;span class="w"&gt; &lt;/span&gt;    static CT constexpr c200 = 200;
&lt;span class="gu"&gt;@@ -274,13 +276,61 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            // Find the starting point for Newton&amp;#39;s method.
&lt;span class="w"&gt; &lt;/span&gt;            CT dnm;
&lt;span class="w"&gt; &lt;/span&gt;            sigma12 = newton_start(sin_beta1, cos_beta1, dn1,
&lt;span class="gd"&gt;-                                 sin_beta2, cos_beta2, dn2,&lt;/span&gt;
&lt;span class="gd"&gt;-                                 lam12, sin_lam12, cos_lam12,&lt;/span&gt;
&lt;span class="gd"&gt;-                                 sin_alpha1, cos_alpha1,&lt;/span&gt;
&lt;span class="gd"&gt;-                                 sin_alpha2, cos_alpha2,&lt;/span&gt;
&lt;span class="gd"&gt;-                                 dnm, coeffs_C1, ep2,&lt;/span&gt;
&lt;span class="gd"&gt;-                                 tol1, tol2, etol2,&lt;/span&gt;
&lt;span class="gd"&gt;-                                 n, f);&lt;/span&gt;
&lt;span class="gi"&gt;+                                   sin_beta2, cos_beta2, dn2,&lt;/span&gt;
&lt;span class="gi"&gt;+                                   lam12, sin_lam12, cos_lam12,&lt;/span&gt;
&lt;span class="gi"&gt;+                                   sin_alpha1, cos_alpha1,&lt;/span&gt;
&lt;span class="gi"&gt;+                                   sin_alpha2, cos_alpha2,&lt;/span&gt;
&lt;span class="gi"&gt;+                                   dnm, coeffs_C1, ep2,&lt;/span&gt;
&lt;span class="gi"&gt;+                                   tol1, tol2, etol2,&lt;/span&gt;
&lt;span class="gi"&gt;+                                   n, f);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            if (sigma12 &amp;gt;= c0)&lt;/span&gt;
&lt;span class="gi"&gt;+            {&lt;/span&gt;
&lt;span class="gi"&gt;+                // Short lines case (newton_start sets salp2, calp2, dnm)&lt;/span&gt;
&lt;span class="gi"&gt;+                s12x = sigma12 * b * dnm;&lt;/span&gt;
&lt;span class="gi"&gt;+                m12x = math::sqr(dnm) * b * sin(sigma12 / dnm);&lt;/span&gt;
&lt;span class="gi"&gt;+                if (BOOST_GEOMETRY_CONDITION(EnableGeodesicScale))&lt;/span&gt;
&lt;span class="gi"&gt;+                {&lt;/span&gt;
&lt;span class="gi"&gt;+                    result.geodesic_scale = cos(sigma12 / dnm);&lt;/span&gt;
&lt;span class="gi"&gt;+                }&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+                // Convert to radians.&lt;/span&gt;
&lt;span class="gi"&gt;+                a12 = sigma12 / math::d2r&amp;lt;CT&amp;gt;();&lt;/span&gt;
&lt;span class="gi"&gt;+                omega12 = lam12 / (one_minus_f * dnm);&lt;/span&gt;
&lt;span class="gi"&gt;+            }&lt;/span&gt;
&lt;span class="gi"&gt;+            else&lt;/span&gt;
&lt;span class="gi"&gt;+            {&lt;/span&gt;
&lt;span class="gi"&gt;+                // Apply the Newton&amp;#39;s method.&lt;/span&gt;
&lt;span class="gi"&gt;+                CT sin_sigma1 = c0, cos_sigma1 = c0;&lt;/span&gt;
&lt;span class="gi"&gt;+                CT sin_sigma2 = c0, cos_sigma2 = c0;&lt;/span&gt;
&lt;span class="gi"&gt;+                CT eps = c0, diff_omega12 = c0;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+                // Bracketing range.&lt;/span&gt;
&lt;span class="gi"&gt;+                CT sin_alpha1a = tiny, cos_alpha1a = c1;&lt;/span&gt;
&lt;span class="gi"&gt;+                CT sin_alpha1b = tiny, cos_alpha1b = -c1;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+                size_t iteration = 0;&lt;/span&gt;
&lt;span class="gi"&gt;+                size_t max_iterations = 20 + std::numeric_limits&amp;lt;size_t&amp;gt;::digits + 10;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+                for (bool tripn = false, tripb = false;&lt;/span&gt;
&lt;span class="gi"&gt;+                     iteration &amp;lt; max_iterations;&lt;/span&gt;
&lt;span class="gi"&gt;+                     ++iteration)&lt;/span&gt;
&lt;span class="gi"&gt;+                {&lt;/span&gt;
&lt;span class="gi"&gt;+                    CT dv;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+                    CT v = lambda12(sin_beta1, cos_beta1, dn1,&lt;/span&gt;
&lt;span class="gi"&gt;+                                    sin_beta2, cos_beta2, dn2,&lt;/span&gt;
&lt;span class="gi"&gt;+                                    sin_alpha1, cos_alpha1,&lt;/span&gt;
&lt;span class="gi"&gt;+                                    sin_lam12, cos_lam12,&lt;/span&gt;
&lt;span class="gi"&gt;+                                    sin_alpha2, cos_alpha2,&lt;/span&gt;
&lt;span class="gi"&gt;+                                    sigma12,&lt;/span&gt;
&lt;span class="gi"&gt;+                                    sin_sigma1, cos_sigma1,&lt;/span&gt;
&lt;span class="gi"&gt;+                                    sin_sigma2, cos_sigma2,&lt;/span&gt;
&lt;span class="gi"&gt;+                                    eps, diff_omega12,&lt;/span&gt;
&lt;span class="gi"&gt;+                                    iteration &amp;lt; max_iterations,&lt;/span&gt;
&lt;span class="gi"&gt;+                                    dv, f, n, ep2, tiny, coeffs_C1);&lt;/span&gt;
&lt;span class="gi"&gt;+                }&lt;/span&gt;
&lt;span class="gi"&gt;+            }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="w"&gt; &lt;/span&gt;    }

&lt;span class="gu"&gt;@@ -550,7 +600,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    }

&lt;span class="w"&gt; &lt;/span&gt;    /*
&lt;span class="gd"&gt;-     Solve the astroid problem using this equation:&lt;/span&gt;
&lt;span class="gi"&gt;+     Solve the astroid problem using the equation:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;     κ4 + 2κ3 + (1 − x2 − y 2 )κ2 − 2y 2 κ − y 2 = 0.

&lt;span class="w"&gt; &lt;/span&gt;     For details, please refer to Eq. (65) in,
&lt;span class="gu"&gt;@@ -615,11 +665,132 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        else // q == 0 &amp;amp;&amp;amp; r &amp;lt;= 0
&lt;span class="w"&gt; &lt;/span&gt;        {
&lt;span class="w"&gt; &lt;/span&gt;            // y = 0 with |x| &amp;lt;= 1. Handle this case directly.
&lt;span class="gd"&gt;-            // For y small, positive root is k = abs(y)/sqrt(1-x^2)&lt;/span&gt;
&lt;span class="gi"&gt;+            // For y small, positive root is k = abs(y)/sqrt(1-x^2).&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            k = c0;
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="w"&gt; &lt;/span&gt;        return k;
&lt;span class="w"&gt; &lt;/span&gt;    }
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    static inline CT lambda12(CT sin_beta1, CT cos_beta1, CT dn1,&lt;/span&gt;
&lt;span class="gi"&gt;+                              CT sin_beta2, CT cos_beta2, CT dn2,&lt;/span&gt;
&lt;span class="gi"&gt;+                              CT sin_alpha1, CT cos_alpha1,&lt;/span&gt;
&lt;span class="gi"&gt;+                              CT sin_lam120, CT cos_lam120,&lt;/span&gt;
&lt;span class="gi"&gt;+                              CT&amp;amp; sin_alpha2, CT&amp;amp; cos_alpha2,&lt;/span&gt;
&lt;span class="gi"&gt;+                              CT&amp;amp; sigma12,&lt;/span&gt;
&lt;span class="gi"&gt;+                              CT&amp;amp; sin_sigma1, CT&amp;amp; cos_sigma1,&lt;/span&gt;
&lt;span class="gi"&gt;+                              CT&amp;amp; sin_sigma2, CT&amp;amp; cos_sigma2,&lt;/span&gt;
&lt;span class="gi"&gt;+                              CT&amp;amp; eps, CT&amp;amp; diff_omega12,&lt;/span&gt;
&lt;span class="gi"&gt;+                              bool diffp, CT&amp;amp; diff_lam12,&lt;/span&gt;
&lt;span class="gi"&gt;+                              CT f, CT n, CT ep2, CT tiny,&lt;/span&gt;
&lt;span class="gi"&gt;+                              CT coeffs_C1[])&lt;/span&gt;
&lt;span class="gi"&gt;+    {&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const one_minus_f = c1 - f;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        if (sin_beta1 == c0 &amp;amp;&amp;amp; cos_alpha1 == c0)&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            // Break degeneracy of equatorial line.&lt;/span&gt;
&lt;span class="gi"&gt;+            cos_alpha1 = -tiny;&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        CT sin_alpha0 = sin_alpha1 * cos_beta1;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT cos_alpha0 = boost::math::hypot(cos_alpha1, sin_alpha1 * sin_beta1);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        CT sin_omega1, cos_omega1;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT sin_omega2, cos_omega2;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT sin_omega12, cos_omega12;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        CT lam12;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        sin_sigma1 = sin_beta1;&lt;/span&gt;
&lt;span class="gi"&gt;+        sin_omega1 = sin_alpha0 * sin_beta1;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        cos_sigma1 = cos_omega1 =&lt;/span&gt;
&lt;span class="gi"&gt;+            cos_alpha1 * cos_beta1;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        math::normalize(sin_sigma1, cos_sigma1);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        // Enforce symmetries in the case abs(beta2) = -beta1.&lt;/span&gt;
&lt;span class="gi"&gt;+        // Otherwise, this can yield singularities in the Newton iteration.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        // sin(alpha2) * cos(beta2) = sin(alpha0).&lt;/span&gt;
&lt;span class="gi"&gt;+        sin_alpha2 = cos_beta2 != cos_beta1 ?&lt;/span&gt;
&lt;span class="gi"&gt;+            sin_alpha0 / cos_beta2 : sin_alpha1;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        cos_alpha2 = cos_beta2 != cos_beta1 || std::abs(sin_beta2) != -sin_beta1 ?&lt;/span&gt;
&lt;span class="gi"&gt;+            sqrt(math::sqr(cos_alpha1 * cos_beta1) +&lt;/span&gt;
&lt;span class="gi"&gt;+                (cos_beta1 &amp;lt; -sin_beta1 ?&lt;/span&gt;
&lt;span class="gi"&gt;+                    (cos_beta2 - cos_beta1) * (cos_beta1 + cos_beta2) :&lt;/span&gt;
&lt;span class="gi"&gt;+                    (sin_beta1 - sin_beta2) * (sin_beta1 + sin_beta2))) / cos_beta2 :&lt;/span&gt;
&lt;span class="gi"&gt;+            std::abs(cos_alpha1);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        sin_sigma2 = sin_beta2;&lt;/span&gt;
&lt;span class="gi"&gt;+        sin_omega2 = sin_alpha0 * sin_beta2;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        cos_sigma2 = cos_omega2 =&lt;/span&gt;
&lt;span class="gi"&gt;+            cos_alpha2 * cos_beta2;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        math::normalize(sin_sigma2, cos_sigma2);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        // sig12 = sig2 - sig1, limit to [0, pi].&lt;/span&gt;
&lt;span class="gi"&gt;+        sigma12 = atan2(std::max(c0, cos_sigma1 * sin_sigma2 - sin_sigma1 * cos_sigma2),&lt;/span&gt;
&lt;span class="gi"&gt;+                                cos_sigma1 * cos_sigma2 + sin_sigma1 * sin_sigma2);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        // omg12 = omg2 - omg1, limit to [0, pi].&lt;/span&gt;
&lt;span class="gi"&gt;+        sin_omega12 = std::max(c0, cos_omega1 * sin_omega2 - sin_omega1 * cos_omega2);&lt;/span&gt;
&lt;span class="gi"&gt;+        cos_omega12 = cos_omega1 * cos_omega2 + sin_omega1 * sin_omega2;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        // eta = omg12 - lam120.&lt;/span&gt;
&lt;span class="gi"&gt;+        CT eta = atan2(sin_omega12 * cos_lam120 - cos_omega12 * sin_lam120,&lt;/span&gt;
&lt;span class="gi"&gt;+                       cos_omega12 * cos_lam120 + sin_omega12 * sin_lam120);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        CT B312;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT k2 = math::sqr(cos_alpha0) * ep2;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        eps = k2 / (c2 * (c1 + std::sqrt(c1 + k2)) + k2);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        // Compute the size of coefficient array.&lt;/span&gt;
&lt;span class="gi"&gt;+        size_t const coeffs_C3_size = (SeriesOrder * (SeriesOrder - 1)) / 2;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT coeffs_C3x[coeffs_C3_size];&lt;/span&gt;
&lt;span class="gi"&gt;+        series_expansion::evaluate_coeffs_C3x&amp;lt;CT, SeriesOrder&amp;gt;(n, coeffs_C3x);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        // Evaluate C3 coefficients.&lt;/span&gt;
&lt;span class="gi"&gt;+        CT coeffs_C3[SeriesOrder];&lt;/span&gt;
&lt;span class="gi"&gt;+        series_expansion::evaluate_coeffs_C3&amp;lt;CT, SeriesOrder&amp;gt;(eps, coeffs_C3, coeffs_C3x);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        B312 = series_expansion::sin_cos_series&amp;lt;CT, SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+                   (sin_sigma2, cos_sigma2, coeffs_C3) -&lt;/span&gt;
&lt;span class="gi"&gt;+               series_expansion::sin_cos_series&amp;lt;CT, SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+                   (sin_sigma1, cos_sigma1, coeffs_C3);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        CT coeffs_A3[SeriesOrder];&lt;/span&gt;
&lt;span class="gi"&gt;+        series_expansion::evaluate_coeffs_A3&amp;lt;double, SeriesOrder&amp;gt;(n, coeffs_A3);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const A3 = math::horner_evaluate(eps, coeffs_A3, coeffs_A3 + SeriesOrder);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        diff_omega12 = -f * A3 * sin_alpha0 * (sigma12 + B312);&lt;/span&gt;
&lt;span class="gi"&gt;+        lam12 = eta + diff_omega12;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        if (diffp)&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            if (cos_alpha2 == c0)&lt;/span&gt;
&lt;span class="gi"&gt;+            {&lt;/span&gt;
&lt;span class="gi"&gt;+                diff_lam12 = - c2 * one_minus_f * dn1 / sin_beta1;&lt;/span&gt;
&lt;span class="gi"&gt;+            }&lt;/span&gt;
&lt;span class="gi"&gt;+            else&lt;/span&gt;
&lt;span class="gi"&gt;+            {&lt;/span&gt;
&lt;span class="gi"&gt;+                CT dummy;&lt;/span&gt;
&lt;span class="gi"&gt;+                meridian_length(n, eps, sigma12, sin_sigma1, cos_sigma1, dn1,&lt;/span&gt;
&lt;span class="gi"&gt;+                                                 sin_sigma2, cos_sigma2, dn2,&lt;/span&gt;
&lt;span class="gi"&gt;+                                                 cos_beta1, cos_beta2, dummy,&lt;/span&gt;
&lt;span class="gi"&gt;+                                                 diff_lam12, dummy, dummy,&lt;/span&gt;
&lt;span class="gi"&gt;+                                                 dummy, coeffs_C1);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+                diff_lam12 *= one_minus_f / (cos_alpha2 * cos_beta2);&lt;/span&gt;
&lt;span class="gi"&gt;+            }&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+        return lam12;&lt;/span&gt;
&lt;span class="gi"&gt;+    }&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;};

&lt;span class="w"&gt; &lt;/span&gt;}}} // namespace boost::geometry::formula

commit 642a84c551d78eb9973070d23fb7109694093741
Merge: 2aac402 943672b
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Thu Jun 14 13:34:29 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Merge branch &amp;#39;feature/geodesic_direct&amp;#39; into feature/karney_inverse

commit 943672bd3a7b2f6422cfa995ac94a04c73027c0e
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Thu Jun 14 13:27:33 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [formulas] Use template argument CT instead of double

&lt;span class="w"&gt; &lt;/span&gt;   This change is made in Karney&amp;#39;s direct method.

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/formulas/karney_direct.hpp b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index de101e9..e64e1da 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -186,7 +186,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                                     cos_omega2 * cos_omega1 + sin_omega2 * sin_omega1);

&lt;span class="w"&gt; &lt;/span&gt;            CT coeffs_A3[SeriesOrder];
&lt;span class="gd"&gt;-            series_expansion::evaluate_coeffs_A3&amp;lt;double, SeriesOrder&amp;gt;(n, coeffs_A3);&lt;/span&gt;
&lt;span class="gi"&gt;+            series_expansion::evaluate_coeffs_A3&amp;lt;CT, SeriesOrder&amp;gt;(n, coeffs_A3);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            CT const A3 = math::horner_evaluate(epsilon, coeffs_A3, coeffs_A3 + SeriesOrder);
&lt;span class="w"&gt; &lt;/span&gt;            CT const A3c = -f * sin_alpha0 * A3;
&lt;span class="gu"&gt;@@ -194,11 +194,11 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            // Compute the size of coefficient array.
&lt;span class="w"&gt; &lt;/span&gt;            size_t const coeffs_C3_size = (SeriesOrder * (SeriesOrder - 1)) / 2;
&lt;span class="w"&gt; &lt;/span&gt;            CT coeffs_C3x[coeffs_C3_size];
&lt;span class="gd"&gt;-            series_expansion::evaluate_coeffs_C3x&amp;lt;double, SeriesOrder&amp;gt;(n, coeffs_C3x);&lt;/span&gt;
&lt;span class="gi"&gt;+            series_expansion::evaluate_coeffs_C3x&amp;lt;CT, SeriesOrder&amp;gt;(n, coeffs_C3x);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            // Evaluate C3 coefficients.
&lt;span class="w"&gt; &lt;/span&gt;            CT coeffs_C3[SeriesOrder];
&lt;span class="gd"&gt;-            series_expansion::evaluate_coeffs_C3&amp;lt;double, SeriesOrder&amp;gt;(epsilon, coeffs_C3, coeffs_C3x);&lt;/span&gt;
&lt;span class="gi"&gt;+            series_expansion::evaluate_coeffs_C3&amp;lt;CT, SeriesOrder&amp;gt;(epsilon, coeffs_C3, coeffs_C3x);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            CT const B31 =
&lt;span class="w"&gt; &lt;/span&gt;                series_expansion::sin_cos_series&amp;lt;CT, SeriesOrder&amp;gt;(sin_sigma1, cos_sigma1, coeffs_C3);

commit 2aac4027aa514f3f9ea5ad4c883c12404d85d351
Merge: 15d5cd6 d11b716
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Thu Jun 14 11:54:09 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Merge branch &amp;#39;feature/geodesic_direct&amp;#39; into feature/karney_inverse

commit d11b7160b2460f78830844c6892af2a94f6771a6
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Wed Jun 13 12:26:56 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [formulas] Fix calculation of t for finding the geodesic scale (M12)

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/formulas/karney_direct.hpp b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 6f63cb1..de101e9 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -250,7 +250,7 @@ public:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            // Find the geodesic scale.
&lt;span class="w"&gt; &lt;/span&gt;            CT const t = k2 * (sin_sigma2 - sin_sigma1) *
&lt;span class="gd"&gt;-                              (sin_sigma2 * sin_sigma1) / (dn1 + dn2);&lt;/span&gt;
&lt;span class="gi"&gt;+                              (sin_sigma2 + sin_sigma1) / (dn1 + dn2);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            result.geodesic_scale = cos_sigma12 +
&lt;span class="w"&gt; &lt;/span&gt;                                    (t * sin_sigma2 - cos_sigma2 * J12) *

commit 15d5cd628100ef4f605b5b9ae5ee7aa484f33af8
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Tue Jun 12 18:32:26 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [formulas] Perform normalization on starting guess if it passes the sanity check

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/formulas/karney_inverse.hpp b/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 2ad4505..4e988b3 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -534,6 +534,19 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                    math::sqr(sin_omega12) / (c1 - cos_omega12);
&lt;span class="w"&gt; &lt;/span&gt;            }
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        // Apply sanity check on starting guess. Backwards check allows NaN through.&lt;/span&gt;
&lt;span class="gi"&gt;+        if (!(sin_alpha1 &amp;lt;= c0))&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+          math::normalize&amp;lt;CT&amp;gt;(sin_alpha1, cos_alpha1);&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+        else&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+          sin_alpha1 = c1;&lt;/span&gt;
&lt;span class="gi"&gt;+          cos_alpha1 = c0;&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        return sig12;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    }

&lt;span class="w"&gt; &lt;/span&gt;    /*

commit 2bde1c119a3dc04ce3c24261712e894824ff3d05
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Tue Jun 12 18:27:29 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [formulas] Solve the astroid equation for inverse problem

&lt;span class="w"&gt; &lt;/span&gt;   For details, please refer to Eq. (65) in,
&lt;span class="w"&gt; &lt;/span&gt;   Geodesics on an ellipsoid of revolution, Charles F.F Karney,
&lt;span class="w"&gt; &lt;/span&gt;   https://arxiv.org/abs/1102.1215

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/formulas/karney_inverse.hpp b/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 253198a..2ad4505 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -57,10 +57,12 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    static CT constexpr c1 = 1;
&lt;span class="w"&gt; &lt;/span&gt;    static CT constexpr c2 = 2;
&lt;span class="w"&gt; &lt;/span&gt;    static CT constexpr c3 = 3;
&lt;span class="gi"&gt;+    static CT constexpr c4 = 4;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    static CT constexpr c6 = 6;
&lt;span class="w"&gt; &lt;/span&gt;    static CT constexpr c90 = 90;
&lt;span class="w"&gt; &lt;/span&gt;    static CT constexpr c180 = 180;
&lt;span class="w"&gt; &lt;/span&gt;    static CT constexpr c200 = 200;
&lt;span class="gi"&gt;+    static CT constexpr c1000 = 1000;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    template &amp;lt;typename T1, typename T2, typename Spheroid&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;    static inline result_type apply(T1 const&amp;amp; lo1,
&lt;span class="gu"&gt;@@ -276,7 +278,9 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                                 lam12, sin_lam12, cos_lam12,
&lt;span class="w"&gt; &lt;/span&gt;                                 sin_alpha1, cos_alpha1,
&lt;span class="w"&gt; &lt;/span&gt;                                 sin_alpha2, cos_alpha2,
&lt;span class="gd"&gt;-                                 dnm, coeffs_C1, ep2, etol2, n, f);&lt;/span&gt;
&lt;span class="gi"&gt;+                                 dnm, coeffs_C1, ep2,&lt;/span&gt;
&lt;span class="gi"&gt;+                                 tol1, tol2, etol2,&lt;/span&gt;
&lt;span class="gi"&gt;+                                 n, f);&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="w"&gt; &lt;/span&gt;    }

&lt;span class="gu"&gt;@@ -395,9 +399,10 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                                  CT&amp;amp; sin_alpha1, CT&amp;amp; cos_alpha1,
&lt;span class="w"&gt; &lt;/span&gt;                                  CT&amp;amp; sin_alpha2, CT&amp;amp; cos_alpha2,
&lt;span class="w"&gt; &lt;/span&gt;                                  CT&amp;amp; dnm, CT coeffs_C1[], CT ep2,
&lt;span class="gd"&gt;-                                  CT etol2, CT n, CT f)&lt;/span&gt;
&lt;span class="gi"&gt;+                                  CT tol1, CT tol2, CT etol2, CT n, CT f)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    {
&lt;span class="w"&gt; &lt;/span&gt;        CT const one_minus_f = c1 - f;
&lt;span class="gi"&gt;+        CT const x_thresh = c1000 * tol2;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT sig12 = -c1;

&lt;span class="w"&gt; &lt;/span&gt;        CT sin_beta12 = sin_beta2 * cos_beta1 - cos_beta2 * sin_beta1;
&lt;span class="gu"&gt;@@ -497,7 +502,110 @@ public:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;                y = lam12x / lambda_scale;
&lt;span class="w"&gt; &lt;/span&gt;            }
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            if (y &amp;gt; -tol1 &amp;amp;&amp;amp; x &amp;gt; -c1 - x_thresh)&lt;/span&gt;
&lt;span class="gi"&gt;+            {&lt;/span&gt;
&lt;span class="gi"&gt;+                // Strip near cut.&lt;/span&gt;
&lt;span class="gi"&gt;+                if (f &amp;gt;= c0)&lt;/span&gt;
&lt;span class="gi"&gt;+                {&lt;/span&gt;
&lt;span class="gi"&gt;+                    sin_alpha1 = std::min(c1, -CT(x));&lt;/span&gt;
&lt;span class="gi"&gt;+                    cos_alpha1 = - std::sqrt(c1 - math::sqr(sin_alpha1));&lt;/span&gt;
&lt;span class="gi"&gt;+                }&lt;/span&gt;
&lt;span class="gi"&gt;+                else&lt;/span&gt;
&lt;span class="gi"&gt;+                {&lt;/span&gt;
&lt;span class="gi"&gt;+                    cos_alpha1 = std::max(x &amp;gt; -tol1 ? c0 : -c1, CT(x));&lt;/span&gt;
&lt;span class="gi"&gt;+                    sin_alpha1 = std::sqrt(c1 - math::sqr(cos_alpha1));&lt;/span&gt;
&lt;span class="gi"&gt;+                }&lt;/span&gt;
&lt;span class="gi"&gt;+            }&lt;/span&gt;
&lt;span class="gi"&gt;+            else&lt;/span&gt;
&lt;span class="gi"&gt;+            {&lt;/span&gt;
&lt;span class="gi"&gt;+                // Solve the astroid problem.&lt;/span&gt;
&lt;span class="gi"&gt;+                CT k = astroid(x, y);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+                CT omega12a = lambda_scale * (f &amp;gt;= c0 ? -x * k /&lt;/span&gt;
&lt;span class="gi"&gt;+                    (c1 + k) : -y * (c1 + k) / k);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+                CT sin_omega12 = sin(omega12a);&lt;/span&gt;
&lt;span class="gi"&gt;+                CT cos_omega12 = -cos(omega12a);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+                // Update spherical estimate of alpha1 using omgega12 instead of lam12.&lt;/span&gt;
&lt;span class="gi"&gt;+                sin_alpha1 = cos_beta2 * sin_omega12;&lt;/span&gt;
&lt;span class="gi"&gt;+                cos_alpha1 = sin_beta12a - cos_beta2 * sin_beta1 *&lt;/span&gt;
&lt;span class="gi"&gt;+                    math::sqr(sin_omega12) / (c1 - cos_omega12);&lt;/span&gt;
&lt;span class="gi"&gt;+            }&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+    }&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    /*&lt;/span&gt;
&lt;span class="gi"&gt;+     Solve the astroid problem using this equation:&lt;/span&gt;
&lt;span class="gi"&gt;+     κ4 + 2κ3 + (1 − x2 − y 2 )κ2 − 2y 2 κ − y 2 = 0.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+     For details, please refer to Eq. (65) in,&lt;/span&gt;
&lt;span class="gi"&gt;+     Geodesics on an ellipsoid of revolution, Charles F.F Karney,&lt;/span&gt;
&lt;span class="gi"&gt;+     https://arxiv.org/abs/1102.1215&lt;/span&gt;
&lt;span class="gi"&gt;+    */&lt;/span&gt;
&lt;span class="gi"&gt;+    static inline CT astroid(CT x, CT y)&lt;/span&gt;
&lt;span class="gi"&gt;+    {&lt;/span&gt;
&lt;span class="gi"&gt;+        CT k;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        CT p = math::sqr(x);&lt;/span&gt;
&lt;span class="gi"&gt;+        CT q = math::sqr(y);&lt;/span&gt;
&lt;span class="gi"&gt;+        CT r = (p + q - c1) / c6;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        if (!(q == c0 &amp;amp;&amp;amp; r &amp;lt;= c0))&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            // Avoid possible division by zero when r = 0 by multiplying&lt;/span&gt;
&lt;span class="gi"&gt;+            // equations for s and t by r^3 and r, respectively.&lt;/span&gt;
&lt;span class="gi"&gt;+            CT S = p * q / c4;&lt;/span&gt;
&lt;span class="gi"&gt;+            CT r2 = math::sqr(r);&lt;/span&gt;
&lt;span class="gi"&gt;+            CT r3 = r * r2;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            // The discriminant of the quadratic equation for T3. This is&lt;/span&gt;
&lt;span class="gi"&gt;+            // zero on the evolute curve p^(1/3)+q^(1/3) = 1.&lt;/span&gt;
&lt;span class="gi"&gt;+            CT discriminant = S * (S + c2 * r3);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            CT u = r;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            if (discriminant &amp;gt;= c0)&lt;/span&gt;
&lt;span class="gi"&gt;+            {&lt;/span&gt;
&lt;span class="gi"&gt;+                CT T3 = S + r3;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+                // Pick the sign on the sqrt to maximize abs(T3). This minimizes&lt;/span&gt;
&lt;span class="gi"&gt;+                // loss of precision due to cancellation. The result is unchanged&lt;/span&gt;
&lt;span class="gi"&gt;+                // because of the way the T is used in definition of u.&lt;/span&gt;
&lt;span class="gi"&gt;+                T3 += T3 &amp;lt; c0 ? -std::sqrt(discriminant) : std::sqrt(discriminant);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+                CT T = std::cbrt(T3);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+                // T can be zero; but then r2 / T -&amp;gt; 0.&lt;/span&gt;
&lt;span class="gi"&gt;+                u += T + (T != c0 ? r2 / T : c0);&lt;/span&gt;
&lt;span class="gi"&gt;+            }&lt;/span&gt;
&lt;span class="gi"&gt;+            else&lt;/span&gt;
&lt;span class="gi"&gt;+            {&lt;/span&gt;
&lt;span class="gi"&gt;+                CT ang = std::atan2(std::sqrt(-discriminant), -(S + r3));&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+                // There are three possible cube roots. We choose the root which avoids&lt;/span&gt;
&lt;span class="gi"&gt;+                // cancellation. Note that discriminant &amp;lt; 0 implies that r &amp;lt; 0.&lt;/span&gt;
&lt;span class="gi"&gt;+                u += c2 * r * cos(ang / c3);&lt;/span&gt;
&lt;span class="gi"&gt;+            }&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            CT v = std::sqrt(math::sqr(u) + q);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            // Avoid loss of accuracy when u &amp;lt; 0.&lt;/span&gt;
&lt;span class="gi"&gt;+            CT uv = u &amp;lt; c0 ? q / (v - u) : u + v;&lt;/span&gt;
&lt;span class="gi"&gt;+            CT w = (uv - q) / (c2 * v);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            // Rearrange expression for k to avoid loss of accuracy due to&lt;/span&gt;
&lt;span class="gi"&gt;+            // subtraction. Division by 0 not possible because uv &amp;gt; 0, w &amp;gt;= 0.&lt;/span&gt;
&lt;span class="gi"&gt;+            k = uv / (std::sqrt(uv + math::sqr(w)) + w);&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+        else // q == 0 &amp;amp;&amp;amp; r &amp;lt;= 0&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            // y = 0 with |x| &amp;lt;= 1. Handle this case directly.&lt;/span&gt;
&lt;span class="gi"&gt;+            // For y small, positive root is k = abs(y)/sqrt(1-x^2)&lt;/span&gt;
&lt;span class="gi"&gt;+            k = c0;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="gi"&gt;+        return k;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    }
&lt;span class="w"&gt; &lt;/span&gt;};


commit 5bb581c932295f0243386cb9504c8b7409bbab2b
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Tue Jun 12 15:44:07 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [formulas] Flip sign of cos_lam12

&lt;span class="w"&gt; &lt;/span&gt;   This fixes the inaccuracy caused during the
&lt;span class="w"&gt; &lt;/span&gt;   calculation of Newton&amp;#39;s starting point.

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/formulas/karney_inverse.hpp b/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 0f3c686..253198a 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -91,6 +91,7 @@ public:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        CT tiny = std::sqrt(std::numeric_limits&amp;lt;CT&amp;gt;::min());

&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT const n = f / two_minus_f;
&lt;span class="w"&gt; &lt;/span&gt;        CT const e2 = f * two_minus_f;
&lt;span class="w"&gt; &lt;/span&gt;        CT const ep2 = e2 / math::sqr(one_minus_f);
&lt;span class="gu"&gt;@@ -110,8 +111,15 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT sin_lam12;
&lt;span class="w"&gt; &lt;/span&gt;        CT cos_lam12;

&lt;span class="gd"&gt;-        lon12 &amp;gt; c90 ? math::sin_cos_degrees(lon12_error, sin_lam12, cos_lam12)&lt;/span&gt;
&lt;span class="gd"&gt;-                    : math::sin_cos_degrees(lon12, sin_lam12, cos_lam12);&lt;/span&gt;
&lt;span class="gi"&gt;+        if (lon12 &amp;gt; c90)&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            math::sin_cos_degrees(lon12_error, sin_lam12, cos_lam12);&lt;/span&gt;
&lt;span class="gi"&gt;+            cos_lam12 *= -c1;&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+        else&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            math::sin_cos_degrees(lon12, sin_lam12, cos_lam12);&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        // Make points close to the equator to lie on it.
&lt;span class="w"&gt; &lt;/span&gt;        lat1 = std::abs(lat1) &amp;gt; 90 ? math::NaN&amp;lt;CT&amp;gt;() : lat1;
&lt;span class="gu"&gt;@@ -421,8 +429,6 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        }

&lt;span class="w"&gt; &lt;/span&gt;        sin_alpha1 = cos_beta2 * sin_omega12;
&lt;span class="gd"&gt;-        // TODO: adl1995 - Resolve inaccuracy with&lt;/span&gt;
&lt;span class="gd"&gt;-        // cos_alpha1 calculation.&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        cos_alpha1 = cos_omega12 &amp;gt;= c0 ?
&lt;span class="w"&gt; &lt;/span&gt;            sin_beta12 + cos_beta2 * sin_beta1 * math::sqr(sin_omega12) / (c1 + cos_omega12) :
&lt;span class="w"&gt; &lt;/span&gt;            sin_beta12a - cos_beta2 * sin_beta1 * math::sqr(sin_omega12) / (c1 - cos_omega12);

commit 71fbc86f44ec2d8ebf261808f233ec10cabf3260
Merge: 485a2f9 2cba2fa
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Tue Jun 12 15:42:41 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Merge branch &amp;#39;feature/geodesic_direct&amp;#39; into feature/karney_inverse

commit 2cba2fa83fe62c52efd5fc2d7fcb1401264701cf
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Tue Jun 12 12:00:52 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [test] Calculate geodesic scale (M12) using high precision arithmetic

&lt;span class="w"&gt; &lt;/span&gt;   Instructions for building GeographicLib are given here:
&lt;span class="w"&gt; &lt;/span&gt;   https://geographiclib.sourceforge.io/html/highprec.html

&lt;span class="w"&gt; &lt;/span&gt;   It requires the use of libquadmath package, which is present
&lt;span class="w"&gt; &lt;/span&gt;   in g++-7 by default. The cmake command used is:
&lt;span class="w"&gt; &lt;/span&gt;   cmake -DCMAKE_CXX_COMPILER=g++-7 -DGEOGRAPHICLIB_PRECISION=4 ..

&lt;span class="w"&gt; &lt;/span&gt;   Internally, GeographicLib makes use of float128 as part of Boost
&lt;span class="w"&gt; &lt;/span&gt;   Multiprecision library. To output full precision values, use:
&lt;span class="w"&gt; &lt;/span&gt;   std::setprecision(std::numeric_limits&amp;lt;float128&amp;gt;::max_digits10)

&lt;span class="gh"&gt;diff --git a/test/formulas/direct_cases_antipodal.hpp b/test/formulas/direct_cases_antipodal.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index fdbbc6e..ad5db5d 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/test/formulas/direct_cases_antipodal.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/test/formulas/direct_cases_antipodal.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -35,304 +35,304 @@ expected_results_antipodal expected_antipodal[] =&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;{
&lt;span class="w"&gt; &lt;/span&gt;    {
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 31.394417440639 }, 19980218.4055399, 34.266322930672,
&lt;span class="gd"&gt;-        { 179.615601631202912322, -31.275540610835465807, 145.782701113414306756, 49490.8807994496209, -0.9961164510125257909767434 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.615601631202912322, -31.275540610835465807, 145.782701113414306756, 49490.8807994496209, -0.996116451012525883079717914370121434 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 29.788792273749 }, 19887224.5407334, 74.302205994192,
&lt;span class="gd"&gt;-        { 178.569451327813675741, -29.558013672069422725, 106.156240654579267308, 97043.7545600593058, -0.9986240311478448829847661 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.569451327813675741, -29.558013672069422725, 106.156240654579267308, 97043.7545600593058, -0.998624031147844926081802441331092268 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 46.471843094141 }, 19944337.8863917, 63.693680310665,
&lt;span class="gd"&gt;-        { 179.083144618009561276, -46.284166405924629853, 116.699978859005570535, 53139.140576552365, -0.9975973096455918888284843 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.083144618009561276, -46.284166405924629853, 116.699978859005570535, 53139.140576552365, -0.997597309645591900917338534782174975 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 63.016506345929 }, 20000925.7533636, 153.393656073038,
&lt;span class="gd"&gt;-        { 179.862869954071637855, -63.02943882703369735, 26.619056019474552953, 12713.9284725111772, -1.003813177921433850612286 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.862869954071637855, -63.02943882703369735, 26.619056019474552953, 12713.9284725111772, -1.00381317792143387457315384381217882 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 19.796231412719 }, 19956338.1330537, 28.272934411318,
&lt;span class="gd"&gt;-        { 179.546498474461283862, -19.470586923091672503, 151.789094611690988249, 87191.1749625132931, -0.9970154090276648134395961 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.546498474461283862, -19.470586923091672503, 151.789094611690988249, 87191.1749625132931, -0.997015409027664833985227232915349305 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 6.373459459035 }, 19946581.6983394, 56.859050230583,
&lt;span class="gd"&gt;-        { 179.240009269347556917, -6.204887833274217382, 123.169200847008284851, 53958.8698005263939, -0.9993490490811010928734123 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.240009269347556917, -6.204887833274217382, 123.169200847008284851, 53958.8698005263939, -0.999349049081101004077254401636309922 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 66.380766469414 }, 19986277.7696849, 38.646950203356,
&lt;span class="gd"&gt;-        { 179.632633596894388233, -66.27177494016956425, 141.550919825824399405, 22198.215635049214, -0.996949176054954412282659 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.632633596894388233, -66.27177494016956425, 141.550919825824399405, 22198.215635049214, -0.996949176054954366854587988200364634 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 16.483421185231 }, 19962737.9842573, 163.431254767325,
&lt;span class="gd"&gt;-        { 179.731567273052604726, -16.818424446748042212, 16.598399455529231288, 95318.4104529881431, -1.002722102329797498345386 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.731567273052604726, -16.818424446748042212, 16.598399455529231288, 95318.4104529881431, -1.00272210232979741562076014815829694 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 4.215702155486 }, 19932517.393764, 65.543168480886,
&lt;span class="gd"&gt;-        { 179.093771177769992874, -4.051917290690976764, 114.482669479963380006, 55205.4553703842317, -0.9996558584250565483090949 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.093771177769992874, -4.051917290690976764, 114.482669479963380006, 55205.4553703842317, -0.999655858425056553784315838129259646 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 40.71372085907 }, 19951133.3595356, 143.672151631634,
&lt;span class="gd"&gt;-        { 179.404612926861498984, -41.047052242159400671, 36.54002600969304553, 70931.1530155553621, -1.004141695740772786027591 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.404612926861498984, -41.047052242159400671, 36.54002600969304553, 70931.1530155553621, -1.00414169574077272173440178448799998 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 15.465481491654 }, 19877383.8879911, 36.289185640976,
&lt;span class="gd"&gt;-        { 179.020726605204181801, -14.622355549425900341, 143.875673907461159912, 156419.0806764376957, -0.9976390743971695870329942 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.020726605204181801, -14.622355549425900341, 143.875673907461159912, 156419.0806764376957, -0.997639074397169589580869342171354219 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 17.586197343531 }, 19982280.4639115, 157.929615091529,
&lt;span class="gd"&gt;-        { 179.722490735835379144, -17.731394230364437075, 22.089021105298661023, 69727.5357849255557, -1.00280451698301228773396 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.722490735835379144, -17.731394230364437075, 22.089021105298661023, 69727.5357849255557, -1.00280451698301242835498214844847098 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 5.7442768247 }, 19902873.7431814, 116.146983678305,
&lt;span class="gd"&gt;-        { 178.85894724576868462, -6.039853564481335581, 63.91482549951374061, 87149.6188944111673, -1.000393328930967508458988 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.85894724576868462, -6.039853564481335581, 63.91482549951374061, 87149.6188944111673, -1.00039332893096744037109147029696032 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 32.002904282111 }, 19967670.3104795, 163.052160078191,
&lt;span class="gd"&gt;-        { 179.744925422107715439, -32.297934520693132807, 17.004175883388454943, 78311.3164829640582, -1.004499034453024313763049 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.744925422107715439, -32.297934520693132807, 17.004175883388454943, 78311.3164829640582, -1.00449903445302446414189034840092063 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 55.902716926362 }, 19970525.337607, 98.927641063414,
&lt;span class="gd"&gt;-        { 179.300685189522463007, -55.934320218634018206, 81.374264168520557301, 23554.0093185709067, -1.0007278877908344522649 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.300685189522463007, -55.934320218634018206, 81.374264168520557301, 23554.0093185709067, -1.00072788779083454713259015989024192 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 22.69939784398 }, 19959286.1903172, 74.253870776761,
&lt;span class="gd"&gt;-        { 179.294173474584020749, -22.654875407651067149, 105.811588890213155275, 22369.7179951557679, -0.9989721814190034790826959 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.294173474584020749, -22.654875407651067149, 105.811588890213155275, 22369.7179951557679, -0.998972181419003457669703038845909759 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 41.312328471121 }, 19962690.5721867, 11.277616109847,
&lt;span class="gd"&gt;-        { 179.817186837717804928, -40.954523601529804886, 168.784288786443902199, 77252.6121237260201, -0.9948251514715273688795397 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.817186837717804928, -40.954523601529804886, 168.784288786443902199, 77252.6121237260201, -0.994825151471527391322524636052548885 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 27.927415327453 }, 19961296.8828333, 23.166421459647,
&lt;span class="gd"&gt;-        { 179.636508875679110143, -27.607314264234172721, 156.905194492817275222, 83096.5801709291101, -0.9959596927676567156505726 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.636508875679110143, -27.607314264234172721, 156.905194492817275222, 83096.5801709291101, -0.995959692767656723511038308060960844 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 41.567228741451 }, 19944253.4454809, 176.66609526064,
&lt;span class="gd"&gt;-        { 179.931812964300204608, -42.103039532074194347, 3.361859685835349219, 96859.08180779197, -1.005136071404876275492239 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.931812964300204608, -42.103039532074194347, 3.361859685835349219, 96859.08180779197, -1.00513607140487626345759508694754913 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 37.384208978567 }, 19928705.5911445, 39.072534864532,
&lt;span class="gd"&gt;-        { 179.225180174670992261, -36.916085670712060029, 141.212743814390850106, 92667.7834060578402, -0.9959555168591592858788435 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.225180174670992261, -36.916085670712060029, 141.212743814390850106, 92667.7834060578402, -0.995955516859159284415170532156480476 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 59.011868682852 }, 19970442.3788306, 44.970301291063,
&lt;span class="gd"&gt;-        { 179.424923485514312807, -58.82705468054708336, 135.333817989802309531, 38071.1136293083857, -0.9966589428927073192592684 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.424923485514312807, -58.82705468054708336, 135.333817989802309531, 38071.1136293083857, -0.996658942892707400140750451100757346 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 35.515406087737 }, 19948918.9139751, 28.528972431952,
&lt;span class="gd"&gt;-        { 179.50369572149476218, -35.119747127350258822, 151.622257906284404073, 84564.0387217601751, -0.9955628617991693771129176 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.50369572149476218, -35.119747127350258822, 151.622257906284404073, 84564.0387217601751, -0.995562861799169418475230486365035176 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 58.170252463184 }, 19961407.0813807, 128.021116291844,
&lt;span class="gd"&gt;-        { 179.254737571455023977, -58.372261836268550805, 52.399129705193347143, 43715.3070711393309, -1.002852737132807475024451 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.254737571455023977, -58.372261836268550805, 52.399129705193347143, 43715.3070711393309, -1.00285273713280753682397516968194395 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 34.012183807959 }, 19970955.843065, 168.944519134772,
&lt;span class="gd"&gt;-        { 179.83713352180447672, -34.29640782899529639, 11.093048811826875835, 76493.5814538538151, -1.004765235455867102248688 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.83713352180447672, -34.29640782899529639, 11.093048811826875835, 76493.5814538538151, -1.0047652354558671561335359001532197 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 45.510762948553 }, 19940248.3450143, 99.886784003837,
&lt;span class="gd"&gt;-        { 178.981682578823726535, -45.582753595227824235, 80.542330522982505877, 48555.1946627894972, -1.000838077509063557589755 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.981682578823726535, -45.582753595227824235, 80.542330522982505877, 48555.1946627894972, -1.00083807750906350619857221317943186 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 4.19841765451 }, 19970496.5132933, 89.561550657928,
&lt;span class="gd"&gt;-        { 179.398024428225540172, -4.198416896099783242, 90.438456568689151881, 14.8790480103109, -0.9999941048102859313703554 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.398024428225540172, -4.198416896099783242, 90.438456568689151881, 14.8790480103109, -0.999994104810285944218151144013972953 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 40.890119148103 }, 19926563.5817492, 165.437641169967,
&lt;span class="gd"&gt;-        { 179.6557148951668192, -41.553556264538302258, 14.713597527941311478, 111805.7305227545923, -1.004922949334065759787078 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.6557148951668192, -41.553556264538302258, 14.713597527941311478, 111805.7305227545923, -1.00492294933406567380984597548376769 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 28.096672787686 }, 19883901.8482359, 115.174366374632,
&lt;span class="gd"&gt;-        { 178.606868012231657724, -28.472055035513955205, 65.257367020445564176, 107880.4353518862363, -1.001708030733315814243475 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.606868012231657724, -28.472055035513955205, 65.257367020445564176, 107880.4353518862363, -1.00170803073331593502359737613005564 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 6.50572154271 }, 19917276.4101551, 79.069492719523,
&lt;span class="gd"&gt;-        { 178.926013840891647541, -6.411745140559297675, 100.985091481519557845, 57073.3242952680707, -0.9997366669338085325645891 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.926013840891647541, -6.411745140559297675, 100.985091481519557845, 57073.3242952680707, -0.999736666933808471036115861352300271 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, .468835109567 }, 19849380.7342734, 80.234636214474,
&lt;span class="gd"&gt;-        { 178.325942223692180692, -.281751687044281805, 99.77243368342786593, 123845.4568822078908, -0.9998014372091407881673782 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.325942223692180692, -.281751687044281805, 99.77243368342786593, 123845.4568822078908, -0.999801437209140719808431185811059549 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 1.682746325049 }, 19890026.0274781, 10.076182752451,
&lt;span class="gd"&gt;-        { 179.717131561406935483, -.677647430701204515, 169.927471515299313238, 177917.2104306563981, -0.9995380556912621520019267 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.717131561406935483, -.677647430701204515, 169.927471515299313238, 177917.2104306563981, -0.999538055691262194990542866435134783 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 10.711305126218 }, 19962987.2134077, 7.528253696796,
&lt;span class="gd"&gt;-        { 179.874050163405229937, -10.349315378531556046, 172.480576051850009046, 104175.1095378254456, -0.9980718537552389151793625 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.874050163405229937, -10.349315378531556046, 172.480576051850009046, 104175.1095378254456, -0.998071853755238880268052525934763253 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 53.374321544652 }, 19980478.1457438, 23.324715976877,
&lt;span class="gd"&gt;-        { 179.729445806011012057, -53.196257519024042184, 156.777734080146664812, 41907.8869272231053, -0.9953335962777076279710611 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.729445806011012057, -53.196257519024042184, 156.777734080146664812, 41907.8869272231053, -0.995333596277707566279957518418086693 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 39.680221664519 }, 19956191.7841809, 7.075406493429,
&lt;span class="gd"&gt;-        { 179.87506206720154785, -39.256187213040660911, 172.967624741991546131, 86943.8110669895148, -0.9948010879096678981434003 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.87506206720154785, -39.256187213040660911, 172.967624741991546131, 86943.8110669895148, -0.994801087909667924868983845954062417 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 1.377666714083 }, 19925401.4931301, 95.29199069739,
&lt;span class="gd"&gt;-        { 178.994542525209058878, -1.415358715570225495, 84.7178724483824156, 45800.9140624827059, -0.9999980317051245028462866 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.994542525209058878, -1.415358715570225495, 84.7178724483824156, 45800.9140624827059, -0.99999803170512457928253979844157584 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 48.751426624188 }, 19988599.1160495, 40.252328570137,
&lt;span class="gd"&gt;-        { 179.661697715070846977, -48.688146707479475147, 139.808452951157199824, 26322.3790862461568, -0.9959992457241298392713741 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.661697715070846977, -48.688146707479475147, 139.808452951157199824, 26322.3790862461568, -0.995999245724129789181233718409202993 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 59.443039048494 }, 19969935.9534732, 93.052184108221,
&lt;span class="gd"&gt;-        { 179.247605418616998285, -59.454371825393424121, 87.331416513795326158, 25342.4691896499534, -1.000207278488970808263958 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.247605418616998285, -59.454371825393424121, 87.331416513795326158, 25342.4691896499534, -1.00020727848897084122370415570912883 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 4.122408476235 }, 19938291.6332293, 167.73479753304,
&lt;span class="gd"&gt;-        { 179.749430572914989772, -4.689124208743755363, 12.274635577599782826, 127855.6475863583497, -1.000686009028376572587218 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.749430572914989772, -4.689124208743755363, 12.274635577599782826, 127855.6475863583497, -1.00068600902837667732114823593292385 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 46.422470082432 }, 19931980.7029341, 86.67365350297,
&lt;span class="gd"&gt;-        { 178.857408435141563774, -46.390934261324541952, 93.852683224054943377, 56114.680046867064, -0.9996070961163002954936486 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.857408435141563774, -46.390934261324541952, 93.852683224054943377, 56114.680046867064, -0.999607096116300386512421027873642743 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 32.614423729024 }, 19926887.3785175, 24.943814520557,
&lt;span class="gd"&gt;-        { 179.460593512880455451, -32.01874745886238612, 155.229917137448282531, 112355.3319340873104, -0.9955621506768719286041558 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.460593512880455451, -32.01874745886238612, 155.229917137448282531, 112355.3319340873104, -0.995562150676871926435751447570510209 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 3.242895277973 }, 19964490.4789049, 30.247458779683,
&lt;span class="gd"&gt;-        { 179.556428318080663113, -3.001106476068264917, 149.760178923092147784, 80929.0418317066044, -0.9994741842703449041015323 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.556428318080663113, -3.001106476068264917, 149.760178923092147784, 80929.0418317066044, -0.999474184270344845337774586369050667 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 6.29069210113 }, 19877160.8505733, 94.34299459284,
&lt;span class="gd"&gt;-        { 178.556859259685624933, -6.354208910915346725, 85.750059038253282986, 94127.1566760840083, -0.9999763973509048521344338 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.556859259685624933, -6.354208910915346725, 85.750059038253282986, 94127.1566760840083, -0.999976397350904933070125935046235099 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 18.232086569498 }, 19927978.7462175, 164.41905055334,
&lt;span class="gd"&gt;-        { 179.658073278238477245, -18.87394850776853555, 15.640779355822506503, 129771.1882449660559, -1.002934604390638743953712 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.658073278238477245, -18.87394850776853555, 15.640779355822506503, 129771.1882449660559, -1.00293460439063886191490837518358603 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 12.049849333181 }, 19908004.4552909, 9.418096768309,
&lt;span class="gd"&gt;-        { 179.761046682699610657, -11.201990279782499264, 170.610608272305604585, 157761.5040571466343, -0.9977614744975109587398973 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.761046682699610657, -11.201990279782499264, 170.610608272305604585, 157761.5040571466343, -0.997761474497510958414636661473196 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 40.289465276136 }, 19985674.936106, 143.092606818963,
&lt;span class="gd"&gt;-        { 179.644208494155329095, -40.370034926441385999, 36.958610382613096419, 36200.8933724688593, -1.004149658760912620643173 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.644208494155329095, -40.370034926441385999, 36.958610382613096419, 36200.8933724688593, -1.00414965876091266672176516294712201 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 2.197784650379 }, 19910509.7517973, 1.542117609437,
&lt;span class="gd"&gt;-        { 179.961199531084784854, -1.353440827124394777, 178.458582198505846426, 160403.6285079348996, -0.999488724639301117833555 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.961199531084784854, -1.353440827124394777, 178.458582198505846426, 160403.6285079348996, -0.999488724639301051588802238256903365 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 1.966575272177 }, 19875595.6267266, 170.112968791865,
&lt;span class="gd"&gt;-        { 179.699817324905962184, -3.101125282483752618, 9.89572776349855838, 192355.7206665719908, -1.000154635898045583290543 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.699817324905962184, -3.101125282483752618, 9.89572776349855838, 192355.7206665719908, -1.00015463589804554089823795948177576 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 25.078832492684 }, 19887997.7953866, 77.264585323781,
&lt;span class="gd"&gt;-        { 178.600804840925824646, -24.897833702325682511, 103.101167809583406892, 92442.9124509225839, -0.9989811898386008840464503 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.600804840925824646, -24.897833702325682511, 103.101167809583406892, 92442.9124509225839, -0.998981189838600847075156252685701475 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 31.740361941314 }, 19972325.3556069, 143.930820896999,
&lt;span class="gd"&gt;-        { 179.553485210731879874, -31.909206787477701871, 36.145242998351638503, 54883.4113710054145, -1.003794616281159395249425 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.553485210731879874, -31.909206787477701871, 36.145242998351638503, 54883.4113710054145, -1.00379461628115951299378139083273709 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, .05479250563 }, 19858049.4780499, 41.349430623518,
&lt;span class="gd"&gt;-        { 178.822647462220726609, .836079031223269324, 138.645259065012502544, 169078.442370111714, -0.9997793696948588326408637 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.822647462220726609, .836079031223269324, 138.645259065012502544, 169078.442370111714, -0.9997793696948588104689292777038645 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 36.685139871608 }, 19968965.6773632, 89.167975517493,
&lt;span class="gd"&gt;-        { 179.366667224014334712, -36.6833040833258687, 90.921025521408327068, 13327.2156799476918, -0.9999165379463486296850866 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.366667224014334712, -36.6833040833258687, 90.921025521408327068, 13327.2156799476918, -0.999916537946348604748436628142371774 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 3.451199399671 }, 19938203.3838544, 91.541212417048,
&lt;span class="gd"&gt;-        { 179.107509334399258305, -3.459003521120242021, 88.476282464773035164, 32316.1747698810781, -1.000003974843958109899184 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.107509334399258305, -3.459003521120242021, 88.476282464773035164, 32316.1747698810781, -1.00000397484395819880376166111091152 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 27.692898794247 }, 19883493.6699045, 88.406440883665,
&lt;span class="gd"&gt;-        { 178.512356615673144314, -27.666009301228316555, 92.036345087713397961, 94128.7880896190836, -0.9997364583229516115064732 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.512356615673144314, -27.666009301228316555, 92.036345087713397961, 94128.7880896190836, -0.999736458322951659916100197733612731 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 17.363238291869 }, 19980749.7638027, 39.697196316589,
&lt;span class="gd"&gt;-        { 179.567921315455829491, -17.288872648596950413, 140.321938237586060826, 46975.9359427664379, -0.9976876919817150126995782 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.567921315455829491, -17.288872648596950413, 140.321938237586060826, 46975.9359427664379, -0.997687691981715030209443284547887743 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 37.006775102539 }, 19949309.9180043, 116.455543532607,
&lt;span class="gd"&gt;-        { 179.191103068859169842, -37.156365616364686838, 63.771817992036617793, 45856.1961421018701, -1.002219628589184219390543 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.191103068859169842, -37.156365616364686838, 63.771817992036617793, 45856.1961421018701, -1.00221962858918423044940482213860378 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 45.572883540957 }, 19940027.8586414, 137.627256708444,
&lt;span class="gd"&gt;-        { 179.224707765088686272, -45.94675931323086696, 42.723991162977357301, 74208.4359612889496, -1.003808877864471522261582 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.224707765088686272, -45.94675931323086696, 42.723991162977357301, 74208.4359612889496, -1.00380887786447159371050474874209613 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 43.63393981955 }, 19931045.2914508, 91.203625101465,
&lt;span class="gd"&gt;-        { 178.878236417027994157, -43.642335115130514773, 89.268780774643462256, 55253.5406349861764, -1.000029741531505212250378 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.878236417027994157, -43.642335115130514773, 89.268780774643462256, 55253.5406349861764, -1.00002974153150514524668324156664312 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 38.4995307019 }, 19918391.2222193, 141.232864609445,
&lt;span class="gd"&gt;-        { 179.143856004445269342, -39.042223438550921467, 39.117947060740562295, 102217.2563106863077, -1.003881641157329480734325 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.143856004445269342, -39.042223438550921467, 39.117947060740562295, 102217.2563106863077, -1.00388164115732947401227193040540442 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 27.55015339382 }, 19986004.7358853, 137.025135713548,
&lt;span class="gd"&gt;-        { 179.596220103573824099, -27.587412128122249651, 42.992898351962011956, 33938.7346646670654, -1.003160443902811658199205 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.596220103573824099, -27.587412128122249651, 42.992898351962011956, 33938.7346646670654, -1.00316044390281167153489150223322213 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 1.54507498314 }, 19978593.3191777, 36.816106412092,
&lt;span class="gd"&gt;-        { 179.567115633151308577, -1.448861185025252004, 143.185763012309022403, 56320.5800276739168, -0.9997704994624671810221454 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.567115633151308577, -1.448861185025252004, 143.185763012309022403, 56320.5800276739168, -0.999770499462467210349814195069484413 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 45.217063644222 }, 19987042.0782465, 18.114645812265,
&lt;span class="gd"&gt;-        { 179.807382581661125, -45.086424050571516283, 161.928120141429818658, 45544.2915061261936, -0.9949741794148549555321798 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.807382581661125, -45.086424050571516283, 161.928120141429818658, 45544.2915061261936, -0.994974179414854997816064496873877943 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 13.473522450751 }, 19987364.078382, 156.839609002403,
&lt;span class="gd"&gt;-        { 179.726941062277208626, -13.570372758027936877, 23.170293747820406391, 65329.9068132034472, -1.002190931895065541886065 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.726941062277208626, -13.570372758027936877, 23.170293747820406391, 65329.9068132034472, -1.00219093189506569530067281448282301 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 6.287741997374 }, 19912159.8245954, 132.954797451112,
&lt;span class="gd"&gt;-        { 179.071252372259552052, -6.743450924917895817, 47.100789519677419746, 104772.4027498097375, -1.00071252411103014544249 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.071252372259552052, -6.743450924917895817, 47.100789519677419746, 104772.4027498097375, -1.00071252411103017720961361192166805 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 7.639709001531 }, 19976374.3699535, 29.731916588299,
&lt;span class="gd"&gt;-        { 179.616156296978583335, -7.48702643786017917, 150.279582966919438164, 69224.6591757209539, -0.9987897920867413009931961 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.616156296978583335, -7.48702643786017917, 150.279582966919438164, 69224.6591757209539, -0.998789792086741234911073661351110786 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 5.893688050348 }, 19886907.2520668, 14.653438882877,
&lt;span class="gd"&gt;-        { 179.586212000450856399, -4.888408917114795625, 165.371181401863458848, 177183.5330818593022, -0.9987946470311208020708207 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.586212000450856399, -4.888408917114795625, 165.371181401863458848, 177183.5330818593022, -0.998794647031120752522781458537792787 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 61.997076235476 }, 19976288.2901729, 149.562797049254,
&lt;span class="gd"&gt;-        { 179.605779116829636081, -62.19593758437129915, 30.65850204223272625, 36696.2853801462176, -1.003730714324371446686916 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.605779116829636081, -62.19593758437129915, 30.65850204223272625, 36696.2853801462176, -1.00373071432437144245852778112748638 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 50.507637741656 }, 19979542.5263293, 171.564028344478,
&lt;span class="gd"&gt;-        { 179.893569206021038536, -50.721890799900161112, 8.4746613464253591, 50644.5234828162697, -1.005088816322817598953449 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.893569206021038536, -50.721890799900161112, 8.4746613464253591, 50644.5234828162697, -1.00508881632281776852266830246662721 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 7.484475238477 }, 19867425.2906303, 57.020570370985,
&lt;span class="gd"&gt;-        { 178.638400003000590878, -6.926155588124333461, 123.087267812322270238, 132929.2775641349633, -0.9990970426773380551267907 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.638400003000590878, -6.926155588124333461, 123.087267812322270238, 132929.2775641349633, -0.999097042677338120775232255255104974 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 56.851165323215 }, 19988235.9960515, 112.345749045605,
&lt;span class="gd"&gt;-        { 179.587046628550073045, -56.875248360744638525, 67.744017057185404441, 9971.0934553515518, -1.001828592498714131384455 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.587046628550073045, -56.875248360744638525, 67.744017057185404441, 9971.0934553515518, -1.00182859249871403228837607457535341 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 10.692273150738 }, 19893210.3050033, 102.824601316946,
&lt;span class="gd"&gt;-        { 178.709520715733071393, -10.851727623036704339, 77.308514969817191459, 83032.7122948051111, -1.000343455845084243900113 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.709520715733071393, -10.851727623036704339, 77.308514969817191459, 83032.7122948051111, -1.00034345584508432835946223349310458 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 46.694739303788 }, 19975447.9283188, 174.663684259477,
&lt;span class="gd"&gt;-        { 179.926838145841924189, -46.948618153686522669, 5.361568174833475454, 59614.5876209460645, -1.005204848752017347248908 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.926838145841924189, -46.948618153686522669, 5.361568174833475454, 59614.5876209460645, -1.00520484875201732144489596976200119 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 15.804386137005 }, 19855850.8800526, 74.932089158884,
&lt;span class="gd"&gt;-        { 178.367587635209819128, -15.522042847777054984, 105.357235560913450667, 123350.4326645237628, -0.9990915785464752706503469 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.367587635209819128, -15.522042847777054984, 105.357235560913450667, 123350.4326645237628, -0.999091578546475345135036150168161839 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 4.371450175299 }, 19979071.1035552, 164.163592252794,
&lt;span class="gd"&gt;-        { 179.780887420199549421, -4.566109732313098407, 15.840695025950408814, 84137.2115482558728, -1.00076323969894748092211 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.780887420199549421, -4.566109732313098407, 15.840695025950408814, 84137.2115482558728, -1.00076323969894742660358133434783667 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 30.894388279688 }, 19968681.8321577, 77.35154610481,
&lt;span class="gd"&gt;-        { 179.375426183521944524, -30.871308884744172663, 102.709506078439532936, 14048.0277985734058, -0.9989751763364228440383701 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.375426183521944524, -30.871308884744172663, 102.709506078439532936, 14048.0277985734058, -0.998975176336422854284080585784977302 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 9.541166838639 }, 19848553.7844137, 118.441353539081,
&lt;span class="gd"&gt;-        { 178.432934555386452839, -10.09982228112793472, 61.736686215549403663, 144831.1911566651614, -1.000605486201104942726309 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.432934555386452839, -10.09982228112793472, 61.736686215549403663, 144831.1911566651614, -1.00060548620110489892454097571317106 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 8.489292700054 }, 19995477.1669578, 171.963952699866,
&lt;span class="gd"&gt;-        { 179.906698338023119097, -8.559237750032113623, 8.037517851139094467, 72192.60793572974, -1.001520684863064588341133 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.906698338023119097, -8.559237750032113623, 8.037517851139094467, 72192.60793572974, -1.00152068486306466965629624610301107 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 19.562401114224 }, 19893208.1788508, 126.362762598128,
&lt;span class="gd"&gt;-        { 178.838724116996037606, -20.05038360490599475, 53.875560227496658204, 112181.7524188837615, -1.001852026688027773315692 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.838724116996037606, -20.05038360490599475, 53.875560227496658204, 112181.7524188837615, -1.00185202668802775249901060306001455 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 42.260350252749 }, 19942715.0054774, 170.703419847646,
&lt;span class="gd"&gt;-        { 179.807860448877064601, -42.79985897702184353, 9.377654670896439828, 96336.3477142010769, -1.005086424064435460636394 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.807860448877064601, -42.79985897702184353, 9.377654670896439828, 96336.3477142010769, -1.00508642406443549077721399953588843 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 24.511403144656 }, 19924809.5184876, 102.913211410163,
&lt;span class="gd"&gt;-        { 178.957598444862223515, -24.616808725039883945, 77.297538210434837096, 55403.453072179318, -1.00084083091888388877648 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.957598444862223515, -24.616808725039883945, 77.297538210434837096, 55403.453072179318, -1.0008408309188838725134473861544393 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 20.844284170708 }, 19909084.6340808, 44.172784008084,
&lt;span class="gd"&gt;-        { 179.069258863637226633, -20.321320573298341477, 136.01627115731728436, 111009.0987238994608, -0.997389183621779017239594 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.069258863637226633, -20.321320573298341477, 136.01627115731728436, 111009.0987238994608, -0.997389183621778974142557672166731209 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 2.426010809098 }, 19840940.6924189, 94.315194952561,
&lt;span class="gd"&gt;-        { 178.236397468862000784, -2.513715200833756776, 85.734896842737189557, 130002.6104886615638, -0.9998252498449916409405634 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.236397468862000784, -2.513715200833756776, 85.734896842737189557, 130002.6104886615638, -0.999825249844991659209370027383556589 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 6.600682554664 }, 19878412.28273, 168.167678684515,
&lt;span class="gd"&gt;-        { 179.646475458013797028, -7.699164822656561787, 11.861035812918738552, 187426.3958525886692, -1.000982848560649784010017 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.646475458013797028, -7.699164822656561787, 11.861035812918738552, 187426.3958525886692, -1.00098284856064978498579876031726599 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 23.372339802326 }, 19899498.4582543, 161.197647943542,
&lt;span class="gd"&gt;-        { 179.499422665106094027, -24.239465200482591299, 18.932355367478826536, 151863.2545535951091, -1.003476668684313999812749 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.499422665106094027, -24.239465200482591299, 18.932355367478826536, 151863.2545535951091, -1.00347666868431395492677893344080076 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 16.194668264095 }, 19874825.6683239, 148.942349959054,
&lt;span class="gd"&gt;-        { 179.115193814080201851, -17.129419031459576897, 31.225656401221968078, 166033.3161394594622, -1.002220322222336505702499 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.115193814080201851, -17.129419031459576897, 31.225656401221968078, 166033.3161394594622, -1.00222032222233647935638600756647065 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 1.528726471528 }, 19897803.9939987, 69.212891442493,
&lt;span class="gd"&gt;-        { 178.791047180477802091, -1.282203000582034597, 110.802928803578167132, 85252.8333849204133, -0.9998271442281568740497941 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.791047180477802091, -1.282203000582034597, 110.802928803578167132, 85252.8333849204133, -0.999827144228156883265512533398577943 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 6.297249676078 }, 19864042.0495193, 56.274639904925,
&lt;span class="gd"&gt;-        { 178.623258703845895437, -5.709470001196540278, 123.817184177744186806, 137475.1283083659258, -0.9991904501783995893996783 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.623258703845895437, -5.709470001196540278, 123.817184177744186806, 137475.1283083659258, -0.999190450178399580671850799262756482 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 17.393540327984 }, 19962624.6302607, 107.855062015266,
&lt;span class="gd"&gt;-        { 179.330156510680163326, -17.431100690958209424, 72.181322855288535245, 19320.5501845044839, -1.000918417796891303852592 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.330156510680163326, -17.431100690958209424, 72.181322855288535245, 19320.5501845044839, -1.00091841779689127989172447996679693 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 46.284685151236 }, 19990422.3478916, 14.758013867151,
&lt;span class="gd"&gt;-        { 179.852534804091121255, -46.176234945675219984, 165.271681964991897184, 42614.1796365710104, -0.9948945922618399753472283 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.852534804091121255, -46.176234945675219984, 165.271681964991897184, 42614.1796365710104, -0.994894592261839960656288894824683666 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 14.924320176299 }, 19891861.8615337, 31.446544793174,
&lt;span class="gd"&gt;-        { 179.195663739713760883, -14.125476432252858442, 148.678916887199611191, 149419.6596309045804, -0.9976201425853329563713315 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.195663739713760883, -14.125476432252858442, 148.678916887199611191, 149419.6596309045804, -0.997620142585332936313591289945179597 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 23.668824656069 }, 19938736.4442268, 148.091483667618,
&lt;span class="gd"&gt;-        { 179.409875478773990359, -24.107855233601412399, 32.02919257641173958, 97771.7687385830819, -1.003232628720005910772511 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.409875478773990359, -24.107855233601412399, 32.02919257641173958, 97771.7687385830819, -1.00323262872000595891108787327539176 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 46.986276695896 }, 19968596.0414782, 174.796708941456,
&lt;span class="gd"&gt;-        { 179.92040916864362177, -47.301644191214905832, 5.234240076649939638, 66113.7417494369769, -1.005190954526080850226118 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.92040916864362177, -47.301644191214905832, 5.234240076649939638, 66113.7417494369769, -1.00519095452608087093437916337279603 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 65.946144289524 }, 19993734.5109736, 25.375428509648,
&lt;span class="gd"&gt;-        { 179.808282612725835525, -65.871840130833632868, 154.703163938350061652, 18355.2254271672769, -0.9964369359146106126432461 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.808282612725835525, -65.871840130833632868, 154.703163938350061652, 18355.2254271672769, -0.996436935914610577569305860379245132 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 10.950298933293 }, 19975919.5586889, 28.779018914489,
&lt;span class="gd"&gt;-        { 179.624609619829763098, -10.787771536605316781, 151.238005588662201946, 70291.1998404303581, -0.9982720718341152112444356 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.624609619829763098, -10.787771536605316781, 151.238005588662201946, 70291.1998404303581, -0.998272071834115148902810688014142215 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 13.609869340778 }, 19913213.8514358, 129.616021271129,
&lt;span class="gd"&gt;-        { 179.035623147420893383, -14.023624108675206222, 50.506400999466711623, 97596.7664002074776, -1.00146664642314031927646 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.035623147420893383, -14.023624108675206222, 50.506400999466711623, 97596.7664002074776, -1.00146664642314031645753402699483559 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 48.701427557433 }, 19972955.2699173, 102.875149183407,
&lt;span class="gd"&gt;-        { 179.385565054218238481, -48.735316652259656533, 77.294384444682547869, 18461.7742226227697, -1.001146768554290695098057 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.385565054218238481, -48.735316652259656533, 77.294384444682547869, 18461.7742226227697, -1.00114676855429074464609584538266063 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 31.519172055785 }, 19952318.3772514, 26.247105619999,
&lt;span class="gd"&gt;-        { 179.555251675378549409, -31.140142027808697534, 153.865822276646938125, 86354.7117605101002, -0.9957399483998249749283627 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.555251675378549409, -31.140142027808697534, 153.865822276646938125, 86354.7117605101002, -0.995739948399825047786748655198607594 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 31.863784754278 }, 19993324.8682601, 29.572313410211,
&lt;span class="gd"&gt;-        { 179.722489476483407524, -31.826935359797657785, 150.440607907359037187, 41427.6181613499234, -0.9958880090011472640794746 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.722489476483407524, -31.826935359797657785, 150.440607907359037187, 41427.6181613499234, -0.995888009001147267440501309465616941 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 76.434608546092 }, 19997750.023578, 167.428385412814,
&lt;span class="gd"&gt;-        { 179.918287057674124459, -76.48787937532808951, 12.621032110142724567, 9619.5267710862108, -1.002339638930915896456245 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.918287057674124459, -76.48787937532808951, 12.621032110142724567, 9619.5267710862108, -1.00233963893091582164629471662919968 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 73.114273316483 }, 19992866.6147806, 78.154765899661,
&lt;span class="gd"&gt;-        { 179.576736605988553624, -73.098788070892914568, 102.085693546950923465, 8580.6475692800946, -0.9993841433084754598747609 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.576736605988553624, -73.098788070892914568, 102.085693546950923465, 8580.6475692800946, -0.999384143308475469957841141876997426 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 1.125639056292 }, 19852573.5442848, 67.184842289382,
&lt;span class="gd"&gt;-        { 178.426819580880619395, -.694775021853292564, 112.831314850896246589, 132932.8743502563937, -0.9997329579628334908763393 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.426819580880619395, -.694775021853292564, 112.831314850896246589, 132932.8743502563937, -0.999732957962833457266071945923613384 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    }
&lt;span class="w"&gt; &lt;/span&gt;};


commit 485a2f9a0f6d245ae3b2fb3762e664ced7754ea9
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Mon Jun 11 18:44:27 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [formulas] Find starting point for inverse problem (short lines)

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/formulas/karney_inverse.hpp b/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 59b3cbd..0f3c686 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -50,11 +50,17 @@ class karney_inverse&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;public:
&lt;span class="w"&gt; &lt;/span&gt;    typedef result_inverse&amp;lt;CT&amp;gt; result_type;
&lt;span class="w"&gt; &lt;/span&gt;    static CT constexpr c0 = 0;
&lt;span class="gi"&gt;+    static CT constexpr c0_001 = 0.001;&lt;/span&gt;
&lt;span class="gi"&gt;+    static CT constexpr c0_01 = 0.01;&lt;/span&gt;
&lt;span class="gi"&gt;+    static CT constexpr c0_1 = 0.1;&lt;/span&gt;
&lt;span class="gi"&gt;+    static CT constexpr c0_5 = 0.5;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    static CT constexpr c1 = 1;
&lt;span class="w"&gt; &lt;/span&gt;    static CT constexpr c2 = 2;
&lt;span class="w"&gt; &lt;/span&gt;    static CT constexpr c3 = 3;
&lt;span class="gd"&gt;-    static CT constexpr c180 = 180;&lt;/span&gt;
&lt;span class="gi"&gt;+    static CT constexpr c6 = 6;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    static CT constexpr c90 = 90;
&lt;span class="gi"&gt;+    static CT constexpr c180 = 180;&lt;/span&gt;
&lt;span class="gi"&gt;+    static CT constexpr c200 = 200;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    template &amp;lt;typename T1, typename T2, typename Spheroid&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;    static inline result_type apply(T1 const&amp;amp; lo1,
&lt;span class="gu"&gt;@@ -71,14 +77,20 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT lon1 = lo1;
&lt;span class="w"&gt; &lt;/span&gt;        CT lon2 = lo2;

&lt;span class="gd"&gt;-        CT tiny = std::sqrt(std::numeric_limits&amp;lt;CT&amp;gt;::min());&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT const a = CT(get_radius&amp;lt;0&amp;gt;(spheroid));
&lt;span class="w"&gt; &lt;/span&gt;        CT const b = CT(get_radius&amp;lt;2&amp;gt;(spheroid));
&lt;span class="w"&gt; &lt;/span&gt;        CT const f = formula::flattening&amp;lt;CT&amp;gt;(spheroid);
&lt;span class="w"&gt; &lt;/span&gt;        CT const one_minus_f = c1 - f;
&lt;span class="w"&gt; &lt;/span&gt;        CT const two_minus_f = c2 - f;

&lt;span class="gi"&gt;+        CT const tol0 = std::numeric_limits&amp;lt;CT&amp;gt;::epsilon();&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const tol1 = c200 * tol0;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const tol2 = sqrt(tol0);&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const etol2 = c0_1 * tol2 /&lt;/span&gt;
&lt;span class="gi"&gt;+            sqrt(std::max(c0_001, std::abs(f)) * std::min(c1, c1 - f / c2) / c2);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        CT tiny = std::sqrt(std::numeric_limits&amp;lt;CT&amp;gt;::min());&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT const n = f / two_minus_f;
&lt;span class="w"&gt; &lt;/span&gt;        CT const e2 = f * two_minus_f;
&lt;span class="w"&gt; &lt;/span&gt;        CT const ep2 = e2 / math::sqr(one_minus_f);
&lt;span class="gu"&gt;@@ -198,10 +210,12 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            CT sigma12 = std::atan2(std::max(c0, cos_sigma1 * sin_sigma2 - sin_sigma1 * cos_sigma2),
&lt;span class="w"&gt; &lt;/span&gt;                                             cos_sigma1 * cos_sigma2 + sin_sigma1 * sin_sigma2);

&lt;span class="gi"&gt;+            CT dummy;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            meridian_length(n, ep2, sigma12, sin_sigma1, cos_sigma1, dn1,
&lt;span class="w"&gt; &lt;/span&gt;                                             sin_sigma2, cos_sigma2, dn2,
&lt;span class="w"&gt; &lt;/span&gt;                                             cos_beta1, cos_beta2, s12x,
&lt;span class="gd"&gt;-                                             m12x, result.geodesic_scale, M21);&lt;/span&gt;
&lt;span class="gi"&gt;+                                             m12x, dummy, result.geodesic_scale,&lt;/span&gt;
&lt;span class="gi"&gt;+                                             M21, coeffs_C1);&lt;/span&gt;


&lt;span class="w"&gt; &lt;/span&gt;            if (sigma12 &amp;lt; c1 || m12x &amp;gt;= c0)
&lt;span class="gu"&gt;@@ -227,7 +241,6 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        if (!meridian &amp;amp;&amp;amp; sin_beta1 == 0 &amp;amp;&amp;amp;
&lt;span class="w"&gt; &lt;/span&gt;            (f &amp;lt;= 0 || lon12_error &amp;gt;= f * c180))
&lt;span class="w"&gt; &lt;/span&gt;        {
&lt;span class="gd"&gt;-            std::cout &amp;lt;&amp;lt; &amp;quot;Points lie on the equator.&amp;quot; &amp;lt;&amp;lt; std::endl;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            // Points lie on the equator.
&lt;span class="w"&gt; &lt;/span&gt;            cos_alpha1 = cos_alpha2 = c0;
&lt;span class="w"&gt; &lt;/span&gt;            sin_alpha1 = sin_alpha2 = c1;
&lt;span class="gu"&gt;@@ -239,24 +252,37 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            if (BOOST_GEOMETRY_CONDITION(EnableGeodesicScale))
&lt;span class="w"&gt; &lt;/span&gt;            {
&lt;span class="w"&gt; &lt;/span&gt;                result.geodesic_scale = cos(sigma12);
&lt;span class="gd"&gt;-                M21 = cos(sigma12);&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            }
&lt;span class="w"&gt; &lt;/span&gt;            a12 = lon12 / one_minus_f;
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        else if (!meridian)&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            // If point1 and point2 belong within a hemisphere bounded by a&lt;/span&gt;
&lt;span class="gi"&gt;+            // meridian and geodesic is neither meridional nor equatorial.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            // Find the starting point for Newton&amp;#39;s method.&lt;/span&gt;
&lt;span class="gi"&gt;+            CT dnm;&lt;/span&gt;
&lt;span class="gi"&gt;+            sigma12 = newton_start(sin_beta1, cos_beta1, dn1,&lt;/span&gt;
&lt;span class="gi"&gt;+                                 sin_beta2, cos_beta2, dn2,&lt;/span&gt;
&lt;span class="gi"&gt;+                                 lam12, sin_lam12, cos_lam12,&lt;/span&gt;
&lt;span class="gi"&gt;+                                 sin_alpha1, cos_alpha1,&lt;/span&gt;
&lt;span class="gi"&gt;+                                 sin_alpha2, cos_alpha2,&lt;/span&gt;
&lt;span class="gi"&gt;+                                 dnm, coeffs_C1, ep2, etol2, n, f);&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    }

&lt;span class="w"&gt; &lt;/span&gt;    static inline void meridian_length(CT epsilon, CT ep2, CT sigma12,
&lt;span class="w"&gt; &lt;/span&gt;                                       CT sin_sigma1, CT cos_sigma1, CT dn1,
&lt;span class="w"&gt; &lt;/span&gt;                                       CT sin_sigma2, CT cos_sigma2, CT dn2,
&lt;span class="w"&gt; &lt;/span&gt;                                       CT cos_beta1, CT cos_beta2,
&lt;span class="gd"&gt;-                                       CT&amp;amp; s12x, CT&amp;amp; m12x,&lt;/span&gt;
&lt;span class="gd"&gt;-                                       CT&amp;amp; M12, CT&amp;amp; M21)&lt;/span&gt;
&lt;span class="gi"&gt;+                                       CT&amp;amp; s12x, CT&amp;amp; m12x, CT&amp;amp; m0,&lt;/span&gt;
&lt;span class="gi"&gt;+                                       CT&amp;amp; M12, CT&amp;amp; M21, CT coeffs_C1[])&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    {
&lt;span class="w"&gt; &lt;/span&gt;        CT A12x = 0, J12 = 0;
&lt;span class="w"&gt; &lt;/span&gt;        CT expansion_A1, expansion_A2;

&lt;span class="gd"&gt;-        // Index zero element of coeffs_C1 and coeffs_C2 is unused.&lt;/span&gt;
&lt;span class="gd"&gt;-        CT coeffs_C1[SeriesOrder + 1];&lt;/span&gt;
&lt;span class="gi"&gt;+        // Index zero element of coeffs_C2 is unused.&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT coeffs_C2[SeriesOrder + 1];

&lt;span class="w"&gt; &lt;/span&gt;        if (BOOST_GEOMETRY_CONDITION(EnableDistance) ||
&lt;span class="gu"&gt;@@ -331,6 +357,8 @@ public:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        if (BOOST_GEOMETRY_CONDITION(EnableReducedLength))
&lt;span class="w"&gt; &lt;/span&gt;        {
&lt;span class="gi"&gt;+            m0 = A12x;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            m12x = dn2 * (cos_sigma1 * sin_sigma2) -
&lt;span class="w"&gt; &lt;/span&gt;                   dn1 * (sin_sigma1 * cos_sigma2) -
&lt;span class="w"&gt; &lt;/span&gt;                   cos_sigma1 * cos_sigma2 * J12;
&lt;span class="gu"&gt;@@ -347,6 +375,124 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="w"&gt; &lt;/span&gt;    }

&lt;span class="gi"&gt;+    /*&lt;/span&gt;
&lt;span class="gi"&gt;+     Return a starting point for Newton&amp;#39;s method in sin_alpha1 and&lt;/span&gt;
&lt;span class="gi"&gt;+     cos_alpha1 (function value is -1). If Newton&amp;#39;s method&lt;/span&gt;
&lt;span class="gi"&gt;+     doesn&amp;#39;t need to be used, return also sin_alpha2 and&lt;/span&gt;
&lt;span class="gi"&gt;+     cos_alpha2 and function value is sig12.&lt;/span&gt;
&lt;span class="gi"&gt;+    */&lt;/span&gt;
&lt;span class="gi"&gt;+    static inline CT newton_start(CT sin_beta1, CT cos_beta1, CT dn1,&lt;/span&gt;
&lt;span class="gi"&gt;+                                  CT sin_beta2, CT cos_beta2, CT dn2,&lt;/span&gt;
&lt;span class="gi"&gt;+                                  CT lam12, CT sin_lam12, CT cos_lam12,&lt;/span&gt;
&lt;span class="gi"&gt;+                                  CT&amp;amp; sin_alpha1, CT&amp;amp; cos_alpha1,&lt;/span&gt;
&lt;span class="gi"&gt;+                                  CT&amp;amp; sin_alpha2, CT&amp;amp; cos_alpha2,&lt;/span&gt;
&lt;span class="gi"&gt;+                                  CT&amp;amp; dnm, CT coeffs_C1[], CT ep2,&lt;/span&gt;
&lt;span class="gi"&gt;+                                  CT etol2, CT n, CT f)&lt;/span&gt;
&lt;span class="gi"&gt;+    {&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const one_minus_f = c1 - f;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT sig12 = -c1;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        CT sin_beta12 = sin_beta2 * cos_beta1 - cos_beta2 * sin_beta1;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT cos_beta12 = cos_beta2 * cos_beta1 + sin_beta2 * sin_beta1;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        CT sin_beta12a = sin_beta2 * cos_beta1 + cos_beta2 * sin_beta1;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        bool shortline = cos_beta12 &amp;gt;= c0 &amp;amp;&amp;amp; sin_beta12 &amp;lt; c0_5 &amp;amp;&amp;amp;&lt;/span&gt;
&lt;span class="gi"&gt;+                         cos_beta2 * lam12 &amp;lt; c0_5;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        CT sin_omega12, cos_omega12;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        if (shortline)&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            CT sin_beta_m2 = math::sqr(sin_beta1 + sin_beta2);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            sin_beta_m2 /= sin_beta_m2 + math::sqr(cos_beta1 + cos_beta2);&lt;/span&gt;
&lt;span class="gi"&gt;+            dnm = math::sqrt(c1 + ep2 * sin_beta_m2);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            CT omega12 = lam12 / (one_minus_f * dnm);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            sin_omega12 = sin(omega12);&lt;/span&gt;
&lt;span class="gi"&gt;+            cos_omega12 = cos(omega12);&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+        else&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            sin_omega12 = sin_lam12;&lt;/span&gt;
&lt;span class="gi"&gt;+            cos_omega12 = cos_lam12;&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        sin_alpha1 = cos_beta2 * sin_omega12;&lt;/span&gt;
&lt;span class="gi"&gt;+        // TODO: adl1995 - Resolve inaccuracy with&lt;/span&gt;
&lt;span class="gi"&gt;+        // cos_alpha1 calculation.&lt;/span&gt;
&lt;span class="gi"&gt;+        cos_alpha1 = cos_omega12 &amp;gt;= c0 ?&lt;/span&gt;
&lt;span class="gi"&gt;+            sin_beta12 + cos_beta2 * sin_beta1 * math::sqr(sin_omega12) / (c1 + cos_omega12) :&lt;/span&gt;
&lt;span class="gi"&gt;+            sin_beta12a - cos_beta2 * sin_beta1 * math::sqr(sin_omega12) / (c1 - cos_omega12);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        CT sin_sigma12 = boost::math::hypot(sin_alpha1, cos_alpha1);&lt;/span&gt;
&lt;span class="gi"&gt;+        CT cos_sigma12 = sin_beta1 * sin_beta2 + cos_beta1 * cos_beta2 * cos_omega12;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        if (shortline &amp;amp;&amp;amp; sin_sigma12 &amp;lt; etol2)&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            sin_alpha2 = cos_beta1 * sin_omega12;&lt;/span&gt;
&lt;span class="gi"&gt;+            cos_alpha2 = sin_beta12 - cos_beta1 * sin_beta2 *&lt;/span&gt;
&lt;span class="gi"&gt;+                (cos_omega12 &amp;gt;= c0 ? math::sqr(sin_omega12) /&lt;/span&gt;
&lt;span class="gi"&gt;+                (c1 + cos_omega12) : c1 - cos_omega12);&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+        // Skip astroid calculation if too eccentric.&lt;/span&gt;
&lt;span class="gi"&gt;+        else if (std::abs(n) &amp;gt; c0_1 ||&lt;/span&gt;
&lt;span class="gi"&gt;+                 cos_sigma12 &amp;gt;= c0 ||&lt;/span&gt;
&lt;span class="gi"&gt;+                 sin_sigma12 &amp;gt;= c6 * std::abs(n) * math::pi&amp;lt;CT&amp;gt;() *&lt;/span&gt;
&lt;span class="gi"&gt;+                 math::sqr(cos_beta1))&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            // Nothing to do (?).&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+        else&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            // Scale lam12 and beta2 to x, y coordinate system where antipodal&lt;/span&gt;
&lt;span class="gi"&gt;+            // point is at origin and singular point is at y = 0, x = -1.&lt;/span&gt;
&lt;span class="gi"&gt;+            CT lambda_scale, beta_scale;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            CT y;&lt;/span&gt;
&lt;span class="gi"&gt;+            volatile CT x;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            CT lam12x = atan2(-sin_lam12, -cos_lam12);&lt;/span&gt;
&lt;span class="gi"&gt;+            if (f &amp;gt;= 0)&lt;/span&gt;
&lt;span class="gi"&gt;+            {&lt;/span&gt;
&lt;span class="gi"&gt;+                CT k2 = math::sqr(sin_beta1) * ep2;&lt;/span&gt;
&lt;span class="gi"&gt;+                CT epsilon = k2 / (c2 * (c1 * sqrt(c1 + k2)) + k2);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+                CT coeffs_A3[SeriesOrder];&lt;/span&gt;
&lt;span class="gi"&gt;+                series_expansion::evaluate_coeffs_A3&amp;lt;double, SeriesOrder&amp;gt;(n, coeffs_A3);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+                CT const A3 = math::horner_evaluate(epsilon, coeffs_A3, coeffs_A3 + SeriesOrder);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+                lambda_scale = f * cos_beta1 * A3 * math::pi&amp;lt;CT&amp;gt;();&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+                beta_scale = lambda_scale * cos_beta1;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+                x = lam12x / lambda_scale;&lt;/span&gt;
&lt;span class="gi"&gt;+                y = sin_beta12a / beta_scale;&lt;/span&gt;
&lt;span class="gi"&gt;+            }&lt;/span&gt;
&lt;span class="gi"&gt;+            else&lt;/span&gt;
&lt;span class="gi"&gt;+            {&lt;/span&gt;
&lt;span class="gi"&gt;+                CT cos_beta12a = cos_beta2 * cos_beta1 - sin_beta2 * sin_beta1;&lt;/span&gt;
&lt;span class="gi"&gt;+                CT beta12a = atan2(sin_beta12a, cos_beta12a);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+                CT m12b, m0, dummy;&lt;/span&gt;
&lt;span class="gi"&gt;+                meridian_length(n, ep2, math::pi&amp;lt;CT&amp;gt;() + beta12a,&lt;/span&gt;
&lt;span class="gi"&gt;+                                sin_beta1, -cos_beta1, dn1,&lt;/span&gt;
&lt;span class="gi"&gt;+                                sin_beta2, cos_beta2, dn2,&lt;/span&gt;
&lt;span class="gi"&gt;+                                cos_beta1, cos_beta2, dummy,&lt;/span&gt;
&lt;span class="gi"&gt;+                                m12b, m0, dummy, dummy, coeffs_C1);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+                x = -c1 + m12b / (cos_beta1 * cos_beta2 * m0 * math::pi&amp;lt;CT&amp;gt;());&lt;/span&gt;
&lt;span class="gi"&gt;+                beta_scale = x &amp;lt; -c0_01 ? sin_beta12a / x :&lt;/span&gt;
&lt;span class="gi"&gt;+                    -f * math::sqr(cos_beta1) * math::pi&amp;lt;CT&amp;gt;();&lt;/span&gt;
&lt;span class="gi"&gt;+                lambda_scale = beta_scale / cos_beta1;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+                y = lam12x / lambda_scale;&lt;/span&gt;
&lt;span class="gi"&gt;+            }&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+    }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;};

&lt;span class="w"&gt; &lt;/span&gt;}}} // namespace boost::geometry::formula

commit 0344ba5c1a3e596a4558eeb677324bcf7c7d2c30
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Mon Jun 11 11:06:18 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [formulas] Handle case for equatorial points in inverse problem

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/formulas/karney_inverse.hpp b/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 9297034..59b3cbd 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -73,6 +73,7 @@ public:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        CT tiny = std::sqrt(std::numeric_limits&amp;lt;CT&amp;gt;::min());

&lt;span class="gi"&gt;+        CT const a = CT(get_radius&amp;lt;0&amp;gt;(spheroid));&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT const b = CT(get_radius&amp;lt;2&amp;gt;(spheroid));
&lt;span class="w"&gt; &lt;/span&gt;        CT const f = formula::flattening&amp;lt;CT&amp;gt;(spheroid);
&lt;span class="w"&gt; &lt;/span&gt;        CT const one_minus_f = c1 - f;
&lt;span class="gu"&gt;@@ -108,7 +109,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        lat2 = math::round_angle(lat2);

&lt;span class="w"&gt; &lt;/span&gt;        // Arrange points in a canonical form, as explained in
&lt;span class="gd"&gt;-        // paper Algorithms for geodesics, Eq. (44):&lt;/span&gt;
&lt;span class="gi"&gt;+        // paper, Algorithms for geodesics, Eq. (44):&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        //
&lt;span class="w"&gt; &lt;/span&gt;        //     0 &amp;lt;= lon12 &amp;lt;= 180
&lt;span class="w"&gt; &lt;/span&gt;        //     -90 &amp;lt;= lat1 &amp;lt;= 0
&lt;span class="gu"&gt;@@ -141,7 +142,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        cos_beta2 = std::max(tiny, cos_beta2);

&lt;span class="w"&gt; &lt;/span&gt;        // If cos_beta1 &amp;lt; -sin_beta1, then cos_beta2 - cos_beta1 is a
&lt;span class="gd"&gt;-        // sensitive measure of the |beta1| - |beta2|.  Alternatively,&lt;/span&gt;
&lt;span class="gi"&gt;+        // sensitive measure of the |beta1| - |beta2|. Alternatively,&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        // (cos_beta1 &amp;gt;= -sin_beta1), abs(sin_beta2) + sin_beta1 is
&lt;span class="w"&gt; &lt;/span&gt;        // a better measure.
&lt;span class="w"&gt; &lt;/span&gt;        // Sometimes these quantities vanish and in that case we
&lt;span class="gu"&gt;@@ -173,17 +174,20 @@ public:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        bool meridian = lat1 == -90 || sin_lam12 == 0;

&lt;span class="gi"&gt;+        CT cos_alpha1, sin_alpha1;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT cos_alpha2, sin_alpha2;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        if (meridian)
&lt;span class="w"&gt; &lt;/span&gt;        {
&lt;span class="w"&gt; &lt;/span&gt;            // Endpoints lie on a single full meridian.

&lt;span class="w"&gt; &lt;/span&gt;            // Point to the target latitude.
&lt;span class="gd"&gt;-            CT cos_alpha1 = cos_lam12;&lt;/span&gt;
&lt;span class="gd"&gt;-            CT sin_alpha1 = sin_lam12;&lt;/span&gt;
&lt;span class="gi"&gt;+            cos_alpha1 = cos_lam12;&lt;/span&gt;
&lt;span class="gi"&gt;+            sin_alpha1 = sin_lam12;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            // Heading north at the target.
&lt;span class="gd"&gt;-            CT cos_alpha2 = 1;&lt;/span&gt;
&lt;span class="gd"&gt;-            CT sin_alpha2 = 0;&lt;/span&gt;
&lt;span class="gi"&gt;+            cos_alpha2 = 1;&lt;/span&gt;
&lt;span class="gi"&gt;+            sin_alpha2 = 0;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            CT sin_sigma1 = sin_beta1;
&lt;span class="w"&gt; &lt;/span&gt;            CT cos_sigma1 = cos_alpha1 * cos_beta1;
&lt;span class="gu"&gt;@@ -217,6 +221,28 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                meridian = false;
&lt;span class="w"&gt; &lt;/span&gt;            }
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        CT omega12, sin_omega12, cos_omega12;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        if (!meridian &amp;amp;&amp;amp; sin_beta1 == 0 &amp;amp;&amp;amp;&lt;/span&gt;
&lt;span class="gi"&gt;+            (f &amp;lt;= 0 || lon12_error &amp;gt;= f * c180))&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            std::cout &amp;lt;&amp;lt; &amp;quot;Points lie on the equator.&amp;quot; &amp;lt;&amp;lt; std::endl;&lt;/span&gt;
&lt;span class="gi"&gt;+            // Points lie on the equator.&lt;/span&gt;
&lt;span class="gi"&gt;+            cos_alpha1 = cos_alpha2 = c0;&lt;/span&gt;
&lt;span class="gi"&gt;+            sin_alpha1 = sin_alpha2 = c1;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            s12x = a * lam12;&lt;/span&gt;
&lt;span class="gi"&gt;+            sigma12 = omega12 = lam12 / one_minus_f;&lt;/span&gt;
&lt;span class="gi"&gt;+            m12x = b * sin(sigma12);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            if (BOOST_GEOMETRY_CONDITION(EnableGeodesicScale))&lt;/span&gt;
&lt;span class="gi"&gt;+            {&lt;/span&gt;
&lt;span class="gi"&gt;+                result.geodesic_scale = cos(sigma12);&lt;/span&gt;
&lt;span class="gi"&gt;+                M21 = cos(sigma12);&lt;/span&gt;
&lt;span class="gi"&gt;+            }&lt;/span&gt;
&lt;span class="gi"&gt;+            a12 = lon12 / one_minus_f;&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    }

&lt;span class="w"&gt; &lt;/span&gt;    static inline void meridian_length(CT epsilon, CT ep2, CT sigma12,

commit fe1b9ff8ba3b4f7ed799ae4e5a9c5c69bd795f5f
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Sat Jun 9 16:17:42 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [test] Update geodesic scale (M12) in GeographicLib dataset

&lt;span class="w"&gt; &lt;/span&gt;   M12 is calculated manually using GeographicLib. Previously,
&lt;span class="w"&gt; &lt;/span&gt;   GEOGRAPHICLIB_PRECISION was set to 2 (default) with series
&lt;span class="w"&gt; &lt;/span&gt;   order set to 6. The updated values are generated with
&lt;span class="w"&gt; &lt;/span&gt;   GEOGRAPHICLIB_PRECISION set to 3 and series order set to 7.

&lt;span class="w"&gt; &lt;/span&gt;   Build instructions are provided here:
&lt;span class="w"&gt; &lt;/span&gt;   https://geographiclib.sourceforge.io/html/install.html

&lt;span class="gh"&gt;diff --git a/test/formulas/direct_cases_antipodal.hpp b/test/formulas/direct_cases_antipodal.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 37214fe..fdbbc6e 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/test/formulas/direct_cases_antipodal.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/test/formulas/direct_cases_antipodal.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -35,304 +35,304 @@ expected_results_antipodal expected_antipodal[] =&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;{
&lt;span class="w"&gt; &lt;/span&gt;    {
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 31.394417440639 }, 19980218.4055399, 34.266322930672,
&lt;span class="gd"&gt;-        { 179.615601631202912322, -31.275540610835465807, 145.782701113414306756, 49490.8807994496209, -0.99598340069666346785 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.615601631202912322, -31.275540610835465807, 145.782701113414306756, 49490.8807994496209, -0.9961164510125257909767434 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 29.788792273749 }, 19887224.5407334, 74.302205994192,
&lt;span class="gd"&gt;-        { 178.569451327813675741, -29.558013672069422725, 106.156240654579267308, 97043.7545600593058, -0.99585265538534928353 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.569451327813675741, -29.558013672069422725, 106.156240654579267308, 97043.7545600593058, -0.9986240311478448829847661 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 46.471843094141 }, 19944337.8863917, 63.693680310665,
&lt;span class="gd"&gt;-        { 179.083144618009561276, -46.284166405924629853, 116.699978859005570535, 53139.140576552365, -0.99628290151178156009 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.083144618009561276, -46.284166405924629853, 116.699978859005570535, 53139.140576552365, -0.9975973096455918888284843 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 63.016506345929 }, 20000925.7533636, 153.393656073038,
&lt;span class="gd"&gt;-        { 179.862869954071637855, -63.02943882703369735, 26.619056019474552953, 12713.9284725111772, -0.99806730524837738994 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.862869954071637855, -63.02943882703369735, 26.619056019474552953, 12713.9284725111772, -1.003813177921433850612286 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 19.796231412719 }, 19956338.1330537, 28.272934411318,
&lt;span class="gd"&gt;-        { 179.546498474461283862, -19.470586923091672503, 151.789094611690988249, 87191.1749625132931, -0.99680355285706290225 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.546498474461283862, -19.470586923091672503, 151.789094611690988249, 87191.1749625132931, -0.9970154090276648134395961 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 6.373459459035 }, 19946581.6983394, 56.859050230583,
&lt;span class="gd"&gt;-        { 179.240009269347556917, -6.204887833274217382, 123.169200847008284851, 53958.8698005263939, -0.99880439595523196061 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.240009269347556917, -6.204887833274217382, 123.169200847008284851, 53958.8698005263939, -0.9993490490811010928734123 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 66.380766469414 }, 19986277.7696849, 38.646950203356,
&lt;span class="gd"&gt;-        { 179.632633596894388233, -66.27177494016956425, 141.550919825824399405, 22198.215635049214, -0.9984201835509521894 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.632633596894388233, -66.27177494016956425, 141.550919825824399405, 22198.215635049214, -0.996949176054954412282659 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 16.483421185231 }, 19962737.9842573, 163.431254767325,
&lt;span class="gd"&gt;-        { 179.731567273052604726, -16.818424446748042212, 16.598399455529231288, 95318.4104529881431, -0.99723427960580335316 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.731567273052604726, -16.818424446748042212, 16.598399455529231288, 95318.4104529881431, -1.002722102329797498345386 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 4.215702155486 }, 19932517.393764, 65.543168480886,
&lt;span class="gd"&gt;-        { 179.093771177769992874, -4.051917290690976764, 114.482669479963380006, 55205.4553703842317, -0.99916694551569362748 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.093771177769992874, -4.051917290690976764, 114.482669479963380006, 55205.4553703842317, -0.9996558584250565483090949 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 40.71372085907 }, 19951133.3595356, 143.672151631634,
&lt;span class="gd"&gt;-        { 179.404612926861498984, -41.047052242159400671, 36.54002600969304553, 70931.1530155553621, -0.99601226064330683485 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.404612926861498984, -41.047052242159400671, 36.54002600969304553, 70931.1530155553621, -1.004141695740772786027591 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 15.465481491654 }, 19877383.8879911, 36.289185640976,
&lt;span class="gd"&gt;-        { 179.020726605204181801, -14.622355549425900341, 143.875673907461159912, 156419.0806764376957, -0.99717590257108590368 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.020726605204181801, -14.622355549425900341, 143.875673907461159912, 156419.0806764376957, -0.9976390743971695870329942 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 17.586197343531 }, 19982280.4639115, 157.929615091529,
&lt;span class="gd"&gt;-        { 179.722490735835379144, -17.731394230364437075, 22.089021105298661023, 69727.5357849255557, -0.99710409371925123878 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.722490735835379144, -17.731394230364437075, 22.089021105298661023, 69727.5357849255557, -1.00280451698301228773396 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 5.7442768247 }, 19902873.7431814, 116.146983678305,
&lt;span class="gd"&gt;-        { 178.85894724576868462, -6.039853564481335581, 63.91482549951374061, 87149.6188944111673, -0.99883071172416715289 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.85894724576868462, -6.039853564481335581, 63.91482549951374061, 87149.6188944111673, -1.000393328930967508458988 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 32.002904282111 }, 19967670.3104795, 163.052160078191,
&lt;span class="gd"&gt;-        { 179.744925422107715439, -32.297934520693132807, 17.004175883388454943, 78311.3164829640582, -0.99597193334487110761 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.744925422107715439, -32.297934520693132807, 17.004175883388454943, 78311.3164829640582, -1.004499034453024313763049 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 55.902716926362 }, 19970525.337607, 98.927641063414,
&lt;span class="gd"&gt;-        { 179.300685189522463007, -55.934320218634018206, 81.374264168520557301, 23554.0093185709067, -0.99721760041260698593 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.300685189522463007, -55.934320218634018206, 81.374264168520557301, 23554.0093185709067, -1.0007278877908344522649 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 22.69939784398 }, 19959286.1903172, 74.253870776761,
&lt;span class="gd"&gt;-        { 179.294173474584020749, -22.654875407651067149, 105.811588890213155275, 22369.7179951557679, -0.99650952667426662135 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.294173474584020749, -22.654875407651067149, 105.811588890213155275, 22369.7179951557679, -0.9989721814190034790826959 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 41.312328471121 }, 19962690.5721867, 11.277616109847,
&lt;span class="gd"&gt;-        { 179.817186837717804928, -40.954523601529804886, 168.784288786443902199, 77252.6121237260201, -0.99601334953687414853 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.817186837717804928, -40.954523601529804886, 168.784288786443902199, 77252.6121237260201, -0.9948251514715273688795397 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 27.927415327453 }, 19961296.8828333, 23.166421459647,
&lt;span class="gd"&gt;-        { 179.636508875679110143, -27.607314264234172721, 156.905194492817275222, 83096.5801709291101, -0.99610609795569049485 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.636508875679110143, -27.607314264234172721, 156.905194492817275222, 83096.5801709291101, -0.9959596927676567156505726 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 41.567228741451 }, 19944253.4454809, 176.66609526064,
&lt;span class="gd"&gt;-        { 179.931812964300204608, -42.103039532074194347, 3.361859685835349219, 96859.08180779197, -0.99604624873858405021 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.931812964300204608, -42.103039532074194347, 3.361859685835349219, 96859.08180779197, -1.005136071404876275492239 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 37.384208978567 }, 19928705.5911445, 39.072534864532,
&lt;span class="gd"&gt;-        { 179.225180174670992261, -36.916085670712060029, 141.212743814390850106, 92667.7834060578402, -0.99583616315460821156 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.225180174670992261, -36.916085670712060029, 141.212743814390850106, 92667.7834060578402, -0.9959555168591592858788435 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 59.011868682852 }, 19970442.3788306, 44.970301291063,
&lt;span class="gd"&gt;-        { 179.424923485514312807, -58.82705468054708336, 135.333817989802309531, 38071.1136293083857, -0.99754686309848117354 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.424923485514312807, -58.82705468054708336, 135.333817989802309531, 38071.1136293083857, -0.9966589428927073192592684 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 35.515406087737 }, 19948918.9139751, 28.528972431952,
&lt;span class="gd"&gt;-        { 179.50369572149476218, -35.119747127350258822, 151.622257906284404073, 84564.0387217601751, -0.9958683164291525225 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.50369572149476218, -35.119747127350258822, 151.622257906284404073, 84564.0387217601751, -0.9955628617991693771129176 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 58.170252463184 }, 19961407.0813807, 128.021116291844,
&lt;span class="gd"&gt;-        { 179.254737571455023977, -58.372261836268550805, 52.399129705193347143, 43715.3070711393309, -0.99746161015888423762 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.254737571455023977, -58.372261836268550805, 52.399129705193347143, 43715.3070711393309, -1.002852737132807475024451 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 34.012183807959 }, 19970955.843065, 168.944519134772,
&lt;span class="gd"&gt;-        { 179.83713352180447672, -34.29640782899529639, 11.093048811826875835, 76493.5814538538151, -0.99594085068133375582 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.83713352180447672, -34.29640782899529639, 11.093048811826875835, 76493.5814538538151, -1.004765235455867102248688 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 45.510762948553 }, 19940248.3450143, 99.886784003837,
&lt;span class="gd"&gt;-        { 178.981682578823726535, -45.582753595227824235, 80.542330522982505877, 48555.1946627894972, -0.99622530350349169925 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.981682578823726535, -45.582753595227824235, 80.542330522982505877, 48555.1946627894972, -1.000838077509063557589755 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 4.19841765451 }, 19970496.5132933, 89.561550657928,
&lt;span class="gd"&gt;-        { 179.398024428225540172, -4.198416896099783242, 90.438456568689151881, 14.8790480103109, -0.99921964515553229891 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.398024428225540172, -4.198416896099783242, 90.438456568689151881, 14.8790480103109, -0.9999941048102859313703554 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 40.890119148103 }, 19926563.5817492, 165.437641169967,
&lt;span class="gd"&gt;-        { 179.6557148951668192, -41.553556264538302258, 14.713597527941311478, 111805.7305227545923, -0.99598758014963484353 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.6557148951668192, -41.553556264538302258, 14.713597527941311478, 111805.7305227545923, -1.004922949334065759787078 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 28.096672787686 }, 19883901.8482359, 115.174366374632,
&lt;span class="gd"&gt;-        { 178.606868012231657724, -28.472055035513955205, 65.257367020445564176, 107880.4353518862363, -0.99595011741363181912 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.606868012231657724, -28.472055035513955205, 65.257367020445564176, 107880.4353518862363, -1.001708030733315814243475 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 6.50572154271 }, 19917276.4101551, 79.069492719523,
&lt;span class="gd"&gt;-        { 178.926013840891647541, -6.411745140559297675, 100.985091481519557845, 57073.3242952680707, -0.99872883639262399758 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.926013840891647541, -6.411745140559297675, 100.985091481519557845, 57073.3242952680707, -0.9997366669338085325645891 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, .468835109567 }, 19849380.7342734, 80.234636214474,
&lt;span class="gd"&gt;-        { 178.325942223692180692, -.281751687044281805, 99.77243368342786593, 123845.4568822078908, -0.99961835625163353303 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.325942223692180692, -.281751687044281805, 99.77243368342786593, 123845.4568822078908, -0.9998014372091407881673782 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 1.682746325049 }, 19890026.0274781, 10.076182752451,
&lt;span class="gd"&gt;-        { 179.717131561406935483, -.677647430701204515, 169.927471515299313238, 177917.2104306563981, -0.9995304688134802884 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.717131561406935483, -.677647430701204515, 169.927471515299313238, 177917.2104306563981, -0.9995380556912621520019267 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 10.711305126218 }, 19962987.2134077, 7.528253696796,
&lt;span class="gd"&gt;-        { 179.874050163405229937, -10.349315378531556046, 172.480576051850009046, 104175.1095378254456, -0.99808700197799249398 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.874050163405229937, -10.349315378531556046, 172.480576051850009046, 104175.1095378254456, -0.9980718537552389151793625 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 53.374321544652 }, 19980478.1457438, 23.324715976877,
&lt;span class="gd"&gt;-        { 179.729445806011012057, -53.196257519024042184, 156.777734080146664812, 41907.8869272231053, -0.99695322723379165009 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.729445806011012057, -53.196257519024042184, 156.777734080146664812, 41907.8869272231053, -0.9953335962777076279710611 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 39.680221664519 }, 19956191.7841809, 7.075406493429,
&lt;span class="gd"&gt;-        { 179.87506206720154785, -39.256187213040660911, 172.967624741991546131, 86943.8110669895148, -0.99594142266924268192 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.87506206720154785, -39.256187213040660911, 172.967624741991546131, 86943.8110669895148, -0.9948010879096678981434003 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 1.377666714083 }, 19925401.4931301, 95.29199069739,
&lt;span class="gd"&gt;-        { 178.994542525209058878, -1.415358715570225495, 84.7178724483824156, 45800.9140624827059, -0.99967079575227224542 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.994542525209058878, -1.415358715570225495, 84.7178724483824156, 45800.9140624827059, -0.9999980317051245028462866 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 48.751426624188 }, 19988599.1160495, 40.252328570137,
&lt;span class="gd"&gt;-        { 179.661697715070846977, -48.688146707479475147, 139.808452951157199824, 26322.3790862461568, -0.99654000795747821329 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.661697715070846977, -48.688146707479475147, 139.808452951157199824, 26322.3790862461568, -0.9959992457241298392713741 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 59.443039048494 }, 19969935.9534732, 93.052184108221,
&lt;span class="gd"&gt;-        { 179.247605418616998285, -59.454371825393424121, 87.331416513795326158, 25342.4691896499534, -0.99760844716107632824 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.247605418616998285, -59.454371825393424121, 87.331416513795326158, 25342.4691896499534, -1.000207278488970808263958 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 4.122408476235 }, 19938291.6332293, 167.73479753304,
&lt;span class="gd"&gt;-        { 179.749430572914989772, -4.689124208743755363, 12.274635577599782826, 127855.6475863583497, -0.99919442400871316678 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.749430572914989772, -4.689124208743755363, 12.274635577599782826, 127855.6475863583497, -1.000686009028376572587218 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 46.422470082432 }, 19931980.7029341, 86.67365350297,
&lt;span class="gd"&gt;-        { 178.857408435141563774, -46.390934261324541952, 93.852683224054943377, 56114.680046867064, -0.99626178341627869006 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.857408435141563774, -46.390934261324541952, 93.852683224054943377, 56114.680046867064, -0.9996070961163002954936486 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 32.614423729024 }, 19926887.3785175, 24.943814520557,
&lt;span class="gd"&gt;-        { 179.460593512880455451, -32.01874745886238612, 155.229917137448282531, 112355.3319340873104, -0.99584842355298219818 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.460593512880455451, -32.01874745886238612, 155.229917137448282531, 112355.3319340873104, -0.9955621506768719286041558 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 3.242895277973 }, 19964490.4789049, 30.247458779683,
&lt;span class="gd"&gt;-        { 179.556428318080663113, -3.001106476068264917, 149.760178923092147784, 80929.0418317066044, -0.99938705290848561802 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.556428318080663113, -3.001106476068264917, 149.760178923092147784, 80929.0418317066044, -0.9994741842703449041015323 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 6.29069210113 }, 19877160.8505733, 94.34299459284,
&lt;span class="gd"&gt;-        { 178.556859259685624933, -6.354208910915346725, 85.750059038253282986, 94127.1566760840083, -0.99866089792332934927 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.556859259685624933, -6.354208910915346725, 85.750059038253282986, 94127.1566760840083, -0.9999763973509048521344338 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 18.232086569498 }, 19927978.7462175, 164.41905055334,
&lt;span class="gd"&gt;-        { 179.658073278238477245, -18.87394850776853555, 15.640779355822506503, 129771.1882449660559, -0.99696204469368099321 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.658073278238477245, -18.87394850776853555, 15.640779355822506503, 129771.1882449660559, -1.002934604390638743953712 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 12.049849333181 }, 19908004.4552909, 9.418096768309,
&lt;span class="gd"&gt;-        { 179.761046682699610657, -11.201990279782499264, 170.610608272305604585, 157761.5040571466343, -0.99777424243303902696 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.761046682699610657, -11.201990279782499264, 170.610608272305604585, 157761.5040571466343, -0.9977614744975109587398973 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 40.289465276136 }, 19985674.936106, 143.092606818963,
&lt;span class="gd"&gt;-        { 179.644208494155329095, -40.370034926441385999, 36.958610382613096419, 36200.8933724688593, -0.99602880093986934096 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.644208494155329095, -40.370034926441385999, 36.958610382613096419, 36200.8933724688593, -1.004149658760912620643173 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 2.197784650379 }, 19910509.7517973, 1.542117609437,
&lt;span class="gd"&gt;-        { 179.961199531084784854, -1.353440827124394777, 178.458582198505846426, 160403.6285079348996, -0.99948867836200405712 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.961199531084784854, -1.353440827124394777, 178.458582198505846426, 160403.6285079348996, -0.999488724639301117833555 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 1.966575272177 }, 19875595.6267266, 170.112968791865,
&lt;span class="gd"&gt;-        { 179.699817324905962184, -3.101125282483752618, 9.89572776349855838, 192355.7206665719908, -0.99943592820130777721 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.699817324905962184, -3.101125282483752618, 9.89572776349855838, 192355.7206665719908, -1.000154635898045583290543 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 25.078832492684 }, 19887997.7953866, 77.264585323781,
&lt;span class="gd"&gt;-        { 178.600804840925824646, -24.897833702325682511, 103.101167809583406892, 92442.9124509225839, -0.99614702274067257193 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.600804840925824646, -24.897833702325682511, 103.101167809583406892, 92442.9124509225839, -0.9989811898386008840464503 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 31.740361941314 }, 19972325.3556069, 143.930820896999,
&lt;span class="gd"&gt;-        { 179.553485210731879874, -31.909206787477701871, 36.145242998351638503, 54883.4113710054145, -0.99597837783719567195 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.553485210731879874, -31.909206787477701871, 36.145242998351638503, 54883.4113710054145, -1.003794616281159395249425 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, .05479250563 }, 19858049.4780499, 41.349430623518,
&lt;span class="gd"&gt;-        { 178.822647462220726609, .836079031223269324, 138.645259065012502544, 169078.442370111714, -0.9997266451533399767 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.822647462220726609, .836079031223269324, 138.645259065012502544, 169078.442370111714, -0.9997793696948588326408637 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 36.685139871608 }, 19968965.6773632, 89.167975517493,
&lt;span class="gd"&gt;-        { 179.366667224014334712, -36.6833040833258687, 90.921025521408327068, 13327.2156799476918, -0.99592417628692353482 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.366667224014334712, -36.6833040833258687, 90.921025521408327068, 13327.2156799476918, -0.9999165379463486296850866 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 3.451199399671 }, 19938203.3838544, 91.541212417048,
&lt;span class="gd"&gt;-        { 179.107509334399258305, -3.459003521120242021, 88.476282464773035164, 32316.1747698810781, -0.9993151254968675179 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.107509334399258305, -3.459003521120242021, 88.476282464773035164, 32316.1747698810781, -1.000003974843958109899184 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 27.692898794247 }, 19883493.6699045, 88.406440883665,
&lt;span class="gd"&gt;-        { 178.512356615673144314, -27.666009301228316555, 92.036345087713397961, 94128.7880896190836, -0.99595722110800843918 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.512356615673144314, -27.666009301228316555, 92.036345087713397961, 94128.7880896190836, -0.9997364583229516115064732 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 17.363238291869 }, 19980749.7638027, 39.697196316589,
&lt;span class="gd"&gt;-        { 179.567921315455829491, -17.288872648596950413, 140.321938237586060826, 46975.9359427664379, -0.9971281847750985694 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.567921315455829491, -17.288872648596950413, 140.321938237586060826, 46975.9359427664379, -0.9976876919817150126995782 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 37.006775102539 }, 19949309.9180043, 116.455543532607,
&lt;span class="gd"&gt;-        { 179.191103068859169842, -37.156365616364686838, 63.771817992036617793, 45856.1961421018701, -0.99590619058035212419 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.191103068859169842, -37.156365616364686838, 63.771817992036617793, 45856.1961421018701, -1.002219628589184219390543 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 45.572883540957 }, 19940027.8586414, 137.627256708444,
&lt;span class="gd"&gt;-        { 179.224707765088686272, -45.94675931323086696, 42.723991162977357301, 74208.4359612889496, -0.99624902751220101305 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.224707765088686272, -45.94675931323086696, 42.723991162977357301, 74208.4359612889496, -1.003808877864471522261582 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 43.63393981955 }, 19931045.2914508, 91.203625101465,
&lt;span class="gd"&gt;-        { 178.878236417027994157, -43.642335115130514773, 89.268780774643462256, 55253.5406349861764, -0.99608620009401716011 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.878236417027994157, -43.642335115130514773, 89.268780774643462256, 55253.5406349861764, -1.000029741531505212250378 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 38.4995307019 }, 19918391.2222193, 141.232864609445,
&lt;span class="gd"&gt;-        { 179.143856004445269342, -39.042223438550921467, 39.117947060740562295, 102217.2563106863077, -0.99588724635854519729 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.143856004445269342, -39.042223438550921467, 39.117947060740562295, 102217.2563106863077, -1.003881641157329480734325 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 27.55015339382 }, 19986004.7358853, 137.025135713548,
&lt;span class="gd"&gt;-        { 179.596220103573824099, -27.587412128122249651, 42.992898351962011956, 33938.7346646670654, -0.99616434874063342075 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.596220103573824099, -27.587412128122249651, 42.992898351962011956, 33938.7346646670654, -1.003160443902811658199205 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 1.54507498314 }, 19978593.3191777, 36.816106412092,
&lt;span class="gd"&gt;-        { 179.567115633151308577, -1.448861185025252004, 143.185763012309022403, 56320.5800276739168, -0.99970846248568390191 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.567115633151308577, -1.448861185025252004, 143.185763012309022403, 56320.5800276739168, -0.9997704994624671810221454 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 45.217063644222 }, 19987042.0782465, 18.114645812265,
&lt;span class="gd"&gt;-        { 179.807382581661125, -45.086424050571516283, 161.928120141429818658, 45544.2915061261936, -0.99626823185730628563 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.807382581661125, -45.086424050571516283, 161.928120141429818658, 45544.2915061261936, -0.9949741794148549555321798 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 13.473522450751 }, 19987364.078382, 156.839609002403,
&lt;span class="gd"&gt;-        { 179.726941062277208626, -13.570372758027936877, 23.170293747820406391, 65329.9068132034472, -0.99767717345868900392 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.726941062277208626, -13.570372758027936877, 23.170293747820406391, 65329.9068132034472, -1.002190931895065541886065 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 6.287741997374 }, 19912159.8245954, 132.954797451112,
&lt;span class="gd"&gt;-        { 179.071252372259552052, -6.743450924917895817, 47.100789519677419746, 104772.4027498097375, -0.99875728461227553101 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.071252372259552052, -6.743450924917895817, 47.100789519677419746, 104772.4027498097375, -1.00071252411103014544249 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 7.639709001531 }, 19976374.3699535, 29.731916588299,
&lt;span class="gd"&gt;-        { 179.616156296978583335, -7.48702643786017917, 150.279582966919438164, 69224.6591757209539, -0.99861517221927087462 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.616156296978583335, -7.48702643786017917, 150.279582966919438164, 69224.6591757209539, -0.9987897920867413009931961 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 5.893688050348 }, 19886907.2520668, 14.653438882877,
&lt;span class="gd"&gt;-        { 179.586212000450856399, -4.888408917114795625, 165.371181401863458848, 177183.5330818593022, -0.99875831501903877818 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.586212000450856399, -4.888408917114795625, 165.371181401863458848, 177183.5330818593022, -0.9987946470311208020708207 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 61.997076235476 }, 19976288.2901729, 149.562797049254,
&lt;span class="gd"&gt;-        { 179.605779116829636081, -62.19593758437129915, 30.65850204223272625, 36696.2853801462176, -0.99792483695855294101 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.605779116829636081, -62.19593758437129915, 30.65850204223272625, 36696.2853801462176, -1.003730714324371446686916 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 50.507637741656 }, 19979542.5263293, 171.564028344478,
&lt;span class="gd"&gt;-        { 179.893569206021038536, -50.721890799900161112, 8.4746613464253591, 50644.5234828162697, -0.99670226818003293534 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.893569206021038536, -50.721890799900161112, 8.4746613464253591, 50644.5234828162697, -1.005088816322817598953449 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 7.484475238477 }, 19867425.2906303, 57.020570370985,
&lt;span class="gd"&gt;-        { 178.638400003000590878, -6.926155588124333461, 123.087267812322270238, 132929.2775641349633, -0.99841820367274103365 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.638400003000590878, -6.926155588124333461, 123.087267812322270238, 132929.2775641349633, -0.9990970426773380551267907 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 56.851165323215 }, 19988235.9960515, 112.345749045605,
&lt;span class="gd"&gt;-        { 179.587046628550073045, -56.875248360744638525, 67.744017057185404441, 9971.0934553515518, -0.99734849887992094164 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.587046628550073045, -56.875248360744638525, 67.744017057185404441, 9971.0934553515518, -1.001828592498714131384455 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 10.692273150738 }, 19893210.3050033, 102.824601316946,
&lt;span class="gd"&gt;-        { 178.709520715733071393, -10.851727623036704339, 77.308514969817191459, 83032.7122948051111, -0.99796077650539405379 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.709520715733071393, -10.851727623036704339, 77.308514969817191459, 83032.7122948051111, -1.000343455845084243900113 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 46.694739303788 }, 19975447.9283188, 174.663684259477,
&lt;span class="gd"&gt;-        { 179.926838145841924189, -46.948618153686522669, 5.361568174833475454, 59614.5876209460645, -0.9963829846069084395 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.926838145841924189, -46.948618153686522669, 5.361568174833475454, 59614.5876209460645, -1.005204848752017347248908 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 15.804386137005 }, 19855850.8800526, 74.932089158884,
&lt;span class="gd"&gt;-        { 178.367587635209819128, -15.522042847777054984, 105.357235560913450667, 123350.4326645237628, -0.99706137589256171871 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.367587635209819128, -15.522042847777054984, 105.357235560913450667, 123350.4326645237628, -0.9990915785464752706503469 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 4.371450175299 }, 19979071.1035552, 164.163592252794,
&lt;span class="gd"&gt;-        { 179.780887420199549421, -4.566109732313098407, 15.840695025950408814, 84137.2115482558728, -0.99919490909391039946 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.780887420199549421, -4.566109732313098407, 15.840695025950408814, 84137.2115482558728, -1.00076323969894748092211 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 30.894388279688 }, 19968681.8321577, 77.35154610481,
&lt;span class="gd"&gt;-        { 179.375426183521944524, -30.871308884744172663, 102.709506078439532936, 14048.0277985734058, -0.99599179229723178164 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.375426183521944524, -30.871308884744172663, 102.709506078439532936, 14048.0277985734058, -0.9989751763364228440383701 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 9.541166838639 }, 19848553.7844137, 118.441353539081,
&lt;span class="gd"&gt;-        { 178.432934555386452839, -10.09982228112793472, 61.736686215549403663, 144831.1911566651614, -0.99800476808793336936 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.432934555386452839, -10.09982228112793472, 61.736686215549403663, 144831.1911566651614, -1.000605486201104942726309 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 8.489292700054 }, 19995477.1669578, 171.963952699866,
&lt;span class="gd"&gt;-        { 179.906698338023119097, -8.559237750032113623, 8.037517851139094467, 72192.60793572974, -0.9984792781676200546 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.906698338023119097, -8.559237750032113623, 8.037517851139094467, 72192.60793572974, -1.001520684863064588341133 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 19.562401114224 }, 19893208.1788508, 126.362762598128,
&lt;span class="gd"&gt;-        { 178.838724116996037606, -20.05038360490599475, 53.875560227496658204, 112181.7524188837615, -0.99671734436245396083 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.838724116996037606, -20.05038360490599475, 53.875560227496658204, 112181.7524188837615, -1.001852026688027773315692 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 42.260350252749 }, 19942715.0054774, 170.703419847646,
&lt;span class="gd"&gt;-        { 179.807860448877064601, -42.79985897702184353, 9.377654670896439828, 96336.3477142010769, -0.99607495753304098329 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.807860448877064601, -42.79985897702184353, 9.377654670896439828, 96336.3477142010769, -1.005086424064435460636394 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 24.511403144656 }, 19924809.5184876, 102.913211410163,
&lt;span class="gd"&gt;-        { 178.957598444862223515, -24.616808725039883945, 77.297538210434837096, 55403.453072179318, -0.99629589741710011808 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.957598444862223515, -24.616808725039883945, 77.297538210434837096, 55403.453072179318, -1.00084083091888388877648 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 20.844284170708 }, 19909084.6340808, 44.172784008084,
&lt;span class="gd"&gt;-        { 179.069258863637226633, -20.321320573298341477, 136.01627115731728436, 111009.0987238994608, -0.99659406562612795621 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.069258863637226633, -20.321320573298341477, 136.01627115731728436, 111009.0987238994608, -0.997389183621779017239594 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 2.426010809098 }, 19840940.6924189, 94.315194952561,
&lt;span class="gd"&gt;-        { 178.236397468862000784, -2.513715200833756776, 85.734896842737189557, 130002.6104886615638, -0.99922656404178245015 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.236397468862000784, -2.513715200833756776, 85.734896842737189557, 130002.6104886615638, -0.9998252498449916409405634 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 6.600682554664 }, 19878412.28273, 168.167678684515,
&lt;span class="gd"&gt;-        { 179.646475458013797028, -7.699164822656561787, 11.861035812918738552, 187426.3958525886692, -0.99861342289130949901 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.646475458013797028, -7.699164822656561787, 11.861035812918738552, 187426.3958525886692, -1.000982848560649784010017 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 23.372339802326 }, 19899498.4582543, 161.197647943542,
&lt;span class="gd"&gt;-        { 179.499422665106094027, -24.239465200482591299, 18.932355367478826536, 151863.2545535951091, -0.99635421423038206257 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.499422665106094027, -24.239465200482591299, 18.932355367478826536, 151863.2545535951091, -1.003476668684313999812749 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 16.194668264095 }, 19874825.6683239, 148.942349959054,
&lt;span class="gd"&gt;-        { 179.115193814080201851, -17.129419031459576897, 31.225656401221968078, 166033.3161394594622, -0.99709176416695455281 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.115193814080201851, -17.129419031459576897, 31.225656401221968078, 166033.3161394594622, -1.002220322222336505702499 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 1.528726471528 }, 19897803.9939987, 69.212891442493,
&lt;span class="gd"&gt;-        { 178.791047180477802091, -1.282203000582034597, 110.802928803578167132, 85252.8333849204133, -0.99957999688525089876 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.791047180477802091, -1.282203000582034597, 110.802928803578167132, 85252.8333849204133, -0.9998271442281568740497941 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 6.297249676078 }, 19864042.0495193, 56.274639904925,
&lt;span class="gd"&gt;-        { 178.623258703845895437, -5.709470001196540278, 123.817184177744186806, 137475.1283083659258, -0.99861474729636867664 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.623258703845895437, -5.709470001196540278, 123.817184177744186806, 137475.1283083659258, -0.9991904501783995893996783 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 17.393540327984 }, 19962624.6302607, 107.855062015266,
&lt;span class="gd"&gt;-        { 179.330156510680163326, -17.431100690958209424, 72.181322855288535245, 19320.5501845044839, -0.99711019484200580365 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.330156510680163326, -17.431100690958209424, 72.181322855288535245, 19320.5501845044839, -1.000918417796891303852592 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 46.284685151236 }, 19990422.3478916, 14.758013867151,
&lt;span class="gd"&gt;-        { 179.852534804091121255, -46.176234945675219984, 165.271681964991897184, 42614.1796365710104, -0.99634649632519134421 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.852534804091121255, -46.176234945675219984, 165.271681964991897184, 42614.1796365710104, -0.9948945922618399753472283 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 14.924320176299 }, 19891861.8615337, 31.446544793174,
&lt;span class="gd"&gt;-        { 179.195663739713760883, -14.125476432252858442, 148.678916887199611191, 149419.6596309045804, -0.99729741460688270394 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.195663739713760883, -14.125476432252858442, 148.678916887199611191, 149419.6596309045804, -0.9976201425853329563713315 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 23.668824656069 }, 19938736.4442268, 148.091483667618,
&lt;span class="gd"&gt;-        { 179.409875478773990359, -24.107855233601412399, 32.02919257641173958, 97771.7687385830819, -0.99640366072092678706 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.409875478773990359, -24.107855233601412399, 32.02919257641173958, 97771.7687385830819, -1.003232628720005910772511 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 46.986276695896 }, 19968596.0414782, 174.796708941456,
&lt;span class="gd"&gt;-        { 179.92040916864362177, -47.301644191214905832, 5.234240076649939638, 66113.7417494369769, -0.99639889458222818952 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.92040916864362177, -47.301644191214905832, 5.234240076649939638, 66113.7417494369769, -1.005190954526080850226118 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 65.946144289524 }, 19993734.5109736, 25.375428509648,
&lt;span class="gd"&gt;-        { 179.808282612725835525, -65.871840130833632868, 154.703163938350061652, 18355.2254271672769, -0.99838571720931879039 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.808282612725835525, -65.871840130833632868, 154.703163938350061652, 18355.2254271672769, -0.9964369359146106126432461 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 10.950298933293 }, 19975919.5586889, 28.779018914489,
&lt;span class="gd"&gt;-        { 179.624609619829763098, -10.787771536605316781, 151.238005588662201946, 70291.1998404303581, -0.99806099887646559932 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.624609619829763098, -10.787771536605316781, 151.238005588662201946, 70291.1998404303581, -0.9982720718341152112444356 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 13.609869340778 }, 19913213.8514358, 129.616021271129,
&lt;span class="gd"&gt;-        { 179.035623147420893383, -14.023624108675206222, 50.506400999466711623, 97596.7664002074776, -0.99755852834357494618 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.035623147420893383, -14.023624108675206222, 50.506400999466711623, 97596.7664002074776, -1.00146664642314031927646 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 48.701427557433 }, 19972955.2699173, 102.875149183407,
&lt;span class="gd"&gt;-        { 179.385565054218238481, -48.735316652259656533, 77.294384444682547869, 18461.7742226227697, -0.99652223116852467477 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.385565054218238481, -48.735316652259656533, 77.294384444682547869, 18461.7742226227697, -1.001146768554290695098057 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 31.519172055785 }, 19952318.3772514, 26.247105619999,
&lt;span class="gd"&gt;-        { 179.555251675378549409, -31.140142027808697534, 153.865822276646938125, 86354.7117605101002, -0.99593468381798511135 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.555251675378549409, -31.140142027808697534, 153.865822276646938125, 86354.7117605101002, -0.9957399483998249749283627 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 31.863784754278 }, 19993324.8682601, 29.572313410211,
&lt;span class="gd"&gt;-        { 179.722489476483407524, -31.826935359797657785, 150.440607907359037187, 41427.6181613499234, -0.9959831508497293262 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.722489476483407524, -31.826935359797657785, 150.440607907359037187, 41427.6181613499234, -0.9958880090011472640794746 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 76.434608546092 }, 19997750.023578, 167.428385412814,
&lt;span class="gd"&gt;-        { 179.918287057674124459, -76.48787937532808951, 12.621032110142724567, 9619.5267710862108, -0.99943031992965880583 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.918287057674124459, -76.48787937532808951, 12.621032110142724567, 9619.5267710862108, -1.002339638930915896456245 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 73.114273316483 }, 19992866.6147806, 78.154765899661,
&lt;span class="gd"&gt;-        { 179.576736605988553624, -73.098788070892914568, 102.085693546950923465, 8580.6475692800946, -0.99913315932148838439 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.576736605988553624, -73.098788070892914568, 102.085693546950923465, 8580.6475692800946, -0.9993841433084754598747609 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 1.125639056292 }, 19852573.5442848, 67.184842289382,
&lt;span class="gd"&gt;-        { 178.426819580880619395, -.694775021853292564, 112.831314850896246589, 132932.8743502563937, -0.99950982898040086067 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.426819580880619395, -.694775021853292564, 112.831314850896246589, 132932.8743502563937, -0.9997329579628334908763393 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    }
&lt;span class="w"&gt; &lt;/span&gt;};


commit 96074e2ab2e9f3bddd81f56c5d23438574c0c040
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Sat Jun 9 16:15:55 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [test] Use series order 2 for Karney&amp;#39;s direct method

&lt;span class="w"&gt; &lt;/span&gt;   Boost Geometry dataset is created with series order 2. Therefore,
&lt;span class="w"&gt; &lt;/span&gt;   the tests fail at all other values.

&lt;span class="gh"&gt;diff --git a/test/formulas/direct.cpp b/test/formulas/direct.cpp&lt;/span&gt;
&lt;span class="gh"&gt;index bcfcec3..9cd4bdf 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/test/formulas/direct.cpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/test/formulas/direct.cpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -64,7 +64,7 @@ void test_all(expected_results const&amp;amp; results)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    result.reverse_azimuth *= r2d;
&lt;span class="w"&gt; &lt;/span&gt;    check_direct(result, results.thomas, results.karney, 0.0000001);

&lt;span class="gd"&gt;-    typedef bg::formula::karney_direct&amp;lt;double, true, true, true, true, 8&amp;gt; ka_t;&lt;/span&gt;
&lt;span class="gi"&gt;+    typedef bg::formula::karney_direct&amp;lt;double, true, true, true, true, 2&amp;gt; ka_t;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    result = ka_t::apply(lon1d, lat1d, distance, azi12d, spheroid);
&lt;span class="w"&gt; &lt;/span&gt;    check_direct(result, results.thomas, results.karney, 0.0000001);
&lt;span class="w"&gt; &lt;/span&gt;}

commit 043f401fbe22a2fbf8c3cb3e852a16c00e6fd942
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Sat Jun 9 16:11:46 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [util] Use Maxima generated function for computing C3x coefficients

&lt;span class="w"&gt; &lt;/span&gt;   The script used is geod.mac:
&lt;span class="w"&gt; &lt;/span&gt;   https://sourceforge.net/p/geographiclib/code/ci/release/tree/maxima/geod.mac

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/util/series_expansion.hpp b/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 02f1074..e3dc826 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -496,230 +496,112 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;     geometry/doc/other/maxima/geod.mac
&lt;span class="w"&gt; &lt;/span&gt;    */
&lt;span class="w"&gt; &lt;/span&gt;    template &amp;lt;typename CT, int SeriesOrder&amp;gt;
&lt;span class="gd"&gt;-    void evaluate_coeffs_C3x(CT const&amp;amp; n, CT c[], const CT coeff[])&lt;/span&gt;
&lt;span class="gd"&gt;-    {&lt;/span&gt;
&lt;span class="gd"&gt;-        int offset = 0, k = 0;&lt;/span&gt;
&lt;span class="gd"&gt;-        // l is index of C3[l].&lt;/span&gt;
&lt;span class="gd"&gt;-        for (int l = 1; l &amp;lt; SeriesOrder; ++l)&lt;/span&gt;
&lt;span class="gd"&gt;-        {&lt;/span&gt;
&lt;span class="gd"&gt;-            for (int j = SeriesOrder - 1; j &amp;gt;= l; --j)&lt;/span&gt;
&lt;span class="gd"&gt;-            {&lt;/span&gt;
&lt;span class="gd"&gt;-                // Order of polynomial in n.&lt;/span&gt;
&lt;span class="gd"&gt;-                int m = std::min(SeriesOrder - j - 1, j);&lt;/span&gt;
&lt;span class="gd"&gt;-                c[k++] = math::polyval(m, coeff + offset, n) /&lt;/span&gt;
&lt;span class="gd"&gt;-                coeff[offset + m + 1];&lt;/span&gt;
&lt;span class="gd"&gt;-                offset += m + 2;&lt;/span&gt;
&lt;span class="gd"&gt;-            }&lt;/span&gt;
&lt;span class="gd"&gt;-        }&lt;/span&gt;
&lt;span class="gd"&gt;-    }&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    template &amp;lt;typename CT, int SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gd"&gt;-    void evaluate_coeffs_C3x(CT const&amp;amp; n, CT c[]) {&lt;/span&gt;
&lt;span class="gd"&gt;-        if (SeriesOrder == 3) {&lt;/span&gt;
&lt;span class="gd"&gt;-            static const CT coeff[] = {&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[1], coeff of eps^2, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gd"&gt;-            1, 8,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[1], coeff of eps^1, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gd"&gt;-            -1, 1, 4,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[2], coeff of eps^2, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gd"&gt;-            1, 16,&lt;/span&gt;
&lt;span class="gd"&gt;-            };&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-            evaluate_coeffs_C3x&amp;lt;CT, SeriesOrder&amp;gt;(n, c, coeff);&lt;/span&gt;
&lt;span class="gd"&gt;-        }&lt;/span&gt;
&lt;span class="gd"&gt;-        else if (SeriesOrder == 4) {&lt;/span&gt;
&lt;span class="gd"&gt;-            static const CT coeff[] = {&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[1], coeff of eps^3, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gd"&gt;-            3, 64,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[1], coeff of eps^2, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gd"&gt;-            // This is a case where a leading 0 term has been inserted to maintain the&lt;/span&gt;
&lt;span class="gd"&gt;-            // pattern in the orders of the polynomials.&lt;/span&gt;
&lt;span class="gd"&gt;-            0, 1, 8,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[1], coeff of eps^1, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gd"&gt;-            -1, 1, 4,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[2], coeff of eps^3, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gd"&gt;-            3, 64,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[2], coeff of eps^2, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gd"&gt;-            -3, 2, 32,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[3], coeff of eps^3, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gd"&gt;-            5, 192,&lt;/span&gt;
&lt;span class="gd"&gt;-            };&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-            evaluate_coeffs_C3x&amp;lt;CT, SeriesOrder&amp;gt;(n, c, coeff);&lt;/span&gt;
&lt;span class="gd"&gt;-        }&lt;/span&gt;
&lt;span class="gd"&gt;-        else if (SeriesOrder == 5) {&lt;/span&gt;
&lt;span class="gd"&gt;-            static const CT coeff[] = {&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[1], coeff of eps^4, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gd"&gt;-            5, 128,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[1], coeff of eps^3, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gd"&gt;-            3, 3, 64,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[1], coeff of eps^2, polynomial in n of order 2&lt;/span&gt;
&lt;span class="gd"&gt;-            -1, 0, 1, 8,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[1], coeff of eps^1, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gd"&gt;-            -1, 1, 4,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[2], coeff of eps^4, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gd"&gt;-            3, 128,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[2], coeff of eps^3, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gd"&gt;-            -2, 3, 64,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[2], coeff of eps^2, polynomial in n of order 2&lt;/span&gt;
&lt;span class="gd"&gt;-            1, -3, 2, 32,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[3], coeff of eps^4, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gd"&gt;-            3, 128,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[3], coeff of eps^3, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gd"&gt;-            -9, 5, 192,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[4], coeff of eps^4, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gd"&gt;-            7, 512,&lt;/span&gt;
&lt;span class="gd"&gt;-            };&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-            evaluate_coeffs_C3x&amp;lt;CT, SeriesOrder&amp;gt;(n, c, coeff);&lt;/span&gt;
&lt;span class="gd"&gt;-        }&lt;/span&gt;
&lt;span class="gd"&gt;-        else if (SeriesOrder == 6) {&lt;/span&gt;
&lt;span class="gd"&gt;-            static const CT coeff[] = {&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[1], coeff of eps^5, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gd"&gt;-            3, 128,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[1], coeff of eps^4, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gd"&gt;-            2, 5, 128,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[1], coeff of eps^3, polynomial in n of order 2&lt;/span&gt;
&lt;span class="gd"&gt;-            -1, 3, 3, 64,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[1], coeff of eps^2, polynomial in n of order 2&lt;/span&gt;
&lt;span class="gd"&gt;-            -1, 0, 1, 8,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[1], coeff of eps^1, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gd"&gt;-            -1, 1, 4,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[2], coeff of eps^5, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gd"&gt;-            5, 256,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[2], coeff of eps^4, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gd"&gt;-            1, 3, 128,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[2], coeff of eps^3, polynomial in n of order 2&lt;/span&gt;
&lt;span class="gd"&gt;-            -3, -2, 3, 64,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[2], coeff of eps^2, polynomial in n of order 2&lt;/span&gt;
&lt;span class="gd"&gt;-            1, -3, 2, 32,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[3], coeff of eps^5, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gd"&gt;-            7, 512,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[3], coeff of eps^4, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gd"&gt;-            -10, 9, 384,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[3], coeff of eps^3, polynomial in n of order 2&lt;/span&gt;
&lt;span class="gd"&gt;-            5, -9, 5, 192,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[4], coeff of eps^5, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gd"&gt;-            7, 512,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[4], coeff of eps^4, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gd"&gt;-            -14, 7, 512,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[5], coeff of eps^5, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gd"&gt;-            21, 2560,&lt;/span&gt;
&lt;span class="gd"&gt;-            };&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-            evaluate_coeffs_C3x&amp;lt;CT, SeriesOrder&amp;gt;(n, c, coeff);&lt;/span&gt;
&lt;span class="gd"&gt;-        }&lt;/span&gt;
&lt;span class="gd"&gt;-        else if (SeriesOrder == 7) {&lt;/span&gt;
&lt;span class="gd"&gt;-            static const CT coeff[] = {&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[1], coeff of eps^6, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gd"&gt;-            21, 1024,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[1], coeff of eps^5, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gd"&gt;-            11, 12, 512,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[1], coeff of eps^4, polynomial in n of order 2&lt;/span&gt;
&lt;span class="gd"&gt;-            2, 2, 5, 128,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[1], coeff of eps^3, polynomial in n of order 3&lt;/span&gt;
&lt;span class="gd"&gt;-            -5, -1, 3, 3, 64,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[1], coeff of eps^2, polynomial in n of order 2&lt;/span&gt;
&lt;span class="gd"&gt;-            -1, 0, 1, 8,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[1], coeff of eps^1, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gd"&gt;-            -1, 1, 4,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[2], coeff of eps^6, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gd"&gt;-            27, 2048,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[2], coeff of eps^5, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gd"&gt;-            1, 5, 256,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[2], coeff of eps^4, polynomial in n of order 2&lt;/span&gt;
&lt;span class="gd"&gt;-            -9, 2, 6, 256,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[2], coeff of eps^3, polynomial in n of order 3&lt;/span&gt;
&lt;span class="gd"&gt;-            2, -3, -2, 3, 64,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[2], coeff of eps^2, polynomial in n of order 2&lt;/span&gt;
&lt;span class="gd"&gt;-            1, -3, 2, 32,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[3], coeff of eps^6, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gd"&gt;-            3, 256,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[3], coeff of eps^5, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gd"&gt;-            -4, 21, 1536,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[3], coeff of eps^4, polynomial in n of order 2&lt;/span&gt;
&lt;span class="gd"&gt;-            -6, -10, 9, 384,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[3], coeff of eps^3, polynomial in n of order 3&lt;/span&gt;
&lt;span class="gd"&gt;-            -1, 5, -9, 5, 192,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[4], coeff of eps^6, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gd"&gt;-            9, 1024,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[4], coeff of eps^5, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gd"&gt;-            -10, 7, 512,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[4], coeff of eps^4, polynomial in n of order 2&lt;/span&gt;
&lt;span class="gd"&gt;-            10, -14, 7, 512,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[5], coeff of eps^6, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gd"&gt;-            9, 1024,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[5], coeff of eps^5, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gd"&gt;-            -45, 21, 2560,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[6], coeff of eps^6, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gd"&gt;-            11, 2048,&lt;/span&gt;
&lt;span class="gd"&gt;-            };&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-            evaluate_coeffs_C3x&amp;lt;CT, SeriesOrder&amp;gt;(n, c, coeff);&lt;/span&gt;
&lt;span class="gd"&gt;-        }&lt;/span&gt;
&lt;span class="gd"&gt;-        else if (SeriesOrder == 8) {&lt;/span&gt;
&lt;span class="gd"&gt;-            static const CT coeff[] = {&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[1], coeff of eps^7, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gd"&gt;-            243, 16384,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[1], coeff of eps^6, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gd"&gt;-            10, 21, 1024,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[1], coeff of eps^5, polynomial in n of order 2&lt;/span&gt;
&lt;span class="gd"&gt;-            3, 11, 12, 512,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[1], coeff of eps^4, polynomial in n of order 3&lt;/span&gt;
&lt;span class="gd"&gt;-            -2, 2, 2, 5, 128,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[1], coeff of eps^3, polynomial in n of order 3&lt;/span&gt;
&lt;span class="gd"&gt;-            -5, -1, 3, 3, 64,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[1], coeff of eps^2, polynomial in n of order 2&lt;/span&gt;
&lt;span class="gd"&gt;-            -1, 0, 1, 8,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[1], coeff of eps^1, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gd"&gt;-            -1, 1, 4,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[2], coeff of eps^7, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gd"&gt;-            187, 16384,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[2], coeff of eps^6, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gd"&gt;-            69, 108, 8192,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[2], coeff of eps^5, polynomial in n of order 2&lt;/span&gt;
&lt;span class="gd"&gt;-            -2, 1, 5, 256,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[2], coeff of eps^4, polynomial in n of order 3&lt;/span&gt;
&lt;span class="gd"&gt;-            -6, -9, 2, 6, 256,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[2], coeff of eps^3, polynomial in n of order 3&lt;/span&gt;
&lt;span class="gd"&gt;-            2, -3, -2, 3, 64,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[2], coeff of eps^2, polynomial in n of order 2&lt;/span&gt;
&lt;span class="gd"&gt;-            1, -3, 2, 32,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[3], coeff of eps^7, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gd"&gt;-            139, 16384,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[3], coeff of eps^6, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gd"&gt;-            -1, 12, 1024,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[3], coeff of eps^5, polynomial in n of order 2&lt;/span&gt;
&lt;span class="gd"&gt;-            -77, -8, 42, 3072,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[3], coeff of eps^4, polynomial in n of order 3&lt;/span&gt;
&lt;span class="gd"&gt;-            10, -6, -10, 9, 384,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[3], coeff of eps^3, polynomial in n of order 3&lt;/span&gt;
&lt;span class="gd"&gt;-            -1, 5, -9, 5, 192,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[4], coeff of eps^7, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gd"&gt;-            127, 16384,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[4], coeff of eps^6, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gd"&gt;-            -43, 72, 8192,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[4], coeff of eps^5, polynomial in n of order 2&lt;/span&gt;
&lt;span class="gd"&gt;-            -7, -40, 28, 2048,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[4], coeff of eps^4, polynomial in n of order 3&lt;/span&gt;
&lt;span class="gd"&gt;-            -7, 20, -28, 14, 1024,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[5], coeff of eps^7, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gd"&gt;-            99, 16384,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[5], coeff of eps^6, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gd"&gt;-            -15, 9, 1024,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[5], coeff of eps^5, polynomial in n of order 2&lt;/span&gt;
&lt;span class="gd"&gt;-            75, -90, 42, 5120,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[6], coeff of eps^7, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gd"&gt;-            99, 16384,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[6], coeff of eps^6, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gd"&gt;-            -99, 44, 8192,&lt;/span&gt;
&lt;span class="gd"&gt;-            // C3[7], coeff of eps^7, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gd"&gt;-            429, 114688,&lt;/span&gt;
&lt;span class="gd"&gt;-            };&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-            evaluate_coeffs_C3x&amp;lt;CT, SeriesOrder&amp;gt;(n, c, coeff);&lt;/span&gt;
&lt;span class="gi"&gt;+    static inline void evaluate_coeffs_C3x(CT const&amp;amp; n, CT c[]) {&lt;/span&gt;
&lt;span class="gi"&gt;+        const CT n2 = math::sqr(n);&lt;/span&gt;
&lt;span class="gi"&gt;+        switch (SeriesOrder) {&lt;/span&gt;
&lt;span class="gi"&gt;+        case 0:&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 1:&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 2:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[0] = (CT(1)-n)/CT(4);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 3:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[0] = (CT(1)-n)/CT(4);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = (CT(1)-n2)/CT(8);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[2] = ((n-CT(3))*n+CT(2))/CT(32);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 4:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[0] = (CT(1)-n)/CT(4);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = (CT(1)-n2)/CT(8);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[2] = (n*((-CT(5)*n-CT(1))*n+CT(3))+CT(3))/CT(64);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[3] = ((n-CT(3))*n+CT(2))/CT(32);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[4] = (n*(n*(CT(2)*n-CT(3))-CT(2))+CT(3))/CT(64);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[5] = (n*((CT(5)-n)*n-CT(9))+CT(5))/CT(192);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 5:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[0] = (CT(1)-n)/CT(4);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = (CT(1)-n2)/CT(8);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[2] = (n*((-CT(5)*n-CT(1))*n+CT(3))+CT(3))/CT(64);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[3] = (n*((CT(2)-CT(2)*n)*n+CT(2))+CT(5))/CT(128);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[4] = ((n-CT(3))*n+CT(2))/CT(32);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[5] = (n*(n*(CT(2)*n-CT(3))-CT(2))+CT(3))/CT(64);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[6] = (n*((-CT(6)*n-CT(9))*n+CT(2))+CT(6))/CT(256);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[7] = (n*((CT(5)-n)*n-CT(9))+CT(5))/CT(192);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[8] = (n*(n*(CT(10)*n-CT(6))-CT(10))+CT(9))/CT(384);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[9] = (n*((CT(20)-CT(7)*n)*n-CT(28))+CT(14))/CT(1024);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 6:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[0] = (CT(1)-n)/CT(4);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = (CT(1)-n2)/CT(8);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[2] = (n*((-CT(5)*n-CT(1))*n+CT(3))+CT(3))/CT(64);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[3] = (n*((CT(2)-CT(2)*n)*n+CT(2))+CT(5))/CT(128);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[4] = (n*(CT(3)*n+CT(11))+CT(12))/CT(512);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[5] = ((n-CT(3))*n+CT(2))/CT(32);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[6] = (n*(n*(CT(2)*n-CT(3))-CT(2))+CT(3))/CT(64);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[7] = (n*((-CT(6)*n-CT(9))*n+CT(2))+CT(6))/CT(256);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[8] = ((CT(1)-CT(2)*n)*n+CT(5))/CT(256);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[9] = (n*((CT(5)-n)*n-CT(9))+CT(5))/CT(192);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[10] = (n*(n*(CT(10)*n-CT(6))-CT(10))+CT(9))/CT(384);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[11] = ((-CT(77)*n-CT(8))*n+CT(42))/CT(3072);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[12] = (n*((CT(20)-CT(7)*n)*n-CT(28))+CT(14))/CT(1024);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[13] = ((-CT(7)*n-CT(40))*n+CT(28))/CT(2048);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[14] = (n*(CT(75)*n-CT(90))+CT(42))/CT(5120);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 7:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[0] = (CT(1)-n)/CT(4);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = (CT(1)-n2)/CT(8);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[2] = (n*((-CT(5)*n-CT(1))*n+CT(3))+CT(3))/CT(64);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[3] = (n*((CT(2)-CT(2)*n)*n+CT(2))+CT(5))/CT(128);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[4] = (n*(CT(3)*n+CT(11))+CT(12))/CT(512);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[5] = (CT(10)*n+CT(21))/CT(1024);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[6] = ((n-CT(3))*n+CT(2))/CT(32);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[7] = (n*(n*(CT(2)*n-CT(3))-CT(2))+CT(3))/CT(64);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[8] = (n*((-CT(6)*n-CT(9))*n+CT(2))+CT(6))/CT(256);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[9] = ((CT(1)-CT(2)*n)*n+CT(5))/CT(256);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[10] = (CT(69)*n+CT(108))/CT(8192);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[11] = (n*((CT(5)-n)*n-CT(9))+CT(5))/CT(192);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[12] = (n*(n*(CT(10)*n-CT(6))-CT(10))+CT(9))/CT(384);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[13] = ((-CT(77)*n-CT(8))*n+CT(42))/CT(3072);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[14] = (CT(12)-n)/CT(1024);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[15] = (n*((CT(20)-CT(7)*n)*n-CT(28))+CT(14))/CT(1024);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[16] = ((-CT(7)*n-CT(40))*n+CT(28))/CT(2048);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[17] = (CT(72)-CT(43)*n)/CT(8192);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[18] = (n*(CT(75)*n-CT(90))+CT(42))/CT(5120);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[19] = (CT(9)-CT(15)*n)/CT(1024);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[20] = (CT(44)-CT(99)*n)/CT(8192);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 8:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[0] = (CT(1)-n)/CT(4);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = (CT(1)-n2)/CT(8);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[2] = (n*((-CT(5)*n-CT(1))*n+CT(3))+CT(3))/CT(64);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[3] = (n*((CT(2)-CT(2)*n)*n+CT(2))+CT(5))/CT(128);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[4] = (n*(CT(3)*n+CT(11))+CT(12))/CT(512);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[5] = (CT(10)*n+CT(21))/CT(1024);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[6] = CT(243)/CT(16384);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[7] = ((n-CT(3))*n+CT(2))/CT(32);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[8] = (n*(n*(CT(2)*n-CT(3))-CT(2))+CT(3))/CT(64);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[9] = (n*((-CT(6)*n-CT(9))*n+CT(2))+CT(6))/CT(256);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[10] = ((CT(1)-CT(2)*n)*n+CT(5))/CT(256);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[11] = (CT(69)*n+CT(108))/CT(8192);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[12] = CT(187)/CT(16384);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[13] = (n*((CT(5)-n)*n-CT(9))+CT(5))/CT(192);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[14] = (n*(n*(CT(10)*n-CT(6))-CT(10))+CT(9))/CT(384);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[15] = ((-CT(77)*n-CT(8))*n+CT(42))/CT(3072);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[16] = (CT(12)-n)/CT(1024);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[17] = CT(139)/CT(16384);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[18] = (n*((CT(20)-CT(7)*n)*n-CT(28))+CT(14))/CT(1024);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[19] = ((-CT(7)*n-CT(40))*n+CT(28))/CT(2048);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[20] = (CT(72)-CT(43)*n)/CT(8192);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[21] = CT(127)/CT(16384);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[22] = (n*(CT(75)*n-CT(90))+CT(42))/CT(5120);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[23] = (CT(9)-CT(15)*n)/CT(1024);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[24] = CT(99)/CT(16384);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[25] = (CT(44)-CT(99)*n)/CT(8192);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[26] = CT(99)/CT(16384);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[27] = CT(429)/CT(114688);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="gd"&gt;-        // Post condition: offset == sizeof(coeff) / sizeof(CT) &amp;amp;&amp;amp; k == coeffs_C3_size&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    }

&lt;span class="w"&gt; &lt;/span&gt;    /*

commit 276e8e6d7a673a5e9676c4135f23805697a80eee
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Thu Jun 7 18:28:22 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [formulas] Add function for computing the length at the meridians

&lt;span class="w"&gt; &lt;/span&gt;   This method is an integral part of Karney&amp;#39;s solution to inverse
&lt;span class="w"&gt; &lt;/span&gt;   geodesic problem. It is only invoked for points that lie on
&lt;span class="w"&gt; &lt;/span&gt;   the meridian, or are close to it.

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/formulas/karney_inverse.hpp b/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 4929f9c..9297034 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -49,6 +49,12 @@ class karney_inverse&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;public:
&lt;span class="w"&gt; &lt;/span&gt;    typedef result_inverse&amp;lt;CT&amp;gt; result_type;
&lt;span class="gi"&gt;+    static CT constexpr c0 = 0;&lt;/span&gt;
&lt;span class="gi"&gt;+    static CT constexpr c1 = 1;&lt;/span&gt;
&lt;span class="gi"&gt;+    static CT constexpr c2 = 2;&lt;/span&gt;
&lt;span class="gi"&gt;+    static CT constexpr c3 = 3;&lt;/span&gt;
&lt;span class="gi"&gt;+    static CT constexpr c180 = 180;&lt;/span&gt;
&lt;span class="gi"&gt;+    static CT constexpr c90 = 90;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    template &amp;lt;typename T1, typename T2, typename Spheroid&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;    static inline result_type apply(T1 const&amp;amp; lo1,
&lt;span class="gu"&gt;@@ -65,10 +71,16 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT lon1 = lo1;
&lt;span class="w"&gt; &lt;/span&gt;        CT lon2 = lo2;

&lt;span class="gd"&gt;-        CT const c1 = 1;&lt;/span&gt;
&lt;span class="gd"&gt;-        CT const c2 = 2;&lt;/span&gt;
&lt;span class="gd"&gt;-        CT const c180 = 180;&lt;/span&gt;
&lt;span class="gd"&gt;-        CT const c90 = 90;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT tiny = std::sqrt(std::numeric_limits&amp;lt;CT&amp;gt;::min());&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const b = CT(get_radius&amp;lt;2&amp;gt;(spheroid));&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const f = formula::flattening&amp;lt;CT&amp;gt;(spheroid);&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const one_minus_f = c1 - f;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const two_minus_f = c2 - f;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const n = f / two_minus_f;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const e2 = f * two_minus_f;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const ep2 = e2 / math::sqr(one_minus_f);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        // Compute the longitudinal difference.
&lt;span class="w"&gt; &lt;/span&gt;        CT lon12_error;
&lt;span class="gu"&gt;@@ -80,6 +92,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        lon12 = lon12_sign * math::round_angle(lon12);
&lt;span class="w"&gt; &lt;/span&gt;        lon12_error = math::round_angle((c180 - lon12) - lon12_sign * lon12_error);

&lt;span class="gi"&gt;+        // Convert to radians.&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT lam12 = lon12 * math::d2r&amp;lt;CT&amp;gt;();
&lt;span class="w"&gt; &lt;/span&gt;        CT sin_lam12;
&lt;span class="w"&gt; &lt;/span&gt;        CT cos_lam12;
&lt;span class="gu"&gt;@@ -112,7 +125,202 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        int lat_sign = lat1 &amp;lt; 0 ? 1 : -1;
&lt;span class="w"&gt; &lt;/span&gt;        lat1 *= lat_sign;
&lt;span class="w"&gt; &lt;/span&gt;        lat2 *= lat_sign;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        CT sin_beta1, cos_beta1;&lt;/span&gt;
&lt;span class="gi"&gt;+        math::sin_cos_degrees(lat1, sin_beta1, cos_beta1);&lt;/span&gt;
&lt;span class="gi"&gt;+        sin_beta1 *= one_minus_f;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        math::normalize(sin_beta1, cos_beta1);&lt;/span&gt;
&lt;span class="gi"&gt;+        cos_beta1 = std::max(tiny, cos_beta1);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        CT sin_beta2, cos_beta2;&lt;/span&gt;
&lt;span class="gi"&gt;+        math::sin_cos_degrees(lat2, sin_beta2, cos_beta2);&lt;/span&gt;
&lt;span class="gi"&gt;+        sin_beta2 *= one_minus_f;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        math::normalize(sin_beta2, cos_beta2);&lt;/span&gt;
&lt;span class="gi"&gt;+        cos_beta2 = std::max(tiny, cos_beta2);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        // If cos_beta1 &amp;lt; -sin_beta1, then cos_beta2 - cos_beta1 is a&lt;/span&gt;
&lt;span class="gi"&gt;+        // sensitive measure of the |beta1| - |beta2|.  Alternatively,&lt;/span&gt;
&lt;span class="gi"&gt;+        // (cos_beta1 &amp;gt;= -sin_beta1), abs(sin_beta2) + sin_beta1 is&lt;/span&gt;
&lt;span class="gi"&gt;+        // a better measure.&lt;/span&gt;
&lt;span class="gi"&gt;+        // Sometimes these quantities vanish and in that case we&lt;/span&gt;
&lt;span class="gi"&gt;+        // force beta2 = +/- bet1a exactly.&lt;/span&gt;
&lt;span class="gi"&gt;+        if (cos_beta1 &amp;lt; -sin_beta1)&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            if (cos_beta1 == cos_beta2)&lt;/span&gt;
&lt;span class="gi"&gt;+            {&lt;/span&gt;
&lt;span class="gi"&gt;+                sin_beta2 = sin_beta2 &amp;lt; 0 ? sin_beta1 :&lt;/span&gt;
&lt;span class="gi"&gt;+                                            -sin_beta1;&lt;/span&gt;
&lt;span class="gi"&gt;+            }&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+        else&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            if (std::abs(sin_beta2) == -sin_beta1)&lt;/span&gt;
&lt;span class="gi"&gt;+            {&lt;/span&gt;
&lt;span class="gi"&gt;+                cos_beta2 = cos_beta1;&lt;/span&gt;
&lt;span class="gi"&gt;+            }&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const dn1 = sqrt(c1 + ep2 * math::sqr(sin_beta1));&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const dn2 = sqrt(c1 + ep2 * math::sqr(sin_beta2));&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        CT a12, sigma12;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT m12x, s12x, M21;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        // Index zero element of coeffs_C1 is unused.&lt;/span&gt;
&lt;span class="gi"&gt;+        CT coeffs_C1[SeriesOrder + 1];&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        bool meridian = lat1 == -90 || sin_lam12 == 0;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        if (meridian)&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            // Endpoints lie on a single full meridian.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            // Point to the target latitude.&lt;/span&gt;
&lt;span class="gi"&gt;+            CT cos_alpha1 = cos_lam12;&lt;/span&gt;
&lt;span class="gi"&gt;+            CT sin_alpha1 = sin_lam12;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            // Heading north at the target.&lt;/span&gt;
&lt;span class="gi"&gt;+            CT cos_alpha2 = 1;&lt;/span&gt;
&lt;span class="gi"&gt;+            CT sin_alpha2 = 0;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            CT sin_sigma1 = sin_beta1;&lt;/span&gt;
&lt;span class="gi"&gt;+            CT cos_sigma1 = cos_alpha1 * cos_beta1;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            CT sin_sigma2 = sin_beta2;&lt;/span&gt;
&lt;span class="gi"&gt;+            CT cos_sigma2 = cos_alpha2 * cos_beta2;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            CT sigma12 = std::atan2(std::max(c0, cos_sigma1 * sin_sigma2 - sin_sigma1 * cos_sigma2),&lt;/span&gt;
&lt;span class="gi"&gt;+                                             cos_sigma1 * cos_sigma2 + sin_sigma1 * sin_sigma2);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            meridian_length(n, ep2, sigma12, sin_sigma1, cos_sigma1, dn1,&lt;/span&gt;
&lt;span class="gi"&gt;+                                             sin_sigma2, cos_sigma2, dn2,&lt;/span&gt;
&lt;span class="gi"&gt;+                                             cos_beta1, cos_beta2, s12x,&lt;/span&gt;
&lt;span class="gi"&gt;+                                             m12x, result.geodesic_scale, M21);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            if (sigma12 &amp;lt; c1 || m12x &amp;gt;= c0)&lt;/span&gt;
&lt;span class="gi"&gt;+            {&lt;/span&gt;
&lt;span class="gi"&gt;+                if (sigma12 &amp;lt; c3 * tiny)&lt;/span&gt;
&lt;span class="gi"&gt;+                {&lt;/span&gt;
&lt;span class="gi"&gt;+                    sigma12  = m12x = s12x = c0;&lt;/span&gt;
&lt;span class="gi"&gt;+                }&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+                m12x *= b;&lt;/span&gt;
&lt;span class="gi"&gt;+                s12x *= b;&lt;/span&gt;
&lt;span class="gi"&gt;+                a12 = sigma12 / math::d2r&amp;lt;CT&amp;gt;();&lt;/span&gt;
&lt;span class="gi"&gt;+            }&lt;/span&gt;
&lt;span class="gi"&gt;+            else&lt;/span&gt;
&lt;span class="gi"&gt;+            {&lt;/span&gt;
&lt;span class="gi"&gt;+                // m12 &amp;lt; 0, i.e., prolate and too close to anti-podal.&lt;/span&gt;
&lt;span class="gi"&gt;+                meridian = false;&lt;/span&gt;
&lt;span class="gi"&gt;+            }&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    }
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    static inline void meridian_length(CT epsilon, CT ep2, CT sigma12,&lt;/span&gt;
&lt;span class="gi"&gt;+                                       CT sin_sigma1, CT cos_sigma1, CT dn1,&lt;/span&gt;
&lt;span class="gi"&gt;+                                       CT sin_sigma2, CT cos_sigma2, CT dn2,&lt;/span&gt;
&lt;span class="gi"&gt;+                                       CT cos_beta1, CT cos_beta2,&lt;/span&gt;
&lt;span class="gi"&gt;+                                       CT&amp;amp; s12x, CT&amp;amp; m12x,&lt;/span&gt;
&lt;span class="gi"&gt;+                                       CT&amp;amp; M12, CT&amp;amp; M21)&lt;/span&gt;
&lt;span class="gi"&gt;+    {&lt;/span&gt;
&lt;span class="gi"&gt;+        CT A12x = 0, J12 = 0;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT expansion_A1, expansion_A2;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        // Index zero element of coeffs_C1 and coeffs_C2 is unused.&lt;/span&gt;
&lt;span class="gi"&gt;+        CT coeffs_C1[SeriesOrder + 1];&lt;/span&gt;
&lt;span class="gi"&gt;+        CT coeffs_C2[SeriesOrder + 1];&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        if (BOOST_GEOMETRY_CONDITION(EnableDistance) ||&lt;/span&gt;
&lt;span class="gi"&gt;+            BOOST_GEOMETRY_CONDITION(EnableReducedLength) ||&lt;/span&gt;
&lt;span class="gi"&gt;+            BOOST_GEOMETRY_CONDITION(EnableGeodesicScale))&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            // Find the coefficients for A1 by computing the&lt;/span&gt;
&lt;span class="gi"&gt;+            // series expansion using Horner scehme.&lt;/span&gt;
&lt;span class="gi"&gt;+            expansion_A1&lt;/span&gt;
&lt;span class="gi"&gt;+                = series_expansion::evaluate_series_A1&amp;lt;CT, SeriesOrder&amp;gt;(epsilon);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            // Evaluate the coefficients for C1.&lt;/span&gt;
&lt;span class="gi"&gt;+            series_expansion::evaluate_coeffs_C1&amp;lt;CT, SeriesOrder&amp;gt;(epsilon, coeffs_C1);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            if (BOOST_GEOMETRY_CONDITION(EnableReducedLength) ||&lt;/span&gt;
&lt;span class="gi"&gt;+                BOOST_GEOMETRY_CONDITION(EnableGeodesicScale))&lt;/span&gt;
&lt;span class="gi"&gt;+            {&lt;/span&gt;
&lt;span class="gi"&gt;+                // Find the coefficients for A2 by computing the&lt;/span&gt;
&lt;span class="gi"&gt;+                // series expansion using Horner scehme.&lt;/span&gt;
&lt;span class="gi"&gt;+                expansion_A2&lt;/span&gt;
&lt;span class="gi"&gt;+                    = series_expansion::evaluate_series_A2&amp;lt;CT, SeriesOrder&amp;gt;(epsilon);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+                // Evaluate the coefficients for C2.&lt;/span&gt;
&lt;span class="gi"&gt;+                series_expansion::evaluate_coeffs_C2&amp;lt;CT, SeriesOrder&amp;gt;(epsilon, coeffs_C2);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+                A12x = expansion_A1 - expansion_A2;&lt;/span&gt;
&lt;span class="gi"&gt;+                expansion_A2 += c1;&lt;/span&gt;
&lt;span class="gi"&gt;+            }&lt;/span&gt;
&lt;span class="gi"&gt;+            expansion_A1 += c1;&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        if (BOOST_GEOMETRY_CONDITION(EnableDistance))&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            CT B1 = series_expansion::sin_cos_series&amp;lt;CT, SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+                                      (sin_sigma2, cos_sigma2, coeffs_C1)&lt;/span&gt;
&lt;span class="gi"&gt;+                  - series_expansion::sin_cos_series&amp;lt;CT, SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+                                      (sin_sigma1, cos_sigma1, coeffs_C1);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            m12x = expansion_A1 * (sigma12 + B1);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            if (BOOST_GEOMETRY_CONDITION(EnableReducedLength) ||&lt;/span&gt;
&lt;span class="gi"&gt;+                BOOST_GEOMETRY_CONDITION(EnableGeodesicScale))&lt;/span&gt;
&lt;span class="gi"&gt;+            {&lt;/span&gt;
&lt;span class="gi"&gt;+                CT B2 = series_expansion::sin_cos_series&amp;lt;CT, SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+                                          (sin_sigma2, cos_sigma2, coeffs_C2)&lt;/span&gt;
&lt;span class="gi"&gt;+                      - series_expansion::sin_cos_series&amp;lt;CT, SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+                                          (sin_sigma1, cos_sigma1, coeffs_C2);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+                J12 = A12x * sigma12 + (expansion_A1 * B1 -&lt;/span&gt;
&lt;span class="gi"&gt;+                                        expansion_A2 * B2);&lt;/span&gt;
&lt;span class="gi"&gt;+            }&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+        else if (BOOST_GEOMETRY_CONDITION(EnableReducedLength) ||&lt;/span&gt;
&lt;span class="gi"&gt;+                 BOOST_GEOMETRY_CONDITION(EnableGeodesicScale))&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            for (size_t i = 1; i &amp;lt;= SeriesOrder; ++i)&lt;/span&gt;
&lt;span class="gi"&gt;+            {&lt;/span&gt;
&lt;span class="gi"&gt;+                coeffs_C2[i] = expansion_A1 * coeffs_C1[i] -&lt;/span&gt;
&lt;span class="gi"&gt;+                               expansion_A2 * coeffs_C2[i];&lt;/span&gt;
&lt;span class="gi"&gt;+            }&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            J12 = A12x * sigma12 +&lt;/span&gt;
&lt;span class="gi"&gt;+                   (series_expansion::sin_cos_series&amp;lt;CT, SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+                                      (sin_sigma2,&lt;/span&gt;
&lt;span class="gi"&gt;+                                       cos_sigma2,&lt;/span&gt;
&lt;span class="gi"&gt;+                                       coeffs_C2)&lt;/span&gt;
&lt;span class="gi"&gt;+                  - series_expansion::sin_cos_series&amp;lt;CT, SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+                                      (sin_sigma1,&lt;/span&gt;
&lt;span class="gi"&gt;+                                       cos_sigma1,&lt;/span&gt;
&lt;span class="gi"&gt;+                                       coeffs_C2));&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        if (BOOST_GEOMETRY_CONDITION(EnableReducedLength))&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            m12x = dn2 * (cos_sigma1 * sin_sigma2) -&lt;/span&gt;
&lt;span class="gi"&gt;+                   dn1 * (sin_sigma1 * cos_sigma2) -&lt;/span&gt;
&lt;span class="gi"&gt;+                   cos_sigma1 * cos_sigma2 * J12;&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        if (BOOST_GEOMETRY_CONDITION(EnableGeodesicScale))&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            CT cos_sigma12 = cos_sigma1 * cos_sigma2 + sin_sigma1 * sin_sigma2;&lt;/span&gt;
&lt;span class="gi"&gt;+            CT t = ep2 * (cos_beta1 - cos_beta2) *&lt;/span&gt;
&lt;span class="gi"&gt;+                         (cos_beta1 + cos_beta2) / (dn1 + dn2);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            M12 = cos_sigma12 + (t * sin_sigma2 - cos_sigma2 * J12) * sin_sigma1 / dn1;&lt;/span&gt;
&lt;span class="gi"&gt;+            M21 = cos_sigma12 - (t * sin_sigma1 - cos_sigma1 * J12) * sin_sigma2 / dn2;&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+    }&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;};

&lt;span class="w"&gt; &lt;/span&gt;}}} // namespace boost::geometry::formula

commit efd30c8ccc581a6669c836675f5d87ad0f26e733
Merge: 2e064c0 8a2dd63
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Thu Jun 7 11:00:37 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Merge branch &amp;#39;feature/geodesic_direct&amp;#39; into feature/karney_inverse

commit 8a2dd63ccf85f80134d9d88e049ac6d272531f0f
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Thu Jun 7 10:58:13 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [util] Move sin_cos_series function to series_expansion.hpp

&lt;span class="w"&gt; &lt;/span&gt;   This function is extensively used for the direct and
&lt;span class="w"&gt; &lt;/span&gt;   inverse geodesic problem, therefore, it is moved
&lt;span class="w"&gt; &lt;/span&gt;   to a more accessible location.

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/formulas/karney_direct.hpp b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 934122b..6f63cb1 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -48,38 +48,6 @@ class karney_direct&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;public:
&lt;span class="w"&gt; &lt;/span&gt;    typedef result_direct&amp;lt;CT&amp;gt; result_type;

&lt;span class="gd"&gt;-    /*&lt;/span&gt;
&lt;span class="gd"&gt;-     Evaluate&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-     y = sum(c[i] * sin(2*i * x), i, 1, n)&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-     using Clenshaw summation.&lt;/span&gt;
&lt;span class="gd"&gt;-    */&lt;/span&gt;
&lt;span class="gd"&gt;-    static inline CT sin_cos_series(CT sinx,&lt;/span&gt;
&lt;span class="gd"&gt;-                                    CT cosx,&lt;/span&gt;
&lt;span class="gd"&gt;-                                    const CT coeffs[])&lt;/span&gt;
&lt;span class="gd"&gt;-    {&lt;/span&gt;
&lt;span class="gd"&gt;-        size_t n = SeriesOrder;&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        // Point to one beyond last element.&lt;/span&gt;
&lt;span class="gd"&gt;-        coeffs += (n + 1);&lt;/span&gt;
&lt;span class="gd"&gt;-        CT ar = 2 * (cosx - sinx) * (cosx + sinx);&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        CT k0 = n &amp;amp; 1 ? *--coeffs : 0;&lt;/span&gt;
&lt;span class="gd"&gt;-        CT k1 = 0;&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        // Make n even.&lt;/span&gt;
&lt;span class="gd"&gt;-        n /= 2;&lt;/span&gt;
&lt;span class="gd"&gt;-        while (n--) {&lt;/span&gt;
&lt;span class="gd"&gt;-          // Unroll loop x 2, so accumulators return to their original role.&lt;/span&gt;
&lt;span class="gd"&gt;-          k1 = ar * k0 - k1 + *--coeffs;&lt;/span&gt;
&lt;span class="gd"&gt;-          k0 = ar * k1 - k0 + *--coeffs;&lt;/span&gt;
&lt;span class="gd"&gt;-        }&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        return 2 * sinx * cosx * k0;&lt;/span&gt;
&lt;span class="gd"&gt;-    }&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    template &amp;lt;typename T, typename Dist, typename Azi, typename Spheroid&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;    static inline result_type apply(T const&amp;amp; lo1,
&lt;span class="w"&gt; &lt;/span&gt;                                    T const&amp;amp; la1,
&lt;span class="gu"&gt;@@ -157,7 +125,8 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        cos_sigma1 = cos_omega1 = sin_beta1 != 0 || cos_alpha1 != 0 ? cos_beta1 * cos_alpha1 : 1;
&lt;span class="w"&gt; &lt;/span&gt;        math::normalize&amp;lt;CT&amp;gt;(sin_sigma1, cos_sigma1);

&lt;span class="gd"&gt;-        CT const B11 = sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C1);&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const B11 =&lt;/span&gt;
&lt;span class="gi"&gt;+            series_expansion::sin_cos_series&amp;lt;CT, SeriesOrder&amp;gt;(sin_sigma1, cos_sigma1, coeffs_C1);&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT const sin_B11 = sin(B11);
&lt;span class="w"&gt; &lt;/span&gt;        CT const cos_B11 = cos(B11);

&lt;span class="gu"&gt;@@ -170,9 +139,11 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT coeffs_C1p[SeriesOrder + 1];
&lt;span class="w"&gt; &lt;/span&gt;        series_expansion::evaluate_coeffs_C1p&amp;lt;CT, SeriesOrder&amp;gt;(epsilon, coeffs_C1p);

&lt;span class="gd"&gt;-        CT const B12 = - sin_cos_series(sin_tau1 * cos_tau12 + cos_tau1 * sin_tau12,&lt;/span&gt;
&lt;span class="gd"&gt;-                                      cos_tau1 * cos_tau12 - sin_tau1 * sin_tau12,&lt;/span&gt;
&lt;span class="gd"&gt;-                                      coeffs_C1p);&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const B12 =&lt;/span&gt;
&lt;span class="gi"&gt;+            - series_expansion::sin_cos_series&amp;lt;CT, SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+                                (sin_tau1 * cos_tau12 + cos_tau1 * sin_tau12,&lt;/span&gt;
&lt;span class="gi"&gt;+                                 cos_tau1 * cos_tau12 - sin_tau1 * sin_tau12,&lt;/span&gt;
&lt;span class="gi"&gt;+                                 coeffs_C1p);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        CT const sigma12 = tau12 - (B12 - B11);
&lt;span class="w"&gt; &lt;/span&gt;        CT const sin_sigma12 = sin(sigma12);
&lt;span class="gu"&gt;@@ -229,12 +200,14 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            CT coeffs_C3[SeriesOrder];
&lt;span class="w"&gt; &lt;/span&gt;            series_expansion::evaluate_coeffs_C3&amp;lt;double, SeriesOrder&amp;gt;(epsilon, coeffs_C3, coeffs_C3x);

&lt;span class="gd"&gt;-            CT const B31 = sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C3);&lt;/span&gt;
&lt;span class="gi"&gt;+            CT const B31 =&lt;/span&gt;
&lt;span class="gi"&gt;+                series_expansion::sin_cos_series&amp;lt;CT, SeriesOrder&amp;gt;(sin_sigma1, cos_sigma1, coeffs_C3);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            CT const lam12 = omega12 + A3c *
&lt;span class="gd"&gt;-                             (sigma12 + (sin_cos_series(sin_sigma2,&lt;/span&gt;
&lt;span class="gd"&gt;-                                                        cos_sigma2,&lt;/span&gt;
&lt;span class="gd"&gt;-                                                        coeffs_C3) - B31));&lt;/span&gt;
&lt;span class="gi"&gt;+                             (sigma12 + (series_expansion::sin_cos_series&amp;lt;CT, SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+                                                           (sin_sigma2,&lt;/span&gt;
&lt;span class="gi"&gt;+                                                            cos_sigma2,&lt;/span&gt;
&lt;span class="gi"&gt;+                                                            coeffs_C3) - B31));&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            // Convert to radians to get the
&lt;span class="w"&gt; &lt;/span&gt;            // longitudinal difference.
&lt;span class="gu"&gt;@@ -253,8 +226,10 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            CT coeffs_C2[SeriesOrder + 1];
&lt;span class="w"&gt; &lt;/span&gt;            series_expansion::evaluate_coeffs_C2&amp;lt;CT, SeriesOrder&amp;gt;(epsilon, coeffs_C2);

&lt;span class="gd"&gt;-            CT const B21 = sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C2);&lt;/span&gt;
&lt;span class="gd"&gt;-            CT const B22 = sin_cos_series(sin_sigma2, cos_sigma2, coeffs_C2);&lt;/span&gt;
&lt;span class="gi"&gt;+            CT const B21 =&lt;/span&gt;
&lt;span class="gi"&gt;+                series_expansion::sin_cos_series&amp;lt;CT, SeriesOrder&amp;gt;(sin_sigma1, cos_sigma1, coeffs_C2);&lt;/span&gt;
&lt;span class="gi"&gt;+            CT const B22 =&lt;/span&gt;
&lt;span class="gi"&gt;+                series_expansion::sin_cos_series&amp;lt;CT, SeriesOrder&amp;gt;(sin_sigma2, cos_sigma2, coeffs_C2);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            // Find the coefficients for A2 by computing the
&lt;span class="w"&gt; &lt;/span&gt;            // series expansion using Horner scehme.
&lt;span class="gh"&gt;diff --git a/include/boost/geometry/util/series_expansion.hpp b/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index ba3c214..02f1074 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -746,6 +746,38 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        // Post condition: offset == coeffs_C3_size
&lt;span class="w"&gt; &lt;/span&gt;    }

&lt;span class="gi"&gt;+    /*&lt;/span&gt;
&lt;span class="gi"&gt;+    \brief Evaluate the following:&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+     y = sum(c[i] * sin(2*i * x), i, 1, n)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+     using Clenshaw summation.&lt;/span&gt;
&lt;span class="gi"&gt;+    */&lt;/span&gt;
&lt;span class="gi"&gt;+    template &amp;lt;typename CT, size_t SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    static inline CT sin_cos_series(CT sinx,&lt;/span&gt;
&lt;span class="gi"&gt;+                                    CT cosx,&lt;/span&gt;
&lt;span class="gi"&gt;+                                    const CT coeffs[])&lt;/span&gt;
&lt;span class="gi"&gt;+    {&lt;/span&gt;
&lt;span class="gi"&gt;+        size_t n = SeriesOrder;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        // Point to one beyond last element.&lt;/span&gt;
&lt;span class="gi"&gt;+        coeffs += (n + 1);&lt;/span&gt;
&lt;span class="gi"&gt;+        CT ar = 2 * (cosx - sinx) * (cosx + sinx);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        CT k0 = n &amp;amp; 1 ? *--coeffs : 0;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT k1 = 0;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        // Make n even.&lt;/span&gt;
&lt;span class="gi"&gt;+        n /= 2;&lt;/span&gt;
&lt;span class="gi"&gt;+        while (n--) {&lt;/span&gt;
&lt;span class="gi"&gt;+          // Unroll loop x 2, so accumulators return to their original role.&lt;/span&gt;
&lt;span class="gi"&gt;+          k1 = ar * k0 - k1 + *--coeffs;&lt;/span&gt;
&lt;span class="gi"&gt;+          k0 = ar * k1 - k0 + *--coeffs;&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        return 2 * sinx * cosx * k0;&lt;/span&gt;
&lt;span class="gi"&gt;+    }&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;}}} // namespace boost::geometry::series_expansion


commit 2e064c0f0d1cb0e9fde38643413a76257d11346e
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Wed Jun 6 09:42:09 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [formulas] Arrange points in canonical form for inverse geodesic problem

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/formulas/karney_inverse.hpp b/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
new file mode 100644
&lt;span class="gh"&gt;index 0000000..4929f9c&lt;/span&gt;
&lt;span class="gd"&gt;--- /dev/null&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/formulas/karney_inverse.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -0,0 +1,121 @@&lt;/span&gt;
&lt;span class="gi"&gt;+// Boost.Geometry&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+// Contributed and/or modified by Adeel Ahmad.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+// Use, modification and distribution is subject to the Boost Software License,&lt;/span&gt;
&lt;span class="gi"&gt;+// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at&lt;/span&gt;
&lt;span class="gi"&gt;+// http://www.boost.org/LICENSE_1_0.txt)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+#ifndef BOOST_GEOMETRY_FORMULAS_KARNEY_INVERSE_HPP&lt;/span&gt;
&lt;span class="gi"&gt;+#define BOOST_GEOMETRY_FORMULAS_KARNEY_INVERSE_HPP&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+#include &amp;lt;boost/math/constants/constants.hpp&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+#include &amp;lt;boost/math/special_functions/hypot.hpp&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+#include &amp;lt;boost/geometry/util/math.hpp&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+#include &amp;lt;boost/geometry/util/series_expansion.hpp&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+#include &amp;lt;boost/geometry/formulas/flattening.hpp&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+#include &amp;lt;boost/geometry/formulas/result_inverse.hpp&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+namespace boost { namespace geometry { namespace formula&lt;/span&gt;
&lt;span class="gi"&gt;+{&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+/*!&lt;/span&gt;
&lt;span class="gi"&gt;+\brief The solution of the inverse problem of geodesics on latlong coordinates,&lt;/span&gt;
&lt;span class="gi"&gt;+       after Karney (2011).&lt;/span&gt;
&lt;span class="gi"&gt;+\author See&lt;/span&gt;
&lt;span class="gi"&gt;+- Charles F.F Karney, Algorithms for geodesics, 2011&lt;/span&gt;
&lt;span class="gi"&gt;+https://arxiv.org/pdf/1109.4448.pdf&lt;/span&gt;
&lt;span class="gi"&gt;+*/&lt;/span&gt;
&lt;span class="gi"&gt;+template &amp;lt;&lt;/span&gt;
&lt;span class="gi"&gt;+    typename CT,&lt;/span&gt;
&lt;span class="gi"&gt;+    bool EnableDistance,&lt;/span&gt;
&lt;span class="gi"&gt;+    bool EnableAzimuth,&lt;/span&gt;
&lt;span class="gi"&gt;+    bool EnableReverseAzimuth = false,&lt;/span&gt;
&lt;span class="gi"&gt;+    bool EnableReducedLength = false,&lt;/span&gt;
&lt;span class="gi"&gt;+    bool EnableGeodesicScale = false,&lt;/span&gt;
&lt;span class="gi"&gt;+    size_t SeriesOrder = 8&lt;/span&gt;
&lt;span class="gi"&gt;+&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+class karney_inverse&lt;/span&gt;
&lt;span class="gi"&gt;+{&lt;/span&gt;
&lt;span class="gi"&gt;+    static const bool CalcQuantities = EnableReducedLength || EnableGeodesicScale;&lt;/span&gt;
&lt;span class="gi"&gt;+    static const bool CalcAzimuths = EnableAzimuth || EnableReverseAzimuth || CalcQuantities;&lt;/span&gt;
&lt;span class="gi"&gt;+    static const bool CalcFwdAzimuth = EnableAzimuth || CalcQuantities;&lt;/span&gt;
&lt;span class="gi"&gt;+    static const bool CalcRevAzimuth = EnableReverseAzimuth || CalcQuantities;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+public:&lt;/span&gt;
&lt;span class="gi"&gt;+    typedef result_inverse&amp;lt;CT&amp;gt; result_type;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    template &amp;lt;typename T1, typename T2, typename Spheroid&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    static inline result_type apply(T1 const&amp;amp; lo1,&lt;/span&gt;
&lt;span class="gi"&gt;+                                    T1 const&amp;amp; la1,&lt;/span&gt;
&lt;span class="gi"&gt;+                                    T2 const&amp;amp; lo2,&lt;/span&gt;
&lt;span class="gi"&gt;+                                    T2 const&amp;amp; la2,&lt;/span&gt;
&lt;span class="gi"&gt;+                                    Spheroid const&amp;amp; spheroid)&lt;/span&gt;
&lt;span class="gi"&gt;+    {&lt;/span&gt;
&lt;span class="gi"&gt;+        result_type result;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        CT lat1 = la1;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT lat2 = la2;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        CT lon1 = lo1;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT lon2 = lo2;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const c1 = 1;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const c2 = 2;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const c180 = 180;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const c90 = 90;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        // Compute the longitudinal difference.&lt;/span&gt;
&lt;span class="gi"&gt;+        CT lon12_error;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT lon12 = math::difference_angle(lon1, lon2, lon12_error);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        int lon12_sign = lon12 &amp;gt;= 0 ? 1 : -1;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        // Make points close to the meridian to lie on it.&lt;/span&gt;
&lt;span class="gi"&gt;+        lon12 = lon12_sign * math::round_angle(lon12);&lt;/span&gt;
&lt;span class="gi"&gt;+        lon12_error = math::round_angle((c180 - lon12) - lon12_sign * lon12_error);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        CT lam12 = lon12 * math::d2r&amp;lt;CT&amp;gt;();&lt;/span&gt;
&lt;span class="gi"&gt;+        CT sin_lam12;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT cos_lam12;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        lon12 &amp;gt; c90 ? math::sin_cos_degrees(lon12_error, sin_lam12, cos_lam12)&lt;/span&gt;
&lt;span class="gi"&gt;+                    : math::sin_cos_degrees(lon12, sin_lam12, cos_lam12);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        // Make points close to the equator to lie on it.&lt;/span&gt;
&lt;span class="gi"&gt;+        lat1 = std::abs(lat1) &amp;gt; 90 ? math::NaN&amp;lt;CT&amp;gt;() : lat1;&lt;/span&gt;
&lt;span class="gi"&gt;+        lat2 = std::abs(lat2) &amp;gt; 90 ? math::NaN&amp;lt;CT&amp;gt;() : lat2;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        lat1 = math::round_angle(lat1);&lt;/span&gt;
&lt;span class="gi"&gt;+        lat2 = math::round_angle(lat2);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        // Arrange points in a canonical form, as explained in&lt;/span&gt;
&lt;span class="gi"&gt;+        // paper Algorithms for geodesics, Eq. (44):&lt;/span&gt;
&lt;span class="gi"&gt;+        //&lt;/span&gt;
&lt;span class="gi"&gt;+        //     0 &amp;lt;= lon12 &amp;lt;= 180&lt;/span&gt;
&lt;span class="gi"&gt;+        //     -90 &amp;lt;= lat1 &amp;lt;= 0&lt;/span&gt;
&lt;span class="gi"&gt;+        //     lat1 &amp;lt;= lat2 &amp;lt;= -lat1&lt;/span&gt;
&lt;span class="gi"&gt;+        int swap_point = std::abs(lat1) &amp;lt; std::abs(lat2) ? -1 : 1;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        if (swap_point &amp;lt; 0)&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            lon12_sign *= -1;&lt;/span&gt;
&lt;span class="gi"&gt;+            swap(lat1, lat2);&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        // Enforce lat1 to be &amp;lt;= 0.&lt;/span&gt;
&lt;span class="gi"&gt;+        int lat_sign = lat1 &amp;lt; 0 ? 1 : -1;&lt;/span&gt;
&lt;span class="gi"&gt;+        lat1 *= lat_sign;&lt;/span&gt;
&lt;span class="gi"&gt;+        lat2 *= lat_sign;&lt;/span&gt;
&lt;span class="gi"&gt;+    }&lt;/span&gt;
&lt;span class="gi"&gt;+};&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+}}} // namespace boost::geometry::formula&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+#endif // BOOST_GEOMETRY_FORMULAS_KARNEY_INVERSE_HPP&lt;/span&gt;

commit 7561d68c086d85ce990e32196dc95874f11ac4c4
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Wed Jun 6 09:39:31 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [util] Add function for returning NaN (not a number)

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/util/math.hpp b/include/boost/geometry/util/math.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 652ca85..f8b3707 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/util/math.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/util/math.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -810,6 +810,23 @@ inline void sin_cos_degrees(T const&amp;amp; x, T &amp;amp; sinx, T &amp;amp; cosx)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;}

&lt;span class="w"&gt; &lt;/span&gt;/*!
&lt;span class="gi"&gt;+\brief The NaN (not a number)&lt;/span&gt;
&lt;span class="gi"&gt;+*/&lt;/span&gt;
&lt;span class="gi"&gt;+template&amp;lt;typename T&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+inline T NaN()&lt;/span&gt;
&lt;span class="gi"&gt;+{&lt;/span&gt;
&lt;span class="gi"&gt;+#if defined(_MSC_VER)&lt;/span&gt;
&lt;span class="gi"&gt;+    return std::numeric_limits&amp;lt;T&amp;gt;::has_quiet_NaN ?&lt;/span&gt;
&lt;span class="gi"&gt;+      std::numeric_limits&amp;lt;T&amp;gt;::quiet_NaN() :&lt;/span&gt;
&lt;span class="gi"&gt;+      (std::numeric_limits&amp;lt;T&amp;gt;::max)();&lt;/span&gt;
&lt;span class="gi"&gt;+#else&lt;/span&gt;
&lt;span class="gi"&gt;+    return std::numeric_limits&amp;lt;T&amp;gt;::has_quiet_NaN ?&lt;/span&gt;
&lt;span class="gi"&gt;+      std::numeric_limits&amp;lt;T&amp;gt;::quiet_NaN() :&lt;/span&gt;
&lt;span class="gi"&gt;+      std::numeric_limits&amp;lt;T&amp;gt;::max();&lt;/span&gt;
&lt;span class="gi"&gt;+#endif&lt;/span&gt;
&lt;span class="gi"&gt;+}&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+/*!&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;\brief Round off a given angle
&lt;span class="w"&gt; &lt;/span&gt;*/
&lt;span class="w"&gt; &lt;/span&gt;template&amp;lt;typename T&amp;gt;
&lt;span class="gu"&gt;@@ -889,7 +906,7 @@ inline T difference_angle(T x, T y, T&amp;amp; e)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;}


&lt;span class="gd"&gt;-/*&lt;/span&gt;
&lt;span class="gi"&gt;+/*!&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;\brief Evaluate the polynomial in x using Horner&amp;#39;s method.
&lt;span class="w"&gt; &lt;/span&gt;*/
&lt;span class="w"&gt; &lt;/span&gt;// TODO: adl1995 - Merge these functions with formulas/area_formulas.hpp
&lt;span class="gu"&gt;@@ -909,7 +926,7 @@ inline NT horner_evaluate(NT x,&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    return result;
&lt;span class="w"&gt; &lt;/span&gt;}

&lt;span class="gd"&gt;-/*&lt;/span&gt;
&lt;span class="gi"&gt;+/*!&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;\brief Evaluate the polynomial.
&lt;span class="w"&gt; &lt;/span&gt;*/
&lt;span class="w"&gt; &lt;/span&gt;template&amp;lt;typename CT&amp;gt;

commit 49e0a4fdaee2a78b5691c3c0db616fbb9ecd4f28
Merge: b9b0f85 df0cafd
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Wed Jun 6 08:24:27 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Merge branch &amp;#39;feature/geodesic_direct&amp;#39; into feature/karney_inverse

commit df0cafdd190d1ce51e75dab32f9e302527acea49
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Tue Jun 5 17:26:30 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [doc] Move Maxima scripts for geodesics to doc/other/maxima/geod.mac

&lt;span class="gh"&gt;diff --git a/doc/other/maxima/geod.mac b/doc/other/maxima/geod.mac&lt;/span&gt;
new file mode 100644
&lt;span class="gh"&gt;index 0000000..229e949&lt;/span&gt;
&lt;span class="gd"&gt;--- /dev/null&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/doc/other/maxima/geod.mac&lt;/span&gt;
&lt;span class="gu"&gt;@@ -0,0 +1,232 @@&lt;/span&gt;
&lt;span class="gi"&gt;+/*&lt;/span&gt;
&lt;span class="gi"&gt;+Compute the series expansions for the ellipsoidal geodesic problem.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+Copyright (c) Charles Karney (2009-2015) &amp;lt;charles@karney.com&amp;gt; and&lt;/span&gt;
&lt;span class="gi"&gt;+licensed under the MIT/X11 License.  For more information, see&lt;/span&gt;
&lt;span class="gi"&gt;+https://geographiclib.sourceforge.io&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+References:&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+   Charles F. F. Karney,&lt;/span&gt;
&lt;span class="gi"&gt;+   Algorithms for geodesics, J. Geodesy 87, 43-55 (2013),&lt;/span&gt;
&lt;span class="gi"&gt;+   https://doi.org/10.1007/s00190-012-0578-z&lt;/span&gt;
&lt;span class="gi"&gt;+   Addenda: https://geographiclib.sourceforge.io/geod-addenda.html&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+The code below contains minor modifications to conform with&lt;/span&gt;
&lt;span class="gi"&gt;+Boost Geometry style guidelines.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+To run the code, start Maxima and enter&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+  load(&amp;quot;geod.mac&amp;quot;)$&lt;/span&gt;
&lt;span class="gi"&gt;+*/&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+taylordepth:5$&lt;/span&gt;
&lt;span class="gi"&gt;+ataylor(expr,var,ord):=expand(ratdisrep(taylor(expr,var,0,ord)))$&lt;/span&gt;
&lt;span class="gi"&gt;+jtaylor(expr,var1,var2,ord):=block([zz],expand(subst([zz=1],&lt;/span&gt;
&lt;span class="gi"&gt;+ratdisrep(taylor(subst([var1=zz*var1,var2=zz*var2],expr),zz,0,ord)))))$&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+computeI1(maxpow):=block([sintegrand,sintegrandexp,s,sigma,tau1,k2,eps],&lt;/span&gt;
&lt;span class="gi"&gt;+  sintegrand:sqrt(1+k2*sin(sigma)^2),&lt;/span&gt;
&lt;span class="gi"&gt;+  sintegrandexp:ataylor(&lt;/span&gt;
&lt;span class="gi"&gt;+      (1-eps)*subst([k2=4*eps/(1-eps)^2],sintegrand),&lt;/span&gt;
&lt;span class="gi"&gt;+      eps,maxpow),&lt;/span&gt;
&lt;span class="gi"&gt;+  s:trigreduce(integrate(sintegrandexp,sigma)),&lt;/span&gt;
&lt;span class="gi"&gt;+  s:s-subst(sigma=0,s),&lt;/span&gt;
&lt;span class="gi"&gt;+  A1:expand(subst(sigma=2*%pi,s)/(2*%pi)),&lt;/span&gt;
&lt;span class="gi"&gt;+  tau1:ataylor(s/A1,eps,maxpow),&lt;/span&gt;
&lt;span class="gi"&gt;+  for i:1 thru maxpow do C1[i]:coeff(tau1,sin(2*i*sigma)),&lt;/span&gt;
&lt;span class="gi"&gt;+  if expand(tau1-sigma-sum(C1[i]*sin(2*i*sigma),i,1,maxpow)) # 0&lt;/span&gt;
&lt;span class="gi"&gt;+  then error(&amp;quot;left over terms in B1&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+  A1:A1/(1-eps),&lt;/span&gt;
&lt;span class="gi"&gt;+  &amp;#39;done)$&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+codeA1(maxpow):=block([tab2:&amp;quot;    &amp;quot;,tab3:&amp;quot;        &amp;quot;],&lt;/span&gt;
&lt;span class="gi"&gt;+print(&amp;quot;// The scale factor A1-1 = mean value of (d/dsigma)I1 - 1&lt;/span&gt;
&lt;span class="gi"&gt;+static inline CT evaluate_series_A1(CT eps) {&lt;/span&gt;
&lt;span class="gi"&gt;+    CT eps2 = math::sqr(eps);&lt;/span&gt;
&lt;span class="gi"&gt;+    CT t;&lt;/span&gt;
&lt;span class="gi"&gt;+    switch (SeriesOrder/2) {&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+  for n:0 thru entier(maxpow/2) do block([&lt;/span&gt;
&lt;span class="gi"&gt;+    q:horner(ataylor(subst([eps=sqrt(eps2)],A1*(1-eps)-1),eps2,n)),&lt;/span&gt;
&lt;span class="gi"&gt;+    linel:1200],&lt;/span&gt;
&lt;span class="gi"&gt;+    print(concat(tab2,&amp;quot;case &amp;quot;,string(n),&amp;quot;:&amp;quot;)),&lt;/span&gt;
&lt;span class="gi"&gt;+    print(concat(tab3,&amp;quot;t = &amp;quot;,string(q),&amp;quot;;&amp;quot;)),&lt;/span&gt;
&lt;span class="gi"&gt;+    print(concat(tab3,&amp;quot;break;&amp;quot;))),&lt;/span&gt;
&lt;span class="gi"&gt;+  print(&amp;quot;    }&lt;/span&gt;
&lt;span class="gi"&gt;+    return (t + eps) / (1 - eps);&lt;/span&gt;
&lt;span class="gi"&gt;+}&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+&amp;#39;done)$&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+computeI2(maxpow):=block([sintegrand,sintegrandexp,s,sigma,tau1,k2,eps],&lt;/span&gt;
&lt;span class="gi"&gt;+  sintegrand:1/sqrt(1+k2*sin(sigma)^2),&lt;/span&gt;
&lt;span class="gi"&gt;+  sintegrandexp:ataylor(&lt;/span&gt;
&lt;span class="gi"&gt;+      (1+eps)*subst([k2=4*eps/(1-eps)^2],sintegrand),&lt;/span&gt;
&lt;span class="gi"&gt;+      eps,maxpow),&lt;/span&gt;
&lt;span class="gi"&gt;+  s:trigreduce(integrate(sintegrandexp,sigma)),&lt;/span&gt;
&lt;span class="gi"&gt;+  s:s-subst(sigma=0,s),&lt;/span&gt;
&lt;span class="gi"&gt;+  A2:expand(subst(sigma=2*%pi,s)/(2*%pi)),&lt;/span&gt;
&lt;span class="gi"&gt;+  tau1:ataylor(s/A2,eps,maxpow),&lt;/span&gt;
&lt;span class="gi"&gt;+  for i:1 thru maxpow do C2[i]:coeff(tau1,sin(2*i*sigma)),&lt;/span&gt;
&lt;span class="gi"&gt;+  if expand(tau1-sigma-sum(C2[i]*sin(2*i*sigma),i,1,maxpow)) # 0&lt;/span&gt;
&lt;span class="gi"&gt;+  then error(&amp;quot;left over terms in B2&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+  A2:A2/(1+eps),&lt;/span&gt;
&lt;span class="gi"&gt;+  &amp;#39;done)$&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+codeA2(maxpow):=block([tab2:&amp;quot;    &amp;quot;,tab3:&amp;quot;        &amp;quot;],&lt;/span&gt;
&lt;span class="gi"&gt;+print(&amp;quot;// The scale factor A2-1 = mean value of (d/dsigma)I2 - 1&lt;/span&gt;
&lt;span class="gi"&gt;+CT evaluate_series_A2(CT const&amp;amp; eps)&lt;/span&gt;
&lt;span class="gi"&gt;+{&lt;/span&gt;
&lt;span class="gi"&gt;+    CT const eps2 = math::sqr(eps);&lt;/span&gt;
&lt;span class="gi"&gt;+    CT t;&lt;/span&gt;
&lt;span class="gi"&gt;+    switch (SeriesOrder/2) {&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+  for n:0 thru entier(maxpow/2) do block([&lt;/span&gt;
&lt;span class="gi"&gt;+    q:horner(ataylor(subst([eps=sqrt(eps2)],A2*(1+eps)-1),eps2,n)),&lt;/span&gt;
&lt;span class="gi"&gt;+    linel:1200],&lt;/span&gt;
&lt;span class="gi"&gt;+    print(concat(tab2,&amp;quot;case &amp;quot;,string(n),&amp;quot;:&amp;quot;)),&lt;/span&gt;
&lt;span class="gi"&gt;+    print(concat(tab3,&amp;quot;t = &amp;quot;,string(q),&amp;quot;;&amp;quot;)),&lt;/span&gt;
&lt;span class="gi"&gt;+    print(concat(tab3,&amp;quot;break;&amp;quot;))),&lt;/span&gt;
&lt;span class="gi"&gt;+  print(&amp;quot;    }&lt;/span&gt;
&lt;span class="gi"&gt;+    return (t - eps) / (1 + eps);&lt;/span&gt;
&lt;span class="gi"&gt;+}&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+&amp;#39;done)$&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+computeI3(maxpow):=block([int,intexp,dlam,eta,del,eps,nu,f,z,n],&lt;/span&gt;
&lt;span class="gi"&gt;+  maxpow:maxpow-1,&lt;/span&gt;
&lt;span class="gi"&gt;+  int:subst([k2=4*eps/(1-eps)^2],&lt;/span&gt;
&lt;span class="gi"&gt;+    (2-f)/(1+(1-f)*sqrt(1+k2*sin(sigma)^2))),&lt;/span&gt;
&lt;span class="gi"&gt;+  int:subst([f=2*n/(1+n)],int),&lt;/span&gt;
&lt;span class="gi"&gt;+  intexp:jtaylor(int,n,eps,maxpow),&lt;/span&gt;
&lt;span class="gi"&gt;+  dlam:trigreduce(integrate(intexp,sigma)),&lt;/span&gt;
&lt;span class="gi"&gt;+  dlam:dlam-subst(sigma=0,dlam),&lt;/span&gt;
&lt;span class="gi"&gt;+  A3:expand(subst(sigma=2*%pi,dlam)/(2*%pi)),&lt;/span&gt;
&lt;span class="gi"&gt;+  eta:jtaylor(dlam/A3,n,eps,maxpow),&lt;/span&gt;
&lt;span class="gi"&gt;+  A3:jtaylor(A3,n,eps,maxpow),&lt;/span&gt;
&lt;span class="gi"&gt;+  for i:1 thru maxpow do C3[i]:coeff(eta,sin(2*i*sigma)),&lt;/span&gt;
&lt;span class="gi"&gt;+  if expand(eta-sigma-sum(C3[i]*sin(2*i*sigma),i,1,maxpow)) # 0&lt;/span&gt;
&lt;span class="gi"&gt;+  then error(&amp;quot;left over terms in B3&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+  &amp;#39;done)$&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+codeA3(maxpow):=block([tab2:&amp;quot;    &amp;quot;,tab3:&amp;quot;        &amp;quot;],&lt;/span&gt;
&lt;span class="gi"&gt;+print(&amp;quot;// The scale factor A3 = mean value of (d/dsigma)I3&lt;/span&gt;
&lt;span class="gi"&gt;+static inline void evaluate_series_A3(CT const&amp;amp; n, CT c[])&lt;/span&gt;
&lt;span class="gi"&gt;+{&lt;/span&gt;
&lt;span class="gi"&gt;+    switch (SeriesOrder) {&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+  for nn:0 thru maxpow do block(&lt;/span&gt;
&lt;span class="gi"&gt;+    [q:if nn=0 then 0 else&lt;/span&gt;
&lt;span class="gi"&gt;+    jtaylor(subst([n=n],A3),n,eps,nn-1),&lt;/span&gt;
&lt;span class="gi"&gt;+    linel:1200],&lt;/span&gt;
&lt;span class="gi"&gt;+    print(concat(tab2,&amp;quot;case &amp;quot;,string(nn),&amp;quot;:&amp;quot;)),&lt;/span&gt;
&lt;span class="gi"&gt;+    for i : 0 thru nn-1 do&lt;/span&gt;
&lt;span class="gi"&gt;+    print(concat(tab3,&amp;quot;c[&amp;quot;,i,&amp;quot;] = &amp;quot;,&lt;/span&gt;
&lt;span class="gi"&gt;+        string(horner(coeff(q,eps,i))),&amp;quot;;&amp;quot;)),&lt;/span&gt;
&lt;span class="gi"&gt;+    print(concat(tab3,&amp;quot;break;&amp;quot;))),&lt;/span&gt;
&lt;span class="gi"&gt;+  print(&amp;quot;    }&lt;/span&gt;
&lt;span class="gi"&gt;+}&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+&amp;#39;done)$&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+codeC1(maxpow):=block([tab2:&amp;quot;    &amp;quot;,tab3:&amp;quot;        &amp;quot;],&lt;/span&gt;
&lt;span class="gi"&gt;+  print(&amp;quot;// The coefficients C1[l] in the Fourier expansion of B1&lt;/span&gt;
&lt;span class="gi"&gt;+static inline evaluate_coeffs_C1(CT eps, CT c[]) {&lt;/span&gt;
&lt;span class="gi"&gt;+    CT eps2 = math::sqr(eps);&lt;/span&gt;
&lt;span class="gi"&gt;+    CT d = eps;&lt;/span&gt;
&lt;span class="gi"&gt;+    switch (SeriesOrder) {&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+  for n:0 thru maxpow do (&lt;/span&gt;
&lt;span class="gi"&gt;+    print(concat(tab2,&amp;quot;case &amp;quot;,string(n),&amp;quot;:&amp;quot;)),&lt;/span&gt;
&lt;span class="gi"&gt;+    for m:1 thru n do block([q:d*horner(&lt;/span&gt;
&lt;span class="gi"&gt;+        subst([eps=sqrt(eps2)],ataylor(C1[m],eps,n)/eps^m)),&lt;/span&gt;
&lt;span class="gi"&gt;+      linel:1200],&lt;/span&gt;
&lt;span class="gi"&gt;+      if m&amp;gt;1 then print(concat(tab3,&amp;quot;d *= eps;&amp;quot;)),&lt;/span&gt;
&lt;span class="gi"&gt;+      print(concat(tab3,&amp;quot;c[&amp;quot;,string(m),&amp;quot;] = &amp;quot;,string(q),&amp;quot;;&amp;quot;))),&lt;/span&gt;
&lt;span class="gi"&gt;+    print(concat(tab3,&amp;quot;break;&amp;quot;))),&lt;/span&gt;
&lt;span class="gi"&gt;+  print(&amp;quot;    }&lt;/span&gt;
&lt;span class="gi"&gt;+}&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+&amp;#39;done)$&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+revertI1(maxpow):=block([tau,eps,tauacc:1,sigacc:0],&lt;/span&gt;
&lt;span class="gi"&gt;+  for n:1 thru maxpow do (&lt;/span&gt;
&lt;span class="gi"&gt;+    tauacc:trigreduce(ataylor(&lt;/span&gt;
&lt;span class="gi"&gt;+          -sum(C1[j]*sin(2*j*tau),j,1,maxpow-n+1)*tauacc/n,&lt;/span&gt;
&lt;span class="gi"&gt;+          eps,maxpow)),&lt;/span&gt;
&lt;span class="gi"&gt;+    sigacc:sigacc+expand(diff(tauacc,tau,n-1))),&lt;/span&gt;
&lt;span class="gi"&gt;+  for i:1 thru maxpow do C1p[i]:coeff(sigacc,sin(2*i*tau)),&lt;/span&gt;
&lt;span class="gi"&gt;+  if expand(sigacc-sum(C1p[i]*sin(2*i*tau),i,1,maxpow)) # 0&lt;/span&gt;
&lt;span class="gi"&gt;+  then error(&amp;quot;left over terms in B1p&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+  &amp;#39;done)$&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+codeC1p(maxpow):=block([tab2:&amp;quot;    &amp;quot;,tab3:&amp;quot;        &amp;quot;],&lt;/span&gt;
&lt;span class="gi"&gt;+  print(&amp;quot;// The coefficients C1p[l] in the Fourier expansion of B1p&lt;/span&gt;
&lt;span class="gi"&gt;+static inline evaluate_coeffs_C1p(CT eps, CT c[])&lt;/span&gt;
&lt;span class="gi"&gt;+{&lt;/span&gt;
&lt;span class="gi"&gt;+    CT const eps2 = math::sqr(eps);&lt;/span&gt;
&lt;span class="gi"&gt;+    CT d = eps;&lt;/span&gt;
&lt;span class="gi"&gt;+    switch (SeriesOrder) {&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+  for n:0 thru maxpow do (&lt;/span&gt;
&lt;span class="gi"&gt;+    print(concat(tab2,&amp;quot;case &amp;quot;,string(n),&amp;quot;:&amp;quot;)),&lt;/span&gt;
&lt;span class="gi"&gt;+    for m:1 thru n do block([q:d*horner(&lt;/span&gt;
&lt;span class="gi"&gt;+        subst([eps=sqrt(eps2)],ataylor(C1p[m],eps,n)/eps^m)),&lt;/span&gt;
&lt;span class="gi"&gt;+      linel:1200],&lt;/span&gt;
&lt;span class="gi"&gt;+      if m&amp;gt;1 then print(concat(tab3,&amp;quot;d *= eps;&amp;quot;)),&lt;/span&gt;
&lt;span class="gi"&gt;+      print(concat(tab3,&amp;quot;c[&amp;quot;,string(m),&amp;quot;] = &amp;quot;,string(q),&amp;quot;;&amp;quot;))),&lt;/span&gt;
&lt;span class="gi"&gt;+    print(concat(tab3,&amp;quot;break;&amp;quot;))),&lt;/span&gt;
&lt;span class="gi"&gt;+  print(&amp;quot;    }&lt;/span&gt;
&lt;span class="gi"&gt;+}&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+&amp;#39;done)$&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+codeC2(maxpow):=block([tab2:&amp;quot;    &amp;quot;,tab3:&amp;quot;        &amp;quot;],&lt;/span&gt;
&lt;span class="gi"&gt;+print(&amp;quot;// The coefficients C2[l] in the Fourier expansion of B2&lt;/span&gt;
&lt;span class="gi"&gt;+static inline void evaluate_coeffs_C2(CT const&amp;amp; eps, CT c[])&lt;/span&gt;
&lt;span class="gi"&gt;+{&lt;/span&gt;
&lt;span class="gi"&gt;+    CT const eps2 = math::sqr(eps);&lt;/span&gt;
&lt;span class="gi"&gt;+    CT d = eps;&lt;/span&gt;
&lt;span class="gi"&gt;+    switch (SeriesOrder) {&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+  for n:0 thru maxpow do (&lt;/span&gt;
&lt;span class="gi"&gt;+    print(concat(tab2,&amp;quot;case &amp;quot;,string(n),&amp;quot;:&amp;quot;)),&lt;/span&gt;
&lt;span class="gi"&gt;+    for m:1 thru n do block([q:d*horner(&lt;/span&gt;
&lt;span class="gi"&gt;+        subst([eps=sqrt(eps2)],ataylor(C2[m],eps,n)/eps^m)),&lt;/span&gt;
&lt;span class="gi"&gt;+      linel:1200],&lt;/span&gt;
&lt;span class="gi"&gt;+      if m&amp;gt;1 then print(concat(tab3,&amp;quot;d *= eps;&amp;quot;)),&lt;/span&gt;
&lt;span class="gi"&gt;+      print(concat(tab3,&amp;quot;c[&amp;quot;,string(m),&amp;quot;] = &amp;quot;,string(q),&amp;quot;;&amp;quot;))),&lt;/span&gt;
&lt;span class="gi"&gt;+    print(concat(tab3,&amp;quot;break;&amp;quot;))),&lt;/span&gt;
&lt;span class="gi"&gt;+print(&amp;quot;    }&lt;/span&gt;
&lt;span class="gi"&gt;+}&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+&amp;#39;done)$&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+codeC3(maxpow):=block([tab2:&amp;quot;    &amp;quot;,tab3:&amp;quot;        &amp;quot;],&lt;/span&gt;
&lt;span class="gi"&gt;+print(&amp;quot;// The coefficients C3[l] in the Fourier expansion of B3&lt;/span&gt;
&lt;span class="gi"&gt;+static inline void evaluate_coeffs_C3(CT const&amp;amp; n, CT c[])&lt;/span&gt;
&lt;span class="gi"&gt;+{&lt;/span&gt;
&lt;span class="gi"&gt;+    const CT n2 = math::sqr(n);&lt;/span&gt;
&lt;span class="gi"&gt;+    switch (SeriesOrder) {&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+  for nn:0 thru maxpow do block([c],&lt;/span&gt;
&lt;span class="gi"&gt;+    print(concat(tab2,&amp;quot;case &amp;quot;,string(nn),&amp;quot;:&amp;quot;)),&lt;/span&gt;
&lt;span class="gi"&gt;+    c:0,&lt;/span&gt;
&lt;span class="gi"&gt;+    for m:1 thru nn-1 do block(&lt;/span&gt;
&lt;span class="gi"&gt;+      [q:if nn = 0 then 0 else&lt;/span&gt;
&lt;span class="gi"&gt;+      jtaylor(subst([n=n],C3[m]),_n,eps,nn-1),&lt;/span&gt;
&lt;span class="gi"&gt;+      linel:1200],&lt;/span&gt;
&lt;span class="gi"&gt;+      for j:m thru nn-1 do (&lt;/span&gt;
&lt;span class="gi"&gt;+        print(concat(tab3,&amp;quot;c[&amp;quot;,c,&amp;quot;] = &amp;quot;,&lt;/span&gt;
&lt;span class="gi"&gt;+            string(horner(coeff(q,eps,j))),&amp;quot;;&amp;quot;)),&lt;/span&gt;
&lt;span class="gi"&gt;+        c:c+1)&lt;/span&gt;
&lt;span class="gi"&gt;+    ),&lt;/span&gt;
&lt;span class="gi"&gt;+    print(concat(tab3,&amp;quot;break;&amp;quot;))),&lt;/span&gt;
&lt;span class="gi"&gt;+  print(&amp;quot;    }&lt;/span&gt;
&lt;span class="gi"&gt;+}&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+&amp;#39;done)$&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+maxpow:8$&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+computeI1(maxpow)$&lt;/span&gt;
&lt;span class="gi"&gt;+computeI2(maxpow)$&lt;/span&gt;
&lt;span class="gi"&gt;+computeI3(maxpow)$&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+revertI1(maxpow)$&lt;/span&gt;
&lt;span class="gi"&gt;+codeA1(maxpow)$&lt;/span&gt;
&lt;span class="gi"&gt;+codeA2(maxpow)$&lt;/span&gt;
&lt;span class="gi"&gt;+codeA3(maxpow)$&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+codeC1(maxpow)$&lt;/span&gt;
&lt;span class="gi"&gt;+codeC2(maxpow)$&lt;/span&gt;
&lt;span class="gi"&gt;+codeC3(maxpow)$&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+codeC1p(maxpow)$&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/include/boost/geometry/util/series_expansion.hpp b/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index a61df68..ba3c214 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -30,51 +30,8 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;     The expansion above is performed in Maxima, a Computer Algebra System.
&lt;span class="w"&gt; &lt;/span&gt;     The C++ code (that yields the function evaluate_series_A1 below) is
&lt;span class="gd"&gt;-     generated by the following Maxima script and is based on script:&lt;/span&gt;
&lt;span class="gd"&gt;-     http://geographiclib.sourceforge.net/html/geod.mac&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        // Maxima script begin&lt;/span&gt;
&lt;span class="gd"&gt;-        taylordepth:5$&lt;/span&gt;
&lt;span class="gd"&gt;-        ataylor(expr,var,ord):=expand(ratdisrep(taylor(expr,var,0,ord)))$&lt;/span&gt;
&lt;span class="gd"&gt;-        jtaylor(expr,var1,var2,ord):=block([zz],expand(subst([zz=1],&lt;/span&gt;
&lt;span class="gd"&gt;-        ratdisrep(taylor(subst([var1=zz*var1,var2=zz*var2],expr),zz,0,ord)))))$&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        computeI1(maxpow):=block([sintegrand,sintegrandexp,s,sigma,tau1,k2,eps],&lt;/span&gt;
&lt;span class="gd"&gt;-          sintegrand:sqrt(1+k2*sin(sigma)^2),&lt;/span&gt;
&lt;span class="gd"&gt;-          sintegrandexp:ataylor(&lt;/span&gt;
&lt;span class="gd"&gt;-              (1-eps)*subst([k2=4*eps/(1-eps)^2],sintegrand),&lt;/span&gt;
&lt;span class="gd"&gt;-              eps,maxpow),&lt;/span&gt;
&lt;span class="gd"&gt;-          s:trigreduce(integrate(sintegrandexp,sigma)),&lt;/span&gt;
&lt;span class="gd"&gt;-          s:s-subst(sigma=0,s),&lt;/span&gt;
&lt;span class="gd"&gt;-          A1:expand(subst(sigma=2*%pi,s)/(2*%pi)),&lt;/span&gt;
&lt;span class="gd"&gt;-          tau1:ataylor(s/A1,eps,maxpow),&lt;/span&gt;
&lt;span class="gd"&gt;-          for i:1 thru maxpow do C1[i]:coeff(tau1,sin(2*i*sigma)),&lt;/span&gt;
&lt;span class="gd"&gt;-          if expand(tau1-sigma-sum(C1[i]*sin(2*i*sigma),i,1,maxpow)) # 0&lt;/span&gt;
&lt;span class="gd"&gt;-          then error(&amp;quot;left over terms in B1&amp;quot;),&lt;/span&gt;
&lt;span class="gd"&gt;-          A1:A1/(1-eps),&lt;/span&gt;
&lt;span class="gd"&gt;-          &amp;#39;done)$&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        codeA1(maxpow):=block([tab2:&amp;quot;    &amp;quot;,tab3:&amp;quot;        &amp;quot;],&lt;/span&gt;
&lt;span class="gd"&gt;-        print(&amp;quot;// The scale factor A1-1 = mean value of (d/dsigma)I1 - 1&lt;/span&gt;
&lt;span class="gd"&gt;-        static inline CT evaluate_series_A1(CT eps) {&lt;/span&gt;
&lt;span class="gd"&gt;-            CT eps2 = math::sqr(eps);&lt;/span&gt;
&lt;span class="gd"&gt;-            CT t;&lt;/span&gt;
&lt;span class="gd"&gt;-            switch (SeriesOrder/2) {&amp;quot;),&lt;/span&gt;
&lt;span class="gd"&gt;-          for n:0 thru entier(maxpow/2) do block([&lt;/span&gt;
&lt;span class="gd"&gt;-            q:horner(ataylor(subst([eps=sqrt(eps2)],A1*(1-eps)-1),eps2,n)),&lt;/span&gt;
&lt;span class="gd"&gt;-            linel:1200],&lt;/span&gt;
&lt;span class="gd"&gt;-            print(concat(tab2,&amp;quot;case &amp;quot;,string(n),&amp;quot;:&amp;quot;)),&lt;/span&gt;
&lt;span class="gd"&gt;-            print(concat(tab3,&amp;quot;t = &amp;quot;,string(q),&amp;quot;;&amp;quot;)),&lt;/span&gt;
&lt;span class="gd"&gt;-            print(concat(tab3,&amp;quot;break;&amp;quot;))),&lt;/span&gt;
&lt;span class="gd"&gt;-          print(&amp;quot;    }&lt;/span&gt;
&lt;span class="gd"&gt;-            return (t + eps) / (1 - eps);&lt;/span&gt;
&lt;span class="gd"&gt;-        }&amp;quot;),&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;#39;done)$&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        maxpow:8$&lt;/span&gt;
&lt;span class="gd"&gt;-        computeI1(maxpow)$&lt;/span&gt;
&lt;span class="gd"&gt;-        codeA1(maxpow)$&lt;/span&gt;
&lt;span class="gd"&gt;-        // Maxima script end&lt;/span&gt;
&lt;span class="gi"&gt;+     generated by the following Maxima script:&lt;/span&gt;
&lt;span class="gi"&gt;+     geometry/doc/other/maxima/geod.mac&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;     To replace each number x by CT(x) the following
&lt;span class="w"&gt; &lt;/span&gt;     script can be used:
&lt;span class="gu"&gt;@@ -123,52 +80,8 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;     The expansion above is performed in Maxima, a Computer Algebra System.
&lt;span class="w"&gt; &lt;/span&gt;     The C++ code (that yields the function evaluate_series_A2 below) is
&lt;span class="gd"&gt;-     generated by the following Maxima script and is based on script:&lt;/span&gt;
&lt;span class="gd"&gt;-     http://geographiclib.sourceforge.net/html/geod.mac&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        // Maxima script begin&lt;/span&gt;
&lt;span class="gd"&gt;-        computeI2(maxpow):=block([sintegrand,sintegrandexp,s,sigma,tau1,k2,eps],&lt;/span&gt;
&lt;span class="gd"&gt;-          sintegrand:1/sqrt(1+k2*sin(sigma)^2),&lt;/span&gt;
&lt;span class="gd"&gt;-          sintegrandexp:ataylor(&lt;/span&gt;
&lt;span class="gd"&gt;-              (1+eps)*subst([k2=4*eps/(1-eps)^2],sintegrand),&lt;/span&gt;
&lt;span class="gd"&gt;-              eps,maxpow),&lt;/span&gt;
&lt;span class="gd"&gt;-          s:trigreduce(integrate(sintegrandexp,sigma)),&lt;/span&gt;
&lt;span class="gd"&gt;-          s:s-subst(sigma=0,s),&lt;/span&gt;
&lt;span class="gd"&gt;-          A2:expand(subst(sigma=2*%pi,s)/(2*%pi)),&lt;/span&gt;
&lt;span class="gd"&gt;-          tau1:ataylor(s/A2,eps,maxpow),&lt;/span&gt;
&lt;span class="gd"&gt;-          for i:1 thru maxpow do C2[i]:coeff(tau1,sin(2*i*sigma)),&lt;/span&gt;
&lt;span class="gd"&gt;-          if expand(tau1-sigma-sum(C2[i]*sin(2*i*sigma),i,1,maxpow)) # 0&lt;/span&gt;
&lt;span class="gd"&gt;-          then error(&amp;quot;left over terms in B2&amp;quot;),&lt;/span&gt;
&lt;span class="gd"&gt;-          A2:A2/(1+eps),&lt;/span&gt;
&lt;span class="gd"&gt;-          &amp;#39;done)$&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        codeA2(maxpow):=block([tab2:&amp;quot;    &amp;quot;,tab3:&amp;quot;        &amp;quot;],&lt;/span&gt;
&lt;span class="gd"&gt;-        print(&amp;quot;// The scale factor A2-1 = mean value of (d/dsigma)I2 - 1&lt;/span&gt;
&lt;span class="gd"&gt;-        CT evaluate_series_A2(CT const&amp;amp; eps)&lt;/span&gt;
&lt;span class="gd"&gt;-        {&lt;/span&gt;
&lt;span class="gd"&gt;-            CT const eps2 = math::sqr(eps);&lt;/span&gt;
&lt;span class="gd"&gt;-            CT t;&lt;/span&gt;
&lt;span class="gd"&gt;-            switch (SeriesOrder/2) {&amp;quot;),&lt;/span&gt;
&lt;span class="gd"&gt;-          for n:0 thru entier(maxpow/2) do block([&lt;/span&gt;
&lt;span class="gd"&gt;-            q:horner(ataylor(subst([eps=sqrt(eps2)],A2*(1+eps)-1),eps2,n)),&lt;/span&gt;
&lt;span class="gd"&gt;-            linel:1200],&lt;/span&gt;
&lt;span class="gd"&gt;-            print(concat(tab2,&amp;quot;case &amp;quot;,string(n),&amp;quot;:&amp;quot;)),&lt;/span&gt;
&lt;span class="gd"&gt;-            print(concat(tab3,&amp;quot;t = &amp;quot;,string(q),&amp;quot;;&amp;quot;)),&lt;/span&gt;
&lt;span class="gd"&gt;-            print(concat(tab3,&amp;quot;break;&amp;quot;))),&lt;/span&gt;
&lt;span class="gd"&gt;-          print(&amp;quot;    }&lt;/span&gt;
&lt;span class="gd"&gt;-            return (t - eps) / (1 + eps);&lt;/span&gt;
&lt;span class="gd"&gt;-        }&amp;quot;),&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;#39;done)$&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        maxpow:8$&lt;/span&gt;
&lt;span class="gd"&gt;-        computeI2(maxpow)$&lt;/span&gt;
&lt;span class="gd"&gt;-        codeA2(maxpow)$&lt;/span&gt;
&lt;span class="gd"&gt;-        // Maxima script end&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-     To replace each number x by CT(x) the following&lt;/span&gt;
&lt;span class="gd"&gt;-     script can be used:&lt;/span&gt;
&lt;span class="gd"&gt;-       sed -e &amp;#39;s/[0-9]\+/CT(&amp;amp;)/g; s/\[CT/\[/g; s/)\]/\]/g;&lt;/span&gt;
&lt;span class="gd"&gt;-               s/case\sCT(/case /g; s/):/:/g; s/epsCT(2)/eps2/g;&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+     generated by the following Maxima script:&lt;/span&gt;
&lt;span class="gi"&gt;+     geometry/doc/other/maxima/geod.mac&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    */
&lt;span class="w"&gt; &lt;/span&gt;    template &amp;lt;typename CT, std::size_t SeriesOrder&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;    CT evaluate_series_A2(CT const&amp;amp; eps)
&lt;span class="gu"&gt;@@ -212,53 +125,8 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;     The expansion above is performed in Maxima, a Computer Algebra System.
&lt;span class="w"&gt; &lt;/span&gt;     The C++ code (that yields the function evaluate_coeffs_A3 below) is
&lt;span class="gd"&gt;-     generated by the following Maxima script and is based on script:&lt;/span&gt;
&lt;span class="gd"&gt;-     http://geographiclib.sourceforge.net/html/geod.mac&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        // Maxima script begin&lt;/span&gt;
&lt;span class="gd"&gt;-        computeI3(maxpow):=block([int,intexp,dlam,eta,del,eps,nu,f,z,n],&lt;/span&gt;
&lt;span class="gd"&gt;-          maxpow:maxpow-1,&lt;/span&gt;
&lt;span class="gd"&gt;-          int:subst([k2=4*eps/(1-eps)^2],&lt;/span&gt;
&lt;span class="gd"&gt;-            (2-f)/(1+(1-f)*sqrt(1+k2*sin(sigma)^2))),&lt;/span&gt;
&lt;span class="gd"&gt;-          int:subst([f=2*n/(1+n)],int),&lt;/span&gt;
&lt;span class="gd"&gt;-          intexp:jtaylor(int,n,eps,maxpow),&lt;/span&gt;
&lt;span class="gd"&gt;-          dlam:trigreduce(integrate(intexp,sigma)),&lt;/span&gt;
&lt;span class="gd"&gt;-          dlam:dlam-subst(sigma=0,dlam),&lt;/span&gt;
&lt;span class="gd"&gt;-          A3:expand(subst(sigma=2*%pi,dlam)/(2*%pi)),&lt;/span&gt;
&lt;span class="gd"&gt;-          eta:jtaylor(dlam/A3,n,eps,maxpow),&lt;/span&gt;
&lt;span class="gd"&gt;-          A3:jtaylor(A3,n,eps,maxpow),&lt;/span&gt;
&lt;span class="gd"&gt;-          for i:1 thru maxpow do C3[i]:coeff(eta,sin(2*i*sigma)),&lt;/span&gt;
&lt;span class="gd"&gt;-          if expand(eta-sigma-sum(C3[i]*sin(2*i*sigma),i,1,maxpow)) # 0&lt;/span&gt;
&lt;span class="gd"&gt;-          then error(&amp;quot;left over terms in B3&amp;quot;),&lt;/span&gt;
&lt;span class="gd"&gt;-          &amp;#39;done)$&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        codeA3(maxpow):=block([tab2:&amp;quot;    &amp;quot;,tab3:&amp;quot;        &amp;quot;],&lt;/span&gt;
&lt;span class="gd"&gt;-        print(&amp;quot;// The scale factor A3 = mean value of (d/dsigma)I3&lt;/span&gt;
&lt;span class="gd"&gt;-        static inline void evaluate_series_A3(CT const&amp;amp; n, CT c[])&lt;/span&gt;
&lt;span class="gd"&gt;-        {&lt;/span&gt;
&lt;span class="gd"&gt;-            switch (SeriesOrder) {&amp;quot;),&lt;/span&gt;
&lt;span class="gd"&gt;-          for nn:0 thru maxpow do block(&lt;/span&gt;
&lt;span class="gd"&gt;-            [q:if nn=0 then 0 else&lt;/span&gt;
&lt;span class="gd"&gt;-            jtaylor(subst([n=n],A3),n,eps,nn-1),&lt;/span&gt;
&lt;span class="gd"&gt;-            linel:1200],&lt;/span&gt;
&lt;span class="gd"&gt;-            print(concat(tab2,&amp;quot;case &amp;quot;,string(nn),&amp;quot;:&amp;quot;)),&lt;/span&gt;
&lt;span class="gd"&gt;-            for i : 0 thru nn-1 do&lt;/span&gt;
&lt;span class="gd"&gt;-            print(concat(tab3,&amp;quot;c[&amp;quot;,i,&amp;quot;] = &amp;quot;,&lt;/span&gt;
&lt;span class="gd"&gt;-                string(horner(coeff(q,eps,i))),&amp;quot;;&amp;quot;)),&lt;/span&gt;
&lt;span class="gd"&gt;-            print(concat(tab3,&amp;quot;break;&amp;quot;))),&lt;/span&gt;
&lt;span class="gd"&gt;-          print(&amp;quot;    }&lt;/span&gt;
&lt;span class="gd"&gt;-        }&amp;quot;),&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;#39;done)$&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        maxpow:8$&lt;/span&gt;
&lt;span class="gd"&gt;-        computeI3(maxpow)$&lt;/span&gt;
&lt;span class="gd"&gt;-        codeA3(maxpow)$&lt;/span&gt;
&lt;span class="gd"&gt;-        // Maxima script end&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-     To replace each number x by CT(x) the following&lt;/span&gt;
&lt;span class="gd"&gt;-     script can be used:&lt;/span&gt;
&lt;span class="gd"&gt;-       sed -e &amp;#39;s/[0-9]\+/CT(&amp;amp;)/g; s/\[CT(/\[/g; s/)\]/\]/g;&lt;/span&gt;
&lt;span class="gd"&gt;-               s/case\sCT(/case /g; s/):/:/g&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+     generated by the following Maxima script:&lt;/span&gt;
&lt;span class="gi"&gt;+     geometry/doc/other/maxima/geod.mac&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    */
&lt;span class="w"&gt; &lt;/span&gt;    // TODO: this produces different results that geographiclib
&lt;span class="w"&gt; &lt;/span&gt;    template &amp;lt;typename CT, std::size_t SeriesOrder&amp;gt;
&lt;span class="gu"&gt;@@ -327,38 +195,8 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;     The expansion below is performed in Maxima, a Computer Algebra System.
&lt;span class="w"&gt; &lt;/span&gt;     The C++ code (that yields the function evaluate_coeffs_C1 below) is
&lt;span class="gd"&gt;-     generated by the following Maxima script and is based on script:&lt;/span&gt;
&lt;span class="gd"&gt;-     http://geographiclib.sourceforge.net/html/geod.mac&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        // Maxima script begin&lt;/span&gt;
&lt;span class="gd"&gt;-        codeC1(maxpow):=block([tab2:&amp;quot;    &amp;quot;,tab3:&amp;quot;        &amp;quot;],&lt;/span&gt;
&lt;span class="gd"&gt;-          print(&amp;quot;// The coefficients C1[l] in the Fourier expansion of B1&lt;/span&gt;
&lt;span class="gd"&gt;-        static inline evaluate_coeffs_C1(CT eps, CT c[]) {&lt;/span&gt;
&lt;span class="gd"&gt;-            CT eps2 = math::sqr(eps);&lt;/span&gt;
&lt;span class="gd"&gt;-            CT d = eps;&lt;/span&gt;
&lt;span class="gd"&gt;-            switch (SeriesOrder) {&amp;quot;),&lt;/span&gt;
&lt;span class="gd"&gt;-          for n:0 thru maxpow do (&lt;/span&gt;
&lt;span class="gd"&gt;-            print(concat(tab2,&amp;quot;case &amp;quot;,string(n),&amp;quot;:&amp;quot;)),&lt;/span&gt;
&lt;span class="gd"&gt;-            for m:1 thru n do block([q:d*horner(&lt;/span&gt;
&lt;span class="gd"&gt;-                subst([eps=sqrt(eps2)],ataylor(C1[m],eps,n)/eps^m)),&lt;/span&gt;
&lt;span class="gd"&gt;-              linel:1200],&lt;/span&gt;
&lt;span class="gd"&gt;-              if m&amp;gt;1 then print(concat(tab3,&amp;quot;d *= eps;&amp;quot;)),&lt;/span&gt;
&lt;span class="gd"&gt;-              print(concat(tab3,&amp;quot;c[&amp;quot;,string(m),&amp;quot;] = &amp;quot;,string(q),&amp;quot;;&amp;quot;))),&lt;/span&gt;
&lt;span class="gd"&gt;-            print(concat(tab3,&amp;quot;break;&amp;quot;))),&lt;/span&gt;
&lt;span class="gd"&gt;-          print(&amp;quot;    }&lt;/span&gt;
&lt;span class="gd"&gt;-        }&amp;quot;),&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;#39;done)$&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        maxpow:8$&lt;/span&gt;
&lt;span class="gd"&gt;-        computeI1(maxpow)$&lt;/span&gt;
&lt;span class="gd"&gt;-        codeC1(maxpow)$&lt;/span&gt;
&lt;span class="gd"&gt;-        // Maxima script end&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-     To replace each number x by CT(x) the following&lt;/span&gt;
&lt;span class="gd"&gt;-     script can be used:&lt;/span&gt;
&lt;span class="gd"&gt;-       sed -e &amp;#39;s/[0-9]\+/CT(&amp;amp;)/g; s/\[CT(/\[/g; s/)\]/\]/g;&lt;/span&gt;
&lt;span class="gd"&gt;-               s/case\sCT(/case /g; s/):/:/g; s/epsCT(2)/eps2/g;&lt;/span&gt;
&lt;span class="gd"&gt;-               s/eps(CT(2))/eps2/g;&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+     generated by the following Maxima script:&lt;/span&gt;
&lt;span class="gi"&gt;+     geometry/doc/other/maxima/geod.mac&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    */
&lt;span class="w"&gt; &lt;/span&gt;    template &amp;lt;typename CT, std::size_t SeriesOrder&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;    static inline void evaluate_coeffs_C1(CT eps, CT c[])
&lt;span class="gu"&gt;@@ -456,51 +294,8 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;     The expansion below is performed in Maxima, a Computer Algebra System.
&lt;span class="w"&gt; &lt;/span&gt;     The C++ code (that yields the function evaluate_coeffs_C1p below) is
&lt;span class="gd"&gt;-     generated by the following Maxima script and is based on script:&lt;/span&gt;
&lt;span class="gd"&gt;-     http://geographiclib.sourceforge.net/html/geod.mac&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        // Maxima script begin&lt;/span&gt;
&lt;span class="gd"&gt;-        revertI1(maxpow):=block([tau,eps,tauacc:1,sigacc:0],&lt;/span&gt;
&lt;span class="gd"&gt;-          for n:1 thru maxpow do (&lt;/span&gt;
&lt;span class="gd"&gt;-            tauacc:trigreduce(ataylor(&lt;/span&gt;
&lt;span class="gd"&gt;-                  -sum(C1[j]*sin(2*j*tau),j,1,maxpow-n+1)*tauacc/n,&lt;/span&gt;
&lt;span class="gd"&gt;-                  eps,maxpow)),&lt;/span&gt;
&lt;span class="gd"&gt;-            sigacc:sigacc+expand(diff(tauacc,tau,n-1))),&lt;/span&gt;
&lt;span class="gd"&gt;-          for i:1 thru maxpow do C1p[i]:coeff(sigacc,sin(2*i*tau)),&lt;/span&gt;
&lt;span class="gd"&gt;-          if expand(sigacc-sum(C1p[i]*sin(2*i*tau),i,1,maxpow)) # 0&lt;/span&gt;
&lt;span class="gd"&gt;-          then error(&amp;quot;left over terms in B1p&amp;quot;),&lt;/span&gt;
&lt;span class="gd"&gt;-          &amp;#39;done)$&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        codeC1p(maxpow):=block([tab2:&amp;quot;    &amp;quot;,tab3:&amp;quot;        &amp;quot;],&lt;/span&gt;
&lt;span class="gd"&gt;-          print(&amp;quot;// The coefficients C1p[l] in the Fourier expansion of B1p&lt;/span&gt;
&lt;span class="gd"&gt;-        static inline evaluate_coeffs_C1p(CT eps, CT c[])&lt;/span&gt;
&lt;span class="gd"&gt;-        {&lt;/span&gt;
&lt;span class="gd"&gt;-            CT const eps2 = math::sqr(eps);&lt;/span&gt;
&lt;span class="gd"&gt;-            CT d = eps;&lt;/span&gt;
&lt;span class="gd"&gt;-            switch (SeriesOrder) {&amp;quot;),&lt;/span&gt;
&lt;span class="gd"&gt;-          for n:0 thru maxpow do (&lt;/span&gt;
&lt;span class="gd"&gt;-            print(concat(tab2,&amp;quot;case &amp;quot;,string(n),&amp;quot;:&amp;quot;)),&lt;/span&gt;
&lt;span class="gd"&gt;-            for m:1 thru n do block([q:d*horner(&lt;/span&gt;
&lt;span class="gd"&gt;-                subst([eps=sqrt(eps2)],ataylor(C1p[m],eps,n)/eps^m)),&lt;/span&gt;
&lt;span class="gd"&gt;-              linel:1200],&lt;/span&gt;
&lt;span class="gd"&gt;-              if m&amp;gt;1 then print(concat(tab3,&amp;quot;d *= eps;&amp;quot;)),&lt;/span&gt;
&lt;span class="gd"&gt;-              print(concat(tab3,&amp;quot;c[&amp;quot;,string(m),&amp;quot;] = &amp;quot;,string(q),&amp;quot;;&amp;quot;))),&lt;/span&gt;
&lt;span class="gd"&gt;-            print(concat(tab3,&amp;quot;break;&amp;quot;))),&lt;/span&gt;
&lt;span class="gd"&gt;-          print(&amp;quot;    }&lt;/span&gt;
&lt;span class="gd"&gt;-        }&amp;quot;),&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;#39;done)$&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        maxpow:8$&lt;/span&gt;
&lt;span class="gd"&gt;-        computeI1(maxpow)$&lt;/span&gt;
&lt;span class="gd"&gt;-        revertI1(maxpow)$&lt;/span&gt;
&lt;span class="gd"&gt;-        codeC1p(maxpow)$&lt;/span&gt;
&lt;span class="gd"&gt;-        // Maxima script end&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-     To replace each number x by CT(x) the following&lt;/span&gt;
&lt;span class="gd"&gt;-     script can be used:&lt;/span&gt;
&lt;span class="gd"&gt;-       sed -e &amp;#39;s/[0-9]\+/CT(&amp;amp;)/g; s/\[CT(/\[/g; s/)\]/\]/g;&lt;/span&gt;
&lt;span class="gd"&gt;-               s/case\sCT(/case /g; s/):/:/g; s/epsCT(2)/eps2/g;&lt;/span&gt;
&lt;span class="gd"&gt;-               s/eps(CT(2))/eps2/g;&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+     generated by the following Maxima script:&lt;/span&gt;
&lt;span class="gi"&gt;+     geometry/doc/other/maxima/geod.mac&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    */
&lt;span class="w"&gt; &lt;/span&gt;    template &amp;lt;typename CT, std::size_t SeriesOrder&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;    static inline void evaluate_coeffs_C1p(CT eps, CT c[])
&lt;span class="gu"&gt;@@ -598,39 +393,8 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;     The expansion below is performed in Maxima, a Computer Algebra System.
&lt;span class="w"&gt; &lt;/span&gt;     The C++ code (that yields the function evaluate_coeffs_C2 below) is
&lt;span class="gd"&gt;-     generated by the following Maxima script and is based on script:&lt;/span&gt;
&lt;span class="gd"&gt;-     http://geographiclib.sourceforge.net/html/geod.mac&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        // Maxima script begin&lt;/span&gt;
&lt;span class="gd"&gt;-        codeC2(maxpow):=block([tab2:&amp;quot;    &amp;quot;,tab3:&amp;quot;        &amp;quot;],&lt;/span&gt;
&lt;span class="gd"&gt;-        print(&amp;quot;// The coefficients C2[l] in the Fourier expansion of B2&lt;/span&gt;
&lt;span class="gd"&gt;-        static inline void evaluate_coeffs_C2(CT const&amp;amp; eps, CT c[])&lt;/span&gt;
&lt;span class="gd"&gt;-        {&lt;/span&gt;
&lt;span class="gd"&gt;-            CT const eps2 = math::sqr(eps);&lt;/span&gt;
&lt;span class="gd"&gt;-            CT d = eps;&lt;/span&gt;
&lt;span class="gd"&gt;-            switch (SeriesOrder) {&amp;quot;),&lt;/span&gt;
&lt;span class="gd"&gt;-          for n:0 thru maxpow do (&lt;/span&gt;
&lt;span class="gd"&gt;-            print(concat(tab2,&amp;quot;case &amp;quot;,string(n),&amp;quot;:&amp;quot;)),&lt;/span&gt;
&lt;span class="gd"&gt;-            for m:1 thru n do block([q:d*horner(&lt;/span&gt;
&lt;span class="gd"&gt;-                subst([eps=sqrt(eps2)],ataylor(C2[m],eps,n)/eps^m)),&lt;/span&gt;
&lt;span class="gd"&gt;-              linel:1200],&lt;/span&gt;
&lt;span class="gd"&gt;-              if m&amp;gt;1 then print(concat(tab3,&amp;quot;d *= eps;&amp;quot;)),&lt;/span&gt;
&lt;span class="gd"&gt;-              print(concat(tab3,&amp;quot;c[&amp;quot;,string(m),&amp;quot;] = &amp;quot;,string(q),&amp;quot;;&amp;quot;))),&lt;/span&gt;
&lt;span class="gd"&gt;-            print(concat(tab3,&amp;quot;break;&amp;quot;))),&lt;/span&gt;
&lt;span class="gd"&gt;-        print(&amp;quot;    }&lt;/span&gt;
&lt;span class="gd"&gt;-        }&amp;quot;),&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;#39;done)$&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        maxpow:8$&lt;/span&gt;
&lt;span class="gd"&gt;-        computeI2(maxpow)$&lt;/span&gt;
&lt;span class="gd"&gt;-        codeC2(maxpow)$&lt;/span&gt;
&lt;span class="gd"&gt;-        // Maxima script end&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-     To replace each number x by CT(x) the following&lt;/span&gt;
&lt;span class="gd"&gt;-     script can be used:&lt;/span&gt;
&lt;span class="gd"&gt;-       sed -e &amp;#39;s/[0-9]\+/CT(&amp;amp;)/g; s/\[CT(/\[/g; s/)\]/\]/g;&lt;/span&gt;
&lt;span class="gd"&gt;-               s/case\sCT(/case /g; s/):/:/g; s/epsCT(2)/eps2/g;&lt;/span&gt;
&lt;span class="gd"&gt;-               s/eps(CT(2))/eps2/g;&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+     generated by the following Maxima script:&lt;/span&gt;
&lt;span class="gi"&gt;+     geometry/doc/other/maxima/geod.mac&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    */
&lt;span class="w"&gt; &lt;/span&gt;    template &amp;lt;typename CT, std::size_t SeriesOrder&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;    static inline void evaluate_coeffs_C2(CT const&amp;amp; eps, CT c[])
&lt;span class="gu"&gt;@@ -728,43 +492,8 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;     The expansion below is performed in Maxima, a Computer Algebra System.
&lt;span class="w"&gt; &lt;/span&gt;     The C++ code (that yields the function evaluate_coeffs_C3 below) is
&lt;span class="gd"&gt;-     generated by the following Maxima script and is based on script:&lt;/span&gt;
&lt;span class="gd"&gt;-     http://geographiclib.sourceforge.net/html/geod.mac&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        // Maxima script begin&lt;/span&gt;
&lt;span class="gd"&gt;-        codeC3(maxpow):=block([tab2:&amp;quot;    &amp;quot;,tab3:&amp;quot;        &amp;quot;],&lt;/span&gt;
&lt;span class="gd"&gt;-        print(&amp;quot;// The coefficients C3[l] in the Fourier expansion of B3&lt;/span&gt;
&lt;span class="gd"&gt;-        static inline void evaluate_coeffs_C3(CT const&amp;amp; n, CT c[])&lt;/span&gt;
&lt;span class="gd"&gt;-        {&lt;/span&gt;
&lt;span class="gd"&gt;-            const CT n2 = math::sqr(n);&lt;/span&gt;
&lt;span class="gd"&gt;-            switch (SeriesOrder) {&amp;quot;),&lt;/span&gt;
&lt;span class="gd"&gt;-          for nn:0 thru maxpow do block([c],&lt;/span&gt;
&lt;span class="gd"&gt;-            print(concat(tab2,&amp;quot;case &amp;quot;,string(nn),&amp;quot;:&amp;quot;)),&lt;/span&gt;
&lt;span class="gd"&gt;-            c:0,&lt;/span&gt;
&lt;span class="gd"&gt;-            for m:1 thru nn-1 do block(&lt;/span&gt;
&lt;span class="gd"&gt;-              [q:if nn = 0 then 0 else&lt;/span&gt;
&lt;span class="gd"&gt;-              jtaylor(subst([n=n],C3[m]),_n,eps,nn-1),&lt;/span&gt;
&lt;span class="gd"&gt;-              linel:1200],&lt;/span&gt;
&lt;span class="gd"&gt;-              for j:m thru nn-1 do (&lt;/span&gt;
&lt;span class="gd"&gt;-                print(concat(tab3,&amp;quot;c[&amp;quot;,c,&amp;quot;] = &amp;quot;,&lt;/span&gt;
&lt;span class="gd"&gt;-                    string(horner(coeff(q,eps,j))),&amp;quot;;&amp;quot;)),&lt;/span&gt;
&lt;span class="gd"&gt;-                c:c+1)&lt;/span&gt;
&lt;span class="gd"&gt;-            ),&lt;/span&gt;
&lt;span class="gd"&gt;-            print(concat(tab3,&amp;quot;break;&amp;quot;))),&lt;/span&gt;
&lt;span class="gd"&gt;-          print(&amp;quot;    }&lt;/span&gt;
&lt;span class="gd"&gt;-        }&amp;quot;),&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;#39;done)$&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        maxpow:8$&lt;/span&gt;
&lt;span class="gd"&gt;-        computeI3(maxpow)$&lt;/span&gt;
&lt;span class="gd"&gt;-        codeC3(maxpow)$&lt;/span&gt;
&lt;span class="gd"&gt;-        // Maxima script end&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-     To replace each number x by CT(x) the following&lt;/span&gt;
&lt;span class="gd"&gt;-     script can be used:&lt;/span&gt;
&lt;span class="gd"&gt;-       sed -e &amp;#39;s/[0-9]\+/CT(&amp;amp;)/g; s/\[CT(/\[/g; s/)\]/\]/g;&lt;/span&gt;
&lt;span class="gd"&gt;-               s/case\sCT(/case /g; s/):/:/g; s/epsCT(2)/eps2/g;&lt;/span&gt;
&lt;span class="gd"&gt;-               s/eps(CT(2))/eps2/g;&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+     generated by the following Maxima script:&lt;/span&gt;
&lt;span class="gi"&gt;+     geometry/doc/other/maxima/geod.mac&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    */
&lt;span class="w"&gt; &lt;/span&gt;    template &amp;lt;typename CT, int SeriesOrder&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;    void evaluate_coeffs_C3x(CT const&amp;amp; n, CT c[], const CT coeff[])

commit b9b0f85560e719278e8d4e9fdc7da998dd5c7857
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Tue Jun 5 12:44:36 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [util] Add functions to normalize / sum two given values (angles)

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/util/math.hpp b/include/boost/geometry/util/math.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index e0ab1c8..652ca85 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/util/math.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/util/math.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -845,13 +845,50 @@ inline void normalize(T&amp;amp; x, T&amp;amp; y)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;\brief Normalize a given angle.
&lt;span class="w"&gt; &lt;/span&gt;*/
&lt;span class="w"&gt; &lt;/span&gt;template&amp;lt;typename T&amp;gt;
&lt;span class="gd"&gt;-    inline T normalize_angle(T x)&lt;/span&gt;
&lt;span class="gi"&gt;+inline T normalize_angle(T x)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;{
&lt;span class="w"&gt; &lt;/span&gt;    T y = std::fmod(x, T(360));

&lt;span class="w"&gt; &lt;/span&gt;    return y &amp;lt;= -180 ? y + 360 : (y &amp;lt;= 180 ? y : y - 360);
&lt;span class="w"&gt; &lt;/span&gt;}

&lt;span class="gi"&gt;+/*!&lt;/span&gt;
&lt;span class="gi"&gt;+\brief The error-free sum of two numbers.&lt;/span&gt;
&lt;span class="gi"&gt;+*/&lt;/span&gt;
&lt;span class="gi"&gt;+template&amp;lt;typename T&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+inline T sum_error(T u, T v, T&amp;amp; t)&lt;/span&gt;
&lt;span class="gi"&gt;+{&lt;/span&gt;
&lt;span class="gi"&gt;+    volatile T s = u + v;&lt;/span&gt;
&lt;span class="gi"&gt;+    volatile T up = s - v;&lt;/span&gt;
&lt;span class="gi"&gt;+    volatile T vpp = s - up;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    up -= u;&lt;/span&gt;
&lt;span class="gi"&gt;+    vpp -= v;&lt;/span&gt;
&lt;span class="gi"&gt;+    t = -(up + vpp);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    return s;&lt;/span&gt;
&lt;span class="gi"&gt;+}&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+/*!&lt;/span&gt;
&lt;span class="gi"&gt;+\brief The exact difference of two angles reduced to&lt;/span&gt;
&lt;span class="gi"&gt;+       (&amp;amp;minus;180&amp;amp;deg;, 180&amp;amp;deg;].&lt;/span&gt;
&lt;span class="gi"&gt;+*/&lt;/span&gt;
&lt;span class="gi"&gt;+template&amp;lt;typename T&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+inline T difference_angle(T x, T y, T&amp;amp; e)&lt;/span&gt;
&lt;span class="gi"&gt;+{&lt;/span&gt;
&lt;span class="gi"&gt;+    T t, d = normalize_angle(sum_error(std::remainder(-x, T(360)),&lt;/span&gt;
&lt;span class="gi"&gt;+                                       std::remainder(y, T(360)), t));&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    // Here y - x = d + t (mod 360), exactly, where d is in (-180,180] and&lt;/span&gt;
&lt;span class="gi"&gt;+    // abs(t) &amp;lt;= eps (eps = 2^-45 for doubles).  The only case where the&lt;/span&gt;
&lt;span class="gi"&gt;+    // addition of t takes the result outside the range (-180,180] is d = 180&lt;/span&gt;
&lt;span class="gi"&gt;+    // and t &amp;gt; 0.  The case, d = -180 + eps, t = -eps, can&amp;#39;t happen, since&lt;/span&gt;
&lt;span class="gi"&gt;+    // sum_error would have returned the exact result in such a case (i.e., given t = 0).&lt;/span&gt;
&lt;span class="gi"&gt;+    return sum_error(d == 180 &amp;amp;&amp;amp; t &amp;gt; 0 ? -180 : d, t, e);&lt;/span&gt;
&lt;span class="gi"&gt;+}&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;/*
&lt;span class="w"&gt; &lt;/span&gt;\brief Evaluate the polynomial in x using Horner&amp;#39;s method.
&lt;span class="w"&gt; &lt;/span&gt;*/

commit a10815366a58d1dd058dd0447843fbac89327b37
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Tue Jun 5 10:21:49 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [test] Add comment providing dataset source and how it is parsed

&lt;span class="gh"&gt;diff --git a/test/formulas/direct_cases_antipodal.hpp b/test/formulas/direct_cases_antipodal.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 541d3bf..37214fe 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/test/formulas/direct_cases_antipodal.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/test/formulas/direct_cases_antipodal.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -20,6 +20,17 @@ struct expected_results_antipodal&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    expected_result karney;
&lt;span class="w"&gt; &lt;/span&gt;};

&lt;span class="gi"&gt;+/*&lt;/span&gt;
&lt;span class="gi"&gt;+ These values are collected from GeodTest which is associated with GeographicLib:&lt;/span&gt;
&lt;span class="gi"&gt;+     https://zenodo.org/record/32156&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+ The conversion to C++ array format is done using this Python script:&lt;/span&gt;
&lt;span class="gi"&gt;+     https://github.com/adl1995/boost-geometry-extra/blob/master/geographicLib-dataset-parse.py&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+ Geodesic scale (M12) is absent from the GeodTest dataset, so it is manually generated&lt;/span&gt;
&lt;span class="gi"&gt;+ using GeographicLib through this C++ script:&lt;/span&gt;
&lt;span class="gi"&gt;+     https://github.com/adl1995/boost-geometry-extra/blob/master/geographicLib-direct-antipodal.cpp&lt;/span&gt;
&lt;span class="gi"&gt;+*/&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;expected_results_antipodal expected_antipodal[] =
&lt;span class="w"&gt; &lt;/span&gt;{
&lt;span class="w"&gt; &lt;/span&gt;    {

commit cc19342b4eac30183fd6f40d4d4d0a8b36fed885
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Tue Jun 5 09:28:37 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [util] Remove duplicated Maxima code from series_expansion.hpp

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/util/series_expansion.hpp b/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 3566aca..a61df68 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -39,7 +39,7 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        jtaylor(expr,var1,var2,ord):=block([zz],expand(subst([zz=1],
&lt;span class="w"&gt; &lt;/span&gt;        ratdisrep(taylor(subst([var1=zz*var1,var2=zz*var2],expr),zz,0,ord)))))$

&lt;span class="gd"&gt;-        computeintegral(maxpow):=block([sintegrand,sintegrandexp,s,sigma,tau1,k2,eps],&lt;/span&gt;
&lt;span class="gi"&gt;+        computeI1(maxpow):=block([sintegrand,sintegrandexp,s,sigma,tau1,k2,eps],&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;          sintegrand:sqrt(1+k2*sin(sigma)^2),
&lt;span class="w"&gt; &lt;/span&gt;          sintegrandexp:ataylor(
&lt;span class="w"&gt; &lt;/span&gt;              (1-eps)*subst([k2=4*eps/(1-eps)^2],sintegrand),
&lt;span class="gu"&gt;@@ -54,7 +54,7 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;          A1:A1/(1-eps),
&lt;span class="w"&gt; &lt;/span&gt;          &amp;#39;done)$

&lt;span class="gd"&gt;-        generatecode(maxpow):=block([tab2:&amp;quot;    &amp;quot;,tab3:&amp;quot;        &amp;quot;],&lt;/span&gt;
&lt;span class="gi"&gt;+        codeA1(maxpow):=block([tab2:&amp;quot;    &amp;quot;,tab3:&amp;quot;        &amp;quot;],&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        print(&amp;quot;// The scale factor A1-1 = mean value of (d/dsigma)I1 - 1
&lt;span class="w"&gt; &lt;/span&gt;        static inline CT evaluate_series_A1(CT eps) {
&lt;span class="w"&gt; &lt;/span&gt;            CT eps2 = math::sqr(eps);
&lt;span class="gu"&gt;@@ -72,8 +72,8 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;#39;done)$

&lt;span class="w"&gt; &lt;/span&gt;        maxpow:8$
&lt;span class="gd"&gt;-        computeintegral(maxpow)$&lt;/span&gt;
&lt;span class="gd"&gt;-        generatecode(maxpow)$&lt;/span&gt;
&lt;span class="gi"&gt;+        computeI1(maxpow)$&lt;/span&gt;
&lt;span class="gi"&gt;+        codeA1(maxpow)$&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        // Maxima script end

&lt;span class="w"&gt; &lt;/span&gt;     To replace each number x by CT(x) the following
&lt;span class="gu"&gt;@@ -122,16 +122,11 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;     The scale factor A2-1 = mean value of (d/dsigma)2 - 1

&lt;span class="w"&gt; &lt;/span&gt;     The expansion above is performed in Maxima, a Computer Algebra System.
&lt;span class="gd"&gt;-     The C++ code (that yields the function evaluate_series_A1 below) is&lt;/span&gt;
&lt;span class="gi"&gt;+     The C++ code (that yields the function evaluate_series_A2 below) is&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;     generated by the following Maxima script and is based on script:
&lt;span class="w"&gt; &lt;/span&gt;     http://geographiclib.sourceforge.net/html/geod.mac

&lt;span class="w"&gt; &lt;/span&gt;        // Maxima script begin
&lt;span class="gd"&gt;-        taylordepth:5$&lt;/span&gt;
&lt;span class="gd"&gt;-        ataylor(expr,var,ord):=expand(ratdisrep(taylor(expr,var,0,ord)))$&lt;/span&gt;
&lt;span class="gd"&gt;-        jtaylor(expr,var1,var2,ord):=block([zz],expand(subst([zz=1],&lt;/span&gt;
&lt;span class="gd"&gt;-        ratdisrep(taylor(subst([var1=zz*var1,var2=zz*var2],expr),zz,0,ord)))))$&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        computeI2(maxpow):=block([sintegrand,sintegrandexp,s,sigma,tau1,k2,eps],
&lt;span class="w"&gt; &lt;/span&gt;          sintegrand:1/sqrt(1+k2*sin(sigma)^2),
&lt;span class="w"&gt; &lt;/span&gt;          sintegrandexp:ataylor(
&lt;span class="gu"&gt;@@ -216,16 +211,11 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;     The scale factor A3 = mean value of (d/dsigma)I3

&lt;span class="w"&gt; &lt;/span&gt;     The expansion above is performed in Maxima, a Computer Algebra System.
&lt;span class="gd"&gt;-     The C++ code (that yields the function evaluate_series_A1 below) is&lt;/span&gt;
&lt;span class="gi"&gt;+     The C++ code (that yields the function evaluate_coeffs_A3 below) is&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;     generated by the following Maxima script and is based on script:
&lt;span class="w"&gt; &lt;/span&gt;     http://geographiclib.sourceforge.net/html/geod.mac

&lt;span class="w"&gt; &lt;/span&gt;        // Maxima script begin
&lt;span class="gd"&gt;-        taylordepth:5$&lt;/span&gt;
&lt;span class="gd"&gt;-        ataylor(expr,var,ord):=expand(ratdisrep(taylor(expr,var,0,ord)))$&lt;/span&gt;
&lt;span class="gd"&gt;-        jtaylor(expr,var1,var2,ord):=block([zz],expand(subst([zz=1],&lt;/span&gt;
&lt;span class="gd"&gt;-        ratdisrep(taylor(subst([var1=zz*var1,var2=zz*var2],expr),zz,0,ord)))))$&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        computeI3(maxpow):=block([int,intexp,dlam,eta,del,eps,nu,f,z,n],
&lt;span class="w"&gt; &lt;/span&gt;          maxpow:maxpow-1,
&lt;span class="w"&gt; &lt;/span&gt;          int:subst([k2=4*eps/(1-eps)^2],
&lt;span class="gu"&gt;@@ -336,17 +326,12 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;     The coefficients C1[l] in the Fourier expansion of B1.

&lt;span class="w"&gt; &lt;/span&gt;     The expansion below is performed in Maxima, a Computer Algebra System.
&lt;span class="gd"&gt;-     The C++ code (that yields the function evaluate_series_A1 below) is&lt;/span&gt;
&lt;span class="gi"&gt;+     The C++ code (that yields the function evaluate_coeffs_C1 below) is&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;     generated by the following Maxima script and is based on script:
&lt;span class="w"&gt; &lt;/span&gt;     http://geographiclib.sourceforge.net/html/geod.mac

&lt;span class="w"&gt; &lt;/span&gt;        // Maxima script begin
&lt;span class="gd"&gt;-        taylordepth:5$&lt;/span&gt;
&lt;span class="gd"&gt;-        ataylor(expr,var,ord):=expand(ratdisrep(taylor(expr,var,0,ord)))$&lt;/span&gt;
&lt;span class="gd"&gt;-        jtaylor(expr,var1,var2,ord):=block([zz],expand(subst([zz=1],&lt;/span&gt;
&lt;span class="gd"&gt;-        ratdisrep(taylor(subst([var1=zz*var1,var2=zz*var2],expr),zz,0,ord)))))$&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        generatecode(maxpow):=block([tab2:&amp;quot;    &amp;quot;,tab3:&amp;quot;        &amp;quot;],&lt;/span&gt;
&lt;span class="gi"&gt;+        codeC1(maxpow):=block([tab2:&amp;quot;    &amp;quot;,tab3:&amp;quot;        &amp;quot;],&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;          print(&amp;quot;// The coefficients C1[l] in the Fourier expansion of B1
&lt;span class="w"&gt; &lt;/span&gt;        static inline evaluate_coeffs_C1(CT eps, CT c[]) {
&lt;span class="w"&gt; &lt;/span&gt;            CT eps2 = math::sqr(eps);
&lt;span class="gu"&gt;@@ -365,8 +350,8 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;#39;done)$

&lt;span class="w"&gt; &lt;/span&gt;        maxpow:8$
&lt;span class="gd"&gt;-        computeintegral(maxpow)$&lt;/span&gt;
&lt;span class="gd"&gt;-        generatecode(maxpow)$&lt;/span&gt;
&lt;span class="gi"&gt;+        computeI1(maxpow)$&lt;/span&gt;
&lt;span class="gi"&gt;+        codeC1(maxpow)$&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        // Maxima script end

&lt;span class="w"&gt; &lt;/span&gt;     To replace each number x by CT(x) the following
&lt;span class="gu"&gt;@@ -470,31 +455,11 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;     The coefficients C1p[l] in the Fourier expansion of B1p.

&lt;span class="w"&gt; &lt;/span&gt;     The expansion below is performed in Maxima, a Computer Algebra System.
&lt;span class="gd"&gt;-     The C++ code (that yields the function evaluate_series_A1 below) is&lt;/span&gt;
&lt;span class="gi"&gt;+     The C++ code (that yields the function evaluate_coeffs_C1p below) is&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;     generated by the following Maxima script and is based on script:
&lt;span class="w"&gt; &lt;/span&gt;     http://geographiclib.sourceforge.net/html/geod.mac

&lt;span class="w"&gt; &lt;/span&gt;        // Maxima script begin
&lt;span class="gd"&gt;-        taylordepth:5$&lt;/span&gt;
&lt;span class="gd"&gt;-        ataylor(expr,var,ord):=expand(ratdisrep(taylor(expr,var,0,ord)))$&lt;/span&gt;
&lt;span class="gd"&gt;-        jtaylor(expr,var1,var2,ord):=block([zz],expand(subst([zz=1],&lt;/span&gt;
&lt;span class="gd"&gt;-        ratdisrep(taylor(subst([var1=zz*var1,var2=zz*var2],expr),zz,0,ord)))))$&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        computeintegral(maxpow):=block([sintegrand,sintegrandexp,s,sigma,tau1,k2,eps],&lt;/span&gt;
&lt;span class="gd"&gt;-          sintegrand:sqrt(1+k2*sin(sigma)^2),&lt;/span&gt;
&lt;span class="gd"&gt;-          sintegrandexp:ataylor(&lt;/span&gt;
&lt;span class="gd"&gt;-              (1-eps)*subst([k2=4*eps/(1-eps)^2],sintegrand),&lt;/span&gt;
&lt;span class="gd"&gt;-              eps,maxpow),&lt;/span&gt;
&lt;span class="gd"&gt;-          s:trigreduce(integrate(sintegrandexp,sigma)),&lt;/span&gt;
&lt;span class="gd"&gt;-          s:s-subst(sigma=0,s),&lt;/span&gt;
&lt;span class="gd"&gt;-          A1:expand(subst(sigma=2*%pi,s)/(2*%pi)),&lt;/span&gt;
&lt;span class="gd"&gt;-          tau1:ataylor(s/A1,eps,maxpow),&lt;/span&gt;
&lt;span class="gd"&gt;-          for i:1 thru maxpow do C1[i]:coeff(tau1,sin(2*i*sigma)),&lt;/span&gt;
&lt;span class="gd"&gt;-          if expand(tau1-sigma-sum(C1[i]*sin(2*i*sigma),i,1,maxpow)) # 0&lt;/span&gt;
&lt;span class="gd"&gt;-          then error(&amp;quot;left over terms in B1&amp;quot;),&lt;/span&gt;
&lt;span class="gd"&gt;-          A1:A1/(1-eps),&lt;/span&gt;
&lt;span class="gd"&gt;-          &amp;#39;done)$&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        revertI1(maxpow):=block([tau,eps,tauacc:1,sigacc:0],
&lt;span class="w"&gt; &lt;/span&gt;          for n:1 thru maxpow do (
&lt;span class="w"&gt; &lt;/span&gt;            tauacc:trigreduce(ataylor(
&lt;span class="gu"&gt;@@ -506,7 +471,7 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;          then error(&amp;quot;left over terms in B1p&amp;quot;),
&lt;span class="w"&gt; &lt;/span&gt;          &amp;#39;done)$

&lt;span class="gd"&gt;-        generatecodeC1p(maxpow):=block([tab2:&amp;quot;    &amp;quot;,tab3:&amp;quot;        &amp;quot;],&lt;/span&gt;
&lt;span class="gi"&gt;+        codeC1p(maxpow):=block([tab2:&amp;quot;    &amp;quot;,tab3:&amp;quot;        &amp;quot;],&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;          print(&amp;quot;// The coefficients C1p[l] in the Fourier expansion of B1p
&lt;span class="w"&gt; &lt;/span&gt;        static inline evaluate_coeffs_C1p(CT eps, CT c[])
&lt;span class="w"&gt; &lt;/span&gt;        {
&lt;span class="gu"&gt;@@ -526,9 +491,9 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;#39;done)$

&lt;span class="w"&gt; &lt;/span&gt;        maxpow:8$
&lt;span class="gd"&gt;-        computeintegral(maxpow)$&lt;/span&gt;
&lt;span class="gi"&gt;+        computeI1(maxpow)$&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        revertI1(maxpow)$
&lt;span class="gd"&gt;-        generatecodeC1p(maxpow)$&lt;/span&gt;
&lt;span class="gi"&gt;+        codeC1p(maxpow)$&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        // Maxima script end

&lt;span class="w"&gt; &lt;/span&gt;     To replace each number x by CT(x) the following
&lt;span class="gu"&gt;@@ -632,31 +597,11 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;     The coefficients C2[l] in the Fourier expansion of B2.

&lt;span class="w"&gt; &lt;/span&gt;     The expansion below is performed in Maxima, a Computer Algebra System.
&lt;span class="gd"&gt;-     The C++ code (that yields the function evaluate_series_A1 below) is&lt;/span&gt;
&lt;span class="gi"&gt;+     The C++ code (that yields the function evaluate_coeffs_C2 below) is&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;     generated by the following Maxima script and is based on script:
&lt;span class="w"&gt; &lt;/span&gt;     http://geographiclib.sourceforge.net/html/geod.mac

&lt;span class="w"&gt; &lt;/span&gt;        // Maxima script begin
&lt;span class="gd"&gt;-        taylordepth:5$&lt;/span&gt;
&lt;span class="gd"&gt;-        ataylor(expr,var,ord):=expand(ratdisrep(taylor(expr,var,0,ord)))$&lt;/span&gt;
&lt;span class="gd"&gt;-        jtaylor(expr,var1,var2,ord):=block([zz],expand(subst([zz=1],&lt;/span&gt;
&lt;span class="gd"&gt;-        ratdisrep(taylor(subst([var1=zz*var1,var2=zz*var2],expr),zz,0,ord)))))$&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        computeI2(maxpow):=block([sintegrand,sintegrandexp,s,sigma,tau1,k2,eps],&lt;/span&gt;
&lt;span class="gd"&gt;-          sintegrand:1/sqrt(1+k2*sin(sigma)^2),&lt;/span&gt;
&lt;span class="gd"&gt;-          sintegrandexp:ataylor(&lt;/span&gt;
&lt;span class="gd"&gt;-              (1+eps)*subst([k2=4*eps/(1-eps)^2],sintegrand),&lt;/span&gt;
&lt;span class="gd"&gt;-              eps,maxpow),&lt;/span&gt;
&lt;span class="gd"&gt;-          s:trigreduce(integrate(sintegrandexp,sigma)),&lt;/span&gt;
&lt;span class="gd"&gt;-          s:s-subst(sigma=0,s),&lt;/span&gt;
&lt;span class="gd"&gt;-          A2:expand(subst(sigma=2*%pi,s)/(2*%pi)),&lt;/span&gt;
&lt;span class="gd"&gt;-          tau1:ataylor(s/A2,eps,maxpow),&lt;/span&gt;
&lt;span class="gd"&gt;-          for i:1 thru maxpow do C2[i]:coeff(tau1,sin(2*i*sigma)),&lt;/span&gt;
&lt;span class="gd"&gt;-          if expand(tau1-sigma-sum(C2[i]*sin(2*i*sigma),i,1,maxpow)) # 0&lt;/span&gt;
&lt;span class="gd"&gt;-          then error(&amp;quot;left over terms in B2&amp;quot;),&lt;/span&gt;
&lt;span class="gd"&gt;-          A2:A2/(1+eps),&lt;/span&gt;
&lt;span class="gd"&gt;-          &amp;#39;done)$&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        codeC2(maxpow):=block([tab2:&amp;quot;    &amp;quot;,tab3:&amp;quot;        &amp;quot;],
&lt;span class="w"&gt; &lt;/span&gt;        print(&amp;quot;// The coefficients C2[l] in the Fourier expansion of B2
&lt;span class="w"&gt; &lt;/span&gt;        static inline void evaluate_coeffs_C2(CT const&amp;amp; eps, CT c[])
&lt;span class="gu"&gt;@@ -782,32 +727,11 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;     The coefficients C3[l] in the Fourier expansion of B3.

&lt;span class="w"&gt; &lt;/span&gt;     The expansion below is performed in Maxima, a Computer Algebra System.
&lt;span class="gd"&gt;-     The C++ code (that yields the function evaluate_series_A1 below) is&lt;/span&gt;
&lt;span class="gi"&gt;+     The C++ code (that yields the function evaluate_coeffs_C3 below) is&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;     generated by the following Maxima script and is based on script:
&lt;span class="w"&gt; &lt;/span&gt;     http://geographiclib.sourceforge.net/html/geod.mac

&lt;span class="w"&gt; &lt;/span&gt;        // Maxima script begin
&lt;span class="gd"&gt;-        taylordepth:5$&lt;/span&gt;
&lt;span class="gd"&gt;-        ataylor(expr,var,ord):=expand(ratdisrep(taylor(expr,var,0,ord)))$&lt;/span&gt;
&lt;span class="gd"&gt;-        jtaylor(expr,var1,var2,ord):=block([zz],expand(subst([zz=1],&lt;/span&gt;
&lt;span class="gd"&gt;-        ratdisrep(taylor(subst([var1=zz*var1,var2=zz*var2],expr),zz,0,ord)))))$&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        computeI3(maxpow):=block([int,intexp,dlam,eta,del,eps,nu,f,z,n],&lt;/span&gt;
&lt;span class="gd"&gt;-          maxpow:maxpow-1,&lt;/span&gt;
&lt;span class="gd"&gt;-          int:subst([k2=4*eps/(1-eps)^2],&lt;/span&gt;
&lt;span class="gd"&gt;-            (2-f)/(1+(1-f)*sqrt(1+k2*sin(sigma)^2))),&lt;/span&gt;
&lt;span class="gd"&gt;-          int:subst([f=2*n/(1+n)],int),&lt;/span&gt;
&lt;span class="gd"&gt;-          intexp:jtaylor(int,n,eps,maxpow),&lt;/span&gt;
&lt;span class="gd"&gt;-          dlam:trigreduce(integrate(intexp,sigma)),&lt;/span&gt;
&lt;span class="gd"&gt;-          dlam:dlam-subst(sigma=0,dlam),&lt;/span&gt;
&lt;span class="gd"&gt;-          A3:expand(subst(sigma=2*%pi,dlam)/(2*%pi)),&lt;/span&gt;
&lt;span class="gd"&gt;-          eta:jtaylor(dlam/A3,n,eps,maxpow),&lt;/span&gt;
&lt;span class="gd"&gt;-          A3:jtaylor(A3,n,eps,maxpow),&lt;/span&gt;
&lt;span class="gd"&gt;-          for i:1 thru maxpow do C3[i]:coeff(eta,sin(2*i*sigma)),&lt;/span&gt;
&lt;span class="gd"&gt;-          if expand(eta-sigma-sum(C3[i]*sin(2*i*sigma),i,1,maxpow)) # 0&lt;/span&gt;
&lt;span class="gd"&gt;-          then error(&amp;quot;left over terms in B3&amp;quot;),&lt;/span&gt;
&lt;span class="gd"&gt;-          &amp;#39;done)$&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        codeC3(maxpow):=block([tab2:&amp;quot;    &amp;quot;,tab3:&amp;quot;        &amp;quot;],
&lt;span class="w"&gt; &lt;/span&gt;        print(&amp;quot;// The coefficients C3[l] in the Fourier expansion of B3
&lt;span class="w"&gt; &lt;/span&gt;        static inline void evaluate_coeffs_C3(CT const&amp;amp; n, CT c[])

commit 831873752dd7875e485a87a70e6ce2e75803a672
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Mon Jun 4 19:12:50 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [formulas] Use constant type variables for comparison

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/formulas/karney_direct.hpp b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index e24bd74..934122b 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -93,7 +93,9 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT const lat1 = la1;
&lt;span class="w"&gt; &lt;/span&gt;        Azi const azi12 = math::normalize_angle&amp;lt;CT&amp;gt;(azimuth12);

&lt;span class="gd"&gt;-        if (math::equals(distance, Dist(0)) || distance &amp;lt; Dist(0))&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const dist_c0 = 0;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        if (math::equals(distance, dist_c0) || distance &amp;lt; dist_c0)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        {
&lt;span class="w"&gt; &lt;/span&gt;            result.lon2 = lon1;
&lt;span class="w"&gt; &lt;/span&gt;            result.lat2 = lat1;
&lt;span class="gu"&gt;@@ -263,8 +265,8 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            CT const AB2 = (c1 + expansion_A2) * (B22 - B21);
&lt;span class="w"&gt; &lt;/span&gt;            CT const J12 = (expansion_A1 - expansion_A2) * sigma12 + (AB1 - AB2);

&lt;span class="gd"&gt;-            CT const dn1 = sqrt(1 + ep2 * math::sqr(sin_beta1));&lt;/span&gt;
&lt;span class="gd"&gt;-            CT const dn2 = sqrt(1 + k2 * math::sqr(sin_sigma2));&lt;/span&gt;
&lt;span class="gi"&gt;+            CT const dn1 = sqrt(c1 + ep2 * math::sqr(sin_beta1));&lt;/span&gt;
&lt;span class="gi"&gt;+            CT const dn2 = sqrt(c1 + k2 * math::sqr(sin_sigma2));&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            // Find the reduced length.
&lt;span class="w"&gt; &lt;/span&gt;            result.reduced_length = b * ((dn2 * (cos_sigma1 * sin_sigma2) -

commit 1ed5f103f48356f8c3b300905107117f3eca76f5
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Mon Jun 4 19:09:07 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [formulas] Move SeriesOrder to the end of template parameter list

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/formulas/karney_direct.hpp b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index c0faa05..e24bd74 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -33,11 +33,11 @@ https://arxiv.org/pdf/1109.4448.pdf&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;*/
&lt;span class="w"&gt; &lt;/span&gt;template &amp;lt;
&lt;span class="w"&gt; &lt;/span&gt;    typename CT,
&lt;span class="gd"&gt;-    size_t SeriesOrder = 8,&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    bool EnableCoordinates = true,
&lt;span class="w"&gt; &lt;/span&gt;    bool EnableReverseAzimuth = false,
&lt;span class="w"&gt; &lt;/span&gt;    bool EnableReducedLength = false,
&lt;span class="gd"&gt;-    bool EnableGeodesicScale = false&lt;/span&gt;
&lt;span class="gi"&gt;+    bool EnableGeodesicScale = false,&lt;/span&gt;
&lt;span class="gi"&gt;+    size_t SeriesOrder = 8&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;class karney_direct
&lt;span class="w"&gt; &lt;/span&gt;{
&lt;span class="gh"&gt;diff --git a/test/formulas/direct.cpp b/test/formulas/direct.cpp&lt;/span&gt;
&lt;span class="gh"&gt;index 0fb500b..bcfcec3 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/test/formulas/direct.cpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/test/formulas/direct.cpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -64,7 +64,7 @@ void test_all(expected_results const&amp;amp; results)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    result.reverse_azimuth *= r2d;
&lt;span class="w"&gt; &lt;/span&gt;    check_direct(result, results.thomas, results.karney, 0.0000001);

&lt;span class="gd"&gt;-    typedef bg::formula::karney_direct&amp;lt;double, 8, true, true, true, true&amp;gt; ka_t;&lt;/span&gt;
&lt;span class="gi"&gt;+    typedef bg::formula::karney_direct&amp;lt;double, true, true, true, true, 8&amp;gt; ka_t;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    result = ka_t::apply(lon1d, lat1d, distance, azi12d, spheroid);
&lt;span class="w"&gt; &lt;/span&gt;    check_direct(result, results.thomas, results.karney, 0.0000001);
&lt;span class="w"&gt; &lt;/span&gt;}
&lt;span class="gu"&gt;@@ -81,7 +81,7 @@ void test_karney_antipodal(expected_results_antipodal const&amp;amp; results)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    bg::formula::result_direct&amp;lt;double&amp;gt; result;

&lt;span class="gd"&gt;-    typedef bg::formula::karney_direct&amp;lt;double, 8, true, true, true, true&amp;gt; ka_t;&lt;/span&gt;
&lt;span class="gi"&gt;+    typedef bg::formula::karney_direct&amp;lt;double, true, true, true, true, 8&amp;gt; ka_t;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    result = ka_t::apply(lon1d, lat1d, distance, azi12d, spheroid);
&lt;span class="w"&gt; &lt;/span&gt;    check_direct(result, results.karney, results.karney, 0.0000001, true);
&lt;span class="w"&gt; &lt;/span&gt;}

commit 4fa4a8206a65ddd63c136989de026c0bb0d3e64f
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Thu May 31 17:40:53 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [util] Update series expansion for C3x

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/util/series_expansion.hpp b/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 59511c2..3566aca 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -842,9 +842,24 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;               s/case\sCT(/case /g; s/):/:/g; s/epsCT(2)/eps2/g;
&lt;span class="w"&gt; &lt;/span&gt;               s/eps(CT(2))/eps2/g;&amp;#39;
&lt;span class="w"&gt; &lt;/span&gt;    */
&lt;span class="gd"&gt;-    // TODO: adl1995&lt;/span&gt;
&lt;span class="gd"&gt;-    // Update docstring and function body.&lt;/span&gt;
&lt;span class="gd"&gt;-    // The coefficients C3[l] in the Fourier expansion of B3&lt;/span&gt;
&lt;span class="gi"&gt;+    template &amp;lt;typename CT, int SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    void evaluate_coeffs_C3x(CT const&amp;amp; n, CT c[], const CT coeff[])&lt;/span&gt;
&lt;span class="gi"&gt;+    {&lt;/span&gt;
&lt;span class="gi"&gt;+        int offset = 0, k = 0;&lt;/span&gt;
&lt;span class="gi"&gt;+        // l is index of C3[l].&lt;/span&gt;
&lt;span class="gi"&gt;+        for (int l = 1; l &amp;lt; SeriesOrder; ++l)&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            for (int j = SeriesOrder - 1; j &amp;gt;= l; --j)&lt;/span&gt;
&lt;span class="gi"&gt;+            {&lt;/span&gt;
&lt;span class="gi"&gt;+                // Order of polynomial in n.&lt;/span&gt;
&lt;span class="gi"&gt;+                int m = std::min(SeriesOrder - j - 1, j);&lt;/span&gt;
&lt;span class="gi"&gt;+                c[k++] = math::polyval(m, coeff + offset, n) /&lt;/span&gt;
&lt;span class="gi"&gt;+                coeff[offset + m + 1];&lt;/span&gt;
&lt;span class="gi"&gt;+                offset += m + 2;&lt;/span&gt;
&lt;span class="gi"&gt;+            }&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+    }&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    template &amp;lt;typename CT, int SeriesOrder&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;    void evaluate_coeffs_C3x(CT const&amp;amp; n, CT c[]) {
&lt;span class="w"&gt; &lt;/span&gt;        if (SeriesOrder == 3) {
&lt;span class="gu"&gt;@@ -856,6 +871,8 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            // C3[2], coeff of eps^2, polynomial in n of order 0
&lt;span class="w"&gt; &lt;/span&gt;            1, 16,
&lt;span class="w"&gt; &lt;/span&gt;            };
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            evaluate_coeffs_C3x&amp;lt;CT, SeriesOrder&amp;gt;(n, c, coeff);&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="w"&gt; &lt;/span&gt;        else if (SeriesOrder == 4) {
&lt;span class="w"&gt; &lt;/span&gt;            static const CT coeff[] = {
&lt;span class="gu"&gt;@@ -874,6 +891,8 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            // C3[3], coeff of eps^3, polynomial in n of order 0
&lt;span class="w"&gt; &lt;/span&gt;            5, 192,
&lt;span class="w"&gt; &lt;/span&gt;            };
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            evaluate_coeffs_C3x&amp;lt;CT, SeriesOrder&amp;gt;(n, c, coeff);&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="w"&gt; &lt;/span&gt;        else if (SeriesOrder == 5) {
&lt;span class="w"&gt; &lt;/span&gt;            static const CT coeff[] = {
&lt;span class="gu"&gt;@@ -898,6 +917,8 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            // C3[4], coeff of eps^4, polynomial in n of order 0
&lt;span class="w"&gt; &lt;/span&gt;            7, 512,
&lt;span class="w"&gt; &lt;/span&gt;            };
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            evaluate_coeffs_C3x&amp;lt;CT, SeriesOrder&amp;gt;(n, c, coeff);&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="w"&gt; &lt;/span&gt;        else if (SeriesOrder == 6) {
&lt;span class="w"&gt; &lt;/span&gt;            static const CT coeff[] = {
&lt;span class="gu"&gt;@@ -932,6 +953,8 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            // C3[5], coeff of eps^5, polynomial in n of order 0
&lt;span class="w"&gt; &lt;/span&gt;            21, 2560,
&lt;span class="w"&gt; &lt;/span&gt;            };
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            evaluate_coeffs_C3x&amp;lt;CT, SeriesOrder&amp;gt;(n, c, coeff);&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="w"&gt; &lt;/span&gt;        else if (SeriesOrder == 7) {
&lt;span class="w"&gt; &lt;/span&gt;            static const CT coeff[] = {
&lt;span class="gu"&gt;@@ -978,6 +1001,8 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            // C3[6], coeff of eps^6, polynomial in n of order 0
&lt;span class="w"&gt; &lt;/span&gt;            11, 2048,
&lt;span class="w"&gt; &lt;/span&gt;            };
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            evaluate_coeffs_C3x&amp;lt;CT, SeriesOrder&amp;gt;(n, c, coeff);&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="w"&gt; &lt;/span&gt;        else if (SeriesOrder == 8) {
&lt;span class="w"&gt; &lt;/span&gt;            static const CT coeff[] = {
&lt;span class="gu"&gt;@@ -1039,17 +1064,7 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            429, 114688,
&lt;span class="w"&gt; &lt;/span&gt;            };

&lt;span class="gd"&gt;-            int offset = 0, k = 0;&lt;/span&gt;
&lt;span class="gd"&gt;-            // l is index of C3[l].&lt;/span&gt;
&lt;span class="gd"&gt;-            for (int l = 1; l &amp;lt; SeriesOrder; ++l) {&lt;/span&gt;
&lt;span class="gd"&gt;-                for (int j = SeriesOrder - 1; j &amp;gt;= l; --j) {&lt;/span&gt;
&lt;span class="gd"&gt;-                    // Order of polynomial in n.&lt;/span&gt;
&lt;span class="gd"&gt;-                    int m = std::min(SeriesOrder - j - 1, j);&lt;/span&gt;
&lt;span class="gd"&gt;-                    c[k++] = math::polyval(m, coeff + offset, n) /&lt;/span&gt;
&lt;span class="gd"&gt;-                             coeff[offset + m + 1];&lt;/span&gt;
&lt;span class="gd"&gt;-                    offset += m + 2;&lt;/span&gt;
&lt;span class="gd"&gt;-                }&lt;/span&gt;
&lt;span class="gd"&gt;-            }&lt;/span&gt;
&lt;span class="gi"&gt;+            evaluate_coeffs_C3x&amp;lt;CT, SeriesOrder&amp;gt;(n, c, coeff);&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="w"&gt; &lt;/span&gt;        // Post condition: offset == sizeof(coeff) / sizeof(CT) &amp;amp;&amp;amp; k == coeffs_C3_size
&lt;span class="w"&gt; &lt;/span&gt;    }

commit 6ff97a69910529d31f880d0674a4015723156864
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Thu May 31 16:36:16 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [test] Test Karney&amp;#39;s method on antipodal points dataset

&lt;span class="gh"&gt;diff --git a/test/formulas/direct.cpp b/test/formulas/direct.cpp&lt;/span&gt;
&lt;span class="gh"&gt;index 7cf076b..0fb500b 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/test/formulas/direct.cpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/test/formulas/direct.cpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -12,6 +12,7 @@&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;#include &amp;quot;test_formula.hpp&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;#include &amp;quot;direct_cases.hpp&amp;quot;
&lt;span class="gi"&gt;+#include &amp;quot;direct_cases_antipodal.hpp&amp;quot;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;#include &amp;lt;boost/geometry/formulas/vincenty_direct.hpp&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;#include &amp;lt;boost/geometry/formulas/thomas_direct.hpp&amp;gt;
&lt;span class="gu"&gt;@@ -20,13 +21,14 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;#include &amp;lt;boost/geometry/srs/spheroid.hpp&amp;gt;

&lt;span class="w"&gt; &lt;/span&gt;template &amp;lt;typename Result&amp;gt;
&lt;span class="gd"&gt;-void check_direct(Result const&amp;amp; result, expected_result const&amp;amp; expected, expected_result const&amp;amp; reference, double reference_error)&lt;/span&gt;
&lt;span class="gi"&gt;+void check_direct(Result const&amp;amp; result, expected_result const&amp;amp; expected, expected_result const&amp;amp; reference,&lt;/span&gt;
&lt;span class="gi"&gt;+                  double reference_error, bool check_reference_only = false)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;{
&lt;span class="gd"&gt;-    check_one(result.lon2, expected.lon2, reference.lon2, reference_error);&lt;/span&gt;
&lt;span class="gd"&gt;-    check_one(result.lat2, expected.lat2, reference.lat2, reference_error);&lt;/span&gt;
&lt;span class="gd"&gt;-    check_one(result.reverse_azimuth, expected.reverse_azimuth, reference.reverse_azimuth, reference_error, true);&lt;/span&gt;
&lt;span class="gd"&gt;-    check_one(result.reduced_length, expected.reduced_length, reference.reduced_length, reference_error);&lt;/span&gt;
&lt;span class="gd"&gt;-    check_one(result.geodesic_scale, expected.geodesic_scale, reference.geodesic_scale, reference_error);&lt;/span&gt;
&lt;span class="gi"&gt;+    check_one(result.lon2, expected.lon2, reference.lon2, reference_error, false, check_reference_only);&lt;/span&gt;
&lt;span class="gi"&gt;+    check_one(result.lat2, expected.lat2, reference.lat2, reference_error, false, check_reference_only);&lt;/span&gt;
&lt;span class="gi"&gt;+    check_one(result.reverse_azimuth, expected.reverse_azimuth, reference.reverse_azimuth, reference_error, true, check_reference_only);&lt;/span&gt;
&lt;span class="gi"&gt;+    check_one(result.reduced_length, expected.reduced_length, reference.reduced_length, reference_error, false, check_reference_only);&lt;/span&gt;
&lt;span class="gi"&gt;+    check_one(result.geodesic_scale, expected.geodesic_scale, reference.geodesic_scale, reference_error, false, check_reference_only);&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;}

&lt;span class="w"&gt; &lt;/span&gt;void test_all(expected_results const&amp;amp; results)
&lt;span class="gu"&gt;@@ -67,6 +69,23 @@ void test_all(expected_results const&amp;amp; results)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    check_direct(result, results.thomas, results.karney, 0.0000001);
&lt;span class="w"&gt; &lt;/span&gt;}

&lt;span class="gi"&gt;+void test_karney_antipodal(expected_results_antipodal const&amp;amp; results)&lt;/span&gt;
&lt;span class="gi"&gt;+{&lt;/span&gt;
&lt;span class="gi"&gt;+    double lon1d = results.p1.lon;&lt;/span&gt;
&lt;span class="gi"&gt;+    double lat1d = results.p1.lat;&lt;/span&gt;
&lt;span class="gi"&gt;+    double distance = results.distance;&lt;/span&gt;
&lt;span class="gi"&gt;+    double azi12d = results.azimuth12;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    // WGS84&lt;/span&gt;
&lt;span class="gi"&gt;+    bg::srs::spheroid&amp;lt;double&amp;gt; spheroid(6378137.0, 6356752.3142451793);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    bg::formula::result_direct&amp;lt;double&amp;gt; result;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    typedef bg::formula::karney_direct&amp;lt;double, 8, true, true, true, true&amp;gt; ka_t;&lt;/span&gt;
&lt;span class="gi"&gt;+    result = ka_t::apply(lon1d, lat1d, distance, azi12d, spheroid);&lt;/span&gt;
&lt;span class="gi"&gt;+    check_direct(result, results.karney, results.karney, 0.0000001, true);&lt;/span&gt;
&lt;span class="gi"&gt;+}&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;int test_main(int, char*[])
&lt;span class="w"&gt; &lt;/span&gt;{
&lt;span class="w"&gt; &lt;/span&gt;    for (size_t i = 0; i &amp;lt; expected_size; ++i)
&lt;span class="gu"&gt;@@ -74,5 +93,10 @@ int test_main(int, char*[])&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        test_all(expected[i]);
&lt;span class="w"&gt; &lt;/span&gt;    }

&lt;span class="gi"&gt;+    for (size_t i = 0; i &amp;lt; expected_size_antipodal; ++i)&lt;/span&gt;
&lt;span class="gi"&gt;+    {&lt;/span&gt;
&lt;span class="gi"&gt;+        test_karney_antipodal(expected_antipodal[i]);&lt;/span&gt;
&lt;span class="gi"&gt;+    }&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    return 0;
&lt;span class="w"&gt; &lt;/span&gt;}

commit 3c21b13c9161f0d7bd3169c44a23ebdd87846bbb
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Thu May 31 16:28:09 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [test] Add geodesic length to antipodal points dataset

&lt;span class="w"&gt; &lt;/span&gt;   The geodesic length is calculated manually using GeographicLib/Geodesic.hpp
&lt;span class="w"&gt; &lt;/span&gt;   in C++. However, this value differs when calculated using the
&lt;span class="w"&gt; &lt;/span&gt;   CLI tool GeodSolve.

&lt;span class="gh"&gt;diff --git a/test/formulas/direct_cases_antipodal.hpp b/test/formulas/direct_cases_antipodal.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 8e24847..541d3bf 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/test/formulas/direct_cases_antipodal.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/test/formulas/direct_cases_antipodal.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -10,8 +10,7 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;#ifndef BOOST_GEOMETRY_TEST_DIRECT_CASES_ANTIPODAL_HPP
&lt;span class="w"&gt; &lt;/span&gt;#define BOOST_GEOMETRY_TEST_DIRECT_CASES_ANTIPODAL_HPP

&lt;span class="gd"&gt;-// TODO: adl1995&lt;/span&gt;
&lt;span class="gd"&gt;-// Add geodesic scale to expected_antipodal[] array.&lt;/span&gt;
&lt;span class="gi"&gt;+#include &amp;quot;direct_cases.hpp&amp;quot;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;struct expected_results_antipodal
&lt;span class="w"&gt; &lt;/span&gt;{
&lt;span class="gu"&gt;@@ -25,304 +24,304 @@ expected_results_antipodal expected_antipodal[] =&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;{
&lt;span class="w"&gt; &lt;/span&gt;    {
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 31.394417440639 }, 19980218.4055399, 34.266322930672,
&lt;span class="gd"&gt;-        { 179.615601631202912322, -31.275540610835465807, 145.782701113414306756, 49490.8807994496209 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.615601631202912322, -31.275540610835465807, 145.782701113414306756, 49490.8807994496209, -0.99598340069666346785 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 29.788792273749 }, 19887224.5407334, 74.302205994192,
&lt;span class="gd"&gt;-        { 178.569451327813675741, -29.558013672069422725, 106.156240654579267308, 97043.7545600593058 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.569451327813675741, -29.558013672069422725, 106.156240654579267308, 97043.7545600593058, -0.99585265538534928353 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 46.471843094141 }, 19944337.8863917, 63.693680310665,
&lt;span class="gd"&gt;-        { 179.083144618009561276, -46.284166405924629853, 116.699978859005570535, 53139.140576552365 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.083144618009561276, -46.284166405924629853, 116.699978859005570535, 53139.140576552365, -0.99628290151178156009 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 63.016506345929 }, 20000925.7533636, 153.393656073038,
&lt;span class="gd"&gt;-        { 179.862869954071637855, -63.02943882703369735, 26.619056019474552953, 12713.9284725111772 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.862869954071637855, -63.02943882703369735, 26.619056019474552953, 12713.9284725111772, -0.99806730524837738994 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 19.796231412719 }, 19956338.1330537, 28.272934411318,
&lt;span class="gd"&gt;-        { 179.546498474461283862, -19.470586923091672503, 151.789094611690988249, 87191.1749625132931 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.546498474461283862, -19.470586923091672503, 151.789094611690988249, 87191.1749625132931, -0.99680355285706290225 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 6.373459459035 }, 19946581.6983394, 56.859050230583,
&lt;span class="gd"&gt;-        { 179.240009269347556917, -6.204887833274217382, 123.169200847008284851, 53958.8698005263939 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.240009269347556917, -6.204887833274217382, 123.169200847008284851, 53958.8698005263939, -0.99880439595523196061 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 66.380766469414 }, 19986277.7696849, 38.646950203356,
&lt;span class="gd"&gt;-        { 179.632633596894388233, -66.27177494016956425, 141.550919825824399405, 22198.215635049214 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.632633596894388233, -66.27177494016956425, 141.550919825824399405, 22198.215635049214, -0.9984201835509521894 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 16.483421185231 }, 19962737.9842573, 163.431254767325,
&lt;span class="gd"&gt;-        { 179.731567273052604726, -16.818424446748042212, 16.598399455529231288, 95318.4104529881431 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.731567273052604726, -16.818424446748042212, 16.598399455529231288, 95318.4104529881431, -0.99723427960580335316 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 4.215702155486 }, 19932517.393764, 65.543168480886,
&lt;span class="gd"&gt;-        { 179.093771177769992874, -4.051917290690976764, 114.482669479963380006, 55205.4553703842317 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.093771177769992874, -4.051917290690976764, 114.482669479963380006, 55205.4553703842317, -0.99916694551569362748 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 40.71372085907 }, 19951133.3595356, 143.672151631634,
&lt;span class="gd"&gt;-        { 179.404612926861498984, -41.047052242159400671, 36.54002600969304553, 70931.1530155553621 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.404612926861498984, -41.047052242159400671, 36.54002600969304553, 70931.1530155553621, -0.99601226064330683485 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 15.465481491654 }, 19877383.8879911, 36.289185640976,
&lt;span class="gd"&gt;-        { 179.020726605204181801, -14.622355549425900341, 143.875673907461159912, 156419.0806764376957 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.020726605204181801, -14.622355549425900341, 143.875673907461159912, 156419.0806764376957, -0.99717590257108590368 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 17.586197343531 }, 19982280.4639115, 157.929615091529,
&lt;span class="gd"&gt;-        { 179.722490735835379144, -17.731394230364437075, 22.089021105298661023, 69727.5357849255557 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.722490735835379144, -17.731394230364437075, 22.089021105298661023, 69727.5357849255557, -0.99710409371925123878 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 5.7442768247 }, 19902873.7431814, 116.146983678305,
&lt;span class="gd"&gt;-        { 178.85894724576868462, -6.039853564481335581, 63.91482549951374061, 87149.6188944111673 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.85894724576868462, -6.039853564481335581, 63.91482549951374061, 87149.6188944111673, -0.99883071172416715289 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 32.002904282111 }, 19967670.3104795, 163.052160078191,
&lt;span class="gd"&gt;-        { 179.744925422107715439, -32.297934520693132807, 17.004175883388454943, 78311.3164829640582 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.744925422107715439, -32.297934520693132807, 17.004175883388454943, 78311.3164829640582, -0.99597193334487110761 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 55.902716926362 }, 19970525.337607, 98.927641063414,
&lt;span class="gd"&gt;-        { 179.300685189522463007, -55.934320218634018206, 81.374264168520557301, 23554.0093185709067 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.300685189522463007, -55.934320218634018206, 81.374264168520557301, 23554.0093185709067, -0.99721760041260698593 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 22.69939784398 }, 19959286.1903172, 74.253870776761,
&lt;span class="gd"&gt;-        { 179.294173474584020749, -22.654875407651067149, 105.811588890213155275, 22369.7179951557679 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.294173474584020749, -22.654875407651067149, 105.811588890213155275, 22369.7179951557679, -0.99650952667426662135 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 41.312328471121 }, 19962690.5721867, 11.277616109847,
&lt;span class="gd"&gt;-        { 179.817186837717804928, -40.954523601529804886, 168.784288786443902199, 77252.6121237260201 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.817186837717804928, -40.954523601529804886, 168.784288786443902199, 77252.6121237260201, -0.99601334953687414853 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 27.927415327453 }, 19961296.8828333, 23.166421459647,
&lt;span class="gd"&gt;-        { 179.636508875679110143, -27.607314264234172721, 156.905194492817275222, 83096.5801709291101 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.636508875679110143, -27.607314264234172721, 156.905194492817275222, 83096.5801709291101, -0.99610609795569049485 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 41.567228741451 }, 19944253.4454809, 176.66609526064,
&lt;span class="gd"&gt;-        { 179.931812964300204608, -42.103039532074194347, 3.361859685835349219, 96859.08180779197 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.931812964300204608, -42.103039532074194347, 3.361859685835349219, 96859.08180779197, -0.99604624873858405021 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 37.384208978567 }, 19928705.5911445, 39.072534864532,
&lt;span class="gd"&gt;-        { 179.225180174670992261, -36.916085670712060029, 141.212743814390850106, 92667.7834060578402 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.225180174670992261, -36.916085670712060029, 141.212743814390850106, 92667.7834060578402, -0.99583616315460821156 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 59.011868682852 }, 19970442.3788306, 44.970301291063,
&lt;span class="gd"&gt;-        { 179.424923485514312807, -58.82705468054708336, 135.333817989802309531, 38071.1136293083857 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.424923485514312807, -58.82705468054708336, 135.333817989802309531, 38071.1136293083857, -0.99754686309848117354 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 35.515406087737 }, 19948918.9139751, 28.528972431952,
&lt;span class="gd"&gt;-        { 179.50369572149476218, -35.119747127350258822, 151.622257906284404073, 84564.0387217601751 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.50369572149476218, -35.119747127350258822, 151.622257906284404073, 84564.0387217601751, -0.9958683164291525225 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 58.170252463184 }, 19961407.0813807, 128.021116291844,
&lt;span class="gd"&gt;-        { 179.254737571455023977, -58.372261836268550805, 52.399129705193347143, 43715.3070711393309 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.254737571455023977, -58.372261836268550805, 52.399129705193347143, 43715.3070711393309, -0.99746161015888423762 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 34.012183807959 }, 19970955.843065, 168.944519134772,
&lt;span class="gd"&gt;-        { 179.83713352180447672, -34.29640782899529639, 11.093048811826875835, 76493.5814538538151 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.83713352180447672, -34.29640782899529639, 11.093048811826875835, 76493.5814538538151, -0.99594085068133375582 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 45.510762948553 }, 19940248.3450143, 99.886784003837,
&lt;span class="gd"&gt;-        { 178.981682578823726535, -45.582753595227824235, 80.542330522982505877, 48555.1946627894972 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.981682578823726535, -45.582753595227824235, 80.542330522982505877, 48555.1946627894972, -0.99622530350349169925 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 4.19841765451 }, 19970496.5132933, 89.561550657928,
&lt;span class="gd"&gt;-        { 179.398024428225540172, -4.198416896099783242, 90.438456568689151881, 14.8790480103109 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.398024428225540172, -4.198416896099783242, 90.438456568689151881, 14.8790480103109, -0.99921964515553229891 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 40.890119148103 }, 19926563.5817492, 165.437641169967,
&lt;span class="gd"&gt;-        { 179.6557148951668192, -41.553556264538302258, 14.713597527941311478, 111805.7305227545923 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.6557148951668192, -41.553556264538302258, 14.713597527941311478, 111805.7305227545923, -0.99598758014963484353 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 28.096672787686 }, 19883901.8482359, 115.174366374632,
&lt;span class="gd"&gt;-        { 178.606868012231657724, -28.472055035513955205, 65.257367020445564176, 107880.4353518862363 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.606868012231657724, -28.472055035513955205, 65.257367020445564176, 107880.4353518862363, -0.99595011741363181912 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 6.50572154271 }, 19917276.4101551, 79.069492719523,
&lt;span class="gd"&gt;-        { 178.926013840891647541, -6.411745140559297675, 100.985091481519557845, 57073.3242952680707 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.926013840891647541, -6.411745140559297675, 100.985091481519557845, 57073.3242952680707, -0.99872883639262399758 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, .468835109567 }, 19849380.7342734, 80.234636214474,
&lt;span class="gd"&gt;-        { 178.325942223692180692, -.281751687044281805, 99.77243368342786593, 123845.4568822078908 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.325942223692180692, -.281751687044281805, 99.77243368342786593, 123845.4568822078908, -0.99961835625163353303 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 1.682746325049 }, 19890026.0274781, 10.076182752451,
&lt;span class="gd"&gt;-        { 179.717131561406935483, -.677647430701204515, 169.927471515299313238, 177917.2104306563981 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.717131561406935483, -.677647430701204515, 169.927471515299313238, 177917.2104306563981, -0.9995304688134802884 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 10.711305126218 }, 19962987.2134077, 7.528253696796,
&lt;span class="gd"&gt;-        { 179.874050163405229937, -10.349315378531556046, 172.480576051850009046, 104175.1095378254456 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.874050163405229937, -10.349315378531556046, 172.480576051850009046, 104175.1095378254456, -0.99808700197799249398 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 53.374321544652 }, 19980478.1457438, 23.324715976877,
&lt;span class="gd"&gt;-        { 179.729445806011012057, -53.196257519024042184, 156.777734080146664812, 41907.8869272231053 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.729445806011012057, -53.196257519024042184, 156.777734080146664812, 41907.8869272231053, -0.99695322723379165009 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 39.680221664519 }, 19956191.7841809, 7.075406493429,
&lt;span class="gd"&gt;-        { 179.87506206720154785, -39.256187213040660911, 172.967624741991546131, 86943.8110669895148 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.87506206720154785, -39.256187213040660911, 172.967624741991546131, 86943.8110669895148, -0.99594142266924268192 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 1.377666714083 }, 19925401.4931301, 95.29199069739,
&lt;span class="gd"&gt;-        { 178.994542525209058878, -1.415358715570225495, 84.7178724483824156, 45800.9140624827059 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.994542525209058878, -1.415358715570225495, 84.7178724483824156, 45800.9140624827059, -0.99967079575227224542 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 48.751426624188 }, 19988599.1160495, 40.252328570137,
&lt;span class="gd"&gt;-        { 179.661697715070846977, -48.688146707479475147, 139.808452951157199824, 26322.3790862461568 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.661697715070846977, -48.688146707479475147, 139.808452951157199824, 26322.3790862461568, -0.99654000795747821329 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 59.443039048494 }, 19969935.9534732, 93.052184108221,
&lt;span class="gd"&gt;-        { 179.247605418616998285, -59.454371825393424121, 87.331416513795326158, 25342.4691896499534 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.247605418616998285, -59.454371825393424121, 87.331416513795326158, 25342.4691896499534, -0.99760844716107632824 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 4.122408476235 }, 19938291.6332293, 167.73479753304,
&lt;span class="gd"&gt;-        { 179.749430572914989772, -4.689124208743755363, 12.274635577599782826, 127855.6475863583497 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.749430572914989772, -4.689124208743755363, 12.274635577599782826, 127855.6475863583497, -0.99919442400871316678 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 46.422470082432 }, 19931980.7029341, 86.67365350297,
&lt;span class="gd"&gt;-        { 178.857408435141563774, -46.390934261324541952, 93.852683224054943377, 56114.680046867064 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.857408435141563774, -46.390934261324541952, 93.852683224054943377, 56114.680046867064, -0.99626178341627869006 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 32.614423729024 }, 19926887.3785175, 24.943814520557,
&lt;span class="gd"&gt;-        { 179.460593512880455451, -32.01874745886238612, 155.229917137448282531, 112355.3319340873104 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.460593512880455451, -32.01874745886238612, 155.229917137448282531, 112355.3319340873104, -0.99584842355298219818 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 3.242895277973 }, 19964490.4789049, 30.247458779683,
&lt;span class="gd"&gt;-        { 179.556428318080663113, -3.001106476068264917, 149.760178923092147784, 80929.0418317066044 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.556428318080663113, -3.001106476068264917, 149.760178923092147784, 80929.0418317066044, -0.99938705290848561802 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 6.29069210113 }, 19877160.8505733, 94.34299459284,
&lt;span class="gd"&gt;-        { 178.556859259685624933, -6.354208910915346725, 85.750059038253282986, 94127.1566760840083 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.556859259685624933, -6.354208910915346725, 85.750059038253282986, 94127.1566760840083, -0.99866089792332934927 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 18.232086569498 }, 19927978.7462175, 164.41905055334,
&lt;span class="gd"&gt;-        { 179.658073278238477245, -18.87394850776853555, 15.640779355822506503, 129771.1882449660559 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.658073278238477245, -18.87394850776853555, 15.640779355822506503, 129771.1882449660559, -0.99696204469368099321 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 12.049849333181 }, 19908004.4552909, 9.418096768309,
&lt;span class="gd"&gt;-        { 179.761046682699610657, -11.201990279782499264, 170.610608272305604585, 157761.5040571466343 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.761046682699610657, -11.201990279782499264, 170.610608272305604585, 157761.5040571466343, -0.99777424243303902696 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 40.289465276136 }, 19985674.936106, 143.092606818963,
&lt;span class="gd"&gt;-        { 179.644208494155329095, -40.370034926441385999, 36.958610382613096419, 36200.8933724688593 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.644208494155329095, -40.370034926441385999, 36.958610382613096419, 36200.8933724688593, -0.99602880093986934096 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 2.197784650379 }, 19910509.7517973, 1.542117609437,
&lt;span class="gd"&gt;-        { 179.961199531084784854, -1.353440827124394777, 178.458582198505846426, 160403.6285079348996 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.961199531084784854, -1.353440827124394777, 178.458582198505846426, 160403.6285079348996, -0.99948867836200405712 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 1.966575272177 }, 19875595.6267266, 170.112968791865,
&lt;span class="gd"&gt;-        { 179.699817324905962184, -3.101125282483752618, 9.89572776349855838, 192355.7206665719908 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.699817324905962184, -3.101125282483752618, 9.89572776349855838, 192355.7206665719908, -0.99943592820130777721 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 25.078832492684 }, 19887997.7953866, 77.264585323781,
&lt;span class="gd"&gt;-        { 178.600804840925824646, -24.897833702325682511, 103.101167809583406892, 92442.9124509225839 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.600804840925824646, -24.897833702325682511, 103.101167809583406892, 92442.9124509225839, -0.99614702274067257193 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 31.740361941314 }, 19972325.3556069, 143.930820896999,
&lt;span class="gd"&gt;-        { 179.553485210731879874, -31.909206787477701871, 36.145242998351638503, 54883.4113710054145 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.553485210731879874, -31.909206787477701871, 36.145242998351638503, 54883.4113710054145, -0.99597837783719567195 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, .05479250563 }, 19858049.4780499, 41.349430623518,
&lt;span class="gd"&gt;-        { 178.822647462220726609, .836079031223269324, 138.645259065012502544, 169078.442370111714 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.822647462220726609, .836079031223269324, 138.645259065012502544, 169078.442370111714, -0.9997266451533399767 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 36.685139871608 }, 19968965.6773632, 89.167975517493,
&lt;span class="gd"&gt;-        { 179.366667224014334712, -36.6833040833258687, 90.921025521408327068, 13327.2156799476918 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.366667224014334712, -36.6833040833258687, 90.921025521408327068, 13327.2156799476918, -0.99592417628692353482 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 3.451199399671 }, 19938203.3838544, 91.541212417048,
&lt;span class="gd"&gt;-        { 179.107509334399258305, -3.459003521120242021, 88.476282464773035164, 32316.1747698810781 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.107509334399258305, -3.459003521120242021, 88.476282464773035164, 32316.1747698810781, -0.9993151254968675179 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 27.692898794247 }, 19883493.6699045, 88.406440883665,
&lt;span class="gd"&gt;-        { 178.512356615673144314, -27.666009301228316555, 92.036345087713397961, 94128.7880896190836 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.512356615673144314, -27.666009301228316555, 92.036345087713397961, 94128.7880896190836, -0.99595722110800843918 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 17.363238291869 }, 19980749.7638027, 39.697196316589,
&lt;span class="gd"&gt;-        { 179.567921315455829491, -17.288872648596950413, 140.321938237586060826, 46975.9359427664379 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.567921315455829491, -17.288872648596950413, 140.321938237586060826, 46975.9359427664379, -0.9971281847750985694 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 37.006775102539 }, 19949309.9180043, 116.455543532607,
&lt;span class="gd"&gt;-        { 179.191103068859169842, -37.156365616364686838, 63.771817992036617793, 45856.1961421018701 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.191103068859169842, -37.156365616364686838, 63.771817992036617793, 45856.1961421018701, -0.99590619058035212419 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 45.572883540957 }, 19940027.8586414, 137.627256708444,
&lt;span class="gd"&gt;-        { 179.224707765088686272, -45.94675931323086696, 42.723991162977357301, 74208.4359612889496 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.224707765088686272, -45.94675931323086696, 42.723991162977357301, 74208.4359612889496, -0.99624902751220101305 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 43.63393981955 }, 19931045.2914508, 91.203625101465,
&lt;span class="gd"&gt;-        { 178.878236417027994157, -43.642335115130514773, 89.268780774643462256, 55253.5406349861764 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.878236417027994157, -43.642335115130514773, 89.268780774643462256, 55253.5406349861764, -0.99608620009401716011 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 38.4995307019 }, 19918391.2222193, 141.232864609445,
&lt;span class="gd"&gt;-        { 179.143856004445269342, -39.042223438550921467, 39.117947060740562295, 102217.2563106863077 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.143856004445269342, -39.042223438550921467, 39.117947060740562295, 102217.2563106863077, -0.99588724635854519729 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 27.55015339382 }, 19986004.7358853, 137.025135713548,
&lt;span class="gd"&gt;-        { 179.596220103573824099, -27.587412128122249651, 42.992898351962011956, 33938.7346646670654 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.596220103573824099, -27.587412128122249651, 42.992898351962011956, 33938.7346646670654, -0.99616434874063342075 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 1.54507498314 }, 19978593.3191777, 36.816106412092,
&lt;span class="gd"&gt;-        { 179.567115633151308577, -1.448861185025252004, 143.185763012309022403, 56320.5800276739168 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.567115633151308577, -1.448861185025252004, 143.185763012309022403, 56320.5800276739168, -0.99970846248568390191 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 45.217063644222 }, 19987042.0782465, 18.114645812265,
&lt;span class="gd"&gt;-        { 179.807382581661125, -45.086424050571516283, 161.928120141429818658, 45544.2915061261936 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.807382581661125, -45.086424050571516283, 161.928120141429818658, 45544.2915061261936, -0.99626823185730628563 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 13.473522450751 }, 19987364.078382, 156.839609002403,
&lt;span class="gd"&gt;-        { 179.726941062277208626, -13.570372758027936877, 23.170293747820406391, 65329.9068132034472 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.726941062277208626, -13.570372758027936877, 23.170293747820406391, 65329.9068132034472, -0.99767717345868900392 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 6.287741997374 }, 19912159.8245954, 132.954797451112,
&lt;span class="gd"&gt;-        { 179.071252372259552052, -6.743450924917895817, 47.100789519677419746, 104772.4027498097375 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.071252372259552052, -6.743450924917895817, 47.100789519677419746, 104772.4027498097375, -0.99875728461227553101 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 7.639709001531 }, 19976374.3699535, 29.731916588299,
&lt;span class="gd"&gt;-        { 179.616156296978583335, -7.48702643786017917, 150.279582966919438164, 69224.6591757209539 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.616156296978583335, -7.48702643786017917, 150.279582966919438164, 69224.6591757209539, -0.99861517221927087462 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 5.893688050348 }, 19886907.2520668, 14.653438882877,
&lt;span class="gd"&gt;-        { 179.586212000450856399, -4.888408917114795625, 165.371181401863458848, 177183.5330818593022 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.586212000450856399, -4.888408917114795625, 165.371181401863458848, 177183.5330818593022, -0.99875831501903877818 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 61.997076235476 }, 19976288.2901729, 149.562797049254,
&lt;span class="gd"&gt;-        { 179.605779116829636081, -62.19593758437129915, 30.65850204223272625, 36696.2853801462176 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.605779116829636081, -62.19593758437129915, 30.65850204223272625, 36696.2853801462176, -0.99792483695855294101 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 50.507637741656 }, 19979542.5263293, 171.564028344478,
&lt;span class="gd"&gt;-        { 179.893569206021038536, -50.721890799900161112, 8.4746613464253591, 50644.5234828162697 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.893569206021038536, -50.721890799900161112, 8.4746613464253591, 50644.5234828162697, -0.99670226818003293534 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 7.484475238477 }, 19867425.2906303, 57.020570370985,
&lt;span class="gd"&gt;-        { 178.638400003000590878, -6.926155588124333461, 123.087267812322270238, 132929.2775641349633 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.638400003000590878, -6.926155588124333461, 123.087267812322270238, 132929.2775641349633, -0.99841820367274103365 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 56.851165323215 }, 19988235.9960515, 112.345749045605,
&lt;span class="gd"&gt;-        { 179.587046628550073045, -56.875248360744638525, 67.744017057185404441, 9971.0934553515518 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.587046628550073045, -56.875248360744638525, 67.744017057185404441, 9971.0934553515518, -0.99734849887992094164 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 10.692273150738 }, 19893210.3050033, 102.824601316946,
&lt;span class="gd"&gt;-        { 178.709520715733071393, -10.851727623036704339, 77.308514969817191459, 83032.7122948051111 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.709520715733071393, -10.851727623036704339, 77.308514969817191459, 83032.7122948051111, -0.99796077650539405379 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 46.694739303788 }, 19975447.9283188, 174.663684259477,
&lt;span class="gd"&gt;-        { 179.926838145841924189, -46.948618153686522669, 5.361568174833475454, 59614.5876209460645 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.926838145841924189, -46.948618153686522669, 5.361568174833475454, 59614.5876209460645, -0.9963829846069084395 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 15.804386137005 }, 19855850.8800526, 74.932089158884,
&lt;span class="gd"&gt;-        { 178.367587635209819128, -15.522042847777054984, 105.357235560913450667, 123350.4326645237628 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.367587635209819128, -15.522042847777054984, 105.357235560913450667, 123350.4326645237628, -0.99706137589256171871 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 4.371450175299 }, 19979071.1035552, 164.163592252794,
&lt;span class="gd"&gt;-        { 179.780887420199549421, -4.566109732313098407, 15.840695025950408814, 84137.2115482558728 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.780887420199549421, -4.566109732313098407, 15.840695025950408814, 84137.2115482558728, -0.99919490909391039946 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 30.894388279688 }, 19968681.8321577, 77.35154610481,
&lt;span class="gd"&gt;-        { 179.375426183521944524, -30.871308884744172663, 102.709506078439532936, 14048.0277985734058 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.375426183521944524, -30.871308884744172663, 102.709506078439532936, 14048.0277985734058, -0.99599179229723178164 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 9.541166838639 }, 19848553.7844137, 118.441353539081,
&lt;span class="gd"&gt;-        { 178.432934555386452839, -10.09982228112793472, 61.736686215549403663, 144831.1911566651614 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.432934555386452839, -10.09982228112793472, 61.736686215549403663, 144831.1911566651614, -0.99800476808793336936 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 8.489292700054 }, 19995477.1669578, 171.963952699866,
&lt;span class="gd"&gt;-        { 179.906698338023119097, -8.559237750032113623, 8.037517851139094467, 72192.60793572974 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.906698338023119097, -8.559237750032113623, 8.037517851139094467, 72192.60793572974, -0.9984792781676200546 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 19.562401114224 }, 19893208.1788508, 126.362762598128,
&lt;span class="gd"&gt;-        { 178.838724116996037606, -20.05038360490599475, 53.875560227496658204, 112181.7524188837615 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.838724116996037606, -20.05038360490599475, 53.875560227496658204, 112181.7524188837615, -0.99671734436245396083 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 42.260350252749 }, 19942715.0054774, 170.703419847646,
&lt;span class="gd"&gt;-        { 179.807860448877064601, -42.79985897702184353, 9.377654670896439828, 96336.3477142010769 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.807860448877064601, -42.79985897702184353, 9.377654670896439828, 96336.3477142010769, -0.99607495753304098329 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 24.511403144656 }, 19924809.5184876, 102.913211410163,
&lt;span class="gd"&gt;-        { 178.957598444862223515, -24.616808725039883945, 77.297538210434837096, 55403.453072179318 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.957598444862223515, -24.616808725039883945, 77.297538210434837096, 55403.453072179318, -0.99629589741710011808 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 20.844284170708 }, 19909084.6340808, 44.172784008084,
&lt;span class="gd"&gt;-        { 179.069258863637226633, -20.321320573298341477, 136.01627115731728436, 111009.0987238994608 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.069258863637226633, -20.321320573298341477, 136.01627115731728436, 111009.0987238994608, -0.99659406562612795621 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 2.426010809098 }, 19840940.6924189, 94.315194952561,
&lt;span class="gd"&gt;-        { 178.236397468862000784, -2.513715200833756776, 85.734896842737189557, 130002.6104886615638 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.236397468862000784, -2.513715200833756776, 85.734896842737189557, 130002.6104886615638, -0.99922656404178245015 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 6.600682554664 }, 19878412.28273, 168.167678684515,
&lt;span class="gd"&gt;-        { 179.646475458013797028, -7.699164822656561787, 11.861035812918738552, 187426.3958525886692 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.646475458013797028, -7.699164822656561787, 11.861035812918738552, 187426.3958525886692, -0.99861342289130949901 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 23.372339802326 }, 19899498.4582543, 161.197647943542,
&lt;span class="gd"&gt;-        { 179.499422665106094027, -24.239465200482591299, 18.932355367478826536, 151863.2545535951091 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.499422665106094027, -24.239465200482591299, 18.932355367478826536, 151863.2545535951091, -0.99635421423038206257 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 16.194668264095 }, 19874825.6683239, 148.942349959054,
&lt;span class="gd"&gt;-        { 179.115193814080201851, -17.129419031459576897, 31.225656401221968078, 166033.3161394594622 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.115193814080201851, -17.129419031459576897, 31.225656401221968078, 166033.3161394594622, -0.99709176416695455281 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 1.528726471528 }, 19897803.9939987, 69.212891442493,
&lt;span class="gd"&gt;-        { 178.791047180477802091, -1.282203000582034597, 110.802928803578167132, 85252.8333849204133 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.791047180477802091, -1.282203000582034597, 110.802928803578167132, 85252.8333849204133, -0.99957999688525089876 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 6.297249676078 }, 19864042.0495193, 56.274639904925,
&lt;span class="gd"&gt;-        { 178.623258703845895437, -5.709470001196540278, 123.817184177744186806, 137475.1283083659258 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.623258703845895437, -5.709470001196540278, 123.817184177744186806, 137475.1283083659258, -0.99861474729636867664 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 17.393540327984 }, 19962624.6302607, 107.855062015266,
&lt;span class="gd"&gt;-        { 179.330156510680163326, -17.431100690958209424, 72.181322855288535245, 19320.5501845044839 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.330156510680163326, -17.431100690958209424, 72.181322855288535245, 19320.5501845044839, -0.99711019484200580365 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 46.284685151236 }, 19990422.3478916, 14.758013867151,
&lt;span class="gd"&gt;-        { 179.852534804091121255, -46.176234945675219984, 165.271681964991897184, 42614.1796365710104 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.852534804091121255, -46.176234945675219984, 165.271681964991897184, 42614.1796365710104, -0.99634649632519134421 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 14.924320176299 }, 19891861.8615337, 31.446544793174,
&lt;span class="gd"&gt;-        { 179.195663739713760883, -14.125476432252858442, 148.678916887199611191, 149419.6596309045804 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.195663739713760883, -14.125476432252858442, 148.678916887199611191, 149419.6596309045804, -0.99729741460688270394 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 23.668824656069 }, 19938736.4442268, 148.091483667618,
&lt;span class="gd"&gt;-        { 179.409875478773990359, -24.107855233601412399, 32.02919257641173958, 97771.7687385830819 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.409875478773990359, -24.107855233601412399, 32.02919257641173958, 97771.7687385830819, -0.99640366072092678706 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 46.986276695896 }, 19968596.0414782, 174.796708941456,
&lt;span class="gd"&gt;-        { 179.92040916864362177, -47.301644191214905832, 5.234240076649939638, 66113.7417494369769 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.92040916864362177, -47.301644191214905832, 5.234240076649939638, 66113.7417494369769, -0.99639889458222818952 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 65.946144289524 }, 19993734.5109736, 25.375428509648,
&lt;span class="gd"&gt;-        { 179.808282612725835525, -65.871840130833632868, 154.703163938350061652, 18355.2254271672769 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.808282612725835525, -65.871840130833632868, 154.703163938350061652, 18355.2254271672769, -0.99838571720931879039 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 10.950298933293 }, 19975919.5586889, 28.779018914489,
&lt;span class="gd"&gt;-        { 179.624609619829763098, -10.787771536605316781, 151.238005588662201946, 70291.1998404303581 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.624609619829763098, -10.787771536605316781, 151.238005588662201946, 70291.1998404303581, -0.99806099887646559932 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 13.609869340778 }, 19913213.8514358, 129.616021271129,
&lt;span class="gd"&gt;-        { 179.035623147420893383, -14.023624108675206222, 50.506400999466711623, 97596.7664002074776 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.035623147420893383, -14.023624108675206222, 50.506400999466711623, 97596.7664002074776, -0.99755852834357494618 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 48.701427557433 }, 19972955.2699173, 102.875149183407,
&lt;span class="gd"&gt;-        { 179.385565054218238481, -48.735316652259656533, 77.294384444682547869, 18461.7742226227697 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.385565054218238481, -48.735316652259656533, 77.294384444682547869, 18461.7742226227697, -0.99652223116852467477 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 31.519172055785 }, 19952318.3772514, 26.247105619999,
&lt;span class="gd"&gt;-        { 179.555251675378549409, -31.140142027808697534, 153.865822276646938125, 86354.7117605101002 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.555251675378549409, -31.140142027808697534, 153.865822276646938125, 86354.7117605101002, -0.99593468381798511135 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 31.863784754278 }, 19993324.8682601, 29.572313410211,
&lt;span class="gd"&gt;-        { 179.722489476483407524, -31.826935359797657785, 150.440607907359037187, 41427.6181613499234 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.722489476483407524, -31.826935359797657785, 150.440607907359037187, 41427.6181613499234, -0.9959831508497293262 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 76.434608546092 }, 19997750.023578, 167.428385412814,
&lt;span class="gd"&gt;-        { 179.918287057674124459, -76.48787937532808951, 12.621032110142724567, 9619.5267710862108 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.918287057674124459, -76.48787937532808951, 12.621032110142724567, 9619.5267710862108, -0.99943031992965880583 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 73.114273316483 }, 19992866.6147806, 78.154765899661,
&lt;span class="gd"&gt;-        { 179.576736605988553624, -73.098788070892914568, 102.085693546950923465, 8580.6475692800946 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.576736605988553624, -73.098788070892914568, 102.085693546950923465, 8580.6475692800946, -0.99913315932148838439 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    },{
&lt;span class="w"&gt; &lt;/span&gt;        { 0, 1.125639056292 }, 19852573.5442848, 67.184842289382,
&lt;span class="gd"&gt;-        { 178.426819580880619395, -.694775021853292564, 112.831314850896246589, 132932.8743502563937 }&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.426819580880619395, -.694775021853292564, 112.831314850896246589, 132932.8743502563937, -0.99950982898040086067 }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    }
&lt;span class="w"&gt; &lt;/span&gt;};


commit fdbb3886d3ddfac617f6df0efe83d31e360f1d77
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Wed May 30 12:14:33 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [test] Add nearly antipodal points dataset for direct geodesic problem

&lt;span class="w"&gt; &lt;/span&gt;   Dataset is collected from:
&lt;span class="w"&gt; &lt;/span&gt;   https://zenodo.org/record/32156

&lt;span class="w"&gt; &lt;/span&gt;   It is then parsed using a Python script.

&lt;span class="gh"&gt;diff --git a/test/formulas/direct_cases_antipodal.hpp b/test/formulas/direct_cases_antipodal.hpp&lt;/span&gt;
new file mode 100644
&lt;span class="gh"&gt;index 0000000..8e24847&lt;/span&gt;
&lt;span class="gd"&gt;--- /dev/null&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/test/formulas/direct_cases_antipodal.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -0,0 +1,331 @@&lt;/span&gt;
&lt;span class="gi"&gt;+// Boost.Geometry&lt;/span&gt;
&lt;span class="gi"&gt;+// Unit Test&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+// Contributed and/or modified by Adeel Ahmad.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+// Use, modification and distribution is subject to the Boost Software License,&lt;/span&gt;
&lt;span class="gi"&gt;+// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at&lt;/span&gt;
&lt;span class="gi"&gt;+// http://www.boost.org/LICENSE_1_0.txt)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+#ifndef BOOST_GEOMETRY_TEST_DIRECT_CASES_ANTIPODAL_HPP&lt;/span&gt;
&lt;span class="gi"&gt;+#define BOOST_GEOMETRY_TEST_DIRECT_CASES_ANTIPODAL_HPP&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+// TODO: adl1995&lt;/span&gt;
&lt;span class="gi"&gt;+// Add geodesic scale to expected_antipodal[] array.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+struct expected_results_antipodal&lt;/span&gt;
&lt;span class="gi"&gt;+{&lt;/span&gt;
&lt;span class="gi"&gt;+    coordinates p1;&lt;/span&gt;
&lt;span class="gi"&gt;+    double distance;&lt;/span&gt;
&lt;span class="gi"&gt;+    double azimuth12;&lt;/span&gt;
&lt;span class="gi"&gt;+    expected_result karney;&lt;/span&gt;
&lt;span class="gi"&gt;+};&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+expected_results_antipodal expected_antipodal[] =&lt;/span&gt;
&lt;span class="gi"&gt;+{&lt;/span&gt;
&lt;span class="gi"&gt;+    {&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 31.394417440639 }, 19980218.4055399, 34.266322930672,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.615601631202912322, -31.275540610835465807, 145.782701113414306756, 49490.8807994496209 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 29.788792273749 }, 19887224.5407334, 74.302205994192,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.569451327813675741, -29.558013672069422725, 106.156240654579267308, 97043.7545600593058 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 46.471843094141 }, 19944337.8863917, 63.693680310665,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.083144618009561276, -46.284166405924629853, 116.699978859005570535, 53139.140576552365 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 63.016506345929 }, 20000925.7533636, 153.393656073038,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.862869954071637855, -63.02943882703369735, 26.619056019474552953, 12713.9284725111772 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 19.796231412719 }, 19956338.1330537, 28.272934411318,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.546498474461283862, -19.470586923091672503, 151.789094611690988249, 87191.1749625132931 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 6.373459459035 }, 19946581.6983394, 56.859050230583,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.240009269347556917, -6.204887833274217382, 123.169200847008284851, 53958.8698005263939 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 66.380766469414 }, 19986277.7696849, 38.646950203356,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.632633596894388233, -66.27177494016956425, 141.550919825824399405, 22198.215635049214 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 16.483421185231 }, 19962737.9842573, 163.431254767325,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.731567273052604726, -16.818424446748042212, 16.598399455529231288, 95318.4104529881431 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 4.215702155486 }, 19932517.393764, 65.543168480886,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.093771177769992874, -4.051917290690976764, 114.482669479963380006, 55205.4553703842317 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 40.71372085907 }, 19951133.3595356, 143.672151631634,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.404612926861498984, -41.047052242159400671, 36.54002600969304553, 70931.1530155553621 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 15.465481491654 }, 19877383.8879911, 36.289185640976,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.020726605204181801, -14.622355549425900341, 143.875673907461159912, 156419.0806764376957 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 17.586197343531 }, 19982280.4639115, 157.929615091529,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.722490735835379144, -17.731394230364437075, 22.089021105298661023, 69727.5357849255557 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 5.7442768247 }, 19902873.7431814, 116.146983678305,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.85894724576868462, -6.039853564481335581, 63.91482549951374061, 87149.6188944111673 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 32.002904282111 }, 19967670.3104795, 163.052160078191,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.744925422107715439, -32.297934520693132807, 17.004175883388454943, 78311.3164829640582 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 55.902716926362 }, 19970525.337607, 98.927641063414,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.300685189522463007, -55.934320218634018206, 81.374264168520557301, 23554.0093185709067 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 22.69939784398 }, 19959286.1903172, 74.253870776761,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.294173474584020749, -22.654875407651067149, 105.811588890213155275, 22369.7179951557679 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 41.312328471121 }, 19962690.5721867, 11.277616109847,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.817186837717804928, -40.954523601529804886, 168.784288786443902199, 77252.6121237260201 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 27.927415327453 }, 19961296.8828333, 23.166421459647,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.636508875679110143, -27.607314264234172721, 156.905194492817275222, 83096.5801709291101 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 41.567228741451 }, 19944253.4454809, 176.66609526064,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.931812964300204608, -42.103039532074194347, 3.361859685835349219, 96859.08180779197 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 37.384208978567 }, 19928705.5911445, 39.072534864532,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.225180174670992261, -36.916085670712060029, 141.212743814390850106, 92667.7834060578402 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 59.011868682852 }, 19970442.3788306, 44.970301291063,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.424923485514312807, -58.82705468054708336, 135.333817989802309531, 38071.1136293083857 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 35.515406087737 }, 19948918.9139751, 28.528972431952,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.50369572149476218, -35.119747127350258822, 151.622257906284404073, 84564.0387217601751 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 58.170252463184 }, 19961407.0813807, 128.021116291844,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.254737571455023977, -58.372261836268550805, 52.399129705193347143, 43715.3070711393309 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 34.012183807959 }, 19970955.843065, 168.944519134772,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.83713352180447672, -34.29640782899529639, 11.093048811826875835, 76493.5814538538151 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 45.510762948553 }, 19940248.3450143, 99.886784003837,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.981682578823726535, -45.582753595227824235, 80.542330522982505877, 48555.1946627894972 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 4.19841765451 }, 19970496.5132933, 89.561550657928,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.398024428225540172, -4.198416896099783242, 90.438456568689151881, 14.8790480103109 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 40.890119148103 }, 19926563.5817492, 165.437641169967,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.6557148951668192, -41.553556264538302258, 14.713597527941311478, 111805.7305227545923 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 28.096672787686 }, 19883901.8482359, 115.174366374632,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.606868012231657724, -28.472055035513955205, 65.257367020445564176, 107880.4353518862363 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 6.50572154271 }, 19917276.4101551, 79.069492719523,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.926013840891647541, -6.411745140559297675, 100.985091481519557845, 57073.3242952680707 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, .468835109567 }, 19849380.7342734, 80.234636214474,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.325942223692180692, -.281751687044281805, 99.77243368342786593, 123845.4568822078908 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 1.682746325049 }, 19890026.0274781, 10.076182752451,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.717131561406935483, -.677647430701204515, 169.927471515299313238, 177917.2104306563981 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 10.711305126218 }, 19962987.2134077, 7.528253696796,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.874050163405229937, -10.349315378531556046, 172.480576051850009046, 104175.1095378254456 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 53.374321544652 }, 19980478.1457438, 23.324715976877,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.729445806011012057, -53.196257519024042184, 156.777734080146664812, 41907.8869272231053 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 39.680221664519 }, 19956191.7841809, 7.075406493429,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.87506206720154785, -39.256187213040660911, 172.967624741991546131, 86943.8110669895148 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 1.377666714083 }, 19925401.4931301, 95.29199069739,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.994542525209058878, -1.415358715570225495, 84.7178724483824156, 45800.9140624827059 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 48.751426624188 }, 19988599.1160495, 40.252328570137,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.661697715070846977, -48.688146707479475147, 139.808452951157199824, 26322.3790862461568 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 59.443039048494 }, 19969935.9534732, 93.052184108221,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.247605418616998285, -59.454371825393424121, 87.331416513795326158, 25342.4691896499534 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 4.122408476235 }, 19938291.6332293, 167.73479753304,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.749430572914989772, -4.689124208743755363, 12.274635577599782826, 127855.6475863583497 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 46.422470082432 }, 19931980.7029341, 86.67365350297,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.857408435141563774, -46.390934261324541952, 93.852683224054943377, 56114.680046867064 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 32.614423729024 }, 19926887.3785175, 24.943814520557,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.460593512880455451, -32.01874745886238612, 155.229917137448282531, 112355.3319340873104 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 3.242895277973 }, 19964490.4789049, 30.247458779683,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.556428318080663113, -3.001106476068264917, 149.760178923092147784, 80929.0418317066044 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 6.29069210113 }, 19877160.8505733, 94.34299459284,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.556859259685624933, -6.354208910915346725, 85.750059038253282986, 94127.1566760840083 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 18.232086569498 }, 19927978.7462175, 164.41905055334,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.658073278238477245, -18.87394850776853555, 15.640779355822506503, 129771.1882449660559 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 12.049849333181 }, 19908004.4552909, 9.418096768309,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.761046682699610657, -11.201990279782499264, 170.610608272305604585, 157761.5040571466343 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 40.289465276136 }, 19985674.936106, 143.092606818963,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.644208494155329095, -40.370034926441385999, 36.958610382613096419, 36200.8933724688593 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 2.197784650379 }, 19910509.7517973, 1.542117609437,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.961199531084784854, -1.353440827124394777, 178.458582198505846426, 160403.6285079348996 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 1.966575272177 }, 19875595.6267266, 170.112968791865,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.699817324905962184, -3.101125282483752618, 9.89572776349855838, 192355.7206665719908 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 25.078832492684 }, 19887997.7953866, 77.264585323781,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.600804840925824646, -24.897833702325682511, 103.101167809583406892, 92442.9124509225839 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 31.740361941314 }, 19972325.3556069, 143.930820896999,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.553485210731879874, -31.909206787477701871, 36.145242998351638503, 54883.4113710054145 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, .05479250563 }, 19858049.4780499, 41.349430623518,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.822647462220726609, .836079031223269324, 138.645259065012502544, 169078.442370111714 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 36.685139871608 }, 19968965.6773632, 89.167975517493,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.366667224014334712, -36.6833040833258687, 90.921025521408327068, 13327.2156799476918 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 3.451199399671 }, 19938203.3838544, 91.541212417048,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.107509334399258305, -3.459003521120242021, 88.476282464773035164, 32316.1747698810781 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 27.692898794247 }, 19883493.6699045, 88.406440883665,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.512356615673144314, -27.666009301228316555, 92.036345087713397961, 94128.7880896190836 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 17.363238291869 }, 19980749.7638027, 39.697196316589,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.567921315455829491, -17.288872648596950413, 140.321938237586060826, 46975.9359427664379 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 37.006775102539 }, 19949309.9180043, 116.455543532607,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.191103068859169842, -37.156365616364686838, 63.771817992036617793, 45856.1961421018701 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 45.572883540957 }, 19940027.8586414, 137.627256708444,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.224707765088686272, -45.94675931323086696, 42.723991162977357301, 74208.4359612889496 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 43.63393981955 }, 19931045.2914508, 91.203625101465,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.878236417027994157, -43.642335115130514773, 89.268780774643462256, 55253.5406349861764 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 38.4995307019 }, 19918391.2222193, 141.232864609445,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.143856004445269342, -39.042223438550921467, 39.117947060740562295, 102217.2563106863077 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 27.55015339382 }, 19986004.7358853, 137.025135713548,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.596220103573824099, -27.587412128122249651, 42.992898351962011956, 33938.7346646670654 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 1.54507498314 }, 19978593.3191777, 36.816106412092,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.567115633151308577, -1.448861185025252004, 143.185763012309022403, 56320.5800276739168 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 45.217063644222 }, 19987042.0782465, 18.114645812265,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.807382581661125, -45.086424050571516283, 161.928120141429818658, 45544.2915061261936 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 13.473522450751 }, 19987364.078382, 156.839609002403,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.726941062277208626, -13.570372758027936877, 23.170293747820406391, 65329.9068132034472 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 6.287741997374 }, 19912159.8245954, 132.954797451112,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.071252372259552052, -6.743450924917895817, 47.100789519677419746, 104772.4027498097375 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 7.639709001531 }, 19976374.3699535, 29.731916588299,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.616156296978583335, -7.48702643786017917, 150.279582966919438164, 69224.6591757209539 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 5.893688050348 }, 19886907.2520668, 14.653438882877,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.586212000450856399, -4.888408917114795625, 165.371181401863458848, 177183.5330818593022 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 61.997076235476 }, 19976288.2901729, 149.562797049254,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.605779116829636081, -62.19593758437129915, 30.65850204223272625, 36696.2853801462176 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 50.507637741656 }, 19979542.5263293, 171.564028344478,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.893569206021038536, -50.721890799900161112, 8.4746613464253591, 50644.5234828162697 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 7.484475238477 }, 19867425.2906303, 57.020570370985,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.638400003000590878, -6.926155588124333461, 123.087267812322270238, 132929.2775641349633 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 56.851165323215 }, 19988235.9960515, 112.345749045605,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.587046628550073045, -56.875248360744638525, 67.744017057185404441, 9971.0934553515518 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 10.692273150738 }, 19893210.3050033, 102.824601316946,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.709520715733071393, -10.851727623036704339, 77.308514969817191459, 83032.7122948051111 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 46.694739303788 }, 19975447.9283188, 174.663684259477,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.926838145841924189, -46.948618153686522669, 5.361568174833475454, 59614.5876209460645 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 15.804386137005 }, 19855850.8800526, 74.932089158884,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.367587635209819128, -15.522042847777054984, 105.357235560913450667, 123350.4326645237628 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 4.371450175299 }, 19979071.1035552, 164.163592252794,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.780887420199549421, -4.566109732313098407, 15.840695025950408814, 84137.2115482558728 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 30.894388279688 }, 19968681.8321577, 77.35154610481,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.375426183521944524, -30.871308884744172663, 102.709506078439532936, 14048.0277985734058 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 9.541166838639 }, 19848553.7844137, 118.441353539081,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.432934555386452839, -10.09982228112793472, 61.736686215549403663, 144831.1911566651614 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 8.489292700054 }, 19995477.1669578, 171.963952699866,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.906698338023119097, -8.559237750032113623, 8.037517851139094467, 72192.60793572974 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 19.562401114224 }, 19893208.1788508, 126.362762598128,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.838724116996037606, -20.05038360490599475, 53.875560227496658204, 112181.7524188837615 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 42.260350252749 }, 19942715.0054774, 170.703419847646,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.807860448877064601, -42.79985897702184353, 9.377654670896439828, 96336.3477142010769 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 24.511403144656 }, 19924809.5184876, 102.913211410163,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.957598444862223515, -24.616808725039883945, 77.297538210434837096, 55403.453072179318 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 20.844284170708 }, 19909084.6340808, 44.172784008084,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.069258863637226633, -20.321320573298341477, 136.01627115731728436, 111009.0987238994608 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 2.426010809098 }, 19840940.6924189, 94.315194952561,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.236397468862000784, -2.513715200833756776, 85.734896842737189557, 130002.6104886615638 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 6.600682554664 }, 19878412.28273, 168.167678684515,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.646475458013797028, -7.699164822656561787, 11.861035812918738552, 187426.3958525886692 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 23.372339802326 }, 19899498.4582543, 161.197647943542,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.499422665106094027, -24.239465200482591299, 18.932355367478826536, 151863.2545535951091 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 16.194668264095 }, 19874825.6683239, 148.942349959054,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.115193814080201851, -17.129419031459576897, 31.225656401221968078, 166033.3161394594622 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 1.528726471528 }, 19897803.9939987, 69.212891442493,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.791047180477802091, -1.282203000582034597, 110.802928803578167132, 85252.8333849204133 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 6.297249676078 }, 19864042.0495193, 56.274639904925,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.623258703845895437, -5.709470001196540278, 123.817184177744186806, 137475.1283083659258 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 17.393540327984 }, 19962624.6302607, 107.855062015266,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.330156510680163326, -17.431100690958209424, 72.181322855288535245, 19320.5501845044839 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 46.284685151236 }, 19990422.3478916, 14.758013867151,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.852534804091121255, -46.176234945675219984, 165.271681964991897184, 42614.1796365710104 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 14.924320176299 }, 19891861.8615337, 31.446544793174,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.195663739713760883, -14.125476432252858442, 148.678916887199611191, 149419.6596309045804 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 23.668824656069 }, 19938736.4442268, 148.091483667618,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.409875478773990359, -24.107855233601412399, 32.02919257641173958, 97771.7687385830819 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 46.986276695896 }, 19968596.0414782, 174.796708941456,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.92040916864362177, -47.301644191214905832, 5.234240076649939638, 66113.7417494369769 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 65.946144289524 }, 19993734.5109736, 25.375428509648,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.808282612725835525, -65.871840130833632868, 154.703163938350061652, 18355.2254271672769 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 10.950298933293 }, 19975919.5586889, 28.779018914489,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.624609619829763098, -10.787771536605316781, 151.238005588662201946, 70291.1998404303581 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 13.609869340778 }, 19913213.8514358, 129.616021271129,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.035623147420893383, -14.023624108675206222, 50.506400999466711623, 97596.7664002074776 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 48.701427557433 }, 19972955.2699173, 102.875149183407,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.385565054218238481, -48.735316652259656533, 77.294384444682547869, 18461.7742226227697 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 31.519172055785 }, 19952318.3772514, 26.247105619999,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.555251675378549409, -31.140142027808697534, 153.865822276646938125, 86354.7117605101002 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 31.863784754278 }, 19993324.8682601, 29.572313410211,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.722489476483407524, -31.826935359797657785, 150.440607907359037187, 41427.6181613499234 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 76.434608546092 }, 19997750.023578, 167.428385412814,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.918287057674124459, -76.48787937532808951, 12.621032110142724567, 9619.5267710862108 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 73.114273316483 }, 19992866.6147806, 78.154765899661,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 179.576736605988553624, -73.098788070892914568, 102.085693546950923465, 8580.6475692800946 }&lt;/span&gt;
&lt;span class="gi"&gt;+    },{&lt;/span&gt;
&lt;span class="gi"&gt;+        { 0, 1.125639056292 }, 19852573.5442848, 67.184842289382,&lt;/span&gt;
&lt;span class="gi"&gt;+        { 178.426819580880619395, -.694775021853292564, 112.831314850896246589, 132932.8743502563937 }&lt;/span&gt;
&lt;span class="gi"&gt;+    }&lt;/span&gt;
&lt;span class="gi"&gt;+};&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+size_t const expected_size_antipodal = sizeof(expected_antipodal) / sizeof(expected_results_antipodal);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+#endif // BOOST_GEOMETRY_TEST_DIRECT_CASES_ANTIPODAL_HPP&lt;/span&gt;

commit b8a225e1cfa057b15aded81a9822219fee3323db
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Tue May 29 21:11:53 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [formulas] Fix direct geodesic method by performing normalization

&lt;span class="w"&gt; &lt;/span&gt;   - Add minus sign for B12 evaluation

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/formulas/karney_direct.hpp b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 8e81cf4..c0faa05 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -103,7 +103,6 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT const c1 = 1;
&lt;span class="w"&gt; &lt;/span&gt;        CT const c2 = 2;

&lt;span class="gd"&gt;-        CT const a = CT(get_radius&amp;lt;0&amp;gt;(spheroid));&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT const b = CT(get_radius&amp;lt;2&amp;gt;(spheroid));
&lt;span class="w"&gt; &lt;/span&gt;        CT const f = formula::flattening&amp;lt;CT&amp;gt;(spheroid);
&lt;span class="w"&gt; &lt;/span&gt;        CT const one_minus_f = c1 - f;
&lt;span class="gu"&gt;@@ -121,6 +120,9 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        math::sin_cos_degrees&amp;lt;CT&amp;gt;(math::round_angle&amp;lt;CT&amp;gt;(lat1), sin_beta1, cos_beta1);
&lt;span class="w"&gt; &lt;/span&gt;        sin_beta1 *= one_minus_f;

&lt;span class="gi"&gt;+        math::normalize&amp;lt;CT&amp;gt;(sin_beta1, cos_beta1);&lt;/span&gt;
&lt;span class="gi"&gt;+        cos_beta1 = std::max(sqrt(std::numeric_limits&amp;lt;CT&amp;gt;::min()), cos_beta1);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        // Obtain alpha 0 by solving the spherical triangle.
&lt;span class="w"&gt; &lt;/span&gt;        CT const sin_alpha0
&lt;span class="w"&gt; &lt;/span&gt;            = sin_alpha1 * cos_beta1;
&lt;span class="gu"&gt;@@ -146,8 +148,12 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT const sin_tau12 = sin(tau12);
&lt;span class="w"&gt; &lt;/span&gt;        CT const cos_tau12 = cos(tau12);

&lt;span class="gd"&gt;-        CT const sin_sigma1 = sin_beta1;&lt;/span&gt;
&lt;span class="gd"&gt;-        CT const cos_sigma1 = cos_beta1 * cos_alpha1;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT sin_sigma1 = sin_beta1;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT sin_omega1 = sin_alpha0 * sin_beta1;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        CT cos_sigma1, cos_omega1;&lt;/span&gt;
&lt;span class="gi"&gt;+        cos_sigma1 = cos_omega1 = sin_beta1 != 0 || cos_alpha1 != 0 ? cos_beta1 * cos_alpha1 : 1;&lt;/span&gt;
&lt;span class="gi"&gt;+        math::normalize&amp;lt;CT&amp;gt;(sin_sigma1, cos_sigma1);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        CT const B11 = sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C1);
&lt;span class="w"&gt; &lt;/span&gt;        CT const sin_B11 = sin(B11);
&lt;span class="gu"&gt;@@ -162,9 +168,9 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT coeffs_C1p[SeriesOrder + 1];
&lt;span class="w"&gt; &lt;/span&gt;        series_expansion::evaluate_coeffs_C1p&amp;lt;CT, SeriesOrder&amp;gt;(epsilon, coeffs_C1p);

&lt;span class="gd"&gt;-        CT const B12 = sin_cos_series(sin_tau1 * cos_tau12 + cos_tau1 * sin_tau12,&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const B12 = - sin_cos_series(sin_tau1 * cos_tau12 + cos_tau1 * sin_tau12,&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                                      cos_tau1 * cos_tau12 - sin_tau1 * sin_tau12,
&lt;span class="gd"&gt;-                                      coeffs_C1p); // &amp;lt; 0?&lt;/span&gt;
&lt;span class="gi"&gt;+                                      coeffs_C1p);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        CT const sigma12 = tau12 - (B12 - B11);
&lt;span class="w"&gt; &lt;/span&gt;        CT const sin_sigma12 = sin(sigma12);
&lt;span class="gu"&gt;@@ -200,9 +206,6 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            result.lat2 /= math::d2r&amp;lt;T&amp;gt;();

&lt;span class="w"&gt; &lt;/span&gt;            // Find the longitude at the second point.
&lt;span class="gd"&gt;-            CT const sin_omega1 = sin_beta1 * sin_alpha0;&lt;/span&gt;
&lt;span class="gd"&gt;-            CT const cos_omega1 = cos_beta1 * cos_alpha1;&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            CT const sin_omega2 = sin_alpha0 * sin_sigma2;
&lt;span class="w"&gt; &lt;/span&gt;            CT const cos_omega2 = cos_sigma2;

&lt;span class="gu"&gt;@@ -218,11 +221,11 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            // Compute the size of coefficient array.
&lt;span class="w"&gt; &lt;/span&gt;            size_t const coeffs_C3_size = (SeriesOrder * (SeriesOrder - 1)) / 2;
&lt;span class="w"&gt; &lt;/span&gt;            CT coeffs_C3x[coeffs_C3_size];
&lt;span class="gd"&gt;-            series_expansion::evaluate_coeffs_C3&amp;lt;double, SeriesOrder&amp;gt;(n, coeffs_C3x);&lt;/span&gt;
&lt;span class="gi"&gt;+            series_expansion::evaluate_coeffs_C3x&amp;lt;double, SeriesOrder&amp;gt;(n, coeffs_C3x);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            // Evaluate C3 coefficients.
&lt;span class="w"&gt; &lt;/span&gt;            CT coeffs_C3[SeriesOrder];
&lt;span class="gd"&gt;-            math::evaluate_coeffs_var2&amp;lt;double, SeriesOrder&amp;gt;(epsilon, coeffs_C3x, coeffs_C3);&lt;/span&gt;
&lt;span class="gi"&gt;+            series_expansion::evaluate_coeffs_C3&amp;lt;double, SeriesOrder&amp;gt;(epsilon, coeffs_C3, coeffs_C3x);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            CT const B31 = sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C3);


commit 3dd6bce7202e3050a8f5d1c26b7111d0689a591a
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Tue May 29 21:08:06 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [util] Add functions for normalizing and evaluating polynomial

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/util/math.hpp b/include/boost/geometry/util/math.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 60d3646..e0ab1c8 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/util/math.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/util/math.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -830,10 +830,23 @@ inline T round_angle(T x) {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;}

&lt;span class="w"&gt; &lt;/span&gt;/*!
&lt;span class="gi"&gt;+\brief Normalize the given values.&lt;/span&gt;
&lt;span class="gi"&gt;+*/&lt;/span&gt;
&lt;span class="gi"&gt;+template&amp;lt;typename T&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+inline void normalize(T&amp;amp; x, T&amp;amp; y)&lt;/span&gt;
&lt;span class="gi"&gt;+{&lt;/span&gt;
&lt;span class="gi"&gt;+    T h = boost::math::hypot(x, y);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    x /= h; y /= h;&lt;/span&gt;
&lt;span class="gi"&gt;+}&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+/*!&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;\brief Normalize a given angle.
&lt;span class="w"&gt; &lt;/span&gt;*/
&lt;span class="w"&gt; &lt;/span&gt;template&amp;lt;typename T&amp;gt;
&lt;span class="gd"&gt;-    inline T normalize_angle(T x) {&lt;/span&gt;
&lt;span class="gi"&gt;+    inline T normalize_angle(T x)&lt;/span&gt;
&lt;span class="gi"&gt;+{&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    T y = std::fmod(x, T(360));

&lt;span class="w"&gt; &lt;/span&gt;    return y &amp;lt;= -180 ? y + 360 : (y &amp;lt;= 180 ? y : y - 360);
&lt;span class="gu"&gt;@@ -845,7 +858,7 @@ template&amp;lt;typename T&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;// TODO: adl1995 - Merge these functions with formulas/area_formulas.hpp
&lt;span class="w"&gt; &lt;/span&gt;// i.e. place them in one file.
&lt;span class="w"&gt; &lt;/span&gt;template &amp;lt;typename NT, typename IteratorType&amp;gt;
&lt;span class="gd"&gt;-static inline NT horner_evaluate(NT x,&lt;/span&gt;
&lt;span class="gi"&gt;+inline NT horner_evaluate(NT x,&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                                 IteratorType begin,
&lt;span class="w"&gt; &lt;/span&gt;                                 IteratorType end)
&lt;span class="w"&gt; &lt;/span&gt;{
&lt;span class="gu"&gt;@@ -860,20 +873,21 @@ static inline NT horner_evaluate(NT x,&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;}

&lt;span class="w"&gt; &lt;/span&gt;/*
&lt;span class="gd"&gt;-\brief Given the set of coefficients coeffs1[] evaluate on&lt;/span&gt;
&lt;span class="gd"&gt;-    var2 and return the set of coefficients coeffs2[].&lt;/span&gt;
&lt;span class="gi"&gt;+\brief Evaluate the polynomial.&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;*/
&lt;span class="gd"&gt;-template &amp;lt;typename CT, std::size_t SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gd"&gt;-static inline void evaluate_coeffs_var2(CT var2,&lt;/span&gt;
&lt;span class="gd"&gt;-                                        CT coeffs1[],&lt;/span&gt;
&lt;span class="gd"&gt;-                                        CT coeffs2[]){&lt;/span&gt;
&lt;span class="gd"&gt;-    std::size_t begin(0), end(0);&lt;/span&gt;
&lt;span class="gd"&gt;-    for(std::size_t i = 0; i &amp;lt;= SeriesOrder; i++){&lt;/span&gt;
&lt;span class="gd"&gt;-        end = begin + SeriesOrder + 1 - i;&lt;/span&gt;
&lt;span class="gd"&gt;-        coeffs2[i] = ((i==0) ? CT(1) : pow(var2,int(i)))&lt;/span&gt;
&lt;span class="gd"&gt;-                    * horner_evaluate(var2, coeffs1 + begin, coeffs1 + end);&lt;/span&gt;
&lt;span class="gd"&gt;-        begin = end;&lt;/span&gt;
&lt;span class="gi"&gt;+template&amp;lt;typename CT&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+inline CT polyval(int N,&lt;/span&gt;
&lt;span class="gi"&gt;+                         const CT coeff[],&lt;/span&gt;
&lt;span class="gi"&gt;+                         const CT eps)&lt;/span&gt;
&lt;span class="gi"&gt;+{&lt;/span&gt;
&lt;span class="gi"&gt;+    CT y = N &amp;lt; 0 ? 0 : *coeff++;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    while (--N &amp;gt;= 0)&lt;/span&gt;
&lt;span class="gi"&gt;+    {&lt;/span&gt;
&lt;span class="gi"&gt;+        y = y * eps + *coeff++;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    }
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    return y;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;}

&lt;span class="w"&gt; &lt;/span&gt;} // namespace math

commit 1e31876e9446d359d4b389d916e6d61486ccc057
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Tue May 29 21:06:19 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [util] Modify function for evaluting C3x coefficient

&lt;span class="w"&gt; &lt;/span&gt;   - Add separate function for evaluating C3 from C3x coefficient

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/util/series_expansion.hpp b/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index f7e4595..59511c2 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -842,116 +842,243 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;               s/case\sCT(/case /g; s/):/:/g; s/epsCT(2)/eps2/g;
&lt;span class="w"&gt; &lt;/span&gt;               s/eps(CT(2))/eps2/g;&amp;#39;
&lt;span class="w"&gt; &lt;/span&gt;    */
&lt;span class="gd"&gt;-    template &amp;lt;typename CT, std::size_t SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gd"&gt;-    static inline void evaluate_coeffs_C3(CT const&amp;amp; n, CT c[])&lt;/span&gt;
&lt;span class="gi"&gt;+    // TODO: adl1995&lt;/span&gt;
&lt;span class="gi"&gt;+    // Update docstring and function body.&lt;/span&gt;
&lt;span class="gi"&gt;+    // The coefficients C3[l] in the Fourier expansion of B3&lt;/span&gt;
&lt;span class="gi"&gt;+    template &amp;lt;typename CT, int SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    void evaluate_coeffs_C3x(CT const&amp;amp; n, CT c[]) {&lt;/span&gt;
&lt;span class="gi"&gt;+        if (SeriesOrder == 3) {&lt;/span&gt;
&lt;span class="gi"&gt;+            static const CT coeff[] = {&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[1], coeff of eps^2, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gi"&gt;+            1, 8,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[1], coeff of eps^1, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gi"&gt;+            -1, 1, 4,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[2], coeff of eps^2, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gi"&gt;+            1, 16,&lt;/span&gt;
&lt;span class="gi"&gt;+            };&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+        else if (SeriesOrder == 4) {&lt;/span&gt;
&lt;span class="gi"&gt;+            static const CT coeff[] = {&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[1], coeff of eps^3, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gi"&gt;+            3, 64,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[1], coeff of eps^2, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gi"&gt;+            // This is a case where a leading 0 term has been inserted to maintain the&lt;/span&gt;
&lt;span class="gi"&gt;+            // pattern in the orders of the polynomials.&lt;/span&gt;
&lt;span class="gi"&gt;+            0, 1, 8,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[1], coeff of eps^1, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gi"&gt;+            -1, 1, 4,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[2], coeff of eps^3, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gi"&gt;+            3, 64,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[2], coeff of eps^2, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gi"&gt;+            -3, 2, 32,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[3], coeff of eps^3, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gi"&gt;+            5, 192,&lt;/span&gt;
&lt;span class="gi"&gt;+            };&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+        else if (SeriesOrder == 5) {&lt;/span&gt;
&lt;span class="gi"&gt;+            static const CT coeff[] = {&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[1], coeff of eps^4, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gi"&gt;+            5, 128,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[1], coeff of eps^3, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gi"&gt;+            3, 3, 64,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[1], coeff of eps^2, polynomial in n of order 2&lt;/span&gt;
&lt;span class="gi"&gt;+            -1, 0, 1, 8,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[1], coeff of eps^1, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gi"&gt;+            -1, 1, 4,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[2], coeff of eps^4, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gi"&gt;+            3, 128,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[2], coeff of eps^3, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gi"&gt;+            -2, 3, 64,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[2], coeff of eps^2, polynomial in n of order 2&lt;/span&gt;
&lt;span class="gi"&gt;+            1, -3, 2, 32,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[3], coeff of eps^4, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gi"&gt;+            3, 128,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[3], coeff of eps^3, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gi"&gt;+            -9, 5, 192,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[4], coeff of eps^4, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gi"&gt;+            7, 512,&lt;/span&gt;
&lt;span class="gi"&gt;+            };&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+        else if (SeriesOrder == 6) {&lt;/span&gt;
&lt;span class="gi"&gt;+            static const CT coeff[] = {&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[1], coeff of eps^5, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gi"&gt;+            3, 128,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[1], coeff of eps^4, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gi"&gt;+            2, 5, 128,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[1], coeff of eps^3, polynomial in n of order 2&lt;/span&gt;
&lt;span class="gi"&gt;+            -1, 3, 3, 64,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[1], coeff of eps^2, polynomial in n of order 2&lt;/span&gt;
&lt;span class="gi"&gt;+            -1, 0, 1, 8,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[1], coeff of eps^1, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gi"&gt;+            -1, 1, 4,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[2], coeff of eps^5, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gi"&gt;+            5, 256,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[2], coeff of eps^4, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gi"&gt;+            1, 3, 128,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[2], coeff of eps^3, polynomial in n of order 2&lt;/span&gt;
&lt;span class="gi"&gt;+            -3, -2, 3, 64,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[2], coeff of eps^2, polynomial in n of order 2&lt;/span&gt;
&lt;span class="gi"&gt;+            1, -3, 2, 32,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[3], coeff of eps^5, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gi"&gt;+            7, 512,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[3], coeff of eps^4, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gi"&gt;+            -10, 9, 384,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[3], coeff of eps^3, polynomial in n of order 2&lt;/span&gt;
&lt;span class="gi"&gt;+            5, -9, 5, 192,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[4], coeff of eps^5, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gi"&gt;+            7, 512,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[4], coeff of eps^4, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gi"&gt;+            -14, 7, 512,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[5], coeff of eps^5, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gi"&gt;+            21, 2560,&lt;/span&gt;
&lt;span class="gi"&gt;+            };&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+        else if (SeriesOrder == 7) {&lt;/span&gt;
&lt;span class="gi"&gt;+            static const CT coeff[] = {&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[1], coeff of eps^6, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gi"&gt;+            21, 1024,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[1], coeff of eps^5, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gi"&gt;+            11, 12, 512,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[1], coeff of eps^4, polynomial in n of order 2&lt;/span&gt;
&lt;span class="gi"&gt;+            2, 2, 5, 128,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[1], coeff of eps^3, polynomial in n of order 3&lt;/span&gt;
&lt;span class="gi"&gt;+            -5, -1, 3, 3, 64,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[1], coeff of eps^2, polynomial in n of order 2&lt;/span&gt;
&lt;span class="gi"&gt;+            -1, 0, 1, 8,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[1], coeff of eps^1, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gi"&gt;+            -1, 1, 4,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[2], coeff of eps^6, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gi"&gt;+            27, 2048,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[2], coeff of eps^5, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gi"&gt;+            1, 5, 256,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[2], coeff of eps^4, polynomial in n of order 2&lt;/span&gt;
&lt;span class="gi"&gt;+            -9, 2, 6, 256,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[2], coeff of eps^3, polynomial in n of order 3&lt;/span&gt;
&lt;span class="gi"&gt;+            2, -3, -2, 3, 64,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[2], coeff of eps^2, polynomial in n of order 2&lt;/span&gt;
&lt;span class="gi"&gt;+            1, -3, 2, 32,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[3], coeff of eps^6, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gi"&gt;+            3, 256,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[3], coeff of eps^5, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gi"&gt;+            -4, 21, 1536,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[3], coeff of eps^4, polynomial in n of order 2&lt;/span&gt;
&lt;span class="gi"&gt;+            -6, -10, 9, 384,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[3], coeff of eps^3, polynomial in n of order 3&lt;/span&gt;
&lt;span class="gi"&gt;+            -1, 5, -9, 5, 192,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[4], coeff of eps^6, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gi"&gt;+            9, 1024,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[4], coeff of eps^5, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gi"&gt;+            -10, 7, 512,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[4], coeff of eps^4, polynomial in n of order 2&lt;/span&gt;
&lt;span class="gi"&gt;+            10, -14, 7, 512,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[5], coeff of eps^6, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gi"&gt;+            9, 1024,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[5], coeff of eps^5, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gi"&gt;+            -45, 21, 2560,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[6], coeff of eps^6, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gi"&gt;+            11, 2048,&lt;/span&gt;
&lt;span class="gi"&gt;+            };&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+        else if (SeriesOrder == 8) {&lt;/span&gt;
&lt;span class="gi"&gt;+            static const CT coeff[] = {&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[1], coeff of eps^7, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gi"&gt;+            243, 16384,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[1], coeff of eps^6, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gi"&gt;+            10, 21, 1024,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[1], coeff of eps^5, polynomial in n of order 2&lt;/span&gt;
&lt;span class="gi"&gt;+            3, 11, 12, 512,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[1], coeff of eps^4, polynomial in n of order 3&lt;/span&gt;
&lt;span class="gi"&gt;+            -2, 2, 2, 5, 128,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[1], coeff of eps^3, polynomial in n of order 3&lt;/span&gt;
&lt;span class="gi"&gt;+            -5, -1, 3, 3, 64,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[1], coeff of eps^2, polynomial in n of order 2&lt;/span&gt;
&lt;span class="gi"&gt;+            -1, 0, 1, 8,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[1], coeff of eps^1, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gi"&gt;+            -1, 1, 4,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[2], coeff of eps^7, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gi"&gt;+            187, 16384,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[2], coeff of eps^6, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gi"&gt;+            69, 108, 8192,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[2], coeff of eps^5, polynomial in n of order 2&lt;/span&gt;
&lt;span class="gi"&gt;+            -2, 1, 5, 256,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[2], coeff of eps^4, polynomial in n of order 3&lt;/span&gt;
&lt;span class="gi"&gt;+            -6, -9, 2, 6, 256,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[2], coeff of eps^3, polynomial in n of order 3&lt;/span&gt;
&lt;span class="gi"&gt;+            2, -3, -2, 3, 64,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[2], coeff of eps^2, polynomial in n of order 2&lt;/span&gt;
&lt;span class="gi"&gt;+            1, -3, 2, 32,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[3], coeff of eps^7, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gi"&gt;+            139, 16384,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[3], coeff of eps^6, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gi"&gt;+            -1, 12, 1024,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[3], coeff of eps^5, polynomial in n of order 2&lt;/span&gt;
&lt;span class="gi"&gt;+            -77, -8, 42, 3072,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[3], coeff of eps^4, polynomial in n of order 3&lt;/span&gt;
&lt;span class="gi"&gt;+            10, -6, -10, 9, 384,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[3], coeff of eps^3, polynomial in n of order 3&lt;/span&gt;
&lt;span class="gi"&gt;+            -1, 5, -9, 5, 192,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[4], coeff of eps^7, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gi"&gt;+            127, 16384,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[4], coeff of eps^6, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gi"&gt;+            -43, 72, 8192,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[4], coeff of eps^5, polynomial in n of order 2&lt;/span&gt;
&lt;span class="gi"&gt;+            -7, -40, 28, 2048,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[4], coeff of eps^4, polynomial in n of order 3&lt;/span&gt;
&lt;span class="gi"&gt;+            -7, 20, -28, 14, 1024,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[5], coeff of eps^7, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gi"&gt;+            99, 16384,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[5], coeff of eps^6, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gi"&gt;+            -15, 9, 1024,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[5], coeff of eps^5, polynomial in n of order 2&lt;/span&gt;
&lt;span class="gi"&gt;+            75, -90, 42, 5120,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[6], coeff of eps^7, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gi"&gt;+            99, 16384,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[6], coeff of eps^6, polynomial in n of order 1&lt;/span&gt;
&lt;span class="gi"&gt;+            -99, 44, 8192,&lt;/span&gt;
&lt;span class="gi"&gt;+            // C3[7], coeff of eps^7, polynomial in n of order 0&lt;/span&gt;
&lt;span class="gi"&gt;+            429, 114688,&lt;/span&gt;
&lt;span class="gi"&gt;+            };&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            int offset = 0, k = 0;&lt;/span&gt;
&lt;span class="gi"&gt;+            // l is index of C3[l].&lt;/span&gt;
&lt;span class="gi"&gt;+            for (int l = 1; l &amp;lt; SeriesOrder; ++l) {&lt;/span&gt;
&lt;span class="gi"&gt;+                for (int j = SeriesOrder - 1; j &amp;gt;= l; --j) {&lt;/span&gt;
&lt;span class="gi"&gt;+                    // Order of polynomial in n.&lt;/span&gt;
&lt;span class="gi"&gt;+                    int m = std::min(SeriesOrder - j - 1, j);&lt;/span&gt;
&lt;span class="gi"&gt;+                    c[k++] = math::polyval(m, coeff + offset, n) /&lt;/span&gt;
&lt;span class="gi"&gt;+                             coeff[offset + m + 1];&lt;/span&gt;
&lt;span class="gi"&gt;+                    offset += m + 2;&lt;/span&gt;
&lt;span class="gi"&gt;+                }&lt;/span&gt;
&lt;span class="gi"&gt;+            }&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+        // Post condition: offset == sizeof(coeff) / sizeof(CT) &amp;amp;&amp;amp; k == coeffs_C3_size&lt;/span&gt;
&lt;span class="gi"&gt;+    }&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    /*&lt;/span&gt;
&lt;span class="gi"&gt;+    \brief Given the set of coefficients coeffs2[] evaluate on&lt;/span&gt;
&lt;span class="gi"&gt;+      C3 and return the set of coefficients coeffs1[].&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+      Elements coeffs1[1] through coeffs1[SeriesOrder - 1] are set.&lt;/span&gt;
&lt;span class="gi"&gt;+    */&lt;/span&gt;
&lt;span class="gi"&gt;+    template &amp;lt;typename CT, size_t SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    static inline void evaluate_coeffs_C3(CT eps, CT coeffs1[], CT coeffs2[])&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    {
&lt;span class="gd"&gt;-        const CT n2 = math::sqr(n);&lt;/span&gt;
&lt;span class="gd"&gt;-        switch (SeriesOrder) {&lt;/span&gt;
&lt;span class="gd"&gt;-        case 0:&lt;/span&gt;
&lt;span class="gd"&gt;-            break;&lt;/span&gt;
&lt;span class="gd"&gt;-        case 1:&lt;/span&gt;
&lt;span class="gd"&gt;-            break;&lt;/span&gt;
&lt;span class="gd"&gt;-        case 2:&lt;/span&gt;
&lt;span class="gd"&gt;-            c[0] = (CT(1)-n)/CT(4);&lt;/span&gt;
&lt;span class="gd"&gt;-            break;&lt;/span&gt;
&lt;span class="gd"&gt;-        case 3:&lt;/span&gt;
&lt;span class="gd"&gt;-            c[0] = (CT(1)-n)/CT(4);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[1] = (CT(1)-n2)/CT(8);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[2] = ((n-CT(3))*n+CT(2))/CT(32);&lt;/span&gt;
&lt;span class="gd"&gt;-            break;&lt;/span&gt;
&lt;span class="gd"&gt;-        case 4:&lt;/span&gt;
&lt;span class="gd"&gt;-            c[0] = (CT(1)-n)/CT(4);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[1] = (CT(1)-n2)/CT(8);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[2] = (n*((-CT(5)*n-CT(1))*n+CT(3))+CT(3))/CT(64);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[3] = ((n-CT(3))*n+CT(2))/CT(32);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[4] = (n*(n*(CT(2)*n-CT(3))-CT(2))+CT(3))/CT(64);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[5] = (n*((CT(5)-n)*n-CT(9))+CT(5))/CT(192);&lt;/span&gt;
&lt;span class="gd"&gt;-            break;&lt;/span&gt;
&lt;span class="gd"&gt;-        case 5:&lt;/span&gt;
&lt;span class="gd"&gt;-            c[0] = (CT(1)-n)/CT(4);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[1] = (CT(1)-n2)/CT(8);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[2] = (n*((-CT(5)*n-CT(1))*n+CT(3))+CT(3))/CT(64);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[3] = (n*((CT(2)-CT(2)*n)*n+CT(2))+CT(5))/CT(128);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[4] = ((n-CT(3))*n+CT(2))/CT(32);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[5] = (n*(n*(CT(2)*n-CT(3))-CT(2))+CT(3))/CT(64);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[6] = (n*((-CT(6)*n-CT(9))*n+CT(2))+CT(6))/CT(256);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[7] = (n*((CT(5)-n)*n-CT(9))+CT(5))/CT(192);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[8] = (n*(n*(CT(10)*n-CT(6))-CT(10))+CT(9))/CT(384);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[9] = (n*((CT(20)-CT(7)*n)*n-CT(28))+CT(14))/CT(1024);&lt;/span&gt;
&lt;span class="gd"&gt;-            break;&lt;/span&gt;
&lt;span class="gd"&gt;-        case 6:&lt;/span&gt;
&lt;span class="gd"&gt;-            c[0] = (CT(1)-n)/CT(4);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[1] = (CT(1)-n2)/CT(8);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[2] = (n*((-CT(5)*n-CT(1))*n+CT(3))+CT(3))/CT(64);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[3] = (n*((CT(2)-CT(2)*n)*n+CT(2))+CT(5))/CT(128);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[4] = (n*(CT(3)*n+CT(11))+CT(12))/CT(512);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[5] = ((n-CT(3))*n+CT(2))/CT(32);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[6] = (n*(n*(CT(2)*n-CT(3))-CT(2))+CT(3))/CT(64);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[7] = (n*((-CT(6)*n-CT(9))*n+CT(2))+CT(6))/CT(256);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[8] = ((CT(1)-CT(2)*n)*n+CT(5))/CT(256);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[9] = (n*((CT(5)-n)*n-CT(9))+CT(5))/CT(192);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[10] = (n*(n*(CT(10)*n-CT(6))-CT(10))+CT(9))/CT(384);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[11] = ((-CT(77)*n-CT(8))*n+CT(42))/CT(3072);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[12] = (n*((CT(20)-CT(7)*n)*n-CT(28))+CT(14))/CT(1024);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[13] = ((-CT(7)*n-CT(40))*n+CT(28))/CT(2048);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[14] = (n*(CT(75)*n-CT(90))+CT(42))/CT(5120);&lt;/span&gt;
&lt;span class="gd"&gt;-            break;&lt;/span&gt;
&lt;span class="gd"&gt;-        case 7:&lt;/span&gt;
&lt;span class="gd"&gt;-            c[0] = (CT(1)-n)/CT(4);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[1] = (CT(1)-n2)/CT(8);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[2] = (n*((-CT(5)*n-CT(1))*n+CT(3))+CT(3))/CT(64);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[3] = (n*((CT(2)-CT(2)*n)*n+CT(2))+CT(5))/CT(128);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[4] = (n*(CT(3)*n+CT(11))+CT(12))/CT(512);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[5] = (CT(10)*n+CT(21))/CT(1024);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[6] = ((n-CT(3))*n+CT(2))/CT(32);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[7] = (n*(n*(CT(2)*n-CT(3))-CT(2))+CT(3))/CT(64);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[8] = (n*((-CT(6)*n-CT(9))*n+CT(2))+CT(6))/CT(256);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[9] = ((CT(1)-CT(2)*n)*n+CT(5))/CT(256);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[10] = (CT(69)*n+CT(108))/CT(8192);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[11] = (n*((CT(5)-n)*n-CT(9))+CT(5))/CT(192);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[12] = (n*(n*(CT(10)*n-CT(6))-CT(10))+CT(9))/CT(384);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[13] = ((-CT(77)*n-CT(8))*n+CT(42))/CT(3072);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[14] = (CT(12)-n)/CT(1024);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[15] = (n*((CT(20)-CT(7)*n)*n-CT(28))+CT(14))/CT(1024);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[16] = ((-CT(7)*n-CT(40))*n+CT(28))/CT(2048);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[17] = (CT(72)-CT(43)*n)/CT(8192);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[18] = (n*(CT(75)*n-CT(90))+CT(42))/CT(5120);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[19] = (CT(9)-CT(15)*n)/CT(1024);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[20] = (CT(44)-CT(99)*n)/CT(8192);&lt;/span&gt;
&lt;span class="gd"&gt;-            break;&lt;/span&gt;
&lt;span class="gd"&gt;-        case 8:&lt;/span&gt;
&lt;span class="gd"&gt;-            c[0] = (CT(1)-n)/CT(4);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[1] = (CT(1)-n2)/CT(8);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[2] = (n*((-CT(5)*n-CT(1))*n+CT(3))+CT(3))/CT(64);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[3] = (n*((CT(2)-CT(2)*n)*n+CT(2))+CT(5))/CT(128);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[4] = (n*(CT(3)*n+CT(11))+CT(12))/CT(512);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[5] = (CT(10)*n+CT(21))/CT(1024);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[6] = CT(243)/CT(16384);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[7] = ((n-CT(3))*n+CT(2))/CT(32);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[8] = (n*(n*(CT(2)*n-CT(3))-CT(2))+CT(3))/CT(64);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[9] = (n*((-CT(6)*n-CT(9))*n+CT(2))+CT(6))/CT(256);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[10] = ((CT(1)-CT(2)*n)*n+CT(5))/CT(256);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[11] = (CT(69)*n+CT(108))/CT(8192);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[12] = CT(187)/CT(16384);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[13] = (n*((CT(5)-n)*n-CT(9))+CT(5))/CT(192);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[14] = (n*(n*(CT(10)*n-CT(6))-CT(10))+CT(9))/CT(384);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[15] = ((-CT(77)*n-CT(8))*n+CT(42))/CT(3072);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[16] = (CT(12)-n)/CT(1024);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[17] = CT(139)/CT(16384);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[18] = (n*((CT(20)-CT(7)*n)*n-CT(28))+CT(14))/CT(1024);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[19] = ((-CT(7)*n-CT(40))*n+CT(28))/CT(2048);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[20] = (CT(72)-CT(43)*n)/CT(8192);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[21] = CT(127)/CT(16384);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[22] = (n*(CT(75)*n-CT(90))+CT(42))/CT(5120);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[23] = (CT(9)-CT(15)*n)/CT(1024);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[24] = CT(99)/CT(16384);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[25] = (CT(44)-CT(99)*n)/CT(8192);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[26] = CT(99)/CT(16384);&lt;/span&gt;
&lt;span class="gd"&gt;-            c[27] = CT(429)/CT(114688);&lt;/span&gt;
&lt;span class="gd"&gt;-            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT mult = 1;&lt;/span&gt;
&lt;span class="gi"&gt;+        int offset = 0;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        // l is the index of C3[l].&lt;/span&gt;
&lt;span class="gi"&gt;+        for (size_t l = 1; l &amp;lt; SeriesOrder; ++l)&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            // Order of polynomial in eps.&lt;/span&gt;
&lt;span class="gi"&gt;+            int m = SeriesOrder - l - 1;&lt;/span&gt;
&lt;span class="gi"&gt;+            mult *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            coeffs1[l] = mult * math::polyval(m, coeffs2 + offset, eps);&lt;/span&gt;
&lt;span class="gi"&gt;+            offset += m + 1;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="gi"&gt;+        // Post condition: offset == coeffs_C3_size&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    }

&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;}}} // namespace boost::geometry::series_expansion

&lt;span class="w"&gt; &lt;/span&gt;#endif // BOOST_GEOMETRY_UTIL_SERIES_EXPANSION_HPP

commit afe7bc679c7e90852e6e4441259f12dda4db7962
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Mon May 28 16:46:20 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [test] Add direct Karney&amp;#39;s method to direct.cpp test cases

&lt;span class="gh"&gt;diff --git a/test/formulas/direct.cpp b/test/formulas/direct.cpp&lt;/span&gt;
&lt;span class="gh"&gt;index 72778ab..7cf076b 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/test/formulas/direct.cpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/test/formulas/direct.cpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,3 +1,4 @@&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;// Boost.Geometry
&lt;span class="w"&gt; &lt;/span&gt;// Unit Test

&lt;span class="gu"&gt;@@ -14,6 +15,7 @@&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;#include &amp;lt;boost/geometry/formulas/vincenty_direct.hpp&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;#include &amp;lt;boost/geometry/formulas/thomas_direct.hpp&amp;gt;
&lt;span class="gi"&gt;+#include &amp;lt;boost/geometry/formulas/karney_direct.hpp&amp;gt;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;#include &amp;lt;boost/geometry/srs/spheroid.hpp&amp;gt;

&lt;span class="gu"&gt;@@ -37,6 +39,10 @@ void test_all(expected_results const&amp;amp; results)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    double distance = results.distance;
&lt;span class="w"&gt; &lt;/span&gt;    double azi12r = results.azimuth12 * d2r;

&lt;span class="gi"&gt;+    double lon1d = results.p1.lon;&lt;/span&gt;
&lt;span class="gi"&gt;+    double lat1d = results.p1.lat;&lt;/span&gt;
&lt;span class="gi"&gt;+    double azi12d = results.azimuth12;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    // WGS84
&lt;span class="w"&gt; &lt;/span&gt;    bg::srs::spheroid&amp;lt;double&amp;gt; spheroid(6378137.0, 6356752.3142451793);

&lt;span class="gu"&gt;@@ -55,6 +61,10 @@ void test_all(expected_results const&amp;amp; results)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    result.lat2 *= r2d;
&lt;span class="w"&gt; &lt;/span&gt;    result.reverse_azimuth *= r2d;
&lt;span class="w"&gt; &lt;/span&gt;    check_direct(result, results.thomas, results.karney, 0.0000001);
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    typedef bg::formula::karney_direct&amp;lt;double, 8, true, true, true, true&amp;gt; ka_t;&lt;/span&gt;
&lt;span class="gi"&gt;+    result = ka_t::apply(lon1d, lat1d, distance, azi12d, spheroid);&lt;/span&gt;
&lt;span class="gi"&gt;+    check_direct(result, results.thomas, results.karney, 0.0000001);&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;}

&lt;span class="w"&gt; &lt;/span&gt;int test_main(int, char*[])

commit 6a2897db25844d1bbcc8dc59bf88328e342cb5a9
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Mon May 28 12:28:14 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [formulas][util] Add missing import and function return type

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/formulas/karney_direct.hpp b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 38460fb..8e81cf4 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -11,6 +11,7 @@&lt;/span&gt;


&lt;span class="w"&gt; &lt;/span&gt;#include &amp;lt;boost/math/constants/constants.hpp&amp;gt;
&lt;span class="gi"&gt;+#include &amp;lt;boost/math/special_functions/hypot.hpp&amp;gt;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;#include &amp;lt;boost/geometry/util/math.hpp&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;#include &amp;lt;boost/geometry/util/series_expansion.hpp&amp;gt;
&lt;span class="gh"&gt;diff --git a/include/boost/geometry/util/series_expansion.hpp b/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index cca4328..f7e4595 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -538,7 +538,7 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;               s/eps(CT(2))/eps2/g;&amp;#39;
&lt;span class="w"&gt; &lt;/span&gt;    */
&lt;span class="w"&gt; &lt;/span&gt;    template &amp;lt;typename CT, std::size_t SeriesOrder&amp;gt;
&lt;span class="gd"&gt;-    static inline evaluate_coeffs_C1p(CT eps, CT c[])&lt;/span&gt;
&lt;span class="gi"&gt;+    static inline void evaluate_coeffs_C1p(CT eps, CT c[])&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    {
&lt;span class="w"&gt; &lt;/span&gt;        CT const eps2 = math::sqr(eps);
&lt;span class="w"&gt; &lt;/span&gt;        CT d = eps;

commit 0c2b8cdbab3fd6e5b525617641b260cba26ee3b9
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Thu May 24 11:37:28 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [formulas] Make variable declarations constant

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/formulas/karney_direct.hpp b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 798a862..38460fb 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -90,7 +90,7 @@ public:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        CT const lon1 = lo1;
&lt;span class="w"&gt; &lt;/span&gt;        CT const lat1 = la1;
&lt;span class="gd"&gt;-        Azi azi12 = azimuth12;&lt;/span&gt;
&lt;span class="gi"&gt;+        Azi const azi12 = math::normalize_angle&amp;lt;CT&amp;gt;(azimuth12);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        if (math::equals(distance, Dist(0)) || distance &amp;lt; Dist(0))
&lt;span class="w"&gt; &lt;/span&gt;        {
&lt;span class="gu"&gt;@@ -112,7 +112,6 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT const e2 = f * two_minus_f;
&lt;span class="w"&gt; &lt;/span&gt;        CT const ep2 = e2 / math::sqr(one_minus_f);

&lt;span class="gd"&gt;-        azi12 = math::normalize_angle&amp;lt;CT&amp;gt;(azi12);&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT sin_alpha1, cos_alpha1;
&lt;span class="w"&gt; &lt;/span&gt;        math::sin_cos_degrees&amp;lt;CT&amp;gt;(math::round_angle&amp;lt;CT&amp;gt;(azi12), sin_alpha1, cos_alpha1);

&lt;span class="gu"&gt;@@ -122,56 +121,63 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        sin_beta1 *= one_minus_f;

&lt;span class="w"&gt; &lt;/span&gt;        // Obtain alpha 0 by solving the spherical triangle.
&lt;span class="gd"&gt;-        CT sin_alpha0 = sin_alpha1 * cos_beta1;&lt;/span&gt;
&lt;span class="gd"&gt;-        CT cos_alpha0 = boost::math::hypot(cos_alpha1, sin_alpha1 * sin_beta1);&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const sin_alpha0&lt;/span&gt;
&lt;span class="gi"&gt;+            = sin_alpha1 * cos_beta1;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const cos_alpha0&lt;/span&gt;
&lt;span class="gi"&gt;+            = boost::math::hypot(cos_alpha1, sin_alpha1 * sin_beta1);&lt;/span&gt;

&lt;span class="gd"&gt;-        CT k2 = math::sqr(cos_alpha0) * ep2;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const k2 = math::sqr(cos_alpha0) * ep2;&lt;/span&gt;

&lt;span class="gd"&gt;-        CT epsilon = k2 / (c2 * (c1 + sqrt(c1 + k2)) + k2);&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const epsilon = k2 / (c2 * (c1 + sqrt(c1 + k2)) + k2);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        // Find the coefficients for A1 by computing the
&lt;span class="w"&gt; &lt;/span&gt;        // series expansion using Horner scehme.
&lt;span class="gd"&gt;-        CT expansion_A1 = series_expansion::evaluate_series_A1&amp;lt;CT, SeriesOrder&amp;gt;(epsilon);&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const expansion_A1&lt;/span&gt;
&lt;span class="gi"&gt;+            = series_expansion::evaluate_series_A1&amp;lt;CT, SeriesOrder&amp;gt;(epsilon);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        // Index zero element of coeffs_C1 is unused.
&lt;span class="w"&gt; &lt;/span&gt;        CT coeffs_C1[SeriesOrder + 1];
&lt;span class="w"&gt; &lt;/span&gt;        series_expansion::evaluate_coeffs_C1&amp;lt;CT, SeriesOrder&amp;gt;(epsilon, coeffs_C1);

&lt;span class="w"&gt; &lt;/span&gt;        // Tau is an integration variable.
&lt;span class="gd"&gt;-        CT tau12 = distance / (b * (c1 + expansion_A1));&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const tau12 = distance / (b * (c1 + expansion_A1));&lt;/span&gt;

&lt;span class="gd"&gt;-        CT sin_tau12 = sin(tau12);&lt;/span&gt;
&lt;span class="gd"&gt;-        CT cos_tau12 = cos(tau12);&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const sin_tau12 = sin(tau12);&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const cos_tau12 = cos(tau12);&lt;/span&gt;

&lt;span class="gd"&gt;-        CT sin_sigma1 = sin_beta1;&lt;/span&gt;
&lt;span class="gd"&gt;-        CT cos_sigma1 = cos_beta1 * cos_alpha1;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const sin_sigma1 = sin_beta1;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const cos_sigma1 = cos_beta1 * cos_alpha1;&lt;/span&gt;

&lt;span class="gd"&gt;-        CT B11 = sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C1);&lt;/span&gt;
&lt;span class="gd"&gt;-        CT sin_B11 = sin(B11);&lt;/span&gt;
&lt;span class="gd"&gt;-        CT cos_B11 = cos(B11);&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const B11 = sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C1);&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const sin_B11 = sin(B11);&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const cos_B11 = cos(B11);&lt;/span&gt;

&lt;span class="gd"&gt;-        CT sin_tau1 = sin_sigma1 * cos_B11 + cos_sigma1 * sin_B11;&lt;/span&gt;
&lt;span class="gd"&gt;-        CT cos_tau1 = cos_sigma1 * cos_B11 - sin_sigma1 * sin_B11;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const sin_tau1&lt;/span&gt;
&lt;span class="gi"&gt;+            = sin_sigma1 * cos_B11 + cos_sigma1 * sin_B11;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const cos_tau1&lt;/span&gt;
&lt;span class="gi"&gt;+            = cos_sigma1 * cos_B11 - sin_sigma1 * sin_B11;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        // Index zero element of coeffs_C1p is unused.
&lt;span class="w"&gt; &lt;/span&gt;        CT coeffs_C1p[SeriesOrder + 1];
&lt;span class="w"&gt; &lt;/span&gt;        series_expansion::evaluate_coeffs_C1p&amp;lt;CT, SeriesOrder&amp;gt;(epsilon, coeffs_C1p);

&lt;span class="gd"&gt;-        CT B12 = sin_cos_series(sin_tau1 * cos_tau12 + cos_tau1 * sin_tau12,&lt;/span&gt;
&lt;span class="gd"&gt;-                                cos_tau1 * cos_tau12 - sin_tau1 * sin_tau12,&lt;/span&gt;
&lt;span class="gd"&gt;-                                coeffs_C1p); // &amp;lt; 0?&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const B12 = sin_cos_series(sin_tau1 * cos_tau12 + cos_tau1 * sin_tau12,&lt;/span&gt;
&lt;span class="gi"&gt;+                                      cos_tau1 * cos_tau12 - sin_tau1 * sin_tau12,&lt;/span&gt;
&lt;span class="gi"&gt;+                                      coeffs_C1p); // &amp;lt; 0?&lt;/span&gt;

&lt;span class="gd"&gt;-        CT sigma12 = tau12 - (B12 - B11);&lt;/span&gt;
&lt;span class="gd"&gt;-        CT sin_sigma12 = sin(sigma12);&lt;/span&gt;
&lt;span class="gd"&gt;-        CT cos_sigma12 = cos(sigma12);&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const sigma12 = tau12 - (B12 - B11);&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const sin_sigma12 = sin(sigma12);&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const cos_sigma12 = cos(sigma12);&lt;/span&gt;

&lt;span class="gd"&gt;-        CT sin_sigma2 = sin_sigma1 * cos_sigma12 + cos_sigma1 * sin_sigma12;&lt;/span&gt;
&lt;span class="gd"&gt;-        CT cos_sigma2 = cos_sigma1 * cos_sigma12 - sin_sigma1 * sin_sigma12;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const sin_sigma2&lt;/span&gt;
&lt;span class="gi"&gt;+            = sin_sigma1 * cos_sigma12 + cos_sigma1 * sin_sigma12;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const cos_sigma2&lt;/span&gt;
&lt;span class="gi"&gt;+            = cos_sigma1 * cos_sigma12 - sin_sigma1 * sin_sigma12;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        if (BOOST_GEOMETRY_CONDITION(CalcRevAzimuth))
&lt;span class="w"&gt; &lt;/span&gt;        {
&lt;span class="gd"&gt;-            CT sin_alpha2 = sin_alpha0;&lt;/span&gt;
&lt;span class="gd"&gt;-            CT cos_alpha2 = cos_alpha0 * cos_sigma2;&lt;/span&gt;
&lt;span class="gi"&gt;+            CT const sin_alpha2 = sin_alpha0;&lt;/span&gt;
&lt;span class="gi"&gt;+            CT const cos_alpha2 = cos_alpha0 * cos_sigma2;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            result.reverse_azimuth = atan2(sin_alpha2, cos_alpha2);

&lt;span class="gu"&gt;@@ -182,8 +188,10 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        if (BOOST_GEOMETRY_CONDITION(CalcCoordinates))
&lt;span class="w"&gt; &lt;/span&gt;        {
&lt;span class="w"&gt; &lt;/span&gt;            // Find the latitude at the second point.
&lt;span class="gd"&gt;-            CT sin_beta2 = cos_alpha0 * sin_sigma2;&lt;/span&gt;
&lt;span class="gd"&gt;-            CT cos_beta2 = boost::math::hypot(sin_alpha0, cos_alpha0 * cos_sigma2);&lt;/span&gt;
&lt;span class="gi"&gt;+            CT const sin_beta2&lt;/span&gt;
&lt;span class="gi"&gt;+                = cos_alpha0 * sin_sigma2;&lt;/span&gt;
&lt;span class="gi"&gt;+            CT const cos_beta2&lt;/span&gt;
&lt;span class="gi"&gt;+                = boost::math::hypot(sin_alpha0, cos_alpha0 * cos_sigma2);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            result.lat2 = atan2(sin_beta2, one_minus_f * cos_beta2);

&lt;span class="gu"&gt;@@ -191,20 +199,20 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            result.lat2 /= math::d2r&amp;lt;T&amp;gt;();

&lt;span class="w"&gt; &lt;/span&gt;            // Find the longitude at the second point.
&lt;span class="gd"&gt;-            CT sin_omega1 = sin_beta1 * sin_alpha0;&lt;/span&gt;
&lt;span class="gd"&gt;-            CT cos_omega1 = cos_beta1 * cos_alpha1;&lt;/span&gt;
&lt;span class="gi"&gt;+            CT const sin_omega1 = sin_beta1 * sin_alpha0;&lt;/span&gt;
&lt;span class="gi"&gt;+            CT const cos_omega1 = cos_beta1 * cos_alpha1;&lt;/span&gt;

&lt;span class="gd"&gt;-            CT sin_omega2 = sin_alpha0 * sin_sigma2;&lt;/span&gt;
&lt;span class="gd"&gt;-            CT cos_omega2 = cos_sigma2;&lt;/span&gt;
&lt;span class="gi"&gt;+            CT const sin_omega2 = sin_alpha0 * sin_sigma2;&lt;/span&gt;
&lt;span class="gi"&gt;+            CT const cos_omega2 = cos_sigma2;&lt;/span&gt;

&lt;span class="gd"&gt;-            CT omega12 = atan2(sin_omega2 * cos_omega1 - cos_omega2 * sin_omega1,&lt;/span&gt;
&lt;span class="gd"&gt;-                               cos_omega2 * cos_omega1 + sin_omega2 * sin_omega1);&lt;/span&gt;
&lt;span class="gi"&gt;+            CT const omega12 = atan2(sin_omega2 * cos_omega1 - cos_omega2 * sin_omega1,&lt;/span&gt;
&lt;span class="gi"&gt;+                                     cos_omega2 * cos_omega1 + sin_omega2 * sin_omega1);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            CT coeffs_A3[SeriesOrder];
&lt;span class="w"&gt; &lt;/span&gt;            series_expansion::evaluate_coeffs_A3&amp;lt;double, SeriesOrder&amp;gt;(n, coeffs_A3);

&lt;span class="gd"&gt;-            CT A3 = math::horner_evaluate(epsilon, coeffs_A3, coeffs_A3 + SeriesOrder);&lt;/span&gt;
&lt;span class="gd"&gt;-            CT A3c = -f * sin_alpha0 * A3;&lt;/span&gt;
&lt;span class="gi"&gt;+            CT const A3 = math::horner_evaluate(epsilon, coeffs_A3, coeffs_A3 + SeriesOrder);&lt;/span&gt;
&lt;span class="gi"&gt;+            CT const A3c = -f * sin_alpha0 * A3;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            // Compute the size of coefficient array.
&lt;span class="w"&gt; &lt;/span&gt;            size_t const coeffs_C3_size = (SeriesOrder * (SeriesOrder - 1)) / 2;
&lt;span class="gu"&gt;@@ -215,16 +223,16 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            CT coeffs_C3[SeriesOrder];
&lt;span class="w"&gt; &lt;/span&gt;            math::evaluate_coeffs_var2&amp;lt;double, SeriesOrder&amp;gt;(epsilon, coeffs_C3x, coeffs_C3);

&lt;span class="gd"&gt;-            CT B31 = sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C3);&lt;/span&gt;
&lt;span class="gi"&gt;+            CT const B31 = sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C3);&lt;/span&gt;

&lt;span class="gd"&gt;-            CT lam12 = omega12 + A3c *&lt;/span&gt;
&lt;span class="gd"&gt;-                       (sigma12 + (sin_cos_series(sin_sigma2,&lt;/span&gt;
&lt;span class="gd"&gt;-                                                  cos_sigma2,&lt;/span&gt;
&lt;span class="gd"&gt;-                                                  coeffs_C3) - B31));&lt;/span&gt;
&lt;span class="gi"&gt;+            CT const lam12 = omega12 + A3c *&lt;/span&gt;
&lt;span class="gi"&gt;+                             (sigma12 + (sin_cos_series(sin_sigma2,&lt;/span&gt;
&lt;span class="gi"&gt;+                                                        cos_sigma2,&lt;/span&gt;
&lt;span class="gi"&gt;+                                                        coeffs_C3) - B31));&lt;/span&gt;

&lt;span class="gd"&gt;-            // Convert to radians to get the longitudinal&lt;/span&gt;
&lt;span class="gd"&gt;-            // difference.&lt;/span&gt;
&lt;span class="gd"&gt;-            CT lon12 = lam12 / math::d2r&amp;lt;T&amp;gt;();&lt;/span&gt;
&lt;span class="gi"&gt;+            // Convert to radians to get the&lt;/span&gt;
&lt;span class="gi"&gt;+            // longitudinal difference.&lt;/span&gt;
&lt;span class="gi"&gt;+            CT const lon12 = lam12 / math::d2r&amp;lt;T&amp;gt;();&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            // Add the longitude at first point to the longitudinal
&lt;span class="w"&gt; &lt;/span&gt;            // difference and normalize the result.
&lt;span class="gu"&gt;@@ -239,16 +247,17 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            CT coeffs_C2[SeriesOrder + 1];
&lt;span class="w"&gt; &lt;/span&gt;            series_expansion::evaluate_coeffs_C2&amp;lt;CT, SeriesOrder&amp;gt;(epsilon, coeffs_C2);

&lt;span class="gd"&gt;-            CT B21 = sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C2);&lt;/span&gt;
&lt;span class="gd"&gt;-            CT B22 = sin_cos_series(sin_sigma2, cos_sigma2, coeffs_C2);&lt;/span&gt;
&lt;span class="gi"&gt;+            CT const B21 = sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C2);&lt;/span&gt;
&lt;span class="gi"&gt;+            CT const B22 = sin_cos_series(sin_sigma2, cos_sigma2, coeffs_C2);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            // Find the coefficients for A2 by computing the
&lt;span class="w"&gt; &lt;/span&gt;            // series expansion using Horner scehme.
&lt;span class="gd"&gt;-            CT expansion_A2 = series_expansion::evaluate_series_A2&amp;lt;CT, SeriesOrder&amp;gt;(epsilon);&lt;/span&gt;
&lt;span class="gi"&gt;+            CT const expansion_A2&lt;/span&gt;
&lt;span class="gi"&gt;+                = series_expansion::evaluate_series_A2&amp;lt;CT, SeriesOrder&amp;gt;(epsilon);&lt;/span&gt;

&lt;span class="gd"&gt;-            CT AB1 = (c1 + expansion_A1) * (B12 - B11);&lt;/span&gt;
&lt;span class="gd"&gt;-            CT AB2 = (c1 + expansion_A2) * (B22 - B21);&lt;/span&gt;
&lt;span class="gd"&gt;-            CT J12 = (expansion_A1 - expansion_A2) * sigma12 + (AB1 - AB2);&lt;/span&gt;
&lt;span class="gi"&gt;+            CT const AB1 = (c1 + expansion_A1) * (B12 - B11);&lt;/span&gt;
&lt;span class="gi"&gt;+            CT const AB2 = (c1 + expansion_A2) * (B22 - B21);&lt;/span&gt;
&lt;span class="gi"&gt;+            CT const J12 = (expansion_A1 - expansion_A2) * sigma12 + (AB1 - AB2);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            CT const dn1 = sqrt(1 + ep2 * math::sqr(sin_beta1));
&lt;span class="w"&gt; &lt;/span&gt;            CT const dn2 = sqrt(1 + k2 * math::sqr(sin_sigma2));
&lt;span class="gu"&gt;@@ -259,8 +268,8 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                                          cos_sigma1 * cos_sigma2 * J12);

&lt;span class="w"&gt; &lt;/span&gt;            // Find the geodesic scale.
&lt;span class="gd"&gt;-            CT t = k2 * (sin_sigma2 - sin_sigma1) *&lt;/span&gt;
&lt;span class="gd"&gt;-                        (sin_sigma2 * sin_sigma1) / (dn1 + dn2);&lt;/span&gt;
&lt;span class="gi"&gt;+            CT const t = k2 * (sin_sigma2 - sin_sigma1) *&lt;/span&gt;
&lt;span class="gi"&gt;+                              (sin_sigma2 * sin_sigma1) / (dn1 + dn2);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            result.geodesic_scale = cos_sigma12 +
&lt;span class="w"&gt; &lt;/span&gt;                                    (t * sin_sigma2 - cos_sigma2 * J12) *

commit ac4e483c9271507aa84b30aadaba32a221efab5f
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Thu May 24 11:26:56 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [formulas] Compute the reduced length and geodesic scale using Karney&amp;#39;s direct method

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/formulas/karney_direct.hpp b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 58ed326..798a862 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,5 +1,7 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;// Boost.Geometry

&lt;span class="gi"&gt;+// Contributed and/or modified by Adeel Ahmad.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;// Use, modification and distribution is subject to the Boost Software License,
&lt;span class="w"&gt; &lt;/span&gt;// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
&lt;span class="w"&gt; &lt;/span&gt;// http://www.boost.org/LICENSE_1_0.txt)
&lt;span class="gu"&gt;@@ -30,7 +32,7 @@ https://arxiv.org/pdf/1109.4448.pdf&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;*/
&lt;span class="w"&gt; &lt;/span&gt;template &amp;lt;
&lt;span class="w"&gt; &lt;/span&gt;    typename CT,
&lt;span class="gd"&gt;-    std::size_t SeriesOrder = 8,&lt;/span&gt;
&lt;span class="gi"&gt;+    size_t SeriesOrder = 8,&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    bool EnableCoordinates = true,
&lt;span class="w"&gt; &lt;/span&gt;    bool EnableReverseAzimuth = false,
&lt;span class="w"&gt; &lt;/span&gt;    bool EnableReducedLength = false,
&lt;span class="gu"&gt;@@ -56,7 +58,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                                    CT cosx,
&lt;span class="w"&gt; &lt;/span&gt;                                    const CT coeffs[])
&lt;span class="w"&gt; &lt;/span&gt;    {
&lt;span class="gd"&gt;-        std::size_t n = SeriesOrder;&lt;/span&gt;
&lt;span class="gi"&gt;+        size_t n = SeriesOrder;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        // Point to one beyond last element.
&lt;span class="w"&gt; &lt;/span&gt;        coeffs += (n + 1);
&lt;span class="gu"&gt;@@ -125,9 +127,9 @@ public:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        CT k2 = math::sqr(cos_alpha0) * ep2;

&lt;span class="gd"&gt;-        CT epsilon = k2 / (c2 * (c1 + std::sqrt(c1 + k2)) + k2);&lt;/span&gt;
&lt;span class="gi"&gt;+        CT epsilon = k2 / (c2 * (c1 + sqrt(c1 + k2)) + k2);&lt;/span&gt;

&lt;span class="gd"&gt;-        // Find the coefficients for Aj by computing the&lt;/span&gt;
&lt;span class="gi"&gt;+        // Find the coefficients for A1 by computing the&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        // series expansion using Horner scehme.
&lt;span class="w"&gt; &lt;/span&gt;        CT expansion_A1 = series_expansion::evaluate_series_A1&amp;lt;CT, SeriesOrder&amp;gt;(epsilon);

&lt;span class="gu"&gt;@@ -171,7 +173,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            CT sin_alpha2 = sin_alpha0;
&lt;span class="w"&gt; &lt;/span&gt;            CT cos_alpha2 = cos_alpha0 * cos_sigma2;

&lt;span class="gd"&gt;-            result.reverse_azimuth = std::atan2(sin_alpha2, cos_alpha2);&lt;/span&gt;
&lt;span class="gi"&gt;+            result.reverse_azimuth = atan2(sin_alpha2, cos_alpha2);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            // Convert the angle to radians.
&lt;span class="w"&gt; &lt;/span&gt;            result.reverse_azimuth /= math::d2r&amp;lt;T&amp;gt;();
&lt;span class="gu"&gt;@@ -183,7 +185,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            CT sin_beta2 = cos_alpha0 * sin_sigma2;
&lt;span class="w"&gt; &lt;/span&gt;            CT cos_beta2 = boost::math::hypot(sin_alpha0, cos_alpha0 * cos_sigma2);

&lt;span class="gd"&gt;-            result.lat2 = std::atan2(sin_beta2, one_minus_f * cos_beta2);&lt;/span&gt;
&lt;span class="gi"&gt;+            result.lat2 = atan2(sin_beta2, one_minus_f * cos_beta2);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            // Convert the coordinate to radians.
&lt;span class="w"&gt; &lt;/span&gt;            result.lat2 /= math::d2r&amp;lt;T&amp;gt;();
&lt;span class="gu"&gt;@@ -195,8 +197,8 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            CT sin_omega2 = sin_alpha0 * sin_sigma2;
&lt;span class="w"&gt; &lt;/span&gt;            CT cos_omega2 = cos_sigma2;

&lt;span class="gd"&gt;-            CT omega12 = std::atan2(sin_omega2 * cos_omega1 - cos_omega2 * sin_omega1,&lt;/span&gt;
&lt;span class="gd"&gt;-                                    cos_omega2 * cos_omega1 + sin_omega2 * sin_omega1);&lt;/span&gt;
&lt;span class="gi"&gt;+            CT omega12 = atan2(sin_omega2 * cos_omega1 - cos_omega2 * sin_omega1,&lt;/span&gt;
&lt;span class="gi"&gt;+                               cos_omega2 * cos_omega1 + sin_omega2 * sin_omega1);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            CT coeffs_A3[SeriesOrder];
&lt;span class="w"&gt; &lt;/span&gt;            series_expansion::evaluate_coeffs_A3&amp;lt;double, SeriesOrder&amp;gt;(n, coeffs_A3);
&lt;span class="gu"&gt;@@ -205,7 +207,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            CT A3c = -f * sin_alpha0 * A3;

&lt;span class="w"&gt; &lt;/span&gt;            // Compute the size of coefficient array.
&lt;span class="gd"&gt;-            const std::size_t coeffs_C3_size = (SeriesOrder * (SeriesOrder - 1)) / 2;&lt;/span&gt;
&lt;span class="gi"&gt;+            size_t const coeffs_C3_size = (SeriesOrder * (SeriesOrder - 1)) / 2;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            CT coeffs_C3x[coeffs_C3_size];
&lt;span class="w"&gt; &lt;/span&gt;            series_expansion::evaluate_coeffs_C3&amp;lt;double, SeriesOrder&amp;gt;(n, coeffs_C3x);

&lt;span class="gu"&gt;@@ -229,6 +231,43 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            result.lon2 = math::normalize_angle(math::normalize_angle(lon1) +
&lt;span class="w"&gt; &lt;/span&gt;                                                math::normalize_angle(lon12));
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        if (BOOST_GEOMETRY_CONDITION(CalcQuantities))&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            // Evaluate the coefficients for C2.&lt;/span&gt;
&lt;span class="gi"&gt;+            // Index zero element of coeffs_C2 is unused.&lt;/span&gt;
&lt;span class="gi"&gt;+            CT coeffs_C2[SeriesOrder + 1];&lt;/span&gt;
&lt;span class="gi"&gt;+            series_expansion::evaluate_coeffs_C2&amp;lt;CT, SeriesOrder&amp;gt;(epsilon, coeffs_C2);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            CT B21 = sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C2);&lt;/span&gt;
&lt;span class="gi"&gt;+            CT B22 = sin_cos_series(sin_sigma2, cos_sigma2, coeffs_C2);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            // Find the coefficients for A2 by computing the&lt;/span&gt;
&lt;span class="gi"&gt;+            // series expansion using Horner scehme.&lt;/span&gt;
&lt;span class="gi"&gt;+            CT expansion_A2 = series_expansion::evaluate_series_A2&amp;lt;CT, SeriesOrder&amp;gt;(epsilon);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            CT AB1 = (c1 + expansion_A1) * (B12 - B11);&lt;/span&gt;
&lt;span class="gi"&gt;+            CT AB2 = (c1 + expansion_A2) * (B22 - B21);&lt;/span&gt;
&lt;span class="gi"&gt;+            CT J12 = (expansion_A1 - expansion_A2) * sigma12 + (AB1 - AB2);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            CT const dn1 = sqrt(1 + ep2 * math::sqr(sin_beta1));&lt;/span&gt;
&lt;span class="gi"&gt;+            CT const dn2 = sqrt(1 + k2 * math::sqr(sin_sigma2));&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            // Find the reduced length.&lt;/span&gt;
&lt;span class="gi"&gt;+            result.reduced_length = b * ((dn2 * (cos_sigma1 * sin_sigma2) -&lt;/span&gt;
&lt;span class="gi"&gt;+                                          dn1 * (sin_sigma1 * cos_sigma2)) -&lt;/span&gt;
&lt;span class="gi"&gt;+                                          cos_sigma1 * cos_sigma2 * J12);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            // Find the geodesic scale.&lt;/span&gt;
&lt;span class="gi"&gt;+            CT t = k2 * (sin_sigma2 - sin_sigma1) *&lt;/span&gt;
&lt;span class="gi"&gt;+                        (sin_sigma2 * sin_sigma1) / (dn1 + dn2);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            result.geodesic_scale = cos_sigma12 +&lt;/span&gt;
&lt;span class="gi"&gt;+                                    (t * sin_sigma2 - cos_sigma2 * J12) *&lt;/span&gt;
&lt;span class="gi"&gt;+                                    sin_sigma1 / dn1;&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        return result;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    }
&lt;span class="w"&gt; &lt;/span&gt;};

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/util/math.hpp b/include/boost/geometry/util/math.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index df7507e..60d3646 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/util/math.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/util/math.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -830,7 +830,7 @@ inline T round_angle(T x) {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;}

&lt;span class="w"&gt; &lt;/span&gt;/*!
&lt;span class="gd"&gt;-\brief Normalize a given angle&lt;/span&gt;
&lt;span class="gi"&gt;+\brief Normalize a given angle.&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;*/
&lt;span class="w"&gt; &lt;/span&gt;template&amp;lt;typename T&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;    inline T normalize_angle(T x) {
&lt;span class="gu"&gt;@@ -839,6 +839,43 @@ template&amp;lt;typename T&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    return y &amp;lt;= -180 ? y + 360 : (y &amp;lt;= 180 ? y : y - 360);
&lt;span class="w"&gt; &lt;/span&gt;}

&lt;span class="gi"&gt;+/*&lt;/span&gt;
&lt;span class="gi"&gt;+\brief Evaluate the polynomial in x using Horner&amp;#39;s method.&lt;/span&gt;
&lt;span class="gi"&gt;+*/&lt;/span&gt;
&lt;span class="gi"&gt;+// TODO: adl1995 - Merge these functions with formulas/area_formulas.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+// i.e. place them in one file.&lt;/span&gt;
&lt;span class="gi"&gt;+template &amp;lt;typename NT, typename IteratorType&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+static inline NT horner_evaluate(NT x,&lt;/span&gt;
&lt;span class="gi"&gt;+                                 IteratorType begin,&lt;/span&gt;
&lt;span class="gi"&gt;+                                 IteratorType end)&lt;/span&gt;
&lt;span class="gi"&gt;+{&lt;/span&gt;
&lt;span class="gi"&gt;+    NT result(0);&lt;/span&gt;
&lt;span class="gi"&gt;+    IteratorType it = end;&lt;/span&gt;
&lt;span class="gi"&gt;+    do&lt;/span&gt;
&lt;span class="gi"&gt;+    {&lt;/span&gt;
&lt;span class="gi"&gt;+        result = result * x + *--it;&lt;/span&gt;
&lt;span class="gi"&gt;+    }&lt;/span&gt;
&lt;span class="gi"&gt;+    while (it != begin);&lt;/span&gt;
&lt;span class="gi"&gt;+    return result;&lt;/span&gt;
&lt;span class="gi"&gt;+}&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+/*&lt;/span&gt;
&lt;span class="gi"&gt;+\brief Given the set of coefficients coeffs1[] evaluate on&lt;/span&gt;
&lt;span class="gi"&gt;+    var2 and return the set of coefficients coeffs2[].&lt;/span&gt;
&lt;span class="gi"&gt;+*/&lt;/span&gt;
&lt;span class="gi"&gt;+template &amp;lt;typename CT, std::size_t SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+static inline void evaluate_coeffs_var2(CT var2,&lt;/span&gt;
&lt;span class="gi"&gt;+                                        CT coeffs1[],&lt;/span&gt;
&lt;span class="gi"&gt;+                                        CT coeffs2[]){&lt;/span&gt;
&lt;span class="gi"&gt;+    std::size_t begin(0), end(0);&lt;/span&gt;
&lt;span class="gi"&gt;+    for(std::size_t i = 0; i &amp;lt;= SeriesOrder; i++){&lt;/span&gt;
&lt;span class="gi"&gt;+        end = begin + SeriesOrder + 1 - i;&lt;/span&gt;
&lt;span class="gi"&gt;+        coeffs2[i] = ((i==0) ? CT(1) : pow(var2,int(i)))&lt;/span&gt;
&lt;span class="gi"&gt;+                    * horner_evaluate(var2, coeffs1 + begin, coeffs1 + end);&lt;/span&gt;
&lt;span class="gi"&gt;+        begin = end;&lt;/span&gt;
&lt;span class="gi"&gt;+    }&lt;/span&gt;
&lt;span class="gi"&gt;+}&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;} // namespace math



commit a0a55f361f6d3f4931369ec2d60ae28722a2d718
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Thu May 24 11:18:26 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [util] Evaluate series and coefficients for A2 and C2 using series expansion

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/util/series_expansion.hpp b/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index c637563..cca4328 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,5 +1,7 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;// Boost.Geometry

&lt;span class="gi"&gt;+// Contributed and/or modified by Adeel Ahmad.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;// Use, modification and distribution is subject to the Boost Software License,
&lt;span class="w"&gt; &lt;/span&gt;// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
&lt;span class="w"&gt; &lt;/span&gt;// http://www.boost.org/LICENSE_1_0.txt)
&lt;span class="gu"&gt;@@ -105,6 +107,100 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    }

&lt;span class="w"&gt; &lt;/span&gt;    /*
&lt;span class="gi"&gt;+     Generate and evaluate the series expansion of the following integral&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+     I2 = integrate( 1/sqrt(1+k2*sin(sigma1)^2), sigma1, 0, sigma )&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+     which is valid for k2 small. We substitute k2 = 4 * eps / (1 - eps)^2&lt;/span&gt;
&lt;span class="gi"&gt;+     and expand (1 - eps) * I2 retaining terms up to order eps^maxpow&lt;/span&gt;
&lt;span class="gi"&gt;+     in A2 and C2[l].&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+     The resulting series is of the form&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+     A2 * ( sigma + sum(C2[l] * sin(2*l*sigma), l, 1, maxpow) )&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+     The scale factor A2-1 = mean value of (d/dsigma)2 - 1&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+     The expansion above is performed in Maxima, a Computer Algebra System.&lt;/span&gt;
&lt;span class="gi"&gt;+     The C++ code (that yields the function evaluate_series_A1 below) is&lt;/span&gt;
&lt;span class="gi"&gt;+     generated by the following Maxima script and is based on script:&lt;/span&gt;
&lt;span class="gi"&gt;+     http://geographiclib.sourceforge.net/html/geod.mac&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        // Maxima script begin&lt;/span&gt;
&lt;span class="gi"&gt;+        taylordepth:5$&lt;/span&gt;
&lt;span class="gi"&gt;+        ataylor(expr,var,ord):=expand(ratdisrep(taylor(expr,var,0,ord)))$&lt;/span&gt;
&lt;span class="gi"&gt;+        jtaylor(expr,var1,var2,ord):=block([zz],expand(subst([zz=1],&lt;/span&gt;
&lt;span class="gi"&gt;+        ratdisrep(taylor(subst([var1=zz*var1,var2=zz*var2],expr),zz,0,ord)))))$&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        computeI2(maxpow):=block([sintegrand,sintegrandexp,s,sigma,tau1,k2,eps],&lt;/span&gt;
&lt;span class="gi"&gt;+          sintegrand:1/sqrt(1+k2*sin(sigma)^2),&lt;/span&gt;
&lt;span class="gi"&gt;+          sintegrandexp:ataylor(&lt;/span&gt;
&lt;span class="gi"&gt;+              (1+eps)*subst([k2=4*eps/(1-eps)^2],sintegrand),&lt;/span&gt;
&lt;span class="gi"&gt;+              eps,maxpow),&lt;/span&gt;
&lt;span class="gi"&gt;+          s:trigreduce(integrate(sintegrandexp,sigma)),&lt;/span&gt;
&lt;span class="gi"&gt;+          s:s-subst(sigma=0,s),&lt;/span&gt;
&lt;span class="gi"&gt;+          A2:expand(subst(sigma=2*%pi,s)/(2*%pi)),&lt;/span&gt;
&lt;span class="gi"&gt;+          tau1:ataylor(s/A2,eps,maxpow),&lt;/span&gt;
&lt;span class="gi"&gt;+          for i:1 thru maxpow do C2[i]:coeff(tau1,sin(2*i*sigma)),&lt;/span&gt;
&lt;span class="gi"&gt;+          if expand(tau1-sigma-sum(C2[i]*sin(2*i*sigma),i,1,maxpow)) # 0&lt;/span&gt;
&lt;span class="gi"&gt;+          then error(&amp;quot;left over terms in B2&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+          A2:A2/(1+eps),&lt;/span&gt;
&lt;span class="gi"&gt;+          &amp;#39;done)$&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        codeA2(maxpow):=block([tab2:&amp;quot;    &amp;quot;,tab3:&amp;quot;        &amp;quot;],&lt;/span&gt;
&lt;span class="gi"&gt;+        print(&amp;quot;// The scale factor A2-1 = mean value of (d/dsigma)I2 - 1&lt;/span&gt;
&lt;span class="gi"&gt;+        CT evaluate_series_A2(CT const&amp;amp; eps)&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            CT const eps2 = math::sqr(eps);&lt;/span&gt;
&lt;span class="gi"&gt;+            CT t;&lt;/span&gt;
&lt;span class="gi"&gt;+            switch (SeriesOrder/2) {&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+          for n:0 thru entier(maxpow/2) do block([&lt;/span&gt;
&lt;span class="gi"&gt;+            q:horner(ataylor(subst([eps=sqrt(eps2)],A2*(1+eps)-1),eps2,n)),&lt;/span&gt;
&lt;span class="gi"&gt;+            linel:1200],&lt;/span&gt;
&lt;span class="gi"&gt;+            print(concat(tab2,&amp;quot;case &amp;quot;,string(n),&amp;quot;:&amp;quot;)),&lt;/span&gt;
&lt;span class="gi"&gt;+            print(concat(tab3,&amp;quot;t = &amp;quot;,string(q),&amp;quot;;&amp;quot;)),&lt;/span&gt;
&lt;span class="gi"&gt;+            print(concat(tab3,&amp;quot;break;&amp;quot;))),&lt;/span&gt;
&lt;span class="gi"&gt;+          print(&amp;quot;    }&lt;/span&gt;
&lt;span class="gi"&gt;+            return (t - eps) / (1 + eps);&lt;/span&gt;
&lt;span class="gi"&gt;+        }&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;#39;done)$&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        maxpow:8$&lt;/span&gt;
&lt;span class="gi"&gt;+        computeI2(maxpow)$&lt;/span&gt;
&lt;span class="gi"&gt;+        codeA2(maxpow)$&lt;/span&gt;
&lt;span class="gi"&gt;+        // Maxima script end&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+     To replace each number x by CT(x) the following&lt;/span&gt;
&lt;span class="gi"&gt;+     script can be used:&lt;/span&gt;
&lt;span class="gi"&gt;+       sed -e &amp;#39;s/[0-9]\+/CT(&amp;amp;)/g; s/\[CT/\[/g; s/)\]/\]/g;&lt;/span&gt;
&lt;span class="gi"&gt;+               s/case\sCT(/case /g; s/):/:/g; s/epsCT(2)/eps2/g;&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+    */&lt;/span&gt;
&lt;span class="gi"&gt;+    template &amp;lt;typename CT, std::size_t SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    CT evaluate_series_A2(CT const&amp;amp; eps)&lt;/span&gt;
&lt;span class="gi"&gt;+    {&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const eps2 = math::sqr(eps);&lt;/span&gt;
&lt;span class="gi"&gt;+        CT t;&lt;/span&gt;
&lt;span class="gi"&gt;+        switch (SeriesOrder/2) {&lt;/span&gt;
&lt;span class="gi"&gt;+        case 0:&lt;/span&gt;
&lt;span class="gi"&gt;+            t = CT(0);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 1:&lt;/span&gt;
&lt;span class="gi"&gt;+            t = -CT(3)*eps2/CT(4);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 2:&lt;/span&gt;
&lt;span class="gi"&gt;+            t = (-CT(7)*eps2-CT(48))*eps2/CT(64);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 3:&lt;/span&gt;
&lt;span class="gi"&gt;+            t = eps2*((-CT(11)*eps2-CT(28))*eps2-CT(192))/CT(256);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 4:&lt;/span&gt;
&lt;span class="gi"&gt;+            t = eps2*(eps2*((-CT(375)*eps2-CT(704))*eps2-CT(1792))-CT(12288))/CT(16384);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+        return (t - eps) / (CT(1) + eps);&lt;/span&gt;
&lt;span class="gi"&gt;+    }&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    /*&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;     Express

&lt;span class="w"&gt; &lt;/span&gt;        I3 = integrate( (2-f)/(1+(1-f)*sqrt(1+k2*sin(sigma1)^2)), sigma1, 0, sigma )
&lt;span class="gu"&gt;@@ -533,6 +629,156 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    }

&lt;span class="w"&gt; &lt;/span&gt;    /*
&lt;span class="gi"&gt;+     The coefficients C2[l] in the Fourier expansion of B2.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+     The expansion below is performed in Maxima, a Computer Algebra System.&lt;/span&gt;
&lt;span class="gi"&gt;+     The C++ code (that yields the function evaluate_series_A1 below) is&lt;/span&gt;
&lt;span class="gi"&gt;+     generated by the following Maxima script and is based on script:&lt;/span&gt;
&lt;span class="gi"&gt;+     http://geographiclib.sourceforge.net/html/geod.mac&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        // Maxima script begin&lt;/span&gt;
&lt;span class="gi"&gt;+        taylordepth:5$&lt;/span&gt;
&lt;span class="gi"&gt;+        ataylor(expr,var,ord):=expand(ratdisrep(taylor(expr,var,0,ord)))$&lt;/span&gt;
&lt;span class="gi"&gt;+        jtaylor(expr,var1,var2,ord):=block([zz],expand(subst([zz=1],&lt;/span&gt;
&lt;span class="gi"&gt;+        ratdisrep(taylor(subst([var1=zz*var1,var2=zz*var2],expr),zz,0,ord)))))$&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        computeI2(maxpow):=block([sintegrand,sintegrandexp,s,sigma,tau1,k2,eps],&lt;/span&gt;
&lt;span class="gi"&gt;+          sintegrand:1/sqrt(1+k2*sin(sigma)^2),&lt;/span&gt;
&lt;span class="gi"&gt;+          sintegrandexp:ataylor(&lt;/span&gt;
&lt;span class="gi"&gt;+              (1+eps)*subst([k2=4*eps/(1-eps)^2],sintegrand),&lt;/span&gt;
&lt;span class="gi"&gt;+              eps,maxpow),&lt;/span&gt;
&lt;span class="gi"&gt;+          s:trigreduce(integrate(sintegrandexp,sigma)),&lt;/span&gt;
&lt;span class="gi"&gt;+          s:s-subst(sigma=0,s),&lt;/span&gt;
&lt;span class="gi"&gt;+          A2:expand(subst(sigma=2*%pi,s)/(2*%pi)),&lt;/span&gt;
&lt;span class="gi"&gt;+          tau1:ataylor(s/A2,eps,maxpow),&lt;/span&gt;
&lt;span class="gi"&gt;+          for i:1 thru maxpow do C2[i]:coeff(tau1,sin(2*i*sigma)),&lt;/span&gt;
&lt;span class="gi"&gt;+          if expand(tau1-sigma-sum(C2[i]*sin(2*i*sigma),i,1,maxpow)) # 0&lt;/span&gt;
&lt;span class="gi"&gt;+          then error(&amp;quot;left over terms in B2&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+          A2:A2/(1+eps),&lt;/span&gt;
&lt;span class="gi"&gt;+          &amp;#39;done)$&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        codeC2(maxpow):=block([tab2:&amp;quot;    &amp;quot;,tab3:&amp;quot;        &amp;quot;],&lt;/span&gt;
&lt;span class="gi"&gt;+        print(&amp;quot;// The coefficients C2[l] in the Fourier expansion of B2&lt;/span&gt;
&lt;span class="gi"&gt;+        static inline void evaluate_coeffs_C2(CT const&amp;amp; eps, CT c[])&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            CT const eps2 = math::sqr(eps);&lt;/span&gt;
&lt;span class="gi"&gt;+            CT d = eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            switch (SeriesOrder) {&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+          for n:0 thru maxpow do (&lt;/span&gt;
&lt;span class="gi"&gt;+            print(concat(tab2,&amp;quot;case &amp;quot;,string(n),&amp;quot;:&amp;quot;)),&lt;/span&gt;
&lt;span class="gi"&gt;+            for m:1 thru n do block([q:d*horner(&lt;/span&gt;
&lt;span class="gi"&gt;+                subst([eps=sqrt(eps2)],ataylor(C2[m],eps,n)/eps^m)),&lt;/span&gt;
&lt;span class="gi"&gt;+              linel:1200],&lt;/span&gt;
&lt;span class="gi"&gt;+              if m&amp;gt;1 then print(concat(tab3,&amp;quot;d *= eps;&amp;quot;)),&lt;/span&gt;
&lt;span class="gi"&gt;+              print(concat(tab3,&amp;quot;c[&amp;quot;,string(m),&amp;quot;] = &amp;quot;,string(q),&amp;quot;;&amp;quot;))),&lt;/span&gt;
&lt;span class="gi"&gt;+            print(concat(tab3,&amp;quot;break;&amp;quot;))),&lt;/span&gt;
&lt;span class="gi"&gt;+        print(&amp;quot;    }&lt;/span&gt;
&lt;span class="gi"&gt;+        }&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;#39;done)$&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        maxpow:8$&lt;/span&gt;
&lt;span class="gi"&gt;+        computeI2(maxpow)$&lt;/span&gt;
&lt;span class="gi"&gt;+        codeC2(maxpow)$&lt;/span&gt;
&lt;span class="gi"&gt;+        // Maxima script end&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+     To replace each number x by CT(x) the following&lt;/span&gt;
&lt;span class="gi"&gt;+     script can be used:&lt;/span&gt;
&lt;span class="gi"&gt;+       sed -e &amp;#39;s/[0-9]\+/CT(&amp;amp;)/g; s/\[CT(/\[/g; s/)\]/\]/g;&lt;/span&gt;
&lt;span class="gi"&gt;+               s/case\sCT(/case /g; s/):/:/g; s/epsCT(2)/eps2/g;&lt;/span&gt;
&lt;span class="gi"&gt;+               s/eps(CT(2))/eps2/g;&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+    */&lt;/span&gt;
&lt;span class="gi"&gt;+    template &amp;lt;typename CT, std::size_t SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    static inline void evaluate_coeffs_C2(CT const&amp;amp; eps, CT c[])&lt;/span&gt;
&lt;span class="gi"&gt;+    {&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const eps2 = math::sqr(eps);&lt;/span&gt;
&lt;span class="gi"&gt;+        CT d = eps;&lt;/span&gt;
&lt;span class="gi"&gt;+        switch (SeriesOrder) {&lt;/span&gt;
&lt;span class="gi"&gt;+        case 0:&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 1:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = d/CT(2);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 2:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = d/CT(2);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[2] = CT(3)*d/CT(16);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 3:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = d*(eps2+CT(8))/CT(16);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[2] = CT(3)*d/CT(16);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[3] = CT(5)*d/CT(48);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 4:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = d*(eps2+CT(8))/CT(16);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[2] = d*(eps2+CT(6))/CT(32);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[3] = CT(5)*d/CT(48);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[4] = CT(35)*d/CT(512);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 5:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = d*(eps2*(eps2+CT(2))+CT(16))/CT(32);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[2] = d*(eps2+CT(6))/CT(32);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[3] = d*(CT(15)*eps2+CT(80))/CT(768);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[4] = CT(35)*d/CT(512);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[5] = CT(63)*d/CT(1280);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 6:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = d*(eps2*(eps2+CT(2))+CT(16))/CT(32);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[2] = d*(eps2*(CT(35)*eps2+CT(64))+CT(384))/CT(2048);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[3] = d*(CT(15)*eps2+CT(80))/CT(768);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[4] = d*(CT(7)*eps2+CT(35))/CT(512);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[5] = CT(63)*d/CT(1280);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[6] = CT(77)*d/CT(2048);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 7:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = d*(eps2*(eps2*(CT(41)*eps2+CT(64))+CT(128))+CT(1024))/CT(2048);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[2] = d*(eps2*(CT(35)*eps2+CT(64))+CT(384))/CT(2048);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[3] = d*(eps2*(CT(69)*eps2+CT(120))+CT(640))/CT(6144);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[4] = d*(CT(7)*eps2+CT(35))/CT(512);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[5] = d*(CT(105)*eps2+CT(504))/CT(10240);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[6] = CT(77)*d/CT(2048);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[7] = CT(429)*d/CT(14336);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 8:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = d*(eps2*(eps2*(CT(41)*eps2+CT(64))+CT(128))+CT(1024))/CT(2048);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[2] = d*(eps2*(eps2*(CT(47)*eps2+CT(70))+CT(128))+CT(768))/CT(4096);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[3] = d*(eps2*(CT(69)*eps2+CT(120))+CT(640))/CT(6144);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[4] = d*(eps2*(CT(133)*eps2+CT(224))+CT(1120))/CT(16384);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[5] = d*(CT(105)*eps2+CT(504))/CT(10240);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[6] = d*(CT(33)*eps2+CT(154))/CT(4096);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[7] = CT(429)*d/CT(14336);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[8] = CT(6435)*d/CT(262144);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+    }&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    /*&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;     The coefficients C3[l] in the Fourier expansion of B3.

&lt;span class="w"&gt; &lt;/span&gt;     The expansion below is performed in Maxima, a Computer Algebra System.

commit 9e2c74afc8523374cc9801817b48e40bcc6f78c3
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Wed May 23 14:22:40 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [formulas] Compute the longitude for second point using the longitudinal difference

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/formulas/karney_direct.hpp b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 058febc..58ed326 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -106,6 +106,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT const one_minus_f = c1 - f;
&lt;span class="w"&gt; &lt;/span&gt;        CT const two_minus_f = c2 - f;

&lt;span class="gi"&gt;+        CT const n = f / two_minus_f;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT const e2 = f * two_minus_f;
&lt;span class="w"&gt; &lt;/span&gt;        CT const ep2 = e2 / math::sqr(one_minus_f);

&lt;span class="gu"&gt;@@ -165,15 +166,68 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT sin_sigma2 = sin_sigma1 * cos_sigma12 + cos_sigma1 * sin_sigma12;
&lt;span class="w"&gt; &lt;/span&gt;        CT cos_sigma2 = cos_sigma1 * cos_sigma12 - sin_sigma1 * sin_sigma12;

&lt;span class="gi"&gt;+        if (BOOST_GEOMETRY_CONDITION(CalcRevAzimuth))&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            CT sin_alpha2 = sin_alpha0;&lt;/span&gt;
&lt;span class="gi"&gt;+            CT cos_alpha2 = cos_alpha0 * cos_sigma2;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            result.reverse_azimuth = std::atan2(sin_alpha2, cos_alpha2);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            // Convert the angle to radians.&lt;/span&gt;
&lt;span class="gi"&gt;+            result.reverse_azimuth /= math::d2r&amp;lt;T&amp;gt;();&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        if (BOOST_GEOMETRY_CONDITION(CalcCoordinates))
&lt;span class="w"&gt; &lt;/span&gt;        {
&lt;span class="gi"&gt;+            // Find the latitude at the second point.&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            CT sin_beta2 = cos_alpha0 * sin_sigma2;
&lt;span class="w"&gt; &lt;/span&gt;            CT cos_beta2 = boost::math::hypot(sin_alpha0, cos_alpha0 * cos_sigma2);

&lt;span class="w"&gt; &lt;/span&gt;            result.lat2 = std::atan2(sin_beta2, one_minus_f * cos_beta2);

&lt;span class="gd"&gt;-            // Convert the angle to radians.&lt;/span&gt;
&lt;span class="gi"&gt;+            // Convert the coordinate to radians.&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            result.lat2 /= math::d2r&amp;lt;T&amp;gt;();
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            // Find the longitude at the second point.&lt;/span&gt;
&lt;span class="gi"&gt;+            CT sin_omega1 = sin_beta1 * sin_alpha0;&lt;/span&gt;
&lt;span class="gi"&gt;+            CT cos_omega1 = cos_beta1 * cos_alpha1;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            CT sin_omega2 = sin_alpha0 * sin_sigma2;&lt;/span&gt;
&lt;span class="gi"&gt;+            CT cos_omega2 = cos_sigma2;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            CT omega12 = std::atan2(sin_omega2 * cos_omega1 - cos_omega2 * sin_omega1,&lt;/span&gt;
&lt;span class="gi"&gt;+                                    cos_omega2 * cos_omega1 + sin_omega2 * sin_omega1);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            CT coeffs_A3[SeriesOrder];&lt;/span&gt;
&lt;span class="gi"&gt;+            series_expansion::evaluate_coeffs_A3&amp;lt;double, SeriesOrder&amp;gt;(n, coeffs_A3);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            CT A3 = math::horner_evaluate(epsilon, coeffs_A3, coeffs_A3 + SeriesOrder);&lt;/span&gt;
&lt;span class="gi"&gt;+            CT A3c = -f * sin_alpha0 * A3;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            // Compute the size of coefficient array.&lt;/span&gt;
&lt;span class="gi"&gt;+            const std::size_t coeffs_C3_size = (SeriesOrder * (SeriesOrder - 1)) / 2;&lt;/span&gt;
&lt;span class="gi"&gt;+            CT coeffs_C3x[coeffs_C3_size];&lt;/span&gt;
&lt;span class="gi"&gt;+            series_expansion::evaluate_coeffs_C3&amp;lt;double, SeriesOrder&amp;gt;(n, coeffs_C3x);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            // Evaluate C3 coefficients.&lt;/span&gt;
&lt;span class="gi"&gt;+            CT coeffs_C3[SeriesOrder];&lt;/span&gt;
&lt;span class="gi"&gt;+            math::evaluate_coeffs_var2&amp;lt;double, SeriesOrder&amp;gt;(epsilon, coeffs_C3x, coeffs_C3);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            CT B31 = sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C3);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            CT lam12 = omega12 + A3c *&lt;/span&gt;
&lt;span class="gi"&gt;+                       (sigma12 + (sin_cos_series(sin_sigma2,&lt;/span&gt;
&lt;span class="gi"&gt;+                                                  cos_sigma2,&lt;/span&gt;
&lt;span class="gi"&gt;+                                                  coeffs_C3) - B31));&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            // Convert to radians to get the longitudinal&lt;/span&gt;
&lt;span class="gi"&gt;+            // difference.&lt;/span&gt;
&lt;span class="gi"&gt;+            CT lon12 = lam12 / math::d2r&amp;lt;T&amp;gt;();&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            // Add the longitude at first point to the longitudinal&lt;/span&gt;
&lt;span class="gi"&gt;+            // difference and normalize the result.&lt;/span&gt;
&lt;span class="gi"&gt;+            result.lon2 = math::normalize_angle(math::normalize_angle(lon1) +&lt;/span&gt;
&lt;span class="gi"&gt;+                                                math::normalize_angle(lon12));&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="w"&gt; &lt;/span&gt;    }
&lt;span class="w"&gt; &lt;/span&gt;};

commit 915219198602320924af1c88f6db02b669233beb
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Wed May 23 12:32:21 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [util] Add functions to evaluate coefficients for A3 and C3 using series expansion

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/util/series_expansion.hpp b/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index f15b2d1..c637563 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -75,7 +75,7 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        // Maxima script end

&lt;span class="w"&gt; &lt;/span&gt;     To replace each number x by CT(x) the following
&lt;span class="gd"&gt;-     scirpt can be used:&lt;/span&gt;
&lt;span class="gi"&gt;+     script can be used:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;       sed -e &amp;#39;s/[0-9]\+/CT(&amp;amp;)/g; s/\[CT/\[/g; s/)\]/\]/g;
&lt;span class="w"&gt; &lt;/span&gt;               s/case\sCT(/case /g; s/):/:/g; s/epsCT(2)/eps2/g;&amp;#39;
&lt;span class="w"&gt; &lt;/span&gt;    */
&lt;span class="gu"&gt;@@ -105,6 +105,138 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    }

&lt;span class="w"&gt; &lt;/span&gt;    /*
&lt;span class="gi"&gt;+     Express&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        I3 = integrate( (2-f)/(1+(1-f)*sqrt(1+k2*sin(sigma1)^2)), sigma1, 0, sigma )&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+     as a series&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        A3 * ( sigma + sum(C3[l] * sin(2*l*sigma), l, 1, maxpow-1) )&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+     valid for f and k2 small.  It is convenient to write k2 = 4 * eps / (1 -&lt;/span&gt;
&lt;span class="gi"&gt;+     eps)^2 and f = 2*n/(1+n) and expand in eps and n.  This procedure leads&lt;/span&gt;
&lt;span class="gi"&gt;+     to a series where the coefficients of eps^j are terminating series in n.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+     The scale factor A3 = mean value of (d/dsigma)I3&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+     The expansion above is performed in Maxima, a Computer Algebra System.&lt;/span&gt;
&lt;span class="gi"&gt;+     The C++ code (that yields the function evaluate_series_A1 below) is&lt;/span&gt;
&lt;span class="gi"&gt;+     generated by the following Maxima script and is based on script:&lt;/span&gt;
&lt;span class="gi"&gt;+     http://geographiclib.sourceforge.net/html/geod.mac&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        // Maxima script begin&lt;/span&gt;
&lt;span class="gi"&gt;+        taylordepth:5$&lt;/span&gt;
&lt;span class="gi"&gt;+        ataylor(expr,var,ord):=expand(ratdisrep(taylor(expr,var,0,ord)))$&lt;/span&gt;
&lt;span class="gi"&gt;+        jtaylor(expr,var1,var2,ord):=block([zz],expand(subst([zz=1],&lt;/span&gt;
&lt;span class="gi"&gt;+        ratdisrep(taylor(subst([var1=zz*var1,var2=zz*var2],expr),zz,0,ord)))))$&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        computeI3(maxpow):=block([int,intexp,dlam,eta,del,eps,nu,f,z,n],&lt;/span&gt;
&lt;span class="gi"&gt;+          maxpow:maxpow-1,&lt;/span&gt;
&lt;span class="gi"&gt;+          int:subst([k2=4*eps/(1-eps)^2],&lt;/span&gt;
&lt;span class="gi"&gt;+            (2-f)/(1+(1-f)*sqrt(1+k2*sin(sigma)^2))),&lt;/span&gt;
&lt;span class="gi"&gt;+          int:subst([f=2*n/(1+n)],int),&lt;/span&gt;
&lt;span class="gi"&gt;+          intexp:jtaylor(int,n,eps,maxpow),&lt;/span&gt;
&lt;span class="gi"&gt;+          dlam:trigreduce(integrate(intexp,sigma)),&lt;/span&gt;
&lt;span class="gi"&gt;+          dlam:dlam-subst(sigma=0,dlam),&lt;/span&gt;
&lt;span class="gi"&gt;+          A3:expand(subst(sigma=2*%pi,dlam)/(2*%pi)),&lt;/span&gt;
&lt;span class="gi"&gt;+          eta:jtaylor(dlam/A3,n,eps,maxpow),&lt;/span&gt;
&lt;span class="gi"&gt;+          A3:jtaylor(A3,n,eps,maxpow),&lt;/span&gt;
&lt;span class="gi"&gt;+          for i:1 thru maxpow do C3[i]:coeff(eta,sin(2*i*sigma)),&lt;/span&gt;
&lt;span class="gi"&gt;+          if expand(eta-sigma-sum(C3[i]*sin(2*i*sigma),i,1,maxpow)) # 0&lt;/span&gt;
&lt;span class="gi"&gt;+          then error(&amp;quot;left over terms in B3&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+          &amp;#39;done)$&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        codeA3(maxpow):=block([tab2:&amp;quot;    &amp;quot;,tab3:&amp;quot;        &amp;quot;],&lt;/span&gt;
&lt;span class="gi"&gt;+        print(&amp;quot;// The scale factor A3 = mean value of (d/dsigma)I3&lt;/span&gt;
&lt;span class="gi"&gt;+        static inline void evaluate_series_A3(CT const&amp;amp; n, CT c[])&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            switch (SeriesOrder) {&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+          for nn:0 thru maxpow do block(&lt;/span&gt;
&lt;span class="gi"&gt;+            [q:if nn=0 then 0 else&lt;/span&gt;
&lt;span class="gi"&gt;+            jtaylor(subst([n=n],A3),n,eps,nn-1),&lt;/span&gt;
&lt;span class="gi"&gt;+            linel:1200],&lt;/span&gt;
&lt;span class="gi"&gt;+            print(concat(tab2,&amp;quot;case &amp;quot;,string(nn),&amp;quot;:&amp;quot;)),&lt;/span&gt;
&lt;span class="gi"&gt;+            for i : 0 thru nn-1 do&lt;/span&gt;
&lt;span class="gi"&gt;+            print(concat(tab3,&amp;quot;c[&amp;quot;,i,&amp;quot;] = &amp;quot;,&lt;/span&gt;
&lt;span class="gi"&gt;+                string(horner(coeff(q,eps,i))),&amp;quot;;&amp;quot;)),&lt;/span&gt;
&lt;span class="gi"&gt;+            print(concat(tab3,&amp;quot;break;&amp;quot;))),&lt;/span&gt;
&lt;span class="gi"&gt;+          print(&amp;quot;    }&lt;/span&gt;
&lt;span class="gi"&gt;+        }&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;#39;done)$&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        maxpow:8$&lt;/span&gt;
&lt;span class="gi"&gt;+        computeI3(maxpow)$&lt;/span&gt;
&lt;span class="gi"&gt;+        codeA3(maxpow)$&lt;/span&gt;
&lt;span class="gi"&gt;+        // Maxima script end&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+     To replace each number x by CT(x) the following&lt;/span&gt;
&lt;span class="gi"&gt;+     script can be used:&lt;/span&gt;
&lt;span class="gi"&gt;+       sed -e &amp;#39;s/[0-9]\+/CT(&amp;amp;)/g; s/\[CT(/\[/g; s/)\]/\]/g;&lt;/span&gt;
&lt;span class="gi"&gt;+               s/case\sCT(/case /g; s/):/:/g&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+    */&lt;/span&gt;
&lt;span class="gi"&gt;+    // TODO: this produces different results that geographiclib&lt;/span&gt;
&lt;span class="gi"&gt;+    template &amp;lt;typename CT, std::size_t SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    static inline void evaluate_coeffs_A3(CT const&amp;amp; n, CT c[])&lt;/span&gt;
&lt;span class="gi"&gt;+    {&lt;/span&gt;
&lt;span class="gi"&gt;+        switch (SeriesOrder) {&lt;/span&gt;
&lt;span class="gi"&gt;+        case 0:&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 1:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[0] = CT(1);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 2:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[0] = CT(1);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = -CT(1)/CT(2);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 3:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[0] = CT(1);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = (n-CT(1))/CT(2);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[2] = -CT(1)/CT(4);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 4:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[0] = CT(1);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = (n-CT(1))/CT(2);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[2] = (-n-CT(2))/CT(8);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[3] = -CT(1)/CT(16);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 5:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[0] = CT(1);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = (n-CT(1))/CT(2);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[2] = (n*(CT(3)*n-CT(1))-CT(2))/CT(8);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[3] = (-CT(3)*n-CT(1))/CT(16);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[4] = -CT(3)/CT(64);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 6:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[0] = CT(1);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = (n-CT(1))/CT(2);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[2] = (n*(CT(3)*n-CT(1))-CT(2))/CT(8);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[3] = ((-n-CT(3))*n-CT(1))/CT(16);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[4] = (-CT(2)*n-CT(3))/CT(64);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[5] = -CT(3)/CT(128);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 7:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[0] = CT(1);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = (n-CT(1))/CT(2);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[2] = (n*(CT(3)*n-CT(1))-CT(2))/CT(8);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[3] = (n*(n*(CT(5)*n-CT(1))-CT(3))-CT(1))/CT(16);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[4] = ((-CT(10)*n-CT(2))*n-CT(3))/CT(64);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[5] = (-CT(5)*n-CT(3))/CT(128);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[6] = -CT(5)/CT(256);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 8:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[0] = CT(1);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = (n-CT(1))/CT(2);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[2] = (n*(CT(3)*n-CT(1))-CT(2))/CT(8);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[3] = (n*(n*(CT(5)*n-CT(1))-CT(3))-CT(1))/CT(16);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[4] = (n*((-CT(5)*n-CT(20))*n-CT(4))-CT(6))/CT(128);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[5] = ((-CT(5)*n-CT(10))*n-CT(6))/CT(256);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[6] = (-CT(15)*n-CT(20))/CT(1024);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[7] = -CT(25)/CT(2048);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+    }&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    /*&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;     The coefficients C1[l] in the Fourier expansion of B1.

&lt;span class="w"&gt; &lt;/span&gt;     The expansion below is performed in Maxima, a Computer Algebra System.
&lt;span class="gu"&gt;@@ -142,7 +274,7 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        // Maxima script end

&lt;span class="w"&gt; &lt;/span&gt;     To replace each number x by CT(x) the following
&lt;span class="gd"&gt;-     scirpt can be used:&lt;/span&gt;
&lt;span class="gi"&gt;+     script can be used:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;       sed -e &amp;#39;s/[0-9]\+/CT(&amp;amp;)/g; s/\[CT(/\[/g; s/)\]/\]/g;
&lt;span class="w"&gt; &lt;/span&gt;               s/case\sCT(/case /g; s/):/:/g; s/epsCT(2)/eps2/g;
&lt;span class="w"&gt; &lt;/span&gt;               s/eps(CT(2))/eps2/g;&amp;#39;
&lt;span class="gu"&gt;@@ -304,7 +436,7 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        // Maxima script end

&lt;span class="w"&gt; &lt;/span&gt;     To replace each number x by CT(x) the following
&lt;span class="gd"&gt;-     scirpt can be used:&lt;/span&gt;
&lt;span class="gi"&gt;+     script can be used:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;       sed -e &amp;#39;s/[0-9]\+/CT(&amp;amp;)/g; s/\[CT(/\[/g; s/)\]/\]/g;
&lt;span class="w"&gt; &lt;/span&gt;               s/case\sCT(/case /g; s/):/:/g; s/epsCT(2)/eps2/g;
&lt;span class="w"&gt; &lt;/span&gt;               s/eps(CT(2))/eps2/g;&amp;#39;
&lt;span class="gu"&gt;@@ -400,6 +532,180 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="w"&gt; &lt;/span&gt;    }

&lt;span class="gi"&gt;+    /*&lt;/span&gt;
&lt;span class="gi"&gt;+     The coefficients C3[l] in the Fourier expansion of B3.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+     The expansion below is performed in Maxima, a Computer Algebra System.&lt;/span&gt;
&lt;span class="gi"&gt;+     The C++ code (that yields the function evaluate_series_A1 below) is&lt;/span&gt;
&lt;span class="gi"&gt;+     generated by the following Maxima script and is based on script:&lt;/span&gt;
&lt;span class="gi"&gt;+     http://geographiclib.sourceforge.net/html/geod.mac&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        // Maxima script begin&lt;/span&gt;
&lt;span class="gi"&gt;+        taylordepth:5$&lt;/span&gt;
&lt;span class="gi"&gt;+        ataylor(expr,var,ord):=expand(ratdisrep(taylor(expr,var,0,ord)))$&lt;/span&gt;
&lt;span class="gi"&gt;+        jtaylor(expr,var1,var2,ord):=block([zz],expand(subst([zz=1],&lt;/span&gt;
&lt;span class="gi"&gt;+        ratdisrep(taylor(subst([var1=zz*var1,var2=zz*var2],expr),zz,0,ord)))))$&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        computeI3(maxpow):=block([int,intexp,dlam,eta,del,eps,nu,f,z,n],&lt;/span&gt;
&lt;span class="gi"&gt;+          maxpow:maxpow-1,&lt;/span&gt;
&lt;span class="gi"&gt;+          int:subst([k2=4*eps/(1-eps)^2],&lt;/span&gt;
&lt;span class="gi"&gt;+            (2-f)/(1+(1-f)*sqrt(1+k2*sin(sigma)^2))),&lt;/span&gt;
&lt;span class="gi"&gt;+          int:subst([f=2*n/(1+n)],int),&lt;/span&gt;
&lt;span class="gi"&gt;+          intexp:jtaylor(int,n,eps,maxpow),&lt;/span&gt;
&lt;span class="gi"&gt;+          dlam:trigreduce(integrate(intexp,sigma)),&lt;/span&gt;
&lt;span class="gi"&gt;+          dlam:dlam-subst(sigma=0,dlam),&lt;/span&gt;
&lt;span class="gi"&gt;+          A3:expand(subst(sigma=2*%pi,dlam)/(2*%pi)),&lt;/span&gt;
&lt;span class="gi"&gt;+          eta:jtaylor(dlam/A3,n,eps,maxpow),&lt;/span&gt;
&lt;span class="gi"&gt;+          A3:jtaylor(A3,n,eps,maxpow),&lt;/span&gt;
&lt;span class="gi"&gt;+          for i:1 thru maxpow do C3[i]:coeff(eta,sin(2*i*sigma)),&lt;/span&gt;
&lt;span class="gi"&gt;+          if expand(eta-sigma-sum(C3[i]*sin(2*i*sigma),i,1,maxpow)) # 0&lt;/span&gt;
&lt;span class="gi"&gt;+          then error(&amp;quot;left over terms in B3&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+          &amp;#39;done)$&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        codeC3(maxpow):=block([tab2:&amp;quot;    &amp;quot;,tab3:&amp;quot;        &amp;quot;],&lt;/span&gt;
&lt;span class="gi"&gt;+        print(&amp;quot;// The coefficients C3[l] in the Fourier expansion of B3&lt;/span&gt;
&lt;span class="gi"&gt;+        static inline void evaluate_coeffs_C3(CT const&amp;amp; n, CT c[])&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            const CT n2 = math::sqr(n);&lt;/span&gt;
&lt;span class="gi"&gt;+            switch (SeriesOrder) {&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+          for nn:0 thru maxpow do block([c],&lt;/span&gt;
&lt;span class="gi"&gt;+            print(concat(tab2,&amp;quot;case &amp;quot;,string(nn),&amp;quot;:&amp;quot;)),&lt;/span&gt;
&lt;span class="gi"&gt;+            c:0,&lt;/span&gt;
&lt;span class="gi"&gt;+            for m:1 thru nn-1 do block(&lt;/span&gt;
&lt;span class="gi"&gt;+              [q:if nn = 0 then 0 else&lt;/span&gt;
&lt;span class="gi"&gt;+              jtaylor(subst([n=n],C3[m]),_n,eps,nn-1),&lt;/span&gt;
&lt;span class="gi"&gt;+              linel:1200],&lt;/span&gt;
&lt;span class="gi"&gt;+              for j:m thru nn-1 do (&lt;/span&gt;
&lt;span class="gi"&gt;+                print(concat(tab3,&amp;quot;c[&amp;quot;,c,&amp;quot;] = &amp;quot;,&lt;/span&gt;
&lt;span class="gi"&gt;+                    string(horner(coeff(q,eps,j))),&amp;quot;;&amp;quot;)),&lt;/span&gt;
&lt;span class="gi"&gt;+                c:c+1)&lt;/span&gt;
&lt;span class="gi"&gt;+            ),&lt;/span&gt;
&lt;span class="gi"&gt;+            print(concat(tab3,&amp;quot;break;&amp;quot;))),&lt;/span&gt;
&lt;span class="gi"&gt;+          print(&amp;quot;    }&lt;/span&gt;
&lt;span class="gi"&gt;+        }&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;#39;done)$&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        maxpow:8$&lt;/span&gt;
&lt;span class="gi"&gt;+        computeI3(maxpow)$&lt;/span&gt;
&lt;span class="gi"&gt;+        codeC3(maxpow)$&lt;/span&gt;
&lt;span class="gi"&gt;+        // Maxima script end&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+     To replace each number x by CT(x) the following&lt;/span&gt;
&lt;span class="gi"&gt;+     script can be used:&lt;/span&gt;
&lt;span class="gi"&gt;+       sed -e &amp;#39;s/[0-9]\+/CT(&amp;amp;)/g; s/\[CT(/\[/g; s/)\]/\]/g;&lt;/span&gt;
&lt;span class="gi"&gt;+               s/case\sCT(/case /g; s/):/:/g; s/epsCT(2)/eps2/g;&lt;/span&gt;
&lt;span class="gi"&gt;+               s/eps(CT(2))/eps2/g;&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+    */&lt;/span&gt;
&lt;span class="gi"&gt;+    template &amp;lt;typename CT, std::size_t SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    static inline void evaluate_coeffs_C3(CT const&amp;amp; n, CT c[])&lt;/span&gt;
&lt;span class="gi"&gt;+    {&lt;/span&gt;
&lt;span class="gi"&gt;+        const CT n2 = math::sqr(n);&lt;/span&gt;
&lt;span class="gi"&gt;+        switch (SeriesOrder) {&lt;/span&gt;
&lt;span class="gi"&gt;+        case 0:&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 1:&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 2:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[0] = (CT(1)-n)/CT(4);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 3:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[0] = (CT(1)-n)/CT(4);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = (CT(1)-n2)/CT(8);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[2] = ((n-CT(3))*n+CT(2))/CT(32);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 4:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[0] = (CT(1)-n)/CT(4);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = (CT(1)-n2)/CT(8);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[2] = (n*((-CT(5)*n-CT(1))*n+CT(3))+CT(3))/CT(64);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[3] = ((n-CT(3))*n+CT(2))/CT(32);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[4] = (n*(n*(CT(2)*n-CT(3))-CT(2))+CT(3))/CT(64);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[5] = (n*((CT(5)-n)*n-CT(9))+CT(5))/CT(192);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 5:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[0] = (CT(1)-n)/CT(4);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = (CT(1)-n2)/CT(8);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[2] = (n*((-CT(5)*n-CT(1))*n+CT(3))+CT(3))/CT(64);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[3] = (n*((CT(2)-CT(2)*n)*n+CT(2))+CT(5))/CT(128);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[4] = ((n-CT(3))*n+CT(2))/CT(32);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[5] = (n*(n*(CT(2)*n-CT(3))-CT(2))+CT(3))/CT(64);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[6] = (n*((-CT(6)*n-CT(9))*n+CT(2))+CT(6))/CT(256);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[7] = (n*((CT(5)-n)*n-CT(9))+CT(5))/CT(192);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[8] = (n*(n*(CT(10)*n-CT(6))-CT(10))+CT(9))/CT(384);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[9] = (n*((CT(20)-CT(7)*n)*n-CT(28))+CT(14))/CT(1024);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 6:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[0] = (CT(1)-n)/CT(4);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = (CT(1)-n2)/CT(8);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[2] = (n*((-CT(5)*n-CT(1))*n+CT(3))+CT(3))/CT(64);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[3] = (n*((CT(2)-CT(2)*n)*n+CT(2))+CT(5))/CT(128);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[4] = (n*(CT(3)*n+CT(11))+CT(12))/CT(512);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[5] = ((n-CT(3))*n+CT(2))/CT(32);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[6] = (n*(n*(CT(2)*n-CT(3))-CT(2))+CT(3))/CT(64);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[7] = (n*((-CT(6)*n-CT(9))*n+CT(2))+CT(6))/CT(256);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[8] = ((CT(1)-CT(2)*n)*n+CT(5))/CT(256);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[9] = (n*((CT(5)-n)*n-CT(9))+CT(5))/CT(192);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[10] = (n*(n*(CT(10)*n-CT(6))-CT(10))+CT(9))/CT(384);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[11] = ((-CT(77)*n-CT(8))*n+CT(42))/CT(3072);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[12] = (n*((CT(20)-CT(7)*n)*n-CT(28))+CT(14))/CT(1024);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[13] = ((-CT(7)*n-CT(40))*n+CT(28))/CT(2048);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[14] = (n*(CT(75)*n-CT(90))+CT(42))/CT(5120);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 7:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[0] = (CT(1)-n)/CT(4);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = (CT(1)-n2)/CT(8);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[2] = (n*((-CT(5)*n-CT(1))*n+CT(3))+CT(3))/CT(64);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[3] = (n*((CT(2)-CT(2)*n)*n+CT(2))+CT(5))/CT(128);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[4] = (n*(CT(3)*n+CT(11))+CT(12))/CT(512);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[5] = (CT(10)*n+CT(21))/CT(1024);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[6] = ((n-CT(3))*n+CT(2))/CT(32);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[7] = (n*(n*(CT(2)*n-CT(3))-CT(2))+CT(3))/CT(64);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[8] = (n*((-CT(6)*n-CT(9))*n+CT(2))+CT(6))/CT(256);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[9] = ((CT(1)-CT(2)*n)*n+CT(5))/CT(256);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[10] = (CT(69)*n+CT(108))/CT(8192);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[11] = (n*((CT(5)-n)*n-CT(9))+CT(5))/CT(192);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[12] = (n*(n*(CT(10)*n-CT(6))-CT(10))+CT(9))/CT(384);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[13] = ((-CT(77)*n-CT(8))*n+CT(42))/CT(3072);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[14] = (CT(12)-n)/CT(1024);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[15] = (n*((CT(20)-CT(7)*n)*n-CT(28))+CT(14))/CT(1024);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[16] = ((-CT(7)*n-CT(40))*n+CT(28))/CT(2048);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[17] = (CT(72)-CT(43)*n)/CT(8192);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[18] = (n*(CT(75)*n-CT(90))+CT(42))/CT(5120);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[19] = (CT(9)-CT(15)*n)/CT(1024);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[20] = (CT(44)-CT(99)*n)/CT(8192);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 8:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[0] = (CT(1)-n)/CT(4);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = (CT(1)-n2)/CT(8);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[2] = (n*((-CT(5)*n-CT(1))*n+CT(3))+CT(3))/CT(64);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[3] = (n*((CT(2)-CT(2)*n)*n+CT(2))+CT(5))/CT(128);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[4] = (n*(CT(3)*n+CT(11))+CT(12))/CT(512);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[5] = (CT(10)*n+CT(21))/CT(1024);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[6] = CT(243)/CT(16384);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[7] = ((n-CT(3))*n+CT(2))/CT(32);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[8] = (n*(n*(CT(2)*n-CT(3))-CT(2))+CT(3))/CT(64);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[9] = (n*((-CT(6)*n-CT(9))*n+CT(2))+CT(6))/CT(256);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[10] = ((CT(1)-CT(2)*n)*n+CT(5))/CT(256);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[11] = (CT(69)*n+CT(108))/CT(8192);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[12] = CT(187)/CT(16384);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[13] = (n*((CT(5)-n)*n-CT(9))+CT(5))/CT(192);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[14] = (n*(n*(CT(10)*n-CT(6))-CT(10))+CT(9))/CT(384);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[15] = ((-CT(77)*n-CT(8))*n+CT(42))/CT(3072);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[16] = (CT(12)-n)/CT(1024);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[17] = CT(139)/CT(16384);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[18] = (n*((CT(20)-CT(7)*n)*n-CT(28))+CT(14))/CT(1024);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[19] = ((-CT(7)*n-CT(40))*n+CT(28))/CT(2048);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[20] = (CT(72)-CT(43)*n)/CT(8192);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[21] = CT(127)/CT(16384);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[22] = (n*(CT(75)*n-CT(90))+CT(42))/CT(5120);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[23] = (CT(9)-CT(15)*n)/CT(1024);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[24] = CT(99)/CT(16384);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[25] = (CT(44)-CT(99)*n)/CT(8192);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[26] = CT(99)/CT(16384);&lt;/span&gt;
&lt;span class="gi"&gt;+            c[27] = CT(429)/CT(114688);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+    }&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;}}} // namespace boost::geometry::series_expansion

&lt;span class="w"&gt; &lt;/span&gt;#endif // BOOST_GEOMETRY_UTIL_SERIES_EXPANSION_HPP

commit 881c73c90b131525deb03a59a4350868be8ffbe2
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Tue May 22 17:18:37 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [formulas] Compute the latitude for second point following Karney&amp;#39;s method

&lt;span class="w"&gt; &lt;/span&gt;   - Link to paper: https://arxiv.org/pdf/1109.4448.pdf

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/formulas/karney_direct.hpp b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index c6fb03c..058febc 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -119,6 +119,7 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        sin_beta1 *= one_minus_f;

&lt;span class="w"&gt; &lt;/span&gt;        // Obtain alpha 0 by solving the spherical triangle.
&lt;span class="gi"&gt;+        CT sin_alpha0 = sin_alpha1 * cos_beta1;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT cos_alpha0 = boost::math::hypot(cos_alpha1, sin_alpha1 * sin_beta1);

&lt;span class="w"&gt; &lt;/span&gt;        CT k2 = math::sqr(cos_alpha0) * ep2;
&lt;span class="gu"&gt;@@ -134,11 +135,47 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        series_expansion::evaluate_coeffs_C1&amp;lt;CT, SeriesOrder&amp;gt;(epsilon, coeffs_C1);

&lt;span class="w"&gt; &lt;/span&gt;        // Tau is an integration variable.
&lt;span class="gd"&gt;-        CT tau12 = distance / (b + (c1 + expansion_A1));&lt;/span&gt;
&lt;span class="gi"&gt;+        CT tau12 = distance / (b * (c1 + expansion_A1));&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT sin_tau12 = sin(tau12);
&lt;span class="w"&gt; &lt;/span&gt;        CT cos_tau12 = cos(tau12);
&lt;span class="gd"&gt;-    }&lt;/span&gt;

&lt;span class="gi"&gt;+        CT sin_sigma1 = sin_beta1;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT cos_sigma1 = cos_beta1 * cos_alpha1;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        CT B11 = sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C1);&lt;/span&gt;
&lt;span class="gi"&gt;+        CT sin_B11 = sin(B11);&lt;/span&gt;
&lt;span class="gi"&gt;+        CT cos_B11 = cos(B11);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        CT sin_tau1 = sin_sigma1 * cos_B11 + cos_sigma1 * sin_B11;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT cos_tau1 = cos_sigma1 * cos_B11 - sin_sigma1 * sin_B11;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        // Index zero element of coeffs_C1p is unused.&lt;/span&gt;
&lt;span class="gi"&gt;+        CT coeffs_C1p[SeriesOrder + 1];&lt;/span&gt;
&lt;span class="gi"&gt;+        series_expansion::evaluate_coeffs_C1p&amp;lt;CT, SeriesOrder&amp;gt;(epsilon, coeffs_C1p);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        CT B12 = sin_cos_series(sin_tau1 * cos_tau12 + cos_tau1 * sin_tau12,&lt;/span&gt;
&lt;span class="gi"&gt;+                                cos_tau1 * cos_tau12 - sin_tau1 * sin_tau12,&lt;/span&gt;
&lt;span class="gi"&gt;+                                coeffs_C1p); // &amp;lt; 0?&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        CT sigma12 = tau12 - (B12 - B11);&lt;/span&gt;
&lt;span class="gi"&gt;+        CT sin_sigma12 = sin(sigma12);&lt;/span&gt;
&lt;span class="gi"&gt;+        CT cos_sigma12 = cos(sigma12);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        CT sin_sigma2 = sin_sigma1 * cos_sigma12 + cos_sigma1 * sin_sigma12;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT cos_sigma2 = cos_sigma1 * cos_sigma12 - sin_sigma1 * sin_sigma12;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        if (BOOST_GEOMETRY_CONDITION(CalcCoordinates))&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            CT sin_beta2 = cos_alpha0 * sin_sigma2;&lt;/span&gt;
&lt;span class="gi"&gt;+            CT cos_beta2 = boost::math::hypot(sin_alpha0, cos_alpha0 * cos_sigma2);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            result.lat2 = std::atan2(sin_beta2, one_minus_f * cos_beta2);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            // Convert the angle to radians.&lt;/span&gt;
&lt;span class="gi"&gt;+            result.lat2 /= math::d2r&amp;lt;T&amp;gt;();&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+    }&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;};

&lt;span class="w"&gt; &lt;/span&gt;}}} // namespace boost::geometry::formula

commit 6ce1b099cb33e94d5577a089460af4310962a964
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Mon May 21 16:32:28 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [util] Evaluate coefficients for C1p using series expansion

&lt;span class="w"&gt; &lt;/span&gt;   - Fix conversion from degree to radian in sin_cos_degrees function

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/util/math.hpp b/include/boost/geometry/util/math.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 4d927b3..df7507e 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/util/math.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/util/math.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -790,7 +790,7 @@ inline void sin_cos_degrees(T const&amp;amp; x, T &amp;amp; sinx, T &amp;amp; cosx)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    remainder -= 90 * quotient;

&lt;span class="w"&gt; &lt;/span&gt;    // Convert to radians.
&lt;span class="gd"&gt;-    remainder = as_radian&amp;lt;T&amp;gt;(remainder);&lt;/span&gt;
&lt;span class="gi"&gt;+    remainder *= d2r&amp;lt;T&amp;gt;();&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    T s = std::sin(remainder), c = std::cos(remainder);

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/util/series_expansion.hpp b/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index b2e2277..f15b2d1 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -105,7 +105,7 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    }

&lt;span class="w"&gt; &lt;/span&gt;    /*
&lt;span class="gd"&gt;-     The coefficients C1[l] in the Fourier expansion of B1&lt;/span&gt;
&lt;span class="gi"&gt;+     The coefficients C1[l] in the Fourier expansion of B1.&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;     The expansion below is performed in Maxima, a Computer Algebra System.
&lt;span class="w"&gt; &lt;/span&gt;     The C++ code (that yields the function evaluate_series_A1 below) is
&lt;span class="gu"&gt;@@ -238,6 +238,168 @@ namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="w"&gt; &lt;/span&gt;    }

&lt;span class="gd"&gt;-}}} // namespace boost::geometry::formula&lt;/span&gt;
&lt;span class="gi"&gt;+    /*&lt;/span&gt;
&lt;span class="gi"&gt;+     The coefficients C1p[l] in the Fourier expansion of B1p.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+     The expansion below is performed in Maxima, a Computer Algebra System.&lt;/span&gt;
&lt;span class="gi"&gt;+     The C++ code (that yields the function evaluate_series_A1 below) is&lt;/span&gt;
&lt;span class="gi"&gt;+     generated by the following Maxima script and is based on script:&lt;/span&gt;
&lt;span class="gi"&gt;+     http://geographiclib.sourceforge.net/html/geod.mac&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        // Maxima script begin&lt;/span&gt;
&lt;span class="gi"&gt;+        taylordepth:5$&lt;/span&gt;
&lt;span class="gi"&gt;+        ataylor(expr,var,ord):=expand(ratdisrep(taylor(expr,var,0,ord)))$&lt;/span&gt;
&lt;span class="gi"&gt;+        jtaylor(expr,var1,var2,ord):=block([zz],expand(subst([zz=1],&lt;/span&gt;
&lt;span class="gi"&gt;+        ratdisrep(taylor(subst([var1=zz*var1,var2=zz*var2],expr),zz,0,ord)))))$&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        computeintegral(maxpow):=block([sintegrand,sintegrandexp,s,sigma,tau1,k2,eps],&lt;/span&gt;
&lt;span class="gi"&gt;+          sintegrand:sqrt(1+k2*sin(sigma)^2),&lt;/span&gt;
&lt;span class="gi"&gt;+          sintegrandexp:ataylor(&lt;/span&gt;
&lt;span class="gi"&gt;+              (1-eps)*subst([k2=4*eps/(1-eps)^2],sintegrand),&lt;/span&gt;
&lt;span class="gi"&gt;+              eps,maxpow),&lt;/span&gt;
&lt;span class="gi"&gt;+          s:trigreduce(integrate(sintegrandexp,sigma)),&lt;/span&gt;
&lt;span class="gi"&gt;+          s:s-subst(sigma=0,s),&lt;/span&gt;
&lt;span class="gi"&gt;+          A1:expand(subst(sigma=2*%pi,s)/(2*%pi)),&lt;/span&gt;
&lt;span class="gi"&gt;+          tau1:ataylor(s/A1,eps,maxpow),&lt;/span&gt;
&lt;span class="gi"&gt;+          for i:1 thru maxpow do C1[i]:coeff(tau1,sin(2*i*sigma)),&lt;/span&gt;
&lt;span class="gi"&gt;+          if expand(tau1-sigma-sum(C1[i]*sin(2*i*sigma),i,1,maxpow)) # 0&lt;/span&gt;
&lt;span class="gi"&gt;+          then error(&amp;quot;left over terms in B1&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+          A1:A1/(1-eps),&lt;/span&gt;
&lt;span class="gi"&gt;+          &amp;#39;done)$&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        revertI1(maxpow):=block([tau,eps,tauacc:1,sigacc:0],&lt;/span&gt;
&lt;span class="gi"&gt;+          for n:1 thru maxpow do (&lt;/span&gt;
&lt;span class="gi"&gt;+            tauacc:trigreduce(ataylor(&lt;/span&gt;
&lt;span class="gi"&gt;+                  -sum(C1[j]*sin(2*j*tau),j,1,maxpow-n+1)*tauacc/n,&lt;/span&gt;
&lt;span class="gi"&gt;+                  eps,maxpow)),&lt;/span&gt;
&lt;span class="gi"&gt;+            sigacc:sigacc+expand(diff(tauacc,tau,n-1))),&lt;/span&gt;
&lt;span class="gi"&gt;+          for i:1 thru maxpow do C1p[i]:coeff(sigacc,sin(2*i*tau)),&lt;/span&gt;
&lt;span class="gi"&gt;+          if expand(sigacc-sum(C1p[i]*sin(2*i*tau),i,1,maxpow)) # 0&lt;/span&gt;
&lt;span class="gi"&gt;+          then error(&amp;quot;left over terms in B1p&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+          &amp;#39;done)$&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        generatecodeC1p(maxpow):=block([tab2:&amp;quot;    &amp;quot;,tab3:&amp;quot;        &amp;quot;],&lt;/span&gt;
&lt;span class="gi"&gt;+          print(&amp;quot;// The coefficients C1p[l] in the Fourier expansion of B1p&lt;/span&gt;
&lt;span class="gi"&gt;+        static inline evaluate_coeffs_C1p(CT eps, CT c[])&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            CT const eps2 = math::sqr(eps);&lt;/span&gt;
&lt;span class="gi"&gt;+            CT d = eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            switch (SeriesOrder) {&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+          for n:0 thru maxpow do (&lt;/span&gt;
&lt;span class="gi"&gt;+            print(concat(tab2,&amp;quot;case &amp;quot;,string(n),&amp;quot;:&amp;quot;)),&lt;/span&gt;
&lt;span class="gi"&gt;+            for m:1 thru n do block([q:d*horner(&lt;/span&gt;
&lt;span class="gi"&gt;+                subst([eps=sqrt(eps2)],ataylor(C1p[m],eps,n)/eps^m)),&lt;/span&gt;
&lt;span class="gi"&gt;+              linel:1200],&lt;/span&gt;
&lt;span class="gi"&gt;+              if m&amp;gt;1 then print(concat(tab3,&amp;quot;d *= eps;&amp;quot;)),&lt;/span&gt;
&lt;span class="gi"&gt;+              print(concat(tab3,&amp;quot;c[&amp;quot;,string(m),&amp;quot;] = &amp;quot;,string(q),&amp;quot;;&amp;quot;))),&lt;/span&gt;
&lt;span class="gi"&gt;+            print(concat(tab3,&amp;quot;break;&amp;quot;))),&lt;/span&gt;
&lt;span class="gi"&gt;+          print(&amp;quot;    }&lt;/span&gt;
&lt;span class="gi"&gt;+        }&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;#39;done)$&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        maxpow:8$&lt;/span&gt;
&lt;span class="gi"&gt;+        computeintegral(maxpow)$&lt;/span&gt;
&lt;span class="gi"&gt;+        revertI1(maxpow)$&lt;/span&gt;
&lt;span class="gi"&gt;+        generatecodeC1p(maxpow)$&lt;/span&gt;
&lt;span class="gi"&gt;+        // Maxima script end&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+     To replace each number x by CT(x) the following&lt;/span&gt;
&lt;span class="gi"&gt;+     scirpt can be used:&lt;/span&gt;
&lt;span class="gi"&gt;+       sed -e &amp;#39;s/[0-9]\+/CT(&amp;amp;)/g; s/\[CT(/\[/g; s/)\]/\]/g;&lt;/span&gt;
&lt;span class="gi"&gt;+               s/case\sCT(/case /g; s/):/:/g; s/epsCT(2)/eps2/g;&lt;/span&gt;
&lt;span class="gi"&gt;+               s/eps(CT(2))/eps2/g;&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+    */&lt;/span&gt;
&lt;span class="gi"&gt;+    template &amp;lt;typename CT, std::size_t SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    static inline evaluate_coeffs_C1p(CT eps, CT c[])&lt;/span&gt;
&lt;span class="gi"&gt;+    {&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const eps2 = math::sqr(eps);&lt;/span&gt;
&lt;span class="gi"&gt;+        CT d = eps;&lt;/span&gt;
&lt;span class="gi"&gt;+        switch (SeriesOrder) {&lt;/span&gt;
&lt;span class="gi"&gt;+        case 0:&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 1:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = d/CT(2);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 2:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = d/CT(2);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[2] = CT(5)*d/CT(16);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 3:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = d*(CT(16)-CT(9)*eps2)/CT(32);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[2] = CT(5)*d/CT(16);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[3] = CT(29)*d/CT(96);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 4:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = d*(CT(16)-CT(9)*eps2)/CT(32);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[2] = d*(CT(30)-CT(37)*eps2)/CT(96);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[3] = CT(29)*d/CT(96);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[4] = CT(539)*d/CT(1536);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 5:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = d*(eps2*(CT(205)*eps2-CT(432))+CT(768))/CT(1536);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[2] = d*(CT(30)-CT(37)*eps2)/CT(96);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[3] = d*(CT(116)-CT(225)*eps2)/CT(384);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[4] = CT(539)*d/CT(1536);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[5] = CT(3467)*d/CT(7680);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 6:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = d*(eps2*(CT(205)*eps2-CT(432))+CT(768))/CT(1536);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[2] = d*(eps2*(CT(4005)*eps2-CT(4736))+CT(3840))/CT(12288);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[3] = d*(CT(116)-CT(225)*eps2)/CT(384);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[4] = d*(CT(2695)-CT(7173)*eps2)/CT(7680);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[5] = CT(3467)*d/CT(7680);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[6] = CT(38081)*d/CT(61440);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 7:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = d*(eps2*((CT(9840)-CT(4879)*eps2)*eps2-CT(20736))+CT(36864))/CT(73728);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[2] = d*(eps2*(CT(4005)*eps2-CT(4736))+CT(3840))/CT(12288);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[3] = d*(eps2*(CT(8703)*eps2-CT(7200))+CT(3712))/CT(12288);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[4] = d*(CT(2695)-CT(7173)*eps2)/CT(7680);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[5] = d*(CT(41604)-CT(141115)*eps2)/CT(92160);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[6] = CT(38081)*d/CT(61440);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[7] = CT(459485)*d/CT(516096);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 8:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = d*(eps2*((CT(9840)-CT(4879)*eps2)*eps2-CT(20736))+CT(36864))/CT(73728);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[2] = d*(eps2*((CT(120150)-CT(86171)*eps2)*eps2-CT(142080))+CT(115200))/CT(368640);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[3] = d*(eps2*(CT(8703)*eps2-CT(7200))+CT(3712))/CT(12288);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[4] = d*(eps2*(CT(1082857)*eps2-CT(688608))+CT(258720))/CT(737280);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[5] = d*(CT(41604)-CT(141115)*eps2)/CT(92160);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[6] = d*(CT(533134)-CT(2200311)*eps2)/CT(860160);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[7] = CT(459485)*d/CT(516096);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[8] = CT(109167851)*d/CT(82575360);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+    }&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+}}} // namespace boost::geometry::series_expansion&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;#endif // BOOST_GEOMETRY_UTIL_SERIES_EXPANSION_HPP

commit 3700b4fafa16959d3b64233ff4a3611988eabccb
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Sat May 19 21:49:24 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [util] Move series expansion functions to util/series_expansion.hpp

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/formulas/karney_direct.hpp b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 32ee804..c6fb03c 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -11,6 +11,7 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;#include &amp;lt;boost/math/constants/constants.hpp&amp;gt;

&lt;span class="w"&gt; &lt;/span&gt;#include &amp;lt;boost/geometry/util/math.hpp&amp;gt;
&lt;span class="gi"&gt;+#include &amp;lt;boost/geometry/util/series_expansion.hpp&amp;gt;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;#include &amp;lt;boost/geometry/formulas/flattening.hpp&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;#include &amp;lt;boost/geometry/formulas/result_direct.hpp&amp;gt;
&lt;span class="gu"&gt;@@ -45,231 +46,6 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    typedef result_direct&amp;lt;CT&amp;gt; result_type;

&lt;span class="w"&gt; &lt;/span&gt;    /*
&lt;span class="gd"&gt;-     Generate and evaluate the series expansion of the following integral&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-     I1 = integrate( sqrt(1+k2*sin(sigma1)^2), sigma1, 0, sigma )&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-     which is valid for k2 small. We substitute k2 = 4 * eps / (1 - eps)^2&lt;/span&gt;
&lt;span class="gd"&gt;-     and expand (1 - eps) * I1 retaining terms up to order eps^maxpow&lt;/span&gt;
&lt;span class="gd"&gt;-     in A1 and C1[l].&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-     The resulting series is of the form&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-     A1 * ( sigma + sum(C1[l] * sin(2*l*sigma), l, 1, maxpow) ).&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-     The scale factor A1-1 = mean value of (d/dsigma)I1 - 1&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-     The expansion above is performed in Maxima, a Computer Algebra System.&lt;/span&gt;
&lt;span class="gd"&gt;-     The C++ code (that yields the function evaluate_series_A1 below) is&lt;/span&gt;
&lt;span class="gd"&gt;-     generated by the following Maxima script and is based on script:&lt;/span&gt;
&lt;span class="gd"&gt;-     http://geographiclib.sourceforge.net/html/geod.mac&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        // Maxima script begin&lt;/span&gt;
&lt;span class="gd"&gt;-        taylordepth:5$&lt;/span&gt;
&lt;span class="gd"&gt;-        ataylor(expr,var,ord):=expand(ratdisrep(taylor(expr,var,0,ord)))$&lt;/span&gt;
&lt;span class="gd"&gt;-        jtaylor(expr,var1,var2,ord):=block([zz],expand(subst([zz=1],&lt;/span&gt;
&lt;span class="gd"&gt;-        ratdisrep(taylor(subst([var1=zz*var1,var2=zz*var2],expr),zz,0,ord)))))$&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        computeintegral(maxpow):=block([sintegrand,sintegrandexp,s,sigma,tau1,k2,eps],&lt;/span&gt;
&lt;span class="gd"&gt;-          sintegrand:sqrt(1+k2*sin(sigma)^2),&lt;/span&gt;
&lt;span class="gd"&gt;-          sintegrandexp:ataylor(&lt;/span&gt;
&lt;span class="gd"&gt;-              (1-eps)*subst([k2=4*eps/(1-eps)^2],sintegrand),&lt;/span&gt;
&lt;span class="gd"&gt;-              eps,maxpow),&lt;/span&gt;
&lt;span class="gd"&gt;-          s:trigreduce(integrate(sintegrandexp,sigma)),&lt;/span&gt;
&lt;span class="gd"&gt;-          s:s-subst(sigma=0,s),&lt;/span&gt;
&lt;span class="gd"&gt;-          A1:expand(subst(sigma=2*%pi,s)/(2*%pi)),&lt;/span&gt;
&lt;span class="gd"&gt;-          tau1:ataylor(s/A1,eps,maxpow),&lt;/span&gt;
&lt;span class="gd"&gt;-          for i:1 thru maxpow do C1[i]:coeff(tau1,sin(2*i*sigma)),&lt;/span&gt;
&lt;span class="gd"&gt;-          if expand(tau1-sigma-sum(C1[i]*sin(2*i*sigma),i,1,maxpow)) # 0&lt;/span&gt;
&lt;span class="gd"&gt;-          then error(&amp;quot;left over terms in B1&amp;quot;),&lt;/span&gt;
&lt;span class="gd"&gt;-          A1:A1/(1-eps),&lt;/span&gt;
&lt;span class="gd"&gt;-          &amp;#39;done)$&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        generatecode(maxpow):=block([tab2:&amp;quot;    &amp;quot;,tab3:&amp;quot;        &amp;quot;],&lt;/span&gt;
&lt;span class="gd"&gt;-        print(&amp;quot;// The scale factor A1-1 = mean value of (d/dsigma)I1 - 1&lt;/span&gt;
&lt;span class="gd"&gt;-        static inline CT evaluate_series_A1(CT eps) {&lt;/span&gt;
&lt;span class="gd"&gt;-            CT eps2 = math::sqr(eps);&lt;/span&gt;
&lt;span class="gd"&gt;-            CT t;&lt;/span&gt;
&lt;span class="gd"&gt;-            switch (SeriesOrder/2) {&amp;quot;),&lt;/span&gt;
&lt;span class="gd"&gt;-          for n:0 thru entier(maxpow/2) do block([&lt;/span&gt;
&lt;span class="gd"&gt;-            q:horner(ataylor(subst([eps=sqrt(eps2)],A1*(1-eps)-1),eps2,n)),&lt;/span&gt;
&lt;span class="gd"&gt;-            linel:1200],&lt;/span&gt;
&lt;span class="gd"&gt;-            print(concat(tab2,&amp;quot;case &amp;quot;,string(n),&amp;quot;:&amp;quot;)),&lt;/span&gt;
&lt;span class="gd"&gt;-            print(concat(tab3,&amp;quot;t = &amp;quot;,string(q),&amp;quot;;&amp;quot;)),&lt;/span&gt;
&lt;span class="gd"&gt;-            print(concat(tab3,&amp;quot;break;&amp;quot;))),&lt;/span&gt;
&lt;span class="gd"&gt;-          print(&amp;quot;    }&lt;/span&gt;
&lt;span class="gd"&gt;-            return (t + eps) / (1 - eps);&lt;/span&gt;
&lt;span class="gd"&gt;-        }&amp;quot;),&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;#39;done)$&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        maxpow:8$&lt;/span&gt;
&lt;span class="gd"&gt;-        computeintegral(maxpow)$&lt;/span&gt;
&lt;span class="gd"&gt;-        generatecode(maxpow)$&lt;/span&gt;
&lt;span class="gd"&gt;-        // Maxima script end&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-     To replace each number x by CT(x) the following&lt;/span&gt;
&lt;span class="gd"&gt;-     scirpt can be used:&lt;/span&gt;
&lt;span class="gd"&gt;-       sed -e &amp;#39;s/[0-9]\+/CT(&amp;amp;)/g; s/\[CT/\[/g; s/)\]/\]/g;&lt;/span&gt;
&lt;span class="gd"&gt;-               s/case\sCT(/case /g; s/):/:/g; s/epsCT(2)/eps2/g;&amp;#39;&lt;/span&gt;
&lt;span class="gd"&gt;-    */&lt;/span&gt;
&lt;span class="gd"&gt;-    static inline CT evaluate_series_A1(CT eps)&lt;/span&gt;
&lt;span class="gd"&gt;-    {&lt;/span&gt;
&lt;span class="gd"&gt;-        CT eps2 = math::sqr(eps);&lt;/span&gt;
&lt;span class="gd"&gt;-        CT t;&lt;/span&gt;
&lt;span class="gd"&gt;-        switch (SeriesOrder/2) {&lt;/span&gt;
&lt;span class="gd"&gt;-        case 0:&lt;/span&gt;
&lt;span class="gd"&gt;-            t = CT(0);&lt;/span&gt;
&lt;span class="gd"&gt;-            break;&lt;/span&gt;
&lt;span class="gd"&gt;-        case 1:&lt;/span&gt;
&lt;span class="gd"&gt;-            t = eps2/CT(4);&lt;/span&gt;
&lt;span class="gd"&gt;-            break;&lt;/span&gt;
&lt;span class="gd"&gt;-        case 2:&lt;/span&gt;
&lt;span class="gd"&gt;-            t = eps2*(eps2+CT(16))/CT(64);&lt;/span&gt;
&lt;span class="gd"&gt;-            break;&lt;/span&gt;
&lt;span class="gd"&gt;-        case 3:&lt;/span&gt;
&lt;span class="gd"&gt;-            t = eps2*(eps2*(eps2+CT(4))+CT(64))/CT(256);&lt;/span&gt;
&lt;span class="gd"&gt;-            break;&lt;/span&gt;
&lt;span class="gd"&gt;-        case 4:&lt;/span&gt;
&lt;span class="gd"&gt;-            t = eps2*(eps2*(eps2*(CT(25)*eps2+CT(64))+CT(256))+CT(4096))/CT(16384);&lt;/span&gt;
&lt;span class="gd"&gt;-            break;&lt;/span&gt;
&lt;span class="gd"&gt;-        }&lt;/span&gt;
&lt;span class="gd"&gt;-        return (t + eps) / (CT(1) - eps);&lt;/span&gt;
&lt;span class="gd"&gt;-    }&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    /*&lt;/span&gt;
&lt;span class="gd"&gt;-     The coefficients C1[l] in the Fourier expansion of B1&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-     The expansion below is performed in Maxima, a Computer Algebra System.&lt;/span&gt;
&lt;span class="gd"&gt;-     The C++ code (that yields the function evaluate_series_A1 below) is&lt;/span&gt;
&lt;span class="gd"&gt;-     generated by the following Maxima script and is based on script:&lt;/span&gt;
&lt;span class="gd"&gt;-     http://geographiclib.sourceforge.net/html/geod.mac&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        // Maxima script begin&lt;/span&gt;
&lt;span class="gd"&gt;-        taylordepth:5$&lt;/span&gt;
&lt;span class="gd"&gt;-        ataylor(expr,var,ord):=expand(ratdisrep(taylor(expr,var,0,ord)))$&lt;/span&gt;
&lt;span class="gd"&gt;-        jtaylor(expr,var1,var2,ord):=block([zz],expand(subst([zz=1],&lt;/span&gt;
&lt;span class="gd"&gt;-        ratdisrep(taylor(subst([var1=zz*var1,var2=zz*var2],expr),zz,0,ord)))))$&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        generatecode(maxpow):=block([tab2:&amp;quot;    &amp;quot;,tab3:&amp;quot;        &amp;quot;],&lt;/span&gt;
&lt;span class="gd"&gt;-          print(&amp;quot;// The coefficients C1[l] in the Fourier expansion of B1&lt;/span&gt;
&lt;span class="gd"&gt;-        static inline evaluate_coeffs_C1(CT eps, CT c[]) {&lt;/span&gt;
&lt;span class="gd"&gt;-            CT eps2 = math::sqr(eps);&lt;/span&gt;
&lt;span class="gd"&gt;-            CT d = eps;&lt;/span&gt;
&lt;span class="gd"&gt;-            switch (SeriesOrder) {&amp;quot;),&lt;/span&gt;
&lt;span class="gd"&gt;-          for n:0 thru maxpow do (&lt;/span&gt;
&lt;span class="gd"&gt;-            print(concat(tab2,&amp;quot;case &amp;quot;,string(n),&amp;quot;:&amp;quot;)),&lt;/span&gt;
&lt;span class="gd"&gt;-            for m:1 thru n do block([q:d*horner(&lt;/span&gt;
&lt;span class="gd"&gt;-                subst([eps=sqrt(eps2)],ataylor(C1[m],eps,n)/eps^m)),&lt;/span&gt;
&lt;span class="gd"&gt;-              linel:1200],&lt;/span&gt;
&lt;span class="gd"&gt;-              if m&amp;gt;1 then print(concat(tab3,&amp;quot;d *= eps;&amp;quot;)),&lt;/span&gt;
&lt;span class="gd"&gt;-              print(concat(tab3,&amp;quot;c[&amp;quot;,string(m),&amp;quot;] = &amp;quot;,string(q),&amp;quot;;&amp;quot;))),&lt;/span&gt;
&lt;span class="gd"&gt;-            print(concat(tab3,&amp;quot;break;&amp;quot;))),&lt;/span&gt;
&lt;span class="gd"&gt;-          print(&amp;quot;    }&lt;/span&gt;
&lt;span class="gd"&gt;-        }&amp;quot;),&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;#39;done)$&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        maxpow:8$&lt;/span&gt;
&lt;span class="gd"&gt;-        computeintegral(maxpow)$&lt;/span&gt;
&lt;span class="gd"&gt;-        generatecode(maxpow)$&lt;/span&gt;
&lt;span class="gd"&gt;-        // Maxima script end&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-     To replace each number x by CT(x) the following&lt;/span&gt;
&lt;span class="gd"&gt;-     scirpt can be used:&lt;/span&gt;
&lt;span class="gd"&gt;-       sed -e &amp;#39;s/[0-9]\+/CT(&amp;amp;)/g; s/\[CT(/\[/g; s/)\]/\]/g;&lt;/span&gt;
&lt;span class="gd"&gt;-               s/case\sCT(/case /g; s/):/:/g; s/epsCT(2)/eps2/g;&lt;/span&gt;
&lt;span class="gd"&gt;-               s/eps(CT(2))/eps2/g;&amp;#39;&lt;/span&gt;
&lt;span class="gd"&gt;-    */&lt;/span&gt;
&lt;span class="gd"&gt;-    static inline void evaluate_coeffs_C1(CT eps, CT c[])&lt;/span&gt;
&lt;span class="gd"&gt;-    {&lt;/span&gt;
&lt;span class="gd"&gt;-        CT eps2 = math::sqr(eps);&lt;/span&gt;
&lt;span class="gd"&gt;-        CT d = eps;&lt;/span&gt;
&lt;span class="gd"&gt;-        switch (SeriesOrder) {&lt;/span&gt;
&lt;span class="gd"&gt;-        case 0:&lt;/span&gt;
&lt;span class="gd"&gt;-            break;&lt;/span&gt;
&lt;span class="gd"&gt;-        case 1:&lt;/span&gt;
&lt;span class="gd"&gt;-            c[1] = -d/CT(2);&lt;/span&gt;
&lt;span class="gd"&gt;-            break;&lt;/span&gt;
&lt;span class="gd"&gt;-        case 2:&lt;/span&gt;
&lt;span class="gd"&gt;-            c[1] = -d/CT(2);&lt;/span&gt;
&lt;span class="gd"&gt;-            d *= eps;&lt;/span&gt;
&lt;span class="gd"&gt;-            c[2] = -d/CT(16);&lt;/span&gt;
&lt;span class="gd"&gt;-            break;&lt;/span&gt;
&lt;span class="gd"&gt;-        case 3:&lt;/span&gt;
&lt;span class="gd"&gt;-            c[1] = d*(CT(3)*eps2-CT(8))/CT(16);&lt;/span&gt;
&lt;span class="gd"&gt;-            d *= eps;&lt;/span&gt;
&lt;span class="gd"&gt;-            c[2] = -d/CT(16);&lt;/span&gt;
&lt;span class="gd"&gt;-            d *= eps;&lt;/span&gt;
&lt;span class="gd"&gt;-            c[3] = -d/CT(48);&lt;/span&gt;
&lt;span class="gd"&gt;-            break;&lt;/span&gt;
&lt;span class="gd"&gt;-        case 4:&lt;/span&gt;
&lt;span class="gd"&gt;-            c[1] = d*(CT(3)*eps2-CT(8))/CT(16);&lt;/span&gt;
&lt;span class="gd"&gt;-            d *= eps;&lt;/span&gt;
&lt;span class="gd"&gt;-            c[2] = d*(eps2-CT(2))/CT(32);&lt;/span&gt;
&lt;span class="gd"&gt;-            d *= eps;&lt;/span&gt;
&lt;span class="gd"&gt;-            c[3] = -d/CT(48);&lt;/span&gt;
&lt;span class="gd"&gt;-            d *= eps;&lt;/span&gt;
&lt;span class="gd"&gt;-            c[4] = -CT(5)*d/CT(512);&lt;/span&gt;
&lt;span class="gd"&gt;-            break;&lt;/span&gt;
&lt;span class="gd"&gt;-        case 5:&lt;/span&gt;
&lt;span class="gd"&gt;-            c[1] = d*((CT(6)-eps2)*eps2-CT(16))/CT(32);&lt;/span&gt;
&lt;span class="gd"&gt;-            d *= eps;&lt;/span&gt;
&lt;span class="gd"&gt;-            c[2] = d*(eps2-CT(2))/CT(32);&lt;/span&gt;
&lt;span class="gd"&gt;-            d *= eps;&lt;/span&gt;
&lt;span class="gd"&gt;-            c[3] = d*(CT(9)*eps2-CT(16))/CT(768);&lt;/span&gt;
&lt;span class="gd"&gt;-            d *= eps;&lt;/span&gt;
&lt;span class="gd"&gt;-            c[4] = -CT(5)*d/CT(512);&lt;/span&gt;
&lt;span class="gd"&gt;-            d *= eps;&lt;/span&gt;
&lt;span class="gd"&gt;-            c[5] = -CT(7)*d/CT(1280);&lt;/span&gt;
&lt;span class="gd"&gt;-            break;&lt;/span&gt;
&lt;span class="gd"&gt;-        case 6:&lt;/span&gt;
&lt;span class="gd"&gt;-            c[1] = d*((CT(6)-eps2)*eps2-CT(16))/CT(32);&lt;/span&gt;
&lt;span class="gd"&gt;-            d *= eps;&lt;/span&gt;
&lt;span class="gd"&gt;-            c[2] = d*((CT(64)-CT(9)*eps2)*eps2-CT(128))/CT(2048);&lt;/span&gt;
&lt;span class="gd"&gt;-            d *= eps;&lt;/span&gt;
&lt;span class="gd"&gt;-            c[3] = d*(CT(9)*eps2-CT(16))/CT(768);&lt;/span&gt;
&lt;span class="gd"&gt;-            d *= eps;&lt;/span&gt;
&lt;span class="gd"&gt;-            c[4] = d*(CT(3)*eps2-CT(5))/CT(512);&lt;/span&gt;
&lt;span class="gd"&gt;-            d *= eps;&lt;/span&gt;
&lt;span class="gd"&gt;-            c[5] = -CT(7)*d/CT(1280);&lt;/span&gt;
&lt;span class="gd"&gt;-            d *= eps;&lt;/span&gt;
&lt;span class="gd"&gt;-            c[6] = -CT(7)*d/CT(2048);&lt;/span&gt;
&lt;span class="gd"&gt;-            break;&lt;/span&gt;
&lt;span class="gd"&gt;-        case 7:&lt;/span&gt;
&lt;span class="gd"&gt;-            c[1] = d*(eps2*(eps2*(CT(19)*eps2-CT(64))+CT(384))-CT(1024))/CT(2048);&lt;/span&gt;
&lt;span class="gd"&gt;-            d *= eps;&lt;/span&gt;
&lt;span class="gd"&gt;-            c[2] = d*((CT(64)-CT(9)*eps2)*eps2-CT(128))/CT(2048);&lt;/span&gt;
&lt;span class="gd"&gt;-            d *= eps;&lt;/span&gt;
&lt;span class="gd"&gt;-            c[3] = d*((CT(72)-CT(9)*eps2)*eps2-CT(128))/CT(6144);&lt;/span&gt;
&lt;span class="gd"&gt;-            d *= eps;&lt;/span&gt;
&lt;span class="gd"&gt;-            c[4] = d*(CT(3)*eps2-CT(5))/CT(512);&lt;/span&gt;
&lt;span class="gd"&gt;-            d *= eps;&lt;/span&gt;
&lt;span class="gd"&gt;-            c[5] = d*(CT(35)*eps2-CT(56))/CT(10240);&lt;/span&gt;
&lt;span class="gd"&gt;-            d *= eps;&lt;/span&gt;
&lt;span class="gd"&gt;-            c[6] = -CT(7)*d/CT(2048);&lt;/span&gt;
&lt;span class="gd"&gt;-            d *= eps;&lt;/span&gt;
&lt;span class="gd"&gt;-            c[7] = -CT(33)*d/CT(14336);&lt;/span&gt;
&lt;span class="gd"&gt;-            break;&lt;/span&gt;
&lt;span class="gd"&gt;-        case 8:&lt;/span&gt;
&lt;span class="gd"&gt;-            c[1] = d*(eps2*(eps2*(CT(19)*eps2-CT(64))+CT(384))-CT(1024))/CT(2048);&lt;/span&gt;
&lt;span class="gd"&gt;-            d *= eps;&lt;/span&gt;
&lt;span class="gd"&gt;-            c[2] = d*(eps2*(eps2*(CT(7)*eps2-CT(18))+CT(128))-CT(256))/CT(4096);&lt;/span&gt;
&lt;span class="gd"&gt;-            d *= eps;&lt;/span&gt;
&lt;span class="gd"&gt;-            c[3] = d*((CT(72)-CT(9)*eps2)*eps2-CT(128))/CT(6144);&lt;/span&gt;
&lt;span class="gd"&gt;-            d *= eps;&lt;/span&gt;
&lt;span class="gd"&gt;-            c[4] = d*((CT(96)-CT(11)*eps2)*eps2-CT(160))/CT(16384);&lt;/span&gt;
&lt;span class="gd"&gt;-            d *= eps;&lt;/span&gt;
&lt;span class="gd"&gt;-            c[5] = d*(CT(35)*eps2-CT(56))/CT(10240);&lt;/span&gt;
&lt;span class="gd"&gt;-            d *= eps;&lt;/span&gt;
&lt;span class="gd"&gt;-            c[6] = d*(CT(9)*eps2-CT(14))/CT(4096);&lt;/span&gt;
&lt;span class="gd"&gt;-            d *= eps;&lt;/span&gt;
&lt;span class="gd"&gt;-            c[7] = -CT(33)*d/CT(14336);&lt;/span&gt;
&lt;span class="gd"&gt;-            d *= eps;&lt;/span&gt;
&lt;span class="gd"&gt;-            c[8] = -CT(429)*d/CT(262144);&lt;/span&gt;
&lt;span class="gd"&gt;-            break;&lt;/span&gt;
&lt;span class="gd"&gt;-        }&lt;/span&gt;
&lt;span class="gd"&gt;-    }&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    /*&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;     Evaluate

&lt;span class="w"&gt; &lt;/span&gt;     y = sum(c[i] * sin(2*i * x), i, 1, n)
&lt;span class="gu"&gt;@@ -351,11 +127,11 @@ public:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        // Find the coefficients for Aj by computing the
&lt;span class="w"&gt; &lt;/span&gt;        // series expansion using Horner scehme.
&lt;span class="gd"&gt;-        CT expansion_A1 = evaluate_series_A1(epsilon);&lt;/span&gt;
&lt;span class="gi"&gt;+        CT expansion_A1 = series_expansion::evaluate_series_A1&amp;lt;CT, SeriesOrder&amp;gt;(epsilon);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        // Index zero element of coeffs_C1 is unused.
&lt;span class="w"&gt; &lt;/span&gt;        CT coeffs_C1[SeriesOrder + 1];
&lt;span class="gd"&gt;-        evaluate_coeffs_C1(epsilon, coeffs_C1);&lt;/span&gt;
&lt;span class="gi"&gt;+        series_expansion::evaluate_coeffs_C1&amp;lt;CT, SeriesOrder&amp;gt;(epsilon, coeffs_C1);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        // Tau is an integration variable.
&lt;span class="w"&gt; &lt;/span&gt;        CT tau12 = distance / (b + (c1 + expansion_A1));
&lt;span class="gh"&gt;diff --git a/include/boost/geometry/util/series_expansion.hpp b/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
new file mode 100644
&lt;span class="gh"&gt;index 0000000..b2e2277&lt;/span&gt;
&lt;span class="gd"&gt;--- /dev/null&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/util/series_expansion.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -0,0 +1,243 @@&lt;/span&gt;
&lt;span class="gi"&gt;+// Boost.Geometry&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+// Use, modification and distribution is subject to the Boost Software License,&lt;/span&gt;
&lt;span class="gi"&gt;+// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at&lt;/span&gt;
&lt;span class="gi"&gt;+// http://www.boost.org/LICENSE_1_0.txt)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+#ifndef BOOST_GEOMETRY_UTIL_SERIES_EXPANSION_HPP&lt;/span&gt;
&lt;span class="gi"&gt;+#define BOOST_GEOMETRY_UTIL_SERIES_EXPANSION_HPP&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+#include &amp;lt;boost/geometry/util/math.hpp&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+namespace boost { namespace geometry { namespace series_expansion {&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    /*&lt;/span&gt;
&lt;span class="gi"&gt;+     Generate and evaluate the series expansion of the following integral&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+     I1 = integrate( sqrt(1+k2*sin(sigma1)^2), sigma1, 0, sigma )&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+     which is valid for k2 small. We substitute k2 = 4 * eps / (1 - eps)^2&lt;/span&gt;
&lt;span class="gi"&gt;+     and expand (1 - eps) * I1 retaining terms up to order eps^maxpow&lt;/span&gt;
&lt;span class="gi"&gt;+     in A1 and C1[l].&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+     The resulting series is of the form&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+     A1 * ( sigma + sum(C1[l] * sin(2*l*sigma), l, 1, maxpow) ).&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+     The scale factor A1-1 = mean value of (d/dsigma)I1 - 1&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+     The expansion above is performed in Maxima, a Computer Algebra System.&lt;/span&gt;
&lt;span class="gi"&gt;+     The C++ code (that yields the function evaluate_series_A1 below) is&lt;/span&gt;
&lt;span class="gi"&gt;+     generated by the following Maxima script and is based on script:&lt;/span&gt;
&lt;span class="gi"&gt;+     http://geographiclib.sourceforge.net/html/geod.mac&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        // Maxima script begin&lt;/span&gt;
&lt;span class="gi"&gt;+        taylordepth:5$&lt;/span&gt;
&lt;span class="gi"&gt;+        ataylor(expr,var,ord):=expand(ratdisrep(taylor(expr,var,0,ord)))$&lt;/span&gt;
&lt;span class="gi"&gt;+        jtaylor(expr,var1,var2,ord):=block([zz],expand(subst([zz=1],&lt;/span&gt;
&lt;span class="gi"&gt;+        ratdisrep(taylor(subst([var1=zz*var1,var2=zz*var2],expr),zz,0,ord)))))$&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        computeintegral(maxpow):=block([sintegrand,sintegrandexp,s,sigma,tau1,k2,eps],&lt;/span&gt;
&lt;span class="gi"&gt;+          sintegrand:sqrt(1+k2*sin(sigma)^2),&lt;/span&gt;
&lt;span class="gi"&gt;+          sintegrandexp:ataylor(&lt;/span&gt;
&lt;span class="gi"&gt;+              (1-eps)*subst([k2=4*eps/(1-eps)^2],sintegrand),&lt;/span&gt;
&lt;span class="gi"&gt;+              eps,maxpow),&lt;/span&gt;
&lt;span class="gi"&gt;+          s:trigreduce(integrate(sintegrandexp,sigma)),&lt;/span&gt;
&lt;span class="gi"&gt;+          s:s-subst(sigma=0,s),&lt;/span&gt;
&lt;span class="gi"&gt;+          A1:expand(subst(sigma=2*%pi,s)/(2*%pi)),&lt;/span&gt;
&lt;span class="gi"&gt;+          tau1:ataylor(s/A1,eps,maxpow),&lt;/span&gt;
&lt;span class="gi"&gt;+          for i:1 thru maxpow do C1[i]:coeff(tau1,sin(2*i*sigma)),&lt;/span&gt;
&lt;span class="gi"&gt;+          if expand(tau1-sigma-sum(C1[i]*sin(2*i*sigma),i,1,maxpow)) # 0&lt;/span&gt;
&lt;span class="gi"&gt;+          then error(&amp;quot;left over terms in B1&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+          A1:A1/(1-eps),&lt;/span&gt;
&lt;span class="gi"&gt;+          &amp;#39;done)$&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        generatecode(maxpow):=block([tab2:&amp;quot;    &amp;quot;,tab3:&amp;quot;        &amp;quot;],&lt;/span&gt;
&lt;span class="gi"&gt;+        print(&amp;quot;// The scale factor A1-1 = mean value of (d/dsigma)I1 - 1&lt;/span&gt;
&lt;span class="gi"&gt;+        static inline CT evaluate_series_A1(CT eps) {&lt;/span&gt;
&lt;span class="gi"&gt;+            CT eps2 = math::sqr(eps);&lt;/span&gt;
&lt;span class="gi"&gt;+            CT t;&lt;/span&gt;
&lt;span class="gi"&gt;+            switch (SeriesOrder/2) {&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+          for n:0 thru entier(maxpow/2) do block([&lt;/span&gt;
&lt;span class="gi"&gt;+            q:horner(ataylor(subst([eps=sqrt(eps2)],A1*(1-eps)-1),eps2,n)),&lt;/span&gt;
&lt;span class="gi"&gt;+            linel:1200],&lt;/span&gt;
&lt;span class="gi"&gt;+            print(concat(tab2,&amp;quot;case &amp;quot;,string(n),&amp;quot;:&amp;quot;)),&lt;/span&gt;
&lt;span class="gi"&gt;+            print(concat(tab3,&amp;quot;t = &amp;quot;,string(q),&amp;quot;;&amp;quot;)),&lt;/span&gt;
&lt;span class="gi"&gt;+            print(concat(tab3,&amp;quot;break;&amp;quot;))),&lt;/span&gt;
&lt;span class="gi"&gt;+          print(&amp;quot;    }&lt;/span&gt;
&lt;span class="gi"&gt;+            return (t + eps) / (1 - eps);&lt;/span&gt;
&lt;span class="gi"&gt;+        }&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;#39;done)$&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        maxpow:8$&lt;/span&gt;
&lt;span class="gi"&gt;+        computeintegral(maxpow)$&lt;/span&gt;
&lt;span class="gi"&gt;+        generatecode(maxpow)$&lt;/span&gt;
&lt;span class="gi"&gt;+        // Maxima script end&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+     To replace each number x by CT(x) the following&lt;/span&gt;
&lt;span class="gi"&gt;+     scirpt can be used:&lt;/span&gt;
&lt;span class="gi"&gt;+       sed -e &amp;#39;s/[0-9]\+/CT(&amp;amp;)/g; s/\[CT/\[/g; s/)\]/\]/g;&lt;/span&gt;
&lt;span class="gi"&gt;+               s/case\sCT(/case /g; s/):/:/g; s/epsCT(2)/eps2/g;&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+    */&lt;/span&gt;
&lt;span class="gi"&gt;+    template &amp;lt;typename CT, std::size_t SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    static inline CT evaluate_series_A1(CT eps)&lt;/span&gt;
&lt;span class="gi"&gt;+    {&lt;/span&gt;
&lt;span class="gi"&gt;+        CT eps2 = math::sqr(eps);&lt;/span&gt;
&lt;span class="gi"&gt;+        CT t;&lt;/span&gt;
&lt;span class="gi"&gt;+        switch (SeriesOrder/2) {&lt;/span&gt;
&lt;span class="gi"&gt;+        case 0:&lt;/span&gt;
&lt;span class="gi"&gt;+            t = CT(0);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 1:&lt;/span&gt;
&lt;span class="gi"&gt;+            t = eps2/CT(4);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 2:&lt;/span&gt;
&lt;span class="gi"&gt;+            t = eps2*(eps2+CT(16))/CT(64);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 3:&lt;/span&gt;
&lt;span class="gi"&gt;+            t = eps2*(eps2*(eps2+CT(4))+CT(64))/CT(256);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 4:&lt;/span&gt;
&lt;span class="gi"&gt;+            t = eps2*(eps2*(eps2*(CT(25)*eps2+CT(64))+CT(256))+CT(4096))/CT(16384);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+        return (t + eps) / (CT(1) - eps);&lt;/span&gt;
&lt;span class="gi"&gt;+    }&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    /*&lt;/span&gt;
&lt;span class="gi"&gt;+     The coefficients C1[l] in the Fourier expansion of B1&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+     The expansion below is performed in Maxima, a Computer Algebra System.&lt;/span&gt;
&lt;span class="gi"&gt;+     The C++ code (that yields the function evaluate_series_A1 below) is&lt;/span&gt;
&lt;span class="gi"&gt;+     generated by the following Maxima script and is based on script:&lt;/span&gt;
&lt;span class="gi"&gt;+     http://geographiclib.sourceforge.net/html/geod.mac&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        // Maxima script begin&lt;/span&gt;
&lt;span class="gi"&gt;+        taylordepth:5$&lt;/span&gt;
&lt;span class="gi"&gt;+        ataylor(expr,var,ord):=expand(ratdisrep(taylor(expr,var,0,ord)))$&lt;/span&gt;
&lt;span class="gi"&gt;+        jtaylor(expr,var1,var2,ord):=block([zz],expand(subst([zz=1],&lt;/span&gt;
&lt;span class="gi"&gt;+        ratdisrep(taylor(subst([var1=zz*var1,var2=zz*var2],expr),zz,0,ord)))))$&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        generatecode(maxpow):=block([tab2:&amp;quot;    &amp;quot;,tab3:&amp;quot;        &amp;quot;],&lt;/span&gt;
&lt;span class="gi"&gt;+          print(&amp;quot;// The coefficients C1[l] in the Fourier expansion of B1&lt;/span&gt;
&lt;span class="gi"&gt;+        static inline evaluate_coeffs_C1(CT eps, CT c[]) {&lt;/span&gt;
&lt;span class="gi"&gt;+            CT eps2 = math::sqr(eps);&lt;/span&gt;
&lt;span class="gi"&gt;+            CT d = eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            switch (SeriesOrder) {&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+          for n:0 thru maxpow do (&lt;/span&gt;
&lt;span class="gi"&gt;+            print(concat(tab2,&amp;quot;case &amp;quot;,string(n),&amp;quot;:&amp;quot;)),&lt;/span&gt;
&lt;span class="gi"&gt;+            for m:1 thru n do block([q:d*horner(&lt;/span&gt;
&lt;span class="gi"&gt;+                subst([eps=sqrt(eps2)],ataylor(C1[m],eps,n)/eps^m)),&lt;/span&gt;
&lt;span class="gi"&gt;+              linel:1200],&lt;/span&gt;
&lt;span class="gi"&gt;+              if m&amp;gt;1 then print(concat(tab3,&amp;quot;d *= eps;&amp;quot;)),&lt;/span&gt;
&lt;span class="gi"&gt;+              print(concat(tab3,&amp;quot;c[&amp;quot;,string(m),&amp;quot;] = &amp;quot;,string(q),&amp;quot;;&amp;quot;))),&lt;/span&gt;
&lt;span class="gi"&gt;+            print(concat(tab3,&amp;quot;break;&amp;quot;))),&lt;/span&gt;
&lt;span class="gi"&gt;+          print(&amp;quot;    }&lt;/span&gt;
&lt;span class="gi"&gt;+        }&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;#39;done)$&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        maxpow:8$&lt;/span&gt;
&lt;span class="gi"&gt;+        computeintegral(maxpow)$&lt;/span&gt;
&lt;span class="gi"&gt;+        generatecode(maxpow)$&lt;/span&gt;
&lt;span class="gi"&gt;+        // Maxima script end&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+     To replace each number x by CT(x) the following&lt;/span&gt;
&lt;span class="gi"&gt;+     scirpt can be used:&lt;/span&gt;
&lt;span class="gi"&gt;+       sed -e &amp;#39;s/[0-9]\+/CT(&amp;amp;)/g; s/\[CT(/\[/g; s/)\]/\]/g;&lt;/span&gt;
&lt;span class="gi"&gt;+               s/case\sCT(/case /g; s/):/:/g; s/epsCT(2)/eps2/g;&lt;/span&gt;
&lt;span class="gi"&gt;+               s/eps(CT(2))/eps2/g;&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+    */&lt;/span&gt;
&lt;span class="gi"&gt;+    template &amp;lt;typename CT, std::size_t SeriesOrder&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    static inline void evaluate_coeffs_C1(CT eps, CT c[])&lt;/span&gt;
&lt;span class="gi"&gt;+    {&lt;/span&gt;
&lt;span class="gi"&gt;+        CT eps2 = math::sqr(eps);&lt;/span&gt;
&lt;span class="gi"&gt;+        CT d = eps;&lt;/span&gt;
&lt;span class="gi"&gt;+        switch (SeriesOrder) {&lt;/span&gt;
&lt;span class="gi"&gt;+        case 0:&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 1:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = -d/CT(2);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 2:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = -d/CT(2);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[2] = -d/CT(16);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 3:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = d*(CT(3)*eps2-CT(8))/CT(16);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[2] = -d/CT(16);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[3] = -d/CT(48);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 4:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = d*(CT(3)*eps2-CT(8))/CT(16);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[2] = d*(eps2-CT(2))/CT(32);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[3] = -d/CT(48);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[4] = -CT(5)*d/CT(512);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 5:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = d*((CT(6)-eps2)*eps2-CT(16))/CT(32);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[2] = d*(eps2-CT(2))/CT(32);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[3] = d*(CT(9)*eps2-CT(16))/CT(768);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[4] = -CT(5)*d/CT(512);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[5] = -CT(7)*d/CT(1280);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 6:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = d*((CT(6)-eps2)*eps2-CT(16))/CT(32);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[2] = d*((CT(64)-CT(9)*eps2)*eps2-CT(128))/CT(2048);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[3] = d*(CT(9)*eps2-CT(16))/CT(768);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[4] = d*(CT(3)*eps2-CT(5))/CT(512);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[5] = -CT(7)*d/CT(1280);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[6] = -CT(7)*d/CT(2048);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 7:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = d*(eps2*(eps2*(CT(19)*eps2-CT(64))+CT(384))-CT(1024))/CT(2048);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[2] = d*((CT(64)-CT(9)*eps2)*eps2-CT(128))/CT(2048);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[3] = d*((CT(72)-CT(9)*eps2)*eps2-CT(128))/CT(6144);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[4] = d*(CT(3)*eps2-CT(5))/CT(512);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[5] = d*(CT(35)*eps2-CT(56))/CT(10240);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[6] = -CT(7)*d/CT(2048);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[7] = -CT(33)*d/CT(14336);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 8:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = d*(eps2*(eps2*(CT(19)*eps2-CT(64))+CT(384))-CT(1024))/CT(2048);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[2] = d*(eps2*(eps2*(CT(7)*eps2-CT(18))+CT(128))-CT(256))/CT(4096);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[3] = d*((CT(72)-CT(9)*eps2)*eps2-CT(128))/CT(6144);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[4] = d*((CT(96)-CT(11)*eps2)*eps2-CT(160))/CT(16384);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[5] = d*(CT(35)*eps2-CT(56))/CT(10240);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[6] = d*(CT(9)*eps2-CT(14))/CT(4096);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[7] = -CT(33)*d/CT(14336);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[8] = -CT(429)*d/CT(262144);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+    }&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+}}} // namespace boost::geometry::formula&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+#endif // BOOST_GEOMETRY_UTIL_SERIES_EXPANSION_HPP&lt;/span&gt;

commit b6fcee8f303d37016449f7014c31cfcf00b926ae
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Sat May 19 21:33:01 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [formulas] Compute sin cos series using Clenshaw summation

&lt;span class="w"&gt; &lt;/span&gt;   - Update function headers

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/formulas/karney_direct.hpp b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 3057715..32ee804 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -112,7 +112,8 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;       sed -e &amp;#39;s/[0-9]\+/CT(&amp;amp;)/g; s/\[CT/\[/g; s/)\]/\]/g;
&lt;span class="w"&gt; &lt;/span&gt;               s/case\sCT(/case /g; s/):/:/g; s/epsCT(2)/eps2/g;&amp;#39;
&lt;span class="w"&gt; &lt;/span&gt;    */
&lt;span class="gd"&gt;-    static inline CT evaluate_series_A1(CT eps) {&lt;/span&gt;
&lt;span class="gi"&gt;+    static inline CT evaluate_series_A1(CT eps)&lt;/span&gt;
&lt;span class="gi"&gt;+    {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT eps2 = math::sqr(eps);
&lt;span class="w"&gt; &lt;/span&gt;        CT t;
&lt;span class="w"&gt; &lt;/span&gt;        switch (SeriesOrder/2) {
&lt;span class="gu"&gt;@@ -178,7 +179,8 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;               s/case\sCT(/case /g; s/):/:/g; s/epsCT(2)/eps2/g;
&lt;span class="w"&gt; &lt;/span&gt;               s/eps(CT(2))/eps2/g;&amp;#39;
&lt;span class="w"&gt; &lt;/span&gt;    */
&lt;span class="gd"&gt;-    static inline evaluate_coeffs_C1(CT eps, CT c[]) {&lt;/span&gt;
&lt;span class="gi"&gt;+    static inline void evaluate_coeffs_C1(CT eps, CT c[])&lt;/span&gt;
&lt;span class="gi"&gt;+    {&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT eps2 = math::sqr(eps);
&lt;span class="w"&gt; &lt;/span&gt;        CT d = eps;
&lt;span class="w"&gt; &lt;/span&gt;        switch (SeriesOrder) {
&lt;span class="gu"&gt;@@ -267,6 +269,38 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        }
&lt;span class="w"&gt; &lt;/span&gt;    }

&lt;span class="gi"&gt;+    /*&lt;/span&gt;
&lt;span class="gi"&gt;+     Evaluate&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+     y = sum(c[i] * sin(2*i * x), i, 1, n)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+     using Clenshaw summation.&lt;/span&gt;
&lt;span class="gi"&gt;+    */&lt;/span&gt;
&lt;span class="gi"&gt;+    static inline CT sin_cos_series(CT sinx,&lt;/span&gt;
&lt;span class="gi"&gt;+                                    CT cosx,&lt;/span&gt;
&lt;span class="gi"&gt;+                                    const CT coeffs[])&lt;/span&gt;
&lt;span class="gi"&gt;+    {&lt;/span&gt;
&lt;span class="gi"&gt;+        std::size_t n = SeriesOrder;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        // Point to one beyond last element.&lt;/span&gt;
&lt;span class="gi"&gt;+        coeffs += (n + 1);&lt;/span&gt;
&lt;span class="gi"&gt;+        CT ar = 2 * (cosx - sinx) * (cosx + sinx);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        CT k0 = n &amp;amp; 1 ? *--coeffs : 0;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT k1 = 0;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        // Make n even.&lt;/span&gt;
&lt;span class="gi"&gt;+        n /= 2;&lt;/span&gt;
&lt;span class="gi"&gt;+        while (n--) {&lt;/span&gt;
&lt;span class="gi"&gt;+          // Unroll loop x 2, so accumulators return to their original role.&lt;/span&gt;
&lt;span class="gi"&gt;+          k1 = ar * k0 - k1 + *--coeffs;&lt;/span&gt;
&lt;span class="gi"&gt;+          k0 = ar * k1 - k0 + *--coeffs;&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        return 2 * sinx * cosx * k0;&lt;/span&gt;
&lt;span class="gi"&gt;+    }&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    template &amp;lt;typename T, typename Dist, typename Azi, typename Spheroid&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;    static inline result_type apply(T const&amp;amp; lo1,
&lt;span class="w"&gt; &lt;/span&gt;                                    T const&amp;amp; la1,
&lt;span class="gu"&gt;@@ -278,6 +312,7 @@ public:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        CT const lon1 = lo1;
&lt;span class="w"&gt; &lt;/span&gt;        CT const lat1 = la1;
&lt;span class="gi"&gt;+        Azi azi12 = azimuth12;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        if (math::equals(distance, Dist(0)) || distance &amp;lt; Dist(0))
&lt;span class="w"&gt; &lt;/span&gt;        {
&lt;span class="gu"&gt;@@ -298,13 +333,13 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT const e2 = f * two_minus_f;
&lt;span class="w"&gt; &lt;/span&gt;        CT const ep2 = e2 / math::sqr(one_minus_f);

&lt;span class="gd"&gt;-        azi12 = math::AngNormalize&amp;lt;CT&amp;gt;(azi12);&lt;/span&gt;
&lt;span class="gi"&gt;+        azi12 = math::normalize_angle&amp;lt;CT&amp;gt;(azi12);&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT sin_alpha1, cos_alpha1;
&lt;span class="gd"&gt;-        math::sin_cos_degrees&amp;lt;CT&amp;gt;(azimuth12, sin_alpha1, cos_alpha1);&lt;/span&gt;
&lt;span class="gi"&gt;+        math::sin_cos_degrees&amp;lt;CT&amp;gt;(math::round_angle&amp;lt;CT&amp;gt;(azi12), sin_alpha1, cos_alpha1);&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        // Find the reduced latitude.
&lt;span class="w"&gt; &lt;/span&gt;        CT sin_beta1, cos_beta1;
&lt;span class="gd"&gt;-        math::sin_cos_degrees&amp;lt;CT&amp;gt;(lat1, sin_beta1, cos_beta1);&lt;/span&gt;
&lt;span class="gi"&gt;+        math::sin_cos_degrees&amp;lt;CT&amp;gt;(math::round_angle&amp;lt;CT&amp;gt;(lat1), sin_beta1, cos_beta1);&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        sin_beta1 *= one_minus_f;

&lt;span class="w"&gt; &lt;/span&gt;        // Obtain alpha 0 by solving the spherical triangle.
&lt;span class="gu"&gt;@@ -318,8 +353,14 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        // series expansion using Horner scehme.
&lt;span class="w"&gt; &lt;/span&gt;        CT expansion_A1 = evaluate_series_A1(epsilon);

&lt;span class="gi"&gt;+        // Index zero element of coeffs_C1 is unused.&lt;/span&gt;
&lt;span class="gi"&gt;+        CT coeffs_C1[SeriesOrder + 1];&lt;/span&gt;
&lt;span class="gi"&gt;+        evaluate_coeffs_C1(epsilon, coeffs_C1);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        // Tau is an integration variable.
&lt;span class="w"&gt; &lt;/span&gt;        CT tau12 = distance / (b + (c1 + expansion_A1));
&lt;span class="gi"&gt;+        CT sin_tau12 = sin(tau12);&lt;/span&gt;
&lt;span class="gi"&gt;+        CT cos_tau12 = cos(tau12);&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    }

&lt;span class="w"&gt; &lt;/span&gt;};

commit 320891caefdfbb920b9e81259c14d19889faf70c
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Sat May 19 21:32:09 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [utils] Add function for normalizing and rounding off an angle

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/util/math.hpp b/include/boost/geometry/util/math.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 9d9fa10..4d927b3 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/util/math.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/util/math.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -809,6 +809,36 @@ inline void sin_cos_degrees(T const&amp;amp; x, T &amp;amp; sinx, T &amp;amp; cosx)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    }
&lt;span class="w"&gt; &lt;/span&gt;}

&lt;span class="gi"&gt;+/*!&lt;/span&gt;
&lt;span class="gi"&gt;+\brief Round off a given angle&lt;/span&gt;
&lt;span class="gi"&gt;+*/&lt;/span&gt;
&lt;span class="gi"&gt;+template&amp;lt;typename T&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+inline T round_angle(T x) {&lt;/span&gt;
&lt;span class="gi"&gt;+    static const T z = 1/T(16);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    if (x == 0)&lt;/span&gt;
&lt;span class="gi"&gt;+    {&lt;/span&gt;
&lt;span class="gi"&gt;+        return 0;&lt;/span&gt;
&lt;span class="gi"&gt;+    }&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    T y = std::abs(x);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    // z - (z - y) must not be simplified to y.&lt;/span&gt;
&lt;span class="gi"&gt;+    y = y &amp;lt; z ? z - (z - y) : y;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    return x &amp;lt; 0 ? -y : y;&lt;/span&gt;
&lt;span class="gi"&gt;+}&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+/*!&lt;/span&gt;
&lt;span class="gi"&gt;+\brief Normalize a given angle&lt;/span&gt;
&lt;span class="gi"&gt;+*/&lt;/span&gt;
&lt;span class="gi"&gt;+template&amp;lt;typename T&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    inline T normalize_angle(T x) {&lt;/span&gt;
&lt;span class="gi"&gt;+    T y = std::fmod(x, T(360));&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    return y &amp;lt;= -180 ? y + 360 : (y &amp;lt;= 180 ? y : y - 360);&lt;/span&gt;
&lt;span class="gi"&gt;+}&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;} // namespace math



commit c73ea927c4f88bd3da590757ffa3fec77ba4aa72
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Tue May 15 23:43:52 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [formulas] Add function for evaluating coefficients for C1

&lt;span class="w"&gt; &lt;/span&gt;   - Add SED script for converting x to CT(x)
&lt;span class="w"&gt; &lt;/span&gt;   - Improve code documentation

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/formulas/karney_direct.hpp b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index 900a4da..3057715 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -65,7 +65,27 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;     http://geographiclib.sourceforge.net/html/geod.mac

&lt;span class="w"&gt; &lt;/span&gt;        // Maxima script begin
&lt;span class="gd"&gt;-        codeA1(maxpow):=block([tab2:&amp;quot;    &amp;quot;,tab3:&amp;quot;        &amp;quot;],&lt;/span&gt;
&lt;span class="gi"&gt;+        taylordepth:5$&lt;/span&gt;
&lt;span class="gi"&gt;+        ataylor(expr,var,ord):=expand(ratdisrep(taylor(expr,var,0,ord)))$&lt;/span&gt;
&lt;span class="gi"&gt;+        jtaylor(expr,var1,var2,ord):=block([zz],expand(subst([zz=1],&lt;/span&gt;
&lt;span class="gi"&gt;+        ratdisrep(taylor(subst([var1=zz*var1,var2=zz*var2],expr),zz,0,ord)))))$&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        computeintegral(maxpow):=block([sintegrand,sintegrandexp,s,sigma,tau1,k2,eps],&lt;/span&gt;
&lt;span class="gi"&gt;+          sintegrand:sqrt(1+k2*sin(sigma)^2),&lt;/span&gt;
&lt;span class="gi"&gt;+          sintegrandexp:ataylor(&lt;/span&gt;
&lt;span class="gi"&gt;+              (1-eps)*subst([k2=4*eps/(1-eps)^2],sintegrand),&lt;/span&gt;
&lt;span class="gi"&gt;+              eps,maxpow),&lt;/span&gt;
&lt;span class="gi"&gt;+          s:trigreduce(integrate(sintegrandexp,sigma)),&lt;/span&gt;
&lt;span class="gi"&gt;+          s:s-subst(sigma=0,s),&lt;/span&gt;
&lt;span class="gi"&gt;+          A1:expand(subst(sigma=2*%pi,s)/(2*%pi)),&lt;/span&gt;
&lt;span class="gi"&gt;+          tau1:ataylor(s/A1,eps,maxpow),&lt;/span&gt;
&lt;span class="gi"&gt;+          for i:1 thru maxpow do C1[i]:coeff(tau1,sin(2*i*sigma)),&lt;/span&gt;
&lt;span class="gi"&gt;+          if expand(tau1-sigma-sum(C1[i]*sin(2*i*sigma),i,1,maxpow)) # 0&lt;/span&gt;
&lt;span class="gi"&gt;+          then error(&amp;quot;left over terms in B1&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+          A1:A1/(1-eps),&lt;/span&gt;
&lt;span class="gi"&gt;+          &amp;#39;done)$&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        generatecode(maxpow):=block([tab2:&amp;quot;    &amp;quot;,tab3:&amp;quot;        &amp;quot;],&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        print(&amp;quot;// The scale factor A1-1 = mean value of (d/dsigma)I1 - 1
&lt;span class="w"&gt; &lt;/span&gt;        static inline CT evaluate_series_A1(CT eps) {
&lt;span class="w"&gt; &lt;/span&gt;            CT eps2 = math::sqr(eps);
&lt;span class="gu"&gt;@@ -81,9 +101,17 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            return (t + eps) / (1 - eps);
&lt;span class="w"&gt; &lt;/span&gt;        }&amp;quot;),
&lt;span class="w"&gt; &lt;/span&gt;        &amp;#39;done)$
&lt;span class="gd"&gt;-        codeA1(8)$        &lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        maxpow:8$&lt;/span&gt;
&lt;span class="gi"&gt;+        computeintegral(maxpow)$&lt;/span&gt;
&lt;span class="gi"&gt;+        generatecode(maxpow)$&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        // Maxima script end
&lt;span class="gd"&gt;-     */&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+     To replace each number x by CT(x) the following&lt;/span&gt;
&lt;span class="gi"&gt;+     scirpt can be used:&lt;/span&gt;
&lt;span class="gi"&gt;+       sed -e &amp;#39;s/[0-9]\+/CT(&amp;amp;)/g; s/\[CT/\[/g; s/)\]/\]/g;&lt;/span&gt;
&lt;span class="gi"&gt;+               s/case\sCT(/case /g; s/):/:/g; s/epsCT(2)/eps2/g;&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+    */&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    static inline CT evaluate_series_A1(CT eps) {
&lt;span class="w"&gt; &lt;/span&gt;        CT eps2 = math::sqr(eps);
&lt;span class="w"&gt; &lt;/span&gt;        CT t;
&lt;span class="gu"&gt;@@ -107,6 +135,138 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        return (t + eps) / (CT(1) - eps);
&lt;span class="w"&gt; &lt;/span&gt;    }

&lt;span class="gi"&gt;+    /*&lt;/span&gt;
&lt;span class="gi"&gt;+     The coefficients C1[l] in the Fourier expansion of B1&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+     The expansion below is performed in Maxima, a Computer Algebra System.&lt;/span&gt;
&lt;span class="gi"&gt;+     The C++ code (that yields the function evaluate_series_A1 below) is&lt;/span&gt;
&lt;span class="gi"&gt;+     generated by the following Maxima script and is based on script:&lt;/span&gt;
&lt;span class="gi"&gt;+     http://geographiclib.sourceforge.net/html/geod.mac&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        // Maxima script begin&lt;/span&gt;
&lt;span class="gi"&gt;+        taylordepth:5$&lt;/span&gt;
&lt;span class="gi"&gt;+        ataylor(expr,var,ord):=expand(ratdisrep(taylor(expr,var,0,ord)))$&lt;/span&gt;
&lt;span class="gi"&gt;+        jtaylor(expr,var1,var2,ord):=block([zz],expand(subst([zz=1],&lt;/span&gt;
&lt;span class="gi"&gt;+        ratdisrep(taylor(subst([var1=zz*var1,var2=zz*var2],expr),zz,0,ord)))))$&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        generatecode(maxpow):=block([tab2:&amp;quot;    &amp;quot;,tab3:&amp;quot;        &amp;quot;],&lt;/span&gt;
&lt;span class="gi"&gt;+          print(&amp;quot;// The coefficients C1[l] in the Fourier expansion of B1&lt;/span&gt;
&lt;span class="gi"&gt;+        static inline evaluate_coeffs_C1(CT eps, CT c[]) {&lt;/span&gt;
&lt;span class="gi"&gt;+            CT eps2 = math::sqr(eps);&lt;/span&gt;
&lt;span class="gi"&gt;+            CT d = eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            switch (SeriesOrder) {&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+          for n:0 thru maxpow do (&lt;/span&gt;
&lt;span class="gi"&gt;+            print(concat(tab2,&amp;quot;case &amp;quot;,string(n),&amp;quot;:&amp;quot;)),&lt;/span&gt;
&lt;span class="gi"&gt;+            for m:1 thru n do block([q:d*horner(&lt;/span&gt;
&lt;span class="gi"&gt;+                subst([eps=sqrt(eps2)],ataylor(C1[m],eps,n)/eps^m)),&lt;/span&gt;
&lt;span class="gi"&gt;+              linel:1200],&lt;/span&gt;
&lt;span class="gi"&gt;+              if m&amp;gt;1 then print(concat(tab3,&amp;quot;d *= eps;&amp;quot;)),&lt;/span&gt;
&lt;span class="gi"&gt;+              print(concat(tab3,&amp;quot;c[&amp;quot;,string(m),&amp;quot;] = &amp;quot;,string(q),&amp;quot;;&amp;quot;))),&lt;/span&gt;
&lt;span class="gi"&gt;+            print(concat(tab3,&amp;quot;break;&amp;quot;))),&lt;/span&gt;
&lt;span class="gi"&gt;+          print(&amp;quot;    }&lt;/span&gt;
&lt;span class="gi"&gt;+        }&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;#39;done)$&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        maxpow:8$&lt;/span&gt;
&lt;span class="gi"&gt;+        computeintegral(maxpow)$&lt;/span&gt;
&lt;span class="gi"&gt;+        generatecode(maxpow)$&lt;/span&gt;
&lt;span class="gi"&gt;+        // Maxima script end&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+     To replace each number x by CT(x) the following&lt;/span&gt;
&lt;span class="gi"&gt;+     scirpt can be used:&lt;/span&gt;
&lt;span class="gi"&gt;+       sed -e &amp;#39;s/[0-9]\+/CT(&amp;amp;)/g; s/\[CT(/\[/g; s/)\]/\]/g;&lt;/span&gt;
&lt;span class="gi"&gt;+               s/case\sCT(/case /g; s/):/:/g; s/epsCT(2)/eps2/g;&lt;/span&gt;
&lt;span class="gi"&gt;+               s/eps(CT(2))/eps2/g;&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+    */&lt;/span&gt;
&lt;span class="gi"&gt;+    static inline evaluate_coeffs_C1(CT eps, CT c[]) {&lt;/span&gt;
&lt;span class="gi"&gt;+        CT eps2 = math::sqr(eps);&lt;/span&gt;
&lt;span class="gi"&gt;+        CT d = eps;&lt;/span&gt;
&lt;span class="gi"&gt;+        switch (SeriesOrder) {&lt;/span&gt;
&lt;span class="gi"&gt;+        case 0:&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 1:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = -d/CT(2);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 2:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = -d/CT(2);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[2] = -d/CT(16);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 3:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = d*(CT(3)*eps2-CT(8))/CT(16);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[2] = -d/CT(16);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[3] = -d/CT(48);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 4:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = d*(CT(3)*eps2-CT(8))/CT(16);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[2] = d*(eps2-CT(2))/CT(32);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[3] = -d/CT(48);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[4] = -CT(5)*d/CT(512);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 5:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = d*((CT(6)-eps2)*eps2-CT(16))/CT(32);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[2] = d*(eps2-CT(2))/CT(32);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[3] = d*(CT(9)*eps2-CT(16))/CT(768);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[4] = -CT(5)*d/CT(512);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[5] = -CT(7)*d/CT(1280);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 6:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = d*((CT(6)-eps2)*eps2-CT(16))/CT(32);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[2] = d*((CT(64)-CT(9)*eps2)*eps2-CT(128))/CT(2048);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[3] = d*(CT(9)*eps2-CT(16))/CT(768);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[4] = d*(CT(3)*eps2-CT(5))/CT(512);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[5] = -CT(7)*d/CT(1280);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[6] = -CT(7)*d/CT(2048);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 7:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = d*(eps2*(eps2*(CT(19)*eps2-CT(64))+CT(384))-CT(1024))/CT(2048);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[2] = d*((CT(64)-CT(9)*eps2)*eps2-CT(128))/CT(2048);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[3] = d*((CT(72)-CT(9)*eps2)*eps2-CT(128))/CT(6144);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[4] = d*(CT(3)*eps2-CT(5))/CT(512);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[5] = d*(CT(35)*eps2-CT(56))/CT(10240);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[6] = -CT(7)*d/CT(2048);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[7] = -CT(33)*d/CT(14336);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 8:&lt;/span&gt;
&lt;span class="gi"&gt;+            c[1] = d*(eps2*(eps2*(CT(19)*eps2-CT(64))+CT(384))-CT(1024))/CT(2048);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[2] = d*(eps2*(eps2*(CT(7)*eps2-CT(18))+CT(128))-CT(256))/CT(4096);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[3] = d*((CT(72)-CT(9)*eps2)*eps2-CT(128))/CT(6144);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[4] = d*((CT(96)-CT(11)*eps2)*eps2-CT(160))/CT(16384);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[5] = d*(CT(35)*eps2-CT(56))/CT(10240);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[6] = d*(CT(9)*eps2-CT(14))/CT(4096);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[7] = -CT(33)*d/CT(14336);&lt;/span&gt;
&lt;span class="gi"&gt;+            d *= eps;&lt;/span&gt;
&lt;span class="gi"&gt;+            c[8] = -CT(429)*d/CT(262144);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+    }&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    template &amp;lt;typename T, typename Dist, typename Azi, typename Spheroid&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;    static inline result_type apply(T const&amp;amp; lo1,
&lt;span class="w"&gt; &lt;/span&gt;                                    T const&amp;amp; la1,
&lt;span class="gu"&gt;@@ -138,21 +298,27 @@ public:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT const e2 = f * two_minus_f;
&lt;span class="w"&gt; &lt;/span&gt;        CT const ep2 = e2 / math::sqr(one_minus_f);

&lt;span class="gi"&gt;+        azi12 = math::AngNormalize&amp;lt;CT&amp;gt;(azi12);&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT sin_alpha1, cos_alpha1;
&lt;span class="w"&gt; &lt;/span&gt;        math::sin_cos_degrees&amp;lt;CT&amp;gt;(azimuth12, sin_alpha1, cos_alpha1);

&lt;span class="gd"&gt;-        // The reduced latitude.&lt;/span&gt;
&lt;span class="gi"&gt;+        // Find the reduced latitude.&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT sin_beta1, cos_beta1;
&lt;span class="w"&gt; &lt;/span&gt;        math::sin_cos_degrees&amp;lt;CT&amp;gt;(lat1, sin_beta1, cos_beta1);
&lt;span class="w"&gt; &lt;/span&gt;        sin_beta1 *= one_minus_f;

&lt;span class="gi"&gt;+        // Obtain alpha 0 by solving the spherical triangle.&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT cos_alpha0 = boost::math::hypot(cos_alpha1, sin_alpha1 * sin_beta1);

&lt;span class="w"&gt; &lt;/span&gt;        CT k2 = math::sqr(cos_alpha0) * ep2;

&lt;span class="w"&gt; &lt;/span&gt;        CT epsilon = k2 / (c2 * (c1 + std::sqrt(c1 + k2)) + k2);

&lt;span class="gi"&gt;+        // Find the coefficients for Aj by computing the&lt;/span&gt;
&lt;span class="gi"&gt;+        // series expansion using Horner scehme.&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT expansion_A1 = evaluate_series_A1(epsilon);
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        // Tau is an integration variable.&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        CT tau12 = distance / (b + (c1 + expansion_A1));
&lt;span class="w"&gt; &lt;/span&gt;    }


commit 8d5d3bc8a96b819830a25d96bd1200d8389f68da
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Sat May 12 22:09:26 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [formulas] Add draft of direct geodesic problem from Karney (2011)

&lt;span class="w"&gt; &lt;/span&gt;   The paper can be found at: https://arxiv.org/pdf/1109.4448.pdf
&lt;span class="w"&gt; &lt;/span&gt;   This commit also introduces the evaluate_series_A1 function
&lt;span class="w"&gt; &lt;/span&gt;   for evaluating the series expantion, which was generated
&lt;span class="w"&gt; &lt;/span&gt;   using Maxima: http://maxima.sourceforge.net

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/formulas/karney_direct.hpp b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
new file mode 100644
&lt;span class="gh"&gt;index 0000000..900a4da&lt;/span&gt;
&lt;span class="gd"&gt;--- /dev/null&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/formulas/karney_direct.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -0,0 +1,164 @@&lt;/span&gt;
&lt;span class="gi"&gt;+// Boost.Geometry&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+// Use, modification and distribution is subject to the Boost Software License,&lt;/span&gt;
&lt;span class="gi"&gt;+// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at&lt;/span&gt;
&lt;span class="gi"&gt;+// http://www.boost.org/LICENSE_1_0.txt)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+#ifndef BOOST_GEOMETRY_FORMULAS_KARNEY_DIRECT_HPP&lt;/span&gt;
&lt;span class="gi"&gt;+#define BOOST_GEOMETRY_FORMULAS_KARNEY_DIRECT_HPP&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+#include &amp;lt;boost/math/constants/constants.hpp&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+#include &amp;lt;boost/geometry/util/math.hpp&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+#include &amp;lt;boost/geometry/formulas/flattening.hpp&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+#include &amp;lt;boost/geometry/formulas/result_direct.hpp&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+namespace boost { namespace geometry { namespace formula&lt;/span&gt;
&lt;span class="gi"&gt;+{&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+/*!&lt;/span&gt;
&lt;span class="gi"&gt;+\brief The solution of the direct problem of geodesics on latlong coordinates,&lt;/span&gt;
&lt;span class="gi"&gt;+       after Karney (2011).&lt;/span&gt;
&lt;span class="gi"&gt;+\author See&lt;/span&gt;
&lt;span class="gi"&gt;+- Charles F.F Karney, Algorithms for geodesics, 2011&lt;/span&gt;
&lt;span class="gi"&gt;+https://arxiv.org/pdf/1109.4448.pdf&lt;/span&gt;
&lt;span class="gi"&gt;+*/&lt;/span&gt;
&lt;span class="gi"&gt;+template &amp;lt;&lt;/span&gt;
&lt;span class="gi"&gt;+    typename CT,&lt;/span&gt;
&lt;span class="gi"&gt;+    std::size_t SeriesOrder = 8,&lt;/span&gt;
&lt;span class="gi"&gt;+    bool EnableCoordinates = true,&lt;/span&gt;
&lt;span class="gi"&gt;+    bool EnableReverseAzimuth = false,&lt;/span&gt;
&lt;span class="gi"&gt;+    bool EnableReducedLength = false,&lt;/span&gt;
&lt;span class="gi"&gt;+    bool EnableGeodesicScale = false&lt;/span&gt;
&lt;span class="gi"&gt;+&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+class karney_direct&lt;/span&gt;
&lt;span class="gi"&gt;+{&lt;/span&gt;
&lt;span class="gi"&gt;+    static const bool CalcQuantities = EnableReducedLength || EnableGeodesicScale;&lt;/span&gt;
&lt;span class="gi"&gt;+    static const bool CalcCoordinates = EnableCoordinates || CalcQuantities;&lt;/span&gt;
&lt;span class="gi"&gt;+    static const bool CalcRevAzimuth = EnableReverseAzimuth || CalcCoordinates || CalcQuantities;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+public:&lt;/span&gt;
&lt;span class="gi"&gt;+    typedef result_direct&amp;lt;CT&amp;gt; result_type;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    /*&lt;/span&gt;
&lt;span class="gi"&gt;+     Generate and evaluate the series expansion of the following integral&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+     I1 = integrate( sqrt(1+k2*sin(sigma1)^2), sigma1, 0, sigma )&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+     which is valid for k2 small. We substitute k2 = 4 * eps / (1 - eps)^2&lt;/span&gt;
&lt;span class="gi"&gt;+     and expand (1 - eps) * I1 retaining terms up to order eps^maxpow&lt;/span&gt;
&lt;span class="gi"&gt;+     in A1 and C1[l].&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+     The resulting series is of the form&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+     A1 * ( sigma + sum(C1[l] * sin(2*l*sigma), l, 1, maxpow) ).&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+     The scale factor A1-1 = mean value of (d/dsigma)I1 - 1&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+     The expansion above is performed in Maxima, a Computer Algebra System.&lt;/span&gt;
&lt;span class="gi"&gt;+     The C++ code (that yields the function evaluate_series_A1 below) is&lt;/span&gt;
&lt;span class="gi"&gt;+     generated by the following Maxima script and is based on script:&lt;/span&gt;
&lt;span class="gi"&gt;+     http://geographiclib.sourceforge.net/html/geod.mac&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        // Maxima script begin&lt;/span&gt;
&lt;span class="gi"&gt;+        codeA1(maxpow):=block([tab2:&amp;quot;    &amp;quot;,tab3:&amp;quot;        &amp;quot;],&lt;/span&gt;
&lt;span class="gi"&gt;+        print(&amp;quot;// The scale factor A1-1 = mean value of (d/dsigma)I1 - 1&lt;/span&gt;
&lt;span class="gi"&gt;+        static inline CT evaluate_series_A1(CT eps) {&lt;/span&gt;
&lt;span class="gi"&gt;+            CT eps2 = math::sqr(eps);&lt;/span&gt;
&lt;span class="gi"&gt;+            CT t;&lt;/span&gt;
&lt;span class="gi"&gt;+            switch (SeriesOrder/2) {&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+          for n:0 thru entier(maxpow/2) do block([&lt;/span&gt;
&lt;span class="gi"&gt;+            q:horner(ataylor(subst([eps=sqrt(eps2)],A1*(1-eps)-1),eps2,n)),&lt;/span&gt;
&lt;span class="gi"&gt;+            linel:1200],&lt;/span&gt;
&lt;span class="gi"&gt;+            print(concat(tab2,&amp;quot;case &amp;quot;,string(n),&amp;quot;:&amp;quot;)),&lt;/span&gt;
&lt;span class="gi"&gt;+            print(concat(tab3,&amp;quot;t = &amp;quot;,string(q),&amp;quot;;&amp;quot;)),&lt;/span&gt;
&lt;span class="gi"&gt;+            print(concat(tab3,&amp;quot;break;&amp;quot;))),&lt;/span&gt;
&lt;span class="gi"&gt;+          print(&amp;quot;    }&lt;/span&gt;
&lt;span class="gi"&gt;+            return (t + eps) / (1 - eps);&lt;/span&gt;
&lt;span class="gi"&gt;+        }&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;#39;done)$&lt;/span&gt;
&lt;span class="gi"&gt;+        codeA1(8)$        &lt;/span&gt;
&lt;span class="gi"&gt;+        // Maxima script end&lt;/span&gt;
&lt;span class="gi"&gt;+     */&lt;/span&gt;
&lt;span class="gi"&gt;+    static inline CT evaluate_series_A1(CT eps) {&lt;/span&gt;
&lt;span class="gi"&gt;+        CT eps2 = math::sqr(eps);&lt;/span&gt;
&lt;span class="gi"&gt;+        CT t;&lt;/span&gt;
&lt;span class="gi"&gt;+        switch (SeriesOrder/2) {&lt;/span&gt;
&lt;span class="gi"&gt;+        case 0:&lt;/span&gt;
&lt;span class="gi"&gt;+            t = CT(0);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 1:&lt;/span&gt;
&lt;span class="gi"&gt;+            t = eps2/CT(4);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 2:&lt;/span&gt;
&lt;span class="gi"&gt;+            t = eps2*(eps2+CT(16))/CT(64);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 3:&lt;/span&gt;
&lt;span class="gi"&gt;+            t = eps2*(eps2*(eps2+CT(4))+CT(64))/CT(256);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 4:&lt;/span&gt;
&lt;span class="gi"&gt;+            t = eps2*(eps2*(eps2*(CT(25)*eps2+CT(64))+CT(256))+CT(4096))/CT(16384);&lt;/span&gt;
&lt;span class="gi"&gt;+            break;&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+        return (t + eps) / (CT(1) - eps);&lt;/span&gt;
&lt;span class="gi"&gt;+    }&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    template &amp;lt;typename T, typename Dist, typename Azi, typename Spheroid&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    static inline result_type apply(T const&amp;amp; lo1,&lt;/span&gt;
&lt;span class="gi"&gt;+                                    T const&amp;amp; la1,&lt;/span&gt;
&lt;span class="gi"&gt;+                                    Dist const&amp;amp; distance,&lt;/span&gt;
&lt;span class="gi"&gt;+                                    Azi const&amp;amp; azimuth12,&lt;/span&gt;
&lt;span class="gi"&gt;+                                    Spheroid const&amp;amp; spheroid)&lt;/span&gt;
&lt;span class="gi"&gt;+    {&lt;/span&gt;
&lt;span class="gi"&gt;+        result_type result;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const lon1 = lo1;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const lat1 = la1;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        if (math::equals(distance, Dist(0)) || distance &amp;lt; Dist(0))&lt;/span&gt;
&lt;span class="gi"&gt;+        {&lt;/span&gt;
&lt;span class="gi"&gt;+            result.lon2 = lon1;&lt;/span&gt;
&lt;span class="gi"&gt;+            result.lat2 = lat1;&lt;/span&gt;
&lt;span class="gi"&gt;+            return result;&lt;/span&gt;
&lt;span class="gi"&gt;+        }&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const c1 = 1;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const c2 = 2;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const a = CT(get_radius&amp;lt;0&amp;gt;(spheroid));&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const b = CT(get_radius&amp;lt;2&amp;gt;(spheroid));&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const f = formula::flattening&amp;lt;CT&amp;gt;(spheroid);&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const one_minus_f = c1 - f;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const two_minus_f = c2 - f;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const e2 = f * two_minus_f;&lt;/span&gt;
&lt;span class="gi"&gt;+        CT const ep2 = e2 / math::sqr(one_minus_f);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        CT sin_alpha1, cos_alpha1;&lt;/span&gt;
&lt;span class="gi"&gt;+        math::sin_cos_degrees&amp;lt;CT&amp;gt;(azimuth12, sin_alpha1, cos_alpha1);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        // The reduced latitude.&lt;/span&gt;
&lt;span class="gi"&gt;+        CT sin_beta1, cos_beta1;&lt;/span&gt;
&lt;span class="gi"&gt;+        math::sin_cos_degrees&amp;lt;CT&amp;gt;(lat1, sin_beta1, cos_beta1);&lt;/span&gt;
&lt;span class="gi"&gt;+        sin_beta1 *= one_minus_f;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        CT cos_alpha0 = boost::math::hypot(cos_alpha1, sin_alpha1 * sin_beta1);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        CT k2 = math::sqr(cos_alpha0) * ep2;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        CT epsilon = k2 / (c2 * (c1 + std::sqrt(c1 + k2)) + k2);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        CT expansion_A1 = evaluate_series_A1(epsilon);&lt;/span&gt;
&lt;span class="gi"&gt;+        CT tau12 = distance / (b + (c1 + expansion_A1));&lt;/span&gt;
&lt;span class="gi"&gt;+    }&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+};&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+}}} // namespace boost::geometry::formula&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+#endif // BOOST_GEOMETRY_FORMULAS_KARNEY_DIRECT_HPP&lt;/span&gt;

commit 302e24066776301b5fdae795d43e3497a137678e
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Sat May 12 22:08:25 2018 +0500

&lt;span class="w"&gt; &lt;/span&gt;   [utils] Add function sin_cos_degrees for evaluating sine and cosine function

&lt;span class="gh"&gt;diff --git a/include/boost/geometry/util/math.hpp b/include/boost/geometry/util/math.hpp&lt;/span&gt;
&lt;span class="gh"&gt;index b1c3648..9d9fa10 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/include/boost/geometry/util/math.hpp&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/include/boost/geometry/util/math.hpp&lt;/span&gt;
&lt;span class="gu"&gt;@@ -771,6 +771,44 @@ inline Result rounding_cast(T const&amp;amp; v)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    return detail::rounding_cast&amp;lt;Result, T&amp;gt;::apply(v);
&lt;span class="w"&gt; &lt;/span&gt;}

&lt;span class="gi"&gt;+/*!&lt;/span&gt;
&lt;span class="gi"&gt;+\brief Evaluate the sine and cosine function with the argument in degrees&lt;/span&gt;
&lt;span class="gi"&gt;+\note The results obey exactly the elementary properties of the trigonometric&lt;/span&gt;
&lt;span class="gi"&gt;+      functions, e.g., sin 9&amp;amp;deg; = cos 81&amp;amp;deg; = &amp;amp;minus; sin 123456789&amp;amp;deg;.&lt;/span&gt;
&lt;span class="gi"&gt;+      If x = &amp;amp;minus;0, then \e sinx = &amp;amp;minus;0; this is the only case where&lt;/span&gt;
&lt;span class="gi"&gt;+      &amp;amp;minus;0 is returned.&lt;/span&gt;
&lt;span class="gi"&gt;+*/&lt;/span&gt;
&lt;span class="gi"&gt;+template&amp;lt;typename T&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+inline void sin_cos_degrees(T const&amp;amp; x, T &amp;amp; sinx, T &amp;amp; cosx)&lt;/span&gt;
&lt;span class="gi"&gt;+{&lt;/span&gt;
&lt;span class="gi"&gt;+    // In order to minimize round-off errors, this function exactly reduces&lt;/span&gt;
&lt;span class="gi"&gt;+    // the argument to the range [-45, 45] before converting it to radians.&lt;/span&gt;
&lt;span class="gi"&gt;+    T remainder; int quotient;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    remainder = std::fmod(x, T(360));&lt;/span&gt;
&lt;span class="gi"&gt;+    quotient = int(std::floor(remainder / 90 + T(0.5)));&lt;/span&gt;
&lt;span class="gi"&gt;+    remainder -= 90 * quotient;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    // Convert to radians.&lt;/span&gt;
&lt;span class="gi"&gt;+    remainder = as_radian&amp;lt;T&amp;gt;(remainder);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    T s = std::sin(remainder), c = std::cos(remainder);&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    switch (unsigned(quotient) &amp;amp; 3U)&lt;/span&gt;
&lt;span class="gi"&gt;+    {&lt;/span&gt;
&lt;span class="gi"&gt;+        case 0U: sinx =  s; cosx =  c; break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 1U: sinx =  c; cosx = -s; break;&lt;/span&gt;
&lt;span class="gi"&gt;+        case 2U: sinx = -s; cosx = -c; break;&lt;/span&gt;
&lt;span class="gi"&gt;+        default: sinx = -c; cosx =  s; break; // case 3U&lt;/span&gt;
&lt;span class="gi"&gt;+    }&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    // Set sign of 0 results. -0 only produced for sin(-0).&lt;/span&gt;
&lt;span class="gi"&gt;+    if (x != 0)&lt;/span&gt;
&lt;span class="gi"&gt;+    {&lt;/span&gt;
&lt;span class="gi"&gt;+        sinx += T(0); cosx += T(0);&lt;/span&gt;
&lt;span class="gi"&gt;+    }&lt;/span&gt;
&lt;span class="gi"&gt;+}&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;} // namespace math
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content><category term="GSoC"/></entry><entry><title>Using variadic templates with lambda expressions in C++ for constrained optimization</title><link href="https://adl1995.github.io/using-variadic-templates-with-lambda-expressions-in-c-for-constrained-optimization.html" rel="alternate"/><published>2018-06-01T15:44:00+02:00</published><updated>2018-06-01T15:44:00+02:00</updated><author><name>Adeel Ahmad</name></author><id>tag:adl1995.github.io,2018-06-01:/using-variadic-templates-with-lambda-expressions-in-c-for-constrained-optimization.html</id><summary type="html">&lt;p&gt;Constrained optimization problems are encountered in numerous domains, such as &lt;a href="https://www.princeton.edu/~fhs/paper199/paper199.pdf"&gt;protein folding&lt;/a&gt;, &lt;a href="http://cai2r.net/public/documents/FlorianKnoll_thesis.pdf"&gt;Magnetic Resonance Image reconstruction&lt;/a&gt;, and &lt;a href="https://ieeexplore.ieee.org/document/4100822"&gt;radiation therapy&lt;/a&gt;. In this problem, we are given with an objective function which is to be minimized or maximized with respect to constraints on some variables. The constraints can either be soft constraints or hard constraints, which can be specified by boolean operators, such as equality, relational, and conditional&amp;nbsp;operators. &lt;/p&gt;
&lt;p&gt;This post provides insight on how to model constraints using lambda expressions, and how to pass a varying number of constraints to a function using variadic templates. Before moving on with the C++ implementation, it will be helpful to review how variadic functions are used in C and how they differ from the …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Constrained optimization problems are encountered in numerous domains, such as &lt;a href="https://www.princeton.edu/~fhs/paper199/paper199.pdf"&gt;protein folding&lt;/a&gt;, &lt;a href="http://cai2r.net/public/documents/FlorianKnoll_thesis.pdf"&gt;Magnetic Resonance Image reconstruction&lt;/a&gt;, and &lt;a href="https://ieeexplore.ieee.org/document/4100822"&gt;radiation therapy&lt;/a&gt;. In this problem, we are given with an objective function which is to be minimized or maximized with respect to constraints on some variables. The constraints can either be soft constraints or hard constraints, which can be specified by boolean operators, such as equality, relational, and conditional&amp;nbsp;operators. &lt;/p&gt;
&lt;p&gt;This post provides insight on how to model constraints using lambda expressions, and how to pass a varying number of constraints to a function using variadic templates. Before moving on with the C++ implementation, it will be helpful to review how variadic functions are used in C and how they differ from the current&amp;nbsp;standard.&lt;/p&gt;
&lt;h1&gt;C-style variadic&amp;nbsp;functions&lt;/h1&gt;
&lt;p&gt;Support for &lt;a href="https://en.wikipedia.org/wiki/Variadic_macro"&gt;variadic macros&lt;/a&gt; was introduced in C as part of the &lt;a href="https://en.wikipedia.org/wiki/C99"&gt;C99 standard&lt;/a&gt;. This allowed users to define functions that could take a varying number of arguments. A common example of this is the &lt;a href="http://www.cplusplus.com/reference/cstdio/printf"&gt;printf&lt;/a&gt; library function, which can be used to print an arbitrary number of&amp;nbsp;values.&lt;/p&gt;
&lt;p&gt;The &lt;a href="http://www.cplusplus.com/reference/cstdarg"&gt;cstdarg&lt;/a&gt; header file defines the necessary macros to create variadic functions. The syntax requires specifying the function name followed by its arguments. The last argument is always the&amp;nbsp;ellipsis &lt;code&gt;...&lt;/code&gt; operator. An example of this is shown&amp;nbsp;below:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;print_arguments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nargs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;...)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// (1)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kt"&gt;va_list&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;vl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;                          &lt;/span&gt;&lt;span class="c1"&gt;// (2)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;va_start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nargs&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;                 &lt;/span&gt;&lt;span class="c1"&gt;// (3)&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nargs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;%s&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;va_arg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="c1"&gt;// (4)&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;va_end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;                          &lt;/span&gt;&lt;span class="c1"&gt;// (5)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Calling the above function&amp;nbsp;as &lt;code&gt;print_arguments(5, "h", "e", "l", "l", "o")&lt;/code&gt; will produce the&amp;nbsp;output &lt;code&gt;hello&lt;/code&gt;. Let&amp;#8217;s go over the function definition&amp;nbsp;step-by-step:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;nargs&lt;/code&gt; is the first argument which, in this case, specifies the argument count this function should expect. The&amp;nbsp;ellipsis &lt;code&gt;...&lt;/code&gt; operator specifies an arbitrary number of&amp;nbsp;arguments.&lt;/li&gt;
&lt;li&gt;An object of &lt;a href="http://www.cplusplus.com/reference/cstdarg/va_list"&gt;va_list&lt;/a&gt; is created to hold information about the variable&amp;nbsp;arguments.&lt;/li&gt;
&lt;li&gt;The variable argument&amp;nbsp;list &lt;code&gt;vl&lt;/code&gt; is initialized using the &lt;a href="http://www.cplusplus.com/reference/cstdarg/va_start"&gt;va_start&lt;/a&gt; macro.&amp;nbsp;Here &lt;code&gt;nargs&lt;/code&gt; specifies the number of&amp;nbsp;arguments.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.cplusplus.com/reference/cstdarg/va_arg"&gt;va_arg&lt;/a&gt; retrieves the next argument. Each call to this macro modifies the state&amp;nbsp;of &lt;code&gt;vl&lt;/code&gt; to point to the next element. Notice that we had to explicitly pass the function type, so this function is not type&amp;nbsp;agnostic.&lt;/li&gt;
&lt;li&gt;The &lt;a href="http://www.cplusplus.com/reference/cstdarg/va_end"&gt;va_end&lt;/a&gt; macro should be invoked before the function returns. It performs cleanup of&amp;nbsp;the &lt;code&gt;va_list&lt;/code&gt; object so that it&amp;#8217;s no longer&amp;nbsp;usable.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Drawbacks&lt;/h3&gt;
&lt;p&gt;There are several drawbacks using the above function. Firstly, we have to specify the number of arguments as part of the function call, although, this is not necessary, as we could also pass a token specifying argument end, such as&amp;nbsp;the &lt;code&gt;NULL&lt;/code&gt; operator.&amp;nbsp;The &lt;code&gt;printf&lt;/code&gt; function does this by looking at the formatting specifiers&amp;nbsp;(&lt;code&gt;%s&lt;/code&gt; in this case). This is why it fails if the format string does not match the argument&amp;nbsp;list.&lt;/p&gt;
&lt;p&gt;Another major drawback is that we have to know, in advance, the type of data we&amp;#8217;re passing. Passing an incorrect type&amp;nbsp;to &lt;code&gt;va_arg&lt;/code&gt; can result in a segmentation fault. This makes it cumbersome to process data with varying types. Finally, the types are evaluated at run-time, which can be a performance&amp;nbsp;overhead.&lt;/p&gt;
&lt;p&gt;C-style variadic functions are discouraged in C++ as they are not type safe, apart from other issues that are mentioned above. In C++, we have the ability to define variadic templates, which are strongly typed. These are discussed in the following&amp;nbsp;section.&lt;/p&gt;
&lt;h1&gt;Variadic templates in&amp;nbsp;C++&lt;/h1&gt;
&lt;p&gt;Variadic templates became part of the C++ standard in August 2011, commonly known as &lt;a href="https://en.wikipedia.org/wiki/C%2B%2B11"&gt;C++11&lt;/a&gt;. They allow a way of writing functions that take a varying number of arguments in a type safe way using &lt;a href="https://en.cppreference.com/w/cpp/language/parameter_pack#Pack_expansion"&gt;pack expansion&lt;/a&gt;. Another advantage they have over the traditional variadic functions is that all argument handling logic is performed at compile-time, which leads to better&amp;nbsp;performance.&lt;/p&gt;
&lt;p&gt;The example below defines a variadic function template which returns the number of arguments passed to&amp;nbsp;it:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typename&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Types&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;// (1)&lt;/span&gt;
&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nargs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Types&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// (2)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="c1"&gt;// (3)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Calling &lt;code&gt;nargs(1, "2", 3.5)&lt;/code&gt; will&amp;nbsp;return &lt;code&gt;3&lt;/code&gt;. The function details are described&amp;nbsp;below:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We first specify a variadic template&amp;nbsp;using &lt;code&gt;typename... Types&lt;/code&gt;, the contents of which are called parameter&amp;nbsp;packs.&lt;/li&gt;
&lt;li&gt;The parameters packs are unpacked inside the function header. So, in the above case,&amp;nbsp;calling &lt;code&gt;nargs(1, "2", 3.5)&lt;/code&gt; will&amp;nbsp;generate:&lt;/li&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;nargs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;param1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;param2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;param3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;li&gt;The &lt;a href="http://en.cppreference.com/w/cpp/language/sizeof..."&gt;sizeof…&lt;/a&gt; operator queries the number of arguments in a parameter&amp;nbsp;pack.&lt;/li&gt;
&lt;/ol&gt;

&lt;!-- - We first specify a variadic template using `typename... Types`, the contents of which are called parameter packs.
- The parameters packs are unpacked inside the function header. So, in the above case, calling `nargs(1, "2", 3.5)` should generate:


&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nargs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;param1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;param2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;param3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



- The [sizeof&amp;#8230;](http://en.cppreference.com/w/cpp/language/sizeof&amp;#8230;) operator queries the number of arguments in a parameter pack.
&amp;#8212;&gt;

&lt;p&gt;Now let&amp;#8217;s look at an example which makes use of pack expansion. The function below&amp;nbsp;returns &lt;code&gt;true&lt;/code&gt; if all the arguments passed to it&amp;nbsp;are &lt;code&gt;true&lt;/code&gt;, that is, it performs an &lt;span class="caps"&gt;AND&lt;/span&gt;&amp;nbsp;operation.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="c1"&gt;// The base case: just return the passed argument.&lt;/span&gt;
&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typename&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;isTrue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// The recursive case: perform an AND operation. &lt;/span&gt;
&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typename&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;typename&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;isTrue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;isTrue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;...);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It is important to mention that we cannot use the iterative C++  style here. More precisely, we cannot loop over the passed arguments. Instead, variadic templates require functions to be written recursively, thus a separate function for the base case, and the recursive case, as shown&amp;nbsp;above.&lt;/p&gt;
&lt;p&gt;Let&amp;#8217;s trace the above function with&amp;nbsp;the &lt;code&gt;isTrue(true, false, true)&lt;/code&gt; call:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Since the number of arguments are greater that one, the second overloaded function, that is, the recursive case is&amp;nbsp;called.&lt;/li&gt;
&lt;li&gt;The compiler deduces the template type to&amp;nbsp;be &lt;code&gt;bool&lt;/code&gt;, so the parameter pack&amp;nbsp;becomes &lt;code&gt;T = bool; Args = {bool, bool}&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;We now use pack&amp;nbsp;expansion &lt;code&gt;...&lt;/code&gt; with &lt;code&gt;isTrue(args...)&lt;/code&gt;. The compiler translates this&amp;nbsp;into &lt;code&gt;isTrue(false, true)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The header generated&amp;nbsp;above &lt;code&gt;isTrue(false, true)&lt;/code&gt; again calls the recursive&amp;nbsp;case.&lt;/li&gt;
&lt;li&gt;The compiler deduces the type to&amp;nbsp;be &lt;code&gt;T = bool; Args = {bool}&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;This&amp;nbsp;time &lt;code&gt;isTrue(args...)&lt;/code&gt; is translated&amp;nbsp;into &lt;code&gt;isTrue(true)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Since there is only one argument passed&amp;nbsp;to &lt;code&gt;isTrue&lt;/code&gt; we have reached the base case, and the recursion is&amp;nbsp;finished.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This can be confirmed by using&amp;nbsp;the &lt;code&gt;__PRETTY_FUNCTION__&lt;/code&gt; macro, which generates the call stack as listed&amp;nbsp;below:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;isTrue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;...)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;
&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;isTrue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;...)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;
&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;isTrue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: Variadic recursion differs from traditional recursion. In traditional recursion a function calls itself a number of times using the same signature, until it reaches its base case. However, in variadic recursion the function signature is always changing, as the number of arguments are decreasing with each call, so the compiler generates a different function header for each&amp;nbsp;call.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;Constrained optimization problems: a practical&amp;nbsp;use-case&lt;/h1&gt;
&lt;p&gt;A practical use-case where variadic templates can be employed are &lt;a href="https://en.wikipedia.org/wiki/Constrained_optimization#Constraint_optimization_problems"&gt;constrained optimization problems&lt;/a&gt;. They require the state to  satisfy certain constraints for them to be considered eligible. Constraints can be of many types, such as boundary constraints, which can be specified by equality, inequality, and relational&amp;nbsp;operators.&lt;/p&gt;
&lt;p&gt;Let&amp;#8217;s consider the constrained variant of &lt;a href="https://en.wikipedia.org/wiki/Particle_swarm_optimization"&gt;particle swarm optimization&lt;/a&gt; (&lt;span class="caps"&gt;PSO&lt;/span&gt;). The details of this algorithm are beyond the scope of this post. Briefly, &lt;span class="caps"&gt;PSO&lt;/span&gt; tries to find the local or global minimum from a given a set of problems, which must satisfy certain constraints. Suppose there is a&amp;nbsp;class &lt;code&gt;PSO&lt;/code&gt; which provides this implementation. We need to create a convenient &lt;span class="caps"&gt;API&lt;/span&gt; through which a user can pass constraints to a class method,&amp;nbsp;say &lt;code&gt;PSO::optimize&lt;/code&gt;. One option is by passing each constraint as an argument to the class method. However, the number of constraints are arbitrary, and can vary from problem to problem. This can be solved using variadic&amp;nbsp;templates.&lt;/p&gt;
&lt;h3&gt;Lambda&amp;nbsp;expressions&lt;/h3&gt;
&lt;p&gt;Ideally, a constraint should be a function which takes data as input and returns a boolean, which indicates whether the given data satisfies the constraint or not. This can be conveniently specified using a &lt;a href="en.cppreference.com/w/cpp/language/lambda"&gt;lambda expression&lt;/a&gt;, which was introduced in&amp;nbsp;C++11.&lt;/p&gt;
&lt;p&gt;A lambda expression is an inline anonymous function that is simple to read and interpret. It can be specified as&amp;nbsp;follows:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;constraint&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[](&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;a href="http://en.cppreference.com/w/cpp/language/auto"&gt;auto&lt;/a&gt; specifier is also part of the C++11 standard. It allows the compiler to deduce expression type from&amp;nbsp;the &lt;code&gt;return&lt;/code&gt; statement. &lt;code&gt;[]&lt;/code&gt; is the capture list which can take a comma separated list of captures, such as by&amp;nbsp;reference &lt;code&gt;&amp;amp;&lt;/code&gt; or by&amp;nbsp;value &lt;code&gt;=&lt;/code&gt;. In this case, we don&amp;#8217;t capture anything, so the list is empty. The argument list and function body are similar to standard C++&amp;nbsp;functions.&lt;/p&gt;
&lt;h3&gt;Variadic template with lambda&amp;nbsp;expressions&lt;/h3&gt;
&lt;p&gt;We now have to define a variadic template that can take arbitrary number of lambda expressions and&amp;nbsp;return &lt;code&gt;true&lt;/code&gt; if all the constraints are satisfied. This is similar to what we defined&amp;nbsp;above:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typename&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;Constraint&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;evaluateConstraints&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Constraint&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;constraint&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;constraint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typename&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;typename&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;evaluateConstraints&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;constraint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;constraints&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;constraint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="n"&gt;evaluateConstraints&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;constraints&lt;/span&gt;&lt;span class="p"&gt;...);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The first&amp;nbsp;parameter &lt;code&gt;x&lt;/code&gt; is the value on which we want to enforce our constraints. Our class method can now take a varying number of constraints (lambda expressions) using pack expansion and call&amp;nbsp;the &lt;code&gt;evaluateConstraints&lt;/code&gt; function. For the sake of demonstration, this could look something&amp;nbsp;like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typename&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="kr"&gt;inline&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;PSO&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;optimize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;constraints&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;// In actual implementation the initial starting&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;// point can be specified by the user.&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;evaluateConstraints&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;constraints&lt;/span&gt;&lt;span class="p"&gt;...))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Perform optimization.&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="c1"&gt;// Update x.&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In conclusion, this post&amp;nbsp;discussed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Drawbacks of C-style variadic functions which are defined&amp;nbsp;using &lt;code&gt;va_list&lt;/code&gt;, &lt;code&gt;va_start&lt;/code&gt;, &lt;code&gt;va_arg&lt;/code&gt;,&amp;nbsp;and &lt;code&gt;va_end&lt;/code&gt; macros.&lt;/li&gt;
&lt;li&gt;C++ varaidic templates require writing a base case and a recursive case. They are also type safe and provide better performance due to compile time&amp;nbsp;computation.&lt;/li&gt;
&lt;li&gt;Expressing constraints as lambda expressions in constrained optimizations&amp;nbsp;problems.&lt;/li&gt;
&lt;li&gt;Combining variadic templates with lambda expressions for handling multiple&amp;nbsp;constraints.&lt;/li&gt;
&lt;/ul&gt;</content><category term="GSoC"/><category term="C++"/></entry><entry><title>Inaccuracy in Boost Geometry geodesic algorithms for nearly antipodal points</title><link href="https://adl1995.github.io/inaccuracy-in-boost-geometry-geodesic-algorithms-for-nearly-antipodal-points.html" rel="alternate"/><published>2018-05-10T21:55:00+02:00</published><updated>2018-05-10T21:55:00+02:00</updated><author><name>Adeel Ahmad</name></author><id>tag:adl1995.github.io,2018-05-10:/inaccuracy-in-boost-geometry-geodesic-algorithms-for-nearly-antipodal-points.html</id><summary type="html">&lt;p&gt;Nearly antipodal points or antipodes refer to the most geographically distant points on a sphere, that is, the points are diametrically opposite to each other. If a line is drawn between these two points, it passes through the center of the sphere and forms its&amp;nbsp;diameter.&lt;/p&gt;
&lt;p&gt;Computing the great circle distance between these two points is often a corner case for most geodesic computations, and the distance is either overestimated or underestimated. In case of &lt;a href="https://en.wikipedia.org/wiki/Vincenty%27s_formulae"&gt;Vincenty&amp;#8217;s formulae&lt;/a&gt;, the solution fails to converge, or provides inaccurate results. This can have major implications in applications which rely on accurate results, such as flight navigation systems. The software can handle this either by doing an error analysis check and providing specific values …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Nearly antipodal points or antipodes refer to the most geographically distant points on a sphere, that is, the points are diametrically opposite to each other. If a line is drawn between these two points, it passes through the center of the sphere and forms its&amp;nbsp;diameter.&lt;/p&gt;
&lt;p&gt;Computing the great circle distance between these two points is often a corner case for most geodesic computations, and the distance is either overestimated or underestimated. In case of &lt;a href="https://en.wikipedia.org/wiki/Vincenty%27s_formulae"&gt;Vincenty&amp;#8217;s formulae&lt;/a&gt;, the solution fails to converge, or provides inaccurate results. This can have major implications in applications which rely on accurate results, such as flight navigation systems. The software can handle this either by doing an error analysis check and providing specific values through which the inaccuracy can be identified, or by choosing a different method&amp;nbsp;altogether.&lt;/p&gt;
&lt;p&gt;This post provides a general description of geodesic algorithms and shows their inaccuracy for nearly antipodal points in the &lt;a href="http://boost.org/libs/geometry"&gt;Boost Geometry&lt;/a&gt; library. A solution is then proposed following the paper &lt;a href="https://arxiv.org/pdf/1109.4448.pdf"&gt;Algorithm for geodesics&lt;/a&gt;, by Charles Karney, the implementation for which is provided in the &lt;a href="https://sourceforge.net/projects/geographiclib"&gt;GeographicLib&lt;/a&gt;&amp;nbsp;library.&lt;/p&gt;
&lt;h1&gt;Problem&amp;nbsp;description&lt;/h1&gt;
&lt;p&gt;The algorithms described below model the Earth as an ellipsoid, which is obtained by deforming a sphere by means of directional scalings, or more generally, of an &lt;a href="https://en.wikipedia.org/wiki/Affine_transformation"&gt;affine transformation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Geodesics algorithms are employed for solving the direct and inverse geodetic&amp;nbsp;problems.&lt;/p&gt;
&lt;h3&gt;Direct and inverse geodesic&amp;nbsp;problems&lt;/h3&gt;
&lt;p&gt;Our goal in the direct geodesic problem is to find the longitudinal&amp;nbsp;difference &lt;span class="math"&gt;\(\lambda12\)&lt;/span&gt;, the&amp;nbsp;latitude &lt;span class="math"&gt;\(\phi2\)&lt;/span&gt;, and the&amp;nbsp;azimuth &lt;span class="math"&gt;\(\alpha2\)&lt;/span&gt; for the second point. The information we are provided for solving this is the&amp;nbsp;latitude &lt;span class="math"&gt;\(\phi1\)&lt;/span&gt; and&amp;nbsp;azimuth &lt;span class="math"&gt;\(\alpha1\)&lt;/span&gt; at the first point, and the distance between the two&amp;nbsp;points &lt;span class="math"&gt;\(s12\)&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;On the contrary, the inverse geodesic problem deals with finding the azimuths for the two&amp;nbsp;points &lt;span class="math"&gt;\(\alpha1\)&lt;/span&gt; and &lt;span class="math"&gt;\(\alpha2\)&lt;/span&gt;, and the distance between&amp;nbsp;them &lt;span class="math"&gt;\(s12\)&lt;/span&gt;. The input data includes the latitudes for the two&amp;nbsp;points &lt;span class="math"&gt;\(\phi1\)&lt;/span&gt; and &lt;span class="math"&gt;\(\phi2\)&lt;/span&gt;, and their longitudinal&amp;nbsp;difference &lt;span class="math"&gt;\(\lambda12\)&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;The direct or forward geodesic problem is much simpler than the inverse or backward problem because the equatorial&amp;nbsp;azimuth &lt;span class="math"&gt;\(\alpha0\)&lt;/span&gt; can be determined directly from the given&amp;nbsp;quantities &lt;span class="math"&gt;\(\phi1\)&lt;/span&gt; and &lt;span class="math"&gt;\(\alpha1\)&lt;/span&gt;. However, the inverse problem is intrinsically more complex. In this case, the angle given by the longitude&amp;nbsp;of &lt;span class="math"&gt;\(B\)&lt;/span&gt; relative&amp;nbsp;to &lt;span class="math"&gt;\(A\)&lt;/span&gt; (&lt;span class="math"&gt;\(\lambda12\)&lt;/span&gt;) is related to the corresponding angle on the auxiliary&amp;nbsp;sphere &lt;span class="math"&gt;\(\omega12\)&lt;/span&gt; via an unknown equatorial&amp;nbsp;azimuth &lt;span class="math"&gt;\(\alpha0\)&lt;/span&gt;. This is shown in the figure&amp;nbsp;below.&lt;/p&gt;
&lt;center&gt;![geodesic]({static}/images/geodesic-small.png)&lt;/center&gt;

&lt;p&gt;Over the years, numerous algorithms have been proposed for solving the above problems. The solution proposed by &lt;a href="https://www.ngs.noaa.gov/PUBS_LIB/inverse.pdf"&gt;Vincenty (1975)&lt;/a&gt; is based on the &lt;a href="https://en.wikipedia.org/wiki/Series_expansion"&gt;series expansion&lt;/a&gt; carried out to third order in the &lt;a href="https://en.wikipedia.org/wiki/Flattening"&gt;flattening&lt;/a&gt; and provides an accuracy up to 0.1 millimeters for the &lt;a href="https://en.wikipedia.org/wiki/WGS84"&gt;&lt;span class="caps"&gt;WGS84&lt;/span&gt;&lt;/a&gt; ellipsoid, however, as noted above, the inverse method fails to converge for nearly antipodal&amp;nbsp;points.&lt;/p&gt;
&lt;p&gt;In the paper &lt;a href="https://arxiv.org/pdf/1109.4448.pdf"&gt;Algorithm for geodesics&lt;/a&gt;, authored by Charles Karney, a solution is provided for direct and inverse methods that is accurate up to 15 nanometers. It continues the series expansions to sixth&amp;nbsp;order &lt;span class="math"&gt;\(O(f^6)\)&lt;/span&gt; which suffices to provide full &lt;a href="https://en.wikipedia.org/wiki/Double_precision"&gt;double precision&lt;/a&gt; accuracy&amp;nbsp;for &lt;span class="math"&gt;\(|f| \leq \frac{1}{50}\)&lt;/span&gt;,&amp;nbsp;where &lt;span class="math"&gt;\(f\)&lt;/span&gt; is the &lt;a href="https://en.wikipedia.org/wiki/Flattening"&gt;flattening of the ellipsoid&lt;/a&gt; and is given&amp;nbsp;by:&lt;/p&gt;
&lt;div class="math"&gt;$$f = (a - b) / a.$$&lt;/div&gt;
&lt;p&gt;Where &lt;span class="math"&gt;\(a\)&lt;/span&gt; defines the equatorial radius,&amp;nbsp;and &lt;span class="math"&gt;\(b\)&lt;/span&gt; refers to the polar&amp;nbsp;radius.&lt;/p&gt;
&lt;p&gt;For solving the inverse problem, it employs the &lt;a href="https://en.wikipedia.org/wiki/Newton%27s_method"&gt;Newton&amp;#8217;s method&lt;/a&gt; for&amp;nbsp;adjusting &lt;span class="math"&gt;\(\alpha1\)&lt;/span&gt; until the&amp;nbsp;correct &lt;span class="math"&gt;\(\lambda12\)&lt;/span&gt; is obtained. This typically requires two to four iterations and works for all types of input. Whereas, Vincenty&amp;#8217;s solution can require thousands of iterations before it&amp;nbsp;converges. &lt;/p&gt;
&lt;h1&gt;Problem&amp;nbsp;demonstration&lt;/h1&gt;
&lt;p&gt;The strategies currently offered by &lt;a href="http://boost.org/libs/geometry"&gt;Boost Geometry&lt;/a&gt;, also known as Generic Geometry Library (&lt;span class="caps"&gt;GGL&lt;/span&gt;) do not correctly handle corner cases for nearly antipodal points. To illustrate this, we select two points and compare their result with the &lt;a href="https://sourceforge.net/projects/geographiclib"&gt;GeographicLib&lt;/a&gt;&amp;nbsp;library.&lt;/p&gt;
&lt;h3&gt;Inaccuracy in inverse&amp;nbsp;problem&lt;/h3&gt;
&lt;p&gt;For comparing the results, we select two geographically distant points on the Earth&amp;#8217;s surface which are almost diametrically opposite to each other i.e. they are nearly antipodal. The values below are represented in latitude / longitude pairs&amp;nbsp;(degrees).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="mf"&gt;2.179167&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;73.787500&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;2.162200&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="mf"&gt;106.139064&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Using Vincenty&amp;#8217;s strategy to compute the&amp;nbsp;distance:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;lt;boost/geometry.hpp&amp;gt;&lt;/span&gt;

&lt;span class="k"&gt;using&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;namespace&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;boost&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;geometry&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;typedef&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;point&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;spherical_equatorial&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;degree&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;spherical_point&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;typedef&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;srs&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;spheroid&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;stype&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Define the strategy.&lt;/span&gt;
&lt;span class="k"&gt;typedef&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;strategy&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;distance&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vincenty&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;stype&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;vincenty_type&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;spherical_point&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;point1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;-73.787500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2.179167&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="n"&gt;point2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;106.139064&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;-2.162200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// The distance is returned in meters.&lt;/span&gt;
&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;distance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;point1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;point2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;vincenty_type&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: Boost Geometry&amp;#8217;s convention is that coordinate values of 2D tuple are given according to mathematical axis order: X, Y. In terms of geographic coordinate system: longitude, latitude. For a detailed explanation, refer to the &lt;a href="https://www.boost.org/doc/libs/1_67_0/libs/geometry/doc/html/geometry/design.html#geometry.design.axis_order"&gt;Design Rationale&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The distance returned is 19963713.360&amp;nbsp;meters.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: The reason we get a result in Boost Geometry is due to the &lt;a href="https://github.com/boostorg/geometry/blob/develop/include/boost/geometry/formulas/vincenty_inverse.hpp#L159"&gt;BOOST_GEOMETRY_DETAIL_VINCENTY_MAX_STEPS&lt;/a&gt; macro which terminates the loop after a certain number of iterations. Otherwise, the solution may never&amp;nbsp;converge.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We compare the previous result with GeographicLib which correctly handles the case for nearly antipodal&amp;nbsp;points.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;lt;GeographicLib/Geodesic.hpp&amp;gt;&lt;/span&gt;

&lt;span class="k"&gt;using&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;namespace&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;GeographicLib&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Geodesic&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;geod&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Geodesic&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;WGS84&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;distance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;geod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Inverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;2.179167&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;-73.787500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;-2.162200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;106.13906&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;distance&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Alternatively, the &lt;span class="caps"&gt;CLI&lt;/span&gt;&amp;nbsp;tool &lt;code&gt;GeodSolve&lt;/code&gt; can be passed the latitude and longitude positions of the two points.&amp;nbsp;The &lt;code&gt;-i&lt;/code&gt; flag refers to the inverse&amp;nbsp;problem.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;GeodSolve&lt;span class="w"&gt; &lt;/span&gt;-i&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;2.179167 -73.787500 -2.162200 106.13906&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This returns the distance as 20001571.083 meters. Thus, the distance computed using Boost Geometry was off my 37857.723&amp;nbsp;meters.&lt;/p&gt;
&lt;h3&gt;Inaccuracy in direct&amp;nbsp;problem&lt;/h3&gt;
&lt;p&gt;To illustrate the problem with the current implementation of direct Vincenty strategy, we use the latitude and longitude positions of our first pair of points we used earlier, and try to recover the latitude and longitude positions for the second pair or&amp;nbsp;destination.&lt;/p&gt;
&lt;p&gt;In Boost Geometry, the direct strategy can be specified as&amp;nbsp;follows.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;lt;boost/geometry.hpp&amp;gt;&lt;/span&gt;

&lt;span class="k"&gt;using&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;namespace&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;boost&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;geometry&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// For storing the resulting values.&lt;/span&gt;
&lt;span class="n"&gt;formula&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;result_direct&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// WGS-84 spheroid.&lt;/span&gt;
&lt;span class="n"&gt;srs&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;spheroid&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;spheroid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;6378137.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;6356752.3142451793&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Define the strategy.&lt;/span&gt;
&lt;span class="k"&gt;typedef&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;formula&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vincenty_direct&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;vincenty_direct_type&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;vincenty_direct_type&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;-73.787500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2.179167&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;20001571.135&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;6.80724316&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;spheroid&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This gives us the latitude and longitude positions as 0.962372 and -76.9321, respectively. We again compare this with&amp;nbsp;GeographicLib.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="cpf"&gt;&amp;lt;GeographicLib/Geodesic.hpp&amp;gt;&lt;/span&gt;

&lt;span class="k"&gt;using&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;namespace&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;GeographicLib&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Geodesic&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;geod&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Geodesic&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;WGS84&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lat2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lon2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;geod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Direct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;2.179167&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;-73.787500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;6.80724316&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;20001571.135&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lat2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;lon2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;To use&amp;nbsp;the &lt;code&gt;GeodSolve&lt;/code&gt; &lt;span class="caps"&gt;CLI&lt;/span&gt; tool, drop&amp;nbsp;the &lt;code&gt;-i&lt;/code&gt; flag, which then defaults to the direct geodesic&amp;nbsp;problem.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;GeodSolve&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;2.179167 -73.787500 6.80724316 20001571.135&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The resulting values for latitude and longitude positions are -2.16220000 and 106.13906400, respectively. Thus, we can see that the result provided by GeographicLib is almost identical to our input points (second pair), whereas, the result provided by Boost Geometry provides incorrect&amp;nbsp;values.&lt;/p&gt;
&lt;h1&gt;Google Summer of Code 2018&amp;nbsp;project&lt;/h1&gt;
&lt;p&gt;Several issues have been raised over the past few years relating with the inaccuracy of geodesic algorithms in Boost Geometry for nearly antipodal points. The relevant tickets can be found at Boost &lt;span class="caps"&gt;SVN&lt;/span&gt; (&lt;a href="https://svn.boost.org/trac10/ticket/11917"&gt;#11917&lt;/a&gt;, &lt;a href="https://svn.boost.org/trac10/ticket/11929"&gt;#11929&lt;/a&gt;) and GitHub (&lt;a href="https://github.com/boostorg/geometry/issues/426"&gt;#426&lt;/a&gt;, &lt;a href="https://github.com/boostorg/geometry/issues/449"&gt;#449&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;&lt;a href="https://summerofcode.withgoogle.com/projects/#5333818521681920"&gt;This project&lt;/a&gt; has been selected as part of the Google Summer of Code program under the &lt;a href="https://www.boost.org"&gt;Boost C++ organization&lt;/a&gt;. I will work under the supervision of my mentor &lt;a href="https://github.com/vissarion"&gt;Vissarion Fisikopoulos&lt;/a&gt; and co-mentor &lt;a href="https://github.com/awulkiew"&gt;Adam Wulkiewicz&lt;/a&gt;, and implement the method proposed by Charles Karney in his paper &lt;a href="https://arxiv.org/pdf/1109.4448.pdf"&gt;Algorithm for geodesics&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://docs.google.com/document/d/1PlFoX3d_gzhTYTlc8Bk0WGYOrKdGsU6Bb4K1gVKf9b4/edit?usp=sharing"&gt;project proposal&lt;/a&gt; goes into the mathematical details of the algorithm along with the proposed timeline. During this period, I will provide my code in the &lt;a href="https://github.com/BoostGSoC18/geometry"&gt;BoostGSoC18/geometry&lt;/a&gt; GitHub repository which will be later on merged with &lt;a href="https://github.com/boostorg/geometry"&gt;boostorg/geometry&lt;/a&gt;&amp;nbsp;repository.&lt;/p&gt;
&lt;script type="text/javascript"&gt;if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
    var align = "center",
        indent = "0em",
        linebreak = "false";

    if (false) {
        align = (screen.width &lt; 768) ? "left" : align;
        indent = (screen.width &lt; 768) ? "0em" : indent;
        linebreak = (screen.width &lt; 768) ? 'true' : linebreak;
    }

    var mathjaxscript = document.createElement('script');
    mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
    mathjaxscript.type = 'text/javascript';
    mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML';

    var configscript = document.createElement('script');
    configscript.type = 'text/x-mathjax-config';
    configscript[(window.opera ? "innerHTML" : "text")] =
        "MathJax.Hub.Config({" +
        "    config: ['MMLorHTML.js']," +
        "    TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'none' } }," +
        "    jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
        "    extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
        "    displayAlign: '"+ align +"'," +
        "    displayIndent: '"+ indent +"'," +
        "    showMathMenu: true," +
        "    messageStyle: 'normal'," +
        "    tex2jax: { " +
        "        inlineMath: [ ['\\\\(','\\\\)'] ], " +
        "        displayMath: [ ['$$','$$'] ]," +
        "        processEscapes: true," +
        "        preview: 'TeX'," +
        "    }, " +
        "    'HTML-CSS': { " +
        "        availableFonts: ['STIX', 'TeX']," +
        "        preferredFont: 'STIX'," +
        "        styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
        "        linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
        "    }, " +
        "}); " +
        "if ('default' !== 'default') {" +
            "MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
            "MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
        "}";

    (document.body || document.getElementsByTagName('head')[0]).appendChild(configscript);
    (document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
&lt;/script&gt;</content><category term="GSoC"/></entry><entry><title>An overview of activation functions used in neural networks</title><link href="https://adl1995.github.io/an-overview-of-activation-functions-used-in-neural-networks.html" rel="alternate"/><published>2017-11-13T10:54:00+01:00</published><updated>2017-11-13T10:54:00+01:00</updated><author><name>Adeel Ahmad</name></author><id>tag:adl1995.github.io,2017-11-13:/an-overview-of-activation-functions-used-in-neural-networks.html</id><summary type="html">&lt;p&gt;An activation function is used to introduce non-linearity in an artificial neural network. It allows us to model a class label or score that varies non-linearly with independent variables. Non-linearity means that the output cannot be replicated from a linear combination of inputs; this allows the model to learn complex mappings from the available data, and thus the network becomes a &lt;a href="https://en.wikipedia.org/wiki/Universal_approximation_theorem"&gt;universal approximator&lt;/a&gt;. On the other hand, a model which uses a linear function (i.e. no activation function) is unable to make sense of complicated data, such as, speech, videos, etc. and is effective only up to a single&amp;nbsp;layer.&lt;/p&gt;
&lt;p&gt;To allow backpropagation through the network, the selected activation function should be differentiable. This property is required to compute …&lt;/p&gt;</summary><content type="html">&lt;p&gt;An activation function is used to introduce non-linearity in an artificial neural network. It allows us to model a class label or score that varies non-linearly with independent variables. Non-linearity means that the output cannot be replicated from a linear combination of inputs; this allows the model to learn complex mappings from the available data, and thus the network becomes a &lt;a href="https://en.wikipedia.org/wiki/Universal_approximation_theorem"&gt;universal approximator&lt;/a&gt;. On the other hand, a model which uses a linear function (i.e. no activation function) is unable to make sense of complicated data, such as, speech, videos, etc. and is effective only up to a single&amp;nbsp;layer.&lt;/p&gt;
&lt;p&gt;To allow backpropagation through the network, the selected activation function should be differentiable. This property is required to compute the gradients which allows us to tune the network weights. The non-linear functions are continuous and transform the input (normally &lt;a href="http://cs231n.github.io/neural-networks-2/#datapre"&gt;zero-centered&lt;/a&gt;, however, these values get beyond their original scale once they get multiplied with their respective weights) in the&amp;nbsp;range &lt;span class="math"&gt;\((0, 1)\)&lt;/span&gt;, &lt;span class="math"&gt;\((-1, 1)\)&lt;/span&gt;, etc. In a neural network, it is possible for some neurons to have linear activation functions, but they must be accompanied by neurons with non-linear activation functions in some other part of the same&amp;nbsp;network.&lt;/p&gt;
&lt;p&gt;Although any non-linear function can be used as an activation function, in practice, only a small fraction of these are used. The sections below describe various activation functions. These are accompanied with a Python snippet to plot them using &lt;a href="http://www.numpy.org/"&gt;NumPy&lt;/a&gt; and &lt;a href="https://matplotlib.org/"&gt;Matplotlib&lt;/a&gt;:&lt;/p&gt;
&lt;h2&gt;Binary&amp;nbsp;step&lt;/h2&gt;
&lt;div class="math"&gt;$$a^i_j = f(z^i_j) = \begin{cases} 0  \hspace{1em} \text{if} \hspace{0.3em} z^i_j &amp;lt; 0 \\ 1 \hspace{1em} \text{if} \hspace{0.3em} z^i_j &amp;gt; 0 \end{cases}$$&lt;/div&gt;
&lt;p&gt;A binary step function is generally used in the &lt;a href="https://en.wikipedia.org/wiki/Perceptron"&gt;Perceptron linear classifier&lt;/a&gt;. It thresholds the input values&amp;nbsp;to &lt;span class="math"&gt;\(1\)&lt;/span&gt; and &lt;span class="math"&gt;\(0\)&lt;/span&gt;, if they are greater or less than zero,&amp;nbsp;respectively.&lt;/p&gt;
&lt;p&gt;This activation function is useful when the input pattern can only belong to one or two groups, that is, binary&amp;nbsp;classification.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img alt="step" src="https://adl1995.github.io/images/plots/step.svg"&gt;&lt;/p&gt;
&lt;h2&gt;&lt;span class="math"&gt;\(\tanh\)&lt;/span&gt;&lt;/h2&gt;
&lt;div class="math"&gt;$$a^i_j = f(x^i_j) = \tanh(x^i_j)$$&lt;/div&gt;
&lt;p&gt;The &lt;span class="math"&gt;\(\tanh\)&lt;/span&gt; non-linearity compresses the input in the&amp;nbsp;range &lt;span class="math"&gt;\((-1, 1)\)&lt;/span&gt;. It provides an output which is zero-centered. So, large negative values are mapped to negative outputs. Similarly, zero-valued inputs are mapped to near zero&amp;nbsp;outputs.&lt;/p&gt;
&lt;p&gt;Also, the gradients&amp;nbsp;for &lt;span class="math"&gt;\(\tanh\)&lt;/span&gt; are steeper than sigmoid, but it suffers from the &lt;a href="https://en.wikipedia.org/wiki/Vanishing_gradient_problem"&gt;vanishing gradient problem&lt;/a&gt;. &lt;span class="math"&gt;\(\tanh\)&lt;/span&gt; is commonly referred to as the scaled version of sigmoid, and generally this equation&amp;nbsp;holds: &lt;span class="math"&gt;\(\tanh(x) = 2 \sigma(2x) - 1\)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;An alternative equation for&amp;nbsp;the &lt;span class="math"&gt;\(\tanh\)&lt;/span&gt; activation function&amp;nbsp;is:&lt;/p&gt;
&lt;div class="math"&gt;$$a^i_j = f(x^i_j) = \frac{2}{1+\exp(-2x^i_j)} - 1$$&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tanh&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img alt="tanh" src="https://adl1995.github.io/images/plots/tanh.svg"&gt;&lt;/p&gt;
&lt;h2&gt;ArcTan&lt;/h2&gt;
&lt;div class="math"&gt;$$a^i_j = f(x^i_j) = \tanh^{-1}(x^i_j)$$&lt;/div&gt;
&lt;p&gt;This activation function maps the input values in the&amp;nbsp;range &lt;span class="math"&gt;\((-\pi/2, \pi/2)\)&lt;/span&gt;. Its derivative converges quadratically&amp;nbsp;against &lt;span class="math"&gt;\(0\)&lt;/span&gt; for large input values. On the other hand, in the sigmoid activation function, the derivative converges exponentially&amp;nbsp;against &lt;span class="math"&gt;\(0\)&lt;/span&gt;, which can cause problems during&amp;nbsp;back-propagation.&lt;/p&gt;
&lt;p&gt;Its graph is slightly flatter&amp;nbsp;than &lt;span class="math"&gt;\(\tanh\)&lt;/span&gt;, so it has a better tendency to differentiate between similar input&amp;nbsp;values.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arctan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img alt="arctan" src="https://adl1995.github.io/images/plots/arctan.svg"&gt;&lt;/p&gt;
&lt;h2&gt;LeCun&amp;#8217;s&amp;nbsp;Tanh&lt;/h2&gt;
&lt;div class="math"&gt;$$a^i_j = f(x^i_j) = 1.7159 \tanh\!\left( \frac{2}{3} x^i_j\right)$$&lt;/div&gt;
&lt;p&gt;This activation function was first introduced in &lt;a href="http://yann.lecun.com/"&gt;Yann LeCun&lt;/a&gt;&amp;#8216;s paper &lt;a href="http://yann.lecun.com/exdb/publis/pdf/lecun-98b.pdf"&gt;Efficient BackProp&lt;/a&gt;. The constants in the above equation have been chosen to keep the variance of the output close&amp;nbsp;to &lt;span class="math"&gt;\(1\)&lt;/span&gt;, because the gain of the sigmoid is&amp;nbsp;roughly &lt;span class="math"&gt;\(1\)&lt;/span&gt; over its useful&amp;nbsp;range.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.7159&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tanh&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img alt="lecuns-tanh" src="https://adl1995.github.io/images/plots/lecuns-tanh.svg"&gt;&lt;/p&gt;
&lt;h2&gt;Hard&amp;nbsp;Tanh&lt;/h2&gt;
&lt;div class="math"&gt;$$a^i_j = f(x^i_j) = \max(-1, \min(1, x^i_j))$$&lt;/div&gt;
&lt;p&gt;Compared&amp;nbsp;to &lt;span class="math"&gt;\(\tanh\)&lt;/span&gt;, the&amp;nbsp;hard &lt;span class="math"&gt;\(\tanh\)&lt;/span&gt; activation function is computationally cheaper. It also saturates for magnitudes&amp;nbsp;of &lt;span class="math"&gt;\(x\)&lt;/span&gt; greater&amp;nbsp;than &lt;span class="math"&gt;\(1\)&lt;/span&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;maximum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;minimum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img alt="hard-tanh" src="https://adl1995.github.io/images/plots/hard-tanh.svg"&gt;&lt;/p&gt;
&lt;h2&gt;Sigmoid&lt;/h2&gt;
&lt;div class="math"&gt;$$a^i_j = f(x^i_j) = \frac{1}{1+\exp(-x^i_j)}$$&lt;/div&gt;
&lt;p&gt;The sigmoid or logistic activation function maps the input values in the&amp;nbsp;range &lt;span class="math"&gt;\((0, 1)\)&lt;/span&gt;, which is essentially their probability of belonging to a class. So, it is mostly used for multi-class&amp;nbsp;classification.&lt;/p&gt;
&lt;p&gt;However,&amp;nbsp;like &lt;span class="math"&gt;\(\tanh\)&lt;/span&gt;, it also suffers from the vanishing gradient problem. Also, its output is not zero-centered, which causes difficulties during the optimization step. It also has a low convergence&amp;nbsp;rate.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img alt="sigmoid" src="https://adl1995.github.io/images/plots/sigmoid.svg"&gt;&lt;/p&gt;
&lt;h2&gt;Bipolar&amp;nbsp;Sigmoid&lt;/h2&gt;
&lt;div class="math"&gt;$$a^i_j = f(x^i_j) = \frac{1-\exp(-x^i_j)}{1+\exp(-x^i_j)}$$&lt;/div&gt;
&lt;p&gt;The sigmoid function can be scaled to have any range of output values, depending upon the problem. When the range is&amp;nbsp;from &lt;span class="math"&gt;\(-1\)&lt;/span&gt; to &lt;span class="math"&gt;\(1\)&lt;/span&gt;, it is called a bipolar&amp;nbsp;sigmoid.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img alt="bipolar-sigmoid" src="https://adl1995.github.io/images/plots/bipolar-sigmoid.svg"&gt;&lt;/p&gt;
&lt;h2&gt;ReLU (Rectified Linear&amp;nbsp;Unit)&lt;/h2&gt;
&lt;div class="math"&gt;$$a^i_j = f(x^i_j) = \max(0, x^i_j)$$&lt;/div&gt;
&lt;p&gt;A rectified linear unit has the&amp;nbsp;output &lt;span class="math"&gt;\(0\)&lt;/span&gt; if its input is less than or equal&amp;nbsp;to &lt;span class="math"&gt;\(0\)&lt;/span&gt;, otherwise, its output is equal to its input. This activation function is also more &lt;a href="https://news.ycombinator.com/item?id=13338389"&gt;biologically accurate&lt;/a&gt;. It has been widely used in &lt;a href="https://en.wikipedia.org/wiki/Convolutional_neural_network"&gt;convolutional neural networks&lt;/a&gt;. It is also superior to the sigmoid&amp;nbsp;and &lt;span class="math"&gt;\(\tanh\)&lt;/span&gt; activation function, as it does not suffer from the vanishing gradient problem. Thus, it allows for faster and effective training of deep neural&amp;nbsp;architectures.&lt;/p&gt;
&lt;p&gt;However, being non-differentiable&amp;nbsp;at &lt;span class="math"&gt;\(0\)&lt;/span&gt;, ReLU neurons have the tendency to become inactive for all inputs, that is, they tend to die out. This can be caused by high learning rates, and can thus reduce the model&amp;#8217;s learning capacity. This is commonly referred to as the &amp;#8220;&lt;a href="https://datascience.stackexchange.com/questions/5706/what-is-the-dying-relu-problem-in-neural-networks"&gt;Dying ReLU&lt;/a&gt;&amp;#8221;&amp;nbsp;problem.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;maximum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img alt="relu" src="https://adl1995.github.io/images/plots/relu.svg"&gt;&lt;/p&gt;
&lt;h2&gt;Leaky&amp;nbsp;ReLU&lt;/h2&gt;
&lt;div class="math"&gt;$$a^i_j = f(x^i_j) = \max(0.01 x^i_j, x^i_j)$$&lt;/div&gt;
&lt;p&gt;The non-differentiability at zero problem can be solved by allowing a small value to flow when the input is less than or equal&amp;nbsp;to &lt;span class="math"&gt;\(0\)&lt;/span&gt;, which thus overcomes the &amp;#8220;Dying ReLU&amp;#8221; problem. It has proved to give better results for some&amp;nbsp;problems.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;maximum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.01&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img alt="leaky-relu" src="https://adl1995.github.io/images/plots/leaky-relu.svg"&gt;&lt;/p&gt;
&lt;h2&gt;Smooth&amp;nbsp;ReLU&lt;/h2&gt;
&lt;div class="math"&gt;$$a^i_j = f(x^i_j) = \log\!\big(1+\exp(x^i_j)\big)$$&lt;/div&gt;
&lt;p&gt;Also known as the softplus unit, this activation function also overcomes the &amp;#8220;Dying ReLU&amp;#8221; problem by making itself differentiable everywhere and causes less saturation&amp;nbsp;overall.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img alt="smooth-relu" src="https://adl1995.github.io/images/plots/smooth-relu.svg"&gt;&lt;/p&gt;
&lt;h2&gt;Logit&lt;/h2&gt;
&lt;div class="math"&gt;$$a^i_j = f(x^i_j) = \log\!\bigg(\frac{x^i_j}{(1 − x^i_j)}\bigg)$$&lt;/div&gt;
&lt;p&gt;This activation function performs the inverse operation of sigmoid,,that is, given probabilities in the&amp;nbsp;range &lt;span class="math"&gt;\((0, 1)\)&lt;/span&gt;, it maps them to the full range of real numbers. The value of the logit function approaches infinity as the probability gets close&amp;nbsp;to &lt;span class="math"&gt;\(1\)&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;It is mostly used in binary classification models, where we want to transform the binary input to real-valued&amp;nbsp;quantities.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img alt="logit" src="https://adl1995.github.io/images/plots/logit.svg"&gt;&lt;/p&gt;
&lt;h2&gt;Softmax&lt;/h2&gt;
&lt;div class="math"&gt;$$a^i_j = f(x^i_j) = \frac{\exp(z^i_j)}{\sum\limits_k \exp(z^i_k)}$$&lt;/div&gt;
&lt;p&gt;The softmax function gives us the probabilities that any of the classes are true. It produces values in the&amp;nbsp;range &lt;span class="math"&gt;\((0, 1)\)&lt;/span&gt;. It also highlights the largest value and tries to suppress values which are below the maximum value; its resulting values always sum&amp;nbsp;to &lt;span class="math"&gt;\(1\)&lt;/span&gt;. This function is widely used in multiple classification logistic regression&amp;nbsp;models.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img alt="softmax" src="https://adl1995.github.io/images/plots/softmax.svg"&gt;&lt;/p&gt;
&lt;p&gt;A &lt;a href="https://github.com/adl1995/adl1995.github.io/blob/master/notebooks/Activation%20functions.ipynb"&gt;Juptyer notebook&lt;/a&gt; containing all the above plots is hosted on&amp;nbsp;GitHub.&lt;/p&gt;
&lt;script type="text/javascript"&gt;if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
    var align = "center",
        indent = "0em",
        linebreak = "false";

    if (false) {
        align = (screen.width &lt; 768) ? "left" : align;
        indent = (screen.width &lt; 768) ? "0em" : indent;
        linebreak = (screen.width &lt; 768) ? 'true' : linebreak;
    }

    var mathjaxscript = document.createElement('script');
    mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
    mathjaxscript.type = 'text/javascript';
    mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML';

    var configscript = document.createElement('script');
    configscript.type = 'text/x-mathjax-config';
    configscript[(window.opera ? "innerHTML" : "text")] =
        "MathJax.Hub.Config({" +
        "    config: ['MMLorHTML.js']," +
        "    TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'none' } }," +
        "    jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
        "    extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
        "    displayAlign: '"+ align +"'," +
        "    displayIndent: '"+ indent +"'," +
        "    showMathMenu: true," +
        "    messageStyle: 'normal'," +
        "    tex2jax: { " +
        "        inlineMath: [ ['\\\\(','\\\\)'] ], " +
        "        displayMath: [ ['$$','$$'] ]," +
        "        processEscapes: true," +
        "        preview: 'TeX'," +
        "    }, " +
        "    'HTML-CSS': { " +
        "        availableFonts: ['STIX', 'TeX']," +
        "        preferredFont: 'STIX'," +
        "        styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
        "        linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
        "    }, " +
        "}); " +
        "if ('default' !== 'default') {" +
            "MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
            "MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
        "}";

    (document.body || document.getElementsByTagName('head')[0]).appendChild(configscript);
    (document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
&lt;/script&gt;</content><category term="Machine Learning"/></entry><entry><title>Parallel tile fetching and CPU-and-memory statistics</title><link href="https://adl1995.github.io/parallel-tile-fetching-and-cpu-and-memory-statistics.html" rel="alternate"/><published>2017-09-09T10:58:00+02:00</published><updated>2017-09-09T10:58:00+02:00</updated><author><name>Adeel Ahmad</name></author><id>tag:adl1995.github.io,2017-09-09:/parallel-tile-fetching-and-cpu-and-memory-statistics.html</id><summary type="html">&lt;p&gt;The &lt;a href="http://hips.readthedocs.io"&gt;hips&lt;/a&gt; package now supports parallel tile fetching. The user can achieve this either using the &lt;a href="https://docs.python.org/3/library/urllib.html"&gt;urllib&lt;/a&gt; or &lt;a href="https://aiohttp.readthedocs.io/en/stable"&gt;aiohttp&lt;/a&gt;&amp;nbsp;package.&lt;/p&gt;
&lt;p&gt;In case of &lt;a href="http://aiohttp.readthedocs.io/en/stable/"&gt;aiohttp&lt;/a&gt;, the fetched tile data is coupled with &lt;a href="http://hips.readthedocs.io/en/latest/api/hips.HipsTileMeta.html"&gt;&lt;code&gt;HipsTileMeta&lt;/code&gt;&lt;/a&gt; to create a &lt;a href="http://hips.readthedocs.io/en/latest/api/hips.HipsTile.html"&gt;&lt;code&gt;HipsTile&lt;/code&gt;&lt;/a&gt; object. This ensures there is no misalignment of tile data, otherwise, tiles could get swapped during the drawing&amp;nbsp;period.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;fetch_tile_aiohttp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;HipsTileMeta&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;HipsTile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Fetch a HiPS tile asynchronously using aiohttp.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;raw_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;HipsTile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;raw_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We also limit the amount of simultaneously open connections using &lt;a href="http://aiohttp.readthedocs.io/en/stable/client_reference.html#aiohttp.TCPConnector"&gt;&lt;code&gt;aiohttp.TCPConnector&lt;/code&gt;&lt;/a&gt; class. The returned object is passed to &lt;a href="http://aiohttp.readthedocs.io/en/stable/client_reference.html#aiohttp.ClientSession"&gt;&lt;code&gt;aiohttp.ClientSession&lt;/code&gt;&lt;/a&gt;&amp;#8216;s &lt;code&gt;__init__&lt;/code&gt; method. This procedure can be understood in …&lt;/p&gt;</summary><content type="html">&lt;p&gt;The &lt;a href="http://hips.readthedocs.io"&gt;hips&lt;/a&gt; package now supports parallel tile fetching. The user can achieve this either using the &lt;a href="https://docs.python.org/3/library/urllib.html"&gt;urllib&lt;/a&gt; or &lt;a href="https://aiohttp.readthedocs.io/en/stable"&gt;aiohttp&lt;/a&gt;&amp;nbsp;package.&lt;/p&gt;
&lt;p&gt;In case of &lt;a href="http://aiohttp.readthedocs.io/en/stable/"&gt;aiohttp&lt;/a&gt;, the fetched tile data is coupled with &lt;a href="http://hips.readthedocs.io/en/latest/api/hips.HipsTileMeta.html"&gt;&lt;code&gt;HipsTileMeta&lt;/code&gt;&lt;/a&gt; to create a &lt;a href="http://hips.readthedocs.io/en/latest/api/hips.HipsTile.html"&gt;&lt;code&gt;HipsTile&lt;/code&gt;&lt;/a&gt; object. This ensures there is no misalignment of tile data, otherwise, tiles could get swapped during the drawing&amp;nbsp;period.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;fetch_tile_aiohttp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;HipsTileMeta&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;HipsTile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Fetch a HiPS tile asynchronously using aiohttp.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;raw_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;HipsTile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;raw_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We also limit the amount of simultaneously open connections using &lt;a href="http://aiohttp.readthedocs.io/en/stable/client_reference.html#aiohttp.TCPConnector"&gt;&lt;code&gt;aiohttp.TCPConnector&lt;/code&gt;&lt;/a&gt; class. The returned object is passed to &lt;a href="http://aiohttp.readthedocs.io/en/stable/client_reference.html#aiohttp.ClientSession"&gt;&lt;code&gt;aiohttp.ClientSession&lt;/code&gt;&lt;/a&gt;&amp;#8216;s &lt;code&gt;__init__&lt;/code&gt; method. This procedure can be understood in the code block&amp;nbsp;below:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;fetch_all_tiles_aiohttp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tile_metas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;HipsTileMeta&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;hips_survey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;HipsSurveyProperties&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;progress_bar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n_parallel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;HipsTile&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Fetch HiPS tiles from a remote URL using aiohttp.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;connector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aiohttp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TCPConnector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;n_parallel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;aiohttp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ClientSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connector&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;connector&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;futures&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;meta&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tile_metas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hips_survey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tile_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;future&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ensure_future&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fetch_tile_aiohttp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="n"&gt;futures&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;future&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Another recently added feature is progress bar reporting functionality, for which we are using the &lt;a href="https://pypi.python.org/pypi/tqdm"&gt;tqdm&lt;/a&gt; package. We achieve this by wrapping &lt;a href="https://docs.python.org/3/library/asyncio-task.html#future"&gt;&lt;code&gt;asyncio.Future&lt;/code&gt;&lt;/a&gt; list&amp;nbsp;in &lt;code&gt;tqdm&lt;/code&gt; function which triggers the progress bar once&amp;nbsp;we &lt;code&gt;await&lt;/code&gt; on the&amp;nbsp;object.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;progress_bar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;tqdm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;tqdm&lt;/span&gt;
    &lt;span class="n"&gt;futures&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tqdm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;futures&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tile_metas&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;desc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Fetching tiles&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;tiles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;future&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;futures&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;tiles&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;future&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Using parallel tile fetching, the overall fetch time is reduced by almost 75%&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt;. The statistics shown below are for the high level &lt;a href="http://hips.readthedocs.io/en/latest/api/hips.make_sky_image.html"&gt;&lt;code&gt;make_sky_image&lt;/code&gt;&lt;/a&gt; function, the result of which can be seen on our &lt;a href="http://hips.readthedocs.io/en/latest/getting_started.html#gs"&gt;Getting started&lt;/a&gt; page. The response time using synchronous fetching&amp;nbsp;is:&lt;/p&gt;
&lt;p&gt;&lt;img alt="tile-fetch-async" src="https://adl1995.github.io/images/tile-fetch-sync-scaled2.png"&gt;&lt;/p&gt;
&lt;p&gt;After adding support for asynchronous fetching, the response time has&amp;nbsp;enhanced:&lt;/p&gt;
&lt;p&gt;&lt;img alt="tile-fetch-sync" src="https://adl1995.github.io/images/tile-fetch-async-scaled2.png"&gt;&lt;/p&gt;
&lt;p&gt;To monitor how this affects our &lt;span class="caps"&gt;CPU&lt;/span&gt; and memory usage, we make use of an open source package &lt;a href="https://github.com/astrofrog/psrecord"&gt;psrecord&lt;/a&gt;, which provides functionality on top of &lt;a href="https://github.com/giampaolo/psutil"&gt;psutil&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The package provides a command line interface which requires a process &lt;span class="caps"&gt;ID&lt;/span&gt; (&lt;span class="caps"&gt;PSID&lt;/span&gt;) to monitor its activity. It also provides facility for plotting the result using &lt;a href="http://matplotlib.org"&gt;matplotlib&lt;/a&gt;, in addition to the standard text&amp;nbsp;log.&lt;/p&gt;
&lt;p&gt;&lt;img alt="plt-make-sky-image" src="https://adl1995.github.io/images/plot_make_sky_image.png"&gt;&lt;/p&gt;
&lt;p&gt;To fully understand the above plot for&amp;nbsp;the &lt;code&gt;make_sky_image&lt;/code&gt; function, let&amp;#8217;s look at the primary steps involved in &lt;a href="http://hips.readthedocs.io/en/latest/api/hips.HipsPainter.html"&gt;&lt;code&gt;HipsPainter&lt;/code&gt;&lt;/a&gt;&amp;nbsp;class:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Asynchronously fetch the HiPS&amp;nbsp;tiles&lt;/li&gt;
&lt;li&gt;Split the parent tile into four children tiles to fix the &lt;a href="https://adl1995.github.io/fixing-tile-distortion-issue-in-hips-package.html"&gt;tile distortion&amp;nbsp;issue&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Apply projective transformation to each&amp;nbsp;tile&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The entire process took&amp;nbsp;around &lt;span class="math"&gt;\(50\)&lt;/span&gt; seconds to finish execution, in which&amp;nbsp;almost &lt;span class="math"&gt;\(40\)&lt;/span&gt; seconds are dedicated to tile fetching. In the beginning, &lt;span class="caps"&gt;CPU&lt;/span&gt; consumption is high, which is due to the creation of threads&amp;nbsp;and &lt;code&gt;Future&lt;/code&gt; objects. After this, the &lt;span class="caps"&gt;CPU&lt;/span&gt; consumption is almost idle for&amp;nbsp;around &lt;span class="math"&gt;\(30\)&lt;/span&gt; seconds, this is when&amp;nbsp;we &lt;code&gt;await&lt;/code&gt; for the&amp;nbsp;result.&lt;/p&gt;
&lt;p&gt;Towards the end, a large rise can be seen in both &lt;span class="caps"&gt;CPU&lt;/span&gt; and memory consumption, this is the drawing phase. As we apply projective transformation on each tile separately, this process is computationally&amp;nbsp;expensive.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://github.com/adl1995/HIPS-to-Py/blob/master/logs/activity_make_sky_image.txt"&gt;full activity text log&lt;/a&gt; can be viewed for a detailed analysis. A document containing &lt;a href="https://github.com/adl1995/HIPS-to-Py/blob/master/notes/response-times.md"&gt;response time comparison between urllib, grequests, aiohttp, and asyncio&lt;/a&gt; can be viewed as well. These response times were calculated using this &lt;a href="https://github.com/adl1995/HIPS-to-Py/blob/master/examples/fetch-tiles-threaded.py"&gt;Python script&lt;/a&gt;.&lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;This percentage was calculated&amp;nbsp;using &lt;code&gt;urllib&lt;/code&gt;.&amp;#160;&lt;a class="footnote-backref" href="#fnref:1" title="Jump back to footnote 1 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;script type="text/javascript"&gt;if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
    var align = "center",
        indent = "0em",
        linebreak = "false";

    if (false) {
        align = (screen.width &lt; 768) ? "left" : align;
        indent = (screen.width &lt; 768) ? "0em" : indent;
        linebreak = (screen.width &lt; 768) ? 'true' : linebreak;
    }

    var mathjaxscript = document.createElement('script');
    mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
    mathjaxscript.type = 'text/javascript';
    mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML';

    var configscript = document.createElement('script');
    configscript.type = 'text/x-mathjax-config';
    configscript[(window.opera ? "innerHTML" : "text")] =
        "MathJax.Hub.Config({" +
        "    config: ['MMLorHTML.js']," +
        "    TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'none' } }," +
        "    jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
        "    extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
        "    displayAlign: '"+ align +"'," +
        "    displayIndent: '"+ indent +"'," +
        "    showMathMenu: true," +
        "    messageStyle: 'normal'," +
        "    tex2jax: { " +
        "        inlineMath: [ ['\\\\(','\\\\)'] ], " +
        "        displayMath: [ ['$$','$$'] ]," +
        "        processEscapes: true," +
        "        preview: 'TeX'," +
        "    }, " +
        "    'HTML-CSS': { " +
        "        availableFonts: ['STIX', 'TeX']," +
        "        preferredFont: 'STIX'," +
        "        styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
        "        linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
        "    }, " +
        "}); " +
        "if ('default' !== 'default') {" +
            "MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
            "MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
        "}";

    (document.body || document.getElementsByTagName('head')[0]).appendChild(configscript);
    (document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
&lt;/script&gt;</content><category term="GSoC"/></entry><entry><title>Google Summer of Code 2017 final evaluation report</title><link href="https://adl1995.github.io/google-summer-of-code-2017-final-evaluation-report.html" rel="alternate"/><published>2017-08-28T22:35:00+02:00</published><updated>2017-08-28T22:35:00+02:00</updated><author><name>Adeel Ahmad</name></author><id>tag:adl1995.github.io,2017-08-28:/google-summer-of-code-2017-final-evaluation-report.html</id><summary type="html">&lt;h4&gt;Link to GitHub repository: &lt;a href="http://github.com/hipspy/hips"&gt;&lt;code&gt;http://github.com/hipspy/hips&lt;/code&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;In addition to the main &lt;a href="http://github.com/hipspy/hips"&gt;hips&lt;/a&gt; repository, I also maintained my personal &lt;a href="https://github.com/adl1995/HIPS-to-Py"&gt;&lt;span class="caps"&gt;HIPS&lt;/span&gt;-to-Py  repository on GitHub&lt;/a&gt;. This contains Jupyter notebooks which showcase the functionality in &lt;a href="http://github.com/hipspy/hips"&gt;hips&lt;/a&gt; and numerous related Python scripts. The &lt;a href="https://github.com/adl1995/HIPS-to-Py/wiki/HiPS-to-Py"&gt;Wiki page&lt;/a&gt; contains a short description on hips. It also contains links to resource documents and telcon notes, which are hosted on Google&amp;nbsp;Docs. &lt;/p&gt;
&lt;h4&gt;List of Pull&amp;nbsp;Requests&lt;/h4&gt;
&lt;h6&gt;Work related with HiPS tile&amp;nbsp;drawing&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/105"&gt;Add progress bar support for fetching and drawing HiPS&amp;nbsp;tiles&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/104"&gt;Add reporting functionality for&amp;nbsp;HipsPainter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/99"&gt;Introduce precise drawing&amp;nbsp;algorithm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/75"&gt;Add methods for drawing markers over sky&amp;nbsp;image&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/91"&gt;Introduce class HipsDrawResult in&amp;nbsp;simple.py&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/89"&gt;Add method to_split in SimpleTilePainter for checking whether to split a …&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;</summary><content type="html">&lt;h4&gt;Link to GitHub repository: &lt;a href="http://github.com/hipspy/hips"&gt;&lt;code&gt;http://github.com/hipspy/hips&lt;/code&gt;&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;In addition to the main &lt;a href="http://github.com/hipspy/hips"&gt;hips&lt;/a&gt; repository, I also maintained my personal &lt;a href="https://github.com/adl1995/HIPS-to-Py"&gt;&lt;span class="caps"&gt;HIPS&lt;/span&gt;-to-Py  repository on GitHub&lt;/a&gt;. This contains Jupyter notebooks which showcase the functionality in &lt;a href="http://github.com/hipspy/hips"&gt;hips&lt;/a&gt; and numerous related Python scripts. The &lt;a href="https://github.com/adl1995/HIPS-to-Py/wiki/HiPS-to-Py"&gt;Wiki page&lt;/a&gt; contains a short description on hips. It also contains links to resource documents and telcon notes, which are hosted on Google&amp;nbsp;Docs. &lt;/p&gt;
&lt;h4&gt;List of Pull&amp;nbsp;Requests&lt;/h4&gt;
&lt;h6&gt;Work related with HiPS tile&amp;nbsp;drawing&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/105"&gt;Add progress bar support for fetching and drawing HiPS&amp;nbsp;tiles&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/104"&gt;Add reporting functionality for&amp;nbsp;HipsPainter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/99"&gt;Introduce precise drawing&amp;nbsp;algorithm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/75"&gt;Add methods for drawing markers over sky&amp;nbsp;image&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/91"&gt;Introduce class HipsDrawResult in&amp;nbsp;simple.py&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/89"&gt;Add method to_split in SimpleTilePainter for checking whether to split a&amp;nbsp;tile&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/73"&gt;Fix &lt;span class="caps"&gt;RGB&lt;/span&gt; dtype&amp;nbsp;issue&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/66"&gt;Reformat SimpleTilePainter&amp;nbsp;class&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/30"&gt;High-level function:&amp;nbsp;make_sky_image&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/63"&gt;Add support for &lt;span class="caps"&gt;RGB&lt;/span&gt;&amp;nbsp;tiles&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h6&gt;Work related with HiPS&amp;nbsp;tiles&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/108"&gt;Make improvements to parallel tile&amp;nbsp;fetching&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/106"&gt;Introduce asynchronous fetching of HiPS&amp;nbsp;tiles&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/101"&gt;Fix tile splitting&amp;nbsp;criterion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/85"&gt;Add property &amp;#8216;children&amp;#8217; for creating four children tiles from parent&amp;nbsp;tile&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/56"&gt;Remove tile_width from&amp;nbsp;HipsTileMeta&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/55"&gt;Add test for &lt;span class="caps"&gt;PNG&lt;/span&gt;&amp;nbsp;tiles&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/37"&gt;Add &lt;span class="caps"&gt;WCS&lt;/span&gt; property in HipsTile&amp;nbsp;class&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/24"&gt;Add HipsTile and HipsTileMeta&amp;nbsp;classes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/21"&gt;Add tiles.hipsdescription&amp;nbsp;module&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/52"&gt;Update tile_access_url&amp;nbsp;method&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/67"&gt;Fix tile width height&amp;nbsp;issue&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h6&gt;Work related with&amp;nbsp;WCSGeometry&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/102"&gt;Reformat docstring for create_from_dict class&amp;nbsp;method&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/100"&gt;Add classmethod create_from_dict in WCSGeometry&amp;nbsp;class&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/49"&gt;Add classmethod create_simple to&amp;nbsp;WCSGeometry&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h6&gt;Work related with HEALPix and HiPS utility&amp;nbsp;functions&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/58"&gt;Add function for computing HiPS&amp;nbsp;order&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/50"&gt;Make improvements to compute_healpix_pixel_indices utility&amp;nbsp;function&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/26"&gt;Add compute_image_pixels function in&amp;nbsp;hips.utils&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/19"&gt;Add utils.healpix&amp;nbsp;module&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h6&gt;Work related with HiPS&amp;nbsp;Surveys&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/95"&gt;Add option to create HipsSurveyProperties from survey&amp;nbsp;name&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/77"&gt;Fix tile_width property in HipsSurveyProperties&amp;nbsp;class&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/60"&gt;Update base_url property in HipsSurveyProperties&amp;nbsp;class&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/27"&gt;Add class HipsSurveyList for parsing list of hips&amp;nbsp;surveys&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h6&gt;Documentation&amp;nbsp;changes&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/68"&gt;Add plot for image in high level&amp;nbsp;docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/34"&gt;Add documentation on HIPS_EXTRA environment&amp;nbsp;variable&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/22"&gt;Update&amp;nbsp;docstring&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/14"&gt;Update Sphinx&amp;nbsp;docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/13"&gt;Sphinx&amp;nbsp;update&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/12"&gt;Sphinx&amp;nbsp;update&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h6&gt;Miscellaneous&lt;/h6&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/87"&gt;Remove print statement from test&amp;nbsp;case&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/48"&gt;Fix&amp;nbsp;typo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/38"&gt;Rename data to&amp;nbsp;raw_data&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/10"&gt;Update Makefile to use python3&amp;nbsp;command&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/8"&gt;Update&amp;nbsp;simple.py&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/11"&gt;Add skeleton class&amp;nbsp;TileFileCache&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/pull/33"&gt;Outline first draft of HipsTileCache&amp;nbsp;class&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;List of&amp;nbsp;commits&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/98ff03be7cd921b06f4ebb77fa2cd7db7124e3ca"&gt;&lt;code&gt;Restructure high level function fetch_tiles&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/d7979afd03c5376c4cd1216e6b20c5e2ee3b7627"&gt;&lt;code&gt;Add timeout and n_parallel option to tiles_aiohttp function&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/d27168bce994e772302636facb30fd7482c82023"&gt;&lt;code&gt;Add example section for fetch_tiles function docstring&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/1116a7a81b01295e5aac21cfdd9e044accf258c2"&gt;&lt;code&gt;Update aiohttp dependency config&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/e5118522ba544bab164116a01b1a87a8c3c4f5a2"&gt;&lt;code&gt;Add tqdm progress bar to urllib and aiohttp fetch methods&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/0eba105e1822e0e50645344e1839e8ec48d5d6de"&gt;&lt;code&gt;Add option to fetch tiles asynchronously using urllib&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/6c1295814affecd4abe5e2c6e44e22df24ae3ae6"&gt;&lt;code&gt;Introduce asynchronous fetching of HiPS tiles&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/25dd9ef7f46e21876968ec784a93f2c14df9c0c4"&gt;&lt;code&gt;Update .travis.yml&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/88f4293f12ba0173bc86ad6e39223b5aff04e4ae"&gt;&lt;code&gt;Update getting started example to contain information on progress bar&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/e2c9c69989072fffb6271b43273fee65d186e274"&gt;&lt;code&gt;Make tqdm an optional dependency&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/ba813ef65fe503bb879927fd25f2975c1a735176"&gt;&lt;code&gt;Add progress bar support for fetching and drawing HiPS tiles&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/c026c96aa19e46f66a8e84a1c3429476c2803bb4"&gt;&lt;code&gt;Add reporting functionality for HipsPainter&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/0981f309aade1f33e0586341108dfff0fd108ab2"&gt;&lt;code&gt;Reformat docstring for create_from_dict class method&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/8140f2943de907bc6bd45f6a06d337b781dd6ff1"&gt;&lt;code&gt;Fix tile splitting criterion&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/b447864b37ea070f2b8b2662ac0797cc2f151c76"&gt;&lt;code&gt;Update test case for create_from_dict classmethod&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/651a5ae14a8896ca81558d9f084a9afe764bdd44"&gt;&lt;code&gt;Add classmethod create_from_dict in WCSGeometry class&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/1980881927671773de80051ea78b79bdac61a484"&gt;&lt;code&gt;Introduce precise drawing algorithm&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/09906ec66668c829a9ca0375840e77090d50de2a"&gt;&lt;code&gt;Add option to create HipsSurveyProperties from survey name&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/821f335f3f6c5ba912bcfdbf2ea55e1d51e6be82"&gt;&lt;code&gt;Update high-level docs example&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/c15092b116d5b02b601a2c087ef6a80fb82107d2"&gt;&lt;code&gt;Add method write_image in HipsDrawResult class&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/eddcaaa07028e59d2d8e8567cbf073d30bbd9ada"&gt;&lt;code&gt;Introduce class HipsDrawResult in simple.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/808e758d2cd9c04c31e0dc49318cf55d4f92cd18"&gt;&lt;code&gt;Add method to_split in SimpleTilePainter for checking whether to split a tile&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/e08ddcdf3c655021d5a0b96bb26ad50577e0e02a"&gt;&lt;code&gt;Add property 'children' for creating four children tiles from parent tile&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/9005878e851ee8f64ac498599bba8737a1fd83f4"&gt;&lt;code&gt;Add methods for drawing markers over sky image&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/46c78a036dec051bb9e6fb75adbf7fcd6a585301"&gt;&lt;code&gt;Fix tile_width property in HipsSurveyProperties class&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/6139a0bf4047b303a01fefb61ef5288de55486c6"&gt;&lt;code&gt;Fix RGB dtype issue&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/eb5cd378624001de9588a4eb7095361b1f9b94d5"&gt;&lt;code&gt;Reformat SimpleTilePainter class&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/e2541ae378982e9261dde09ed7b389a8d4b1b47b"&gt;&lt;code&gt;Add plot in high level docs&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/af4eb6f7cac27c06f60468912388dd2f33a3b851"&gt;&lt;code&gt;Fix tile width height issue&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/8ca56a7036540d528fb891a811a9bec901086aa3"&gt;&lt;code&gt;Add support for RGB tiles&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/10ada966037b2a71aec42b3c85a9773d2bab6a48"&gt;&lt;code&gt;Update base_url property in HipsSurveyProperties class&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/c853878ab027abf70b2f771796ff7ae98c37c6f3"&gt;&lt;code&gt;Add test case for get_order function&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/e1955f6ef699669ff4209cf9a72d26488af6154e"&gt;&lt;code&gt;Add function for computing HiPS order&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/8cdd34cad90facf9348d9bf7014260e9f75aae77"&gt;&lt;code&gt;Add classmethod create_simple to WCSGeometry&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/439bd58bc190c9d957ce4fce9e0acf9c45a40537"&gt;&lt;code&gt;Remove tile_width from HipsTileMeta&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/18fc6b656fe873955c10d7505c207cfed030615f"&gt;&lt;code&gt;Refactor test cases for HipsTile&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/b0f8d4daa85e32a07677522f948b7a9925b07ef7"&gt;&lt;code&gt;Add test for PNG tiles&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/f7aa6c9aa4e6bee51dab7801d7910f02b69dc0eb"&gt;&lt;code&gt;Update tile_access_url method&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/f746d04e6a3cbe73c4c8ce6b8a32d3020c83bce5"&gt;&lt;code&gt;Make improvements to compute_healpix_pixel_indices utility function&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/3121de53a68eabea67fdce9eae1a26412484edaf"&gt;&lt;code&gt;Fix typo&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/880d4a2e9e9ce31e86b508750bc65ea33f4a3b4b"&gt;&lt;code&gt;Add test case test_wcs_healpix_pixel_indices&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/5fe3ed0aa5be6544a1b71b786f0014df626d1b21"&gt;&lt;code&gt;Some code reformatting&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/145bcafab6c47ced08191a45f1feaa69907da4a8"&gt;&lt;code&gt;Add attribute frames in HipsSurveyProperties&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/5c85e128e37e4c0cc86166e08fc13b1d51e3a5c0"&gt;&lt;code&gt;Add property tile_access_url in HipsSurveyProperties&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/a4b6846ef107572e7b542114b731089346c3b1f5"&gt;&lt;code&gt;Remove attribute self.pt from SimpleTilePainter&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/69e09af162b071c3a1f271883ebf51fd00aceee5"&gt;&lt;code&gt;Skip example from doctest&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/112fb37cd4514e84449be3e3543164cc4f171383"&gt;&lt;code&gt;Update example for HipsTileMeta&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/c34d4061a05eae864ae8de05b7fd639da36910dc"&gt;&lt;code&gt;Update requires_hips_extra decorator function&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/451b32a2985b55fad3bb2a0de8eb25ff3c75b218"&gt;&lt;code&gt;Update read classmethod in HipsTile class&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/186286c3a64f92ee1f9a50b053e5bc9724dd438f"&gt;&lt;code&gt;Update test_skycoord_corners in TestHipsTileMeta&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/de5566394997a6a2d7a87e548f12b3b179aa1f53"&gt;&lt;code&gt;Add function frames in utils/healpix.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/a9b0984a670a624b791fc99f11e93972edf97aad"&gt;&lt;code&gt;Rename draw_tile to painter&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/a0ad4cb044373e879a2d88b5559de91c91557e19"&gt;&lt;code&gt;Rename all_sky to sky_sky&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/9b79b9d19c6faa8f1bf5386076af04dc3340e995"&gt;&lt;code&gt;Update test_skycoord_corners in TestHipsTileMeta&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/3c8204abecb09c62748100cdac40d0b33def4513"&gt;&lt;code&gt;Add property projection in SimpleTilePainter&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/a7d4ba4faf0dd826e94181a5ecd092036fdb12bd"&gt;&lt;code&gt;Update test case for HipsTileMeta class&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/2846fca6dd4eb6027b428920f8a81940c1e5a881"&gt;&lt;code&gt;Merge HipsTile and HipsTileMeta within same file&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/309a75a2c1b468dcd7e5969d2bd01f60f6a9005e"&gt;&lt;code&gt;Pass fits_header to fits.PrimaryHDU function&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/a0759cbe24b41ac50a86165db4438d474385291f"&gt;&lt;code&gt;Remove test case for SimpleTilePainter class&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/4571dd88dbd34a408ed934cb47cddfa752642119"&gt;&lt;code&gt;Add comment on data type conversion&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/9e073c9370924fb0651db0c97631e53635b17abd"&gt;&lt;code&gt;Remove docstring&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/9f2e5fac6222fdaa6ddde8a1d6d973fe782968d6"&gt;&lt;code&gt;Add test case for HipsTileMeta&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/69ae7642417b6b76796a448b9b9a3f4b387a9021"&gt;&lt;code&gt;Add function apply_projection in SimpleTilePainter&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/c7d5db5d8c54bb91fd3e91fd24d3991215bcdc1a"&gt;&lt;code&gt;Remove function apply_projection from HipsTileMeta&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/bee4bcd0542657c74b040947ec20a6d9ab41edc3"&gt;&lt;code&gt;Document frame parameter in HipsTileMeta&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/9c5e687cf2948241dd13cedfe0ec79aba0389f38"&gt;&lt;code&gt;Add missing import&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/9c82bc4c642c9417659ade65653490a39294b797"&gt;&lt;code&gt;Use assert_allclose instead of assert&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/8adff08b687c280f302df01bd8d1306a5e0a4008"&gt;&lt;code&gt;Update drawing code&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/1f67310f98e5b873d6309928f428c66245ce3ba9"&gt;&lt;code&gt;Add function apply_projection in HipsTileMeta&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/05b8ac8e97467576883da6c2dbd2be786ead4e73"&gt;&lt;code&gt;Add test case for access_url property&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/f7625a909254992cb4bbd0fc4ba7237bead65e8a"&gt;&lt;code&gt;Add property access_url in HipsSurveyProperties&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/a32ba1b946c22c81d41bc8291206d90134d123d2"&gt;&lt;code&gt;Add example in Getting Started page&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/42ceb7af3d271bf60cd82f6b49136d1fe4bb3920"&gt;&lt;code&gt;Get HiPS order from HipsSurveyProperties class&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/e487e44c68b8b4a8da44fc9fb82d932767abc471"&gt;&lt;code&gt;Update drawing code&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/c3d3d2729a4467667793af560df9b6c371625360"&gt;&lt;code&gt;Fix link to WCSGeometry class in docs&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/1e4bb3b268f7b5893c5279a472d88b26fb4ecafc"&gt;&lt;code&gt;Add property skycoord_corner to HipsTileMeta&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/7897504b97bc594d55aa23182af76dfd29ab152d"&gt;&lt;code&gt;Fix drawing code&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/53a0df576c6f1be0659cd4927609ae134cdb3643"&gt;&lt;code&gt;Reformat some code&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/243ef55f752c701c94f4b3736c20524f62b560a1"&gt;&lt;code&gt;Add dst property in HipsTileMeta class&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/3acf68e070ceeac9cce7e86c89f3c0350ed478aa"&gt;&lt;code&gt;Update corner computation&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/c838f6412cfeddea00d8449ee048d0429ed6a09d"&gt;&lt;code&gt;Update drawing code&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/91e7e394a4f7bf363122703f55048ddcfee5118b"&gt;&lt;code&gt;Store FITS data as float type&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/818a9102b3f12ab61ed13d3987b31b227719a700"&gt;&lt;code&gt;Update drawing function&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/120ff07dd6b4cd13bcd0131927746fab7dbb7f7d"&gt;&lt;code&gt;Add skeleton of drawing code&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/1dcc81164f456a9bd4fa7f7905630c94c429d308"&gt;&lt;code&gt;Merge branch 'master' of https://github.com/hipspy/hips into tiles.draw&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/a3f7261fe255ac8a6904741f576d99bddeba3ffd"&gt;&lt;code&gt;Add documentation on HIPS_EXTRA environment variable&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/8add0250a612c22b9a5ecb5dbd4ce9e4a9369f89"&gt;&lt;code&gt;Add function make_sky_image&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/449e4df92ed21259d527e9b2fb325ae708e46045"&gt;&lt;code&gt;Add class HipsSurveyList for parsing list of hips surveys&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/0bdb052115291c0657841c6becf973cec2ff59d6"&gt;&lt;code&gt;Fix type annotation issue&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/1136490773612a0601b06b5c8a644d728f0f659b"&gt;&lt;code&gt;Update tiles.hips module&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/5db3ef2ae3c3958ae86906e840c4bc4793845adf"&gt;&lt;code&gt;Update tiles.hips module&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/9d2ef083c0dc9b73fb00e4228daf3179ca482348"&gt;&lt;code&gt;Refactor some code&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/616340bd6c87cb070d324b1631ced8cbe76654b8"&gt;&lt;code&gt;Add class HipsTileMeta and update test case&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/6250e9ec0c665151a84403d986a4b71a804ea632"&gt;&lt;code&gt;Minimize HipsTile classs functionality&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/12f0b4fe059cdd29ecb1f37956320570c87791b8"&gt;&lt;code&gt;Use / instead of joinpath&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/2876957883d4eacc7984fa554dc176b75f35c1ad"&gt;&lt;code&gt;Change path opening method&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/b937d1486e2c67c2519c8b572b43e899536ca24c"&gt;&lt;code&gt;Update tiles.hips module&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/7869a5d5e36a1bd2219b5b9212b2dcd68ffc77f9"&gt;&lt;code&gt;Add tile_width property to HipsTile&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/27cca875b322df5a4b0a9caa01d081f9fb3054b0"&gt;&lt;code&gt;Update tiles.hips module&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/b5a268688e48d775a6aceb82898af8fc745c3b9f"&gt;&lt;code&gt;Update tiles.hips module&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/1f0b0dfb873e83e3af0d432e49a245ec650636da"&gt;&lt;code&gt;Remove blank lines and docstring&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/6826c7b9f97f45b2ce62b6f700a7a8758c9661a3"&gt;&lt;code&gt;Update .gitignore to ignore Mypy cache directory&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/a16a78f789b5f96f814a8856f503f979693202cb"&gt;&lt;code&gt;Add tiles.hips package&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/0ffe20e0ea787f11df65cd384c63e03c522aa514"&gt;&lt;code&gt;Update hips.utils package&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/8f3139dfd23e9624e4cd9dafd412889cc49a340e"&gt;&lt;code&gt;Update hips.utils package&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/81d74de79ec5971d0e528a3f5c2d8e0e21fc7e0a"&gt;&lt;code&gt;Add classmethod for creating a WCS object&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/5f52d62d5402080dcd271b106670d86878856f3b"&gt;&lt;code&gt;Add class WCSGeometry&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/6e2dd380d3d397e08a2e0f79489d10756a876787"&gt;&lt;code&gt;Add compute_image_pixels function in hips.utils&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/d369652b5edc9cda4705d0199a862bd8d226f526"&gt;&lt;code&gt;Update class docstring&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/a245521987a4971aaa70f842bb045f72512c760e"&gt;&lt;code&gt;Update tiles.hipsdescription module&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/dae30ac9cff524a0dc64bb638c5e6b62bc4011d4"&gt;&lt;code&gt;Update class docstring&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/fbf89687c9e5f6dab8b0de8a638abaf433ec6a87"&gt;&lt;code&gt;Update tiles.hipsdescription module&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/69a91f0a35369ccd018990746b2f830d0ecb74d8"&gt;&lt;code&gt;Remove whitespace&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/8c235c2ebe5861aa2ea213ff4fe0f5d7f7c89d99"&gt;&lt;code&gt;Update docstring&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/45b4117c3e85bb6fac72aeb790dfa483c03f0b12"&gt;&lt;code&gt;Add entry in api.rst&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/ff3299abeb476ffb1937b6647f6bdae26d659e34"&gt;&lt;code&gt;Add tiles.hipsdescription module&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/0b1359036a1436551234dd21ce47149c533c1bea"&gt;&lt;code&gt;Update utils.healpix module&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/fa832132c016e7285d24abe58d1438fff27e649b"&gt;&lt;code&gt;Add example&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/5de3e05971f7570eeb4402a266e8918b14d9e9ca"&gt;&lt;code&gt;Update test case&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/aca32b631e949222e88bfc48ff25b1ecaa28f3cd"&gt;&lt;code&gt;Update formatting&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/9da1f1fd3e79a552839bff00fc814396dcba7920"&gt;&lt;code&gt;Add newline&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/bbb48be4b0c77180fa34de83a2b6a3614639e3c1"&gt;&lt;code&gt;Update utils.healpix module&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/e4c00b949e0ca03f06c115927256f0539cdde9ad"&gt;&lt;code&gt;Add utils.healpix module&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/53a607b4fa0031fe3fedd0b6bf89ba03436d715a"&gt;&lt;code&gt;Update Sphinx docs&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/c7224fe181a18d041513973f043337e3d5af33fc"&gt;&lt;code&gt;Update Makefile to use python3 command&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/hipspy/hips/commit/c5942b52a60a45dbe05e154dc381dd8ef3d2b46c"&gt;&lt;code&gt;Update simple.py&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;List of commit&amp;nbsp;diffs&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;commit 98ff03be7cd921b06f4ebb77fa2cd7db7124e3ca
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Mon Aug 28 20:49:24 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Restructure high level function fetch_tiles

&lt;span class="gh"&gt;diff --git a/hips/tiles/fetch.py b/hips/tiles/fetch.py&lt;/span&gt;
&lt;span class="gh"&gt;index 5c6063a..1af267c 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/fetch.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/fetch.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -75,83 +75,83 @@ def fetch_tiles(tile_metas: List[HipsTileMeta], hips_survey: HipsSurveyPropertie&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    else:
&lt;span class="w"&gt; &lt;/span&gt;        raise ValueError(f&amp;#39;Invalid package name: {fetch_package}&amp;#39;)

&lt;span class="gd"&gt;-    tiles = fetch_fct(tile_metas, hips_survey, progress_bar, n_parallel, timeout)&lt;/span&gt;
&lt;span class="gi"&gt;+    _tile_urls = tile_urls(tile_metas, hips_survey)&lt;/span&gt;
&lt;span class="gi"&gt;+    response_all = fetch_fct(_tile_urls, hips_survey, progress_bar, n_parallel, timeout)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    # Sort tiles to match the tile_meta list
&lt;span class="w"&gt; &lt;/span&gt;    # TODO: this doesn&amp;#39;t seem like a great solution.
&lt;span class="w"&gt; &lt;/span&gt;    # Use OrderedDict instead?
&lt;span class="gd"&gt;-    out = []&lt;/span&gt;
&lt;span class="gd"&gt;-    for tile_meta in tile_metas:&lt;/span&gt;
&lt;span class="gd"&gt;-        for tile in tiles:&lt;/span&gt;
&lt;span class="gd"&gt;-            if tile.meta == tile_meta:&lt;/span&gt;
&lt;span class="gd"&gt;-                out.append(tile)&lt;/span&gt;
&lt;span class="gd"&gt;-                continue&lt;/span&gt;
&lt;span class="gd"&gt;-    return out&lt;/span&gt;
&lt;span class="gi"&gt;+    tiles = []&lt;/span&gt;
&lt;span class="gi"&gt;+    for tile_url in _tile_urls:&lt;/span&gt;
&lt;span class="gi"&gt;+        for idx, response in enumerate(response_all):&lt;/span&gt;
&lt;span class="gi"&gt;+            if response[&amp;#39;url&amp;#39;] == tile_url:&lt;/span&gt;
&lt;span class="gi"&gt;+                tiles.append(HipsTile(tile_metas[idx], response[&amp;#39;raw_data&amp;#39;]))&lt;/span&gt;

&lt;span class="gi"&gt;+    return tiles&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+def tile_urls(tile_metas: List[HipsTileMeta], hips_survey: HipsSurveyProperties) -&amp;gt; List[str]:&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;Retrun list of tile URLs&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+    return [hips_survey.tile_url(meta) for meta in tile_metas]&lt;/span&gt;

&lt;span class="gd"&gt;-def fetch_tile_urllib(url: str, meta: HipsTileMeta, timeout: float) -&amp;gt; HipsTile:&lt;/span&gt;
&lt;span class="gi"&gt;+def fetch_tile_urllib(url: str, timeout: float) -&amp;gt; dict:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;Fetch a HiPS tile asynchronously.&amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;    with urllib.request.urlopen(url, timeout=timeout) as conn:
&lt;span class="gd"&gt;-        raw_data = conn.read()&lt;/span&gt;
&lt;span class="gd"&gt;-        return HipsTile(meta, raw_data)&lt;/span&gt;
&lt;span class="gi"&gt;+        return {&amp;#39;raw_data&amp;#39;: conn.read(), &amp;#39;url&amp;#39;: url}&lt;/span&gt;


&lt;span class="gd"&gt;-def tiles_urllib(tile_metas: List[HipsTileMeta], hips_survey: HipsSurveyProperties,&lt;/span&gt;
&lt;span class="gd"&gt;-                 progress_bar: bool, n_parallel, timeout: float) -&amp;gt; List[HipsTile]:&lt;/span&gt;
&lt;span class="gi"&gt;+def tiles_urllib(tile_urls: List[str], hips_survey: HipsSurveyProperties,&lt;/span&gt;
&lt;span class="gi"&gt;+                 progress_bar: bool, n_parallel, timeout: float) -&amp;gt; List[dict]:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;Generator function to fetch HiPS tiles from a remote URL.&amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;    with concurrent.futures.ThreadPoolExecutor(max_workers=n_parallel) as executor:
&lt;span class="w"&gt; &lt;/span&gt;        futures = []
&lt;span class="gd"&gt;-        for meta in tile_metas:&lt;/span&gt;
&lt;span class="gd"&gt;-            url = hips_survey.tile_url(meta)&lt;/span&gt;
&lt;span class="gd"&gt;-            future = executor.submit(fetch_tile_urllib, url, meta, timeout)&lt;/span&gt;
&lt;span class="gi"&gt;+        for url in tile_urls:&lt;/span&gt;
&lt;span class="gi"&gt;+            future = executor.submit(fetch_tile_urllib, url, timeout)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            futures.append(future)

&lt;span class="w"&gt; &lt;/span&gt;        futures = concurrent.futures.as_completed(futures)
&lt;span class="w"&gt; &lt;/span&gt;        if progress_bar:
&lt;span class="w"&gt; &lt;/span&gt;            from tqdm import tqdm
&lt;span class="gd"&gt;-            futures = tqdm(futures, total=len(tile_metas), desc=&amp;#39;Fetching tiles&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+            futures = tqdm(futures, total=len(tile_urls), desc=&amp;#39;Fetching tiles&amp;#39;)&lt;/span&gt;

&lt;span class="gd"&gt;-        tiles = []&lt;/span&gt;
&lt;span class="gi"&gt;+        response_all = []&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        for future in futures:
&lt;span class="gd"&gt;-            tiles.append(future.result())&lt;/span&gt;
&lt;span class="gi"&gt;+            response_all.append(future.result())&lt;/span&gt;

&lt;span class="gd"&gt;-    return tiles&lt;/span&gt;
&lt;span class="gi"&gt;+    return response_all&lt;/span&gt;


&lt;span class="gd"&gt;-async def fetch_tile_aiohttp(url: str, meta: HipsTileMeta, session, timeout: float) -&amp;gt; HipsTile:&lt;/span&gt;
&lt;span class="gi"&gt;+async def fetch_tile_aiohttp(url: str, session, timeout: float) -&amp;gt; dict:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;Fetch a HiPS tile asynchronously using aiohttp.&amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;    async with session.get(url, timeout=timeout) as response:
&lt;span class="gd"&gt;-        raw_data = await response.read()&lt;/span&gt;
&lt;span class="gd"&gt;-        return HipsTile(meta, raw_data)&lt;/span&gt;
&lt;span class="gi"&gt;+        return {&amp;#39;raw_data&amp;#39;: await response.read(), &amp;#39;url&amp;#39;: url}&lt;/span&gt;


&lt;span class="gd"&gt;-async def fetch_all_tiles_aiohttp(tile_metas: List[HipsTileMeta], hips_survey: HipsSurveyProperties,&lt;/span&gt;
&lt;span class="gd"&gt;-                                  progress_bar: bool, n_parallel: int, timeout: float) -&amp;gt; List[HipsTile]:&lt;/span&gt;
&lt;span class="gi"&gt;+async def fetch_all_tiles_aiohttp(tile_urls: List[str], hips_survey: HipsSurveyProperties,&lt;/span&gt;
&lt;span class="gi"&gt;+                                  progress_bar: bool, n_parallel: int, timeout: float) -&amp;gt; List[dict]:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;Generator function to fetch HiPS tiles from a remote URL using aiohttp.&amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;    import aiohttp

&lt;span class="w"&gt; &lt;/span&gt;    connector = aiohttp.TCPConnector(limit=n_parallel)
&lt;span class="w"&gt; &lt;/span&gt;    async with aiohttp.ClientSession(connector=connector) as session:
&lt;span class="w"&gt; &lt;/span&gt;        futures = []
&lt;span class="gd"&gt;-        for meta in tile_metas:&lt;/span&gt;
&lt;span class="gd"&gt;-            url = hips_survey.tile_url(meta)&lt;/span&gt;
&lt;span class="gd"&gt;-            future = asyncio.ensure_future(fetch_tile_aiohttp(url, meta, session, timeout))&lt;/span&gt;
&lt;span class="gi"&gt;+        for url in tile_urls:&lt;/span&gt;
&lt;span class="gi"&gt;+            future = asyncio.ensure_future(fetch_tile_aiohttp(url, session, timeout))&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            futures.append(future)

&lt;span class="w"&gt; &lt;/span&gt;        futures = asyncio.as_completed(futures)
&lt;span class="w"&gt; &lt;/span&gt;        if progress_bar:
&lt;span class="w"&gt; &lt;/span&gt;            from tqdm import tqdm
&lt;span class="gd"&gt;-            futures = tqdm(futures, total=len(tile_metas), desc=&amp;#39;Fetching tiles&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+            futures = tqdm(futures, total=len(tile_urls), desc=&amp;#39;Fetching tiles&amp;#39;)&lt;/span&gt;

&lt;span class="gd"&gt;-        tiles = []&lt;/span&gt;
&lt;span class="gi"&gt;+        response_all = []&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        for future in futures:
&lt;span class="gd"&gt;-            tiles.append(await future)&lt;/span&gt;
&lt;span class="gi"&gt;+            response_all.append(await future)&lt;/span&gt;

&lt;span class="gd"&gt;-    return tiles&lt;/span&gt;
&lt;span class="gi"&gt;+    return response_all&lt;/span&gt;


&lt;span class="gd"&gt;-def tiles_aiohttp(tile_metas: List[HipsTileMeta], hips_survey: HipsSurveyProperties,&lt;/span&gt;
&lt;span class="gd"&gt;-                  progress_bar: bool, n_parallel: int, timeout: float) -&amp;gt; List[HipsTile]:&lt;/span&gt;
&lt;span class="gi"&gt;+def tiles_aiohttp(tile_urls: List[str], hips_survey: HipsSurveyProperties,&lt;/span&gt;
&lt;span class="gi"&gt;+                  progress_bar: bool, n_parallel: int, timeout: float) -&amp;gt; List[dict]:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    return asyncio.get_event_loop().run_until_complete(
&lt;span class="gd"&gt;-        fetch_all_tiles_aiohttp(tile_metas, hips_survey, progress_bar, n_parallel, timeout)&lt;/span&gt;
&lt;span class="gi"&gt;+        fetch_all_tiles_aiohttp(tile_urls, hips_survey, progress_bar, n_parallel, timeout)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    )

commit d7979afd03c5376c4cd1216e6b20c5e2ee3b7627
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Aug 25 20:35:59 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Add timeout and n_parallel option to tiles_aiohttp function

&lt;span class="gh"&gt;diff --git a/hips/tiles/fetch.py b/hips/tiles/fetch.py&lt;/span&gt;
&lt;span class="gh"&gt;index 8a8c004..54a5729 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/fetch.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/fetch.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -118,40 +118,40 @@ def tiles_urllib(tile_metas: List[HipsTileMeta], hips_survey: HipsSurveyProperti&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    return tiles


&lt;span class="gd"&gt;-async def fetch_tile_aiohttp(url: str, meta: HipsTileMeta, session) -&amp;gt; Generator:&lt;/span&gt;
&lt;span class="gi"&gt;+async def fetch_tile_aiohttp(url: str, meta: HipsTileMeta, session, timeout: float) -&amp;gt; Generator:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;Fetch a HiPS tile asynchronously using aiohttp.&amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-    async with session.get(url) as response:&lt;/span&gt;
&lt;span class="gi"&gt;+    async with session.get(url, timeout=timeout) as response:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        raw_data = await response.read()
&lt;span class="w"&gt; &lt;/span&gt;        return HipsTile(meta, raw_data)


&lt;span class="gd"&gt;-async def fetch_all_tiles_aiohttp(tile_metas: List[HipsTileMeta],&lt;/span&gt;
&lt;span class="gd"&gt;-                                  hips_survey: HipsSurveyProperties, progress_bar: bool) -&amp;gt; List[HipsTile]:&lt;/span&gt;
&lt;span class="gi"&gt;+async def fetch_all_tiles_aiohttp(tile_metas: List[HipsTileMeta], hips_survey: HipsSurveyProperties,&lt;/span&gt;
&lt;span class="gi"&gt;+                                  progress_bar: bool, n_parallel: int, timeout: float) -&amp;gt; List[HipsTile]:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;Generator function to fetch HiPS tiles from a remote URL using aiohttp.&amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;    import aiohttp

&lt;span class="gd"&gt;-    async with aiohttp.ClientSession() as session:&lt;/span&gt;
&lt;span class="gd"&gt;-        futures = []&lt;/span&gt;
&lt;span class="gd"&gt;-        for meta in tile_metas:&lt;/span&gt;
&lt;span class="gd"&gt;-            url = hips_survey.tile_url(meta)&lt;/span&gt;
&lt;span class="gd"&gt;-            future = asyncio.ensure_future(fetch_tile_aiohttp(url, meta, session))&lt;/span&gt;
&lt;span class="gd"&gt;-            futures.append(future)&lt;/span&gt;
&lt;span class="gi"&gt;+    with await asyncio.Semaphore(n_parallel):&lt;/span&gt;
&lt;span class="gi"&gt;+        async with aiohttp.ClientSession() as session:&lt;/span&gt;
&lt;span class="gi"&gt;+            futures = []&lt;/span&gt;
&lt;span class="gi"&gt;+            for meta in tile_metas:&lt;/span&gt;
&lt;span class="gi"&gt;+                url = hips_survey.tile_url(meta)&lt;/span&gt;
&lt;span class="gi"&gt;+                future = asyncio.ensure_future(fetch_tile_aiohttp(url, meta, session, timeout))&lt;/span&gt;
&lt;span class="gi"&gt;+                futures.append(future)&lt;/span&gt;

&lt;span class="gd"&gt;-        futures = asyncio.as_completed(futures)&lt;/span&gt;
&lt;span class="gd"&gt;-        if progress_bar:&lt;/span&gt;
&lt;span class="gd"&gt;-            from tqdm import tqdm&lt;/span&gt;
&lt;span class="gd"&gt;-            futures = tqdm(futures, total=len(tile_metas), desc=&amp;#39;Fetching tiles&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+            futures = asyncio.as_completed(futures)&lt;/span&gt;
&lt;span class="gi"&gt;+            if progress_bar:&lt;/span&gt;
&lt;span class="gi"&gt;+                from tqdm import tqdm&lt;/span&gt;
&lt;span class="gi"&gt;+                futures = tqdm(futures, total=len(tile_metas), desc=&amp;#39;Fetching tiles&amp;#39;)&lt;/span&gt;

&lt;span class="gd"&gt;-        tiles = []&lt;/span&gt;
&lt;span class="gd"&gt;-        for future in futures:&lt;/span&gt;
&lt;span class="gd"&gt;-            tiles.append(await future)&lt;/span&gt;
&lt;span class="gi"&gt;+            tiles = []&lt;/span&gt;
&lt;span class="gi"&gt;+            for future in futures:&lt;/span&gt;
&lt;span class="gi"&gt;+                tiles.append(await future)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    return tiles


&lt;span class="w"&gt; &lt;/span&gt;def tiles_aiohttp(tile_metas: List[HipsTileMeta], hips_survey: HipsSurveyProperties,
&lt;span class="w"&gt; &lt;/span&gt;                  progress_bar: bool, n_parallel: int = 10, timeout: float = 10) -&amp;gt; List[HipsTile]:
&lt;span class="gd"&gt;-    # TODO: implement n_parallel and timeout&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    return asyncio.get_event_loop().run_until_complete(
&lt;span class="gd"&gt;-        fetch_all_tiles_aiohttp(tile_metas, hips_survey, progress_bar)&lt;/span&gt;
&lt;span class="gi"&gt;+        fetch_all_tiles_aiohttp(tile_metas, hips_survey, progress_bar, n_parallel, timeout)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    )

commit d27168bce994e772302636facb30fd7482c82023
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Tue Aug 22 18:31:40 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Add example section for fetch_tiles function docstring

&lt;span class="gh"&gt;diff --git a/hips/draw/paint.py b/hips/draw/paint.py&lt;/span&gt;
&lt;span class="gh"&gt;index 502a9e8..69fbc87 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/paint.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/paint.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -37,7 +37,8 @@ class HipsPainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    progress_bar : bool
&lt;span class="w"&gt; &lt;/span&gt;        Show a progress bar for tile fetching and drawing
&lt;span class="w"&gt; &lt;/span&gt;    fetch_opts : dict
&lt;span class="gd"&gt;-        Keyword arguments for fetching HiPS tiles&lt;/span&gt;
&lt;span class="gi"&gt;+        Keyword arguments for fetching HiPS tiles. To see the&lt;/span&gt;
&lt;span class="gi"&gt;+        list of passable arguments, refer to `~fetch_tiles`&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    Examples
&lt;span class="w"&gt; &lt;/span&gt;    --------
&lt;span class="gu"&gt;@@ -127,7 +128,7 @@ class HipsPainter:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        if self._tiles is None:
&lt;span class="w"&gt; &lt;/span&gt;            self._tiles = fetch_tiles(tile_metas=tile_metas, hips_survey=self.hips_survey,
&lt;span class="gd"&gt;-                                      progress_bar=self.progress_bar, **self.fetch_opts)&lt;/span&gt;
&lt;span class="gi"&gt;+                                      progress_bar=self.progress_bar, **(self.fetch_opts or {}))&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        return self._tiles

&lt;span class="gh"&gt;diff --git a/hips/draw/ui.py b/hips/draw/ui.py&lt;/span&gt;
&lt;span class="gh"&gt;index e950d40..1b25e8e 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/ui.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/ui.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -34,7 +34,8 @@ def make_sky_image(geometry: Union[dict, WCSGeometry], hips_survey: Union[str, &amp;#39;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    progress_bar : bool
&lt;span class="w"&gt; &lt;/span&gt;        Show a progress bar for tile fetching and drawing
&lt;span class="w"&gt; &lt;/span&gt;    fetch_opts : dict
&lt;span class="gd"&gt;-        Keyword arguments for fetching HiPS tiles&lt;/span&gt;
&lt;span class="gi"&gt;+        Keyword arguments for fetching HiPS tiles. To see the&lt;/span&gt;
&lt;span class="gi"&gt;+        list of passable arguments, refer to `~hips.fetch_tiles`&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    Returns
&lt;span class="w"&gt; &lt;/span&gt;    -------
&lt;span class="gh"&gt;diff --git a/hips/tiles/fetch.py b/hips/tiles/fetch.py&lt;/span&gt;
&lt;span class="gh"&gt;index 28afbf6..a898f78 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/fetch.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/fetch.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -9,8 +9,14 @@ __all__ = [&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;#39;fetch_tiles&amp;#39;,
&lt;span class="w"&gt; &lt;/span&gt;]

&lt;span class="gi"&gt;+__doctest_skip__ = [&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;#39;fetch_tiles&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+]&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;def fetch_tiles(tile_metas: List[HipsTileMeta], hips_survey : HipsSurveyProperties,
&lt;span class="gd"&gt;-                progress_bar: bool = False, n_parallel: int = 10,  timeout: int = 10, fetch_package : str = &amp;#39;urllib&amp;#39;) -&amp;gt; List[HipsTile]:&lt;/span&gt;
&lt;span class="gi"&gt;+                progress_bar: bool = False, n_parallel: int = 10,&lt;/span&gt;
&lt;span class="gi"&gt;+                timeout: float = 10, fetch_package : str = &amp;#39;urllib&amp;#39;) -&amp;gt; List[HipsTile]:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;Fetch a list of HiPS tiles.

&lt;span class="w"&gt; &lt;/span&gt;    This function fetches a list of HiPS tiles based
&lt;span class="gu"&gt;@@ -28,10 +34,37 @@ def fetch_tiles(tile_metas: List[HipsTileMeta], hips_survey : HipsSurveyProperti&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        Show a progress bar for tile fetching and drawing
&lt;span class="w"&gt; &lt;/span&gt;    n_parallel : int
&lt;span class="w"&gt; &lt;/span&gt;        Number of threads to use for fetching HiPS tiles
&lt;span class="gd"&gt;-    timeout : int&lt;/span&gt;
&lt;span class="gi"&gt;+    timeout : float&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        Seconds to timeout for fetching a HiPS tile
&lt;span class="w"&gt; &lt;/span&gt;    fetch_package : {&amp;#39;urllib&amp;#39;, &amp;#39;aiohttp&amp;#39;}
&lt;span class="w"&gt; &lt;/span&gt;        Package to use for fetching HiPS tiles
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    Examples&lt;/span&gt;
&lt;span class="gi"&gt;+    --------&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;gt;&amp;gt;&amp;gt; from hips import HipsSurveyProperties&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;gt;&amp;gt;&amp;gt; from hips import fetch_tiles&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;gt;&amp;gt;&amp;gt; url = &amp;#39;http://alasky.unistra.fr/DSS/DSS2Merged/properties&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;gt;&amp;gt;&amp;gt; hips_survey = HipsSurveyProperties.fetch(url)&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;gt;&amp;gt;&amp;gt; tile_indices = [69623, 69627, 69628, 69629, 69630, 69631]&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;gt;&amp;gt;&amp;gt; tile_metas = []&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;gt;&amp;gt;&amp;gt; for healpix_pixel_index in tile_indices:&lt;/span&gt;
&lt;span class="gi"&gt;+    ...    tile_meta = HipsTileMeta(&lt;/span&gt;
&lt;span class="gi"&gt;+    ...        order=7,&lt;/span&gt;
&lt;span class="gi"&gt;+    ...        ipix=healpix_pixel_index,&lt;/span&gt;
&lt;span class="gi"&gt;+    ...        frame=hips_survey.astropy_frame,&lt;/span&gt;
&lt;span class="gi"&gt;+    ...        file_format=&amp;#39;fits&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+    ...    )&lt;/span&gt;
&lt;span class="gi"&gt;+    ...    tile_metas.append(tile_meta)&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;gt;&amp;gt;&amp;gt; tiles = fetch_tiles(tile_metas, hips_survey)&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;gt;&amp;gt;&amp;gt; tiles[0].meta.file_format&lt;/span&gt;
&lt;span class="gi"&gt;+    fits&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;gt;&amp;gt;&amp;gt; tiles[0].meta.order&lt;/span&gt;
&lt;span class="gi"&gt;+    7&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    Returns&lt;/span&gt;
&lt;span class="gi"&gt;+    -------&lt;/span&gt;
&lt;span class="gi"&gt;+    tiles : List[HipsTile]&lt;/span&gt;
&lt;span class="gi"&gt;+        A Python list of HiPS tiles&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;    if fetch_package == &amp;#39;aiohttp&amp;#39;:
&lt;span class="w"&gt; &lt;/span&gt;        return tiles_aiohttp(tile_metas, hips_survey, progress_bar)
&lt;span class="gu"&gt;@@ -40,36 +73,30 @@ def fetch_tiles(tile_metas: List[HipsTileMeta], hips_survey : HipsSurveyProperti&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    else:
&lt;span class="w"&gt; &lt;/span&gt;        raise ValueError(f&amp;#39;Invalid package name: {fetch_package}&amp;#39;)

&lt;span class="gd"&gt;-def tile_urls(tile_metas: List[HipsTileMeta], hips_survey : HipsSurveyProperties) -&amp;gt; List[str]:&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;quot;&amp;quot;&amp;quot;List of tile URLs&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-    return [hips_survey.tile_url(meta) for meta in tile_metas]&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-def fetch_tile_urllib(url: str, meta: HipsTileMeta, timeout: int) -&amp;gt; Generator:&lt;/span&gt;
&lt;span class="gi"&gt;+def fetch_tile_urllib(url: str, meta: HipsTileMeta, timeout: float) -&amp;gt; Generator:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;Fetch a HiPS tile asynchronously.&amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;    with urllib.request.urlopen(url, timeout=timeout) as conn:
&lt;span class="w"&gt; &lt;/span&gt;        raw_data = conn.read()
&lt;span class="w"&gt; &lt;/span&gt;        return HipsTile(meta, raw_data)

&lt;span class="w"&gt; &lt;/span&gt;def tiles_urllib(tile_metas: List[HipsTileMeta], hips_survey : HipsSurveyProperties,
&lt;span class="gd"&gt;-                 progress_bar: bool = False, n_parallel: int = 10,  timeout: int = 10) -&amp;gt; List[HipsTile]:&lt;/span&gt;
&lt;span class="gi"&gt;+                 progress_bar: bool = False, n_parallel: int = 10,  timeout: float = 10) -&amp;gt; List[HipsTile]:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;Generator function to fetch HiPS tiles from a remote URL.&amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;    with concurrent.futures.ThreadPoolExecutor(max_workers=n_parallel) as executor:
&lt;span class="gd"&gt;-        future_to_url = {executor.submit(&lt;/span&gt;
&lt;span class="gd"&gt;-            fetch_tile_urllib,&lt;/span&gt;
&lt;span class="gd"&gt;-            url,&lt;/span&gt;
&lt;span class="gd"&gt;-            tile_metas[idx],&lt;/span&gt;
&lt;span class="gd"&gt;-            timeout)&lt;/span&gt;
&lt;span class="gd"&gt;-            : url for idx, url in enumerate(tile_urls(tile_metas, hips_survey))}&lt;/span&gt;
&lt;span class="gi"&gt;+        futures = []&lt;/span&gt;
&lt;span class="gi"&gt;+        for meta in tile_metas:&lt;/span&gt;
&lt;span class="gi"&gt;+            url = hips_survey.tile_url(meta)&lt;/span&gt;
&lt;span class="gi"&gt;+            futures.append(executor.submit(fetch_tile_urllib, url, meta, timeout))&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        if progress_bar:
&lt;span class="gd"&gt;-            from tqdm import tqdm&lt;/span&gt;
&lt;span class="gd"&gt;-            requests = tqdm(future_to_url, total=len(future_to_url), desc=&amp;#39;Fetching tiles&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+            from tqdm import tqdm as progress_bar&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        else:
&lt;span class="gd"&gt;-            requests = future_to_url&lt;/span&gt;
&lt;span class="gi"&gt;+            def progress_bar(*args):&lt;/span&gt;
&lt;span class="gi"&gt;+                return args[0]&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        tiles = []
&lt;span class="gd"&gt;-        for request in requests:&lt;/span&gt;
&lt;span class="gd"&gt;-            tiles.append(request.result())&lt;/span&gt;
&lt;span class="gi"&gt;+        for future in progress_bar(concurrent.futures.as_completed(futures), total=len(futures), desc=&amp;#39;Fetching tiles&amp;#39;):&lt;/span&gt;
&lt;span class="gi"&gt;+            tiles.append(future.result())&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    return tiles

&lt;span class="gu"&gt;@@ -79,26 +106,31 @@ async def fetch_tile_aiohttp(url: str, meta : HipsTileMeta, session) -&amp;gt; Generato&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        raw_data = await response.read()
&lt;span class="w"&gt; &lt;/span&gt;        return HipsTile(meta, raw_data)

&lt;span class="gd"&gt;-async def fetch_all_tiles_aiohttp(tile_metas: List[HipsTileMeta], hips_survey : HipsSurveyProperties, progress_bar: bool) -&amp;gt; List[HipsTile]:&lt;/span&gt;
&lt;span class="gi"&gt;+async def fetch_all_tiles_aiohttp(tile_metas: List[HipsTileMeta],&lt;/span&gt;
&lt;span class="gi"&gt;+                                  hips_survey : HipsSurveyProperties, progress_bar: bool) -&amp;gt; List[HipsTile]:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;Generator function to fetch HiPS tiles from a remote URL using aiohttp.&amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;    import aiohttp

&lt;span class="gd"&gt;-    tasks = []&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    async with aiohttp.ClientSession() as session:
&lt;span class="gd"&gt;-        for idx, url in enumerate(tile_urls(tile_metas, hips_survey)):&lt;/span&gt;
&lt;span class="gd"&gt;-            task = asyncio.ensure_future(fetch_tile_aiohttp(url.format(idx), tile_metas[idx], session))&lt;/span&gt;
&lt;span class="gd"&gt;-            tasks.append(task)&lt;/span&gt;
&lt;span class="gi"&gt;+        futures = []&lt;/span&gt;
&lt;span class="gi"&gt;+        for meta in tile_metas:&lt;/span&gt;
&lt;span class="gi"&gt;+            url = hips_survey.tile_url(meta)&lt;/span&gt;
&lt;span class="gi"&gt;+            futures.append(asyncio.ensure_future(fetch_tile_aiohttp(url, meta, session)))&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        if progress_bar:
&lt;span class="gd"&gt;-            from tqdm import tqdm&lt;/span&gt;
&lt;span class="gd"&gt;-            tiles = []&lt;/span&gt;
&lt;span class="gd"&gt;-            for f in tqdm(tasks, total=len(tasks), desc=&amp;#39;Fetching tiles&amp;#39;):&lt;/span&gt;
&lt;span class="gd"&gt;-                tiles.append(await f)&lt;/span&gt;
&lt;span class="gi"&gt;+            from tqdm import tqdm as progress_bar&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        else:
&lt;span class="gd"&gt;-            tiles = await asyncio.gather(*tasks)&lt;/span&gt;
&lt;span class="gi"&gt;+            def progress_bar(*args):&lt;/span&gt;
&lt;span class="gi"&gt;+                return args[0]&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        tiles = []&lt;/span&gt;
&lt;span class="gi"&gt;+        for future in progress_bar(asyncio.as_completed(futures), total=len(futures), desc=&amp;#39;Fetching tiles&amp;#39;):&lt;/span&gt;
&lt;span class="gi"&gt;+            tiles.append(await future)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    return tiles

&lt;span class="w"&gt; &lt;/span&gt;def tiles_aiohttp(tile_metas: List[HipsTileMeta], hips_survey : HipsSurveyProperties,
&lt;span class="w"&gt; &lt;/span&gt;                  progress_bar: bool) -&amp;gt; List[HipsTile]:
&lt;span class="gd"&gt;-    return asyncio.get_event_loop().run_until_complete(fetch_all_tiles_aiohttp(tile_metas, hips_survey, progress_bar))&lt;/span&gt;
&lt;span class="gi"&gt;+    return asyncio.get_event_loop().run_until_complete(&lt;/span&gt;
&lt;span class="gi"&gt;+        fetch_all_tiles_aiohttp(tile_metas, hips_survey, progress_bar)&lt;/span&gt;
&lt;span class="gi"&gt;+    )&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/tiles/tests/test_fetch.py b/hips/tiles/tests/test_fetch.py&lt;/span&gt;
&lt;span class="gh"&gt;index 53d7ff6..3ba29d6 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tests/test_fetch.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tests/test_fetch.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -8,7 +8,7 @@ from ..tile import HipsTileMeta&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;TILE_FETCH_TEST_CASES = [
&lt;span class="w"&gt; &lt;/span&gt;    dict(
&lt;span class="gd"&gt;-        tile_indices=[69623, 69627, 69628, 69629, 69630, 69631],&lt;/span&gt;
&lt;span class="gi"&gt;+        tile_indices=[69623],&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        tile_format=&amp;#39;fits&amp;#39;,
&lt;span class="w"&gt; &lt;/span&gt;        order=7,
&lt;span class="w"&gt; &lt;/span&gt;        url=&amp;#39;http://alasky.unistra.fr/DSS/DSS2Merged/properties&amp;#39;,
&lt;span class="gu"&gt;@@ -17,7 +17,7 @@ TILE_FETCH_TEST_CASES = [&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        fetch_package=&amp;#39;urllib&amp;#39;
&lt;span class="w"&gt; &lt;/span&gt;    ),
&lt;span class="w"&gt; &lt;/span&gt;    dict(
&lt;span class="gd"&gt;-        tile_indices=[69623, 69627, 69628, 69629, 69630, 69631],&lt;/span&gt;
&lt;span class="gi"&gt;+        tile_indices=[69623],&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        tile_format=&amp;#39;fits&amp;#39;,
&lt;span class="w"&gt; &lt;/span&gt;        order=7,
&lt;span class="w"&gt; &lt;/span&gt;        url=&amp;#39;http://alasky.unistra.fr/DSS/DSS2Merged/properties&amp;#39;,

commit 1116a7a81b01295e5aac21cfdd9e044accf258c2
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Mon Aug 21 22:31:44 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Update aiohttp dependency config

&lt;span class="gh"&gt;diff --git a/.rtd-environment.yml b/.rtd-environment.yml&lt;/span&gt;
&lt;span class="gh"&gt;index 6c2884b..529543d 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/.rtd-environment.yml&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/.rtd-environment.yml&lt;/span&gt;
&lt;span class="gu"&gt;@@ -12,6 +12,7 @@ dependencies:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    - reproject
&lt;span class="w"&gt; &lt;/span&gt;    - matplotlib
&lt;span class="w"&gt; &lt;/span&gt;    - tqdm
&lt;span class="gi"&gt;+    - aiohttp&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    # There&amp;#39;s a problem with Sphinx 1.6 with astropy-helpers
&lt;span class="w"&gt; &lt;/span&gt;    # For now, we pin the Sphinx version to something that works
&lt;span class="w"&gt; &lt;/span&gt;    - sphinx==1.5.6
&lt;span class="gh"&gt;diff --git a/.travis.yml b/.travis.yml&lt;/span&gt;
&lt;span class="gh"&gt;index a486285..49e12dd 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/.travis.yml&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/.travis.yml&lt;/span&gt;
&lt;span class="gu"&gt;@@ -27,7 +27,7 @@ env:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        - MAIN_CMD=&amp;#39;python setup.py&amp;#39;
&lt;span class="w"&gt; &lt;/span&gt;        - SETUP_CMD=&amp;#39;test&amp;#39;
&lt;span class="w"&gt; &lt;/span&gt;        - EVENT_TYPE=&amp;#39;pull_request push&amp;#39;
&lt;span class="gd"&gt;-        - CONDA_DEPENDENCIES=&amp;#39;healpy scikit-image Pillow reproject matplotlib tqdm&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        - CONDA_DEPENDENCIES=&amp;#39;healpy scikit-image Pillow reproject matplotlib tqdm aiohttp&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        - PIP_DEPENDENCIES=&amp;#39;&amp;#39;
&lt;span class="w"&gt; &lt;/span&gt;        - CONDA_CHANNELS=&amp;#39;conda-forge astropy-ci-extras astropy&amp;#39;
&lt;span class="w"&gt; &lt;/span&gt;        - SETUP_XVFB=True
&lt;span class="gh"&gt;diff --git a/docs/installation.rst b/docs/installation.rst&lt;/span&gt;
&lt;span class="gh"&gt;index 28bdc62..d88d8f4 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/docs/installation.rst&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/docs/installation.rst&lt;/span&gt;
&lt;span class="gu"&gt;@@ -80,5 +80,6 @@ In addition, the following packages are needed for optional functionality:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;* `Matplotlib`_ 2.0 or later. Used for plotting in examples.
&lt;span class="w"&gt; &lt;/span&gt;* `tqdm`_. Used for showing progress bar either on terminal or in Jupyter notebook.
&lt;span class="gi"&gt;+* `aiohttp`_. Used for fetching HiPS tiles.&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;We have some info at :ref:`py3` on why we don&amp;#39;t support legacy Python (Python 2).
\ No newline at end of file
&lt;span class="gh"&gt;diff --git a/docs/references.txt b/docs/references.txt&lt;/span&gt;
&lt;span class="gh"&gt;index 9230626..53d1586 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/docs/references.txt&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/docs/references.txt&lt;/span&gt;
&lt;span class="gu"&gt;@@ -6,4 +6,5 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;.. _HiPS paper: https://www.aanda.org/articles/aa/pdf/2015/06/aa26075-15.pdf
&lt;span class="w"&gt; &lt;/span&gt;.. _HiPS IVOA recommendation: http://www.ivoa.net/documents/HiPS/
&lt;span class="w"&gt; &lt;/span&gt;.. _HiPS at CDS: http://aladin.u-strasbg.fr/hips/
&lt;span class="gd"&gt;-.. _tqdm: https://pypi.python.org/pypi/tqdm&lt;/span&gt;
\ No newline at end of file
&lt;span class="gi"&gt;+.. _tqdm: https://pypi.python.org/pypi/tqdm&lt;/span&gt;
&lt;span class="gi"&gt;+.. _aiohttp: http://aiohttp.readthedocs.io/en/stable/&lt;/span&gt;
\ No newline at end of file
&lt;span class="gh"&gt;diff --git a/hips/draw/paint.py b/hips/draw/paint.py&lt;/span&gt;
&lt;span class="gh"&gt;index b76cb59..502a9e8 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/paint.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/paint.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -4,7 +4,7 @@ import numpy as np&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from typing import List, Tuple, Union, Dict, Any
&lt;span class="w"&gt; &lt;/span&gt;from astropy.wcs.utils import proj_plane_pixel_scales
&lt;span class="w"&gt; &lt;/span&gt;from skimage.transform import ProjectiveTransform, warp
&lt;span class="gd"&gt;-from ..tiles import HipsSurveyProperties, HipsTile, HipsTileMeta, HipsTileFetcher&lt;/span&gt;
&lt;span class="gi"&gt;+from ..tiles import HipsSurveyProperties, HipsTile, HipsTileMeta, fetch_tiles&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from ..tiles.tile import compute_image_shape
&lt;span class="w"&gt; &lt;/span&gt;from ..utils import WCSGeometry, healpix_pixels_in_sky_image, hips_order_for_pixel_resolution

&lt;span class="gu"&gt;@@ -36,8 +36,8 @@ class HipsPainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        Use the precise drawing algorithm
&lt;span class="w"&gt; &lt;/span&gt;    progress_bar : bool
&lt;span class="w"&gt; &lt;/span&gt;        Show a progress bar for tile fetching and drawing
&lt;span class="gd"&gt;-    fetch_package : {&amp;#39;urllib&amp;#39;, &amp;#39;aiohttp&amp;#39;}&lt;/span&gt;
&lt;span class="gd"&gt;-        Package to use for fetching HiPS tiles&lt;/span&gt;
&lt;span class="gi"&gt;+    fetch_opts : dict&lt;/span&gt;
&lt;span class="gi"&gt;+        Keyword arguments for fetching HiPS tiles&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    Examples
&lt;span class="w"&gt; &lt;/span&gt;    --------
&lt;span class="gu"&gt;@@ -61,13 +61,13 @@ class HipsPainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;

&lt;span class="w"&gt; &lt;/span&gt;    def __init__(self, geometry: Union[dict, WCSGeometry], hips_survey: Union[str, HipsSurveyProperties],
&lt;span class="gd"&gt;-                 tile_format: str, precise: bool = False, progress_bar: bool = True, fetch_package: str = &amp;#39;urllib&amp;#39;) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+                 tile_format: str, precise: bool = False, progress_bar: bool = True, fetch_opts : dict = None) -&amp;gt; None:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        self.geometry = WCSGeometry.make(geometry)
&lt;span class="w"&gt; &lt;/span&gt;        self.hips_survey = HipsSurveyProperties.make(hips_survey)
&lt;span class="w"&gt; &lt;/span&gt;        self.tile_format = tile_format
&lt;span class="w"&gt; &lt;/span&gt;        self.precise = precise
&lt;span class="w"&gt; &lt;/span&gt;        self.progress_bar = progress_bar
&lt;span class="gd"&gt;-        self.fetch_package = fetch_package&lt;/span&gt;
&lt;span class="gi"&gt;+        self.fetch_opts = fetch_opts&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        self._tiles = None
&lt;span class="w"&gt; &lt;/span&gt;        self.float_image = None
&lt;span class="w"&gt; &lt;/span&gt;        self._stats: Dict[str, Any] = {}
&lt;span class="gu"&gt;@@ -125,11 +125,9 @@ class HipsPainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            )
&lt;span class="w"&gt; &lt;/span&gt;            tile_metas.append(tile_meta)

&lt;span class="gd"&gt;-        tile_fetcher = HipsTileFetcher(tile_metas=tile_metas, hips_survey=self.hips_survey,&lt;/span&gt;
&lt;span class="gd"&gt;-                                       progress_bar=self.progress_bar, fetch_package=self.fetch_package)&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        if self._tiles is None:
&lt;span class="gd"&gt;-            self._tiles = tile_fetcher.tiles&lt;/span&gt;
&lt;span class="gi"&gt;+            self._tiles = fetch_tiles(tile_metas=tile_metas, hips_survey=self.hips_survey,&lt;/span&gt;
&lt;span class="gi"&gt;+                                      progress_bar=self.progress_bar, **self.fetch_opts)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        return self._tiles

&lt;span class="gh"&gt;diff --git a/hips/draw/tests/test_paint.py b/hips/draw/tests/test_paint.py&lt;/span&gt;
&lt;span class="gh"&gt;index 0515014..dfb2db8 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/tests/test_paint.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/tests/test_paint.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -20,7 +20,8 @@ class TestHipsPainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            width=2000, height=1000, fov=&amp;quot;3 deg&amp;quot;,
&lt;span class="w"&gt; &lt;/span&gt;            coordsys=&amp;#39;icrs&amp;#39;, projection=&amp;#39;AIT&amp;#39;,
&lt;span class="w"&gt; &lt;/span&gt;        )
&lt;span class="gd"&gt;-        cls.painter = HipsPainter(cls.geometry, cls.hips_survey, &amp;#39;fits&amp;#39;, fetch_package=&amp;#39;aiohttp&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        fetch_opts = dict(fetch_package=&amp;#39;urllib&amp;#39;, timeout=30, n_parallel=10)&lt;/span&gt;
&lt;span class="gi"&gt;+        cls.painter = HipsPainter(cls.geometry, cls.hips_survey, &amp;#39;fits&amp;#39;, fetch_opts=fetch_opts)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def test_draw_hips_order(self):
&lt;span class="w"&gt; &lt;/span&gt;        assert self.painter.draw_hips_order == 7
&lt;span class="gu"&gt;@@ -43,7 +44,8 @@ class TestHipsPainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            coordsys=&amp;#39;icrs&amp;#39;, projection=&amp;#39;AIT&amp;#39;,
&lt;span class="w"&gt; &lt;/span&gt;        )

&lt;span class="gd"&gt;-        simple_tile_painter = HipsPainter(geometry, self.hips_survey, &amp;#39;fits&amp;#39;, fetch_package=&amp;#39;aiohttp&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        fetch_opts = dict(fetch_package=&amp;#39;urllib&amp;#39;, timeout=30, n_parallel=10)&lt;/span&gt;
&lt;span class="gi"&gt;+        simple_tile_painter = HipsPainter(geometry, self.hips_survey, &amp;#39;fits&amp;#39;, fetch_opts=fetch_opts)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        assert simple_tile_painter.draw_hips_order == pars[&amp;#39;order&amp;#39;]

&lt;span class="w"&gt; &lt;/span&gt;    def test_run(self):
&lt;span class="gh"&gt;diff --git a/hips/draw/tests/test_ui.py b/hips/draw/tests/test_ui.py&lt;/span&gt;
&lt;span class="gh"&gt;index 569e047..d699868 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/tests/test_ui.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/tests/test_ui.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -61,7 +61,9 @@ def test_make_sky_image(tmpdir, pars):&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    hips_survey = HipsSurveyProperties.fetch(url=pars[&amp;#39;url&amp;#39;])
&lt;span class="w"&gt; &lt;/span&gt;    geometry = make_test_wcs_geometry()

&lt;span class="gd"&gt;-    result = make_sky_image(geometry=geometry, hips_survey=hips_survey, tile_format=pars[&amp;#39;file_format&amp;#39;], precise=pars[&amp;#39;precise&amp;#39;])&lt;/span&gt;
&lt;span class="gi"&gt;+    fetch_opts = dict(fetch_package=&amp;#39;urllib&amp;#39;, timeout=30, n_parallel=10)&lt;/span&gt;
&lt;span class="gi"&gt;+    result = make_sky_image(geometry=geometry, hips_survey=hips_survey, tile_format=pars[&amp;#39;file_format&amp;#39;],&lt;/span&gt;
&lt;span class="gi"&gt;+                            precise=pars[&amp;#39;precise&amp;#39;], fetch_opts=fetch_opts)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    assert result.image.shape == pars[&amp;#39;shape&amp;#39;]
&lt;span class="w"&gt; &lt;/span&gt;    assert result.image.dtype == pars[&amp;#39;dtype&amp;#39;]
&lt;span class="gh"&gt;diff --git a/hips/draw/ui.py b/hips/draw/ui.py&lt;/span&gt;
&lt;span class="gh"&gt;index 8d7e1a8..e950d40 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/ui.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/ui.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -15,7 +15,7 @@ __all__ = [&lt;/span&gt;


&lt;span class="w"&gt; &lt;/span&gt;def make_sky_image(geometry: Union[dict, WCSGeometry], hips_survey: Union[str, &amp;#39;HipsSurveyProperties&amp;#39;],
&lt;span class="gd"&gt;-                   tile_format: str, precise: bool = False, progress_bar: bool = True, fetch_package: str = &amp;#39;urllib&amp;#39;) -&amp;gt; &amp;#39;HipsDrawResult&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+                   tile_format: str, precise: bool = False, progress_bar: bool = True, fetch_opts: dict = None) -&amp;gt; &amp;#39;HipsDrawResult&amp;#39;:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;Make sky image: fetch tiles and draw.

&lt;span class="w"&gt; &lt;/span&gt;    The example for this can be found on the :ref:`gs` page.
&lt;span class="gu"&gt;@@ -33,15 +33,15 @@ def make_sky_image(geometry: Union[dict, WCSGeometry], hips_survey: Union[str, &amp;#39;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        Use the precise drawing algorithm
&lt;span class="w"&gt; &lt;/span&gt;    progress_bar : bool
&lt;span class="w"&gt; &lt;/span&gt;        Show a progress bar for tile fetching and drawing
&lt;span class="gd"&gt;-    fetch_package : {&amp;#39;urllib&amp;#39;, &amp;#39;aiohttp&amp;#39;}&lt;/span&gt;
&lt;span class="gd"&gt;-        Package to use for fetching HiPS tiles&lt;/span&gt;
&lt;span class="gi"&gt;+    fetch_opts : dict&lt;/span&gt;
&lt;span class="gi"&gt;+        Keyword arguments for fetching HiPS tiles&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    Returns
&lt;span class="w"&gt; &lt;/span&gt;    -------
&lt;span class="w"&gt; &lt;/span&gt;    result : `~hips.HipsDrawResult`
&lt;span class="w"&gt; &lt;/span&gt;        Result object
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-    painter = HipsPainter(geometry, hips_survey, tile_format, precise, progress_bar, fetch_package)&lt;/span&gt;
&lt;span class="gi"&gt;+    painter = HipsPainter(geometry, hips_survey, tile_format, precise, progress_bar, fetch_opts)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    painter.run()
&lt;span class="w"&gt; &lt;/span&gt;    return HipsDrawResult.from_painter(painter)

&lt;span class="gh"&gt;diff --git a/hips/tiles/fetch.py b/hips/tiles/fetch.py&lt;/span&gt;
&lt;span class="gh"&gt;index a776ec3..28afbf6 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/fetch.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/fetch.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,18 +1,23 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="gd"&gt;-import numpy as np&lt;/span&gt;
&lt;span class="gi"&gt;+import asyncio&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;import urllib.request
&lt;span class="w"&gt; &lt;/span&gt;import concurrent.futures
&lt;span class="w"&gt; &lt;/span&gt;from typing import Generator, List
&lt;span class="w"&gt; &lt;/span&gt;from ..tiles import HipsSurveyProperties, HipsTile, HipsTileMeta

&lt;span class="w"&gt; &lt;/span&gt;__all__ = [
&lt;span class="gd"&gt;-    &amp;#39;HipsTileFetcher&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;#39;fetch_tiles&amp;#39;,&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;]

&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-class HipsTileFetcher:&lt;/span&gt;
&lt;span class="gi"&gt;+def fetch_tiles(tile_metas: List[HipsTileMeta], hips_survey : HipsSurveyProperties,&lt;/span&gt;
&lt;span class="gi"&gt;+                progress_bar: bool = False, n_parallel: int = 10,  timeout: int = 10, fetch_package : str = &amp;#39;urllib&amp;#39;) -&amp;gt; List[HipsTile]:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;Fetch a list of HiPS tiles.

&lt;span class="gi"&gt;+    This function fetches a list of HiPS tiles based&lt;/span&gt;
&lt;span class="gi"&gt;+    on their URLs, which are generated using `hips_survey`&lt;/span&gt;
&lt;span class="gi"&gt;+    and `tile_metas`. The tiles are then fetched asynchronously&lt;/span&gt;
&lt;span class="gi"&gt;+    using urllib or aiohttp.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    Parameters
&lt;span class="w"&gt; &lt;/span&gt;    ----------
&lt;span class="w"&gt; &lt;/span&gt;    tile_metas : List[HipsTileMeta]
&lt;span class="gu"&gt;@@ -28,86 +33,72 @@ class HipsTileFetcher:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    fetch_package : {&amp;#39;urllib&amp;#39;, &amp;#39;aiohttp&amp;#39;}
&lt;span class="w"&gt; &lt;/span&gt;        Package to use for fetching HiPS tiles
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    def __init__(self, tile_metas: List[HipsTileMeta], hips_survey : HipsSurveyProperties,&lt;/span&gt;
&lt;span class="gd"&gt;-                 progress_bar: bool = False, n_parallel: int = 10,  timeout: int = 10, fetch_package : str = &amp;#39;urllib&amp;#39;) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gd"&gt;-        self.tile_metas = tile_metas&lt;/span&gt;
&lt;span class="gd"&gt;-        self.hips_survey = hips_survey&lt;/span&gt;
&lt;span class="gd"&gt;-        self.progress_bar = progress_bar&lt;/span&gt;
&lt;span class="gd"&gt;-        self.n_parallel = n_parallel&lt;/span&gt;
&lt;span class="gd"&gt;-        self.timeout = timeout&lt;/span&gt;
&lt;span class="gd"&gt;-        self.fetch_package = fetch_package&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    @property&lt;/span&gt;
&lt;span class="gd"&gt;-    def tile_urls(self) -&amp;gt; List[str]:&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;List of tile URLs&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-        tile_urls = []&lt;/span&gt;
&lt;span class="gd"&gt;-        for meta in self.tile_metas:&lt;/span&gt;
&lt;span class="gd"&gt;-            tile_urls.append(self.hips_survey.tile_url(meta))&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        return tile_urls&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    @property&lt;/span&gt;
&lt;span class="gd"&gt;-    def tiles(self):&lt;/span&gt;
&lt;span class="gd"&gt;-        if self.fetch_package == &amp;#39;aiohttp&amp;#39;:&lt;/span&gt;
&lt;span class="gd"&gt;-            return self.tiles_aiohttp&lt;/span&gt;
&lt;span class="gd"&gt;-        elif self.fetch_package == &amp;#39;urllib&amp;#39;:&lt;/span&gt;
&lt;span class="gd"&gt;-            return self.tiles_urllib&lt;/span&gt;
&lt;span class="gi"&gt;+    if fetch_package == &amp;#39;aiohttp&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+        return tiles_aiohttp(tile_metas, hips_survey, progress_bar)&lt;/span&gt;
&lt;span class="gi"&gt;+    elif fetch_package == &amp;#39;urllib&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+        return tiles_urllib(tile_metas, hips_survey, progress_bar, n_parallel, timeout)&lt;/span&gt;
&lt;span class="gi"&gt;+    else:&lt;/span&gt;
&lt;span class="gi"&gt;+        raise ValueError(f&amp;#39;Invalid package name: {fetch_package}&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+def tile_urls(tile_metas: List[HipsTileMeta], hips_survey : HipsSurveyProperties) -&amp;gt; List[str]:&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;List of tile URLs&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+    return [hips_survey.tile_url(meta) for meta in tile_metas]&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+def fetch_tile_urllib(url: str, meta: HipsTileMeta, timeout: int) -&amp;gt; Generator:&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;Fetch a HiPS tile asynchronously.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+    with urllib.request.urlopen(url, timeout=timeout) as conn:&lt;/span&gt;
&lt;span class="gi"&gt;+        raw_data = conn.read()&lt;/span&gt;
&lt;span class="gi"&gt;+        return HipsTile(meta, raw_data)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+def tiles_urllib(tile_metas: List[HipsTileMeta], hips_survey : HipsSurveyProperties,&lt;/span&gt;
&lt;span class="gi"&gt;+                 progress_bar: bool = False, n_parallel: int = 10,  timeout: int = 10) -&amp;gt; List[HipsTile]:&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;Generator function to fetch HiPS tiles from a remote URL.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+    with concurrent.futures.ThreadPoolExecutor(max_workers=n_parallel) as executor:&lt;/span&gt;
&lt;span class="gi"&gt;+        future_to_url = {executor.submit(&lt;/span&gt;
&lt;span class="gi"&gt;+            fetch_tile_urllib,&lt;/span&gt;
&lt;span class="gi"&gt;+            url,&lt;/span&gt;
&lt;span class="gi"&gt;+            tile_metas[idx],&lt;/span&gt;
&lt;span class="gi"&gt;+            timeout)&lt;/span&gt;
&lt;span class="gi"&gt;+            : url for idx, url in enumerate(tile_urls(tile_metas, hips_survey))}&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        if progress_bar:&lt;/span&gt;
&lt;span class="gi"&gt;+            from tqdm import tqdm&lt;/span&gt;
&lt;span class="gi"&gt;+            requests = tqdm(future_to_url, total=len(future_to_url), desc=&amp;#39;Fetching tiles&amp;#39;)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        else:
&lt;span class="gd"&gt;-            raise ValueError(f&amp;#39;Invalid package name: {self.fetch_package}&amp;#39;)&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    def fetch_tile_urllib(self, url: str, meta : HipsTileMeta) -&amp;gt; Generator:&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;Fetch a HiPS tile asynchronously.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-        with urllib.request.urlopen(url, timeout=self.timeout) as conn:&lt;/span&gt;
&lt;span class="gd"&gt;-            raw_data = conn.read()&lt;/span&gt;
&lt;span class="gd"&gt;-            return HipsTile(meta, raw_data)&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    @property&lt;/span&gt;
&lt;span class="gd"&gt;-    def tiles_urllib(self) -&amp;gt; np.ndarray:&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;Generator function to fetch HiPS tiles from a remote URL.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-        with concurrent.futures.ThreadPoolExecutor(max_workers=self.n_parallel) as executor:&lt;/span&gt;
&lt;span class="gd"&gt;-            future_to_url = {executor.submit(self.fetch_tile_urllib, url, self.tile_metas[idx]) : url for idx, url in enumerate(self.tile_urls)}&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-            if self.progress_bar:&lt;/span&gt;
&lt;span class="gd"&gt;-                from tqdm import tqdm&lt;/span&gt;
&lt;span class="gd"&gt;-                requests = tqdm(concurrent.futures.as_completed(future_to_url), total=len(future_to_url), desc=&amp;#39;Fetching tiles&amp;#39;)&lt;/span&gt;
&lt;span class="gd"&gt;-            else:&lt;/span&gt;
&lt;span class="gd"&gt;-                requests = future_to_url#concurrent.futures.as_completed(future_to_url)&lt;/span&gt;
&lt;span class="gi"&gt;+            requests = future_to_url&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        tiles = []&lt;/span&gt;
&lt;span class="gi"&gt;+        for request in requests:&lt;/span&gt;
&lt;span class="gi"&gt;+            tiles.append(request.result())&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    return tiles&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+async def fetch_tile_aiohttp(url: str, meta : HipsTileMeta, session) -&amp;gt; Generator:&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;Fetch a HiPS tile asynchronously using aiohttp.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+    async with session.get(url) as response:&lt;/span&gt;
&lt;span class="gi"&gt;+        raw_data = await response.read()&lt;/span&gt;
&lt;span class="gi"&gt;+        return HipsTile(meta, raw_data)&lt;/span&gt;

&lt;span class="gi"&gt;+async def fetch_all_tiles_aiohttp(tile_metas: List[HipsTileMeta], hips_survey : HipsSurveyProperties, progress_bar: bool) -&amp;gt; List[HipsTile]:&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;Generator function to fetch HiPS tiles from a remote URL using aiohttp.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+    import aiohttp&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    tasks = []&lt;/span&gt;
&lt;span class="gi"&gt;+    async with aiohttp.ClientSession() as session:&lt;/span&gt;
&lt;span class="gi"&gt;+        for idx, url in enumerate(tile_urls(tile_metas, hips_survey)):&lt;/span&gt;
&lt;span class="gi"&gt;+            task = asyncio.ensure_future(fetch_tile_aiohttp(url.format(idx), tile_metas[idx], session))&lt;/span&gt;
&lt;span class="gi"&gt;+            tasks.append(task)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        if progress_bar:&lt;/span&gt;
&lt;span class="gi"&gt;+            from tqdm import tqdm&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            tiles = []
&lt;span class="gd"&gt;-            for future in requests:&lt;/span&gt;
&lt;span class="gd"&gt;-                tiles.append(future.result())&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        return tiles&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    async def fetch_tile_aiohttp(self, url: str, meta : HipsTileMeta, session) -&amp;gt; Generator:&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;Fetch a HiPS tile asynchronously using aiohttp.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-        async with session.get(url) as response:&lt;/span&gt;
&lt;span class="gd"&gt;-            raw_data = await response.read()&lt;/span&gt;
&lt;span class="gd"&gt;-            return HipsTile(meta, raw_data)&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    @property&lt;/span&gt;
&lt;span class="gd"&gt;-    async def fetch_all_tiles_aiohttp(self) -&amp;gt; np.ndarray:&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;Generator function to fetch HiPS tiles from a remote URL using aiohttp.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-        import aiohttp, asyncio&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        tasks = []&lt;/span&gt;
&lt;span class="gd"&gt;-        async with aiohttp.ClientSession() as session:&lt;/span&gt;
&lt;span class="gd"&gt;-            for idx, url in enumerate(self.tile_urls):&lt;/span&gt;
&lt;span class="gd"&gt;-                task = asyncio.ensure_future(self.fetch_tile_aiohttp(url.format(idx), self.tile_metas[idx], session))&lt;/span&gt;
&lt;span class="gd"&gt;-                tasks.append(task)&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-            if self.progress_bar:&lt;/span&gt;
&lt;span class="gd"&gt;-                from tqdm import tqdm&lt;/span&gt;
&lt;span class="gd"&gt;-                tiles = []&lt;/span&gt;
&lt;span class="gd"&gt;-                for f in tqdm(tasks, total=len(tasks), desc=&amp;#39;Fetching tiles&amp;#39;):&lt;/span&gt;
&lt;span class="gd"&gt;-                    tiles.append(await f)&lt;/span&gt;
&lt;span class="gd"&gt;-            else:&lt;/span&gt;
&lt;span class="gd"&gt;-                tiles = await asyncio.gather(*tasks)&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        return tiles&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    @property&lt;/span&gt;
&lt;span class="gd"&gt;-    def tiles_aiohttp(self) -&amp;gt; np.ndarray:&lt;/span&gt;
&lt;span class="gd"&gt;-        import asyncio&lt;/span&gt;
&lt;span class="gd"&gt;-        return asyncio.get_event_loop().run_until_complete(self.fetch_all_tiles_aiohttp)&lt;/span&gt;
&lt;span class="gi"&gt;+            for f in tqdm(tasks, total=len(tasks), desc=&amp;#39;Fetching tiles&amp;#39;):&lt;/span&gt;
&lt;span class="gi"&gt;+                tiles.append(await f)&lt;/span&gt;
&lt;span class="gi"&gt;+        else:&lt;/span&gt;
&lt;span class="gi"&gt;+            tiles = await asyncio.gather(*tasks)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    return tiles&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+def tiles_aiohttp(tile_metas: List[HipsTileMeta], hips_survey : HipsSurveyProperties,&lt;/span&gt;
&lt;span class="gi"&gt;+                  progress_bar: bool) -&amp;gt; List[HipsTile]:&lt;/span&gt;
&lt;span class="gi"&gt;+    return asyncio.get_event_loop().run_until_complete(fetch_all_tiles_aiohttp(tile_metas, hips_survey, progress_bar))&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/tiles/tests/test_fetch.py b/hips/tiles/tests/test_fetch.py&lt;/span&gt;
&lt;span class="gh"&gt;index d7956df..53d7ff6 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tests/test_fetch.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tests/test_fetch.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,34 +1,47 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="gi"&gt;+import pytest&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from astropy.tests.helper import remote_data
&lt;span class="w"&gt; &lt;/span&gt;from numpy.testing import assert_allclose
&lt;span class="gd"&gt;-from ..fetch import HipsTileFetcher&lt;/span&gt;
&lt;span class="gi"&gt;+from ..fetch import fetch_tiles&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from ..survey import HipsSurveyProperties
&lt;span class="w"&gt; &lt;/span&gt;from ..tile import HipsTileMeta

&lt;span class="gd"&gt;-class TestHipsTileFetcher:&lt;/span&gt;
&lt;span class="gd"&gt;-    @classmethod&lt;/span&gt;
&lt;span class="gd"&gt;-    def setup_class(cls):&lt;/span&gt;
&lt;span class="gd"&gt;-        url = &amp;#39;http://alasky.unistra.fr/DSS/DSS2Merged/properties&amp;#39;&lt;/span&gt;
&lt;span class="gd"&gt;-        hips_survey = HipsSurveyProperties.fetch(url)&lt;/span&gt;
&lt;span class="gi"&gt;+TILE_FETCH_TEST_CASES = [&lt;/span&gt;
&lt;span class="gi"&gt;+    dict(&lt;/span&gt;
&lt;span class="gi"&gt;+        tile_indices=[69623, 69627, 69628, 69629, 69630, 69631],&lt;/span&gt;
&lt;span class="gi"&gt;+        tile_format=&amp;#39;fits&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+        order=7,&lt;/span&gt;
&lt;span class="gi"&gt;+        url=&amp;#39;http://alasky.unistra.fr/DSS/DSS2Merged/properties&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+        progress_bar=True,&lt;/span&gt;
&lt;span class="gi"&gt;+        data=[2101, 1680, 1532, 1625, 2131],&lt;/span&gt;
&lt;span class="gi"&gt;+        fetch_package=&amp;#39;urllib&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+    ),&lt;/span&gt;
&lt;span class="gi"&gt;+    dict(&lt;/span&gt;
&lt;span class="gi"&gt;+        tile_indices=[69623, 69627, 69628, 69629, 69630, 69631],&lt;/span&gt;
&lt;span class="gi"&gt;+        tile_format=&amp;#39;fits&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+        order=7,&lt;/span&gt;
&lt;span class="gi"&gt;+        url=&amp;#39;http://alasky.unistra.fr/DSS/DSS2Merged/properties&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+        progress_bar=True,&lt;/span&gt;
&lt;span class="gi"&gt;+        data=[2101, 1680, 1532, 1625, 2131],&lt;/span&gt;
&lt;span class="gi"&gt;+        fetch_package=&amp;#39;aiohttp&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+    ),&lt;/span&gt;
&lt;span class="gi"&gt;+]&lt;/span&gt;

&lt;span class="gd"&gt;-        tile_metas, tile_indices = [], [69623, 69627, 69628, 69629, 69630, 69631]&lt;/span&gt;
&lt;span class="gd"&gt;-        for healpix_pixel_index in tile_indices:&lt;/span&gt;
&lt;span class="gd"&gt;-            tile_meta = HipsTileMeta(&lt;/span&gt;
&lt;span class="gd"&gt;-                order=7,&lt;/span&gt;
&lt;span class="gd"&gt;-                ipix=healpix_pixel_index,&lt;/span&gt;
&lt;span class="gd"&gt;-                frame=hips_survey.astropy_frame,&lt;/span&gt;
&lt;span class="gd"&gt;-                file_format=&amp;#39;fits&amp;#39;,&lt;/span&gt;
&lt;span class="gd"&gt;-            )&lt;/span&gt;
&lt;span class="gd"&gt;-            tile_metas.append(tile_meta)&lt;/span&gt;

&lt;span class="gd"&gt;-        cls.fetcher = HipsTileFetcher(tile_metas, hips_survey, progress_bar=False)&lt;/span&gt;
&lt;span class="gi"&gt;+@pytest.mark.parametrize(&amp;#39;pars&amp;#39;, TILE_FETCH_TEST_CASES)&lt;/span&gt;
&lt;span class="gi"&gt;+@remote_data&lt;/span&gt;
&lt;span class="gi"&gt;+def test_fetch_tiles(pars):&lt;/span&gt;
&lt;span class="gi"&gt;+    hips_survey = HipsSurveyProperties.fetch(pars[&amp;#39;url&amp;#39;])&lt;/span&gt;

&lt;span class="gd"&gt;-    @remote_data&lt;/span&gt;
&lt;span class="gd"&gt;-    def test_tiles(self):&lt;/span&gt;
&lt;span class="gd"&gt;-        tiles = self.fetcher.tiles&lt;/span&gt;
&lt;span class="gd"&gt;-        assert_allclose(tiles[0].data[0][5:10], [2101, 1680, 1532, 1625, 2131])&lt;/span&gt;
&lt;span class="gi"&gt;+    tile_metas = []&lt;/span&gt;
&lt;span class="gi"&gt;+    for healpix_pixel_index in pars[&amp;#39;tile_indices&amp;#39;]:&lt;/span&gt;
&lt;span class="gi"&gt;+        tile_meta = HipsTileMeta(&lt;/span&gt;
&lt;span class="gi"&gt;+            order=pars[&amp;#39;order&amp;#39;],&lt;/span&gt;
&lt;span class="gi"&gt;+            ipix=healpix_pixel_index,&lt;/span&gt;
&lt;span class="gi"&gt;+            frame=hips_survey.astropy_frame,&lt;/span&gt;
&lt;span class="gi"&gt;+            file_format=pars[&amp;#39;tile_format&amp;#39;],&lt;/span&gt;
&lt;span class="gi"&gt;+        )&lt;/span&gt;
&lt;span class="gi"&gt;+        tile_metas.append(tile_meta)&lt;/span&gt;

&lt;span class="gd"&gt;-    @remote_data&lt;/span&gt;
&lt;span class="gd"&gt;-    def test_tiles_aiohttp(self):&lt;/span&gt;
&lt;span class="gd"&gt;-        tiles = self.fetcher.tiles_aiohttp&lt;/span&gt;
&lt;span class="gd"&gt;-        assert_allclose(tiles[0].data[0][5:10], [2101, 1680, 1532, 1625, 2131])&lt;/span&gt;
&lt;span class="gi"&gt;+    tiles = fetch_tiles(tile_metas, hips_survey, progress_bar=pars[&amp;#39;progress_bar&amp;#39;], fetch_package=pars[&amp;#39;fetch_package&amp;#39;])&lt;/span&gt;
&lt;span class="gi"&gt;+    assert_allclose(tiles[0].data[0][5:10], [2101, 1680, 1532, 1625, 2131])&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/setup.py b/setup.py&lt;/span&gt;
old mode 100755
new mode 100644
&lt;span class="gh"&gt;index af0099f..3ed902f&lt;/span&gt;
&lt;span class="gd"&gt;--- a/setup.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/setup.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -107,6 +107,7 @@ extras_require = dict(&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;#39;matplotlib&amp;gt;=2.0&amp;#39;,
&lt;span class="w"&gt; &lt;/span&gt;        &amp;#39;reproject&amp;gt;=0.3.1&amp;#39;,
&lt;span class="w"&gt; &lt;/span&gt;        &amp;#39;tqdm&amp;#39;,
&lt;span class="gi"&gt;+        &amp;#39;aiohttp&amp;#39;,&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    ],
&lt;span class="w"&gt; &lt;/span&gt;    develop=[
&lt;span class="w"&gt; &lt;/span&gt;        &amp;#39;matplotlib&amp;gt;=2.0&amp;#39;,
&lt;span class="gu"&gt;@@ -114,6 +115,7 @@ extras_require = dict(&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;#39;pytest&amp;gt;=3.0&amp;#39;,
&lt;span class="w"&gt; &lt;/span&gt;        &amp;#39;mypy&amp;gt;=0.501&amp;#39;,
&lt;span class="w"&gt; &lt;/span&gt;        &amp;#39;tqdm&amp;#39;,
&lt;span class="gi"&gt;+        &amp;#39;aiohttp&amp;#39;,&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    ],
&lt;span class="w"&gt; &lt;/span&gt;)


commit e5118522ba544bab164116a01b1a87a8c3c4f5a2
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Thu Aug 17 20:47:31 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Add tqdm progress bar to urllib and aiohttp fetch methods

&lt;span class="gh"&gt;diff --git a/hips/draw/paint.py b/hips/draw/paint.py&lt;/span&gt;
&lt;span class="gh"&gt;index 07affb9..b76cb59 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/paint.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/paint.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,9 +1,7 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="w"&gt; &lt;/span&gt;import time
&lt;span class="gd"&gt;-import aiohttp&lt;/span&gt;
&lt;span class="gd"&gt;-import asyncio&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;import numpy as np
&lt;span class="gd"&gt;-from typing import List, Tuple, Union, Dict, Any, Generator&lt;/span&gt;
&lt;span class="gi"&gt;+from typing import List, Tuple, Union, Dict, Any&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from astropy.wcs.utils import proj_plane_pixel_scales
&lt;span class="w"&gt; &lt;/span&gt;from skimage.transform import ProjectiveTransform, warp
&lt;span class="w"&gt; &lt;/span&gt;from ..tiles import HipsSurveyProperties, HipsTile, HipsTileMeta, HipsTileFetcher
&lt;span class="gu"&gt;@@ -38,6 +36,8 @@ class HipsPainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        Use the precise drawing algorithm
&lt;span class="w"&gt; &lt;/span&gt;    progress_bar : bool
&lt;span class="w"&gt; &lt;/span&gt;        Show a progress bar for tile fetching and drawing
&lt;span class="gi"&gt;+    fetch_package : {&amp;#39;urllib&amp;#39;, &amp;#39;aiohttp&amp;#39;}&lt;/span&gt;
&lt;span class="gi"&gt;+        Package to use for fetching HiPS tiles&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    Examples
&lt;span class="w"&gt; &lt;/span&gt;    --------
&lt;span class="gu"&gt;@@ -61,12 +61,13 @@ class HipsPainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;

&lt;span class="w"&gt; &lt;/span&gt;    def __init__(self, geometry: Union[dict, WCSGeometry], hips_survey: Union[str, HipsSurveyProperties],
&lt;span class="gd"&gt;-                 tile_format: str, precise: bool = False, progress_bar: bool = True) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+                 tile_format: str, precise: bool = False, progress_bar: bool = True, fetch_package: str = &amp;#39;urllib&amp;#39;) -&amp;gt; None:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        self.geometry = WCSGeometry.make(geometry)
&lt;span class="w"&gt; &lt;/span&gt;        self.hips_survey = HipsSurveyProperties.make(hips_survey)
&lt;span class="w"&gt; &lt;/span&gt;        self.tile_format = tile_format
&lt;span class="w"&gt; &lt;/span&gt;        self.precise = precise
&lt;span class="w"&gt; &lt;/span&gt;        self.progress_bar = progress_bar
&lt;span class="gi"&gt;+        self.fetch_package = fetch_package&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        self._tiles = None
&lt;span class="w"&gt; &lt;/span&gt;        self.float_image = None
&lt;span class="w"&gt; &lt;/span&gt;        self._stats: Dict[str, Any] = {}
&lt;span class="gu"&gt;@@ -111,21 +112,10 @@ class HipsPainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        pt.estimate(src, dst)
&lt;span class="w"&gt; &lt;/span&gt;        return pt

&lt;span class="gd"&gt;-    async def fetch_tile_threaded(self, url: str, session: aiohttp.client.ClientSession) -&amp;gt; Generator:&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;Fetch a HiPS tile asynchronously.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-        async with session.get(url) as response:&lt;/span&gt;
&lt;span class="gd"&gt;-            return await response.read()&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    async def _fetch_tiles(self) -&amp;gt; List[HipsTile]:&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;Generator function to fetch HiPS tiles from a remote URL.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-        if self.progress_bar:&lt;/span&gt;
&lt;span class="gd"&gt;-            from tqdm import tqdm&lt;/span&gt;
&lt;span class="gd"&gt;-            tile_indices = tqdm(self.tile_indices, desc=&amp;#39;Fetching tiles&amp;#39;)&lt;/span&gt;
&lt;span class="gd"&gt;-        else:&lt;/span&gt;
&lt;span class="gd"&gt;-            tile_indices = self.tile_indices&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        for healpix_pixel_index in tile_indices:&lt;/span&gt;
&lt;span class="gd"&gt;-        tile_urls, tile_metas = [], []&lt;/span&gt;
&lt;span class="gi"&gt;+    @property&lt;/span&gt;
&lt;span class="gi"&gt;+    def tiles(self) -&amp;gt; List[HipsTile]:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;List of `~hips.HipsTile` (cached on multiple access).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        tile_metas = []&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        for healpix_pixel_index in self.tile_indices:
&lt;span class="w"&gt; &lt;/span&gt;            tile_meta = HipsTileMeta(
&lt;span class="w"&gt; &lt;/span&gt;                order=self.draw_hips_order,
&lt;span class="gu"&gt;@@ -133,27 +123,12 @@ class HipsPainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                frame=self.hips_survey.astropy_frame,
&lt;span class="w"&gt; &lt;/span&gt;                file_format=self.tile_format,
&lt;span class="w"&gt; &lt;/span&gt;            )
&lt;span class="gd"&gt;-            tile_urls.append(self.hips_survey.tile_url(tile_meta))&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            tile_metas.append(tile_meta)

&lt;span class="gd"&gt;-        tasks = []&lt;/span&gt;
&lt;span class="gd"&gt;-        async with aiohttp.ClientSession() as session:&lt;/span&gt;
&lt;span class="gd"&gt;-            for idx, url in enumerate(tile_urls):&lt;/span&gt;
&lt;span class="gd"&gt;-                task = asyncio.ensure_future(self.fetch_tile_threaded(url.format(idx), session))&lt;/span&gt;
&lt;span class="gd"&gt;-                tasks.append(task)&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-            raw_responses = await asyncio.gather(*tasks)&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        tiles = []&lt;/span&gt;
&lt;span class="gd"&gt;-        for idx, raw_data in enumerate(raw_responses):&lt;/span&gt;
&lt;span class="gd"&gt;-            tiles.append(HipsTile(tile_metas[idx], raw_data))&lt;/span&gt;
&lt;span class="gd"&gt;-        return tiles&lt;/span&gt;
&lt;span class="gi"&gt;+        tile_fetcher = HipsTileFetcher(tile_metas=tile_metas, hips_survey=self.hips_survey,&lt;/span&gt;
&lt;span class="gi"&gt;+                                       progress_bar=self.progress_bar, fetch_package=self.fetch_package)&lt;/span&gt;

&lt;span class="gd"&gt;-    @property&lt;/span&gt;
&lt;span class="gd"&gt;-    def tiles(self) -&amp;gt; List[HipsTile]:&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;List of `~hips.HipsTile` (cached on multiple access).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        if self._tiles is None:
&lt;span class="gd"&gt;-            # self._tiles = asyncio.get_event_loop().run_until_complete(tile_fetcher.tiles)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            self._tiles = tile_fetcher.tiles

&lt;span class="w"&gt; &lt;/span&gt;        return self._tiles
&lt;span class="gh"&gt;diff --git a/hips/draw/tests/test_paint.py b/hips/draw/tests/test_paint.py&lt;/span&gt;
&lt;span class="gh"&gt;index 817e143..0515014 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/tests/test_paint.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/tests/test_paint.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -20,7 +20,7 @@ class TestHipsPainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            width=2000, height=1000, fov=&amp;quot;3 deg&amp;quot;,
&lt;span class="w"&gt; &lt;/span&gt;            coordsys=&amp;#39;icrs&amp;#39;, projection=&amp;#39;AIT&amp;#39;,
&lt;span class="w"&gt; &lt;/span&gt;        )
&lt;span class="gd"&gt;-        cls.painter = HipsPainter(cls.geometry, cls.hips_survey, &amp;#39;fits&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        cls.painter = HipsPainter(cls.geometry, cls.hips_survey, &amp;#39;fits&amp;#39;, fetch_package=&amp;#39;aiohttp&amp;#39;)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def test_draw_hips_order(self):
&lt;span class="w"&gt; &lt;/span&gt;        assert self.painter.draw_hips_order == 7
&lt;span class="gu"&gt;@@ -43,7 +43,7 @@ class TestHipsPainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            coordsys=&amp;#39;icrs&amp;#39;, projection=&amp;#39;AIT&amp;#39;,
&lt;span class="w"&gt; &lt;/span&gt;        )

&lt;span class="gd"&gt;-        simple_tile_painter = HipsPainter(geometry, self.hips_survey, &amp;#39;fits&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        simple_tile_painter = HipsPainter(geometry, self.hips_survey, &amp;#39;fits&amp;#39;, fetch_package=&amp;#39;aiohttp&amp;#39;)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        assert simple_tile_painter.draw_hips_order == pars[&amp;#39;order&amp;#39;]

&lt;span class="w"&gt; &lt;/span&gt;    def test_run(self):
&lt;span class="gh"&gt;diff --git a/hips/draw/ui.py b/hips/draw/ui.py&lt;/span&gt;
&lt;span class="gh"&gt;index 47716ea..8d7e1a8 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/ui.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/ui.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -15,7 +15,7 @@ __all__ = [&lt;/span&gt;


&lt;span class="w"&gt; &lt;/span&gt;def make_sky_image(geometry: Union[dict, WCSGeometry], hips_survey: Union[str, &amp;#39;HipsSurveyProperties&amp;#39;],
&lt;span class="gd"&gt;-                   tile_format: str, precise: bool = False, progress_bar: bool = True) -&amp;gt; &amp;#39;HipsDrawResult&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+                   tile_format: str, precise: bool = False, progress_bar: bool = True, fetch_package: str = &amp;#39;urllib&amp;#39;) -&amp;gt; &amp;#39;HipsDrawResult&amp;#39;:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;Make sky image: fetch tiles and draw.

&lt;span class="w"&gt; &lt;/span&gt;    The example for this can be found on the :ref:`gs` page.
&lt;span class="gu"&gt;@@ -33,13 +33,15 @@ def make_sky_image(geometry: Union[dict, WCSGeometry], hips_survey: Union[str, &amp;#39;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        Use the precise drawing algorithm
&lt;span class="w"&gt; &lt;/span&gt;    progress_bar : bool
&lt;span class="w"&gt; &lt;/span&gt;        Show a progress bar for tile fetching and drawing
&lt;span class="gi"&gt;+    fetch_package : {&amp;#39;urllib&amp;#39;, &amp;#39;aiohttp&amp;#39;}&lt;/span&gt;
&lt;span class="gi"&gt;+        Package to use for fetching HiPS tiles&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    Returns
&lt;span class="w"&gt; &lt;/span&gt;    -------
&lt;span class="w"&gt; &lt;/span&gt;    result : `~hips.HipsDrawResult`
&lt;span class="w"&gt; &lt;/span&gt;        Result object
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-    painter = HipsPainter(geometry, hips_survey, tile_format, precise, progress_bar)&lt;/span&gt;
&lt;span class="gi"&gt;+    painter = HipsPainter(geometry, hips_survey, tile_format, precise, progress_bar, fetch_package)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    painter.run()
&lt;span class="w"&gt; &lt;/span&gt;    return HipsDrawResult.from_painter(painter)

&lt;span class="gh"&gt;diff --git a/hips/tiles/fetch.py b/hips/tiles/fetch.py&lt;/span&gt;
&lt;span class="gh"&gt;index fe5c9ce..a776ec3 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/fetch.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/fetch.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,118 +1,113 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="gd"&gt;-import aiohttp&lt;/span&gt;
&lt;span class="gd"&gt;-import asyncio&lt;/span&gt;
&lt;span class="gi"&gt;+import numpy as np&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;import urllib.request
&lt;span class="w"&gt; &lt;/span&gt;import concurrent.futures
&lt;span class="gd"&gt;-import numpy as np&lt;/span&gt;
&lt;span class="gd"&gt;-from typing import Generator&lt;/span&gt;
&lt;span class="gi"&gt;+from typing import Generator, List&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from ..tiles import HipsSurveyProperties, HipsTile, HipsTileMeta

&lt;span class="w"&gt; &lt;/span&gt;__all__ = [
&lt;span class="w"&gt; &lt;/span&gt;    &amp;#39;HipsTileFetcher&amp;#39;,
&lt;span class="w"&gt; &lt;/span&gt;]

&lt;span class="gd"&gt;-__doctest_skip__ = [&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;#39;HipsTileFetcher&amp;#39;,&lt;/span&gt;
&lt;span class="gd"&gt;-]&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;class HipsTileFetcher:
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;Fetch a list of HiPS tiles.

&lt;span class="w"&gt; &lt;/span&gt;    Parameters
&lt;span class="w"&gt; &lt;/span&gt;    ----------
&lt;span class="gd"&gt;-    geometry : dict or `~hips.utils.WCSGeometry`&lt;/span&gt;
&lt;span class="gd"&gt;-        An object of WCSGeometry&lt;/span&gt;
&lt;span class="gd"&gt;-    hips_survey : str or `~hips.HipsSurveyProperties`&lt;/span&gt;
&lt;span class="gi"&gt;+    tile_metas : List[HipsTileMeta]&lt;/span&gt;
&lt;span class="gi"&gt;+        Python list of HipsTileMeta&lt;/span&gt;
&lt;span class="gi"&gt;+    hips_survey : `~hips.HipsSurveyProperties`&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        HiPS survey properties
&lt;span class="gd"&gt;-    tile_format : {&amp;#39;fits&amp;#39;, &amp;#39;jpg&amp;#39;, &amp;#39;png&amp;#39;}&lt;/span&gt;
&lt;span class="gd"&gt;-        Format of HiPS tile&lt;/span&gt;
&lt;span class="gd"&gt;-    precise : bool&lt;/span&gt;
&lt;span class="gd"&gt;-        Use the precise drawing algorithm&lt;/span&gt;
&lt;span class="gi"&gt;+    progress_bar : bool&lt;/span&gt;
&lt;span class="gi"&gt;+        Show a progress bar for tile fetching and drawing&lt;/span&gt;
&lt;span class="gi"&gt;+    n_parallel : int&lt;/span&gt;
&lt;span class="gi"&gt;+        Number of threads to use for fetching HiPS tiles&lt;/span&gt;
&lt;span class="gi"&gt;+    timeout : int&lt;/span&gt;
&lt;span class="gi"&gt;+        Seconds to timeout for fetching a HiPS tile&lt;/span&gt;
&lt;span class="gi"&gt;+    fetch_package : {&amp;#39;urllib&amp;#39;, &amp;#39;aiohttp&amp;#39;}&lt;/span&gt;
&lt;span class="gi"&gt;+        Package to use for fetching HiPS tiles&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;

&lt;span class="gd"&gt;-    def __init__(self, tile_indices: np.ndarray, hips_order: int, hips_survey: HipsSurveyProperties, tile_format: str,&lt;/span&gt;
&lt;span class="gd"&gt;-                 progress_bar: bool, use_aiohttp: bool) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gd"&gt;-        self.tile_indices = tile_indices&lt;/span&gt;
&lt;span class="gd"&gt;-        self.hips_order = hips_order&lt;/span&gt;
&lt;span class="gi"&gt;+    def __init__(self, tile_metas: List[HipsTileMeta], hips_survey : HipsSurveyProperties,&lt;/span&gt;
&lt;span class="gi"&gt;+                 progress_bar: bool = False, n_parallel: int = 10,  timeout: int = 10, fetch_package : str = &amp;#39;urllib&amp;#39;) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+        self.tile_metas = tile_metas&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        self.hips_survey = hips_survey
&lt;span class="gd"&gt;-        self.tile_format = tile_format&lt;/span&gt;
&lt;span class="gi"&gt;+        self.progress_bar = progress_bar&lt;/span&gt;
&lt;span class="gi"&gt;+        self.n_parallel = n_parallel&lt;/span&gt;
&lt;span class="gi"&gt;+        self.timeout = timeout&lt;/span&gt;
&lt;span class="gi"&gt;+        self.fetch_package = fetch_package&lt;/span&gt;

&lt;span class="gd"&gt;-    def fetch_tile_threaded(self, url: str, session: aiohttp.client.ClientSession) -&amp;gt; Generator:&lt;/span&gt;
&lt;span class="gi"&gt;+    @property&lt;/span&gt;
&lt;span class="gi"&gt;+    def tile_urls(self) -&amp;gt; List[str]:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;List of tile URLs&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        tile_urls = []&lt;/span&gt;
&lt;span class="gi"&gt;+        for meta in self.tile_metas:&lt;/span&gt;
&lt;span class="gi"&gt;+            tile_urls.append(self.hips_survey.tile_url(meta))&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        return tile_urls&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @property&lt;/span&gt;
&lt;span class="gi"&gt;+    def tiles(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        if self.fetch_package == &amp;#39;aiohttp&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+            return self.tiles_aiohttp&lt;/span&gt;
&lt;span class="gi"&gt;+        elif self.fetch_package == &amp;#39;urllib&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+            return self.tiles_urllib&lt;/span&gt;
&lt;span class="gi"&gt;+        else:&lt;/span&gt;
&lt;span class="gi"&gt;+            raise ValueError(f&amp;#39;Invalid package name: {self.fetch_package}&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def fetch_tile_urllib(self, url: str, meta : HipsTileMeta) -&amp;gt; Generator:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Fetch a HiPS tile asynchronously.&amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-        with urllib.request.urlopen(url, timeout=60) as conn:&lt;/span&gt;
&lt;span class="gd"&gt;-            return conn.read()&lt;/span&gt;
&lt;span class="gi"&gt;+        with urllib.request.urlopen(url, timeout=self.timeout) as conn:&lt;/span&gt;
&lt;span class="gi"&gt;+            raw_data = conn.read()&lt;/span&gt;
&lt;span class="gi"&gt;+            return HipsTile(meta, raw_data)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    @property
&lt;span class="gd"&gt;-    def tiles(self) -&amp;gt; np.ndarray:&lt;/span&gt;
&lt;span class="gi"&gt;+    def tiles_urllib(self) -&amp;gt; np.ndarray:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Generator function to fetch HiPS tiles from a remote URL.&amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-        tile_urls, tile_metas = [], []&lt;/span&gt;
&lt;span class="gd"&gt;-        for healpix_pixel_index in self.tile_indices:&lt;/span&gt;
&lt;span class="gd"&gt;-            tile_meta = HipsTileMeta(&lt;/span&gt;
&lt;span class="gd"&gt;-                order=self.hips_order,&lt;/span&gt;
&lt;span class="gd"&gt;-                ipix=healpix_pixel_index,&lt;/span&gt;
&lt;span class="gd"&gt;-                frame=self.hips_survey.astropy_frame,&lt;/span&gt;
&lt;span class="gd"&gt;-                file_format=self.tile_format,&lt;/span&gt;
&lt;span class="gd"&gt;-            )&lt;/span&gt;
&lt;span class="gd"&gt;-            tile_urls.append(self.hips_survey.tile_url(tile_meta))&lt;/span&gt;
&lt;span class="gd"&gt;-            tile_metas.append(tile_meta)&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        raw_responses = []&lt;/span&gt;
&lt;span class="gd"&gt;-        with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:&lt;/span&gt;
&lt;span class="gd"&gt;-            # Start the load operations and mark each future with its URL&lt;/span&gt;
&lt;span class="gd"&gt;-            future_to_url = {executor.submit(self.fetch_tile_threaded, url, 60): url for url in tile_urls}&lt;/span&gt;
&lt;span class="gd"&gt;-            for future in concurrent.futures.as_completed(future_to_url):&lt;/span&gt;
&lt;span class="gd"&gt;-                url = future_to_url[future]&lt;/span&gt;
&lt;span class="gd"&gt;-                # try:&lt;/span&gt;
&lt;span class="gd"&gt;-                raw_responses.append(future.result())&lt;/span&gt;
&lt;span class="gd"&gt;-                # except Exception as exc:&lt;/span&gt;
&lt;span class="gd"&gt;-                #     print(&amp;#39;%r generated an exception: %s&amp;#39; % (url, exc))&lt;/span&gt;
&lt;span class="gd"&gt;-                # else:&lt;/span&gt;
&lt;span class="gd"&gt;-                #     print(&amp;#39;%r page is %d bytes&amp;#39; % (url, len(future.result())))&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        #&lt;/span&gt;
&lt;span class="gd"&gt;-        # tasks = []&lt;/span&gt;
&lt;span class="gd"&gt;-        # async with aiohttp.ClientSession() as session:&lt;/span&gt;
&lt;span class="gd"&gt;-        #     for idx, url in enumerate(tile_urls):&lt;/span&gt;
&lt;span class="gd"&gt;-        #         task = asyncio.ensure_future(self.fetch_tile_threaded(url.format(idx), session))&lt;/span&gt;
&lt;span class="gd"&gt;-        #         tasks.append(task)&lt;/span&gt;
&lt;span class="gd"&gt;-        #&lt;/span&gt;
&lt;span class="gd"&gt;-        #     raw_responses = await asyncio.gather(*tasks)&lt;/span&gt;
&lt;span class="gd"&gt;-        #&lt;/span&gt;
&lt;span class="gd"&gt;-        tiles = []&lt;/span&gt;
&lt;span class="gd"&gt;-        for idx, raw_data in enumerate(raw_responses):&lt;/span&gt;
&lt;span class="gd"&gt;-            tiles.append(HipsTile(tile_metas[idx], raw_data))&lt;/span&gt;
&lt;span class="gi"&gt;+        with concurrent.futures.ThreadPoolExecutor(max_workers=self.n_parallel) as executor:&lt;/span&gt;
&lt;span class="gi"&gt;+            future_to_url = {executor.submit(self.fetch_tile_urllib, url, self.tile_metas[idx]) : url for idx, url in enumerate(self.tile_urls)}&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            if self.progress_bar:&lt;/span&gt;
&lt;span class="gi"&gt;+                from tqdm import tqdm&lt;/span&gt;
&lt;span class="gi"&gt;+                requests = tqdm(concurrent.futures.as_completed(future_to_url), total=len(future_to_url), desc=&amp;#39;Fetching tiles&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+            else:&lt;/span&gt;
&lt;span class="gi"&gt;+                requests = future_to_url#concurrent.futures.as_completed(future_to_url)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            tiles = []&lt;/span&gt;
&lt;span class="gi"&gt;+            for future in requests:&lt;/span&gt;
&lt;span class="gi"&gt;+                tiles.append(future.result())&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        return tiles&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    async def fetch_tile_aiohttp(self, url: str, meta : HipsTileMeta, session) -&amp;gt; Generator:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Fetch a HiPS tile asynchronously using aiohttp.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        async with session.get(url) as response:&lt;/span&gt;
&lt;span class="gi"&gt;+            raw_data = await response.read()&lt;/span&gt;
&lt;span class="gi"&gt;+            return HipsTile(meta, raw_data)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @property&lt;/span&gt;
&lt;span class="gi"&gt;+    async def fetch_all_tiles_aiohttp(self) -&amp;gt; np.ndarray:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Generator function to fetch HiPS tiles from a remote URL using aiohttp.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        import aiohttp, asyncio&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        tasks = []&lt;/span&gt;
&lt;span class="gi"&gt;+        async with aiohttp.ClientSession() as session:&lt;/span&gt;
&lt;span class="gi"&gt;+            for idx, url in enumerate(self.tile_urls):&lt;/span&gt;
&lt;span class="gi"&gt;+                task = asyncio.ensure_future(self.fetch_tile_aiohttp(url.format(idx), self.tile_metas[idx], session))&lt;/span&gt;
&lt;span class="gi"&gt;+                tasks.append(task)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            if self.progress_bar:&lt;/span&gt;
&lt;span class="gi"&gt;+                from tqdm import tqdm&lt;/span&gt;
&lt;span class="gi"&gt;+                tiles = []&lt;/span&gt;
&lt;span class="gi"&gt;+                for f in tqdm(tasks, total=len(tasks), desc=&amp;#39;Fetching tiles&amp;#39;):&lt;/span&gt;
&lt;span class="gi"&gt;+                    tiles.append(await f)&lt;/span&gt;
&lt;span class="gi"&gt;+            else:&lt;/span&gt;
&lt;span class="gi"&gt;+                tiles = await asyncio.gather(*tasks)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        return tiles

&lt;span class="gd"&gt;-    # async def fetch_tile_threaded(self, url: str, session: aiohttp.client.ClientSession) -&amp;gt; Generator:&lt;/span&gt;
&lt;span class="gd"&gt;-    #     &amp;quot;&amp;quot;&amp;quot;Fetch a HiPS tile asynchronously.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-    #     async with session.get(url) as response:&lt;/span&gt;
&lt;span class="gd"&gt;-    #         return await response.read()&lt;/span&gt;
&lt;span class="gd"&gt;-    #&lt;/span&gt;
&lt;span class="gd"&gt;-    # @property&lt;/span&gt;
&lt;span class="gd"&gt;-    # async def tiles(self) -&amp;gt; np.ndarray:&lt;/span&gt;
&lt;span class="gd"&gt;-    #     &amp;quot;&amp;quot;&amp;quot;Generator function to fetch HiPS tiles from a remote URL.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-    #     tile_urls, tile_metas = [], []&lt;/span&gt;
&lt;span class="gd"&gt;-    #     for healpix_pixel_index in self.tile_indices:&lt;/span&gt;
&lt;span class="gd"&gt;-    #         tile_meta = HipsTileMeta(&lt;/span&gt;
&lt;span class="gd"&gt;-    #             order=self.hips_order,&lt;/span&gt;
&lt;span class="gd"&gt;-    #             ipix=healpix_pixel_index,&lt;/span&gt;
&lt;span class="gd"&gt;-    #             frame=self.hips_survey.astropy_frame,&lt;/span&gt;
&lt;span class="gd"&gt;-    #             file_format=self.tile_format,&lt;/span&gt;
&lt;span class="gd"&gt;-    #         )&lt;/span&gt;
&lt;span class="gd"&gt;-    #         tile_urls.append(self.hips_survey.tile_url(tile_meta))&lt;/span&gt;
&lt;span class="gd"&gt;-    #         tile_metas.append(tile_meta)&lt;/span&gt;
&lt;span class="gd"&gt;-    #&lt;/span&gt;
&lt;span class="gd"&gt;-    #     tasks = []&lt;/span&gt;
&lt;span class="gd"&gt;-    #     async with aiohttp.ClientSession() as session:&lt;/span&gt;
&lt;span class="gd"&gt;-    #         for idx, url in enumerate(tile_urls):&lt;/span&gt;
&lt;span class="gd"&gt;-    #             task = asyncio.ensure_future(self.fetch_tile_threaded(url.format(idx), session))&lt;/span&gt;
&lt;span class="gd"&gt;-    #             tasks.append(task)&lt;/span&gt;
&lt;span class="gd"&gt;-    #&lt;/span&gt;
&lt;span class="gd"&gt;-    #         raw_responses = await asyncio.gather(*tasks)&lt;/span&gt;
&lt;span class="gd"&gt;-    #&lt;/span&gt;
&lt;span class="gd"&gt;-    #     tiles = []&lt;/span&gt;
&lt;span class="gd"&gt;-    #     for idx, raw_data in enumerate(raw_responses):&lt;/span&gt;
&lt;span class="gd"&gt;-    #         tiles.append(HipsTile(tile_metas[idx], raw_data))&lt;/span&gt;
&lt;span class="gd"&gt;-    #     return tiles&lt;/span&gt;
\ No newline at end of file
&lt;span class="gi"&gt;+    @property&lt;/span&gt;
&lt;span class="gi"&gt;+    def tiles_aiohttp(self) -&amp;gt; np.ndarray:&lt;/span&gt;
&lt;span class="gi"&gt;+        import asyncio&lt;/span&gt;
&lt;span class="gi"&gt;+        return asyncio.get_event_loop().run_until_complete(self.fetch_all_tiles_aiohttp)&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/tiles/tests/test_fetch.py b/hips/tiles/tests/test_fetch.py&lt;/span&gt;
new file mode 100644
&lt;span class="gh"&gt;index 0000000..d7956df&lt;/span&gt;
&lt;span class="gd"&gt;--- /dev/null&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tests/test_fetch.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -0,0 +1,34 @@&lt;/span&gt;
&lt;span class="gi"&gt;+# Licensed under a 3-clause BSD style license - see LICENSE.rst&lt;/span&gt;
&lt;span class="gi"&gt;+from astropy.tests.helper import remote_data&lt;/span&gt;
&lt;span class="gi"&gt;+from numpy.testing import assert_allclose&lt;/span&gt;
&lt;span class="gi"&gt;+from ..fetch import HipsTileFetcher&lt;/span&gt;
&lt;span class="gi"&gt;+from ..survey import HipsSurveyProperties&lt;/span&gt;
&lt;span class="gi"&gt;+from ..tile import HipsTileMeta&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+class TestHipsTileFetcher:&lt;/span&gt;
&lt;span class="gi"&gt;+    @classmethod&lt;/span&gt;
&lt;span class="gi"&gt;+    def setup_class(cls):&lt;/span&gt;
&lt;span class="gi"&gt;+        url = &amp;#39;http://alasky.unistra.fr/DSS/DSS2Merged/properties&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        hips_survey = HipsSurveyProperties.fetch(url)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        tile_metas, tile_indices = [], [69623, 69627, 69628, 69629, 69630, 69631]&lt;/span&gt;
&lt;span class="gi"&gt;+        for healpix_pixel_index in tile_indices:&lt;/span&gt;
&lt;span class="gi"&gt;+            tile_meta = HipsTileMeta(&lt;/span&gt;
&lt;span class="gi"&gt;+                order=7,&lt;/span&gt;
&lt;span class="gi"&gt;+                ipix=healpix_pixel_index,&lt;/span&gt;
&lt;span class="gi"&gt;+                frame=hips_survey.astropy_frame,&lt;/span&gt;
&lt;span class="gi"&gt;+                file_format=&amp;#39;fits&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+            )&lt;/span&gt;
&lt;span class="gi"&gt;+            tile_metas.append(tile_meta)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        cls.fetcher = HipsTileFetcher(tile_metas, hips_survey, progress_bar=False)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @remote_data&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_tiles(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        tiles = self.fetcher.tiles&lt;/span&gt;
&lt;span class="gi"&gt;+        assert_allclose(tiles[0].data[0][5:10], [2101, 1680, 1532, 1625, 2131])&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @remote_data&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_tiles_aiohttp(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        tiles = self.fetcher.tiles_aiohttp&lt;/span&gt;
&lt;span class="gi"&gt;+        assert_allclose(tiles[0].data[0][5:10], [2101, 1680, 1532, 1625, 2131])&lt;/span&gt;

commit 0eba105e1822e0e50645344e1839e8ec48d5d6de
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Thu Aug 17 19:07:57 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Add option to fetch tiles asynchronously using urllib

&lt;span class="gh"&gt;diff --git a/hips/draw/paint.py b/hips/draw/paint.py&lt;/span&gt;
&lt;span class="gh"&gt;index 4a33100..07affb9 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/paint.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/paint.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -6,7 +6,7 @@ import numpy as np&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from typing import List, Tuple, Union, Dict, Any, Generator
&lt;span class="w"&gt; &lt;/span&gt;from astropy.wcs.utils import proj_plane_pixel_scales
&lt;span class="w"&gt; &lt;/span&gt;from skimage.transform import ProjectiveTransform, warp
&lt;span class="gd"&gt;-from ..tiles import HipsSurveyProperties, HipsTile, HipsTileMeta&lt;/span&gt;
&lt;span class="gi"&gt;+from ..tiles import HipsSurveyProperties, HipsTile, HipsTileMeta, HipsTileFetcher&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from ..tiles.tile import compute_image_shape
&lt;span class="w"&gt; &lt;/span&gt;from ..utils import WCSGeometry, healpix_pixels_in_sky_image, hips_order_for_pixel_resolution

&lt;span class="gu"&gt;@@ -153,7 +153,8 @@ class HipsPainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    def tiles(self) -&amp;gt; List[HipsTile]:
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;List of `~hips.HipsTile` (cached on multiple access).&amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;        if self._tiles is None:
&lt;span class="gd"&gt;-            self._tiles = asyncio.get_event_loop().run_until_complete(self._fetch_tiles())&lt;/span&gt;
&lt;span class="gi"&gt;+            # self._tiles = asyncio.get_event_loop().run_until_complete(tile_fetcher.tiles)&lt;/span&gt;
&lt;span class="gi"&gt;+            self._tiles = tile_fetcher.tiles&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        return self._tiles

&lt;span class="gh"&gt;diff --git a/hips/tiles/__init__.py b/hips/tiles/__init__.py&lt;/span&gt;
&lt;span class="gh"&gt;index bc0ac4a..0f5268e 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/__init__.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/__init__.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -3,3 +3,4 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from .tile import *
&lt;span class="w"&gt; &lt;/span&gt;from .survey import *
&lt;span class="w"&gt; &lt;/span&gt;from .allsky import *
&lt;span class="gi"&gt;+from .fetch import *&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/tiles/fetch.py b/hips/tiles/fetch.py&lt;/span&gt;
new file mode 100644
&lt;span class="gh"&gt;index 0000000..fe5c9ce&lt;/span&gt;
&lt;span class="gd"&gt;--- /dev/null&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/fetch.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -0,0 +1,118 @@&lt;/span&gt;
&lt;span class="gi"&gt;+# Licensed under a 3-clause BSD style license - see LICENSE.rst&lt;/span&gt;
&lt;span class="gi"&gt;+import aiohttp&lt;/span&gt;
&lt;span class="gi"&gt;+import asyncio&lt;/span&gt;
&lt;span class="gi"&gt;+import urllib.request&lt;/span&gt;
&lt;span class="gi"&gt;+import concurrent.futures&lt;/span&gt;
&lt;span class="gi"&gt;+import numpy as np&lt;/span&gt;
&lt;span class="gi"&gt;+from typing import Generator&lt;/span&gt;
&lt;span class="gi"&gt;+from ..tiles import HipsSurveyProperties, HipsTile, HipsTileMeta&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+__all__ = [&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;#39;HipsTileFetcher&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+]&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+__doctest_skip__ = [&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;#39;HipsTileFetcher&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+]&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+class HipsTileFetcher:&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;Fetch a list of HiPS tiles.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    Parameters&lt;/span&gt;
&lt;span class="gi"&gt;+    ----------&lt;/span&gt;
&lt;span class="gi"&gt;+    geometry : dict or `~hips.utils.WCSGeometry`&lt;/span&gt;
&lt;span class="gi"&gt;+        An object of WCSGeometry&lt;/span&gt;
&lt;span class="gi"&gt;+    hips_survey : str or `~hips.HipsSurveyProperties`&lt;/span&gt;
&lt;span class="gi"&gt;+        HiPS survey properties&lt;/span&gt;
&lt;span class="gi"&gt;+    tile_format : {&amp;#39;fits&amp;#39;, &amp;#39;jpg&amp;#39;, &amp;#39;png&amp;#39;}&lt;/span&gt;
&lt;span class="gi"&gt;+        Format of HiPS tile&lt;/span&gt;
&lt;span class="gi"&gt;+    precise : bool&lt;/span&gt;
&lt;span class="gi"&gt;+        Use the precise drawing algorithm&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def __init__(self, tile_indices: np.ndarray, hips_order: int, hips_survey: HipsSurveyProperties, tile_format: str,&lt;/span&gt;
&lt;span class="gi"&gt;+                 progress_bar: bool, use_aiohttp: bool) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+        self.tile_indices = tile_indices&lt;/span&gt;
&lt;span class="gi"&gt;+        self.hips_order = hips_order&lt;/span&gt;
&lt;span class="gi"&gt;+        self.hips_survey = hips_survey&lt;/span&gt;
&lt;span class="gi"&gt;+        self.tile_format = tile_format&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def fetch_tile_threaded(self, url: str, session: aiohttp.client.ClientSession) -&amp;gt; Generator:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Fetch a HiPS tile asynchronously.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        with urllib.request.urlopen(url, timeout=60) as conn:&lt;/span&gt;
&lt;span class="gi"&gt;+            return conn.read()&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @property&lt;/span&gt;
&lt;span class="gi"&gt;+    def tiles(self) -&amp;gt; np.ndarray:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Generator function to fetch HiPS tiles from a remote URL.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        tile_urls, tile_metas = [], []&lt;/span&gt;
&lt;span class="gi"&gt;+        for healpix_pixel_index in self.tile_indices:&lt;/span&gt;
&lt;span class="gi"&gt;+            tile_meta = HipsTileMeta(&lt;/span&gt;
&lt;span class="gi"&gt;+                order=self.hips_order,&lt;/span&gt;
&lt;span class="gi"&gt;+                ipix=healpix_pixel_index,&lt;/span&gt;
&lt;span class="gi"&gt;+                frame=self.hips_survey.astropy_frame,&lt;/span&gt;
&lt;span class="gi"&gt;+                file_format=self.tile_format,&lt;/span&gt;
&lt;span class="gi"&gt;+            )&lt;/span&gt;
&lt;span class="gi"&gt;+            tile_urls.append(self.hips_survey.tile_url(tile_meta))&lt;/span&gt;
&lt;span class="gi"&gt;+            tile_metas.append(tile_meta)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        raw_responses = []&lt;/span&gt;
&lt;span class="gi"&gt;+        with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:&lt;/span&gt;
&lt;span class="gi"&gt;+            # Start the load operations and mark each future with its URL&lt;/span&gt;
&lt;span class="gi"&gt;+            future_to_url = {executor.submit(self.fetch_tile_threaded, url, 60): url for url in tile_urls}&lt;/span&gt;
&lt;span class="gi"&gt;+            for future in concurrent.futures.as_completed(future_to_url):&lt;/span&gt;
&lt;span class="gi"&gt;+                url = future_to_url[future]&lt;/span&gt;
&lt;span class="gi"&gt;+                # try:&lt;/span&gt;
&lt;span class="gi"&gt;+                raw_responses.append(future.result())&lt;/span&gt;
&lt;span class="gi"&gt;+                # except Exception as exc:&lt;/span&gt;
&lt;span class="gi"&gt;+                #     print(&amp;#39;%r generated an exception: %s&amp;#39; % (url, exc))&lt;/span&gt;
&lt;span class="gi"&gt;+                # else:&lt;/span&gt;
&lt;span class="gi"&gt;+                #     print(&amp;#39;%r page is %d bytes&amp;#39; % (url, len(future.result())))&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        #&lt;/span&gt;
&lt;span class="gi"&gt;+        # tasks = []&lt;/span&gt;
&lt;span class="gi"&gt;+        # async with aiohttp.ClientSession() as session:&lt;/span&gt;
&lt;span class="gi"&gt;+        #     for idx, url in enumerate(tile_urls):&lt;/span&gt;
&lt;span class="gi"&gt;+        #         task = asyncio.ensure_future(self.fetch_tile_threaded(url.format(idx), session))&lt;/span&gt;
&lt;span class="gi"&gt;+        #         tasks.append(task)&lt;/span&gt;
&lt;span class="gi"&gt;+        #&lt;/span&gt;
&lt;span class="gi"&gt;+        #     raw_responses = await asyncio.gather(*tasks)&lt;/span&gt;
&lt;span class="gi"&gt;+        #&lt;/span&gt;
&lt;span class="gi"&gt;+        tiles = []&lt;/span&gt;
&lt;span class="gi"&gt;+        for idx, raw_data in enumerate(raw_responses):&lt;/span&gt;
&lt;span class="gi"&gt;+            tiles.append(HipsTile(tile_metas[idx], raw_data))&lt;/span&gt;
&lt;span class="gi"&gt;+        return tiles&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    # async def fetch_tile_threaded(self, url: str, session: aiohttp.client.ClientSession) -&amp;gt; Generator:&lt;/span&gt;
&lt;span class="gi"&gt;+    #     &amp;quot;&amp;quot;&amp;quot;Fetch a HiPS tile asynchronously.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+    #     async with session.get(url) as response:&lt;/span&gt;
&lt;span class="gi"&gt;+    #         return await response.read()&lt;/span&gt;
&lt;span class="gi"&gt;+    #&lt;/span&gt;
&lt;span class="gi"&gt;+    # @property&lt;/span&gt;
&lt;span class="gi"&gt;+    # async def tiles(self) -&amp;gt; np.ndarray:&lt;/span&gt;
&lt;span class="gi"&gt;+    #     &amp;quot;&amp;quot;&amp;quot;Generator function to fetch HiPS tiles from a remote URL.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+    #     tile_urls, tile_metas = [], []&lt;/span&gt;
&lt;span class="gi"&gt;+    #     for healpix_pixel_index in self.tile_indices:&lt;/span&gt;
&lt;span class="gi"&gt;+    #         tile_meta = HipsTileMeta(&lt;/span&gt;
&lt;span class="gi"&gt;+    #             order=self.hips_order,&lt;/span&gt;
&lt;span class="gi"&gt;+    #             ipix=healpix_pixel_index,&lt;/span&gt;
&lt;span class="gi"&gt;+    #             frame=self.hips_survey.astropy_frame,&lt;/span&gt;
&lt;span class="gi"&gt;+    #             file_format=self.tile_format,&lt;/span&gt;
&lt;span class="gi"&gt;+    #         )&lt;/span&gt;
&lt;span class="gi"&gt;+    #         tile_urls.append(self.hips_survey.tile_url(tile_meta))&lt;/span&gt;
&lt;span class="gi"&gt;+    #         tile_metas.append(tile_meta)&lt;/span&gt;
&lt;span class="gi"&gt;+    #&lt;/span&gt;
&lt;span class="gi"&gt;+    #     tasks = []&lt;/span&gt;
&lt;span class="gi"&gt;+    #     async with aiohttp.ClientSession() as session:&lt;/span&gt;
&lt;span class="gi"&gt;+    #         for idx, url in enumerate(tile_urls):&lt;/span&gt;
&lt;span class="gi"&gt;+    #             task = asyncio.ensure_future(self.fetch_tile_threaded(url.format(idx), session))&lt;/span&gt;
&lt;span class="gi"&gt;+    #             tasks.append(task)&lt;/span&gt;
&lt;span class="gi"&gt;+    #&lt;/span&gt;
&lt;span class="gi"&gt;+    #         raw_responses = await asyncio.gather(*tasks)&lt;/span&gt;
&lt;span class="gi"&gt;+    #&lt;/span&gt;
&lt;span class="gi"&gt;+    #     tiles = []&lt;/span&gt;
&lt;span class="gi"&gt;+    #     for idx, raw_data in enumerate(raw_responses):&lt;/span&gt;
&lt;span class="gi"&gt;+    #         tiles.append(HipsTile(tile_metas[idx], raw_data))&lt;/span&gt;
&lt;span class="gi"&gt;+    #     return tiles&lt;/span&gt;
\ No newline at end of file

commit 6c1295814affecd4abe5e2c6e44e22df24ae3ae6
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Tue Aug 15 21:28:00 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Introduce asynchronous fetching of HiPS tiles

&lt;span class="gh"&gt;diff --git a/hips/draw/paint.py b/hips/draw/paint.py&lt;/span&gt;
&lt;span class="gh"&gt;index 482b729..4a33100 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/paint.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/paint.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,7 +1,9 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="w"&gt; &lt;/span&gt;import time
&lt;span class="gi"&gt;+import aiohttp&lt;/span&gt;
&lt;span class="gi"&gt;+import asyncio&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;import numpy as np
&lt;span class="gd"&gt;-from typing import List, Tuple, Union, Dict, Any, Iterator&lt;/span&gt;
&lt;span class="gi"&gt;+from typing import List, Tuple, Union, Dict, Any, Generator&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from astropy.wcs.utils import proj_plane_pixel_scales
&lt;span class="w"&gt; &lt;/span&gt;from skimage.transform import ProjectiveTransform, warp
&lt;span class="w"&gt; &lt;/span&gt;from ..tiles import HipsSurveyProperties, HipsTile, HipsTileMeta
&lt;span class="gu"&gt;@@ -109,7 +111,12 @@ class HipsPainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        pt.estimate(src, dst)
&lt;span class="w"&gt; &lt;/span&gt;        return pt

&lt;span class="gd"&gt;-    def _fetch_tiles(self) -&amp;gt; Iterator[HipsTile]:&lt;/span&gt;
&lt;span class="gi"&gt;+    async def fetch_tile_threaded(self, url: str, session: aiohttp.client.ClientSession) -&amp;gt; Generator:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Fetch a HiPS tile asynchronously.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        async with session.get(url) as response:&lt;/span&gt;
&lt;span class="gi"&gt;+            return await response.read()&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    async def _fetch_tiles(self) -&amp;gt; List[HipsTile]:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Generator function to fetch HiPS tiles from a remote URL.&amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;        if self.progress_bar:
&lt;span class="w"&gt; &lt;/span&gt;            from tqdm import tqdm
&lt;span class="gu"&gt;@@ -118,21 +125,35 @@ class HipsPainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            tile_indices = self.tile_indices

&lt;span class="w"&gt; &lt;/span&gt;        for healpix_pixel_index in tile_indices:
&lt;span class="gi"&gt;+        tile_urls, tile_metas = [], []&lt;/span&gt;
&lt;span class="gi"&gt;+        for healpix_pixel_index in self.tile_indices:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            tile_meta = HipsTileMeta(
&lt;span class="w"&gt; &lt;/span&gt;                order=self.draw_hips_order,
&lt;span class="w"&gt; &lt;/span&gt;                ipix=healpix_pixel_index,
&lt;span class="w"&gt; &lt;/span&gt;                frame=self.hips_survey.astropy_frame,
&lt;span class="w"&gt; &lt;/span&gt;                file_format=self.tile_format,
&lt;span class="w"&gt; &lt;/span&gt;            )
&lt;span class="gd"&gt;-            url = self.hips_survey.tile_url(tile_meta)&lt;/span&gt;
&lt;span class="gd"&gt;-            tile = HipsTile.fetch(tile_meta, url)&lt;/span&gt;
&lt;span class="gd"&gt;-            yield tile&lt;/span&gt;
&lt;span class="gi"&gt;+            tile_urls.append(self.hips_survey.tile_url(tile_meta))&lt;/span&gt;
&lt;span class="gi"&gt;+            tile_metas.append(tile_meta)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        tasks = []&lt;/span&gt;
&lt;span class="gi"&gt;+        async with aiohttp.ClientSession() as session:&lt;/span&gt;
&lt;span class="gi"&gt;+            for idx, url in enumerate(tile_urls):&lt;/span&gt;
&lt;span class="gi"&gt;+                task = asyncio.ensure_future(self.fetch_tile_threaded(url.format(idx), session))&lt;/span&gt;
&lt;span class="gi"&gt;+                tasks.append(task)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            raw_responses = await asyncio.gather(*tasks)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        tiles = []&lt;/span&gt;
&lt;span class="gi"&gt;+        for idx, raw_data in enumerate(raw_responses):&lt;/span&gt;
&lt;span class="gi"&gt;+            tiles.append(HipsTile(tile_metas[idx], raw_data))&lt;/span&gt;
&lt;span class="gi"&gt;+        return tiles&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    @property
&lt;span class="w"&gt; &lt;/span&gt;    def tiles(self) -&amp;gt; List[HipsTile]:
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;List of `~hips.HipsTile` (cached on multiple access).&amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;        if self._tiles is None:
&lt;span class="gd"&gt;-            self._tiles = list(self._fetch_tiles())&lt;/span&gt;
&lt;span class="gi"&gt;+            self._tiles = asyncio.get_event_loop().run_until_complete(self._fetch_tiles())&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        return self._tiles

&lt;span class="gu"&gt;@@ -163,7 +184,6 @@ class HipsPainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            self._stats[&amp;#39;consumed_memory&amp;#39;] += len(tile.raw_data)


&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    def make_tile_list(self):
&lt;span class="w"&gt; &lt;/span&gt;        parent_tiles = self.tiles


commit 25dd9ef7f46e21876968ec784a93f2c14df9c0c4
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Wed Aug 16 22:48:31 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Update .travis.yml

&lt;span class="gh"&gt;diff --git a/.travis.yml b/.travis.yml&lt;/span&gt;
&lt;span class="gh"&gt;index ebe7b8a..76fea2b 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/.travis.yml&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/.travis.yml&lt;/span&gt;
&lt;span class="gu"&gt;@@ -74,7 +74,7 @@ matrix:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        # Run static code analysis
&lt;span class="w"&gt; &lt;/span&gt;        - os: linux
&lt;span class="w"&gt; &lt;/span&gt;          env: MAIN_CMD=&amp;#39;make code-analysis&amp;#39; SETUP_CMD=&amp;#39;&amp;#39;
&lt;span class="gd"&gt;-               PIP_DEPENDENCIES=&amp;#39;mypy pycodestyle&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+               PIP_DEPENDENCIES=&amp;#39;mypy pycodestyle tqdm&amp;#39;&lt;/span&gt;


&lt;span class="w"&gt; &lt;/span&gt;install:
&lt;span class="gh"&gt;diff --git a/setup.py b/setup.py&lt;/span&gt;
&lt;span class="gh"&gt;index 3678fa8..4e0cf5d 100755&lt;/span&gt;
&lt;span class="gd"&gt;--- a/setup.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/setup.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -105,6 +105,7 @@ install_requires = [&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;extras_require = dict(
&lt;span class="w"&gt; &lt;/span&gt;    recommended=[
&lt;span class="w"&gt; &lt;/span&gt;        &amp;#39;matplotlib&amp;gt;=2.0&amp;#39;,
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;#39;tqdm&amp;gt;=4.15&amp;#39;,
&lt;span class="w"&gt; &lt;/span&gt;        &amp;#39;reproject&amp;gt;=0.3.1&amp;#39;,
&lt;span class="w"&gt; &lt;/span&gt;    ],
&lt;span class="gu"&gt;@@ -113,6 +114,7 @@ extras_require = dict(&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;#39;reproject&amp;gt;=0.3.1&amp;#39;,
&lt;span class="w"&gt; &lt;/span&gt;        &amp;#39;pytest&amp;gt;=3.0&amp;#39;,
&lt;span class="w"&gt; &lt;/span&gt;        &amp;#39;mypy&amp;gt;=0.501&amp;#39;,
&lt;span class="gi"&gt;+        &amp;#39;tqdm&amp;gt;=4.15&amp;#39;,&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    ],
&lt;span class="w"&gt; &lt;/span&gt;)


commit 88f4293f12ba0173bc86ad6e39223b5aff04e4ae
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Wed Aug 16 19:33:54 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Update getting started example to contain information on progress bar

&lt;span class="gh"&gt;diff --git a/docs/getting_started.rst b/docs/getting_started.rst&lt;/span&gt;
&lt;span class="gh"&gt;index c308ea1..3ce3d93 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/docs/getting_started.rst&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/docs/getting_started.rst&lt;/span&gt;
&lt;span class="gu"&gt;@@ -33,7 +33,10 @@ To draw a sky image from HiPS image tiles with the `hips` package, follow the fo&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    hips_survey = &amp;#39;CDS/P/DSS2/red&amp;#39;

&lt;span class="w"&gt; &lt;/span&gt;3. Call the `~hips.make_sky_image` function to fetch the HiPS data
&lt;span class="gd"&gt;-   and draw it, returning an object of `~hips.HipsDrawResult`::&lt;/span&gt;
&lt;span class="gi"&gt;+   and draw it, returning an object of `~hips.HipsDrawResult`.&lt;/span&gt;
&lt;span class="gi"&gt;+   By default a progress bar is shown for fetching and&lt;/span&gt;
&lt;span class="gi"&gt;+   drawing HiPS tiles. For batch processing, this can be&lt;/span&gt;
&lt;span class="gi"&gt;+   turned off by passing a keyword argument: `progress_bar=False`::&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    from hips import make_sky_image
&lt;span class="w"&gt; &lt;/span&gt;    result = make_sky_image(geometry, hips_survey, &amp;#39;fits&amp;#39;)
&lt;span class="gh"&gt;diff --git a/docs/installation.rst b/docs/installation.rst&lt;/span&gt;
&lt;span class="gh"&gt;index df4bcda..a1dbe1b 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/docs/installation.rst&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/docs/installation.rst&lt;/span&gt;
&lt;span class="gu"&gt;@@ -79,5 +79,6 @@ The ``hips`` package has the following requirements:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;In addition, the following packages are needed for optional functionality:

&lt;span class="w"&gt; &lt;/span&gt;* `Matplotlib`_ 2.0 or later. Used for plotting in examples.
&lt;span class="gi"&gt;+* `tqdm`_ 4.15 or later. Used for showing progress bar either on terminal or in Jupyter notebook.&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;We have some info at :ref:`py3` on why we don&amp;#39;t support legacy Python (Python 2).
\ No newline at end of file
&lt;span class="gh"&gt;diff --git a/docs/references.txt b/docs/references.txt&lt;/span&gt;
&lt;span class="gh"&gt;index cfe3062..9230626 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/docs/references.txt&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/docs/references.txt&lt;/span&gt;
&lt;span class="gu"&gt;@@ -5,4 +5,5 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;.. _Pillow: https://python-pillow.org/
&lt;span class="w"&gt; &lt;/span&gt;.. _HiPS paper: https://www.aanda.org/articles/aa/pdf/2015/06/aa26075-15.pdf
&lt;span class="w"&gt; &lt;/span&gt;.. _HiPS IVOA recommendation: http://www.ivoa.net/documents/HiPS/
&lt;span class="gd"&gt;-.. _HiPS at CDS: http://aladin.u-strasbg.fr/hips/&lt;/span&gt;
\ No newline at end of file
&lt;span class="gi"&gt;+.. _HiPS at CDS: http://aladin.u-strasbg.fr/hips/&lt;/span&gt;
&lt;span class="gi"&gt;+.. _tqdm: https://pypi.python.org/pypi/tqdm&lt;/span&gt;
\ No newline at end of file
&lt;span class="gh"&gt;diff --git a/hips/draw/paint.py b/hips/draw/paint.py&lt;/span&gt;
&lt;span class="gh"&gt;index 18848df..482b729 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/paint.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/paint.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -59,7 +59,7 @@ class HipsPainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;

&lt;span class="w"&gt; &lt;/span&gt;    def __init__(self, geometry: Union[dict, WCSGeometry], hips_survey: Union[str, HipsSurveyProperties],
&lt;span class="gd"&gt;-                 tile_format: str, precise: bool = False, progress_bar: bool = False) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+                 tile_format: str, precise: bool = False, progress_bar: bool = True) -&amp;gt; None:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        self.geometry = WCSGeometry.make(geometry)
&lt;span class="w"&gt; &lt;/span&gt;        self.hips_survey = HipsSurveyProperties.make(hips_survey)
&lt;span class="w"&gt; &lt;/span&gt;        self.tile_format = tile_format
&lt;span class="gu"&gt;@@ -113,7 +113,7 @@ class HipsPainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Generator function to fetch HiPS tiles from a remote URL.&amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;        if self.progress_bar:
&lt;span class="w"&gt; &lt;/span&gt;            from tqdm import tqdm
&lt;span class="gd"&gt;-            tile_indices = tqdm(self.tile_indices, desc=&amp;#39;Fetching tiles&amp;#39;, disable=not self.progress_bar)&lt;/span&gt;
&lt;span class="gi"&gt;+            tile_indices = tqdm(self.tile_indices, desc=&amp;#39;Fetching tiles&amp;#39;)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        else:
&lt;span class="w"&gt; &lt;/span&gt;            tile_indices = self.tile_indices

&lt;span class="gu"&gt;@@ -196,7 +196,7 @@ class HipsPainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        image = self._make_empty_sky_image()
&lt;span class="w"&gt; &lt;/span&gt;        if self.progress_bar:
&lt;span class="w"&gt; &lt;/span&gt;            from tqdm import tqdm
&lt;span class="gd"&gt;-            tiles = tqdm(self.draw_tiles, desc=&amp;#39;Drawing tiles&amp;#39;, disable=not self.progress_bar)&lt;/span&gt;
&lt;span class="gi"&gt;+            tiles = tqdm(self.draw_tiles, desc=&amp;#39;Drawing tiles&amp;#39;)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        else:
&lt;span class="w"&gt; &lt;/span&gt;            tiles = self.draw_tiles

&lt;span class="gh"&gt;diff --git a/hips/draw/ui.py b/hips/draw/ui.py&lt;/span&gt;
&lt;span class="gh"&gt;index b608ac9..47716ea 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/ui.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/ui.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -15,7 +15,7 @@ __all__ = [&lt;/span&gt;


&lt;span class="w"&gt; &lt;/span&gt;def make_sky_image(geometry: Union[dict, WCSGeometry], hips_survey: Union[str, &amp;#39;HipsSurveyProperties&amp;#39;],
&lt;span class="gd"&gt;-                   tile_format: str, precise: bool = False, progress_bar: bool = False) -&amp;gt; &amp;#39;HipsDrawResult&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+                   tile_format: str, precise: bool = False, progress_bar: bool = True) -&amp;gt; &amp;#39;HipsDrawResult&amp;#39;:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;Make sky image: fetch tiles and draw.

&lt;span class="w"&gt; &lt;/span&gt;    The example for this can be found on the :ref:`gs` page.
&lt;span class="gh"&gt;diff --git a/setup.py b/setup.py&lt;/span&gt;
&lt;span class="gh"&gt;index c517001..3678fa8 100755&lt;/span&gt;
&lt;span class="gd"&gt;--- a/setup.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/setup.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -105,6 +105,7 @@ install_requires = [&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;extras_require = dict(
&lt;span class="w"&gt; &lt;/span&gt;    recommended=[
&lt;span class="w"&gt; &lt;/span&gt;        &amp;#39;matplotlib&amp;gt;=2.0&amp;#39;,
&lt;span class="gi"&gt;+        &amp;#39;tqdm&amp;gt;=4.15&amp;#39;,&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;#39;reproject&amp;gt;=0.3.1&amp;#39;,
&lt;span class="w"&gt; &lt;/span&gt;    ],
&lt;span class="w"&gt; &lt;/span&gt;    develop=[

commit e2c9c69989072fffb6271b43273fee65d186e274
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Wed Aug 16 18:15:08 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Make tqdm an optional dependency

&lt;span class="gh"&gt;diff --git a/hips/draw/paint.py b/hips/draw/paint.py&lt;/span&gt;
&lt;span class="gh"&gt;index d227364..18848df 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/paint.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/paint.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,7 +1,6 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="w"&gt; &lt;/span&gt;import time
&lt;span class="w"&gt; &lt;/span&gt;import numpy as np
&lt;span class="gd"&gt;-from tqdm import tqdm&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from typing import List, Tuple, Union, Dict, Any, Iterator
&lt;span class="w"&gt; &lt;/span&gt;from astropy.wcs.utils import proj_plane_pixel_scales
&lt;span class="w"&gt; &lt;/span&gt;from skimage.transform import ProjectiveTransform, warp
&lt;span class="gu"&gt;@@ -112,7 +111,13 @@ class HipsPainter:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def _fetch_tiles(self) -&amp;gt; Iterator[HipsTile]:
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Generator function to fetch HiPS tiles from a remote URL.&amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-        for healpix_pixel_index in tqdm(self.tile_indices, desc=&amp;#39;Fetching tiles&amp;#39;, disable=not self.progress_bar):&lt;/span&gt;
&lt;span class="gi"&gt;+        if self.progress_bar:&lt;/span&gt;
&lt;span class="gi"&gt;+            from tqdm import tqdm&lt;/span&gt;
&lt;span class="gi"&gt;+            tile_indices = tqdm(self.tile_indices, desc=&amp;#39;Fetching tiles&amp;#39;, disable=not self.progress_bar)&lt;/span&gt;
&lt;span class="gi"&gt;+        else:&lt;/span&gt;
&lt;span class="gi"&gt;+            tile_indices = self.tile_indices&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        for healpix_pixel_index in tile_indices:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            tile_meta = HipsTileMeta(
&lt;span class="w"&gt; &lt;/span&gt;                order=self.draw_hips_order,
&lt;span class="w"&gt; &lt;/span&gt;                ipix=healpix_pixel_index,
&lt;span class="gu"&gt;@@ -189,8 +194,13 @@ class HipsPainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    def draw_all_tiles(self):
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Make an empty sky image and draw all the tiles.&amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;        image = self._make_empty_sky_image()
&lt;span class="gi"&gt;+        if self.progress_bar:&lt;/span&gt;
&lt;span class="gi"&gt;+            from tqdm import tqdm&lt;/span&gt;
&lt;span class="gi"&gt;+            tiles = tqdm(self.draw_tiles, desc=&amp;#39;Drawing tiles&amp;#39;, disable=not self.progress_bar)&lt;/span&gt;
&lt;span class="gi"&gt;+        else:&lt;/span&gt;
&lt;span class="gi"&gt;+            tiles = self.draw_tiles&lt;/span&gt;

&lt;span class="gd"&gt;-        for tile in tqdm(self.draw_tiles, desc=&amp;#39;Drawing tiles&amp;#39;, disable=not self.progress_bar):&lt;/span&gt;
&lt;span class="gi"&gt;+        for tile in tiles:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            tile_image = self.warp_image(tile)
&lt;span class="w"&gt; &lt;/span&gt;            # TODO: put better algorithm here instead of summing pixels
&lt;span class="w"&gt; &lt;/span&gt;            # this can lead to pixels that are painted twice and become to bright

commit ba813ef65fe503bb879927fd25f2975c1a735176
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Aug 11 18:48:03 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Add progress bar support for fetching and drawing HiPS tiles

&lt;span class="gh"&gt;diff --git a/hips/draw/paint.py b/hips/draw/paint.py&lt;/span&gt;
&lt;span class="gh"&gt;index f0120c6..d227364 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/paint.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/paint.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,6 +1,7 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="w"&gt; &lt;/span&gt;import time
&lt;span class="w"&gt; &lt;/span&gt;import numpy as np
&lt;span class="gi"&gt;+from tqdm import tqdm&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from typing import List, Tuple, Union, Dict, Any, Iterator
&lt;span class="w"&gt; &lt;/span&gt;from astropy.wcs.utils import proj_plane_pixel_scales
&lt;span class="w"&gt; &lt;/span&gt;from skimage.transform import ProjectiveTransform, warp
&lt;span class="gu"&gt;@@ -34,6 +35,8 @@ class HipsPainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        Format of HiPS tile
&lt;span class="w"&gt; &lt;/span&gt;    precise : bool
&lt;span class="w"&gt; &lt;/span&gt;        Use the precise drawing algorithm
&lt;span class="gi"&gt;+    progress_bar : bool&lt;/span&gt;
&lt;span class="gi"&gt;+        Show a progress bar for tile fetching and drawing&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    Examples
&lt;span class="w"&gt; &lt;/span&gt;    --------
&lt;span class="gu"&gt;@@ -56,11 +59,13 @@ class HipsPainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    (1000, 2000)
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;

&lt;span class="gd"&gt;-    def __init__(self, geometry: Union[dict, WCSGeometry], hips_survey: Union[str, HipsSurveyProperties], tile_format: str, precise: bool = False) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+    def __init__(self, geometry: Union[dict, WCSGeometry], hips_survey: Union[str, HipsSurveyProperties],&lt;/span&gt;
&lt;span class="gi"&gt;+                 tile_format: str, precise: bool = False, progress_bar: bool = False) -&amp;gt; None:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        self.geometry = WCSGeometry.make(geometry)
&lt;span class="w"&gt; &lt;/span&gt;        self.hips_survey = HipsSurveyProperties.make(hips_survey)
&lt;span class="w"&gt; &lt;/span&gt;        self.tile_format = tile_format
&lt;span class="w"&gt; &lt;/span&gt;        self.precise = precise
&lt;span class="gi"&gt;+        self.progress_bar = progress_bar&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        self._tiles = None
&lt;span class="w"&gt; &lt;/span&gt;        self.float_image = None
&lt;span class="w"&gt; &lt;/span&gt;        self._stats: Dict[str, Any] = {}
&lt;span class="gu"&gt;@@ -107,7 +112,7 @@ class HipsPainter:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def _fetch_tiles(self) -&amp;gt; Iterator[HipsTile]:
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Generator function to fetch HiPS tiles from a remote URL.&amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-        for healpix_pixel_index in self.tile_indices:&lt;/span&gt;
&lt;span class="gi"&gt;+        for healpix_pixel_index in tqdm(self.tile_indices, desc=&amp;#39;Fetching tiles&amp;#39;, disable=not self.progress_bar):&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            tile_meta = HipsTileMeta(
&lt;span class="w"&gt; &lt;/span&gt;                order=self.draw_hips_order,
&lt;span class="w"&gt; &lt;/span&gt;                ipix=healpix_pixel_index,
&lt;span class="gu"&gt;@@ -121,12 +126,6 @@ class HipsPainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    @property
&lt;span class="w"&gt; &lt;/span&gt;    def tiles(self) -&amp;gt; List[HipsTile]:
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;List of `~hips.HipsTile` (cached on multiple access).&amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-        # TODO: add progress bar reporting here???&lt;/span&gt;
&lt;span class="gd"&gt;-        # Do it in a separate pull request&lt;/span&gt;
&lt;span class="gd"&gt;-        # It has to work in the terminal and Jupyter notebook&lt;/span&gt;
&lt;span class="gd"&gt;-        # Users have to be able to turn it off&lt;/span&gt;
&lt;span class="gd"&gt;-        # So you have an option for it.&lt;/span&gt;
&lt;span class="gd"&gt;-        # Maybe if you add it now, make it off by default.&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        if self._tiles is None:
&lt;span class="w"&gt; &lt;/span&gt;            self._tiles = list(self._fetch_tiles())

&lt;span class="gu"&gt;@@ -159,6 +158,7 @@ class HipsPainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            self._stats[&amp;#39;consumed_memory&amp;#39;] += len(tile.raw_data)


&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    def make_tile_list(self):
&lt;span class="w"&gt; &lt;/span&gt;        parent_tiles = self.tiles

&lt;span class="gu"&gt;@@ -190,9 +190,7 @@ class HipsPainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Make an empty sky image and draw all the tiles.&amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;        image = self._make_empty_sky_image()

&lt;span class="gd"&gt;-        # TODO: this is the second place where we should add&lt;/span&gt;
&lt;span class="gd"&gt;-        # progress reporting&lt;/span&gt;
&lt;span class="gd"&gt;-        for tile in self.draw_tiles:&lt;/span&gt;
&lt;span class="gi"&gt;+        for tile in tqdm(self.draw_tiles, desc=&amp;#39;Drawing tiles&amp;#39;, disable=not self.progress_bar):&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            tile_image = self.warp_image(tile)
&lt;span class="w"&gt; &lt;/span&gt;            # TODO: put better algorithm here instead of summing pixels
&lt;span class="w"&gt; &lt;/span&gt;            # this can lead to pixels that are painted twice and become to bright
&lt;span class="gh"&gt;diff --git a/hips/draw/ui.py b/hips/draw/ui.py&lt;/span&gt;
&lt;span class="gh"&gt;index fb56b4e..b608ac9 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/ui.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/ui.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -15,7 +15,7 @@ __all__ = [&lt;/span&gt;


&lt;span class="w"&gt; &lt;/span&gt;def make_sky_image(geometry: Union[dict, WCSGeometry], hips_survey: Union[str, &amp;#39;HipsSurveyProperties&amp;#39;],
&lt;span class="gd"&gt;-                   tile_format: str, precise: bool = False) -&amp;gt; &amp;#39;HipsDrawResult&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+                   tile_format: str, precise: bool = False, progress_bar: bool = False) -&amp;gt; &amp;#39;HipsDrawResult&amp;#39;:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;Make sky image: fetch tiles and draw.

&lt;span class="w"&gt; &lt;/span&gt;    The example for this can be found on the :ref:`gs` page.
&lt;span class="gu"&gt;@@ -31,13 +31,15 @@ def make_sky_image(geometry: Union[dict, WCSGeometry], hips_survey: Union[str, &amp;#39;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        (some surveys are available in several formats, so this extra argument is needed)
&lt;span class="w"&gt; &lt;/span&gt;    precise : bool
&lt;span class="w"&gt; &lt;/span&gt;        Use the precise drawing algorithm
&lt;span class="gi"&gt;+    progress_bar : bool&lt;/span&gt;
&lt;span class="gi"&gt;+        Show a progress bar for tile fetching and drawing&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    Returns
&lt;span class="w"&gt; &lt;/span&gt;    -------
&lt;span class="w"&gt; &lt;/span&gt;    result : `~hips.HipsDrawResult`
&lt;span class="w"&gt; &lt;/span&gt;        Result object
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-    painter = HipsPainter(geometry, hips_survey, tile_format, precise)&lt;/span&gt;
&lt;span class="gi"&gt;+    painter = HipsPainter(geometry, hips_survey, tile_format, precise, progress_bar)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    painter.run()
&lt;span class="w"&gt; &lt;/span&gt;    return HipsDrawResult.from_painter(painter)


commit c026c96aa19e46f66a8e84a1c3429476c2803bb4
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Wed Aug 9 21:45:14 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Add reporting functionality for HipsPainter

&lt;span class="gh"&gt;diff --git a/hips/draw/paint.py b/hips/draw/paint.py&lt;/span&gt;
&lt;span class="gh"&gt;index efaa251..142f4b8 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/paint.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/paint.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -153,13 +153,11 @@ class HipsPainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        t1 = time.time()
&lt;span class="w"&gt; &lt;/span&gt;        self._stats[&amp;#39;draw_time&amp;#39;] = t1 - t0

&lt;span class="gd"&gt;-        # Todo for Adeel: copy over the stats dir to the DrawResult&lt;/span&gt;
&lt;span class="gd"&gt;-        # and print it out nicely for the user in the report.&lt;/span&gt;
&lt;span class="gd"&gt;-        # TODO: could add more code here that e.g. tries to measure memory usage&lt;/span&gt;
&lt;span class="gd"&gt;-        # this could be done with https://pypi.python.org/pypi/psutil&lt;/span&gt;
&lt;span class="gd"&gt;-        # or you could just compute the size of `raw_data` and `data` for each tile and sum them up&lt;/span&gt;
&lt;span class="gd"&gt;-        # and also measure the memory used by the output sky image in MB&lt;/span&gt;
&lt;span class="gd"&gt;-        # Other useful info: number of tiles fetched&lt;/span&gt;
&lt;span class="gi"&gt;+        self._stats[&amp;#39;tile_count&amp;#39;] = len(self.draw_tiles)&lt;/span&gt;
&lt;span class="gi"&gt;+        self._stats[&amp;#39;consumed_memory&amp;#39;] = 0&lt;/span&gt;
&lt;span class="gi"&gt;+        for tile in self.draw_tiles:&lt;/span&gt;
&lt;span class="gi"&gt;+            self._stats[&amp;#39;consumed_memory&amp;#39;] += len(tile.raw_data)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def make_tile_list(self):
&lt;span class="w"&gt; &lt;/span&gt;        parent_tiles = self.tiles
&lt;span class="gh"&gt;diff --git a/hips/draw/tests/test_ui.py b/hips/draw/tests/test_ui.py&lt;/span&gt;
&lt;span class="gh"&gt;index dc8238d..569e047 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/tests/test_ui.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/tests/test_ui.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -71,3 +71,4 @@ def test_make_sky_image(tmpdir, pars):&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    assert_allclose(result.image[200, 995], pars[&amp;#39;data_2&amp;#39;])
&lt;span class="w"&gt; &lt;/span&gt;    result.write_image(str(tmpdir / &amp;#39;test.&amp;#39; + pars[&amp;#39;file_format&amp;#39;]))
&lt;span class="w"&gt; &lt;/span&gt;    result.plot()
&lt;span class="gi"&gt;+    result.report()&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/draw/ui.py b/hips/draw/ui.py&lt;/span&gt;
&lt;span class="gh"&gt;index a7857c1..fb56b4e 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/ui.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/ui.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -55,13 +55,16 @@ class HipsDrawResult:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        Format of HiPS tile
&lt;span class="w"&gt; &lt;/span&gt;    tiles : list
&lt;span class="w"&gt; &lt;/span&gt;        Python list of `~hips.HipsTile` objects that were used
&lt;span class="gi"&gt;+    stats : dict&lt;/span&gt;
&lt;span class="gi"&gt;+        Information including time for fetching and drawing HiPS tiles&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;

&lt;span class="gd"&gt;-    def __init__(self, image: np.ndarray, geometry: WCSGeometry, tile_format: str, tiles: List[HipsTile]) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+    def __init__(self, image: np.ndarray, geometry: WCSGeometry, tile_format: str, tiles: List[HipsTile], stats: dict) -&amp;gt; None:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        self.image = image
&lt;span class="w"&gt; &lt;/span&gt;        self.geometry = geometry
&lt;span class="w"&gt; &lt;/span&gt;        self.tile_format = tile_format
&lt;span class="w"&gt; &lt;/span&gt;        self.tiles = tiles
&lt;span class="gi"&gt;+        self.stats = stats&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def __str__(self):
&lt;span class="w"&gt; &lt;/span&gt;        return (
&lt;span class="gu"&gt;@@ -89,6 +92,7 @@ class HipsDrawResult:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            geometry=painter.geometry,
&lt;span class="w"&gt; &lt;/span&gt;            tile_format=painter.tile_format,
&lt;span class="w"&gt; &lt;/span&gt;            tiles=painter.tiles,
&lt;span class="gi"&gt;+            stats=painter._stats,&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        )

&lt;span class="w"&gt; &lt;/span&gt;    def write_image(self, filename: str) -&amp;gt; None:
&lt;span class="gu"&gt;@@ -120,3 +124,13 @@ class HipsDrawResult:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            ax.plot(corners.data.lon.deg, corners.data.lat.deg,
&lt;span class="w"&gt; &lt;/span&gt;                    transform=ax.get_transform(&amp;#39;world&amp;#39;), **opts)
&lt;span class="w"&gt; &lt;/span&gt;        ax.imshow(self.image, origin=&amp;#39;lower&amp;#39;)
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def report(self) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Print a brief report for the fetched data.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        print (&lt;/span&gt;
&lt;span class="gi"&gt;+            f&amp;quot;Time for fetching tiles = {self.stats[&amp;#39;fetch_time&amp;#39;]} seconds\n&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+            f&amp;quot;Time for drawing tiles = {self.stats[&amp;#39;draw_time&amp;#39;]} seconds\n&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+            f&amp;quot;Total memory consumed = {self.stats[&amp;#39;consumed_memory&amp;#39;] / 1e6} MB\n&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+            f&amp;quot;Total tiles fetched = {self.stats[&amp;#39;tile_count&amp;#39;]}\n&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        )&lt;/span&gt;

commit 0981f309aade1f33e0586341108dfff0fd108ab2
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Tue Aug 8 13:59:32 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Reformat docstring for create_from_dict class method

&lt;span class="gh"&gt;diff --git a/hips/tiles/tests/test_allsky.py b/hips/tiles/tests/test_allsky.py&lt;/span&gt;
&lt;span class="gh"&gt;index d933938..625281a 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tests/test_allsky.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tests/test_allsky.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -11,7 +11,7 @@ TEST_CASES = [&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    dict(
&lt;span class="w"&gt; &lt;/span&gt;        label=&amp;#39;fits&amp;#39;,
&lt;span class="w"&gt; &lt;/span&gt;        meta=dict(order=3, ipix=-1, file_format=&amp;#39;fits&amp;#39;),
&lt;span class="gd"&gt;-        url=&amp;#39;http://alasky.unistra.fr/IRAC4/Norder3/Allsky.fits&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+        url=&amp;#39;https://github.com/hipspy/hips-extra/blob/master/datasets/samples/IRAC4/Norder3/Allsky.fits?raw=true&amp;#39;,&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        filename=&amp;#39;datasets/samples/IRAC4/Norder3/Allsky.fits&amp;#39;,

&lt;span class="w"&gt; &lt;/span&gt;        repr=&amp;quot;HipsTileAllskyArray(format=&amp;#39;fits&amp;#39;, order=3, width=1728, &amp;quot;
&lt;span class="gh"&gt;diff --git a/hips/utils/wcs.py b/hips/utils/wcs.py&lt;/span&gt;
&lt;span class="gh"&gt;index b052dec..5e6c240 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/wcs.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/wcs.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -98,7 +98,7 @@ class WCSGeometry:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            Sky coordinate of the WCS reference point
&lt;span class="w"&gt; &lt;/span&gt;        width, height : int
&lt;span class="w"&gt; &lt;/span&gt;            Width and height of the image in pixels
&lt;span class="gd"&gt;-        fov: str or `~astropy.coordinates.Angle`&lt;/span&gt;
&lt;span class="gi"&gt;+        fov : str or `~astropy.coordinates.Angle`&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            Field of view
&lt;span class="w"&gt; &lt;/span&gt;        coordsys : {&amp;#39;icrs&amp;#39;, &amp;#39;galactic&amp;#39;}
&lt;span class="w"&gt; &lt;/span&gt;            Coordinate system
&lt;span class="gu"&gt;@@ -160,9 +160,20 @@ class WCSGeometry:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    @classmethod
&lt;span class="w"&gt; &lt;/span&gt;    def create_from_dict(cls, params: dict) -&amp;gt; &amp;#39;WCSGeometry&amp;#39;:
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;Create WCS object from a dictionary (`WCSGeometry`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Create WCS object from a dictionary (`WCSGeometry`).&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        The extra options are passed to the ``create`` class method, it can take the following parameters:&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        * target&lt;/span&gt;
&lt;span class="gi"&gt;+        * width&lt;/span&gt;
&lt;span class="gi"&gt;+        * height&lt;/span&gt;
&lt;span class="gi"&gt;+        * fov&lt;/span&gt;
&lt;span class="gi"&gt;+        * coordsys&lt;/span&gt;
&lt;span class="gi"&gt;+        * projection&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        For detailed description, see the ``create`` class method&amp;#39;s docstring.&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        skycoord = SkyCoord.from_name(params.pop(&amp;#39;target&amp;#39;), frame=params[&amp;#39;coordsys&amp;#39;])
&lt;span class="gd"&gt;-        # print(**params)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        return cls.create(skycoord, **params)

&lt;span class="w"&gt; &lt;/span&gt;    @classmethod

commit 8140f2943de907bc6bd45f6a06d337b781dd6ff1
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Mon Aug 7 09:33:55 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Fix tile splitting criterion

&lt;span class="gh"&gt;diff --git a/hips/draw/paint.py b/hips/draw/paint.py&lt;/span&gt;
&lt;span class="gh"&gt;index efaa251..9f647a8 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/paint.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/paint.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -260,8 +260,8 @@ def is_tile_distorted(corners: tuple) -&amp;gt; bool:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    return bool(
&lt;span class="w"&gt; &lt;/span&gt;        max(edges) &amp;gt; 300 or
&lt;span class="gd"&gt;-        max(diagonals) &amp;gt; 150 or&lt;/span&gt;
&lt;span class="gd"&gt;-        diagonal_ratio &amp;lt; 0.7&lt;/span&gt;
&lt;span class="gi"&gt;+        (max(diagonals) &amp;gt; 150 and&lt;/span&gt;
&lt;span class="gi"&gt;+        diagonal_ratio &amp;lt; 0.7)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    )


&lt;span class="gh"&gt;diff --git a/hips/tiles/tests/test_allsky.py b/hips/tiles/tests/test_allsky.py&lt;/span&gt;
&lt;span class="gh"&gt;index d933938..625281a 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tests/test_allsky.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tests/test_allsky.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -11,7 +11,7 @@ TEST_CASES = [&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    dict(
&lt;span class="w"&gt; &lt;/span&gt;        label=&amp;#39;fits&amp;#39;,
&lt;span class="w"&gt; &lt;/span&gt;        meta=dict(order=3, ipix=-1, file_format=&amp;#39;fits&amp;#39;),
&lt;span class="gd"&gt;-        url=&amp;#39;http://alasky.unistra.fr/IRAC4/Norder3/Allsky.fits&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+        url=&amp;#39;https://github.com/hipspy/hips-extra/blob/master/datasets/samples/IRAC4/Norder3/Allsky.fits?raw=true&amp;#39;,&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        filename=&amp;#39;datasets/samples/IRAC4/Norder3/Allsky.fits&amp;#39;,

&lt;span class="w"&gt; &lt;/span&gt;        repr=&amp;quot;HipsTileAllskyArray(format=&amp;#39;fits&amp;#39;, order=3, width=1728, &amp;quot;
&lt;span class="gh"&gt;diff --git a/hips/utils/tests/test_wcs.py b/hips/utils/tests/test_wcs.py&lt;/span&gt;
&lt;span class="gh"&gt;index cbab50f..26cf46f 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/tests/test_wcs.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/tests/test_wcs.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,4 +1,5 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="gi"&gt;+from astropy.tests.helper import remote_data&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from numpy.testing import assert_allclose
&lt;span class="w"&gt; &lt;/span&gt;from astropy.coordinates import SkyCoord
&lt;span class="w"&gt; &lt;/span&gt;from ..wcs import WCSGeometry
&lt;span class="gu"&gt;@@ -35,6 +36,7 @@ class TestWCSGeometry:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        assert_allclose(self.geometry.wcs.wcs.crpix, [1000., 500.])
&lt;span class="w"&gt; &lt;/span&gt;        assert_allclose(self.geometry.wcs.wcs.cdelt, [-0.0015, 0.0015])

&lt;span class="gi"&gt;+    @remote_data&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    def test_create_from_dict(self):
&lt;span class="w"&gt; &lt;/span&gt;        params = dict(target=&amp;#39;crab&amp;#39;, width=2000, height=1000, fov=&amp;#39;3 deg&amp;#39;,
&lt;span class="w"&gt; &lt;/span&gt;                      coordsys=&amp;#39;galactic&amp;#39;, projection=&amp;#39;AIT&amp;#39;)
&lt;span class="gh"&gt;diff --git a/hips/utils/wcs.py b/hips/utils/wcs.py&lt;/span&gt;
&lt;span class="gh"&gt;index b052dec..f47bb6b 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/wcs.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/wcs.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -98,7 +98,7 @@ class WCSGeometry:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            Sky coordinate of the WCS reference point
&lt;span class="w"&gt; &lt;/span&gt;        width, height : int
&lt;span class="w"&gt; &lt;/span&gt;            Width and height of the image in pixels
&lt;span class="gd"&gt;-        fov: str or `~astropy.coordinates.Angle`&lt;/span&gt;
&lt;span class="gi"&gt;+        fov : str or `~astropy.coordinates.Angle`&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            Field of view
&lt;span class="w"&gt; &lt;/span&gt;        coordsys : {&amp;#39;icrs&amp;#39;, &amp;#39;galactic&amp;#39;}
&lt;span class="w"&gt; &lt;/span&gt;            Coordinate system
&lt;span class="gu"&gt;@@ -162,7 +162,6 @@ class WCSGeometry:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    def create_from_dict(cls, params: dict) -&amp;gt; &amp;#39;WCSGeometry&amp;#39;:
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Create WCS object from a dictionary (`WCSGeometry`).&amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;        skycoord = SkyCoord.from_name(params.pop(&amp;#39;target&amp;#39;), frame=params[&amp;#39;coordsys&amp;#39;])
&lt;span class="gd"&gt;-        # print(**params)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        return cls.create(skycoord, **params)

&lt;span class="w"&gt; &lt;/span&gt;    @classmethod

commit b447864b37ea070f2b8b2662ac0797cc2f151c76
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Wed Aug 2 16:36:27 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Update test case for create_from_dict classmethod

&lt;span class="gh"&gt;diff --git a/hips/utils/tests/test_wcs.py b/hips/utils/tests/test_wcs.py&lt;/span&gt;
&lt;span class="gh"&gt;index 7c72be6..cbab50f 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/tests/test_wcs.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/tests/test_wcs.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -40,5 +40,9 @@ class TestWCSGeometry:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                      coordsys=&amp;#39;galactic&amp;#39;, projection=&amp;#39;AIT&amp;#39;)
&lt;span class="w"&gt; &lt;/span&gt;        geometry = WCSGeometry.create_from_dict(params)

&lt;span class="gd"&gt;-        c = self.geometry.center_skycoord&lt;/span&gt;
&lt;span class="gi"&gt;+        c = geometry.center_skycoord&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        assert c.frame.name == &amp;#39;galactic&amp;#39;
&lt;span class="gi"&gt;+        assert_allclose(c.l.deg, 184.55, atol=1e-2)&lt;/span&gt;
&lt;span class="gi"&gt;+        assert_allclose(c.b.deg, -5.78, atol=1e-2)&lt;/span&gt;
&lt;span class="gi"&gt;+        assert_allclose(self.geometry.wcs.wcs.crpix, [1000., 500.])&lt;/span&gt;
&lt;span class="gi"&gt;+        assert_allclose(self.geometry.wcs.wcs.cdelt, [-0.0015, 0.0015])&lt;/span&gt;

commit 651a5ae14a8896ca81558d9f084a9afe764bdd44
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Wed Aug 2 13:29:26 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Add classmethod create_from_dict in WCSGeometry class

&lt;span class="gh"&gt;diff --git a/hips/draw/paint.py b/hips/draw/paint.py&lt;/span&gt;
&lt;span class="gh"&gt;index 7ea03b1..efaa251 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/paint.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/paint.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -26,7 +26,7 @@ class HipsPainter:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    Parameters
&lt;span class="w"&gt; &lt;/span&gt;    ----------
&lt;span class="gd"&gt;-    geometry : `~hips.utils.WCSGeometry`&lt;/span&gt;
&lt;span class="gi"&gt;+    geometry : dict or `~hips.utils.WCSGeometry`&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        An object of WCSGeometry
&lt;span class="w"&gt; &lt;/span&gt;    hips_survey : str or `~hips.HipsSurveyProperties`
&lt;span class="w"&gt; &lt;/span&gt;        HiPS survey properties
&lt;span class="gu"&gt;@@ -56,8 +56,8 @@ class HipsPainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    (1000, 2000)
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;

&lt;span class="gd"&gt;-    def __init__(self, geometry: WCSGeometry, hips_survey: Union[str, HipsSurveyProperties], tile_format: str, precise: bool = False) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gd"&gt;-        self.geometry = geometry&lt;/span&gt;
&lt;span class="gi"&gt;+    def __init__(self, geometry: Union[dict, WCSGeometry], hips_survey: Union[str, HipsSurveyProperties], tile_format: str, precise: bool = False) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+        self.geometry = WCSGeometry.make(geometry)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        self.hips_survey = HipsSurveyProperties.make(hips_survey)
&lt;span class="w"&gt; &lt;/span&gt;        self.tile_format = tile_format
&lt;span class="w"&gt; &lt;/span&gt;        self.precise = precise
&lt;span class="gh"&gt;diff --git a/hips/draw/ui.py b/hips/draw/ui.py&lt;/span&gt;
&lt;span class="gh"&gt;index 8a1db36..a7857c1 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/ui.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/ui.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -14,7 +14,7 @@ __all__ = [&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;]


&lt;span class="gd"&gt;-def make_sky_image(geometry: WCSGeometry, hips_survey: Union[str, &amp;#39;HipsSurveyProperties&amp;#39;],&lt;/span&gt;
&lt;span class="gi"&gt;+def make_sky_image(geometry: Union[dict, WCSGeometry], hips_survey: Union[str, &amp;#39;HipsSurveyProperties&amp;#39;],&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                   tile_format: str, precise: bool = False) -&amp;gt; &amp;#39;HipsDrawResult&amp;#39;:
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;Make sky image: fetch tiles and draw.

&lt;span class="gu"&gt;@@ -22,7 +22,7 @@ def make_sky_image(geometry: WCSGeometry, hips_survey: Union[str, &amp;#39;HipsSurveyPro&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    Parameters
&lt;span class="w"&gt; &lt;/span&gt;    ----------
&lt;span class="gd"&gt;-    geometry : `~hips.utils.WCSGeometry`&lt;/span&gt;
&lt;span class="gi"&gt;+    geometry : dict or `~hips.utils.WCSGeometry`&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        Geometry of the output image
&lt;span class="w"&gt; &lt;/span&gt;    hips_survey : str or `~hips.HipsSurveyProperties`
&lt;span class="w"&gt; &lt;/span&gt;        HiPS survey properties
&lt;span class="gh"&gt;diff --git a/hips/utils/tests/test_wcs.py b/hips/utils/tests/test_wcs.py&lt;/span&gt;
&lt;span class="gh"&gt;index 22832f7..7c72be6 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/tests/test_wcs.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/tests/test_wcs.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -34,3 +34,11 @@ class TestWCSGeometry:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        assert_allclose(c.dec.deg, 0.00075, atol=1e-2)
&lt;span class="w"&gt; &lt;/span&gt;        assert_allclose(self.geometry.wcs.wcs.crpix, [1000., 500.])
&lt;span class="w"&gt; &lt;/span&gt;        assert_allclose(self.geometry.wcs.wcs.cdelt, [-0.0015, 0.0015])
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_create_from_dict(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        params = dict(target=&amp;#39;crab&amp;#39;, width=2000, height=1000, fov=&amp;#39;3 deg&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+                      coordsys=&amp;#39;galactic&amp;#39;, projection=&amp;#39;AIT&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        geometry = WCSGeometry.create_from_dict(params)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        c = self.geometry.center_skycoord&lt;/span&gt;
&lt;span class="gi"&gt;+        assert c.frame.name == &amp;#39;galactic&amp;#39;&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/utils/wcs.py b/hips/utils/wcs.py&lt;/span&gt;
&lt;span class="gh"&gt;index bf2574e..b052dec 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/wcs.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/wcs.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -158,6 +158,23 @@ class WCSGeometry:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        return cls(w, width, height)

&lt;span class="gi"&gt;+    @classmethod&lt;/span&gt;
&lt;span class="gi"&gt;+    def create_from_dict(cls, params: dict) -&amp;gt; &amp;#39;WCSGeometry&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Create WCS object from a dictionary (`WCSGeometry`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        skycoord = SkyCoord.from_name(params.pop(&amp;#39;target&amp;#39;), frame=params[&amp;#39;coordsys&amp;#39;])&lt;/span&gt;
&lt;span class="gi"&gt;+        # print(**params)&lt;/span&gt;
&lt;span class="gi"&gt;+        return cls.create(skycoord, **params)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @classmethod&lt;/span&gt;
&lt;span class="gi"&gt;+    def make(cls, geometry: Union[dict, &amp;#39;WCSGeometry&amp;#39;]) -&amp;gt; &amp;#39;WCSGeometry&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Convenience constructor for create_from_dict classmethod or existing object (`WCSGeometry`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        if isinstance(geometry, str):&lt;/span&gt;
&lt;span class="gi"&gt;+            return WCSGeometry.create_from_dict(geometry)&lt;/span&gt;
&lt;span class="gi"&gt;+        elif isinstance(geometry, WCSGeometry):&lt;/span&gt;
&lt;span class="gi"&gt;+            return geometry&lt;/span&gt;
&lt;span class="gi"&gt;+        else:&lt;/span&gt;
&lt;span class="gi"&gt;+            raise TypeError(f&amp;#39;geometry must be of type dict or `WCSGeometry`. You gave {type(geometry)}&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    @property
&lt;span class="w"&gt; &lt;/span&gt;    def celestial_frame(self) -&amp;gt; str:
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Celestial frame for the given WCS (str).

commit 1980881927671773de80051ea78b79bdac61a484
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Tue Aug 1 16:03:48 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Introduce precise drawing algorithm

&lt;span class="gh"&gt;diff --git a/hips/draw/paint.py b/hips/draw/paint.py&lt;/span&gt;
&lt;span class="gh"&gt;index 8114b02..7ea03b1 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/paint.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/paint.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -17,8 +17,6 @@ __doctest_skip__ = [&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;]


&lt;span class="gd"&gt;-# TODO for Adeel: add option &amp;quot;precise&amp;quot;.&lt;/span&gt;
&lt;span class="gd"&gt;-# Should be default &amp;quot;true&amp;quot; eventually, for now, can use &amp;quot;false&amp;quot; if it&amp;#39;s very slow.&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;class HipsPainter:
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;Paint a sky image from HiPS image tiles.

&lt;span class="gu"&gt;@@ -34,6 +32,8 @@ class HipsPainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        HiPS survey properties
&lt;span class="w"&gt; &lt;/span&gt;    tile_format : {&amp;#39;fits&amp;#39;, &amp;#39;jpg&amp;#39;, &amp;#39;png&amp;#39;}
&lt;span class="w"&gt; &lt;/span&gt;        Format of HiPS tile
&lt;span class="gi"&gt;+    precise : bool&lt;/span&gt;
&lt;span class="gi"&gt;+        Use the precise drawing algorithm&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    Examples
&lt;span class="w"&gt; &lt;/span&gt;    --------
&lt;span class="gu"&gt;@@ -56,10 +56,11 @@ class HipsPainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    (1000, 2000)
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;

&lt;span class="gd"&gt;-    def __init__(self, geometry: WCSGeometry, hips_survey: Union[str, HipsSurveyProperties], tile_format: str) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+    def __init__(self, geometry: WCSGeometry, hips_survey: Union[str, HipsSurveyProperties], tile_format: str, precise: bool = False) -&amp;gt; None:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        self.geometry = geometry
&lt;span class="w"&gt; &lt;/span&gt;        self.hips_survey = HipsSurveyProperties.make(hips_survey)
&lt;span class="w"&gt; &lt;/span&gt;        self.tile_format = tile_format
&lt;span class="gi"&gt;+        self.precise = precise&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        self._tiles = None
&lt;span class="w"&gt; &lt;/span&gt;        self.float_image = None
&lt;span class="w"&gt; &lt;/span&gt;        self._stats: Dict[str, Any] = {}
&lt;span class="gu"&gt;@@ -161,21 +162,23 @@ class HipsPainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        # Other useful info: number of tiles fetched

&lt;span class="w"&gt; &lt;/span&gt;    def make_tile_list(self):
&lt;span class="gd"&gt;-        # Fetch all tiles needed&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        parent_tiles = self.tiles

&lt;span class="gd"&gt;-        # TODO for Adeel: implement distortion correction here.&lt;/span&gt;
&lt;span class="gd"&gt;-        # Create new list of tiles, where some have been replaced by 4 children.&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        # Suggestion: for now, just split once, no recursion.
&lt;span class="w"&gt; &lt;/span&gt;        # Leave TODO, to discuss with Thomas next week.
&lt;span class="w"&gt; &lt;/span&gt;        # See also: https://github.com/hipspy/hips/issues/92

&lt;span class="gd"&gt;-        # For now, we just create a 1:1 copy&lt;/span&gt;
&lt;span class="gd"&gt;-        tiles = []&lt;/span&gt;
&lt;span class="gd"&gt;-        for tile in parent_tiles:&lt;/span&gt;
&lt;span class="gd"&gt;-            tiles.append(tile)&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        self.draw_tiles = tiles&lt;/span&gt;
&lt;span class="gi"&gt;+        if self.precise == True:&lt;/span&gt;
&lt;span class="gi"&gt;+            tiles = []&lt;/span&gt;
&lt;span class="gi"&gt;+            for tile in parent_tiles:&lt;/span&gt;
&lt;span class="gi"&gt;+                corners = tile.meta.skycoord_corners.to_pixel(self.geometry.wcs)&lt;/span&gt;
&lt;span class="gi"&gt;+                if is_tile_distorted(corners):&lt;/span&gt;
&lt;span class="gi"&gt;+                    tiles.append(tile.children)&lt;/span&gt;
&lt;span class="gi"&gt;+                else:&lt;/span&gt;
&lt;span class="gi"&gt;+                    tiles.append(tile)&lt;/span&gt;
&lt;span class="gi"&gt;+            self.draw_tiles = [tile for children in tiles for tile in children]&lt;/span&gt;
&lt;span class="gi"&gt;+        else:&lt;/span&gt;
&lt;span class="gi"&gt;+            self.draw_tiles = parent_tiles&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def _make_empty_sky_image(self):
&lt;span class="w"&gt; &lt;/span&gt;        shape = compute_image_shape(
&lt;span class="gu"&gt;@@ -207,7 +210,8 @@ class HipsPainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        not something end-users will call or need to know about.
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;        import matplotlib.pyplot as plt
&lt;span class="gd"&gt;-        for tile in self.tiles:&lt;/span&gt;
&lt;span class="gi"&gt;+        self.make_tile_list()&lt;/span&gt;
&lt;span class="gi"&gt;+        for tile in self.draw_tiles:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            corners = tile.meta.skycoord_corners.transform_to(self.geometry.celestial_frame)
&lt;span class="w"&gt; &lt;/span&gt;            ax = plt.subplot(projection=self.geometry.wcs)
&lt;span class="w"&gt; &lt;/span&gt;            opts = dict(color=&amp;#39;red&amp;#39;, lw=1, )
&lt;span class="gh"&gt;diff --git a/hips/draw/tests/test_ui.py b/hips/draw/tests/test_ui.py&lt;/span&gt;
&lt;span class="gh"&gt;index 401874c..dc8238d 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/tests/test_ui.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/tests/test_ui.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -16,7 +16,8 @@ make_sky_image_pars = [&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        data_2=2296,
&lt;span class="w"&gt; &lt;/span&gt;        data_sum=8756493140,
&lt;span class="w"&gt; &lt;/span&gt;        dtype=&amp;#39;&amp;gt;i2&amp;#39;,
&lt;span class="gd"&gt;-        repr=&amp;#39;HipsDrawResult(width=1000, height=2000, channels=2, dtype=&amp;gt;i2, format=fits)&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        repr=&amp;#39;HipsDrawResult(width=1000, height=2000, channels=2, dtype=&amp;gt;i2, format=fits)&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+        precise=False&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    ),
&lt;span class="w"&gt; &lt;/span&gt;    dict(
&lt;span class="w"&gt; &lt;/span&gt;        file_format=&amp;#39;jpg&amp;#39;,
&lt;span class="gu"&gt;@@ -26,7 +27,19 @@ make_sky_image_pars = [&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        data_2=[137, 116, 114],
&lt;span class="w"&gt; &lt;/span&gt;        data_sum=828908873,
&lt;span class="w"&gt; &lt;/span&gt;        dtype=&amp;#39;uint8&amp;#39;,
&lt;span class="gd"&gt;-        repr=&amp;#39;HipsDrawResult(width=1000, height=2000, channels=3, dtype=uint8, format=jpg)&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        repr=&amp;#39;HipsDrawResult(width=1000, height=2000, channels=3, dtype=uint8, format=jpg)&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+        precise=False&lt;/span&gt;
&lt;span class="gi"&gt;+    ),&lt;/span&gt;
&lt;span class="gi"&gt;+    dict(&lt;/span&gt;
&lt;span class="gi"&gt;+        file_format=&amp;#39;jpg&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+        shape=(1000, 2000, 3),&lt;/span&gt;
&lt;span class="gi"&gt;+        url=&amp;#39;https://raw.githubusercontent.com/hipspy/hips-extra/master/datasets/samples/FermiColor/properties&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+        data_1=[139, 114, 115],&lt;/span&gt;
&lt;span class="gi"&gt;+        data_2=[142, 113, 110],&lt;/span&gt;
&lt;span class="gi"&gt;+        data_sum=825148172,&lt;/span&gt;
&lt;span class="gi"&gt;+        dtype=&amp;#39;uint8&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+        repr=&amp;#39;HipsDrawResult(width=1000, height=2000, channels=3, dtype=uint8, format=jpg)&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+        precise=True&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    ),
&lt;span class="w"&gt; &lt;/span&gt;    dict(
&lt;span class="w"&gt; &lt;/span&gt;        file_format=&amp;#39;png&amp;#39;,
&lt;span class="gu"&gt;@@ -36,7 +49,8 @@ make_sky_image_pars = [&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        data_2=[227, 217, 205, 255],
&lt;span class="w"&gt; &lt;/span&gt;        data_sum=1635622838,
&lt;span class="w"&gt; &lt;/span&gt;        dtype=&amp;#39;uint8&amp;#39;,
&lt;span class="gd"&gt;-        repr=&amp;#39;HipsDrawResult(width=1000, height=2000, channels=3, dtype=uint8, format=png)&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        repr=&amp;#39;HipsDrawResult(width=1000, height=2000, channels=3, dtype=uint8, format=png)&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+        precise=False&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    ),
&lt;span class="w"&gt; &lt;/span&gt;]

&lt;span class="gu"&gt;@@ -47,7 +61,7 @@ def test_make_sky_image(tmpdir, pars):&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    hips_survey = HipsSurveyProperties.fetch(url=pars[&amp;#39;url&amp;#39;])
&lt;span class="w"&gt; &lt;/span&gt;    geometry = make_test_wcs_geometry()

&lt;span class="gd"&gt;-    result = make_sky_image(geometry=geometry, hips_survey=hips_survey, tile_format=pars[&amp;#39;file_format&amp;#39;])&lt;/span&gt;
&lt;span class="gi"&gt;+    result = make_sky_image(geometry=geometry, hips_survey=hips_survey, tile_format=pars[&amp;#39;file_format&amp;#39;], precise=pars[&amp;#39;precise&amp;#39;])&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    assert result.image.shape == pars[&amp;#39;shape&amp;#39;]
&lt;span class="w"&gt; &lt;/span&gt;    assert result.image.dtype == pars[&amp;#39;dtype&amp;#39;]
&lt;span class="gh"&gt;diff --git a/hips/draw/ui.py b/hips/draw/ui.py&lt;/span&gt;
&lt;span class="gh"&gt;index 11facac..8a1db36 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/ui.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/ui.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -15,7 +15,7 @@ __all__ = [&lt;/span&gt;


&lt;span class="w"&gt; &lt;/span&gt;def make_sky_image(geometry: WCSGeometry, hips_survey: Union[str, &amp;#39;HipsSurveyProperties&amp;#39;],
&lt;span class="gd"&gt;-                   tile_format: str) -&amp;gt; &amp;#39;HipsDrawResult&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+                   tile_format: str, precise: bool = False) -&amp;gt; &amp;#39;HipsDrawResult&amp;#39;:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;Make sky image: fetch tiles and draw.

&lt;span class="w"&gt; &lt;/span&gt;    The example for this can be found on the :ref:`gs` page.
&lt;span class="gu"&gt;@@ -29,13 +29,15 @@ def make_sky_image(geometry: WCSGeometry, hips_survey: Union[str, &amp;#39;HipsSurveyPro&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    tile_format : {&amp;#39;fits&amp;#39;, &amp;#39;jpg&amp;#39;, &amp;#39;png&amp;#39;}
&lt;span class="w"&gt; &lt;/span&gt;        Format of HiPS tile to use
&lt;span class="w"&gt; &lt;/span&gt;        (some surveys are available in several formats, so this extra argument is needed)
&lt;span class="gi"&gt;+    precise : bool&lt;/span&gt;
&lt;span class="gi"&gt;+        Use the precise drawing algorithm&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    Returns
&lt;span class="w"&gt; &lt;/span&gt;    -------
&lt;span class="w"&gt; &lt;/span&gt;    result : `~hips.HipsDrawResult`
&lt;span class="w"&gt; &lt;/span&gt;        Result object
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-    painter = HipsPainter(geometry, hips_survey, tile_format)&lt;/span&gt;
&lt;span class="gi"&gt;+    painter = HipsPainter(geometry, hips_survey, tile_format, precise)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    painter.run()
&lt;span class="w"&gt; &lt;/span&gt;    return HipsDrawResult.from_painter(painter)

&lt;span class="gh"&gt;diff --git a/hips/tiles/tests/test_tile.py b/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gh"&gt;index eeef134..c2712f7 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -82,7 +82,7 @@ HIPS_TILE_TEST_CASES = [&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        child_shape=(256, 256),
&lt;span class="w"&gt; &lt;/span&gt;        child_ipix=[1852, 1853, 1854, 1855],
&lt;span class="w"&gt; &lt;/span&gt;        child_pix_idx=[[255], [255]],
&lt;span class="gd"&gt;-        child_pix_val=[2153, 2418, 2437, 2124],&lt;/span&gt;
&lt;span class="gi"&gt;+        child_pix_val=[2437, 2153, 2124, 2418],&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    ),
&lt;span class="w"&gt; &lt;/span&gt;    dict(
&lt;span class="w"&gt; &lt;/span&gt;        label=&amp;#39;jpg&amp;#39;,
&lt;span class="gu"&gt;@@ -99,7 +99,7 @@ HIPS_TILE_TEST_CASES = [&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        child_shape=(256, 256, 3),
&lt;span class="w"&gt; &lt;/span&gt;        child_ipix=[1852, 1853, 1854, 1855],
&lt;span class="w"&gt; &lt;/span&gt;        child_pix_idx=[[255], [255]],
&lt;span class="gd"&gt;-        child_pix_val=[[[245, 214, 211]], [[137, 97, 87]], [[255, 241, 225]], [[109, 95, 86]]],&lt;/span&gt;
&lt;span class="gi"&gt;+        child_pix_val=[[[255, 241, 225]], [[245, 214, 211]], [[109, 95, 86]], [[137, 97, 87]]],&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    ),
&lt;span class="w"&gt; &lt;/span&gt;    dict(
&lt;span class="w"&gt; &lt;/span&gt;        label=&amp;#39;png&amp;#39;,
&lt;span class="gu"&gt;@@ -116,7 +116,7 @@ HIPS_TILE_TEST_CASES = [&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        child_shape=(256, 256, 4),
&lt;span class="w"&gt; &lt;/span&gt;        child_ipix=[24448, 24449, 24450, 24451],
&lt;span class="w"&gt; &lt;/span&gt;        child_pix_idx=[[255], [255]],
&lt;span class="gd"&gt;-        child_pix_val=[[[17, 17, 17, 255]], [[13, 13, 13, 255]], [[15, 15, 15, 255]], [[20, 20, 20, 255]]],&lt;/span&gt;
&lt;span class="gi"&gt;+        child_pix_val=[[[15,  15,  15, 255]], [[17, 17, 17, 255]], [[20, 20, 20, 255]], [[13, 13, 13, 255]]],&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    ),
&lt;span class="w"&gt; &lt;/span&gt;]

&lt;span class="gh"&gt;diff --git a/hips/tiles/tile.py b/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gh"&gt;index eebaeb7..88ae6ca 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -231,10 +231,10 @@ class HipsTile:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Create four children tiles from parent tile.&amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;        w = self.data.shape[0] // 2
&lt;span class="w"&gt; &lt;/span&gt;        data = [
&lt;span class="gd"&gt;-            self.data[0: w, 0: w],&lt;/span&gt;
&lt;span class="gd"&gt;-            self.data[0: w, w: w * 2],&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            self.data[w: w * 2, 0: w],
&lt;span class="gd"&gt;-            self.data[w: w * 2, w: w * 2]&lt;/span&gt;
&lt;span class="gi"&gt;+            self.data[0: w, 0: w],&lt;/span&gt;
&lt;span class="gi"&gt;+            self.data[w: w * 2, w: w * 2],&lt;/span&gt;
&lt;span class="gi"&gt;+            self.data[0: w, w: w * 2]&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        ]

&lt;span class="w"&gt; &lt;/span&gt;        tiles = []
&lt;span class="gu"&gt;@@ -243,7 +243,8 @@ class HipsTile:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                self.meta.order + 1,
&lt;span class="w"&gt; &lt;/span&gt;                self.meta.ipix * 4 + idx,
&lt;span class="w"&gt; &lt;/span&gt;                self.meta.file_format,
&lt;span class="gd"&gt;-                self.meta.frame&lt;/span&gt;
&lt;span class="gi"&gt;+                self.meta.frame,&lt;/span&gt;
&lt;span class="gi"&gt;+                len(data[0])&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            )
&lt;span class="w"&gt; &lt;/span&gt;            tile = self.from_numpy(meta, data[idx])
&lt;span class="w"&gt; &lt;/span&gt;            tiles.append(tile)

commit 09906ec66668c829a9ca0375840e77090d50de2a
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Thu Jul 27 18:19:06 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Add option to create HipsSurveyProperties from survey name

&lt;span class="gh"&gt;diff --git a/docs/getting_started.rst b/docs/getting_started.rst&lt;/span&gt;
&lt;span class="gh"&gt;index 88cdf06..c308ea1 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/docs/getting_started.rst&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/docs/getting_started.rst&lt;/span&gt;
&lt;span class="gu"&gt;@@ -26,21 +26,16 @@ To draw a sky image from HiPS image tiles with the `hips` package, follow the fo&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;         coordsys=&amp;#39;galactic&amp;#39;, projection=&amp;#39;AIT&amp;#39;,
&lt;span class="w"&gt; &lt;/span&gt;    )

&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-2. Specify the HiPS survey you want by creating a `~hips.HipsSurveyProperties` object.&lt;/span&gt;
&lt;span class="gi"&gt;+2. Specify the HiPS survey you want. You just need to provide a valid HiPS survey ID.&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;   A good address that lists available HiPS data is http://aladin.u-strasbg.fr/hips/list ::

&lt;span class="gd"&gt;-    from hips import HipsSurveyProperties&lt;/span&gt;
&lt;span class="gd"&gt;-    url = &amp;#39;http://alasky.unistra.fr/DSS/DSS2Merged/properties&amp;#39;&lt;/span&gt;
&lt;span class="gd"&gt;-    hips_survey = HipsSurveyProperties.fetch(url)&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gi"&gt;+    hips_survey = &amp;#39;CDS/P/DSS2/red&amp;#39;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;3. Call the `~hips.make_sky_image` function to fetch the HiPS data
&lt;span class="w"&gt; &lt;/span&gt;   and draw it, returning an object of `~hips.HipsDrawResult`::

&lt;span class="w"&gt; &lt;/span&gt;    from hips import make_sky_image
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    result = make_sky_image(geometry, hips_survey, &amp;#39;fits&amp;#39;)

&lt;span class="w"&gt; &lt;/span&gt;Of course, you could change the parameters to chose any sky image geometry and
&lt;span class="gh"&gt;diff --git a/hips/draw/simple.py b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index 5f06e9d..a8fe05f 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -4,7 +4,7 @@ import time&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;import numpy as np
&lt;span class="w"&gt; &lt;/span&gt;from PIL import Image
&lt;span class="w"&gt; &lt;/span&gt;from astropy.io import fits
&lt;span class="gd"&gt;-from typing import List, Tuple&lt;/span&gt;
&lt;span class="gi"&gt;+from typing import List, Tuple, Union&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from astropy.wcs.utils import proj_plane_pixel_scales
&lt;span class="w"&gt; &lt;/span&gt;from skimage.transform import ProjectiveTransform, warp
&lt;span class="w"&gt; &lt;/span&gt;from ..tiles import HipsSurveyProperties, HipsTile, HipsTileMeta
&lt;span class="gu"&gt;@@ -61,7 +61,7 @@ class HipsPainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    (1000, 2000)
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;

&lt;span class="gd"&gt;-    def __init__(self, geometry: WCSGeometry, hips_survey: HipsSurveyProperties, tile_format: str) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+    def __init__(self, geometry: WCSGeometry, hips_survey: Union[str, HipsSurveyProperties], tile_format: str) -&amp;gt; None:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        self.geometry = geometry
&lt;span class="w"&gt; &lt;/span&gt;        self.hips_survey = HipsSurveyProperties.make(hips_survey)
&lt;span class="w"&gt; &lt;/span&gt;        self.tile_format = tile_format
&lt;span class="gu"&gt;@@ -382,7 +382,7 @@ def plot_mpl_single_tile(geometry: WCSGeometry, tile: HipsTile, image: np.ndarra&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    ax.imshow(image, origin=&amp;#39;lower&amp;#39;)


&lt;span class="gd"&gt;-def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties, tile_format: str) -&amp;gt; &amp;#39;HipsDrawResult&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+def make_sky_image(geometry: WCSGeometry, hips_survey: Union[str, &amp;#39;HipsSurveyProperties&amp;#39;], tile_format: str) -&amp;gt; &amp;#39;HipsDrawResult&amp;#39;:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;Make sky image: fetch tiles and draw.

&lt;span class="w"&gt; &lt;/span&gt;    The example for this can be found on the :ref:`gs` page.
&lt;span class="gu"&gt;@@ -399,8 +399,8 @@ def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties, til&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    Returns
&lt;span class="w"&gt; &lt;/span&gt;    -------
&lt;span class="gd"&gt;-    image : `~numpy.ndarray`&lt;/span&gt;
&lt;span class="gd"&gt;-        Output image pixels&lt;/span&gt;
&lt;span class="gi"&gt;+    result : `~hips.HipsDrawResult`&lt;/span&gt;
&lt;span class="gi"&gt;+        Result object&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;    painter = HipsPainter(geometry, hips_survey, tile_format)
&lt;span class="w"&gt; &lt;/span&gt;    painter.run()
&lt;span class="gh"&gt;diff --git a/hips/tiles/surveys.py b/hips/tiles/surveys.py&lt;/span&gt;
&lt;span class="gh"&gt;index 614ce1f..41165f4 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/surveys.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/surveys.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -3,7 +3,7 @@ from collections import OrderedDict&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from io import StringIO
&lt;span class="w"&gt; &lt;/span&gt;from csv import DictWriter
&lt;span class="w"&gt; &lt;/span&gt;import urllib.request
&lt;span class="gd"&gt;-from typing import List&lt;/span&gt;
&lt;span class="gi"&gt;+from typing import List, Union&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from astropy.table import Table
&lt;span class="w"&gt; &lt;/span&gt;from .tile import HipsTileMeta

&lt;span class="gu"&gt;@@ -44,33 +44,22 @@ class HipsSurveyProperties:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    def __init__(self, data: OrderedDict) -&amp;gt; None:
&lt;span class="w"&gt; &lt;/span&gt;        self.data = data

&lt;span class="gd"&gt;-    # TODO for Adeel: implement this (add docstring &amp;amp; test)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    @classmethod
&lt;span class="gd"&gt;-    def from_name(cls, name):&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+    def from_name(cls, name: str) -&amp;gt; &amp;#39;HipsSurveyProperties&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Create object from Survey ID (`HipsSurveyProperties`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        # TODO: implement some kind of caching for HipsSurveyPropertiesList
&lt;span class="gd"&gt;-        # Also update the getting started example to use this simple solution.&lt;/span&gt;
&lt;span class="gd"&gt;-        # Discuss with Thomas how to do it.&lt;/span&gt;
&lt;span class="gd"&gt;-        # See https://github.com/hipspy/hips/issues/81&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        surveys = HipsSurveyPropertiesList.fetch()
&lt;span class="gd"&gt;-        for survey in surveys.data:&lt;/span&gt;
&lt;span class="gd"&gt;-            if survey.data[&amp;#39;ID&amp;#39;].strip() == name.strip():&lt;/span&gt;
&lt;span class="gd"&gt;-                return survey&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        raise KeyError(f&amp;#39;Survey not found: {name}&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        return surveys.from_name(name)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    @classmethod
&lt;span class="gd"&gt;-    def make(cls, hips_survey):&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;Convenience constructor from string or existing object.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-        return hips_survey&lt;/span&gt;
&lt;span class="gd"&gt;-        # TODO for Adeel: Implement the `HipsSurveyProperties.from_name` and add a test for this lookup by name&lt;/span&gt;
&lt;span class="gd"&gt;-        # if isinstance(hips_survey, str):&lt;/span&gt;
&lt;span class="gd"&gt;-        #     hips_survey = HipsSurveyProperties.from_name(hips_survey)&lt;/span&gt;
&lt;span class="gd"&gt;-        # elif isinstance(hips_survey, HipsSurveyProperties):&lt;/span&gt;
&lt;span class="gd"&gt;-        #     pass&lt;/span&gt;
&lt;span class="gd"&gt;-        # else:&lt;/span&gt;
&lt;span class="gd"&gt;-        #     raise TypeError(f&amp;#39;hips_survey must be str or HipsSurveyProperties. You gave {type(hips_survey)}&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+    def make(cls, hips_survey: Union[str, &amp;#39;HipsSurveyProperties&amp;#39;]) -&amp;gt; &amp;#39;HipsSurveyProperties&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Convenience constructor for from_string classmethod or existing object (`HipsSurveyProperties`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        if isinstance(hips_survey, str):&lt;/span&gt;
&lt;span class="gi"&gt;+            return HipsSurveyProperties.from_name(hips_survey)&lt;/span&gt;
&lt;span class="gi"&gt;+        elif isinstance(hips_survey, HipsSurveyProperties):&lt;/span&gt;
&lt;span class="gi"&gt;+            return hips_survey&lt;/span&gt;
&lt;span class="gi"&gt;+        else:&lt;/span&gt;
&lt;span class="gi"&gt;+            raise TypeError(f&amp;#39;hips_survey must be of type str or `HipsSurveyProperties`. You gave {type(hips_survey)}&amp;#39;)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    @classmethod
&lt;span class="w"&gt; &lt;/span&gt;    def read(cls, filename: str) -&amp;gt; &amp;#39;HipsSurveyProperties&amp;#39;:
&lt;span class="gu"&gt;@@ -294,3 +283,11 @@ class HipsSurveyPropertiesList:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        writer.writeheader()
&lt;span class="w"&gt; &lt;/span&gt;        writer.writerows(rows)
&lt;span class="w"&gt; &lt;/span&gt;        return Table.read(buffer.getvalue(), format=&amp;#39;ascii.csv&amp;#39;, guess=False)
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def from_name(self, name: str) -&amp;gt; &amp;#39;HipsSurveyProperties&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Return a matching HiPS survey (`HipsSurveyProperties`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        for survey in self.data:&lt;/span&gt;
&lt;span class="gi"&gt;+            if survey.data[&amp;#39;ID&amp;#39;].strip() == name.strip():&lt;/span&gt;
&lt;span class="gi"&gt;+                return survey&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        raise KeyError(f&amp;#39;Survey not found: {name}&amp;#39;)&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/tiles/tests/test_surveys.py b/hips/tiles/tests/test_surveys.py&lt;/span&gt;
&lt;span class="gh"&gt;index b6d6a94..e8fb0be 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tests/test_surveys.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tests/test_surveys.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,4 +1,5 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="gi"&gt;+import pytest&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from numpy.testing import assert_allclose
&lt;span class="w"&gt; &lt;/span&gt;from astropy.utils.data import get_pkg_data_filename
&lt;span class="w"&gt; &lt;/span&gt;from astropy.tests.helper import remote_data
&lt;span class="gu"&gt;@@ -11,33 +12,44 @@ class TestHipsSurveyProperties:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    @classmethod
&lt;span class="w"&gt; &lt;/span&gt;    def setup_class(cls):
&lt;span class="w"&gt; &lt;/span&gt;        filename = get_pkg_data_filename(&amp;#39;data/properties.txt&amp;#39;)
&lt;span class="gd"&gt;-        cls.hips_survey = HipsSurveyProperties.read(filename)&lt;/span&gt;
&lt;span class="gi"&gt;+        cls.survey = HipsSurveyProperties.read(filename)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @remote_data&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_from_name(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        survey = HipsSurveyProperties.from_name(&amp;#39;CDS/P/2MASS/color&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        assert survey.title == &amp;#39;2MASS color J (1.23 microns), H (1.66 microns), K (2.16 microns)&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @remote_data&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_make(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        survey = HipsSurveyProperties.make(&amp;#39;CDS/P/EGRET/Dif/300-500&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        assert survey.title == &amp;#39;EGRET Dif 300-500&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        assert self.survey is HipsSurveyProperties.make(self.survey)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def test_title(self):
&lt;span class="gd"&gt;-        assert self.hips_survey.title == &amp;#39;DSS colored&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        assert self.survey.title == &amp;#39;DSS colored&amp;#39;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def test_hips_version(self):
&lt;span class="gd"&gt;-        assert self.hips_survey.hips_version == &amp;#39;1.31&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        assert self.survey.hips_version == &amp;#39;1.31&amp;#39;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def test_hips_frame(self):
&lt;span class="gd"&gt;-        assert self.hips_survey.hips_frame == &amp;#39;equatorial&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        assert self.survey.hips_frame == &amp;#39;equatorial&amp;#39;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def test_astropy_frame(self):
&lt;span class="gd"&gt;-        assert self.hips_survey.astropy_frame == &amp;#39;icrs&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        assert self.survey.astropy_frame == &amp;#39;icrs&amp;#39;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def test_hips_order(self):
&lt;span class="gd"&gt;-        assert self.hips_survey.hips_order == 9&lt;/span&gt;
&lt;span class="gi"&gt;+        assert self.survey.hips_order == 9&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def test_tile_format(self):
&lt;span class="gd"&gt;-        assert self.hips_survey.tile_format == &amp;#39;jpeg&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        assert self.survey.tile_format == &amp;#39;jpeg&amp;#39;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def test_base_url(self):
&lt;span class="w"&gt; &lt;/span&gt;        expected = &amp;#39;http://alasky.u-strasbg.fr/DSS/DSSColor&amp;#39;
&lt;span class="gd"&gt;-        assert self.hips_survey.base_url == expected&lt;/span&gt;
&lt;span class="gi"&gt;+        assert self.survey.base_url == expected&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def test_tile_default_url(self):
&lt;span class="w"&gt; &lt;/span&gt;        tile_meta = HipsTileMeta(order=9, ipix=54321, file_format=&amp;#39;fits&amp;#39;)
&lt;span class="gd"&gt;-        url = self.hips_survey.tile_url(tile_meta)&lt;/span&gt;
&lt;span class="gi"&gt;+        url = self.survey.tile_url(tile_meta)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        assert url == &amp;#39;http://alasky.u-strasbg.fr/DSS/DSSColor/Norder9/Dir50000/Npix54321.fits&amp;#39;

&lt;span class="w"&gt; &lt;/span&gt;    @staticmethod
&lt;span class="gu"&gt;@@ -85,8 +97,12 @@ class TestHipsSurveyPropertiesList:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        surveys = HipsSurveyPropertiesList.fetch()
&lt;span class="w"&gt; &lt;/span&gt;        assert len(surveys.data) &amp;gt; 3

&lt;span class="gd"&gt;-        # TODO: look up survey by name here&lt;/span&gt;
&lt;span class="gd"&gt;-        # Otherwise this will break when the list changes&lt;/span&gt;
&lt;span class="gd"&gt;-        survey = surveys.data[0]&lt;/span&gt;
&lt;span class="gi"&gt;+        survey = surveys.from_name(&amp;#39;CDS/P/2MASS/H&amp;#39;)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        assert survey.title == &amp;#39;2MASS H (1.66 microns)&amp;#39;
&lt;span class="w"&gt; &lt;/span&gt;        assert survey.hips_order == 9
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @remote_data&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_key_error(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        with pytest.raises(KeyError):&lt;/span&gt;
&lt;span class="gi"&gt;+            surveys = HipsSurveyPropertiesList.fetch()&lt;/span&gt;
&lt;span class="gi"&gt;+            surveys.from_name(&amp;#39;Kronka Lonka&amp;#39;)&lt;/span&gt;

commit 821f335f3f6c5ba912bcfdbf2ea55e1d51e6be82
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Tue Jul 25 19:30:10 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Update high-level docs example

&lt;span class="gh"&gt;diff --git a/docs/getting_started.rst b/docs/getting_started.rst&lt;/span&gt;
&lt;span class="gh"&gt;index ac958e1..0771e95 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/docs/getting_started.rst&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/docs/getting_started.rst&lt;/span&gt;
&lt;span class="gu"&gt;@@ -37,22 +37,34 @@ To make a sky image with the `hips` package, follow the following three steps:&lt;/span&gt;


&lt;span class="w"&gt; &lt;/span&gt;3. Call the `~hips.make_sky_image` function to fetch the HiPS data
&lt;span class="gd"&gt;-   and draw it, returning the sky image pixel data as a Numpy array::&lt;/span&gt;
&lt;span class="gi"&gt;+   and draw it, returning an object of `~hips.HipsDrawResult`::&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    from hips import make_sky_image

&lt;span class="gd"&gt;-    data = make_sky_image(geometry, hips_survey, &amp;#39;fits&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+    result = make_sky_image(geometry, hips_survey, &amp;#39;fits&amp;#39;)&lt;/span&gt;


&lt;span class="w"&gt; &lt;/span&gt;That&amp;#39;s it. Go ahead and try it out for your favourite sky region and survey.

&lt;span class="w"&gt; &lt;/span&gt;Now you can then save the sky image to local disk e.g. FITS file format::

&lt;span class="gd"&gt;-    from astropy.io import fits&lt;/span&gt;
&lt;span class="gd"&gt;-    hdu = fits.PrimaryHDU(data=data, header=geometry.fits_header)&lt;/span&gt;
&lt;span class="gd"&gt;-    hdu.writeto(&amp;#39;my_image.fits&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+    result.write_image(&amp;#39;my_image.fits&amp;#39;)&lt;/span&gt;

&lt;span class="gd"&gt;-or plot and analyse the sky image however you like.&lt;/span&gt;
&lt;span class="gi"&gt;+The ``result`` object also contains other useful information, such as::&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    result.image&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+will return a NumPy array containing pixel data, you can also get the WCS information using::&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    result.geometry&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+If you want, you could also print out information about the ``result``::&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    print(result)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+or plot and analyse the sky image using::&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    result.plot()&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;If you execute the example above, you will get this sky image which was plotted using `astropy.visualization.wcsaxes`

&lt;span class="gh"&gt;diff --git a/docs/plot_fits.py b/docs/plot_fits.py&lt;/span&gt;
&lt;span class="gh"&gt;index 14bcb18..bc1f860 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/docs/plot_fits.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/docs/plot_fits.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -10,11 +10,11 @@ geometry = WCSGeometry.create(&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    width=2000, height=1000, fov=&amp;quot;3 deg&amp;quot;,
&lt;span class="w"&gt; &lt;/span&gt;    coordsys=&amp;#39;galactic&amp;#39;, projection=&amp;#39;AIT&amp;#39;,
&lt;span class="w"&gt; &lt;/span&gt;)
&lt;span class="gd"&gt;-image = make_sky_image(geometry=geometry, hips_survey=hips_survey, tile_format=&amp;#39;fits&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+result = make_sky_image(geometry=geometry, hips_survey=hips_survey, tile_format=&amp;#39;fits&amp;#39;)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;# Draw the sky image
&lt;span class="w"&gt; &lt;/span&gt;import matplotlib.pyplot as plt
&lt;span class="w"&gt; &lt;/span&gt;from astropy.visualization.mpl_normalize import simple_norm
&lt;span class="w"&gt; &lt;/span&gt;ax = plt.subplot(projection=geometry.wcs)
&lt;span class="gd"&gt;-norm = simple_norm(image, &amp;#39;sqrt&amp;#39;, min_percent=1, max_percent=99)&lt;/span&gt;
&lt;span class="gd"&gt;-ax.imshow(image, origin=&amp;#39;lower&amp;#39;, norm=norm, cmap=&amp;#39;gray&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+norm = simple_norm(result.image, &amp;#39;sqrt&amp;#39;, min_percent=1, max_percent=99)&lt;/span&gt;
&lt;span class="gi"&gt;+ax.imshow(result.image, origin=&amp;#39;lower&amp;#39;, norm=norm, cmap=&amp;#39;gray&amp;#39;)&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/docs/plot_jpg.py b/docs/plot_jpg.py&lt;/span&gt;
&lt;span class="gh"&gt;index ba3b852..1eb111b 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/docs/plot_jpg.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/docs/plot_jpg.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -10,9 +10,9 @@ geometry = WCSGeometry.create(&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    width=2000, height=1000, fov=&amp;quot;3 deg&amp;quot;,
&lt;span class="w"&gt; &lt;/span&gt;    coordsys=&amp;#39;galactic&amp;#39;, projection=&amp;#39;AIT&amp;#39;,
&lt;span class="w"&gt; &lt;/span&gt;)
&lt;span class="gd"&gt;-image = make_sky_image(geometry=geometry, hips_survey=hips_survey, tile_format=&amp;#39;jpg&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+result = make_sky_image(geometry=geometry, hips_survey=hips_survey, tile_format=&amp;#39;jpg&amp;#39;)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;# Draw the sky image
&lt;span class="w"&gt; &lt;/span&gt;import matplotlib.pyplot as plt
&lt;span class="w"&gt; &lt;/span&gt;ax = plt.subplot(projection=geometry.wcs)
&lt;span class="gd"&gt;-ax.imshow(image, origin=&amp;#39;lower&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+ax.imshow(result.image, origin=&amp;#39;lower&amp;#39;)&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/draw/simple.py b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index 2539df3..a6dc4d7 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -129,7 +129,8 @@ class SimpleTilePainter:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    @property
&lt;span class="w"&gt; &lt;/span&gt;    def result(self) -&amp;gt; &amp;#39;HipsDrawResult&amp;#39;:
&lt;span class="gd"&gt;-        return HipsDrawResult(self.image, self.geometry, self.tile_format)&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Return an object of `~hips.HipsDrawResult` class.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        return HipsDrawResult(self.image, self.geometry, self.tile_format, self.tiles)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def warp_image(self, tile: HipsTile) -&amp;gt; np.ndarray:
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Warp a HiPS tile and a sky image.&amp;quot;&amp;quot;&amp;quot;
&lt;span class="gu"&gt;@@ -189,25 +190,31 @@ class HipsDrawResult:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        An object of WCSGeometry
&lt;span class="w"&gt; &lt;/span&gt;    tile_format : {&amp;#39;fits&amp;#39;, &amp;#39;jpg&amp;#39;, &amp;#39;png&amp;#39;}
&lt;span class="w"&gt; &lt;/span&gt;        Format of HiPS tile
&lt;span class="gi"&gt;+    tiles: List[HipsTile]&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;

&lt;span class="gd"&gt;-    def __init__(self, image: np.ndarray, geometry: WCSGeometry, tile_format: str) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+    def __init__(self, image: np.ndarray, geometry: WCSGeometry, tile_format: str, tiles: List[HipsTile]) -&amp;gt; None:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        self.image = image
&lt;span class="w"&gt; &lt;/span&gt;        self.geometry = geometry
&lt;span class="w"&gt; &lt;/span&gt;        self.tile_format = tile_format
&lt;span class="gi"&gt;+        self.tiles = tiles&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def __str__(self):
&lt;span class="w"&gt; &lt;/span&gt;        return (
&lt;span class="gd"&gt;-            &amp;#39;This class object contains two attributes: image and geometry&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+            &amp;#39;HiPS draw result:\n&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+            f&amp;#39;Sky image: shape={self.image.shape}, dtype={self.image.dtype}\n&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+            f&amp;#39;WCS geometry: {self.geometry}\n&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        )

&lt;span class="w"&gt; &lt;/span&gt;    def __repr__(self):
&lt;span class="w"&gt; &lt;/span&gt;        return (
&lt;span class="gi"&gt;+            &amp;#39;HipsDrawResult(&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            f&amp;#39;width={self.image.shape[0]}, &amp;#39;
&lt;span class="w"&gt; &lt;/span&gt;            f&amp;#39;height={self.image.shape[1]}, &amp;#39;
&lt;span class="w"&gt; &lt;/span&gt;            f&amp;#39;channels={self.image.ndim}, &amp;#39;
&lt;span class="w"&gt; &lt;/span&gt;            f&amp;#39;dtype={self.image.dtype}, &amp;#39;
&lt;span class="w"&gt; &lt;/span&gt;            f&amp;#39;format={self.tile_format}&amp;#39;
&lt;span class="gi"&gt;+            &amp;#39;)&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        )

&lt;span class="w"&gt; &lt;/span&gt;    def write_image(self, filename: str) -&amp;gt; None:
&lt;span class="gu"&gt;@@ -219,12 +226,24 @@ class HipsDrawResult:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            Filename
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;        if self.tile_format == &amp;#39;fits&amp;#39;:
&lt;span class="gd"&gt;-            hdu = fits.PrimaryHDU(self.image)&lt;/span&gt;
&lt;span class="gi"&gt;+            hdu = fits.PrimaryHDU(data=self.image, header=self.geometry.fits_header)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            hdu.writeto(filename)
&lt;span class="w"&gt; &lt;/span&gt;        else:
&lt;span class="w"&gt; &lt;/span&gt;            image = Image.fromarray(self.image)
&lt;span class="w"&gt; &lt;/span&gt;            image.save(filename)

&lt;span class="gi"&gt;+    def plot(self) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Plot the all sky image using `astropy.visualization.wcsaxes` and showing the HEALPix grid.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        import matplotlib.pyplot as plt&lt;/span&gt;
&lt;span class="gi"&gt;+        for tile in self.tiles:&lt;/span&gt;
&lt;span class="gi"&gt;+            corners = tile.meta.skycoord_corners.transform_to(self.geometry.celestial_frame)&lt;/span&gt;
&lt;span class="gi"&gt;+            ax = plt.subplot(projection=self.geometry.wcs)&lt;/span&gt;
&lt;span class="gi"&gt;+            opts = dict(color=&amp;#39;red&amp;#39;, lw=1, )&lt;/span&gt;
&lt;span class="gi"&gt;+            ax.plot(corners.data.lon.deg, corners.data.lat.deg,&lt;/span&gt;
&lt;span class="gi"&gt;+                    transform=ax.get_transform(&amp;#39;world&amp;#39;), **opts)&lt;/span&gt;
&lt;span class="gi"&gt;+        ax.imshow(self.image, origin=&amp;#39;lower&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;def measure_tile_shape(corners: tuple) -&amp;gt; Tuple[List[float]]:
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;Compute length of tile edges and diagonals.&amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;    x, y = corners
&lt;span class="gh"&gt;diff --git a/hips/draw/tests/test_simple.py b/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index d038f43..4287862 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -19,7 +19,7 @@ make_sky_image_pars = [&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        data_2=2296,
&lt;span class="w"&gt; &lt;/span&gt;        data_sum=8756493140,
&lt;span class="w"&gt; &lt;/span&gt;        dtype=&amp;#39;&amp;gt;i2&amp;#39;,
&lt;span class="gd"&gt;-        repr=&amp;#39;width=1000, height=2000, channels=2, dtype=&amp;gt;i2, format=fits&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        repr=&amp;#39;HipsDrawResult(width=1000, height=2000, channels=2, dtype=&amp;gt;i2, format=fits)&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    ),
&lt;span class="w"&gt; &lt;/span&gt;    dict(
&lt;span class="w"&gt; &lt;/span&gt;        file_format=&amp;#39;jpg&amp;#39;,
&lt;span class="gu"&gt;@@ -29,7 +29,7 @@ make_sky_image_pars = [&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        data_2=[137, 116, 114],
&lt;span class="w"&gt; &lt;/span&gt;        data_sum=828908873,
&lt;span class="w"&gt; &lt;/span&gt;        dtype=&amp;#39;uint8&amp;#39;,
&lt;span class="gd"&gt;-        repr=&amp;#39;width=1000, height=2000, channels=3, dtype=uint8, format=jpg&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        repr=&amp;#39;HipsDrawResult(width=1000, height=2000, channels=3, dtype=uint8, format=jpg)&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    ),
&lt;span class="w"&gt; &lt;/span&gt;    dict(
&lt;span class="w"&gt; &lt;/span&gt;        file_format=&amp;#39;png&amp;#39;,
&lt;span class="gu"&gt;@@ -39,24 +39,25 @@ make_sky_image_pars = [&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        data_2=[227, 217, 205, 255],
&lt;span class="w"&gt; &lt;/span&gt;        data_sum=1635622838,
&lt;span class="w"&gt; &lt;/span&gt;        dtype=&amp;#39;uint8&amp;#39;,
&lt;span class="gd"&gt;-        repr=&amp;#39;width=1000, height=2000, channels=3, dtype=uint8, format=png&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        repr=&amp;#39;HipsDrawResult(width=1000, height=2000, channels=3, dtype=uint8, format=png)&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    ),
&lt;span class="w"&gt; &lt;/span&gt;]


&lt;span class="w"&gt; &lt;/span&gt;@remote_data
&lt;span class="w"&gt; &lt;/span&gt;@pytest.mark.parametrize(&amp;#39;pars&amp;#39;, make_sky_image_pars)
&lt;span class="gd"&gt;-def test_make_sky_image(pars):&lt;/span&gt;
&lt;span class="gi"&gt;+def test_make_sky_image(tmpdir, pars):&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    hips_survey = HipsSurveyProperties.fetch(url=pars[&amp;#39;url&amp;#39;])
&lt;span class="w"&gt; &lt;/span&gt;    geometry = make_test_wcs_geometry()
&lt;span class="w"&gt; &lt;/span&gt;    result = make_sky_image(geometry=geometry, hips_survey=hips_survey, tile_format=pars[&amp;#39;file_format&amp;#39;])
&lt;span class="w"&gt; &lt;/span&gt;    assert result.image.shape == pars[&amp;#39;shape&amp;#39;]
&lt;span class="w"&gt; &lt;/span&gt;    assert result.image.dtype == pars[&amp;#39;dtype&amp;#39;]
&lt;span class="gi"&gt;+    assert repr(result) == pars[&amp;#39;repr&amp;#39;]&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    assert_allclose(np.sum(result.image), pars[&amp;#39;data_sum&amp;#39;])
&lt;span class="w"&gt; &lt;/span&gt;    assert_allclose(result.image[200, 994], pars[&amp;#39;data_1&amp;#39;])
&lt;span class="w"&gt; &lt;/span&gt;    assert_allclose(result.image[200, 995], pars[&amp;#39;data_2&amp;#39;])
&lt;span class="gd"&gt;-    # result.write_image(&amp;#39;test.&amp;#39; + pars[&amp;#39;file_format&amp;#39;])&lt;/span&gt;
&lt;span class="gd"&gt;-    assert repr(result) == pars[&amp;#39;repr&amp;#39;]&lt;/span&gt;
&lt;span class="gi"&gt;+    result.write_image(str(tmpdir / &amp;#39;test.&amp;#39; + pars[&amp;#39;file_format&amp;#39;]))&lt;/span&gt;
&lt;span class="gi"&gt;+    result.plot()&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;@remote_data
&lt;span class="w"&gt; &lt;/span&gt;class TestSimpleTilePainter:
&lt;span class="gh"&gt;diff --git a/hips/utils/wcs.py b/hips/utils/wcs.py&lt;/span&gt;
&lt;span class="gh"&gt;index 6268dc8..e43b049 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/wcs.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/wcs.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -58,6 +58,13 @@ class WCSGeometry:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        self.wcs = wcs
&lt;span class="w"&gt; &lt;/span&gt;        self.shape = Shape(width=width, height=height)

&lt;span class="gi"&gt;+    def __str__(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        return (&lt;/span&gt;
&lt;span class="gi"&gt;+            &amp;#39;WCSGeometry data:\n&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+            f&amp;#39;WCS: {self.wcs}\n&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+            f&amp;#39;Shape: {self.shape}\n&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        )&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    @property
&lt;span class="w"&gt; &lt;/span&gt;    def center_pix(self) -&amp;gt; Tuple[float, float]:
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Image center in pixel coordinates (tuple of x, y).&amp;quot;&amp;quot;&amp;quot;

commit c15092b116d5b02b601a2c087ef6a80fb82107d2
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Tue Jul 25 19:24:10 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Add method write_image in HipsDrawResult class

&lt;span class="gh"&gt;diff --git a/hips/draw/simple.py b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index c47eaa0..2539df3 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,6 +1,8 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="w"&gt; &lt;/span&gt;&amp;quot;&amp;quot;&amp;quot;HiPS tile drawing -- simple method.&amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;import numpy as np
&lt;span class="gi"&gt;+from PIL import Image&lt;/span&gt;
&lt;span class="gi"&gt;+from astropy.io import fits&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from typing import List, Tuple
&lt;span class="w"&gt; &lt;/span&gt;from astropy.wcs.utils import proj_plane_pixel_scales
&lt;span class="w"&gt; &lt;/span&gt;from skimage.transform import ProjectiveTransform, warp
&lt;span class="gu"&gt;@@ -127,7 +129,7 @@ class SimpleTilePainter:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    @property
&lt;span class="w"&gt; &lt;/span&gt;    def result(self) -&amp;gt; &amp;#39;HipsDrawResult&amp;#39;:
&lt;span class="gd"&gt;-        return HipsDrawResult(self.image, self.geometry)&lt;/span&gt;
&lt;span class="gi"&gt;+        return HipsDrawResult(self.image, self.geometry, self.tile_format)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def warp_image(self, tile: HipsTile) -&amp;gt; np.ndarray:
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Warp a HiPS tile and a sky image.&amp;quot;&amp;quot;&amp;quot;
&lt;span class="gu"&gt;@@ -177,12 +179,51 @@ class SimpleTilePainter:&lt;/span&gt;


&lt;span class="w"&gt; &lt;/span&gt;class HipsDrawResult:
&lt;span class="gd"&gt;-    &amp;quot;&amp;quot;&amp;quot;Container class for reporting information related with fetching / drawing of HiPS tiles.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;Container class for reporting information related with fetching / drawing of HiPS tiles.&lt;/span&gt;

&lt;span class="gd"&gt;-    def __init__(self, image: np.ndarray, geometry: WCSGeometry) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+    Parameters&lt;/span&gt;
&lt;span class="gi"&gt;+    ----------&lt;/span&gt;
&lt;span class="gi"&gt;+    image: `~numpy.ndarray`&lt;/span&gt;
&lt;span class="gi"&gt;+        Container for HiPS tile data&lt;/span&gt;
&lt;span class="gi"&gt;+    geometry : `~hips.utils.WCSGeometry`&lt;/span&gt;
&lt;span class="gi"&gt;+        An object of WCSGeometry&lt;/span&gt;
&lt;span class="gi"&gt;+    tile_format : {&amp;#39;fits&amp;#39;, &amp;#39;jpg&amp;#39;, &amp;#39;png&amp;#39;}&lt;/span&gt;
&lt;span class="gi"&gt;+        Format of HiPS tile&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def __init__(self, image: np.ndarray, geometry: WCSGeometry, tile_format: str) -&amp;gt; None:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        self.image = image
&lt;span class="w"&gt; &lt;/span&gt;        self.geometry = geometry
&lt;span class="gi"&gt;+        self.tile_format = tile_format&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def __str__(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        return (&lt;/span&gt;
&lt;span class="gi"&gt;+            &amp;#39;This class object contains two attributes: image and geometry&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        )&lt;/span&gt;

&lt;span class="gi"&gt;+    def __repr__(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        return (&lt;/span&gt;
&lt;span class="gi"&gt;+            f&amp;#39;width={self.image.shape[0]}, &amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+            f&amp;#39;height={self.image.shape[1]}, &amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+            f&amp;#39;channels={self.image.ndim}, &amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+            f&amp;#39;dtype={self.image.dtype}, &amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+            f&amp;#39;format={self.tile_format}&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        )&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def write_image(self, filename: str) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Write image to file.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        Parameters&lt;/span&gt;
&lt;span class="gi"&gt;+        ----------&lt;/span&gt;
&lt;span class="gi"&gt;+        filename : str&lt;/span&gt;
&lt;span class="gi"&gt;+            Filename&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        if self.tile_format == &amp;#39;fits&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+            hdu = fits.PrimaryHDU(self.image)&lt;/span&gt;
&lt;span class="gi"&gt;+            hdu.writeto(filename)&lt;/span&gt;
&lt;span class="gi"&gt;+        else:&lt;/span&gt;
&lt;span class="gi"&gt;+            image = Image.fromarray(self.image)&lt;/span&gt;
&lt;span class="gi"&gt;+            image.save(filename)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;def measure_tile_shape(corners: tuple) -&amp;gt; Tuple[List[float]]:
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;Compute length of tile edges and diagonals.&amp;quot;&amp;quot;&amp;quot;
&lt;span class="gh"&gt;diff --git a/hips/draw/tests/test_simple.py b/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index 8985954..d038f43 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -19,6 +19,7 @@ make_sky_image_pars = [&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        data_2=2296,
&lt;span class="w"&gt; &lt;/span&gt;        data_sum=8756493140,
&lt;span class="w"&gt; &lt;/span&gt;        dtype=&amp;#39;&amp;gt;i2&amp;#39;,
&lt;span class="gi"&gt;+        repr=&amp;#39;width=1000, height=2000, channels=2, dtype=&amp;gt;i2, format=fits&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    ),
&lt;span class="w"&gt; &lt;/span&gt;    dict(
&lt;span class="w"&gt; &lt;/span&gt;        file_format=&amp;#39;jpg&amp;#39;,
&lt;span class="gu"&gt;@@ -28,6 +29,7 @@ make_sky_image_pars = [&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        data_2=[137, 116, 114],
&lt;span class="w"&gt; &lt;/span&gt;        data_sum=828908873,
&lt;span class="w"&gt; &lt;/span&gt;        dtype=&amp;#39;uint8&amp;#39;,
&lt;span class="gi"&gt;+        repr=&amp;#39;width=1000, height=2000, channels=3, dtype=uint8, format=jpg&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    ),
&lt;span class="w"&gt; &lt;/span&gt;    dict(
&lt;span class="w"&gt; &lt;/span&gt;        file_format=&amp;#39;png&amp;#39;,
&lt;span class="gu"&gt;@@ -37,6 +39,7 @@ make_sky_image_pars = [&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        data_2=[227, 217, 205, 255],
&lt;span class="w"&gt; &lt;/span&gt;        data_sum=1635622838,
&lt;span class="w"&gt; &lt;/span&gt;        dtype=&amp;#39;uint8&amp;#39;,
&lt;span class="gi"&gt;+        repr=&amp;#39;width=1000, height=2000, channels=3, dtype=uint8, format=png&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    ),
&lt;span class="w"&gt; &lt;/span&gt;]

&lt;span class="gu"&gt;@@ -52,7 +55,8 @@ def test_make_sky_image(pars):&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    assert_allclose(np.sum(result.image), pars[&amp;#39;data_sum&amp;#39;])
&lt;span class="w"&gt; &lt;/span&gt;    assert_allclose(result.image[200, 994], pars[&amp;#39;data_1&amp;#39;])
&lt;span class="w"&gt; &lt;/span&gt;    assert_allclose(result.image[200, 995], pars[&amp;#39;data_2&amp;#39;])
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gi"&gt;+    # result.write_image(&amp;#39;test.&amp;#39; + pars[&amp;#39;file_format&amp;#39;])&lt;/span&gt;
&lt;span class="gi"&gt;+    assert repr(result) == pars[&amp;#39;repr&amp;#39;]&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;@remote_data
&lt;span class="w"&gt; &lt;/span&gt;class TestSimpleTilePainter:

commit eddcaaa07028e59d2d8e8567cbf073d30bbd9ada
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Tue Jul 25 16:45:32 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Introduce class HipsDrawResult in simple.py

&lt;span class="gh"&gt;diff --git a/hips/draw/simple.py b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index b53bad8..c47eaa0 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -125,6 +125,10 @@ class SimpleTilePainter:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        return self._tiles

&lt;span class="gi"&gt;+    @property&lt;/span&gt;
&lt;span class="gi"&gt;+    def result(self) -&amp;gt; &amp;#39;HipsDrawResult&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+        return HipsDrawResult(self.image, self.geometry)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    def warp_image(self, tile: HipsTile) -&amp;gt; np.ndarray:
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Warp a HiPS tile and a sky image.&amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;        return warp(
&lt;span class="gu"&gt;@@ -172,6 +176,14 @@ class SimpleTilePainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        ax.imshow(self.image, origin=&amp;#39;lower&amp;#39;)


&lt;span class="gi"&gt;+class HipsDrawResult:&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;Container class for reporting information related with fetching / drawing of HiPS tiles.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def __init__(self, image: np.ndarray, geometry: WCSGeometry) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+        self.image = image&lt;/span&gt;
&lt;span class="gi"&gt;+        self.geometry = geometry&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;def measure_tile_shape(corners: tuple) -&amp;gt; Tuple[List[float]]:
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;Compute length of tile edges and diagonals.&amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;    x, y = corners
&lt;span class="gu"&gt;@@ -268,7 +280,7 @@ def plot_mpl_single_tile(geometry: WCSGeometry, tile: HipsTile, image: np.ndarra&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    ax.imshow(image, origin=&amp;#39;lower&amp;#39;)


&lt;span class="gd"&gt;-def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties, tile_format: str) -&amp;gt; np.ndarray:&lt;/span&gt;
&lt;span class="gi"&gt;+def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties, tile_format: str) -&amp;gt; &amp;#39;HipsDrawResult&amp;#39;:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;Make sky image: fetch tiles and draw.

&lt;span class="w"&gt; &lt;/span&gt;    The example for this can be found on the :ref:`gs` page.
&lt;span class="gu"&gt;@@ -291,4 +303,4 @@ def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties, til&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    painter = SimpleTilePainter(geometry, hips_survey, tile_format)
&lt;span class="w"&gt; &lt;/span&gt;    painter.run()

&lt;span class="gd"&gt;-    return painter.image&lt;/span&gt;
&lt;span class="gi"&gt;+    return painter.result&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/draw/tests/test_simple.py b/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index 6dbf340..8985954 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -46,12 +46,12 @@ make_sky_image_pars = [&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;def test_make_sky_image(pars):
&lt;span class="w"&gt; &lt;/span&gt;    hips_survey = HipsSurveyProperties.fetch(url=pars[&amp;#39;url&amp;#39;])
&lt;span class="w"&gt; &lt;/span&gt;    geometry = make_test_wcs_geometry()
&lt;span class="gd"&gt;-    image = make_sky_image(geometry=geometry, hips_survey=hips_survey, tile_format=pars[&amp;#39;file_format&amp;#39;])&lt;/span&gt;
&lt;span class="gd"&gt;-    assert image.shape == pars[&amp;#39;shape&amp;#39;]&lt;/span&gt;
&lt;span class="gd"&gt;-    assert image.dtype == pars[&amp;#39;dtype&amp;#39;]&lt;/span&gt;
&lt;span class="gd"&gt;-    assert_allclose(np.sum(image), pars[&amp;#39;data_sum&amp;#39;])&lt;/span&gt;
&lt;span class="gd"&gt;-    assert_allclose(image[200, 994], pars[&amp;#39;data_1&amp;#39;])&lt;/span&gt;
&lt;span class="gd"&gt;-    assert_allclose(image[200, 995], pars[&amp;#39;data_2&amp;#39;])&lt;/span&gt;
&lt;span class="gi"&gt;+    result = make_sky_image(geometry=geometry, hips_survey=hips_survey, tile_format=pars[&amp;#39;file_format&amp;#39;])&lt;/span&gt;
&lt;span class="gi"&gt;+    assert result.image.shape == pars[&amp;#39;shape&amp;#39;]&lt;/span&gt;
&lt;span class="gi"&gt;+    assert result.image.dtype == pars[&amp;#39;dtype&amp;#39;]&lt;/span&gt;
&lt;span class="gi"&gt;+    assert_allclose(np.sum(result.image), pars[&amp;#39;data_sum&amp;#39;])&lt;/span&gt;
&lt;span class="gi"&gt;+    assert_allclose(result.image[200, 994], pars[&amp;#39;data_1&amp;#39;])&lt;/span&gt;
&lt;span class="gi"&gt;+    assert_allclose(result.image[200, 995], pars[&amp;#39;data_2&amp;#39;])&lt;/span&gt;


&lt;span class="w"&gt; &lt;/span&gt;@remote_data

commit 808e758d2cd9c04c31e0dc49318cf55d4f92cd18
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Mon Jul 24 14:09:33 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Add method to_split in SimpleTilePainter for checking whether to split a tile

&lt;span class="gh"&gt;diff --git a/hips/draw/simple.py b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index e9237e0..862f9e8 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -172,6 +172,29 @@ class SimpleTilePainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        ax.imshow(self.image, origin=&amp;#39;lower&amp;#39;)


&lt;span class="gi"&gt;+def _measure_tile_shape(corners: tuple) -&amp;gt; List[list]:&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;Compute length of tile edges and diagonals.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+    x, y = corners&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def compute_distance(i: int, j: int) -&amp;gt; float:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Compute distance between two points.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        return np.sqrt((x[i] - x[j]) ** 2 + (y[i] - y[j]) ** 2)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    edges = [compute_distance((i + 1) % 4, i) for i in range(4)]&lt;/span&gt;
&lt;span class="gi"&gt;+    diagonals = [compute_distance(0, 2), compute_distance(1, 3)]&lt;/span&gt;
&lt;span class="gi"&gt;+    ratio = float(np.min(diagonals) / np.max(diagonals))&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    return [edges, diagonals, ratio]&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+def _is_tile_distorted(corners: tuple) -&amp;gt; bool:&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;Implement tile splitting criteria as mentioned in :ref:`drawing_algo` page.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+    edges, diagonals, ratio = _measure_tile_shape(corners)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    return max(edges) &amp;gt; 300 or \&lt;/span&gt;
&lt;span class="gi"&gt;+           max(diagonals) &amp;gt; 150 or \&lt;/span&gt;
&lt;span class="gi"&gt;+           ratio &amp;lt; 0.7&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;def tile_corner_pixel_coordinates(width, file_format) -&amp;gt; np.ndarray:
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;Tile corner pixel coordinates for projective transform.

&lt;span class="gh"&gt;diff --git a/hips/draw/tests/test_simple.py b/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index d3368f7..85e2d78 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -5,7 +5,7 @@ from numpy.testing import assert_allclose&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from astropy.coordinates import SkyCoord
&lt;span class="w"&gt; &lt;/span&gt;from astropy.tests.helper import remote_data
&lt;span class="w"&gt; &lt;/span&gt;from ...tiles import HipsSurveyProperties
&lt;span class="gd"&gt;-from ..simple import make_sky_image, SimpleTilePainter, plot_mpl_single_tile&lt;/span&gt;
&lt;span class="gi"&gt;+from ..simple import make_sky_image, SimpleTilePainter, plot_mpl_single_tile, _is_tile_distorted, _measure_tile_shape&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from ...utils.wcs import WCSGeometry
&lt;span class="w"&gt; &lt;/span&gt;from ...utils.testing import make_test_wcs_geometry, requires_hips_extra

&lt;span class="gu"&gt;@@ -86,6 +86,7 @@ class TestSimpleTilePainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            width=2000, height=1000, fov=pars[&amp;#39;fov&amp;#39;],
&lt;span class="w"&gt; &lt;/span&gt;            coordsys=&amp;#39;icrs&amp;#39;, projection=&amp;#39;AIT&amp;#39;,
&lt;span class="w"&gt; &lt;/span&gt;        )
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        simple_tile_painter = SimpleTilePainter(geometry, self.hips_survey, &amp;#39;fits&amp;#39;)
&lt;span class="w"&gt; &lt;/span&gt;        assert simple_tile_painter.draw_hips_order == pars[&amp;#39;order&amp;#39;]

&lt;span class="gu"&gt;@@ -101,3 +102,28 @@ class TestSimpleTilePainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        tile = self.painter.tiles[3]
&lt;span class="w"&gt; &lt;/span&gt;        image = self.painter.image
&lt;span class="w"&gt; &lt;/span&gt;        plot_mpl_single_tile(self.geometry, tile, image)
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_corners(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        tile = self.painter.tiles[3]&lt;/span&gt;
&lt;span class="gi"&gt;+        x, y = tile.meta.skycoord_corners.to_pixel(self.geometry.wcs)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        assert_allclose(x, [764.627476, 999., 764.646551, 530.26981])&lt;/span&gt;
&lt;span class="gi"&gt;+        assert_allclose(y, [300.055412, 101.107245, -97.849955, 101.105373])&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_is_tile_distorted(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        tile = self.painter.tiles[3]&lt;/span&gt;
&lt;span class="gi"&gt;+        corners = tile.meta.skycoord_corners.to_pixel(self.geometry.wcs)&lt;/span&gt;
&lt;span class="gi"&gt;+        assert _is_tile_distorted(corners) == True&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_measure_tile_shape(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        tile = self.painter.tiles[3]&lt;/span&gt;
&lt;span class="gi"&gt;+        corners = tile.meta.skycoord_corners.to_pixel(self.geometry.wcs)&lt;/span&gt;
&lt;span class="gi"&gt;+        edges, diagonals, ratio = _measure_tile_shape(corners)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        edges_precomp = [307.426175, 307.417479, 307.434024, 307.41606]&lt;/span&gt;
&lt;span class="gi"&gt;+        diagonals_precomp = [397.905367, 468.73019]&lt;/span&gt;
&lt;span class="gi"&gt;+        ratio_precomp = 0.848900658905216&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        assert_allclose(edges_precomp, edges)&lt;/span&gt;
&lt;span class="gi"&gt;+        assert_allclose(diagonals_precomp, diagonals)&lt;/span&gt;
&lt;span class="gi"&gt;+        assert_allclose(ratio_precomp, ratio)&lt;/span&gt;

commit e08ddcdf3c655021d5a0b96bb26ad50577e0e02a
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Thu Jul 20 12:32:51 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Add property &amp;#39;children&amp;#39; for creating four children tiles from parent tile

&lt;span class="gh"&gt;diff --git a/hips/tiles/tests/test_tile.py b/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gh"&gt;index f8f156b..2957e40 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -59,8 +59,13 @@ HIPS_TILE_TEST_CASES = [&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        dtype=&amp;#39;int16&amp;#39;,
&lt;span class="w"&gt; &lt;/span&gt;        shape=(512, 512),
&lt;span class="gi"&gt;+        child_order=4,&lt;/span&gt;
&lt;span class="gi"&gt;+        child_shape=(256, 256),&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        pix_idx=[[510], [5]],
&lt;span class="w"&gt; &lt;/span&gt;        pix_val=[3047],
&lt;span class="gi"&gt;+        child_ipix=[1852, 1853, 1854, 1855],&lt;/span&gt;
&lt;span class="gi"&gt;+        child_pix_idx=[[0], [255]],&lt;/span&gt;
&lt;span class="gi"&gt;+        child_pix_val=[2407, 2321, 2465, 2835],&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    ),
&lt;span class="w"&gt; &lt;/span&gt;    dict(
&lt;span class="w"&gt; &lt;/span&gt;        meta=dict(order=3, ipix=463, file_format=&amp;#39;jpg&amp;#39;),
&lt;span class="gu"&gt;@@ -69,8 +74,13 @@ HIPS_TILE_TEST_CASES = [&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        dtype=&amp;#39;uint8&amp;#39;,
&lt;span class="w"&gt; &lt;/span&gt;        shape=(512, 512, 3),
&lt;span class="gi"&gt;+        child_order=4,&lt;/span&gt;
&lt;span class="gi"&gt;+        child_shape=(256, 256, 3),&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        pix_idx=[[510], [5]],
&lt;span class="w"&gt; &lt;/span&gt;        pix_val=[[116, 81, 61]],
&lt;span class="gi"&gt;+        child_ipix=[1852, 1853, 1854, 1855],&lt;/span&gt;
&lt;span class="gi"&gt;+        child_pix_idx=[[0], [255]],&lt;/span&gt;
&lt;span class="gi"&gt;+        child_pix_val=[[[255, 241, 225]], [[109, 95, 86]], [[245, 214, 211]], [[137, 97, 87]]],&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    ),
&lt;span class="w"&gt; &lt;/span&gt;    dict(
&lt;span class="w"&gt; &lt;/span&gt;        meta=dict(order=6, ipix=6112, file_format=&amp;#39;png&amp;#39;),
&lt;span class="gu"&gt;@@ -79,8 +89,13 @@ HIPS_TILE_TEST_CASES = [&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        dtype=&amp;#39;uint8&amp;#39;,
&lt;span class="w"&gt; &lt;/span&gt;        shape=(512, 512, 4),
&lt;span class="gi"&gt;+        child_order=7,&lt;/span&gt;
&lt;span class="gi"&gt;+        child_shape=(256, 256, 4),&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        pix_idx=[[253], [5]],
&lt;span class="w"&gt; &lt;/span&gt;        pix_val=[[19, 19, 19, 255]],
&lt;span class="gi"&gt;+        child_ipix=[24448, 24449, 24450, 24451],&lt;/span&gt;
&lt;span class="gi"&gt;+        child_pix_idx=[[0], [255]],&lt;/span&gt;
&lt;span class="gi"&gt;+        child_pix_val=[[[15, 15, 15, 255]], [[20, 20, 20, 255]], [[17, 17, 17, 255]], [[13, 13, 13, 255]]],&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    ),
&lt;span class="w"&gt; &lt;/span&gt;]

&lt;span class="gu"&gt;@@ -117,7 +132,19 @@ class TestHipsTile:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        data = tile.data
&lt;span class="w"&gt; &lt;/span&gt;        assert data.shape == pars[&amp;#39;shape&amp;#39;]
&lt;span class="w"&gt; &lt;/span&gt;        assert data.dtype.name == pars[&amp;#39;dtype&amp;#39;]
&lt;span class="gd"&gt;-        assert_equal(tile.data[pars[&amp;#39;pix_idx&amp;#39;]], pars[&amp;#39;pix_val&amp;#39;])&lt;/span&gt;
&lt;span class="gi"&gt;+        assert_equal(data[pars[&amp;#39;pix_idx&amp;#39;]], pars[&amp;#39;pix_val&amp;#39;])&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @requires_hips_extra()&lt;/span&gt;
&lt;span class="gi"&gt;+    @pytest.mark.parametrize(&amp;#39;pars&amp;#39;, HIPS_TILE_TEST_CASES)&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_children(self, pars):&lt;/span&gt;
&lt;span class="gi"&gt;+        tile = self._read_tile(pars)&lt;/span&gt;
&lt;span class="gi"&gt;+        child_data = [_.data[pars[&amp;#39;child_pix_idx&amp;#39;]] for _ in tile.children]&lt;/span&gt;
&lt;span class="gi"&gt;+        child_ipix = [_.meta.ipix for _ in tile.children]&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        assert tile.children[0].meta.order == pars[&amp;#39;child_order&amp;#39;]&lt;/span&gt;
&lt;span class="gi"&gt;+        assert tile.children[0].data.shape == pars[&amp;#39;child_shape&amp;#39;]&lt;/span&gt;
&lt;span class="gi"&gt;+        assert_equal(child_ipix, pars[&amp;#39;child_ipix&amp;#39;])&lt;/span&gt;
&lt;span class="gi"&gt;+        assert_equal(child_data, pars[&amp;#39;child_pix_val&amp;#39;])&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    @remote_data
&lt;span class="w"&gt; &lt;/span&gt;    @requires_hips_extra()
&lt;span class="gh"&gt;diff --git a/hips/tiles/tile.py b/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gh"&gt;index 2a86467..32f98c2 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -11,6 +11,7 @@ from astropy.coordinates import SkyCoord&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from astropy.io import fits
&lt;span class="w"&gt; &lt;/span&gt;from ..utils import healpix_pixel_corners
&lt;span class="w"&gt; &lt;/span&gt;from .io import tile_default_url, tile_default_path
&lt;span class="gi"&gt;+from typing import List&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;__all__ = [
&lt;span class="w"&gt; &lt;/span&gt;    &amp;#39;HipsTileMeta&amp;#39;,
&lt;span class="gu"&gt;@@ -167,6 +168,29 @@ class HipsTile:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        return cls(meta, raw_data)

&lt;span class="w"&gt; &lt;/span&gt;    @property
&lt;span class="gi"&gt;+    def children(self) -&amp;gt; List[&amp;#39;HipsTile&amp;#39;]:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Create four children tiles from parent tile.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        children_tiles = []&lt;/span&gt;
&lt;span class="gi"&gt;+        w = self.data.shape[0] // 2&lt;/span&gt;
&lt;span class="gi"&gt;+        child_data = [&lt;/span&gt;
&lt;span class="gi"&gt;+            self.data[0: w, 0: w],&lt;/span&gt;
&lt;span class="gi"&gt;+            self.data[0: w, w: w * 2],&lt;/span&gt;
&lt;span class="gi"&gt;+            self.data[w: w * 2, 0: w],&lt;/span&gt;
&lt;span class="gi"&gt;+            self.data[w: w * 2, w: w * 2]&lt;/span&gt;
&lt;span class="gi"&gt;+        ]&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        for index, data in enumerate(child_data):&lt;/span&gt;
&lt;span class="gi"&gt;+            meta = HipsTileMeta(&lt;/span&gt;
&lt;span class="gi"&gt;+                self.meta.order + 1,&lt;/span&gt;
&lt;span class="gi"&gt;+                self.meta.ipix * 4 + index,&lt;/span&gt;
&lt;span class="gi"&gt;+                self.meta.file_format,&lt;/span&gt;
&lt;span class="gi"&gt;+                self.meta.frame&lt;/span&gt;
&lt;span class="gi"&gt;+            )&lt;/span&gt;
&lt;span class="gi"&gt;+            children_tiles.append(self.from_numpy(meta, data))&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        return children_tiles&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @property&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    def data(self) -&amp;gt; np.ndarray:
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Tile pixel data (`~numpy.ndarray`).&amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;        fmt = self.meta.file_format

commit 9005878e851ee8f64ac498599bba8737a1fd83f4
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Tue Jul 18 13:31:41 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Add methods for drawing markers over sky image

&lt;span class="gh"&gt;diff --git a/hips/draw/simple.py b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index ae2dfc9..9811238 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,7 +1,7 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="w"&gt; &lt;/span&gt;&amp;quot;&amp;quot;&amp;quot;HiPS tile drawing -- simple method.&amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-from typing import Tuple, List&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;import numpy as np
&lt;span class="gi"&gt;+from typing import Tuple, List&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from astropy.wcs.utils import proj_plane_pixel_scales
&lt;span class="w"&gt; &lt;/span&gt;from skimage.transform import ProjectiveTransform, warp
&lt;span class="w"&gt; &lt;/span&gt;from ..tiles import HipsSurveyProperties, HipsTile, HipsTileMeta
&lt;span class="gu"&gt;@@ -9,8 +9,7 @@ from ..utils import WCSGeometry, compute_healpix_pixel_indices, get_hips_order_f&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;__all__ = [
&lt;span class="w"&gt; &lt;/span&gt;    &amp;#39;make_sky_image&amp;#39;,
&lt;span class="gd"&gt;-    &amp;#39;SimpleTilePainter&amp;#39;,&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;#39;SimpleTilePainter&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;]

&lt;span class="w"&gt; &lt;/span&gt;__doctest_skip__ = [
&lt;span class="gu"&gt;@@ -178,11 +177,49 @@ class SimpleTilePainter:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        return image

&lt;span class="gi"&gt;+    def draw_hips_tile_grid(self) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Draw lines on the output image (mainly used for debugging).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        import matplotlib.pyplot as plt&lt;/span&gt;
&lt;span class="gi"&gt;+        for tile in self.tiles:&lt;/span&gt;
&lt;span class="gi"&gt;+            corners = tile.meta.skycoord_corners.transform_to(self.geometry.celestial_frame)&lt;/span&gt;
&lt;span class="gi"&gt;+            ax = plt.subplot(projection=self.geometry.wcs)&lt;/span&gt;
&lt;span class="gi"&gt;+            ax.plot(corners.data.lon.deg, corners.data.lat.deg,&lt;/span&gt;
&lt;span class="gi"&gt;+                    &amp;#39;red&amp;#39;, lw=1, transform=ax.get_transform(&amp;#39;icrs&amp;#39;))&lt;/span&gt;
&lt;span class="gi"&gt;+        ax.imshow(self.image, origin=&amp;#39;lower&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    def run(self) -&amp;gt; None:
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Run all steps of the naive algorithm.&amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;        self.float_image = self.draw_tiles()


&lt;span class="gi"&gt;+def draw_debug_image(geometry: WCSGeometry, tile: HipsTile, image: np.ndarray) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;Draw markers on the output image (mainly used for debugging).&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    The following denotes their correspondence:&lt;/span&gt;
&lt;span class="gi"&gt;+    * red &amp;lt;=&amp;gt; North&lt;/span&gt;
&lt;span class="gi"&gt;+    * green &amp;lt;=&amp;gt; West&lt;/span&gt;
&lt;span class="gi"&gt;+    * blue &amp;lt;=&amp;gt; South&lt;/span&gt;
&lt;span class="gi"&gt;+    * yellow &amp;lt;=&amp;gt; East&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    Parameters&lt;/span&gt;
&lt;span class="gi"&gt;+    ----------&lt;/span&gt;
&lt;span class="gi"&gt;+    geometry : `~hips.utils.WCSGeometry`&lt;/span&gt;
&lt;span class="gi"&gt;+        Geometry of the output image&lt;/span&gt;
&lt;span class="gi"&gt;+    tile : HipsTile&lt;/span&gt;
&lt;span class="gi"&gt;+        HiPS tile&lt;/span&gt;
&lt;span class="gi"&gt;+    image : np.ndarray&lt;/span&gt;
&lt;span class="gi"&gt;+        Image containing HiPS tiles&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+    import matplotlib.pyplot as plt&lt;/span&gt;
&lt;span class="gi"&gt;+    corners = tile.meta.skycoord_corners.transform_to(geometry.celestial_frame)&lt;/span&gt;
&lt;span class="gi"&gt;+    colors = [&amp;#39;red&amp;#39;, &amp;#39;green&amp;#39;, &amp;#39;blue&amp;#39;, &amp;#39;yellow&amp;#39;]&lt;/span&gt;
&lt;span class="gi"&gt;+    ax = plt.subplot(projection=geometry.wcs)&lt;/span&gt;
&lt;span class="gi"&gt;+    for index, corner in enumerate(corners):&lt;/span&gt;
&lt;span class="gi"&gt;+        ax.scatter(corner.data.lon.deg, corner.data.lat.deg,&lt;/span&gt;
&lt;span class="gi"&gt;+                   s=80, transform=ax.get_transform(&amp;#39;icrs&amp;#39;), color=colors[index])&lt;/span&gt;
&lt;span class="gi"&gt;+    ax.imshow(image, origin=&amp;#39;lower&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties, tile_format: str) -&amp;gt; np.ndarray:
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;Make sky image: fetch tiles and draw.

&lt;span class="gh"&gt;diff --git a/hips/draw/tests/test_simple.py b/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index 22150ce..7e8faa2 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -5,7 +5,7 @@ from numpy.testing import assert_allclose&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from astropy.coordinates import SkyCoord
&lt;span class="w"&gt; &lt;/span&gt;from astropy.tests.helper import remote_data
&lt;span class="w"&gt; &lt;/span&gt;from ...tiles import HipsSurveyProperties
&lt;span class="gd"&gt;-from ..simple import make_sky_image, SimpleTilePainter&lt;/span&gt;
&lt;span class="gi"&gt;+from ..simple import make_sky_image, SimpleTilePainter, draw_debug_image&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from ...utils.wcs import WCSGeometry
&lt;span class="w"&gt; &lt;/span&gt;from ...utils.testing import make_test_wcs_geometry, requires_hips_extra

&lt;span class="gu"&gt;@@ -95,3 +95,9 @@ class TestSimpleTilePainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    def test_run(self):
&lt;span class="w"&gt; &lt;/span&gt;        self.simple_tile_painter.run()
&lt;span class="w"&gt; &lt;/span&gt;        assert_allclose(self.simple_tile_painter.image[200, 994], 2120)
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_draw_hips_tile_grid(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        self.simple_tile_painter.draw_hips_tile_grid()&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_draw_debug_image(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        draw_debug_image(self.geometry, self.simple_tile_painter.tiles[3], self.simple_tile_painter.image)&lt;/span&gt;

commit 46c78a036dec051bb9e6fb75adbf7fcd6a585301
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Tue Jul 18 20:16:34 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Fix tile_width property in HipsSurveyProperties class

&lt;span class="gh"&gt;diff --git a/hips/tiles/surveys.py b/hips/tiles/surveys.py&lt;/span&gt;
&lt;span class="gh"&gt;index 28bc65b..f5c462b 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/surveys.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/surveys.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -128,7 +128,10 @@ class HipsSurveyProperties:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    @property
&lt;span class="w"&gt; &lt;/span&gt;    def tile_width(self) -&amp;gt; int:
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;HiPS tile width&amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-        return int(self.data[&amp;#39;hips_tile_width&amp;#39;]) or 512&lt;/span&gt;
&lt;span class="gi"&gt;+        try:&lt;/span&gt;
&lt;span class="gi"&gt;+            return int(self.data[&amp;#39;hips_tile_width&amp;#39;])&lt;/span&gt;
&lt;span class="gi"&gt;+        except KeyError:&lt;/span&gt;
&lt;span class="gi"&gt;+            return 512&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    @property
&lt;span class="w"&gt; &lt;/span&gt;    def tile_format(self) -&amp;gt; str:

commit 6139a0bf4047b303a01fefb61ef5288de55486c6
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Mon Jul 17 14:13:40 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Fix RGB dtype issue

&lt;span class="gh"&gt;diff --git a/hips/draw/simple.py b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index 235b235..f144533 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -63,6 +63,11 @@ class SimpleTilePainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        self._tiles = None

&lt;span class="w"&gt; &lt;/span&gt;    @property
&lt;span class="gi"&gt;+    def image(self) -&amp;gt; np.ndarray:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;All sky image converted to HiPS tile format.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        return self.float_image.astype(self.tiles[0].data.dtype)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @property&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    def draw_hips_order(self) -&amp;gt; int:
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Compute HiPS tile order matching a given image pixel size.&amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;        # Sky image angular resolution (pixel size in degrees)
&lt;span class="gu"&gt;@@ -159,7 +164,7 @@ class SimpleTilePainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Draw HiPS tiles onto an empty image.&amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;        tiles = self.tiles

&lt;span class="gd"&gt;-        image = np.zeros(self.shape)&lt;/span&gt;
&lt;span class="gi"&gt;+        image = np.zeros(self.shape, dtype=np.float32)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        for tile in tiles:
&lt;span class="w"&gt; &lt;/span&gt;            tile_image = self.warp_image(tile)
&lt;span class="w"&gt; &lt;/span&gt;            # TODO: put better algorithm here instead of summing pixels
&lt;span class="gu"&gt;@@ -170,7 +175,7 @@ class SimpleTilePainter:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def run(self) -&amp;gt; None:
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Run all steps of the naive algorithm.&amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-        self.image = self.draw_tiles()&lt;/span&gt;
&lt;span class="gi"&gt;+        self.float_image = self.draw_tiles()&lt;/span&gt;


&lt;span class="w"&gt; &lt;/span&gt;def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties, tile_format: str) -&amp;gt; np.ndarray:
&lt;span class="gh"&gt;diff --git a/hips/draw/tests/test_simple.py b/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index 7ec0e2a..22150ce 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -14,25 +14,28 @@ make_sky_image_pars = [&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        file_format=&amp;#39;fits&amp;#39;,
&lt;span class="w"&gt; &lt;/span&gt;        shape=(1000, 2000),
&lt;span class="w"&gt; &lt;/span&gt;        url=&amp;#39;http://alasky.unistra.fr/DSS/DSS2Merged/properties&amp;#39;,
&lt;span class="gd"&gt;-        data_1=2213.30874796,&lt;/span&gt;
&lt;span class="gd"&gt;-        data_2=2296.93885940,&lt;/span&gt;
&lt;span class="gd"&gt;-        data_sum=8757489268.044867,&lt;/span&gt;
&lt;span class="gi"&gt;+        data_1=2213,&lt;/span&gt;
&lt;span class="gi"&gt;+        data_2=2296,&lt;/span&gt;
&lt;span class="gi"&gt;+        data_sum=8756493140,&lt;/span&gt;
&lt;span class="gi"&gt;+        dtype=&amp;#39;&amp;gt;i2&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    ),
&lt;span class="w"&gt; &lt;/span&gt;    dict(
&lt;span class="w"&gt; &lt;/span&gt;        file_format=&amp;#39;jpg&amp;#39;,
&lt;span class="w"&gt; &lt;/span&gt;        shape=(1000, 2000, 3),
&lt;span class="w"&gt; &lt;/span&gt;        url=&amp;#39;https://raw.githubusercontent.com/hipspy/hips-extra/master/datasets/samples/FermiColor/properties&amp;#39;,
&lt;span class="gd"&gt;-        data_1=[145.388459, 98.579295, 49.792577],&lt;/span&gt;
&lt;span class="gd"&gt;-        data_2=[146.197811, 99.531895, 56.889927],&lt;/span&gt;
&lt;span class="gd"&gt;-        data_sum=813159920.0305891,&lt;/span&gt;
&lt;span class="gi"&gt;+        data_1=[145, 98, 49],&lt;/span&gt;
&lt;span class="gi"&gt;+        data_2=[146, 99, 56],&lt;/span&gt;
&lt;span class="gi"&gt;+        data_sum=808113247,&lt;/span&gt;
&lt;span class="gi"&gt;+        dtype=&amp;#39;uint8&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    ),
&lt;span class="w"&gt; &lt;/span&gt;    dict(
&lt;span class="w"&gt; &lt;/span&gt;        file_format=&amp;#39;png&amp;#39;,
&lt;span class="w"&gt; &lt;/span&gt;        shape=(1000, 2000, 4),
&lt;span class="w"&gt; &lt;/span&gt;        url=&amp;#39;https://raw.githubusercontent.com/hipspy/hips-extra/master/datasets/samples/AKARI-FIS/properties&amp;#39;,
&lt;span class="gd"&gt;-        data_1=[249.064796, 237.805612, 190.408181, 255.],&lt;/span&gt;
&lt;span class="gd"&gt;-        data_2=[249.013527, 238.79403, 195.793398, 255.],&lt;/span&gt;
&lt;span class="gd"&gt;-        data_sum=1645783166.1630714&lt;/span&gt;
&lt;span class="gi"&gt;+        data_1=[249, 237, 190, 255.],&lt;/span&gt;
&lt;span class="gi"&gt;+        data_2=[249, 238, 195, 255.],&lt;/span&gt;
&lt;span class="gi"&gt;+        data_sum=1632505453,&lt;/span&gt;
&lt;span class="gi"&gt;+        dtype=&amp;#39;uint8&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    ),
&lt;span class="w"&gt; &lt;/span&gt;]

&lt;span class="gu"&gt;@@ -44,7 +47,7 @@ def test_make_sky_image(pars):&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    geometry = make_test_wcs_geometry(case=2)
&lt;span class="w"&gt; &lt;/span&gt;    image = make_sky_image(geometry=geometry, hips_survey=hips_survey, tile_format=pars[&amp;#39;file_format&amp;#39;])
&lt;span class="w"&gt; &lt;/span&gt;    assert image.shape == pars[&amp;#39;shape&amp;#39;]
&lt;span class="gd"&gt;-    assert image.dtype == np.float64&lt;/span&gt;
&lt;span class="gi"&gt;+    assert image.dtype == pars[&amp;#39;dtype&amp;#39;]&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    assert_allclose(np.sum(image), pars[&amp;#39;data_sum&amp;#39;])
&lt;span class="w"&gt; &lt;/span&gt;    assert_allclose(image[200, 994], pars[&amp;#39;data_1&amp;#39;])
&lt;span class="w"&gt; &lt;/span&gt;    assert_allclose(image[200, 995], pars[&amp;#39;data_2&amp;#39;])
&lt;span class="gu"&gt;@@ -91,4 +94,4 @@ class TestSimpleTilePainter:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def test_run(self):
&lt;span class="w"&gt; &lt;/span&gt;        self.simple_tile_painter.run()
&lt;span class="gd"&gt;-        assert_allclose(self.simple_tile_painter.image[200, 994], 2120.9609175)&lt;/span&gt;
&lt;span class="gi"&gt;+        assert_allclose(self.simple_tile_painter.image[200, 994], 2120)&lt;/span&gt;

commit eb5cd378624001de9588a4eb7095361b1f9b94d5
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Tue Jul 11 20:29:14 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Reformat SimpleTilePainter class

&lt;span class="gh"&gt;diff --git a/docs/plot_fits.py b/docs/plot_fits.py&lt;/span&gt;
&lt;span class="gh"&gt;index 67b03d9..70cac7a 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/docs/plot_fits.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/docs/plot_fits.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -6,7 +6,6 @@ from astropy.visualization.mpl_normalize import simple_norm&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from hips import HipsSurveyProperties
&lt;span class="w"&gt; &lt;/span&gt;from hips import make_sky_image
&lt;span class="w"&gt; &lt;/span&gt;from hips.utils import WCSGeometry
&lt;span class="gd"&gt;-import numpy as np&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;url = &amp;#39;http://alasky.unistra.fr/DSS/DSS2Merged/properties&amp;#39;
&lt;span class="w"&gt; &lt;/span&gt;hips_survey = HipsSurveyProperties.fetch(url)
&lt;span class="w"&gt; &lt;/span&gt;geometry = WCSGeometry.create_simple(
&lt;span class="gu"&gt;@@ -14,7 +13,7 @@ geometry = WCSGeometry.create_simple(&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    width=2000, height=1000, fov=&amp;quot;3 deg&amp;quot;,
&lt;span class="w"&gt; &lt;/span&gt;    coordsys=&amp;#39;galactic&amp;#39;, projection=&amp;#39;AIT&amp;#39;
&lt;span class="w"&gt; &lt;/span&gt;)
&lt;span class="gd"&gt;-data = make_sky_image(geometry=geometry, hips_survey=hips_survey, tile_format=&amp;#39;fits&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+image = make_sky_image(geometry=geometry, hips_survey=hips_survey, tile_format=&amp;#39;fits&amp;#39;)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;ax = plt.subplot(projection=geometry.wcs)
&lt;span class="gd"&gt;-norm = simple_norm(data, &amp;#39;sqrt&amp;#39;)&lt;/span&gt;
&lt;span class="gd"&gt;-ax.imshow(data, origin=&amp;#39;lower&amp;#39;, norm=norm, cmap=&amp;#39;gray&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+norm = simple_norm(image, &amp;#39;sqrt&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+ax.imshow(image, origin=&amp;#39;lower&amp;#39;, norm=norm, cmap=&amp;#39;gray&amp;#39;)&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/draw/simple.py b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index 2664a59..b0c4ed9 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,80 +1,90 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="w"&gt; &lt;/span&gt;&amp;quot;&amp;quot;&amp;quot;HiPS tile drawing -- simple method.&amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-from typing import Generator, Any&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;import numpy as np
&lt;span class="gi"&gt;+from typing import Tuple&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from astropy.wcs.utils import proj_plane_pixel_scales
&lt;span class="w"&gt; &lt;/span&gt;from skimage.transform import ProjectiveTransform, warp
&lt;span class="w"&gt; &lt;/span&gt;from ..tiles import HipsSurveyProperties, HipsTile, HipsTileMeta
&lt;span class="gd"&gt;-from ..utils import WCSGeometry, compute_healpix_pixel_indices&lt;/span&gt;
&lt;span class="gi"&gt;+from ..utils import WCSGeometry, compute_healpix_pixel_indices, get_hips_order_for_resolution&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;__all__ = [
&lt;span class="gd"&gt;-    &amp;#39;draw_sky_image&amp;#39;,&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;#39;make_sky_image&amp;#39;,
&lt;span class="w"&gt; &lt;/span&gt;    &amp;#39;SimpleTilePainter&amp;#39;,
&lt;span class="gd"&gt;-    &amp;#39;compute_matching_hips_order&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;]

&lt;span class="w"&gt; &lt;/span&gt;__doctest_skip__ = [
&lt;span class="gd"&gt;-    &amp;#39;compute_matching_hips_order&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;#39;SimpleTilePainter&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;]


&lt;span class="gd"&gt;-# TODO: Fix type annotation issue&lt;/span&gt;
&lt;span class="gd"&gt;-def draw_sky_image(geometry: WCSGeometry, tiles: Generator[HipsTile, Any, Any],&lt;/span&gt;
&lt;span class="gd"&gt;-                   hips_survey: HipsSurveyProperties, tile_format: str) -&amp;gt; np.ndarray:&lt;/span&gt;
&lt;span class="gi"&gt;+class SimpleTilePainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;Draw sky image using the simple and quick method.

&lt;span class="gi"&gt;+    Paint HiPS tiles onto an all-sky image using a simple projective&lt;/span&gt;
&lt;span class="gi"&gt;+    transformation method. The algorithm implemented is described&lt;/span&gt;
&lt;span class="gi"&gt;+    here: :ref:`drawing_algo`.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    Parameters
&lt;span class="w"&gt; &lt;/span&gt;    ----------
&lt;span class="w"&gt; &lt;/span&gt;    geometry : `~hips.utils.WCSGeometry`
&lt;span class="w"&gt; &lt;/span&gt;        An object of WCSGeometry
&lt;span class="gd"&gt;-    tiles : List[HipsTile]&lt;/span&gt;
&lt;span class="gd"&gt;-        A list of HipsTile&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    hips_survey : `~hips.HipsSurveyProperties`
&lt;span class="w"&gt; &lt;/span&gt;        HiPS survey properties
&lt;span class="w"&gt; &lt;/span&gt;    tile_format : `str`
&lt;span class="w"&gt; &lt;/span&gt;        Format of HiPS tile

&lt;span class="gd"&gt;-    Returns&lt;/span&gt;
&lt;span class="gd"&gt;-    -------&lt;/span&gt;
&lt;span class="gd"&gt;-    np.ndarray&lt;/span&gt;
&lt;span class="gd"&gt;-        Returns a numpy array containing all HiPS tiles projected onto it&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-    if tile_format == &amp;#39;jpg&amp;#39;:&lt;/span&gt;
&lt;span class="gd"&gt;-        shape = (geometry.shape.height, geometry.shape.width, 3)&lt;/span&gt;
&lt;span class="gd"&gt;-    elif tile_format == &amp;#39;png&amp;#39;:&lt;/span&gt;
&lt;span class="gd"&gt;-        shape = (geometry.shape.height, geometry.shape.width, 4)&lt;/span&gt;
&lt;span class="gd"&gt;-    else:&lt;/span&gt;
&lt;span class="gd"&gt;-        shape = (geometry.shape.height, geometry.shape.width)&lt;/span&gt;
&lt;span class="gd"&gt;-    image = np.zeros(shape)&lt;/span&gt;
&lt;span class="gd"&gt;-    for tile in tiles:&lt;/span&gt;
&lt;span class="gd"&gt;-        painter = SimpleTilePainter(geometry, hips_survey, tile)&lt;/span&gt;
&lt;span class="gd"&gt;-        image += painter.warp_image()&lt;/span&gt;
&lt;span class="gd"&gt;-    return image&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-class SimpleTilePainter:&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;quot;&amp;quot;&amp;quot;Paint a single tile using a simple projective transformation method.&lt;/span&gt;
&lt;span class="gd"&gt;-    The algorithm implemented is described here: :ref:`drawing_algo`.&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    Parameters&lt;/span&gt;
&lt;span class="gd"&gt;-    ----------&lt;/span&gt;
&lt;span class="gd"&gt;-    geometry : `~hips.utils.WCSGeometry`&lt;/span&gt;
&lt;span class="gd"&gt;-        An object of WCSGeometry&lt;/span&gt;
&lt;span class="gd"&gt;-    hips_survey : `~hips.HipsSurveyProperties`&lt;/span&gt;
&lt;span class="gd"&gt;-        HiPS survey properties&lt;/span&gt;
&lt;span class="gd"&gt;-    tile : `HipsTile`&lt;/span&gt;
&lt;span class="gd"&gt;-       An object of HipsTile&lt;/span&gt;
&lt;span class="gi"&gt;+    Examples&lt;/span&gt;
&lt;span class="gi"&gt;+    --------&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;gt;&amp;gt;&amp;gt; from hips.utils import WCSGeometry&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;gt;&amp;gt;&amp;gt; from hips.draw import SimpleTilePainter&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;gt;&amp;gt;&amp;gt; from hips.tiles import HipsSurveyProperties&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;gt;&amp;gt;&amp;gt; from astropy.coordinates import SkyCoord&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;gt;&amp;gt;&amp;gt; url = &amp;#39;http://alasky.unistra.fr/DSS/DSS2Merged/properties&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;gt;&amp;gt;&amp;gt; hips_survey = HipsSurveyProperties.fetch(url)&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;gt;&amp;gt;&amp;gt; geometry = WCSGeometry.create_simple(&lt;/span&gt;
&lt;span class="gi"&gt;+    ...     skydir=SkyCoord(0, 0, unit=&amp;#39;deg&amp;#39;, frame=&amp;#39;icrs&amp;#39;),&lt;/span&gt;
&lt;span class="gi"&gt;+    ...     width=2000, height=1000, fov=&amp;quot;3 deg&amp;quot;,&lt;/span&gt;
&lt;span class="gi"&gt;+    ...     coordsys=&amp;#39;icrs&amp;#39;, projection=&amp;#39;AIT&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+    ... )&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;gt;&amp;gt;&amp;gt; painter = SimpleTilePainter(geometry, hips_survey, &amp;#39;fits&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;gt;&amp;gt;&amp;gt; painter.draw_hips_order&lt;/span&gt;
&lt;span class="gi"&gt;+    7&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;gt;&amp;gt;&amp;gt; painter.run()&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;gt;&amp;gt;&amp;gt; painter.image.shape&lt;/span&gt;
&lt;span class="gi"&gt;+    (1000, 2000)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;

&lt;span class="gd"&gt;-    def __init__(self, geometry: WCSGeometry, hips_survey: HipsSurveyProperties, tile: HipsTile) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+    def __init__(self, geometry: WCSGeometry, hips_survey: HipsSurveyProperties, tile_format: str) -&amp;gt; None:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        self.geometry = geometry
&lt;span class="w"&gt; &lt;/span&gt;        self.hips_survey = hips_survey
&lt;span class="gd"&gt;-        self.tile = tile&lt;/span&gt;
&lt;span class="gi"&gt;+        self.tile_format = tile_format&lt;/span&gt;
&lt;span class="gi"&gt;+        self._tiles = None&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @property&lt;/span&gt;
&lt;span class="gi"&gt;+    def draw_hips_order(self) -&amp;gt; int:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Compute HiPS tile order matching a given image pixel size.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        # Sky image angular resolution (pixel size in degrees)&lt;/span&gt;
&lt;span class="gi"&gt;+        resolution = np.min(proj_plane_pixel_scales(self.geometry.wcs))&lt;/span&gt;
&lt;span class="gi"&gt;+        desired_order = get_hips_order_for_resolution(self.hips_survey.tile_width, resolution)&lt;/span&gt;
&lt;span class="gi"&gt;+        # Return the desired order, or the highest resolution available.&lt;/span&gt;
&lt;span class="gi"&gt;+        # Note that HiPS never has resolution less than 3,&lt;/span&gt;
&lt;span class="gi"&gt;+        # and that limit is handled in _get_hips_order_for_resolution&lt;/span&gt;
&lt;span class="gi"&gt;+        return np.min([desired_order, self.hips_survey.hips_order])&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @property&lt;/span&gt;
&lt;span class="gi"&gt;+    def tile_indices(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Get list of index values for HiPS tiles.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        return compute_healpix_pixel_indices(&lt;/span&gt;
&lt;span class="gi"&gt;+            wcs_geometry=self.geometry,&lt;/span&gt;
&lt;span class="gi"&gt;+            order=self.draw_hips_order,&lt;/span&gt;
&lt;span class="gi"&gt;+            healpix_frame=self.hips_survey.astropy_frame,&lt;/span&gt;
&lt;span class="gi"&gt;+        )&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    @property
&lt;span class="w"&gt; &lt;/span&gt;    def dst(self) -&amp;gt; np.ndarray:
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;Destination array for projective transform&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Destination array for projective transform.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        width = self.hips_survey.tile_width
&lt;span class="w"&gt; &lt;/span&gt;        return np.array(
&lt;span class="w"&gt; &lt;/span&gt;            [[width - 1, 0],
&lt;span class="gu"&gt;@@ -83,112 +93,74 @@ class SimpleTilePainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;             [0, 0]],
&lt;span class="w"&gt; &lt;/span&gt;        )

&lt;span class="gd"&gt;-    @property&lt;/span&gt;
&lt;span class="gd"&gt;-    def projection(self) -&amp;gt; ProjectiveTransform:&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;Estimate projective transformation on a HiPS tile&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-        corners = self.tile.meta.skycoord_corners.to_pixel(self.geometry.wcs)&lt;/span&gt;
&lt;span class="gi"&gt;+    def projection(self, tile: HipsTile) -&amp;gt; ProjectiveTransform:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Estimate projective transformation on a HiPS tile.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        corners = tile.meta.skycoord_corners.to_pixel(self.geometry.wcs)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        src = np.array(corners).T.reshape((4, 2))
&lt;span class="w"&gt; &lt;/span&gt;        dst = self.dst
&lt;span class="w"&gt; &lt;/span&gt;        pt = ProjectiveTransform()
&lt;span class="w"&gt; &lt;/span&gt;        pt.estimate(src, dst)
&lt;span class="w"&gt; &lt;/span&gt;        return pt

&lt;span class="gd"&gt;-    def warp_image(self) -&amp;gt; np.ndarray:&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;Warp a HiPS tile and a sky image&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+    def _fetch_tiles(self) -&amp;gt; &amp;#39;HipsTile&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Generator function to fetch HiPS tiles from a remote URL.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        for healpix_pixel_index in self.tile_indices:&lt;/span&gt;
&lt;span class="gi"&gt;+            tile_meta = HipsTileMeta(&lt;/span&gt;
&lt;span class="gi"&gt;+                order=self.draw_hips_order,&lt;/span&gt;
&lt;span class="gi"&gt;+                ipix=healpix_pixel_index,&lt;/span&gt;
&lt;span class="gi"&gt;+                frame=self.hips_survey.astropy_frame,&lt;/span&gt;
&lt;span class="gi"&gt;+                file_format=self.tile_format,&lt;/span&gt;
&lt;span class="gi"&gt;+            )&lt;/span&gt;
&lt;span class="gi"&gt;+            url = self.hips_survey.tile_access_url(order=self.draw_hips_order, ipix=healpix_pixel_index) + tile_meta.filename&lt;/span&gt;
&lt;span class="gi"&gt;+            tile = HipsTile.fetch(tile_meta, url)&lt;/span&gt;
&lt;span class="gi"&gt;+            yield tile&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @property&lt;/span&gt;
&lt;span class="gi"&gt;+    def tiles(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        if self._tiles is None:&lt;/span&gt;
&lt;span class="gi"&gt;+            self._tiles = list(self._fetch_tiles())&lt;/span&gt;
&lt;span class="gi"&gt;+        return self._tiles&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @property&lt;/span&gt;
&lt;span class="gi"&gt;+    def shape(self) -&amp;gt; Tuple[int]:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Shape of the output image.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        The shape will be two dimensional in case of FITS file format,&lt;/span&gt;
&lt;span class="gi"&gt;+        three dimensions (RGB) in case of JPG, and four channels (RGBA)&lt;/span&gt;
&lt;span class="gi"&gt;+        in case of PNG tile. We follow the same axis order and coordinate&lt;/span&gt;
&lt;span class="gi"&gt;+        conventions that are used by others for grayscale and RGB images.&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        if self.tile_format == &amp;#39;jpg&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+            return self.geometry.shape.height, self.geometry.shape.width, 3&lt;/span&gt;
&lt;span class="gi"&gt;+        elif self.tile_format == &amp;#39;png&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+            return self.geometry.shape.height, self.geometry.shape.width, 4&lt;/span&gt;
&lt;span class="gi"&gt;+        else:&lt;/span&gt;
&lt;span class="gi"&gt;+            return self.geometry.shape.height, self.geometry.shape.width&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def warp_image(self, tile: HipsTile) -&amp;gt; np.ndarray:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Warp a HiPS tile and a sky image.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        return warp(
&lt;span class="gd"&gt;-            self.tile.data,&lt;/span&gt;
&lt;span class="gd"&gt;-            self.projection,&lt;/span&gt;
&lt;span class="gi"&gt;+            tile.data,&lt;/span&gt;
&lt;span class="gi"&gt;+            self.projection(tile),&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            output_shape=self.geometry.shape,
&lt;span class="w"&gt; &lt;/span&gt;            preserve_range=True,
&lt;span class="w"&gt; &lt;/span&gt;        )

&lt;span class="gi"&gt;+    def draw_tiles(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Draw HiPS tiles onto an empty image.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        image = np.zeros(self.shape)&lt;/span&gt;
&lt;span class="gi"&gt;+        for tile in self.tiles:&lt;/span&gt;
&lt;span class="gi"&gt;+            image += self.warp_image(tile)&lt;/span&gt;
&lt;span class="gi"&gt;+        return image&lt;/span&gt;

&lt;span class="gd"&gt;-def fetch_tiles(healpix_pixel_indices: np.ndarray, order: int,&lt;/span&gt;
&lt;span class="gd"&gt;-                hips_survey: HipsSurveyProperties, tile_format: str) -&amp;gt; &amp;#39;HipsTile&amp;#39;:&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;quot;&amp;quot;&amp;quot;Fetch HiPS tiles from a remote URL.&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    Parameters&lt;/span&gt;
&lt;span class="gd"&gt;-    ----------&lt;/span&gt;
&lt;span class="gd"&gt;-    healpix_pixel_indices : np.ndarray&lt;/span&gt;
&lt;span class="gd"&gt;-        A list of HEALPix pixel indices&lt;/span&gt;
&lt;span class="gd"&gt;-    order : int&lt;/span&gt;
&lt;span class="gd"&gt;-        Order of the HEALPix map&lt;/span&gt;
&lt;span class="gd"&gt;-    hips_survey : HipsSurveyProperties&lt;/span&gt;
&lt;span class="gd"&gt;-        An object of HipsSurveyProperties&lt;/span&gt;
&lt;span class="gd"&gt;-    tile_format : `str`&lt;/span&gt;
&lt;span class="gd"&gt;-        Format of HiPS tile&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    Returns&lt;/span&gt;
&lt;span class="gd"&gt;-    -------&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;#39;HipsTile&amp;#39;&lt;/span&gt;
&lt;span class="gd"&gt;-        Returns an object of HipsTile&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-    for healpix_pixel_index in healpix_pixel_indices:&lt;/span&gt;
&lt;span class="gd"&gt;-        tile_meta = HipsTileMeta(&lt;/span&gt;
&lt;span class="gd"&gt;-            order=order,&lt;/span&gt;
&lt;span class="gd"&gt;-            ipix=healpix_pixel_index,&lt;/span&gt;
&lt;span class="gd"&gt;-            frame=hips_survey.astropy_frame,&lt;/span&gt;
&lt;span class="gd"&gt;-            file_format=tile_format,&lt;/span&gt;
&lt;span class="gd"&gt;-        )&lt;/span&gt;
&lt;span class="gd"&gt;-        tile = HipsTile.fetch(tile_meta, hips_survey.tile_access_url(order=order, ipix=healpix_pixel_index) + tile_meta.filename)&lt;/span&gt;
&lt;span class="gd"&gt;-        yield tile&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-def compute_matching_hips_order(geometry: WCSGeometry, hips_survey: HipsSurveyProperties) -&amp;gt; int:&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;quot;&amp;quot;&amp;quot;Compute HiPS tile order matching a given image pixel size.&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    Parameters&lt;/span&gt;
&lt;span class="gd"&gt;-    ----------&lt;/span&gt;
&lt;span class="gd"&gt;-    geometry : WCSGeometry&lt;/span&gt;
&lt;span class="gd"&gt;-        Geometry of the output image&lt;/span&gt;
&lt;span class="gd"&gt;-    hips_survey : HipsSurveyProperties&lt;/span&gt;
&lt;span class="gd"&gt;-        An object of HipsSurveyProperties&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    Returns&lt;/span&gt;
&lt;span class="gd"&gt;-    -------&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;#39;int&amp;#39;&lt;/span&gt;
&lt;span class="gd"&gt;-        Returns HiPS order&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    Examples&lt;/span&gt;
&lt;span class="gd"&gt;-    --------&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;gt;&amp;gt;&amp;gt; from hips.draw import compute_matching_hips_order&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;gt;&amp;gt;&amp;gt; from astropy.coordinates import SkyCoord&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;gt;&amp;gt;&amp;gt; url = &amp;#39;http://alasky.unistra.fr/DSS/DSS2Merged/properties&amp;#39;&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;gt;&amp;gt;&amp;gt; hips_survey = HipsSurveyProperties.fetch(url)&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;gt;&amp;gt;&amp;gt; geometry = WCSGeometry.create_simple(&lt;/span&gt;
&lt;span class="gd"&gt;-    ...     skydir=SkyCoord(0, 0, unit=&amp;#39;deg&amp;#39;, frame=&amp;#39;icrs&amp;#39;),&lt;/span&gt;
&lt;span class="gd"&gt;-    ...     width=2000, height=1000, fov=&amp;quot;3 deg&amp;quot;,&lt;/span&gt;
&lt;span class="gd"&gt;-    ...     coordsys=&amp;#39;icrs&amp;#39;, projection=&amp;#39;AIT&amp;#39;&lt;/span&gt;
&lt;span class="gd"&gt;-    ... )&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;gt;&amp;gt;&amp;gt; compute_matching_hips_order(geometry, hips_survey)&lt;/span&gt;
&lt;span class="gd"&gt;-    7&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    # Sky image angular resolution (pixel size in degree)&lt;/span&gt;
&lt;span class="gd"&gt;-    resolution = np.min(proj_plane_pixel_scales(geometry.wcs))&lt;/span&gt;
&lt;span class="gd"&gt;-    desired_order = _get_hips_order_for_resolution(hips_survey.tile_width, resolution)&lt;/span&gt;
&lt;span class="gd"&gt;-    # Return the desired order, or the highest resolution available.&lt;/span&gt;
&lt;span class="gd"&gt;-    # Note that HiPS never has resolution less than 3,&lt;/span&gt;
&lt;span class="gd"&gt;-    # and that limit is handled in _get_hips_order_for_resolution&lt;/span&gt;
&lt;span class="gd"&gt;-    return np.min([desired_order, hips_survey.hips_order])&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-def _get_hips_order_for_resolution(tile_width, resolution):&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;quot;&amp;quot;&amp;quot;Finding the best HiPS order by looping through all possible options.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-    tile_order = np.log2(tile_width)&lt;/span&gt;
&lt;span class="gd"&gt;-    full_sphere_area = 4 * np.pi * np.square(180 / np.pi)&lt;/span&gt;
&lt;span class="gd"&gt;-    # 29 is the maximum order supported by healpy and 3 is the minimum order&lt;/span&gt;
&lt;span class="gd"&gt;-    for candidate_tile_order in range(3, 29 + 1):&lt;/span&gt;
&lt;span class="gd"&gt;-        tile_resolution = np.sqrt(full_sphere_area / 12 / 4 ** (candidate_tile_order + tile_order))&lt;/span&gt;
&lt;span class="gd"&gt;-        # Finding the smaller tile order with a resolution equal or better than geometric resolution&lt;/span&gt;
&lt;span class="gd"&gt;-        if tile_resolution &amp;lt;= resolution:&lt;/span&gt;
&lt;span class="gd"&gt;-            break&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    return candidate_tile_order&lt;/span&gt;
&lt;span class="gi"&gt;+    def run(self) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Run all steps of the naive algorithm.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        self.image = self.draw_tiles()&lt;/span&gt;


&lt;span class="w"&gt; &lt;/span&gt;def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties, tile_format: str) -&amp;gt; np.ndarray:
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;Make sky image: fetch tiles and draw.
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    The example for this can be found on the :ref:`gs` page.

&lt;span class="w"&gt; &lt;/span&gt;    Parameters
&lt;span class="gu"&gt;@@ -202,18 +174,11 @@ def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties, til&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    Returns
&lt;span class="w"&gt; &lt;/span&gt;    -------
&lt;span class="gd"&gt;-    data : `~numpy.ndarray`&lt;/span&gt;
&lt;span class="gi"&gt;+    image : `~numpy.ndarray`&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        Output image pixels
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-    order = compute_matching_hips_order(geometry, hips_survey)&lt;/span&gt;
&lt;span class="gd"&gt;-    healpix_pixel_indices = compute_healpix_pixel_indices(&lt;/span&gt;
&lt;span class="gd"&gt;-        wcs_geometry=geometry,&lt;/span&gt;
&lt;span class="gd"&gt;-        order=order,&lt;/span&gt;
&lt;span class="gd"&gt;-        healpix_frame=hips_survey.astropy_frame,&lt;/span&gt;
&lt;span class="gd"&gt;-    )&lt;/span&gt;
&lt;span class="gd"&gt;-    # TODO: this isn&amp;#39;t a good API. Will become better when we have a cache.&lt;/span&gt;
&lt;span class="gd"&gt;-    tiles = fetch_tiles(healpix_pixel_indices, order, hips_survey, tile_format)&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    image_data = draw_sky_image(geometry, tiles, hips_survey, tile_format)&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    return image_data&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    painter = SimpleTilePainter(geometry, hips_survey, tile_format)&lt;/span&gt;
&lt;span class="gi"&gt;+    painter.run()&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    return painter.image&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/draw/tests/test_simple.py b/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index 4614d9e..7a9dba0 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,102 +1,76 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="w"&gt; &lt;/span&gt;import pytest
&lt;span class="w"&gt; &lt;/span&gt;import numpy as np
&lt;span class="gd"&gt;-import healpy as hp&lt;/span&gt;
&lt;span class="gd"&gt;-from astropy.coordinates import SkyCoord&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from numpy.testing import assert_allclose
&lt;span class="gi"&gt;+from astropy.coordinates import SkyCoord&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from astropy.tests.helper import remote_data
&lt;span class="gd"&gt;-from ..simple import make_sky_image, draw_sky_image, compute_matching_hips_order, _get_hips_order_for_resolution&lt;/span&gt;
&lt;span class="gd"&gt;-from ...tiles import HipsSurveyProperties, HipsTileMeta, HipsTile&lt;/span&gt;
&lt;span class="gd"&gt;-from ...utils import WCSGeometry&lt;/span&gt;
&lt;span class="gd"&gt;-from ...utils.testing import get_hips_extra_file, make_test_wcs_geometry, requires_hips_extra&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-def get_test_tiles(file_format, survey, order, ipix_list):&lt;/span&gt;
&lt;span class="gd"&gt;-    filename = get_hips_extra_file(&amp;#39;datasets/samples/&amp;#39; + survey + &amp;#39;/properties&amp;#39;)&lt;/span&gt;
&lt;span class="gd"&gt;-    hips_survey = HipsSurveyProperties.read(filename)&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    tiles = []&lt;/span&gt;
&lt;span class="gd"&gt;-    for ipix in ipix_list:&lt;/span&gt;
&lt;span class="gd"&gt;-        tiles.append(HipsTile.read(&lt;/span&gt;
&lt;span class="gd"&gt;-            meta=HipsTileMeta(order=order, ipix=ipix, file_format=file_format, frame=hips_survey.astropy_frame),&lt;/span&gt;
&lt;span class="gd"&gt;-            full_path=get_hips_extra_file(&amp;#39;datasets/samples/&amp;#39; + survey + hips_survey.tile_path(order=order, ipix=ipix, tile_format=file_format)),&lt;/span&gt;
&lt;span class="gd"&gt;-        ))&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    return tiles&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-draw_sky_image_pars = [&lt;/span&gt;
&lt;span class="gd"&gt;-    dict(file_format=&amp;#39;fits&amp;#39;, shape=(1000, 2000), survey=&amp;#39;DSS2Red&amp;#39;, data_1=2866.0101409848185,&lt;/span&gt;
&lt;span class="gd"&gt;-         data_2=2563.6916727348043, data_sum=4575235421.512643, order=3, ipix_list=[450, 451]),&lt;/span&gt;
&lt;span class="gd"&gt;-    dict(file_format=&amp;#39;jpg&amp;#39;, shape=(1000, 2000, 3), survey=&amp;#39;DSS2Red&amp;#39;, data_1=[13.040878, 13.040878, 13.040878],&lt;/span&gt;
&lt;span class="gd"&gt;-         data_2=[17.235874, 17.235874, 17.235874], data_sum=243177268.56158745, order=3, ipix_list=[450, 451]),&lt;/span&gt;
&lt;span class="gd"&gt;-    dict(file_format=&amp;#39;png&amp;#39;, shape=(1000, 2000, 4), survey=&amp;#39;AKARI-FIS&amp;#39;, data_1=[254., 254., 254., 255.],&lt;/span&gt;
&lt;span class="gd"&gt;-         data_2=[254., 254., 254., 255.], data_sum=946809963.7487414, order=3, ipix_list=[450, 451])&lt;/span&gt;
&lt;span class="gi"&gt;+from ...tiles import HipsSurveyProperties&lt;/span&gt;
&lt;span class="gi"&gt;+from ..simple import make_sky_image, SimpleTilePainter&lt;/span&gt;
&lt;span class="gi"&gt;+from ...utils.wcs import WCSGeometry&lt;/span&gt;
&lt;span class="gi"&gt;+from ...utils.testing import make_test_wcs_geometry, requires_hips_extra&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+make_sky_image_pars = [&lt;/span&gt;
&lt;span class="gi"&gt;+    dict(file_format=&amp;#39;fits&amp;#39;, shape=(1000, 2000), url=&amp;#39;http://alasky.unistra.fr/DSS/DSS2Merged/properties&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+         data_1=2213.30874796, data_2=2296.93885940, data_sum=8757489268.044867),&lt;/span&gt;
&lt;span class="gi"&gt;+    dict(file_format=&amp;#39;jpg&amp;#39;, shape=(1000, 2000, 3), url=&amp;#39;https://raw.githubusercontent.com/hipspy/hips-extra/master/datasets/samples/FermiColor/properties&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+         data_1=[145.388459, 98.579295, 49.792577], data_2=[146.197811, 99.531895, 56.889927], data_sum=813159920.0305891),&lt;/span&gt;
&lt;span class="gi"&gt;+    dict(file_format=&amp;#39;png&amp;#39;, shape=(1000, 2000, 4), url=&amp;#39;https://raw.githubusercontent.com/hipspy/hips-extra/master/datasets/samples/AKARI-FIS/properties&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+         data_1=[249.064796, 237.805612, 190.408181, 255.], data_2=[249.013527, 238.79403, 195.793398, 255.], data_sum=1645783166.1630714)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;]


&lt;span class="w"&gt; &lt;/span&gt;@remote_data
&lt;span class="gd"&gt;-@requires_hips_extra()&lt;/span&gt;
&lt;span class="gd"&gt;-@pytest.mark.parametrize(&amp;#39;pars&amp;#39;, draw_sky_image_pars)&lt;/span&gt;
&lt;span class="gd"&gt;-def test_draw_sky_image(pars):&lt;/span&gt;
&lt;span class="gi"&gt;+@pytest.mark.parametrize(&amp;#39;pars&amp;#39;, make_sky_image_pars)&lt;/span&gt;
&lt;span class="gi"&gt;+def test_make_sky_image(pars):&lt;/span&gt;
&lt;span class="gi"&gt;+    hips_survey = HipsSurveyProperties.fetch(url=pars[&amp;#39;url&amp;#39;])&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    geometry = make_test_wcs_geometry(case=2)
&lt;span class="gd"&gt;-    tiles = get_test_tiles(file_format=pars[&amp;#39;file_format&amp;#39;], survey=pars[&amp;#39;survey&amp;#39;], order=pars[&amp;#39;order&amp;#39;], ipix_list=pars[&amp;#39;ipix_list&amp;#39;])&lt;/span&gt;
&lt;span class="gd"&gt;-    url = &amp;#39;https://raw.githubusercontent.com/hipspy/hips-extra/master/datasets/samples/&amp;#39; + pars[&amp;#39;survey&amp;#39;] + &amp;#39;/properties&amp;#39;&lt;/span&gt;
&lt;span class="gd"&gt;-    hips_survey = HipsSurveyProperties.fetch(url)&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    data = draw_sky_image(geometry=geometry, tiles=tiles, hips_survey=hips_survey, tile_format=pars[&amp;#39;file_format&amp;#39;])&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    assert data.shape == pars[&amp;#39;shape&amp;#39;]&lt;/span&gt;
&lt;span class="gd"&gt;-    assert data.dtype == np.float64&lt;/span&gt;
&lt;span class="gd"&gt;-    assert_allclose(np.sum(data), pars[&amp;#39;data_sum&amp;#39;])&lt;/span&gt;
&lt;span class="gd"&gt;-    assert_allclose(data[400, 500], pars[&amp;#39;data_1&amp;#39;])&lt;/span&gt;
&lt;span class="gd"&gt;-    assert_allclose(data[400, 501], pars[&amp;#39;data_2&amp;#39;])&lt;/span&gt;
&lt;span class="gi"&gt;+    image = make_sky_image(geometry=geometry, hips_survey=hips_survey, tile_format=pars[&amp;#39;file_format&amp;#39;])&lt;/span&gt;
&lt;span class="gi"&gt;+    assert image.shape == pars[&amp;#39;shape&amp;#39;]&lt;/span&gt;
&lt;span class="gi"&gt;+    assert image.dtype == np.float64&lt;/span&gt;
&lt;span class="gi"&gt;+    assert_allclose(np.sum(image), pars[&amp;#39;data_sum&amp;#39;])&lt;/span&gt;
&lt;span class="gi"&gt;+    assert_allclose(image[200, 994], pars[&amp;#39;data_1&amp;#39;])&lt;/span&gt;
&lt;span class="gi"&gt;+    assert_allclose(image[200, 995], pars[&amp;#39;data_2&amp;#39;])&lt;/span&gt;


&lt;span class="w"&gt; &lt;/span&gt;@remote_data
&lt;span class="gd"&gt;-def test_make_sky_image():&lt;/span&gt;
&lt;span class="gd"&gt;-    # The same example is used in the high level docs getting started page&lt;/span&gt;
&lt;span class="gd"&gt;-    url = &amp;#39;http://alasky.unistra.fr/DSS/DSS2Merged/properties&amp;#39;&lt;/span&gt;
&lt;span class="gd"&gt;-    hips_survey = HipsSurveyProperties.fetch(url)&lt;/span&gt;
&lt;span class="gd"&gt;-    geometry = make_test_wcs_geometry(case=2)&lt;/span&gt;
&lt;span class="gd"&gt;-    data = make_sky_image(geometry=geometry, hips_survey=hips_survey, tile_format=&amp;#39;fits&amp;#39;)&lt;/span&gt;
&lt;span class="gd"&gt;-    assert data.shape == geometry.shape&lt;/span&gt;
&lt;span class="gd"&gt;-    assert data.dtype == np.float64&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    assert_allclose(np.sum(data), 8757489268.044867)&lt;/span&gt;
&lt;span class="gd"&gt;-    assert_allclose(data[200, 994], 2213.30874796)&lt;/span&gt;
&lt;span class="gd"&gt;-    assert_allclose(data[200, 995], 2296.93885940)&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-hips_order_pars = [&lt;/span&gt;
&lt;span class="gd"&gt;-    dict(order=7, fov=&amp;quot;3 deg&amp;quot;),&lt;/span&gt;
&lt;span class="gd"&gt;-    dict(order=5, fov=&amp;quot;10 deg&amp;quot;),&lt;/span&gt;
&lt;span class="gd"&gt;-    dict(order=4, fov=&amp;quot;15 deg&amp;quot;),&lt;/span&gt;
&lt;span class="gd"&gt;-]&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-@requires_hips_extra()&lt;/span&gt;
&lt;span class="gd"&gt;-@pytest.mark.parametrize(&amp;#39;pars&amp;#39;, hips_order_pars)&lt;/span&gt;
&lt;span class="gd"&gt;-def test_compute_matching_hips_order(pars):&lt;/span&gt;
&lt;span class="gd"&gt;-    full_path = get_hips_extra_file(&amp;#39;datasets/samples/2MASS6XH/properties&amp;#39;)&lt;/span&gt;
&lt;span class="gd"&gt;-    hips_survey = HipsSurveyProperties.read(filename=full_path)&lt;/span&gt;
&lt;span class="gd"&gt;-    geometry = WCSGeometry.create_simple(&lt;/span&gt;
&lt;span class="gd"&gt;-        skydir=SkyCoord(0, 0, unit=&amp;#39;deg&amp;#39;, frame=&amp;#39;icrs&amp;#39;),&lt;/span&gt;
&lt;span class="gd"&gt;-        width=2000, height=1000, fov=pars[&amp;#39;fov&amp;#39;],&lt;/span&gt;
&lt;span class="gd"&gt;-        coordsys=&amp;#39;icrs&amp;#39;, projection=&amp;#39;AIT&amp;#39;&lt;/span&gt;
&lt;span class="gd"&gt;-    )&lt;/span&gt;
&lt;span class="gd"&gt;-    assert compute_matching_hips_order(geometry, hips_survey) == pars[&amp;#39;order&amp;#39;]&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-get_hips_order_for_resolution_pars = [&lt;/span&gt;
&lt;span class="gd"&gt;-    dict(tile_width=512, resolution=0.01232, resolution_res=0.06395791924665553, order=4),&lt;/span&gt;
&lt;span class="gd"&gt;-    dict(tile_width=256, resolution=0.0016022, resolution_res=0.003997369952915971, order=8),&lt;/span&gt;
&lt;span class="gd"&gt;-    dict(tile_width=128, resolution=0.00009032, resolution_res=0.00012491781102862408, order=13),&lt;/span&gt;
&lt;span class="gd"&gt;-]&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-@pytest.mark.parametrize(&amp;#39;pars&amp;#39;, get_hips_order_for_resolution_pars)&lt;/span&gt;
&lt;span class="gd"&gt;-def test_get_hips_order_for_resolution(pars):&lt;/span&gt;
&lt;span class="gd"&gt;-    hips_order = _get_hips_order_for_resolution(pars[&amp;#39;tile_width&amp;#39;], pars[&amp;#39;resolution&amp;#39;])&lt;/span&gt;
&lt;span class="gd"&gt;-    assert hips_order == pars[&amp;#39;order&amp;#39;]&lt;/span&gt;
&lt;span class="gd"&gt;-    hips_resolution = hp.nside2resol(hp.order2nside(hips_order))&lt;/span&gt;
&lt;span class="gd"&gt;-    assert_allclose(hips_resolution, pars[&amp;#39;resolution_res&amp;#39;])&lt;/span&gt;
&lt;span class="gd"&gt;-# TODO: add tests for SimpleTilePainter with asserts on the intermediate computed things.&lt;/span&gt;
&lt;span class="gi"&gt;+class TestSimpleTilePainter:&lt;/span&gt;
&lt;span class="gi"&gt;+    @classmethod&lt;/span&gt;
&lt;span class="gi"&gt;+    def setup_class(cls):&lt;/span&gt;
&lt;span class="gi"&gt;+        url = &amp;#39;http://alasky.unistra.fr/DSS/DSS2Merged/properties&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        cls.hips_survey = HipsSurveyProperties.fetch(url)&lt;/span&gt;
&lt;span class="gi"&gt;+        cls.geometry = WCSGeometry.create_simple(&lt;/span&gt;
&lt;span class="gi"&gt;+            skydir=SkyCoord(0, 0, unit=&amp;#39;deg&amp;#39;, frame=&amp;#39;icrs&amp;#39;),&lt;/span&gt;
&lt;span class="gi"&gt;+            width=2000, height=1000, fov=&amp;quot;3 deg&amp;quot;,&lt;/span&gt;
&lt;span class="gi"&gt;+            coordsys=&amp;#39;icrs&amp;#39;, projection=&amp;#39;AIT&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        )&lt;/span&gt;
&lt;span class="gi"&gt;+        cls.simple_tile_painter = SimpleTilePainter(cls.geometry, cls.hips_survey, &amp;#39;fits&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_draw_hips_order(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        assert self.simple_tile_painter.draw_hips_order == 7&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_shape(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        assert self.simple_tile_painter.shape == (1000, 2000)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_tile_indices(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        assert list(self.simple_tile_painter.tile_indices)[:4] == [69623, 69627, 69628, 69629]&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    draw_hips_order_pars = [&lt;/span&gt;
&lt;span class="gi"&gt;+        dict(order=7, fov=&amp;quot;3 deg&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+        dict(order=5, fov=&amp;quot;10 deg&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+        dict(order=4, fov=&amp;quot;15 deg&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+    ]&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @requires_hips_extra()&lt;/span&gt;
&lt;span class="gi"&gt;+    @pytest.mark.parametrize(&amp;#39;pars&amp;#39;, draw_hips_order_pars)&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_compute_matching_hips_order(self, pars):&lt;/span&gt;
&lt;span class="gi"&gt;+        geometry = WCSGeometry.create_simple(&lt;/span&gt;
&lt;span class="gi"&gt;+            skydir=SkyCoord(0, 0, unit=&amp;#39;deg&amp;#39;, frame=&amp;#39;icrs&amp;#39;),&lt;/span&gt;
&lt;span class="gi"&gt;+            width=2000, height=1000, fov=pars[&amp;#39;fov&amp;#39;],&lt;/span&gt;
&lt;span class="gi"&gt;+            coordsys=&amp;#39;icrs&amp;#39;, projection=&amp;#39;AIT&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        )&lt;/span&gt;
&lt;span class="gi"&gt;+        simple_tile_painter = SimpleTilePainter(geometry, self.hips_survey, &amp;#39;fits&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        assert simple_tile_painter.draw_hips_order == pars[&amp;#39;order&amp;#39;]&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_run(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        self.simple_tile_painter.run()&lt;/span&gt;
&lt;span class="gi"&gt;+        assert_allclose(self.simple_tile_painter.image[200, 994], 2120.9609175)&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/utils/healpix.py b/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gh"&gt;index 6e8213f..50e314e 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -13,7 +13,8 @@ from .wcs import WCSGeometry&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;__all__ = [
&lt;span class="w"&gt; &lt;/span&gt;    &amp;#39;boundaries&amp;#39;,
&lt;span class="gd"&gt;-    &amp;#39;compute_healpix_pixel_indices&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;#39;compute_healpix_pixel_indices&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;#39;get_hips_order_for_resolution&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;]

&lt;span class="w"&gt; &lt;/span&gt;__doctest_skip__ = [&amp;#39;boundaries&amp;#39;, &amp;#39;compute_healpix_pixel_indices&amp;#39;]
&lt;span class="gu"&gt;@@ -109,3 +110,30 @@ def compute_healpix_pixel_indices(wcs_geometry: WCSGeometry, order: int, healpix&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    theta, phi = _skycoord_to_theta_phi(pixel_coords)
&lt;span class="w"&gt; &lt;/span&gt;    ipix = hp.ang2pix(nside, theta, phi, nest=True)
&lt;span class="w"&gt; &lt;/span&gt;    return np.unique(ipix)
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+def get_hips_order_for_resolution(tile_width: int, resolution: int) -&amp;gt; int:&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;Find the best HiPS order by looping through all possible options.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    Parameters&lt;/span&gt;
&lt;span class="gi"&gt;+    ----------&lt;/span&gt;
&lt;span class="gi"&gt;+    tile_width : int&lt;/span&gt;
&lt;span class="gi"&gt;+        HiPS tile width&lt;/span&gt;
&lt;span class="gi"&gt;+    resolution : int&lt;/span&gt;
&lt;span class="gi"&gt;+        Sky image angular resolution (pixel size in degrees)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    Returns&lt;/span&gt;
&lt;span class="gi"&gt;+    -------&lt;/span&gt;
&lt;span class="gi"&gt;+    candidate_tile_order : int&lt;/span&gt;
&lt;span class="gi"&gt;+        Best HiPS tile order&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+    tile_order = np.log2(tile_width)&lt;/span&gt;
&lt;span class="gi"&gt;+    full_sphere_area = 4 * np.pi * np.square(180 / np.pi)&lt;/span&gt;
&lt;span class="gi"&gt;+    # 29 is the maximum order supported by healpy and 3 is the minimum order&lt;/span&gt;
&lt;span class="gi"&gt;+    for candidate_tile_order in range(3, 29 + 1):&lt;/span&gt;
&lt;span class="gi"&gt;+        tile_resolution = np.sqrt(full_sphere_area / 12 / 4 ** (candidate_tile_order + tile_order))&lt;/span&gt;
&lt;span class="gi"&gt;+        # Finding the smaller tile order with a resolution equal to or better than geometric resolution&lt;/span&gt;
&lt;span class="gi"&gt;+        if tile_resolution &amp;lt;= resolution:&lt;/span&gt;
&lt;span class="gi"&gt;+            break&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    return candidate_tile_order&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/utils/tests/test_healpix.py b/hips/utils/tests/test_healpix.py&lt;/span&gt;
&lt;span class="gh"&gt;index aff6dc3..6c8b231 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/tests/test_healpix.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/tests/test_healpix.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -4,7 +4,7 @@ import numpy as np&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from numpy.testing import assert_allclose
&lt;span class="w"&gt; &lt;/span&gt;from astropy.coordinates import SkyCoord
&lt;span class="w"&gt; &lt;/span&gt;import healpy as hp
&lt;span class="gd"&gt;-from ..healpix import boundaries, compute_healpix_pixel_indices&lt;/span&gt;
&lt;span class="gi"&gt;+from ..healpix import boundaries, compute_healpix_pixel_indices, get_hips_order_for_resolution&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from ..testing import make_test_wcs_geometry


&lt;span class="gu"&gt;@@ -33,3 +33,17 @@ def test_wcs_healpix_pixel_indices(pars):&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    geometry = make_test_wcs_geometry(case=2)
&lt;span class="w"&gt; &lt;/span&gt;    healpix_pixel_indices = compute_healpix_pixel_indices(geometry, order=3, healpix_frame=pars[&amp;#39;frame&amp;#39;])
&lt;span class="w"&gt; &lt;/span&gt;    assert list(healpix_pixel_indices) == pars[&amp;#39;ipix&amp;#39;]
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+hips_order_for_resolution_pars = [&lt;/span&gt;
&lt;span class="gi"&gt;+    dict(tile_width=512, resolution=0.01232, resolution_res=0.06395791924665553, order=4),&lt;/span&gt;
&lt;span class="gi"&gt;+    dict(tile_width=256, resolution=0.0016022, resolution_res=0.003997369952915971, order=8),&lt;/span&gt;
&lt;span class="gi"&gt;+    dict(tile_width=128, resolution=0.00009032, resolution_res=0.00012491781102862408, order=13),&lt;/span&gt;
&lt;span class="gi"&gt;+]&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+@pytest.mark.parametrize(&amp;#39;pars&amp;#39;, hips_order_for_resolution_pars)&lt;/span&gt;
&lt;span class="gi"&gt;+def test_get_hips_order_for_resolution(pars):&lt;/span&gt;
&lt;span class="gi"&gt;+    hips_order = get_hips_order_for_resolution(pars[&amp;#39;tile_width&amp;#39;], pars[&amp;#39;resolution&amp;#39;])&lt;/span&gt;
&lt;span class="gi"&gt;+    assert hips_order == pars[&amp;#39;order&amp;#39;]&lt;/span&gt;
&lt;span class="gi"&gt;+    hips_resolution = hp.nside2resol(hp.order2nside(hips_order))&lt;/span&gt;
&lt;span class="gi"&gt;+    assert_allclose(hips_resolution, pars[&amp;#39;resolution_res&amp;#39;])&lt;/span&gt;

commit e2541ae378982e9261dde09ed7b389a8d4b1b47b
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Wed Jul 12 12:07:34 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Add plot in high level docs

&lt;span class="gh"&gt;diff --git a/docs/getting_started.rst b/docs/getting_started.rst&lt;/span&gt;
&lt;span class="gh"&gt;index 05e9d49..bf8ff0d 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/docs/getting_started.rst&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/docs/getting_started.rst&lt;/span&gt;
&lt;span class="gu"&gt;@@ -54,7 +54,9 @@ Now you can then save the sky image to local disk e.g. FITS file format::&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;or plot and analyse the sky image however you like.

&lt;span class="gd"&gt;-TODO: show how to plot the image with `astropy.visualization.wcsaxes`.&lt;/span&gt;
&lt;span class="gi"&gt;+If you execute the example above, you will get this sky image which was plotted using `astropy.visualization.wcsaxes`&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+.. plot:: plot_fits.py&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;HiPS data
&lt;span class="w"&gt; &lt;/span&gt;---------
&lt;span class="gh"&gt;diff --git a/docs/plot_fits.py b/docs/plot_fits.py&lt;/span&gt;
new file mode 100644
&lt;span class="gh"&gt;index 0000000..67b03d9&lt;/span&gt;
&lt;span class="gd"&gt;--- /dev/null&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/docs/plot_fits.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -0,0 +1,20 @@&lt;/span&gt;
&lt;span class="gi"&gt;+&amp;quot;&amp;quot;&amp;quot;This script plots the all-sky image following the example on getting started page&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+import matplotlib.pyplot as plt&lt;/span&gt;
&lt;span class="gi"&gt;+from astropy.coordinates import SkyCoord&lt;/span&gt;
&lt;span class="gi"&gt;+from astropy.visualization.mpl_normalize import simple_norm&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+from hips import HipsSurveyProperties&lt;/span&gt;
&lt;span class="gi"&gt;+from hips import make_sky_image&lt;/span&gt;
&lt;span class="gi"&gt;+from hips.utils import WCSGeometry&lt;/span&gt;
&lt;span class="gi"&gt;+import numpy as np&lt;/span&gt;
&lt;span class="gi"&gt;+url = &amp;#39;http://alasky.unistra.fr/DSS/DSS2Merged/properties&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+hips_survey = HipsSurveyProperties.fetch(url)&lt;/span&gt;
&lt;span class="gi"&gt;+geometry = WCSGeometry.create_simple(&lt;/span&gt;
&lt;span class="gi"&gt;+    skydir=SkyCoord(0, 0, unit=&amp;#39;deg&amp;#39;, frame=&amp;#39;galactic&amp;#39;),&lt;/span&gt;
&lt;span class="gi"&gt;+    width=2000, height=1000, fov=&amp;quot;3 deg&amp;quot;,&lt;/span&gt;
&lt;span class="gi"&gt;+    coordsys=&amp;#39;galactic&amp;#39;, projection=&amp;#39;AIT&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+)&lt;/span&gt;
&lt;span class="gi"&gt;+data = make_sky_image(geometry=geometry, hips_survey=hips_survey, tile_format=&amp;#39;fits&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+ax = plt.subplot(projection=geometry.wcs)&lt;/span&gt;
&lt;span class="gi"&gt;+norm = simple_norm(data, &amp;#39;sqrt&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+ax.imshow(data, origin=&amp;#39;lower&amp;#39;, norm=norm, cmap=&amp;#39;gray&amp;#39;)&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/setup.cfg b/setup.cfg&lt;/span&gt;
&lt;span class="gh"&gt;index 36b3fd1..02f3659 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/setup.cfg&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/setup.cfg&lt;/span&gt;
&lt;span class="gu"&gt;@@ -11,6 +11,7 @@ show-response = 1&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;minversion = 3.0
&lt;span class="w"&gt; &lt;/span&gt;norecursedirs = build docs/_build
&lt;span class="w"&gt; &lt;/span&gt;doctest_plus = enabled
&lt;span class="gi"&gt;+doctest_norecursedirs = docs/plot_fits.py&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;[ah_bootstrap]
&lt;span class="w"&gt; &lt;/span&gt;auto_use = True

commit af4eb6f7cac27c06f60468912388dd2f33a3b851
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Wed Jul 12 10:17:36 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Fix tile width height issue

&lt;span class="gh"&gt;diff --git a/docs/getting_started.rst b/docs/getting_started.rst&lt;/span&gt;
&lt;span class="gh"&gt;index 9248dd4..05e9d49 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/docs/getting_started.rst&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/docs/getting_started.rst&lt;/span&gt;
&lt;span class="gu"&gt;@@ -41,7 +41,7 @@ To make a sky image with the `hips` package, follow the following three steps:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    from hips.draw import make_sky_image

&lt;span class="gd"&gt;-    data = make_sky_image(geometry, hips_survey)&lt;/span&gt;
&lt;span class="gi"&gt;+    data = make_sky_image(geometry, hips_survey, &amp;#39;fits&amp;#39;)&lt;/span&gt;


&lt;span class="w"&gt; &lt;/span&gt;That&amp;#39;s it. Go ahead and try it out for your favourite sky region and survey.
&lt;span class="gh"&gt;diff --git a/hips/draw/simple.py b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index c6d2ad5..2664a59 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -41,11 +41,11 @@ def draw_sky_image(geometry: WCSGeometry, tiles: Generator[HipsTile, Any, Any],&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        Returns a numpy array containing all HiPS tiles projected onto it
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;    if tile_format == &amp;#39;jpg&amp;#39;:
&lt;span class="gd"&gt;-        shape = (geometry.shape.width, geometry.shape.height, 3)&lt;/span&gt;
&lt;span class="gi"&gt;+        shape = (geometry.shape.height, geometry.shape.width, 3)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    elif tile_format == &amp;#39;png&amp;#39;:
&lt;span class="gd"&gt;-        shape = (geometry.shape.width, geometry.shape.height, 4)&lt;/span&gt;
&lt;span class="gi"&gt;+        shape = (geometry.shape.height, geometry.shape.width, 4)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    else:
&lt;span class="gd"&gt;-        shape = (geometry.shape.width, geometry.shape.height)&lt;/span&gt;
&lt;span class="gi"&gt;+        shape = (geometry.shape.height, geometry.shape.width)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    image = np.zeros(shape)
&lt;span class="w"&gt; &lt;/span&gt;    for tile in tiles:
&lt;span class="w"&gt; &lt;/span&gt;        painter = SimpleTilePainter(geometry, hips_survey, tile)
&lt;span class="gh"&gt;diff --git a/hips/draw/tests/test_simple.py b/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index b910517..4614d9e 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -26,12 +26,12 @@ def get_test_tiles(file_format, survey, order, ipix_list):&lt;/span&gt;


&lt;span class="w"&gt; &lt;/span&gt;draw_sky_image_pars = [
&lt;span class="gd"&gt;-    dict(file_format=&amp;#39;fits&amp;#39;, shape=(2000, 1000), survey=&amp;#39;DSS2Red&amp;#39;, data_1=2866.0101409848185,&lt;/span&gt;
&lt;span class="gd"&gt;-         data_2=2563.6916727348043, data_sum=2992643842.729775, order=3, ipix_list=[450, 451]),&lt;/span&gt;
&lt;span class="gd"&gt;-    dict(file_format=&amp;#39;jpg&amp;#39;, shape=(2000, 1000, 3), survey=&amp;#39;DSS2Red&amp;#39;, data_1=[13.040878, 13.040878, 13.040878],&lt;/span&gt;
&lt;span class="gd"&gt;-         data_2=[17.235874, 17.235874, 17.235874], data_sum=155233709.20236143, order=3, ipix_list=[450, 451]),&lt;/span&gt;
&lt;span class="gd"&gt;-    dict(file_format=&amp;#39;png&amp;#39;, shape=(2000, 1000, 4), survey=&amp;#39;AKARI-FIS&amp;#39;, data_1=[254., 254., 254., 255.],&lt;/span&gt;
&lt;span class="gd"&gt;-         data_2=[254., 254., 254., 255.], data_sum=586208559.2450126, order=3, ipix_list=[450, 451])&lt;/span&gt;
&lt;span class="gi"&gt;+    dict(file_format=&amp;#39;fits&amp;#39;, shape=(1000, 2000), survey=&amp;#39;DSS2Red&amp;#39;, data_1=2866.0101409848185,&lt;/span&gt;
&lt;span class="gi"&gt;+         data_2=2563.6916727348043, data_sum=4575235421.512643, order=3, ipix_list=[450, 451]),&lt;/span&gt;
&lt;span class="gi"&gt;+    dict(file_format=&amp;#39;jpg&amp;#39;, shape=(1000, 2000, 3), survey=&amp;#39;DSS2Red&amp;#39;, data_1=[13.040878, 13.040878, 13.040878],&lt;/span&gt;
&lt;span class="gi"&gt;+         data_2=[17.235874, 17.235874, 17.235874], data_sum=243177268.56158745, order=3, ipix_list=[450, 451]),&lt;/span&gt;
&lt;span class="gi"&gt;+    dict(file_format=&amp;#39;png&amp;#39;, shape=(1000, 2000, 4), survey=&amp;#39;AKARI-FIS&amp;#39;, data_1=[254., 254., 254., 255.],&lt;/span&gt;
&lt;span class="gi"&gt;+         data_2=[254., 254., 254., 255.], data_sum=946809963.7487414, order=3, ipix_list=[450, 451])&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;]


&lt;span class="gu"&gt;@@ -62,7 +62,8 @@ def test_make_sky_image():&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    data = make_sky_image(geometry=geometry, hips_survey=hips_survey, tile_format=&amp;#39;fits&amp;#39;)
&lt;span class="w"&gt; &lt;/span&gt;    assert data.shape == geometry.shape
&lt;span class="w"&gt; &lt;/span&gt;    assert data.dtype == np.float64
&lt;span class="gd"&gt;-    assert_allclose(np.sum(data), 7615817463.1612253)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    assert_allclose(np.sum(data), 8757489268.044867)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    assert_allclose(data[200, 994], 2213.30874796)
&lt;span class="w"&gt; &lt;/span&gt;    assert_allclose(data[200, 995], 2296.93885940)

&lt;span class="gh"&gt;diff --git a/hips/tiles/tests/test_surveys.py b/hips/tiles/tests/test_surveys.py&lt;/span&gt;
&lt;span class="gh"&gt;index ee1d856..47aab83 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tests/test_surveys.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tests/test_surveys.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -5,6 +5,7 @@ from astropy.tests.helper import remote_data&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from ..surveys import HipsSurveyProperties, HipsSurveyPropertiesList
&lt;span class="w"&gt; &lt;/span&gt;from ...utils.testing import get_hips_extra_file, requires_hips_extra

&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;class TestHipsSurveyProperties:
&lt;span class="w"&gt; &lt;/span&gt;    @classmethod
&lt;span class="w"&gt; &lt;/span&gt;    def setup_class(cls):
&lt;span class="gh"&gt;diff --git a/hips/utils/tests/test_healpix.py b/hips/utils/tests/test_healpix.py&lt;/span&gt;
&lt;span class="gh"&gt;index 9d0dd47..aff6dc3 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/tests/test_healpix.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/tests/test_healpix.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -23,8 +23,8 @@ def test_boundaries():&lt;/span&gt;


&lt;span class="w"&gt; &lt;/span&gt;compute_healpix_pixel_indices_pars = [
&lt;span class="gd"&gt;-    dict(frame=&amp;#39;galactic&amp;#39;, ipix=[269, 271, 280, 282, 283, 304, 305, 307, 308, 310]),&lt;/span&gt;
&lt;span class="gd"&gt;-    dict(frame=&amp;#39;icrs&amp;#39;, ipix=[448, 449, 450, 451, 454, 456, 457, 458, 459, 460]),&lt;/span&gt;
&lt;span class="gi"&gt;+    dict(frame=&amp;#39;galactic&amp;#39;, ipix=[269, 270, 271, 280, 282, 283, 292, 293, 295, 304, 305, 306]),&lt;/span&gt;
&lt;span class="gi"&gt;+    dict(frame=&amp;#39;icrs&amp;#39;, ipix=[448, 449, 450, 451, 454, 456, 457, 460, 661, 663, 669]),&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;]


&lt;span class="gh"&gt;diff --git a/hips/utils/wcs.py b/hips/utils/wcs.py&lt;/span&gt;
&lt;span class="gh"&gt;index 7dd3db2..305092b 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/wcs.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/wcs.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -15,7 +15,7 @@ __doctest_skip__ = [&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;#39;*&amp;#39;,
&lt;span class="w"&gt; &lt;/span&gt;]

&lt;span class="gd"&gt;-Shape = namedtuple(&amp;#39;Shape&amp;#39;, [&amp;#39;width&amp;#39;, &amp;#39;height&amp;#39;])&lt;/span&gt;
&lt;span class="gi"&gt;+Shape = namedtuple(&amp;#39;Shape&amp;#39;, [&amp;#39;height&amp;#39;, &amp;#39;width&amp;#39;])&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&amp;quot;&amp;quot;&amp;quot;Helper for 2-dim image shape, to make it clearer what value is width and height.&amp;quot;&amp;quot;&amp;quot;



commit 8ca56a7036540d528fb891a811a9bec901086aa3
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Mon Jul 10 19:33:10 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Add support for RGB tiles

&lt;span class="gh"&gt;diff --git a/hips/draw/simple.py b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index bcac055..c6d2ad5 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -18,8 +18,10 @@ __doctest_skip__ = [&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;#39;compute_matching_hips_order&amp;#39;,
&lt;span class="w"&gt; &lt;/span&gt;]

&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# TODO: Fix type annotation issue
&lt;span class="gd"&gt;-def draw_sky_image(geometry: WCSGeometry, tiles: Generator[HipsTile, Any, Any], hips_survey: HipsSurveyProperties) -&amp;gt; np.ndarray:&lt;/span&gt;
&lt;span class="gi"&gt;+def draw_sky_image(geometry: WCSGeometry, tiles: Generator[HipsTile, Any, Any],&lt;/span&gt;
&lt;span class="gi"&gt;+                   hips_survey: HipsSurveyProperties, tile_format: str) -&amp;gt; np.ndarray:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;Draw sky image using the simple and quick method.

&lt;span class="w"&gt; &lt;/span&gt;    Parameters
&lt;span class="gu"&gt;@@ -30,13 +32,21 @@ def draw_sky_image(geometry: WCSGeometry, tiles: Generator[HipsTile, Any, Any],&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        A list of HipsTile
&lt;span class="w"&gt; &lt;/span&gt;    hips_survey : `~hips.HipsSurveyProperties`
&lt;span class="w"&gt; &lt;/span&gt;        HiPS survey properties
&lt;span class="gi"&gt;+    tile_format : `str`&lt;/span&gt;
&lt;span class="gi"&gt;+        Format of HiPS tile&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    Returns
&lt;span class="w"&gt; &lt;/span&gt;    -------
&lt;span class="w"&gt; &lt;/span&gt;    np.ndarray
&lt;span class="w"&gt; &lt;/span&gt;        Returns a numpy array containing all HiPS tiles projected onto it
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-    image = np.zeros(geometry.shape)&lt;/span&gt;
&lt;span class="gi"&gt;+    if tile_format == &amp;#39;jpg&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+        shape = (geometry.shape.width, geometry.shape.height, 3)&lt;/span&gt;
&lt;span class="gi"&gt;+    elif tile_format == &amp;#39;png&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+        shape = (geometry.shape.width, geometry.shape.height, 4)&lt;/span&gt;
&lt;span class="gi"&gt;+    else:&lt;/span&gt;
&lt;span class="gi"&gt;+        shape = (geometry.shape.width, geometry.shape.height)&lt;/span&gt;
&lt;span class="gi"&gt;+    image = np.zeros(shape)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    for tile in tiles:
&lt;span class="w"&gt; &lt;/span&gt;        painter = SimpleTilePainter(geometry, hips_survey, tile)
&lt;span class="w"&gt; &lt;/span&gt;        image += painter.warp_image()
&lt;span class="gu"&gt;@@ -45,7 +55,6 @@ def draw_sky_image(geometry: WCSGeometry, tiles: Generator[HipsTile, Any, Any],&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;class SimpleTilePainter:
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;Paint a single tile using a simple projective transformation method.
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    The algorithm implemented is described here: :ref:`drawing_algo`.

&lt;span class="w"&gt; &lt;/span&gt;    Parameters
&lt;span class="gu"&gt;@@ -73,6 +82,7 @@ class SimpleTilePainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;             [0, width - 1],
&lt;span class="w"&gt; &lt;/span&gt;             [0, 0]],
&lt;span class="w"&gt; &lt;/span&gt;        )
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    @property
&lt;span class="w"&gt; &lt;/span&gt;    def projection(self) -&amp;gt; ProjectiveTransform:
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Estimate projective transformation on a HiPS tile&amp;quot;&amp;quot;&amp;quot;
&lt;span class="gu"&gt;@@ -93,7 +103,8 @@ class SimpleTilePainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        )


&lt;span class="gd"&gt;-def fetch_tiles(healpix_pixel_indices: np.ndarray, order: int, hips_survey: HipsSurveyProperties) -&amp;gt; &amp;#39;HipsTile&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+def fetch_tiles(healpix_pixel_indices: np.ndarray, order: int,&lt;/span&gt;
&lt;span class="gi"&gt;+                hips_survey: HipsSurveyProperties, tile_format: str) -&amp;gt; &amp;#39;HipsTile&amp;#39;:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;Fetch HiPS tiles from a remote URL.

&lt;span class="w"&gt; &lt;/span&gt;    Parameters
&lt;span class="gu"&gt;@@ -104,18 +115,20 @@ def fetch_tiles(healpix_pixel_indices: np.ndarray, order: int, hips_survey: Hips&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        Order of the HEALPix map
&lt;span class="w"&gt; &lt;/span&gt;    hips_survey : HipsSurveyProperties
&lt;span class="w"&gt; &lt;/span&gt;        An object of HipsSurveyProperties
&lt;span class="gi"&gt;+    tile_format : `str`&lt;/span&gt;
&lt;span class="gi"&gt;+        Format of HiPS tile&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    Returns
&lt;span class="w"&gt; &lt;/span&gt;    -------
&lt;span class="w"&gt; &lt;/span&gt;    &amp;#39;HipsTile&amp;#39;
&lt;span class="gd"&gt;-        Returns an object of  HipsTile&lt;/span&gt;
&lt;span class="gi"&gt;+        Returns an object of HipsTile&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;    for healpix_pixel_index in healpix_pixel_indices:
&lt;span class="w"&gt; &lt;/span&gt;        tile_meta = HipsTileMeta(
&lt;span class="w"&gt; &lt;/span&gt;            order=order,
&lt;span class="w"&gt; &lt;/span&gt;            ipix=healpix_pixel_index,
&lt;span class="w"&gt; &lt;/span&gt;            frame=hips_survey.astropy_frame,
&lt;span class="gd"&gt;-            file_format=&amp;#39;fits&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+            file_format=tile_format,&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        )
&lt;span class="w"&gt; &lt;/span&gt;        tile = HipsTile.fetch(tile_meta, hips_survey.tile_access_url(order=order, ipix=healpix_pixel_index) + tile_meta.filename)
&lt;span class="w"&gt; &lt;/span&gt;        yield tile
&lt;span class="gu"&gt;@@ -174,9 +187,8 @@ def _get_hips_order_for_resolution(tile_width, resolution):&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    return candidate_tile_order


&lt;span class="gd"&gt;-def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties) -&amp;gt; np.ndarray:&lt;/span&gt;
&lt;span class="gi"&gt;+def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties, tile_format: str) -&amp;gt; np.ndarray:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;Make sky image: fetch tiles and draw.
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    The example for this can be found on the :ref:`gs` page.

&lt;span class="w"&gt; &lt;/span&gt;    Parameters
&lt;span class="gu"&gt;@@ -185,6 +197,8 @@ def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties) -&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        Geometry of the output image
&lt;span class="w"&gt; &lt;/span&gt;    hips_survey : `~hips.HipsSurveyProperties`
&lt;span class="w"&gt; &lt;/span&gt;        HiPS survey properties
&lt;span class="gi"&gt;+    tile_format : `str`&lt;/span&gt;
&lt;span class="gi"&gt;+        Format of HiPS tile&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    Returns
&lt;span class="w"&gt; &lt;/span&gt;    -------
&lt;span class="gu"&gt;@@ -198,8 +212,8 @@ def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties) -&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        healpix_frame=hips_survey.astropy_frame,
&lt;span class="w"&gt; &lt;/span&gt;    )
&lt;span class="w"&gt; &lt;/span&gt;    # TODO: this isn&amp;#39;t a good API. Will become better when we have a cache.
&lt;span class="gd"&gt;-    tiles = fetch_tiles(healpix_pixel_indices, order, hips_survey)&lt;/span&gt;
&lt;span class="gi"&gt;+    tiles = fetch_tiles(healpix_pixel_indices, order, hips_survey, tile_format)&lt;/span&gt;

&lt;span class="gd"&gt;-    image_data = draw_sky_image(geometry, tiles, hips_survey)&lt;/span&gt;
&lt;span class="gi"&gt;+    image_data = draw_sky_image(geometry, tiles, hips_survey, tile_format)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    return image_data
&lt;span class="gh"&gt;diff --git a/hips/draw/tests/test_simple.py b/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index 249d77e..b910517 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -4,45 +4,53 @@ import numpy as np&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;import healpy as hp
&lt;span class="w"&gt; &lt;/span&gt;from astropy.coordinates import SkyCoord
&lt;span class="w"&gt; &lt;/span&gt;from numpy.testing import assert_allclose
&lt;span class="gd"&gt;-from astropy.utils.data import get_pkg_data_filename&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from astropy.tests.helper import remote_data
&lt;span class="w"&gt; &lt;/span&gt;from ..simple import make_sky_image, draw_sky_image, compute_matching_hips_order, _get_hips_order_for_resolution
&lt;span class="w"&gt; &lt;/span&gt;from ...tiles import HipsSurveyProperties, HipsTileMeta, HipsTile
&lt;span class="w"&gt; &lt;/span&gt;from ...utils import WCSGeometry
&lt;span class="w"&gt; &lt;/span&gt;from ...utils.testing import get_hips_extra_file, make_test_wcs_geometry, requires_hips_extra

&lt;span class="gd"&gt;-def get_test_tiles():&lt;/span&gt;
&lt;span class="gd"&gt;-    filename = get_pkg_data_filename(&amp;#39;../../tiles/tests/data/properties.txt&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+def get_test_tiles(file_format, survey, order, ipix_list):&lt;/span&gt;
&lt;span class="gi"&gt;+    filename = get_hips_extra_file(&amp;#39;datasets/samples/&amp;#39; + survey + &amp;#39;/properties&amp;#39;)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    hips_survey = HipsSurveyProperties.read(filename)

&lt;span class="gd"&gt;-    tile1 = HipsTile.read(&lt;/span&gt;
&lt;span class="gd"&gt;-        meta=HipsTileMeta(order=3, ipix=450, file_format=&amp;#39;fits&amp;#39;, frame=hips_survey.astropy_frame),&lt;/span&gt;
&lt;span class="gd"&gt;-        full_path=get_hips_extra_file(&amp;#39;datasets/samples/DSS2Red/Norder3/Dir0/Npix450.fits&amp;#39;),&lt;/span&gt;
&lt;span class="gd"&gt;-    )&lt;/span&gt;
&lt;span class="gi"&gt;+    tiles = []&lt;/span&gt;
&lt;span class="gi"&gt;+    for ipix in ipix_list:&lt;/span&gt;
&lt;span class="gi"&gt;+        tiles.append(HipsTile.read(&lt;/span&gt;
&lt;span class="gi"&gt;+            meta=HipsTileMeta(order=order, ipix=ipix, file_format=file_format, frame=hips_survey.astropy_frame),&lt;/span&gt;
&lt;span class="gi"&gt;+            full_path=get_hips_extra_file(&amp;#39;datasets/samples/&amp;#39; + survey + hips_survey.tile_path(order=order, ipix=ipix, tile_format=file_format)),&lt;/span&gt;
&lt;span class="gi"&gt;+        ))&lt;/span&gt;

&lt;span class="gd"&gt;-    tile2 = HipsTile.read(&lt;/span&gt;
&lt;span class="gd"&gt;-        meta=HipsTileMeta(order=3, ipix=451, file_format=&amp;#39;fits&amp;#39;, frame=hips_survey.astropy_frame),&lt;/span&gt;
&lt;span class="gd"&gt;-        full_path=get_hips_extra_file(&amp;#39;datasets/samples/DSS2Red/Norder3/Dir0/Npix451.fits&amp;#39;),&lt;/span&gt;
&lt;span class="gd"&gt;-    )&lt;/span&gt;
&lt;span class="gi"&gt;+    return tiles&lt;/span&gt;

&lt;span class="gd"&gt;-    return [tile1, tile2]&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+draw_sky_image_pars = [&lt;/span&gt;
&lt;span class="gi"&gt;+    dict(file_format=&amp;#39;fits&amp;#39;, shape=(2000, 1000), survey=&amp;#39;DSS2Red&amp;#39;, data_1=2866.0101409848185,&lt;/span&gt;
&lt;span class="gi"&gt;+         data_2=2563.6916727348043, data_sum=2992643842.729775, order=3, ipix_list=[450, 451]),&lt;/span&gt;
&lt;span class="gi"&gt;+    dict(file_format=&amp;#39;jpg&amp;#39;, shape=(2000, 1000, 3), survey=&amp;#39;DSS2Red&amp;#39;, data_1=[13.040878, 13.040878, 13.040878],&lt;/span&gt;
&lt;span class="gi"&gt;+         data_2=[17.235874, 17.235874, 17.235874], data_sum=155233709.20236143, order=3, ipix_list=[450, 451]),&lt;/span&gt;
&lt;span class="gi"&gt;+    dict(file_format=&amp;#39;png&amp;#39;, shape=(2000, 1000, 4), survey=&amp;#39;AKARI-FIS&amp;#39;, data_1=[254., 254., 254., 255.],&lt;/span&gt;
&lt;span class="gi"&gt;+         data_2=[254., 254., 254., 255.], data_sum=586208559.2450126, order=3, ipix_list=[450, 451])&lt;/span&gt;
&lt;span class="gi"&gt;+]&lt;/span&gt;


&lt;span class="w"&gt; &lt;/span&gt;@remote_data
&lt;span class="w"&gt; &lt;/span&gt;@requires_hips_extra()
&lt;span class="gd"&gt;-def test_draw_sky_image():&lt;/span&gt;
&lt;span class="gi"&gt;+@pytest.mark.parametrize(&amp;#39;pars&amp;#39;, draw_sky_image_pars)&lt;/span&gt;
&lt;span class="gi"&gt;+def test_draw_sky_image(pars):&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    geometry = make_test_wcs_geometry(case=2)
&lt;span class="gd"&gt;-    tiles = get_test_tiles()&lt;/span&gt;
&lt;span class="gd"&gt;-    url = &amp;#39;https://raw.githubusercontent.com/hipspy/hips-extra/master/datasets/samples/DSS2Red/properties&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+    tiles = get_test_tiles(file_format=pars[&amp;#39;file_format&amp;#39;], survey=pars[&amp;#39;survey&amp;#39;], order=pars[&amp;#39;order&amp;#39;], ipix_list=pars[&amp;#39;ipix_list&amp;#39;])&lt;/span&gt;
&lt;span class="gi"&gt;+    url = &amp;#39;https://raw.githubusercontent.com/hipspy/hips-extra/master/datasets/samples/&amp;#39; + pars[&amp;#39;survey&amp;#39;] + &amp;#39;/properties&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    hips_survey = HipsSurveyProperties.fetch(url)

&lt;span class="gd"&gt;-    data = draw_sky_image(geometry, tiles, hips_survey)&lt;/span&gt;
&lt;span class="gi"&gt;+    data = draw_sky_image(geometry=geometry, tiles=tiles, hips_survey=hips_survey, tile_format=pars[&amp;#39;file_format&amp;#39;])&lt;/span&gt;

&lt;span class="gd"&gt;-    assert data.shape == geometry.shape&lt;/span&gt;
&lt;span class="gi"&gt;+    assert data.shape == pars[&amp;#39;shape&amp;#39;]&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    assert data.dtype == np.float64
&lt;span class="gd"&gt;-    assert_allclose(np.sum(data), 2992643842.729775)&lt;/span&gt;
&lt;span class="gd"&gt;-    assert_allclose(data[400, 500], 2866.0101409848185)&lt;/span&gt;
&lt;span class="gd"&gt;-    assert_allclose(data[400, 501], 2563.6916727348043)&lt;/span&gt;
&lt;span class="gi"&gt;+    assert_allclose(np.sum(data), pars[&amp;#39;data_sum&amp;#39;])&lt;/span&gt;
&lt;span class="gi"&gt;+    assert_allclose(data[400, 500], pars[&amp;#39;data_1&amp;#39;])&lt;/span&gt;
&lt;span class="gi"&gt;+    assert_allclose(data[400, 501], pars[&amp;#39;data_2&amp;#39;])&lt;/span&gt;


&lt;span class="w"&gt; &lt;/span&gt;@remote_data
&lt;span class="gu"&gt;@@ -51,9 +59,10 @@ def test_make_sky_image():&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    url = &amp;#39;http://alasky.unistra.fr/DSS/DSS2Merged/properties&amp;#39;
&lt;span class="w"&gt; &lt;/span&gt;    hips_survey = HipsSurveyProperties.fetch(url)
&lt;span class="w"&gt; &lt;/span&gt;    geometry = make_test_wcs_geometry(case=2)
&lt;span class="gd"&gt;-    data = make_sky_image(geometry, hips_survey)&lt;/span&gt;
&lt;span class="gi"&gt;+    data = make_sky_image(geometry=geometry, hips_survey=hips_survey, tile_format=&amp;#39;fits&amp;#39;)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    assert data.shape == geometry.shape
&lt;span class="w"&gt; &lt;/span&gt;    assert data.dtype == np.float64
&lt;span class="gi"&gt;+    assert_allclose(np.sum(data), 7615817463.1612253)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    assert_allclose(data[200, 994], 2213.30874796)
&lt;span class="w"&gt; &lt;/span&gt;    assert_allclose(data[200, 995], 2296.93885940)

&lt;span class="gh"&gt;diff --git a/hips/tiles/surveys.py b/hips/tiles/surveys.py&lt;/span&gt;
&lt;span class="gh"&gt;index e93a579..bfe814d 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/surveys.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/surveys.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -150,6 +150,20 @@ class HipsSurveyProperties:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;        return self.base_url + &amp;#39;/Norder&amp;#39; + str(order) + &amp;#39;/Dir&amp;#39; + str(self.directory(ipix)) + &amp;#39;/&amp;#39;

&lt;span class="gi"&gt;+    def tile_path(self, order: int, ipix: int, tile_format: str) -&amp;gt; str:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Tile access URL&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        Parameters&lt;/span&gt;
&lt;span class="gi"&gt;+        ----------&lt;/span&gt;
&lt;span class="gi"&gt;+        order : int&lt;/span&gt;
&lt;span class="gi"&gt;+            HiPS order&lt;/span&gt;
&lt;span class="gi"&gt;+        ipix : int&lt;/span&gt;
&lt;span class="gi"&gt;+            Index of the HiPS tile&lt;/span&gt;
&lt;span class="gi"&gt;+        tile_format : str&lt;/span&gt;
&lt;span class="gi"&gt;+            HiPS tile URL&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        return &amp;#39;/Norder&amp;#39; + str(order) + &amp;#39;/Dir&amp;#39; + str(self.directory(ipix)) + &amp;#39;/Npix&amp;#39; + str(ipix) + &amp;#39;.&amp;#39; + tile_format&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    @property
&lt;span class="w"&gt; &lt;/span&gt;    def hips_service_url(self) -&amp;gt; str:
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;HiPS service base URL (`str`).&amp;quot;&amp;quot;&amp;quot;

commit 10ada966037b2a71aec42b3c85a9773d2bab6a48
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Sun Jul 9 11:23:26 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Update base_url property in HipsSurveyProperties class

&lt;span class="gh"&gt;diff --git a/hips/tiles/surveys.py b/hips/tiles/surveys.py&lt;/span&gt;
&lt;span class="gh"&gt;index e93a579..248b112 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/surveys.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/surveys.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -69,16 +69,18 @@ class HipsSurveyProperties:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        with urllib.request.urlopen(url) as response:
&lt;span class="w"&gt; &lt;/span&gt;            text = response.read().decode(&amp;#39;utf-8&amp;#39;)
&lt;span class="gd"&gt;-        return cls.parse(text)&lt;/span&gt;
&lt;span class="gi"&gt;+        return cls.parse(text, url)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    @classmethod
&lt;span class="gd"&gt;-    def parse(cls, text: str) -&amp;gt; &amp;#39;HipsSurveyProperties&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+    def parse(cls, text: str, url: str = None) -&amp;gt; &amp;#39;HipsSurveyProperties&amp;#39;:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Parse HiPS survey description text (`HipsSurveyProperties`).

&lt;span class="w"&gt; &lt;/span&gt;        Parameters
&lt;span class="w"&gt; &lt;/span&gt;        ----------
&lt;span class="w"&gt; &lt;/span&gt;        text : str
&lt;span class="w"&gt; &lt;/span&gt;            Text containing HiPS survey properties
&lt;span class="gi"&gt;+        url : str&lt;/span&gt;
&lt;span class="gi"&gt;+            Properties URL of HiPS&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;        data = OrderedDict()
&lt;span class="w"&gt; &lt;/span&gt;        for line in text.split(&amp;#39;\n&amp;#39;):
&lt;span class="gu"&gt;@@ -92,6 +94,9 @@ class HipsSurveyProperties:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                # Skip bad lines (silently, might not be a good idea to do this)
&lt;span class="w"&gt; &lt;/span&gt;                continue

&lt;span class="gi"&gt;+        if url is not None:&lt;/span&gt;
&lt;span class="gi"&gt;+            data[&amp;#39;properties_url&amp;#39;] = url.rsplit(&amp;#39;/&amp;#39;, 1)[0]&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        return cls(data)

&lt;span class="w"&gt; &lt;/span&gt;    @property
&lt;span class="gu"&gt;@@ -127,7 +132,16 @@ class HipsSurveyProperties:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    @property
&lt;span class="w"&gt; &lt;/span&gt;    def base_url(self) -&amp;gt; str:
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;HiPS access url&amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-        return self.data[&amp;#39;moc_access_url&amp;#39;].rsplit(&amp;#39;/&amp;#39;, 1)[0]&lt;/span&gt;
&lt;span class="gi"&gt;+        try:&lt;/span&gt;
&lt;span class="gi"&gt;+            return self.data[&amp;#39;hips_service_url&amp;#39;]&lt;/span&gt;
&lt;span class="gi"&gt;+        except KeyError:&lt;/span&gt;
&lt;span class="gi"&gt;+            try:&lt;/span&gt;
&lt;span class="gi"&gt;+                return self.data[&amp;#39;moc_access_url&amp;#39;].rsplit(&amp;#39;/&amp;#39;, 1)[0]&lt;/span&gt;
&lt;span class="gi"&gt;+            except KeyError:&lt;/span&gt;
&lt;span class="gi"&gt;+                try:&lt;/span&gt;
&lt;span class="gi"&gt;+                    return self.data[&amp;#39;properties_url&amp;#39;]&lt;/span&gt;
&lt;span class="gi"&gt;+                except:&lt;/span&gt;
&lt;span class="gi"&gt;+                    return ValueError(&amp;#39;URL does not exist!&amp;#39;)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    @property
&lt;span class="w"&gt; &lt;/span&gt;    def tile_width(self) -&amp;gt; int:
&lt;span class="gh"&gt;diff --git a/hips/tiles/tests/test_surveys.py b/hips/tiles/tests/test_surveys.py&lt;/span&gt;
&lt;span class="gh"&gt;index f1a031e..ee1d856 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tests/test_surveys.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tests/test_surveys.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -36,6 +36,13 @@ class TestHipsSurveyProperties:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        assert self.hips_survey_property.tile_access_url(order=9, ipix=54321) == &amp;#39;http://alasky.u-strasbg.fr/DSS/DSSColor/Norder9/Dir50000/&amp;#39;


&lt;span class="gi"&gt;+@remote_data&lt;/span&gt;
&lt;span class="gi"&gt;+def test_base_url():&lt;/span&gt;
&lt;span class="gi"&gt;+    url = &amp;#39;http://alasky.u-strasbg.fr/DSS/DSS2-NIR/properties&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+    survey = HipsSurveyProperties.fetch(url)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    assert survey.base_url == &amp;#39;http://alasky.u-strasbg.fr/DSS/DSS2-NIR&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    @requires_hips_extra()
&lt;span class="w"&gt; &lt;/span&gt;    def test_tile_width(self):
&lt;span class="w"&gt; &lt;/span&gt;        filename = get_hips_extra_file(&amp;#39;datasets/samples/Planck-HFI143/properties&amp;#39;)

commit c853878ab027abf70b2f771796ff7ae98c37c6f3
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Thu Jul 6 23:03:54 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Add test case for get_order function

&lt;span class="gh"&gt;diff --git a/.travis.yml b/.travis.yml&lt;/span&gt;
&lt;span class="gh"&gt;index 3cbe1ae..ebe7b8a 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/.travis.yml&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/.travis.yml&lt;/span&gt;
&lt;span class="gu"&gt;@@ -46,7 +46,7 @@ matrix:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    include:
&lt;span class="w"&gt; &lt;/span&gt;        # Main build -- used for coverage
&lt;span class="w"&gt; &lt;/span&gt;        - os: linux
&lt;span class="gd"&gt;-          env: SETUP_CMD=&amp;#39;test --coverage&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+          env: SETUP_CMD=&amp;#39;test --coverage --remote-data -V&amp;#39;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        # Docs build
&lt;span class="w"&gt; &lt;/span&gt;        - os: linux
&lt;span class="gu"&gt;@@ -55,7 +55,7 @@ matrix:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        # Check that install / tests work with minimal required dependencies
&lt;span class="w"&gt; &lt;/span&gt;        - os: linux
&lt;span class="gd"&gt;-          env: SETUP_CMD=&amp;#39;test&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+          env: SETUP_CMD=&amp;#39;test -V&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;               CONDA_DEPENDENCIES=&amp;#39;healpy scikit-image Pillow&amp;#39;

&lt;span class="w"&gt; &lt;/span&gt;        # Try MacOS X
&lt;span class="gh"&gt;diff --git a/hips/draw/simple.py b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index 2fb930e..bcac055 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -10,9 +10,13 @@ from ..utils import WCSGeometry, compute_healpix_pixel_indices&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;__all__ = [
&lt;span class="w"&gt; &lt;/span&gt;    &amp;#39;draw_sky_image&amp;#39;,
&lt;span class="w"&gt; &lt;/span&gt;    &amp;#39;make_sky_image&amp;#39;,
&lt;span class="gd"&gt;-    &amp;#39;SimpleTilePainter&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;#39;SimpleTilePainter&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;#39;compute_matching_hips_order&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;]

&lt;span class="gi"&gt;+__doctest_skip__ = [&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;#39;compute_matching_hips_order&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+]&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;# TODO: Fix type annotation issue
&lt;span class="w"&gt; &lt;/span&gt;def draw_sky_image(geometry: WCSGeometry, tiles: Generator[HipsTile, Any, Any], hips_survey: HipsSurveyProperties) -&amp;gt; np.ndarray:
&lt;span class="gu"&gt;@@ -117,20 +121,57 @@ def fetch_tiles(healpix_pixel_indices: np.ndarray, order: int, hips_survey: Hips&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        yield tile


&lt;span class="gd"&gt;-def get_order(geometry: WCSGeometry, hips_survey: HipsSurveyProperties) -&amp;gt; int:&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;quot;&amp;quot;&amp;quot;Compute the tile order suited for the given geometry and hips_survey&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+def compute_matching_hips_order(geometry: WCSGeometry, hips_survey: HipsSurveyProperties) -&amp;gt; int:&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;Compute HiPS tile order matching a given image pixel size.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    Parameters&lt;/span&gt;
&lt;span class="gi"&gt;+    ----------&lt;/span&gt;
&lt;span class="gi"&gt;+    geometry : WCSGeometry&lt;/span&gt;
&lt;span class="gi"&gt;+        Geometry of the output image&lt;/span&gt;
&lt;span class="gi"&gt;+    hips_survey : HipsSurveyProperties&lt;/span&gt;
&lt;span class="gi"&gt;+        An object of HipsSurveyProperties&lt;/span&gt;

&lt;span class="gi"&gt;+    Returns&lt;/span&gt;
&lt;span class="gi"&gt;+    -------&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;#39;int&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        Returns HiPS order&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    Examples&lt;/span&gt;
&lt;span class="gi"&gt;+    --------&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;gt;&amp;gt;&amp;gt; from hips.draw import compute_matching_hips_order&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;gt;&amp;gt;&amp;gt; from astropy.coordinates import SkyCoord&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;gt;&amp;gt;&amp;gt; url = &amp;#39;http://alasky.unistra.fr/DSS/DSS2Merged/properties&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;gt;&amp;gt;&amp;gt; hips_survey = HipsSurveyProperties.fetch(url)&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;gt;&amp;gt;&amp;gt; geometry = WCSGeometry.create_simple(&lt;/span&gt;
&lt;span class="gi"&gt;+    ...     skydir=SkyCoord(0, 0, unit=&amp;#39;deg&amp;#39;, frame=&amp;#39;icrs&amp;#39;),&lt;/span&gt;
&lt;span class="gi"&gt;+    ...     width=2000, height=1000, fov=&amp;quot;3 deg&amp;quot;,&lt;/span&gt;
&lt;span class="gi"&gt;+    ...     coordsys=&amp;#39;icrs&amp;#39;, projection=&amp;#39;AIT&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+    ... )&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;gt;&amp;gt;&amp;gt; compute_matching_hips_order(geometry, hips_survey)&lt;/span&gt;
&lt;span class="gi"&gt;+    7&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    # Sky image angular resolution (pixel size in degree)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    resolution = np.min(proj_plane_pixel_scales(geometry.wcs))
&lt;span class="gi"&gt;+    desired_order = _get_hips_order_for_resolution(hips_survey.tile_width, resolution)&lt;/span&gt;
&lt;span class="gi"&gt;+    # Return the desired order, or the highest resolution available.&lt;/span&gt;
&lt;span class="gi"&gt;+    # Note that HiPS never has resolution less than 3,&lt;/span&gt;
&lt;span class="gi"&gt;+    # and that limit is handled in _get_hips_order_for_resolution&lt;/span&gt;
&lt;span class="gi"&gt;+    return np.min([desired_order, hips_survey.hips_order])&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;

&lt;span class="gd"&gt;-    tile_order = np.log2(hips_survey.tile_width)&lt;/span&gt;
&lt;span class="gi"&gt;+def _get_hips_order_for_resolution(tile_width, resolution):&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;Finding the best HiPS order by looping through all possible options.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+    tile_order = np.log2(tile_width)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    full_sphere_area = 4 * np.pi * np.square(180 / np.pi)
&lt;span class="gi"&gt;+    # 29 is the maximum order supported by healpy and 3 is the minimum order&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    for candidate_tile_order in range(3, 29 + 1):
&lt;span class="gd"&gt;-        tile_resolution = np.sqrt(full_sphere_area / 12 / 4**(candidate_tile_order + tile_order))&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gi"&gt;+        tile_resolution = np.sqrt(full_sphere_area / 12 / 4 ** (candidate_tile_order + tile_order))&lt;/span&gt;
&lt;span class="gi"&gt;+        # Finding the smaller tile order with a resolution equal or better than geometric resolution&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        if tile_resolution &amp;lt;= resolution:
&lt;span class="w"&gt; &lt;/span&gt;            break

&lt;span class="gd"&gt;-    return np.min([candidate_tile_order, hips_survey.hips_order])&lt;/span&gt;
&lt;span class="gi"&gt;+    return candidate_tile_order&lt;/span&gt;


&lt;span class="w"&gt; &lt;/span&gt;def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties) -&amp;gt; np.ndarray:
&lt;span class="gu"&gt;@@ -150,7 +191,7 @@ def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties) -&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    data : `~numpy.ndarray`
&lt;span class="w"&gt; &lt;/span&gt;        Output image pixels
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-    order = get_order(geometry, hips_survey)&lt;/span&gt;
&lt;span class="gi"&gt;+    order = compute_matching_hips_order(geometry, hips_survey)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    healpix_pixel_indices = compute_healpix_pixel_indices(
&lt;span class="w"&gt; &lt;/span&gt;        wcs_geometry=geometry,
&lt;span class="w"&gt; &lt;/span&gt;        order=order,
&lt;span class="gh"&gt;diff --git a/hips/draw/tests/test_simple.py b/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index 68b7855..249d77e 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,13 +1,16 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="gi"&gt;+import pytest&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;import numpy as np
&lt;span class="gi"&gt;+import healpy as hp&lt;/span&gt;
&lt;span class="gi"&gt;+from astropy.coordinates import SkyCoord&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from numpy.testing import assert_allclose
&lt;span class="w"&gt; &lt;/span&gt;from astropy.utils.data import get_pkg_data_filename
&lt;span class="w"&gt; &lt;/span&gt;from astropy.tests.helper import remote_data
&lt;span class="gd"&gt;-from ..simple import make_sky_image, draw_sky_image&lt;/span&gt;
&lt;span class="gi"&gt;+from ..simple import make_sky_image, draw_sky_image, compute_matching_hips_order, _get_hips_order_for_resolution&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from ...tiles import HipsSurveyProperties, HipsTileMeta, HipsTile
&lt;span class="gi"&gt;+from ...utils import WCSGeometry&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from ...utils.testing import get_hips_extra_file, make_test_wcs_geometry, requires_hips_extra

&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;def get_test_tiles():
&lt;span class="w"&gt; &lt;/span&gt;    filename = get_pkg_data_filename(&amp;#39;../../tiles/tests/data/properties.txt&amp;#39;)
&lt;span class="w"&gt; &lt;/span&gt;    hips_survey = HipsSurveyProperties.read(filename)
&lt;span class="gu"&gt;@@ -44,14 +47,46 @@ def test_draw_sky_image():&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;@remote_data
&lt;span class="w"&gt; &lt;/span&gt;def test_make_sky_image():
&lt;span class="gd"&gt;-    url = &amp;#39;https://raw.githubusercontent.com/hipspy/hips-extra/master/datasets/samples/DSS2Red/properties&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+    # The same example is used in the high level docs getting started page&lt;/span&gt;
&lt;span class="gi"&gt;+    url = &amp;#39;http://alasky.unistra.fr/DSS/DSS2Merged/properties&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    hips_survey = HipsSurveyProperties.fetch(url)
&lt;span class="w"&gt; &lt;/span&gt;    geometry = make_test_wcs_geometry(case=2)
&lt;span class="w"&gt; &lt;/span&gt;    data = make_sky_image(geometry, hips_survey)
&lt;span class="w"&gt; &lt;/span&gt;    assert data.shape == geometry.shape
&lt;span class="w"&gt; &lt;/span&gt;    assert data.dtype == np.float64
&lt;span class="gd"&gt;-    assert_allclose(data[200, 994], 3717.10091363)&lt;/span&gt;
&lt;span class="gd"&gt;-    assert_allclose(data[200, 995], 3402.55292158)&lt;/span&gt;
&lt;span class="gi"&gt;+    assert_allclose(data[200, 994], 2213.30874796)&lt;/span&gt;
&lt;span class="gi"&gt;+    assert_allclose(data[200, 995], 2296.93885940)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+hips_order_pars = [&lt;/span&gt;
&lt;span class="gi"&gt;+    dict(order=7, fov=&amp;quot;3 deg&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+    dict(order=5, fov=&amp;quot;10 deg&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+    dict(order=4, fov=&amp;quot;15 deg&amp;quot;),&lt;/span&gt;
&lt;span class="gi"&gt;+]&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+@requires_hips_extra()&lt;/span&gt;
&lt;span class="gi"&gt;+@pytest.mark.parametrize(&amp;#39;pars&amp;#39;, hips_order_pars)&lt;/span&gt;
&lt;span class="gi"&gt;+def test_compute_matching_hips_order(pars):&lt;/span&gt;
&lt;span class="gi"&gt;+    full_path = get_hips_extra_file(&amp;#39;datasets/samples/2MASS6XH/properties&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+    hips_survey = HipsSurveyProperties.read(filename=full_path)&lt;/span&gt;
&lt;span class="gi"&gt;+    geometry = WCSGeometry.create_simple(&lt;/span&gt;
&lt;span class="gi"&gt;+        skydir=SkyCoord(0, 0, unit=&amp;#39;deg&amp;#39;, frame=&amp;#39;icrs&amp;#39;),&lt;/span&gt;
&lt;span class="gi"&gt;+        width=2000, height=1000, fov=pars[&amp;#39;fov&amp;#39;],&lt;/span&gt;
&lt;span class="gi"&gt;+        coordsys=&amp;#39;icrs&amp;#39;, projection=&amp;#39;AIT&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+    )&lt;/span&gt;
&lt;span class="gi"&gt;+    assert compute_matching_hips_order(geometry, hips_survey) == pars[&amp;#39;order&amp;#39;]&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+get_hips_order_for_resolution_pars = [&lt;/span&gt;
&lt;span class="gi"&gt;+    dict(tile_width=512, resolution=0.01232, resolution_res=0.06395791924665553, order=4),&lt;/span&gt;
&lt;span class="gi"&gt;+    dict(tile_width=256, resolution=0.0016022, resolution_res=0.003997369952915971, order=8),&lt;/span&gt;
&lt;span class="gi"&gt;+    dict(tile_width=128, resolution=0.00009032, resolution_res=0.00012491781102862408, order=13),&lt;/span&gt;
&lt;span class="gi"&gt;+]&lt;/span&gt;


&lt;span class="gi"&gt;+@pytest.mark.parametrize(&amp;#39;pars&amp;#39;, get_hips_order_for_resolution_pars)&lt;/span&gt;
&lt;span class="gi"&gt;+def test_get_hips_order_for_resolution(pars):&lt;/span&gt;
&lt;span class="gi"&gt;+    hips_order = _get_hips_order_for_resolution(pars[&amp;#39;tile_width&amp;#39;], pars[&amp;#39;resolution&amp;#39;])&lt;/span&gt;
&lt;span class="gi"&gt;+    assert hips_order == pars[&amp;#39;order&amp;#39;]&lt;/span&gt;
&lt;span class="gi"&gt;+    hips_resolution = hp.nside2resol(hp.order2nside(hips_order))&lt;/span&gt;
&lt;span class="gi"&gt;+    assert_allclose(hips_resolution, pars[&amp;#39;resolution_res&amp;#39;])&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# TODO: add tests for SimpleTilePainter with asserts on the intermediate computed things.

commit e1955f6ef699669ff4209cf9a72d26488af6154e
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Thu Jul 6 21:48:05 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Add function for computing HiPS order

&lt;span class="gh"&gt;diff --git a/hips/draw/simple.py b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index c912b62..2fb930e 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -2,6 +2,7 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&amp;quot;&amp;quot;&amp;quot;HiPS tile drawing -- simple method.&amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;from typing import Generator, Any
&lt;span class="w"&gt; &lt;/span&gt;import numpy as np
&lt;span class="gi"&gt;+from astropy.wcs.utils import proj_plane_pixel_scales&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from skimage.transform import ProjectiveTransform, warp
&lt;span class="w"&gt; &lt;/span&gt;from ..tiles import HipsSurveyProperties, HipsTile, HipsTileMeta
&lt;span class="w"&gt; &lt;/span&gt;from ..utils import WCSGeometry, compute_healpix_pixel_indices
&lt;span class="gu"&gt;@@ -116,6 +117,22 @@ def fetch_tiles(healpix_pixel_indices: np.ndarray, order: int, hips_survey: Hips&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        yield tile


&lt;span class="gi"&gt;+def get_order(geometry: WCSGeometry, hips_survey: HipsSurveyProperties) -&amp;gt; int:&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;Compute the tile order suited for the given geometry and hips_survey&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    resolution = np.min(proj_plane_pixel_scales(geometry.wcs))&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    tile_order = np.log2(hips_survey.tile_width)&lt;/span&gt;
&lt;span class="gi"&gt;+    full_sphere_area = 4 * np.pi * np.square(180 / np.pi)&lt;/span&gt;
&lt;span class="gi"&gt;+    for candidate_tile_order in range(3, 29 + 1):&lt;/span&gt;
&lt;span class="gi"&gt;+        tile_resolution = np.sqrt(full_sphere_area / 12 / 4**(candidate_tile_order + tile_order))&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        if tile_resolution &amp;lt;= resolution:&lt;/span&gt;
&lt;span class="gi"&gt;+            break&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    return np.min([candidate_tile_order, hips_survey.hips_order])&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties) -&amp;gt; np.ndarray:
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;Make sky image: fetch tiles and draw.

&lt;span class="gu"&gt;@@ -133,13 +150,14 @@ def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties) -&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    data : `~numpy.ndarray`
&lt;span class="w"&gt; &lt;/span&gt;        Output image pixels
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;
&lt;span class="gi"&gt;+    order = get_order(geometry, hips_survey)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    healpix_pixel_indices = compute_healpix_pixel_indices(
&lt;span class="w"&gt; &lt;/span&gt;        wcs_geometry=geometry,
&lt;span class="gd"&gt;-        order=hips_survey.hips_order,&lt;/span&gt;
&lt;span class="gi"&gt;+        order=order,&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        healpix_frame=hips_survey.astropy_frame,
&lt;span class="w"&gt; &lt;/span&gt;    )
&lt;span class="w"&gt; &lt;/span&gt;    # TODO: this isn&amp;#39;t a good API. Will become better when we have a cache.
&lt;span class="gd"&gt;-    tiles = fetch_tiles(healpix_pixel_indices, hips_survey.hips_order, hips_survey)&lt;/span&gt;
&lt;span class="gi"&gt;+    tiles = fetch_tiles(healpix_pixel_indices, order, hips_survey)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    image_data = draw_sky_image(geometry, tiles, hips_survey)


commit 8cdd34cad90facf9348d9bf7014260e9f75aae77
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Mon Jul 3 15:11:40 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Add classmethod create_simple to WCSGeometry

&lt;span class="gh"&gt;diff --git a/docs/getting_started.rst b/docs/getting_started.rst&lt;/span&gt;
&lt;span class="gh"&gt;index 3133c61..9248dd4 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/docs/getting_started.rst&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/docs/getting_started.rst&lt;/span&gt;
&lt;span class="gu"&gt;@@ -20,10 +20,10 @@ To make a sky image with the `hips` package, follow the following three steps:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    from astropy.coordinates import SkyCoord
&lt;span class="w"&gt; &lt;/span&gt;    from hips.utils import WCSGeometry

&lt;span class="gd"&gt;-    geometry = WCSGeometry.create(&lt;/span&gt;
&lt;span class="gi"&gt;+    geometry = WCSGeometry.create_simple(&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;         skydir=SkyCoord(0, 0, unit=&amp;#39;deg&amp;#39;, frame=&amp;#39;galactic&amp;#39;),
&lt;span class="gd"&gt;-         shape=(1000, 2000), coordsys=&amp;#39;GAL&amp;#39;,&lt;/span&gt;
&lt;span class="gd"&gt;-         projection=&amp;#39;AIT&amp;#39;, cdelt=0.01, crpix=(1000, 500),&lt;/span&gt;
&lt;span class="gi"&gt;+         width=2000, height=1000, fov=&amp;quot;3 deg&amp;quot;,&lt;/span&gt;
&lt;span class="gi"&gt;+         coordsys=&amp;#39;galactic&amp;#39;, projection=&amp;#39;AIT&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    )


&lt;span class="gh"&gt;diff --git a/hips/utils/testing.py b/hips/utils/testing.py&lt;/span&gt;
&lt;span class="gh"&gt;index 0563f7a..cdbbbcf 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/testing.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/testing.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -28,7 +28,6 @@ def has_hips_extra():&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        path = Path(os.environ[&amp;#39;HIPS_EXTRA&amp;#39;]) / &amp;#39;datasets/samples/DSS2Red/properties&amp;#39;
&lt;span class="w"&gt; &lt;/span&gt;        if path.is_file():
&lt;span class="w"&gt; &lt;/span&gt;            return True
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    return False


&lt;span class="gu"&gt;@@ -43,19 +42,19 @@ def make_test_wcs_geometry(case=0):&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    if case == 0:
&lt;span class="w"&gt; &lt;/span&gt;        return WCSGeometry.create(
&lt;span class="w"&gt; &lt;/span&gt;            skydir=SkyCoord(3, 4, unit=&amp;#39;deg&amp;#39;, frame=&amp;#39;galactic&amp;#39;),
&lt;span class="gd"&gt;-            shape=(2, 3), coordsys=&amp;#39;GAL&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+            width=2, height=3, coordsys=&amp;#39;galactic&amp;#39;,&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            projection=&amp;#39;CAR&amp;#39;, cdelt=1.0, crpix=(1, 1),
&lt;span class="w"&gt; &lt;/span&gt;        )
&lt;span class="w"&gt; &lt;/span&gt;    elif case == 1:
&lt;span class="w"&gt; &lt;/span&gt;        return WCSGeometry.create(
&lt;span class="w"&gt; &lt;/span&gt;            skydir=SkyCoord(10, 20, unit=&amp;#39;deg&amp;#39;, frame=&amp;#39;galactic&amp;#39;),
&lt;span class="gd"&gt;-            shape=(10, 20), coordsys=&amp;#39;GAL&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+            width=20, height=10, coordsys=&amp;#39;galactic&amp;#39;,&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            projection=&amp;#39;CAR&amp;#39;, cdelt=1.0, crpix=(1, 1),
&lt;span class="w"&gt; &lt;/span&gt;        )
&lt;span class="w"&gt; &lt;/span&gt;    elif case == 2:
&lt;span class="w"&gt; &lt;/span&gt;        return WCSGeometry.create(
&lt;span class="w"&gt; &lt;/span&gt;            skydir=SkyCoord(0, 0, unit=&amp;#39;deg&amp;#39;, frame=&amp;#39;galactic&amp;#39;),
&lt;span class="gd"&gt;-            shape=(1000, 2000), coordsys=&amp;#39;GAL&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+            width=2000, height=1000, coordsys=&amp;#39;galactic&amp;#39;,&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            projection=&amp;#39;AIT&amp;#39;, cdelt=0.01, crpix=(1000, 500),
&lt;span class="w"&gt; &lt;/span&gt;        )
&lt;span class="w"&gt; &lt;/span&gt;    else:
&lt;span class="gh"&gt;diff --git a/hips/utils/tests/test_healpix.py b/hips/utils/tests/test_healpix.py&lt;/span&gt;
&lt;span class="gh"&gt;index 9094894..9d0dd47 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/tests/test_healpix.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/tests/test_healpix.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -5,7 +5,7 @@ from numpy.testing import assert_allclose&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from astropy.coordinates import SkyCoord
&lt;span class="w"&gt; &lt;/span&gt;import healpy as hp
&lt;span class="w"&gt; &lt;/span&gt;from ..healpix import boundaries, compute_healpix_pixel_indices
&lt;span class="gd"&gt;-from .test_wcs import make_test_wcs_geometry&lt;/span&gt;
&lt;span class="gi"&gt;+from ..testing import make_test_wcs_geometry&lt;/span&gt;


&lt;span class="w"&gt; &lt;/span&gt;def test_boundaries():
&lt;span class="gu"&gt;@@ -23,8 +23,8 @@ def test_boundaries():&lt;/span&gt;


&lt;span class="w"&gt; &lt;/span&gt;compute_healpix_pixel_indices_pars = [
&lt;span class="gd"&gt;-    dict(frame=&amp;#39;galactic&amp;#39;, ipix=[269, 270, 271, 280, 282, 283, 292, 293, 295, 304, 305, 306]),&lt;/span&gt;
&lt;span class="gd"&gt;-    dict(frame=&amp;#39;icrs&amp;#39;, ipix=[448, 449, 450, 451, 454, 456, 457, 460, 661, 663, 669]),&lt;/span&gt;
&lt;span class="gi"&gt;+    dict(frame=&amp;#39;galactic&amp;#39;, ipix=[269, 271, 280, 282, 283, 304, 305, 307, 308, 310]),&lt;/span&gt;
&lt;span class="gi"&gt;+    dict(frame=&amp;#39;icrs&amp;#39;, ipix=[448, 449, 450, 451, 454, 456, 457, 458, 459, 460]),&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;]


&lt;span class="gh"&gt;diff --git a/hips/utils/tests/test_wcs.py b/hips/utils/tests/test_wcs.py&lt;/span&gt;
&lt;span class="gh"&gt;index e4f6749..6673f69 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/tests/test_wcs.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/tests/test_wcs.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,18 +1,39 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="w"&gt; &lt;/span&gt;from numpy.testing import assert_allclose
&lt;span class="gd"&gt;-from ..testing import make_test_wcs_geometry&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gi"&gt;+from astropy.coordinates import SkyCoord&lt;/span&gt;
&lt;span class="gi"&gt;+from ..wcs import WCSGeometry&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;class TestWCSGeometry:
&lt;span class="w"&gt; &lt;/span&gt;    def setup(self):
&lt;span class="gd"&gt;-        self.wcs_geometry = make_test_wcs_geometry(case=0)&lt;/span&gt;
&lt;span class="gi"&gt;+        self.wcs_geometry = WCSGeometry.create_simple(&lt;/span&gt;
&lt;span class="gi"&gt;+            skydir=SkyCoord(0, 0, unit=&amp;#39;deg&amp;#39;, frame=&amp;#39;galactic&amp;#39;),&lt;/span&gt;
&lt;span class="gi"&gt;+            width=2000, height=1000, fov=&amp;quot;3 deg&amp;quot;,&lt;/span&gt;
&lt;span class="gi"&gt;+            coordsys=&amp;#39;galactic&amp;#39;, projection=&amp;#39;AIT&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        )&lt;/span&gt;

&lt;span class="gd"&gt;-    def test_center(self):&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_galactic_frame_center(self):&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        c = self.wcs_geometry.center_skycoord
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        assert c.frame.name == &amp;#39;galactic&amp;#39;
&lt;span class="gd"&gt;-        assert_allclose(c.l.deg, 2, atol=1e-2)&lt;/span&gt;
&lt;span class="gd"&gt;-        assert_allclose(c.b.deg, 4.5, atol=1e-2)&lt;/span&gt;
&lt;span class="gi"&gt;+        assert_allclose(c.l.deg, 359.99, atol=1e-2)&lt;/span&gt;
&lt;span class="gi"&gt;+        assert_allclose(c.b.deg, 0.00075, atol=1e-2)&lt;/span&gt;
&lt;span class="gi"&gt;+        assert_allclose(self.wcs_geometry.wcs.wcs.crpix, [1000., 500.])&lt;/span&gt;
&lt;span class="gi"&gt;+        assert_allclose(self.wcs_geometry.wcs.wcs.cdelt, [-0.0015, 0.0015])&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def test_celestial_frame(self):
&lt;span class="gd"&gt;-        pass&lt;/span&gt;
&lt;span class="gd"&gt;-        # TODO (Adeel): add a test&lt;/span&gt;
&lt;span class="gi"&gt;+        wcs_geometry = WCSGeometry.create_simple(&lt;/span&gt;
&lt;span class="gi"&gt;+            skydir=SkyCoord(0, 0, unit=&amp;#39;deg&amp;#39;, frame=&amp;#39;icrs&amp;#39;),&lt;/span&gt;
&lt;span class="gi"&gt;+            width=2000, height=1000, fov=&amp;quot;3 deg&amp;quot;,&lt;/span&gt;
&lt;span class="gi"&gt;+            coordsys=&amp;#39;icrs&amp;#39;, projection=&amp;#39;AIT&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        )&lt;/span&gt;
&lt;span class="gi"&gt;+        c = wcs_geometry.center_skycoord&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        assert c.frame.name == &amp;#39;icrs&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        assert_allclose(c.ra.deg, 359.99, atol=1e-2)&lt;/span&gt;
&lt;span class="gi"&gt;+        assert_allclose(c.dec.deg, 0.00075, atol=1e-2)&lt;/span&gt;
&lt;span class="gi"&gt;+        assert_allclose(self.wcs_geometry.wcs.wcs.crpix, [1000., 500.])&lt;/span&gt;
&lt;span class="gi"&gt;+        assert_allclose(self.wcs_geometry.wcs.wcs.cdelt, [-0.0015, 0.0015])&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def skycoord_separation(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        skydir=SkyCoord(0, 0, unit=&amp;#39;deg&amp;#39;, frame=&amp;#39;icrs&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        skydir.separation(skydir).degree == 2&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/utils/wcs.py b/hips/utils/wcs.py&lt;/span&gt;
&lt;span class="gh"&gt;index 5e43f6f..31e2e9b 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/wcs.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/wcs.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -4,16 +4,19 @@ from typing import Tuple&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;import numpy as np
&lt;span class="w"&gt; &lt;/span&gt;from astropy.coordinates import SkyCoord
&lt;span class="w"&gt; &lt;/span&gt;from astropy.wcs import WCS
&lt;span class="gi"&gt;+from astropy.coordinates import Angle&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from astropy.wcs.utils import pixel_to_skycoord, wcs_to_celestial_frame

&lt;span class="w"&gt; &lt;/span&gt;__all__ = [
&lt;span class="w"&gt; &lt;/span&gt;    &amp;#39;WCSGeometry&amp;#39;,
&lt;span class="w"&gt; &lt;/span&gt;]

&lt;span class="gd"&gt;-__doctest_skip__ = [&amp;#39;WCSGeometry&amp;#39;]&lt;/span&gt;
&lt;span class="gi"&gt;+__doctest_skip__ = [&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;#39;*&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+]&lt;/span&gt;

&lt;span class="gd"&gt;-Shape = namedtuple(&amp;#39;Shape&amp;#39;, [&amp;#39;ny&amp;#39;, &amp;#39;nx&amp;#39;])&lt;/span&gt;
&lt;span class="gd"&gt;-&amp;quot;&amp;quot;&amp;quot;Helper for 2-dim image shape, to make it clearer what value is x and y.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+Shape = namedtuple(&amp;#39;Shape&amp;#39;, [&amp;#39;width&amp;#39;, &amp;#39;height&amp;#39;])&lt;/span&gt;
&lt;span class="gi"&gt;+&amp;quot;&amp;quot;&amp;quot;Helper for 2-dim image shape, to make it clearer what value is width and height.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;


&lt;span class="w"&gt; &lt;/span&gt;class WCSGeometry:
&lt;span class="gu"&gt;@@ -23,8 +26,10 @@ class WCSGeometry:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    ----------
&lt;span class="w"&gt; &lt;/span&gt;    wcs : `~astropy.wcs.WCS`
&lt;span class="w"&gt; &lt;/span&gt;        WCS projection object
&lt;span class="gd"&gt;-    shape : tuple&lt;/span&gt;
&lt;span class="gd"&gt;-        Shape of the image&lt;/span&gt;
&lt;span class="gi"&gt;+    width : int&lt;/span&gt;
&lt;span class="gi"&gt;+        Width of the image in pixels&lt;/span&gt;
&lt;span class="gi"&gt;+    height : int&lt;/span&gt;
&lt;span class="gi"&gt;+        Height of the image in pixels&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    Examples
&lt;span class="w"&gt; &lt;/span&gt;    --------
&lt;span class="gu"&gt;@@ -32,7 +37,7 @@ class WCSGeometry:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;gt;&amp;gt;&amp;gt; from hips.utils import WCSGeometry
&lt;span class="w"&gt; &lt;/span&gt;    &amp;gt;&amp;gt;&amp;gt; skycoord = SkyCoord(10, 20, unit=&amp;#39;deg&amp;#39;)
&lt;span class="w"&gt; &lt;/span&gt;    &amp;gt;&amp;gt;&amp;gt; wcs_geometry = WCSGeometry.create(
&lt;span class="gd"&gt;-    ...     skydir=skycoord, shape=(10, 20),&lt;/span&gt;
&lt;span class="gi"&gt;+    ...     skydir=skycoord, width=20, height=10,&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    ...     coordsys=&amp;#39;CEL&amp;#39;, projection=&amp;#39;AIT&amp;#39;,
&lt;span class="w"&gt; &lt;/span&gt;    ...     cdelt=1.0, crpix=(1., 1.),
&lt;span class="w"&gt; &lt;/span&gt;    ... )
&lt;span class="gu"&gt;@@ -46,20 +51,20 @@ class WCSGeometry:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    CDELT : -1.0  1.0
&lt;span class="w"&gt; &lt;/span&gt;    NAXIS : 0  0
&lt;span class="w"&gt; &lt;/span&gt;    &amp;gt;&amp;gt;&amp;gt; wcs_geometry.shape
&lt;span class="gd"&gt;-    Shape(ny=10, nx=20)&lt;/span&gt;
&lt;span class="gi"&gt;+    Shape(width=20, height=10)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;    WCS_ORIGIN_DEFAULT = 0
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;Default WCS transform origin, to be used in all WCS pix &amp;lt;-&amp;gt; world calls.&amp;quot;&amp;quot;&amp;quot;

&lt;span class="gd"&gt;-    def __init__(self, wcs: WCS, shape: tuple) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+    def __init__(self, wcs: WCS, width: int, height: int) -&amp;gt; None:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        self.wcs = wcs
&lt;span class="gd"&gt;-        self.shape = Shape(*shape)&lt;/span&gt;
&lt;span class="gi"&gt;+        self.shape = Shape(*(width, height))&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    @property
&lt;span class="w"&gt; &lt;/span&gt;    def center_pix(self) -&amp;gt; Tuple[float, float]:
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Image center in pixel coordinates (tuple of x, y).&amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-        x = float(self.shape.nx - 1) / 2&lt;/span&gt;
&lt;span class="gd"&gt;-        y = float(self.shape.ny - 1) / 2&lt;/span&gt;
&lt;span class="gi"&gt;+        x = float(self.shape.width - 1) / 2&lt;/span&gt;
&lt;span class="gi"&gt;+        y = float(self.shape.height - 1) / 2&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        return x, y

&lt;span class="w"&gt; &lt;/span&gt;    @property
&lt;span class="gu"&gt;@@ -78,7 +83,7 @@ class WCSGeometry:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        return pixel_to_skycoord(x, y, self.wcs, self.WCS_ORIGIN_DEFAULT)

&lt;span class="w"&gt; &lt;/span&gt;    @classmethod
&lt;span class="gd"&gt;-    def create(cls, skydir: SkyCoord, shape: tuple, coordsys: str = &amp;#39;CEL&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+    def create(cls, skydir: SkyCoord, width: int, height: int, coordsys: str = &amp;#39;icrs&amp;#39;,&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;               projection: str = &amp;#39;AIT&amp;#39;, cdelt: float = 1.0, crpix: tuple = (1., 1.)) -&amp;gt; &amp;#39;WCSGeometry&amp;#39;:
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Create WCS object programmatically (`WCSGeometry`).

&lt;span class="gu"&gt;@@ -86,27 +91,30 @@ class WCSGeometry:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        ----------
&lt;span class="w"&gt; &lt;/span&gt;        skydir : `~astropy.coordinates.SkyCoord`
&lt;span class="w"&gt; &lt;/span&gt;            Sky coordinate of the WCS reference point
&lt;span class="gd"&gt;-        shape : `tuple`&lt;/span&gt;
&lt;span class="gd"&gt;-            Shape of the image (Numpy axis order: y, x)&lt;/span&gt;
&lt;span class="gd"&gt;-        coordsys : `str`&lt;/span&gt;
&lt;span class="gi"&gt;+        width : `int`&lt;/span&gt;
&lt;span class="gi"&gt;+            Width of the image in pixels&lt;/span&gt;
&lt;span class="gi"&gt;+        height : `int`&lt;/span&gt;
&lt;span class="gi"&gt;+            Height of the image in pixels&lt;/span&gt;
&lt;span class="gi"&gt;+        coordsys : {&amp;#39;icrs&amp;#39;, &amp;#39;galactic&amp;#39;}&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            Coordinate system
&lt;span class="w"&gt; &lt;/span&gt;        projection : `str`
&lt;span class="gd"&gt;-            Projection of the WCS object&lt;/span&gt;
&lt;span class="gi"&gt;+            Projection of the WCS object.&lt;/span&gt;
&lt;span class="gi"&gt;+            To see list of supported projections&lt;/span&gt;
&lt;span class="gi"&gt;+            visit: http://docs.astropy.org/en/stable/wcs/#supported-projections&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        cdelt : `float`
&lt;span class="w"&gt; &lt;/span&gt;            Coordinate increment at reference point
&lt;span class="w"&gt; &lt;/span&gt;        crpix : `tuple`
&lt;span class="w"&gt; &lt;/span&gt;            Pixel coordinates of reference point
&lt;span class="w"&gt; &lt;/span&gt;            (WCS axis order: x, y and FITS convention origin=1)
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        w = WCS(naxis=2)

&lt;span class="gd"&gt;-        if coordsys == &amp;#39;CEL&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+        if coordsys == &amp;#39;icrs&amp;#39;:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            w.wcs.ctype[0] = f&amp;#39;RA---{projection}&amp;#39;
&lt;span class="w"&gt; &lt;/span&gt;            w.wcs.ctype[1] = f&amp;#39;DEC--{projection}&amp;#39;
&lt;span class="w"&gt; &lt;/span&gt;            w.wcs.crval[0] = skydir.icrs.ra.deg
&lt;span class="w"&gt; &lt;/span&gt;            w.wcs.crval[1] = skydir.icrs.dec.deg
&lt;span class="gd"&gt;-        elif coordsys == &amp;#39;GAL&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+        elif coordsys == &amp;#39;galactic&amp;#39;:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            w.wcs.ctype[0] = f&amp;#39;GLON-{projection}&amp;#39;
&lt;span class="w"&gt; &lt;/span&gt;            w.wcs.ctype[1] = f&amp;#39;GLAT-{projection}&amp;#39;
&lt;span class="w"&gt; &lt;/span&gt;            w.wcs.crval[0] = skydir.galactic.l.deg
&lt;span class="gu"&gt;@@ -122,7 +130,57 @@ class WCSGeometry:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        w = WCS(w.to_header())

&lt;span class="gd"&gt;-        return cls(w, shape)&lt;/span&gt;
&lt;span class="gi"&gt;+        return cls(w, width, height)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @classmethod&lt;/span&gt;
&lt;span class="gi"&gt;+    def create_simple(cls, skydir: SkyCoord, width: int, height: int, fov: {str, Angle},&lt;/span&gt;
&lt;span class="gi"&gt;+                      coordsys: str = &amp;#39;icrs&amp;#39;, projection: str = &amp;#39;AIT&amp;#39;) -&amp;gt; &amp;#39;WCSGeometry&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Create WCS object programmatically using field of view (`WCSGeometry`).&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        Parameters&lt;/span&gt;
&lt;span class="gi"&gt;+        ----------&lt;/span&gt;
&lt;span class="gi"&gt;+        skydir : `~astropy.coordinates.SkyCoord`&lt;/span&gt;
&lt;span class="gi"&gt;+            Sky coordinate of the WCS reference point&lt;/span&gt;
&lt;span class="gi"&gt;+        width : `int`&lt;/span&gt;
&lt;span class="gi"&gt;+            Width of the image in pixels&lt;/span&gt;
&lt;span class="gi"&gt;+        height : `int`&lt;/span&gt;
&lt;span class="gi"&gt;+            Height of the image in pixels&lt;/span&gt;
&lt;span class="gi"&gt;+        fov: `str` or Angle&lt;/span&gt;
&lt;span class="gi"&gt;+            Field of view&lt;/span&gt;
&lt;span class="gi"&gt;+        coordsys : {&amp;#39;icrs&amp;#39;, &amp;#39;galactic&amp;#39;}&lt;/span&gt;
&lt;span class="gi"&gt;+            Coordinate system&lt;/span&gt;
&lt;span class="gi"&gt;+        projection : `str`&lt;/span&gt;
&lt;span class="gi"&gt;+            Projection of the WCS object.&lt;/span&gt;
&lt;span class="gi"&gt;+            To see list of supported projections&lt;/span&gt;
&lt;span class="gi"&gt;+            visit: http://docs.astropy.org/en/stable/wcs/#supported-projections&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        Examples&lt;/span&gt;
&lt;span class="gi"&gt;+        --------&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; from astropy.coordinates import SkyCoord&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; from hips.utils import WCSGeometry&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; skycoord = SkyCoord(10, 20, unit=&amp;#39;deg&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; wcs_geometry = WCSGeometry.create_simple(&lt;/span&gt;
&lt;span class="gi"&gt;+        ...     skydir=SkyCoord(0, 0, unit=&amp;#39;deg&amp;#39;, frame=&amp;#39;galactic&amp;#39;),&lt;/span&gt;
&lt;span class="gi"&gt;+        ...     width=2000, height=1000, fov=&amp;quot;3 deg&amp;quot;,&lt;/span&gt;
&lt;span class="gi"&gt;+        ...     coordsys=&amp;#39;galactic&amp;#39;, projection=&amp;#39;AIT&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        ... )&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; wcs_geometry.wcs&lt;/span&gt;
&lt;span class="gi"&gt;+        Number of WCS axes: 2&lt;/span&gt;
&lt;span class="gi"&gt;+        CTYPE : &amp;#39;GLON-AIT&amp;#39;  &amp;#39;GLAT-AIT&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        CRVAL : 0.0  0.0&lt;/span&gt;
&lt;span class="gi"&gt;+        CRPIX : 500.0  1000.0&lt;/span&gt;
&lt;span class="gi"&gt;+        PC1_1 PC1_2  : 1.0  0.0&lt;/span&gt;
&lt;span class="gi"&gt;+        PC2_1 PC2_2  : 0.0  1.0&lt;/span&gt;
&lt;span class="gi"&gt;+        CDELT : -0.0015  0.0015&lt;/span&gt;
&lt;span class="gi"&gt;+        NAXIS : 0  0&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; wcs_geometry.shape&lt;/span&gt;
&lt;span class="gi"&gt;+        Shape(width=2000, height=1000)&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        fov = Angle(fov)&lt;/span&gt;
&lt;span class="gi"&gt;+        crpix = (float(width / 2), float(height / 2))&lt;/span&gt;
&lt;span class="gi"&gt;+        cdelt = float(fov.degree) / float(max(width, height))&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        return cls.create(skydir, width, height, coordsys, projection, cdelt, crpix)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    @property
&lt;span class="w"&gt; &lt;/span&gt;    def celestial_frame(self) -&amp;gt; str:

commit 439bd58bc190c9d957ce4fce9e0acf9c45a40537
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Wed Jul 5 14:40:03 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Remove tile_width from HipsTileMeta

&lt;span class="gh"&gt;diff --git a/hips/draw/simple.py b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index eb28f51..c912b62 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -14,7 +14,7 @@ __all__ = [&lt;/span&gt;


&lt;span class="w"&gt; &lt;/span&gt;# TODO: Fix type annotation issue
&lt;span class="gd"&gt;-def draw_sky_image(geometry: WCSGeometry, tiles: Generator[HipsTile, Any, Any]) -&amp;gt; np.ndarray:&lt;/span&gt;
&lt;span class="gi"&gt;+def draw_sky_image(geometry: WCSGeometry, tiles: Generator[HipsTile, Any, Any], hips_survey: HipsSurveyProperties) -&amp;gt; np.ndarray:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;Draw sky image using the simple and quick method.

&lt;span class="w"&gt; &lt;/span&gt;    Parameters
&lt;span class="gu"&gt;@@ -23,6 +23,8 @@ def draw_sky_image(geometry: WCSGeometry, tiles: Generator[HipsTile, Any, Any])&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        An object of WCSGeometry
&lt;span class="w"&gt; &lt;/span&gt;    tiles : List[HipsTile]
&lt;span class="w"&gt; &lt;/span&gt;        A list of HipsTile
&lt;span class="gi"&gt;+    hips_survey : `~hips.HipsSurveyProperties`&lt;/span&gt;
&lt;span class="gi"&gt;+        HiPS survey properties&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    Returns
&lt;span class="w"&gt; &lt;/span&gt;    -------
&lt;span class="gu"&gt;@@ -31,7 +33,7 @@ def draw_sky_image(geometry: WCSGeometry, tiles: Generator[HipsTile, Any, Any])&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;    image = np.zeros(geometry.shape)
&lt;span class="w"&gt; &lt;/span&gt;    for tile in tiles:
&lt;span class="gd"&gt;-        painter = SimpleTilePainter(geometry, tile)&lt;/span&gt;
&lt;span class="gi"&gt;+        painter = SimpleTilePainter(geometry, hips_survey, tile)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        image += painter.warp_image()
&lt;span class="w"&gt; &lt;/span&gt;    return image

&lt;span class="gu"&gt;@@ -45,20 +47,33 @@ class SimpleTilePainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    ----------
&lt;span class="w"&gt; &lt;/span&gt;    geometry : `~hips.utils.WCSGeometry`
&lt;span class="w"&gt; &lt;/span&gt;        An object of WCSGeometry
&lt;span class="gi"&gt;+    hips_survey : `~hips.HipsSurveyProperties`&lt;/span&gt;
&lt;span class="gi"&gt;+        HiPS survey properties&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    tile : `HipsTile`
&lt;span class="w"&gt; &lt;/span&gt;       An object of HipsTile
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;

&lt;span class="gd"&gt;-    def __init__(self, geometry: WCSGeometry, tile: HipsTile) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+    def __init__(self, geometry: WCSGeometry, hips_survey: HipsSurveyProperties, tile: HipsTile) -&amp;gt; None:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        self.geometry = geometry
&lt;span class="gi"&gt;+        self.hips_survey = hips_survey&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        self.tile = tile

&lt;span class="w"&gt; &lt;/span&gt;    @property
&lt;span class="gi"&gt;+    def dst(self) -&amp;gt; np.ndarray:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Destination array for projective transform&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        width = self.hips_survey.tile_width&lt;/span&gt;
&lt;span class="gi"&gt;+        return np.array(&lt;/span&gt;
&lt;span class="gi"&gt;+            [[width - 1, 0],&lt;/span&gt;
&lt;span class="gi"&gt;+             [width - 1, width - 1],&lt;/span&gt;
&lt;span class="gi"&gt;+             [0, width - 1],&lt;/span&gt;
&lt;span class="gi"&gt;+             [0, 0]],&lt;/span&gt;
&lt;span class="gi"&gt;+        )&lt;/span&gt;
&lt;span class="gi"&gt;+    @property&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    def projection(self) -&amp;gt; ProjectiveTransform:
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Estimate projective transformation on a HiPS tile&amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;        corners = self.tile.meta.skycoord_corners.to_pixel(self.geometry.wcs)
&lt;span class="w"&gt; &lt;/span&gt;        src = np.array(corners).T.reshape((4, 2))
&lt;span class="gd"&gt;-        dst = self.tile.meta.dst&lt;/span&gt;
&lt;span class="gi"&gt;+        dst = self.dst&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        pt = ProjectiveTransform()
&lt;span class="w"&gt; &lt;/span&gt;        pt.estimate(src, dst)
&lt;span class="w"&gt; &lt;/span&gt;        return pt
&lt;span class="gu"&gt;@@ -126,6 +141,6 @@ def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties) -&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    # TODO: this isn&amp;#39;t a good API. Will become better when we have a cache.
&lt;span class="w"&gt; &lt;/span&gt;    tiles = fetch_tiles(healpix_pixel_indices, hips_survey.hips_order, hips_survey)

&lt;span class="gd"&gt;-    image_data = draw_sky_image(geometry, tiles)&lt;/span&gt;
&lt;span class="gi"&gt;+    image_data = draw_sky_image(geometry, tiles, hips_survey)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    return image_data
&lt;span class="gh"&gt;diff --git a/hips/draw/tests/test_simple.py b/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index ec6bf1a..276c301 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -13,25 +13,27 @@ def get_test_tiles():&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    hips_survey = HipsSurveyProperties.read(filename)

&lt;span class="w"&gt; &lt;/span&gt;    tile1 = HipsTile.read(
&lt;span class="gd"&gt;-        meta=HipsTileMeta(order=3, ipix=450, file_format=&amp;#39;fits&amp;#39;, frame=hips_survey.astropy_frame,&lt;/span&gt;
&lt;span class="gd"&gt;-                          tile_width=512),&lt;/span&gt;
&lt;span class="gi"&gt;+        meta=HipsTileMeta(order=3, ipix=450, file_format=&amp;#39;fits&amp;#39;, frame=hips_survey.astropy_frame),&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        full_path=get_hips_extra_file(&amp;#39;datasets/samples/DSS2Red/Norder3/Dir0/Npix450.fits&amp;#39;),
&lt;span class="w"&gt; &lt;/span&gt;    )

&lt;span class="w"&gt; &lt;/span&gt;    tile2 = HipsTile.read(
&lt;span class="gd"&gt;-        meta=HipsTileMeta(order=3, ipix=451, file_format=&amp;#39;fits&amp;#39;, frame=hips_survey.astropy_frame,&lt;/span&gt;
&lt;span class="gd"&gt;-                          tile_width=512),&lt;/span&gt;
&lt;span class="gi"&gt;+        meta=HipsTileMeta(order=3, ipix=451, file_format=&amp;#39;fits&amp;#39;, frame=hips_survey.astropy_frame),&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        full_path=get_hips_extra_file(&amp;#39;datasets/samples/DSS2Red/Norder3/Dir0/Npix451.fits&amp;#39;),
&lt;span class="w"&gt; &lt;/span&gt;    )

&lt;span class="w"&gt; &lt;/span&gt;    return [tile1, tile2]


&lt;span class="gi"&gt;+@remote_data&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;@requires_hips_extra()
&lt;span class="w"&gt; &lt;/span&gt;def test_draw_sky_image():
&lt;span class="w"&gt; &lt;/span&gt;    geometry = make_test_wcs_geometry(case=2)
&lt;span class="w"&gt; &lt;/span&gt;    tiles = get_test_tiles()
&lt;span class="gd"&gt;-    data = draw_sky_image(geometry, tiles)&lt;/span&gt;
&lt;span class="gi"&gt;+    url = &amp;#39;https://raw.githubusercontent.com/hipspy/hips-extra/master/datasets/samples/DSS2Red/properties&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+    hips_survey = HipsSurveyProperties.fetch(url)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    data = draw_sky_image(geometry, tiles, hips_survey)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    assert data.shape == geometry.shape
&lt;span class="w"&gt; &lt;/span&gt;    assert data.dtype == np.float64
&lt;span class="gh"&gt;diff --git a/hips/tiles/surveys.py b/hips/tiles/surveys.py&lt;/span&gt;
&lt;span class="gh"&gt;index 8013ef5..0190a06 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/surveys.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/surveys.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -128,7 +128,13 @@ class HipsSurveyProperties:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;HiPS access url&amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;        return self.data[&amp;#39;moc_access_url&amp;#39;].rsplit(&amp;#39;/&amp;#39;, 1)[0]

&lt;span class="gi"&gt;+    @property&lt;/span&gt;
&lt;span class="gi"&gt;+    def tile_width(self) -&amp;gt; int:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;HiPS tile width&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        return int(self.data[&amp;#39;hips_tile_width&amp;#39;]) or 512&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    def directory(self, ipix: int) -&amp;gt; int:
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Directory index containing HiPS tile(s)&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        return (ipix // 10000) * 10000

&lt;span class="w"&gt; &lt;/span&gt;    def tile_access_url(self, order: int, ipix: int) -&amp;gt; str:
&lt;span class="gh"&gt;diff --git a/hips/tiles/tests/test_surveys.py b/hips/tiles/tests/test_surveys.py&lt;/span&gt;
&lt;span class="gh"&gt;index df4e812..5192f45 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tests/test_surveys.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tests/test_surveys.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -2,7 +2,7 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from astropy.utils.data import get_pkg_data_filename
&lt;span class="w"&gt; &lt;/span&gt;from astropy.tests.helper import remote_data
&lt;span class="w"&gt; &lt;/span&gt;from ..surveys import HipsSurveyProperties, HipsSurveyPropertiesList
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gi"&gt;+from ...utils.testing import get_hips_extra_file, requires_hips_extra&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;class TestHipsSurveyProperties:
&lt;span class="w"&gt; &lt;/span&gt;    @classmethod
&lt;span class="gu"&gt;@@ -35,6 +35,13 @@ class TestHipsSurveyProperties:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        assert self.hips_survey_property.tile_access_url(order=9, ipix=54321) == &amp;#39;http://alasky.u-strasbg.fr/DSS/DSSColor/Norder9/Dir50000/&amp;#39;


&lt;span class="gi"&gt;+    @requires_hips_extra()&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_tile_width(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        filename = get_hips_extra_file(&amp;#39;datasets/samples/Planck-HFI143/properties&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        survey = HipsSurveyProperties.read(filename)&lt;/span&gt;
&lt;span class="gi"&gt;+        assert survey.tile_width == 256&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;class TestHipsSurveyPropertiesList:
&lt;span class="w"&gt; &lt;/span&gt;    @classmethod
&lt;span class="w"&gt; &lt;/span&gt;    def setup_class(cls):
&lt;span class="gh"&gt;diff --git a/hips/tiles/tests/test_tile.py b/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gh"&gt;index 62aa85d..022ad92 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -69,7 +69,7 @@ class TestHipsTile:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;class TestHipsTileMeta:
&lt;span class="w"&gt; &lt;/span&gt;    @classmethod
&lt;span class="w"&gt; &lt;/span&gt;    def setup_class(cls):
&lt;span class="gd"&gt;-        cls.meta = HipsTileMeta(order=3, ipix=450, file_format=&amp;#39;fits&amp;#39;, frame=&amp;#39;icrs&amp;#39;, tile_width=512)&lt;/span&gt;
&lt;span class="gi"&gt;+        cls.meta = HipsTileMeta(order=3, ipix=450, file_format=&amp;#39;fits&amp;#39;, frame=&amp;#39;icrs&amp;#39;)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def test_path(self):
&lt;span class="w"&gt; &lt;/span&gt;        assert str(self.meta.path) == &amp;#39;hips/tiles/tests/data&amp;#39;
&lt;span class="gu"&gt;@@ -83,16 +83,12 @@ class TestHipsTileMeta:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    def test_nside(self):
&lt;span class="w"&gt; &lt;/span&gt;        assert self.meta.nside == 8

&lt;span class="gd"&gt;-    def test_dst(self):&lt;/span&gt;
&lt;span class="gd"&gt;-        dst = np.array([[511, 0], [511, 511], [0, 511], [0, 0]])&lt;/span&gt;
&lt;span class="gd"&gt;-        assert_allclose(self.meta.dst, dst)&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    def test_skycoord_corners(self):
&lt;span class="w"&gt; &lt;/span&gt;        assert_allclose(self.meta.skycoord_corners.data.lat.deg, [-24.624318, -30., -35.685335, -30.])
&lt;span class="w"&gt; &lt;/span&gt;        assert_allclose(self.meta.skycoord_corners.data.lon.deg, [264.375, 258.75, 264.375, 270.])
&lt;span class="w"&gt; &lt;/span&gt;        assert self.meta.skycoord_corners.frame.name == &amp;#39;icrs&amp;#39;

&lt;span class="gd"&gt;-        meta = HipsTileMeta(order=3, ipix=450, file_format=&amp;#39;fits&amp;#39;, frame=&amp;#39;galactic&amp;#39;, tile_width=512)&lt;/span&gt;
&lt;span class="gi"&gt;+        meta = HipsTileMeta(order=3, ipix=450, file_format=&amp;#39;fits&amp;#39;, frame=&amp;#39;galactic&amp;#39;)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        assert_allclose(meta.skycoord_corners.data.lat.deg, [-24.624318, -30., -35.685335, -30.])
&lt;span class="w"&gt; &lt;/span&gt;        assert_allclose(meta.skycoord_corners.data.lon.deg, [264.375, 258.75, 264.375, 270.])
&lt;span class="w"&gt; &lt;/span&gt;        assert meta.skycoord_corners.frame.name == &amp;#39;galactic&amp;#39;
&lt;span class="gh"&gt;diff --git a/hips/tiles/tile.py b/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gh"&gt;index 550969c..1868daf 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -34,32 +34,28 @@ class HipsTileMeta:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        File format
&lt;span class="w"&gt; &lt;/span&gt;    frame : {&amp;#39;icrs&amp;#39;, &amp;#39;galactic&amp;#39;, &amp;#39;ecliptic&amp;#39;}
&lt;span class="w"&gt; &lt;/span&gt;        Sky coordinate frame
&lt;span class="gd"&gt;-    tile_width : `int`&lt;/span&gt;
&lt;span class="gd"&gt;-        Tile width (in pixels)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    Examples
&lt;span class="w"&gt; &lt;/span&gt;    --------
&lt;span class="w"&gt; &lt;/span&gt;    &amp;gt;&amp;gt;&amp;gt; from hips.tiles import HipsTileMeta
&lt;span class="gd"&gt;-    &amp;gt;&amp;gt;&amp;gt; tile_meta = HipsTileMeta(order=3, ipix=450, file_format=&amp;#39;fits&amp;#39;, frame=&amp;#39;icrs&amp;#39;, tile_width=512)&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;gt;&amp;gt;&amp;gt; tile_meta = HipsTileMeta(order=3, ipix=450, file_format=&amp;#39;fits&amp;#39;, frame=&amp;#39;icrs&amp;#39;)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;gt;&amp;gt;&amp;gt; tile_meta.skycoord_corners
&lt;span class="w"&gt; &lt;/span&gt;    &amp;lt;SkyCoord (ICRS): (ra, dec) in deg
&lt;span class="w"&gt; &lt;/span&gt;    [( 264.375, -24.62431835), ( 258.75 , -30.        ),
&lt;span class="w"&gt; &lt;/span&gt;    ( 264.375, -35.68533471), ( 270.   , -30.        )]&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;

&lt;span class="gd"&gt;-    def __init__(self, order: int, ipix: int, file_format: str, frame: str = &amp;#39;galactic&amp;#39;, tile_width: int = 512) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+    def __init__(self, order: int, ipix: int, file_format: str, frame: str = &amp;#39;galactic&amp;#39;) -&amp;gt; None:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        self.order = order
&lt;span class="w"&gt; &lt;/span&gt;        self.ipix = ipix
&lt;span class="w"&gt; &lt;/span&gt;        self.file_format = file_format
&lt;span class="w"&gt; &lt;/span&gt;        self.frame = frame
&lt;span class="gd"&gt;-        self.tile_width = tile_width&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def __eq__(self, other: &amp;#39;HipsTileMeta&amp;#39;) -&amp;gt; bool:
&lt;span class="w"&gt; &lt;/span&gt;        return (
&lt;span class="w"&gt; &lt;/span&gt;            self.order == other.order and
&lt;span class="w"&gt; &lt;/span&gt;            self.ipix == other.ipix and
&lt;span class="gd"&gt;-            self.file_format == other.file_format and&lt;/span&gt;
&lt;span class="gd"&gt;-            self.tile_width == other.tile_width&lt;/span&gt;
&lt;span class="gi"&gt;+            self.file_format == other.file_format&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        )

&lt;span class="w"&gt; &lt;/span&gt;    @property
&lt;span class="gu"&gt;@@ -83,16 +79,6 @@ class HipsTileMeta:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        return hp.order2nside(self.order)

&lt;span class="w"&gt; &lt;/span&gt;    @property
&lt;span class="gd"&gt;-    def dst(self) -&amp;gt; np.ndarray:&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;Destination array for projective transform&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-        return np.array(&lt;/span&gt;
&lt;span class="gd"&gt;-            [[self.tile_width - 1, 0],&lt;/span&gt;
&lt;span class="gd"&gt;-             [self.tile_width - 1, self.tile_width - 1],&lt;/span&gt;
&lt;span class="gd"&gt;-             [0, self.tile_width - 1],&lt;/span&gt;
&lt;span class="gd"&gt;-             [0, 0]],&lt;/span&gt;
&lt;span class="gd"&gt;-        )&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    @property&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    def skycoord_corners(self) -&amp;gt; SkyCoord:
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Corner values for a HiPS tile&amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;        theta, phi = boundaries(self.nside, self.ipix)

commit 18fc6b656fe873955c10d7505c207cfed030615f
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Wed Jul 5 13:26:31 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Refactor test cases for HipsTile

&lt;span class="gh"&gt;diff --git a/hips/draw/tests/test_simple.py b/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index e6324bf..ec6bf1a 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -15,13 +15,13 @@ def get_test_tiles():&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    tile1 = HipsTile.read(
&lt;span class="w"&gt; &lt;/span&gt;        meta=HipsTileMeta(order=3, ipix=450, file_format=&amp;#39;fits&amp;#39;, frame=hips_survey.astropy_frame,
&lt;span class="w"&gt; &lt;/span&gt;                          tile_width=512),
&lt;span class="gd"&gt;-        filename=get_hips_extra_file(&amp;#39;datasets/samples/DSS2Red/Norder3/Dir0/Npix450.fits&amp;#39;),&lt;/span&gt;
&lt;span class="gi"&gt;+        full_path=get_hips_extra_file(&amp;#39;datasets/samples/DSS2Red/Norder3/Dir0/Npix450.fits&amp;#39;),&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    )

&lt;span class="w"&gt; &lt;/span&gt;    tile2 = HipsTile.read(
&lt;span class="w"&gt; &lt;/span&gt;        meta=HipsTileMeta(order=3, ipix=451, file_format=&amp;#39;fits&amp;#39;, frame=hips_survey.astropy_frame,
&lt;span class="w"&gt; &lt;/span&gt;                          tile_width=512),
&lt;span class="gd"&gt;-        filename=get_hips_extra_file(&amp;#39;datasets/samples/DSS2Red/Norder3/Dir0/Npix451.fits&amp;#39;),&lt;/span&gt;
&lt;span class="gi"&gt;+        full_path=get_hips_extra_file(&amp;#39;datasets/samples/DSS2Red/Norder3/Dir0/Npix451.fits&amp;#39;),&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    )

&lt;span class="w"&gt; &lt;/span&gt;    return [tile1, tile2]
&lt;span class="gh"&gt;diff --git a/hips/tiles/tests/test_tile.py b/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gh"&gt;index a3e972d..62aa85d 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,62 +1,70 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="gi"&gt;+import pytest&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;import numpy as np
&lt;span class="w"&gt; &lt;/span&gt;from astropy.tests.helper import remote_data
&lt;span class="w"&gt; &lt;/span&gt;from numpy.testing import assert_allclose
&lt;span class="w"&gt; &lt;/span&gt;from numpy.testing import assert_equal
&lt;span class="w"&gt; &lt;/span&gt;from ..tile import HipsTile, HipsTileMeta
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gi"&gt;+from ...utils.testing import get_hips_extra_file, requires_hips_extra&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;class TestHipsTile:
&lt;span class="gd"&gt;-    @remote_data&lt;/span&gt;
&lt;span class="gd"&gt;-    def test_fetch_read_write_fits(self, tmpdir):&lt;/span&gt;
&lt;span class="gd"&gt;-        meta = HipsTileMeta(order=6, ipix=30889, file_format=&amp;#39;fits&amp;#39;)&lt;/span&gt;
&lt;span class="gd"&gt;-        url = &amp;#39;http://alasky.unistra.fr/2MASS/H/Norder6/Dir30000/Npix30889.fits&amp;#39;&lt;/span&gt;
&lt;span class="gd"&gt;-        tile = HipsTile.fetch(meta, url)&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        assert tile.data.shape == (512, 512)&lt;/span&gt;
&lt;span class="gd"&gt;-        assert_equal(tile.data[510][5:7], [1, 0])&lt;/span&gt;

&lt;span class="gd"&gt;-        filename = str(tmpdir / &amp;#39;Npix30889.fits&amp;#39;)&lt;/span&gt;
&lt;span class="gd"&gt;-        tile.write(filename)&lt;/span&gt;
&lt;span class="gd"&gt;-        tile2 = HipsTile.read(meta, filename=filename)&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        assert tile == tile2&lt;/span&gt;
&lt;span class="gi"&gt;+    fetch_read_pars = [&lt;/span&gt;
&lt;span class="gi"&gt;+        dict(url=&amp;#39;http://alasky.unistra.fr/DSS/DSS2Merged/Norder3/Dir0/Npix463.fits&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+             full_path=&amp;#39;datasets/samples/DSS2Red/Norder3/Dir0/Npix463.fits&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+             file_name=&amp;#39;Npix463.fits&amp;#39;, file_format=&amp;#39;fits&amp;#39;, order=3, ipix=463),&lt;/span&gt;
&lt;span class="gi"&gt;+        dict(url=&amp;#39;http://alasky.unistra.fr/DSS/DSS2Merged/Norder3/Dir0/Npix463.jpg&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+             full_path=&amp;#39;datasets/samples/DSS2Red/Norder3/Dir0/Npix463.jpg&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+             file_name=&amp;#39;Npix463.jpg&amp;#39;, file_format=&amp;#39;jpg&amp;#39;, order=3, ipix=463),&lt;/span&gt;
&lt;span class="gi"&gt;+        dict(url=&amp;#39;http://alasky.unistra.fr/2MASS6X/2MASS6X_H/Norder6/Dir0/Npix6112.png&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+             full_path=&amp;#39;datasets/samples/2MASS6XH/Norder6/Dir0/Npix6112.png&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+             file_name=&amp;#39;Npix6112.png&amp;#39;, file_format=&amp;#39;png&amp;#39;, order=6, ipix=6112)&lt;/span&gt;
&lt;span class="gi"&gt;+    ]&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    @remote_data
&lt;span class="gd"&gt;-    def test_fetch_read_write_jpg(self, tmpdir):&lt;/span&gt;
&lt;span class="gd"&gt;-        meta = HipsTileMeta(order=6, ipix=30889, file_format=&amp;#39;jpg&amp;#39;)&lt;/span&gt;
&lt;span class="gd"&gt;-        url = &amp;#39;http://alasky.unistra.fr/2MASS/H/Norder6/Dir30000/Npix30889.jpg&amp;#39;&lt;/span&gt;
&lt;span class="gd"&gt;-        tile = HipsTile.fetch(meta, url)&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        assert tile.data.shape == (512, 512, 3)&lt;/span&gt;
&lt;span class="gd"&gt;-        assert_equal(tile.data[510][5:7], [[0, 0, 0], [1, 1, 1]])&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        filename = str(tmpdir / &amp;#39;Npix30889.jpg&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+    @requires_hips_extra()&lt;/span&gt;
&lt;span class="gi"&gt;+    @pytest.mark.parametrize(&amp;#39;pars&amp;#39;, fetch_read_pars)&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_fetch_read(self, pars):&lt;/span&gt;
&lt;span class="gi"&gt;+        meta = HipsTileMeta(order=pars[&amp;#39;order&amp;#39;], ipix=pars[&amp;#39;ipix&amp;#39;], file_format=pars[&amp;#39;file_format&amp;#39;])&lt;/span&gt;
&lt;span class="gi"&gt;+        tile_fetched = HipsTile.fetch(meta, url=pars[&amp;#39;url&amp;#39;])&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        full_path = get_hips_extra_file(pars[&amp;#39;full_path&amp;#39;])&lt;/span&gt;
&lt;span class="gi"&gt;+        tile_local = HipsTile.read(meta, full_path=full_path)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        assert tile_fetched == tile_local&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    read_write_pars = [&lt;/span&gt;
&lt;span class="gi"&gt;+        dict(full_path=&amp;#39;datasets/samples/DSS2Red/Norder3/Dir0/Npix463.fits&amp;#39;, file_name=&amp;#39;Npix463.fits&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+             file_format=&amp;#39;fits&amp;#39;, order=3, ipix=463, shape=(512, 512), tile_data=[3047], index=[[510], [5]]),&lt;/span&gt;
&lt;span class="gi"&gt;+        dict(full_path=&amp;#39;datasets/samples/DSS2Red/Norder3/Dir0/Npix463.jpg&amp;#39;, file_name=&amp;#39;Npix463.jpg&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+             file_format=&amp;#39;jpg&amp;#39;, order=3, ipix=463, shape=(512, 512, 3), tile_data=[[10, 10, 10]], index=[[510], [5]]),&lt;/span&gt;
&lt;span class="gi"&gt;+        dict(full_path=&amp;#39;datasets/samples/2MASS6XH/Norder6/Dir0/Npix6112.png&amp;#39;, file_name=&amp;#39;Npix6112.png&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+             file_format=&amp;#39;png&amp;#39;, order=6, ipix=6112, shape=(512, 512, 4), tile_data=[[19, 19, 19, 255]], index=[[253], [5]])&lt;/span&gt;
&lt;span class="gi"&gt;+    ]&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @requires_hips_extra()&lt;/span&gt;
&lt;span class="gi"&gt;+    @pytest.mark.parametrize(&amp;#39;pars&amp;#39;, read_write_pars)&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_read_write(self, tmpdir, pars):&lt;/span&gt;
&lt;span class="gi"&gt;+        meta = HipsTileMeta(order=pars[&amp;#39;order&amp;#39;], ipix=pars[&amp;#39;ipix&amp;#39;], file_format=pars[&amp;#39;file_format&amp;#39;])&lt;/span&gt;
&lt;span class="gi"&gt;+        full_path = get_hips_extra_file(pars[&amp;#39;full_path&amp;#39;])&lt;/span&gt;
&lt;span class="gi"&gt;+        tile = HipsTile.read(meta, full_path)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        assert tile.data.shape == pars[&amp;#39;shape&amp;#39;]&lt;/span&gt;
&lt;span class="gi"&gt;+        assert_equal(tile.data[pars[&amp;#39;index&amp;#39;]], pars[&amp;#39;tile_data&amp;#39;])&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        filename = str(tmpdir / pars[&amp;#39;file_name&amp;#39;])&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        tile.write(filename)
&lt;span class="gd"&gt;-        tile2 = HipsTile.read(meta, filename=filename)&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        # The following assert fails, because on JPEG write / read&lt;/span&gt;
&lt;span class="gd"&gt;-        # the data is different (for unknown reasons).&lt;/span&gt;
&lt;span class="gd"&gt;-        # TODO: Figure out what&amp;#39;s wrong here and fix!&lt;/span&gt;
&lt;span class="gd"&gt;-        # print(tile.data.sum())&lt;/span&gt;
&lt;span class="gd"&gt;-        # print(tile2.data.sum())&lt;/span&gt;
&lt;span class="gd"&gt;-        # print((tile == tile2).all())&lt;/span&gt;
&lt;span class="gd"&gt;-        # assert tile == tile2&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    @remote_data&lt;/span&gt;
&lt;span class="gd"&gt;-    def test_fetch_read_write_png(self, tmpdir):&lt;/span&gt;
&lt;span class="gd"&gt;-        meta = HipsTileMeta(order=6, ipix=463, file_format=&amp;#39;png&amp;#39;)&lt;/span&gt;
&lt;span class="gd"&gt;-        url = &amp;#39;http://alasky.unistra.fr/2MASS6X/2MASS6X_H/Norder6/Dir0/Npix463.png&amp;#39;&lt;/span&gt;
&lt;span class="gd"&gt;-        tile = HipsTile.fetch(meta, url)&lt;/span&gt;
&lt;span class="gi"&gt;+        tile2 = HipsTile.read(meta, full_path=filename)&lt;/span&gt;

&lt;span class="gd"&gt;-        assert tile.data.shape == (512, 512, 4)&lt;/span&gt;
&lt;span class="gd"&gt;-        assert_equal(tile.data[510][5:7], [[17, 17, 17, 255], [18, 18, 18, 255]])&lt;/span&gt;
&lt;span class="gi"&gt;+        # TODO: Fix JPG write issue&lt;/span&gt;
&lt;span class="gi"&gt;+        # assert tile == tile2&lt;/span&gt;

&lt;span class="gd"&gt;-        filename = str(tmpdir / &amp;#39;Npix463.png&amp;#39;)&lt;/span&gt;
&lt;span class="gd"&gt;-        tile.write(filename)&lt;/span&gt;
&lt;span class="gd"&gt;-        tile2 = HipsTile.read(meta, filename=filename)&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_value_error(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        with pytest.raises(ValueError):&lt;/span&gt;
&lt;span class="gi"&gt;+            meta = HipsTileMeta(order=3, ipix=463, file_format=&amp;#39;jpeg&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+            full_path = get_hips_extra_file(&amp;#39;datasets/samples/DSS2Red/Norder3/Dir0/Npix463.jpg&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+            HipsTile.read(meta, full_path)&lt;/span&gt;

&lt;span class="gd"&gt;-        assert tile == tile2&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;class TestHipsTileMeta:
&lt;span class="w"&gt; &lt;/span&gt;    @classmethod
&lt;span class="gh"&gt;diff --git a/hips/tiles/tile.py b/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gh"&gt;index 4a3373c..550969c 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -159,17 +159,17 @@ class HipsTile:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        return cls._from_raw_data(meta, raw_data)

&lt;span class="w"&gt; &lt;/span&gt;    @classmethod
&lt;span class="gd"&gt;-    def read(cls, meta: HipsTileMeta, filename: str = None) -&amp;gt; &amp;#39;HipsTile&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+    def read(cls, meta: HipsTileMeta, full_path: str = None) -&amp;gt; &amp;#39;HipsTile&amp;#39;:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Read HiPS tile data from a directory and load into memory (`HipsTile`).

&lt;span class="w"&gt; &lt;/span&gt;        Parameters
&lt;span class="w"&gt; &lt;/span&gt;        ----------
&lt;span class="w"&gt; &lt;/span&gt;        meta : `HipsTileMeta`
&lt;span class="w"&gt; &lt;/span&gt;            Metadata of HiPS tile
&lt;span class="gd"&gt;-        filename : `str`&lt;/span&gt;
&lt;span class="gi"&gt;+        full_path : `str`&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            File path to store a HiPS tile
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-        path = Path(filename) if filename else meta.full_path&lt;/span&gt;
&lt;span class="gi"&gt;+        path = Path(full_path) or meta.full_path&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        with path.open(mode=&amp;#39;rb&amp;#39;) as fh:
&lt;span class="w"&gt; &lt;/span&gt;            raw_data = BytesIO(fh.read())

&lt;span class="gu"&gt;@@ -197,15 +197,15 @@ class HipsTile:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            raise ValueError(f&amp;#39;Tile file format not supported: {meta.file_format}. &amp;#39;
&lt;span class="w"&gt; &lt;/span&gt;                              &amp;#39;Supported formats: fits, jpg, png&amp;#39;)

&lt;span class="gd"&gt;-    def write(self, filename: str = None) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+    def write(self, full_path: str = None) -&amp;gt; None:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Write HiPS tile by a given filename.

&lt;span class="w"&gt; &lt;/span&gt;        Parameters
&lt;span class="w"&gt; &lt;/span&gt;        ----------
&lt;span class="gd"&gt;-        filename : `str`&lt;/span&gt;
&lt;span class="gi"&gt;+        full_path : `str`&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            Name of the file
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-        path = Path(filename) if filename else self.meta.full_path&lt;/span&gt;
&lt;span class="gi"&gt;+        path = Path(full_path) or meta.full_path&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        file_format = self.meta.file_format

&lt;span class="w"&gt; &lt;/span&gt;        if file_format == &amp;#39;fits&amp;#39;:
&lt;span class="gu"&gt;@@ -218,5 +218,5 @@ class HipsTile:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            image = Image.fromarray(self.data)
&lt;span class="w"&gt; &lt;/span&gt;            image.save(str(path))
&lt;span class="w"&gt; &lt;/span&gt;        else:
&lt;span class="gd"&gt;-            raise ValueError(f&amp;#39;Tile file format not supported: {meta.file_format}. &amp;#39;&lt;/span&gt;
&lt;span class="gd"&gt;-                              &amp;#39;Supported formats: fits, jpg, png&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+            raise ValueError(f&amp;#39;Tile file format not supported: {file_format}. &amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+                              &amp;#39;Supported formats: fits, jpg, png&amp;#39;)  # pragma: no cover&lt;/span&gt;

commit b0f8d4daa85e32a07677522f948b7a9925b07ef7
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Wed Jul 5 12:20:03 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Add test for PNG tiles

&lt;span class="gh"&gt;diff --git a/hips/tiles/tests/test_tile.py b/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gh"&gt;index 2f3b909..a3e972d 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -43,6 +43,20 @@ class TestHipsTile:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        # print((tile == tile2).all())
&lt;span class="w"&gt; &lt;/span&gt;        # assert tile == tile2

&lt;span class="gi"&gt;+    @remote_data&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_fetch_read_write_png(self, tmpdir):&lt;/span&gt;
&lt;span class="gi"&gt;+        meta = HipsTileMeta(order=6, ipix=463, file_format=&amp;#39;png&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        url = &amp;#39;http://alasky.unistra.fr/2MASS6X/2MASS6X_H/Norder6/Dir0/Npix463.png&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        tile = HipsTile.fetch(meta, url)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        assert tile.data.shape == (512, 512, 4)&lt;/span&gt;
&lt;span class="gi"&gt;+        assert_equal(tile.data[510][5:7], [[17, 17, 17, 255], [18, 18, 18, 255]])&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        filename = str(tmpdir / &amp;#39;Npix463.png&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        tile.write(filename)&lt;/span&gt;
&lt;span class="gi"&gt;+        tile2 = HipsTile.read(meta, filename=filename)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        assert tile == tile2&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;class TestHipsTileMeta:
&lt;span class="w"&gt; &lt;/span&gt;    @classmethod
&lt;span class="gh"&gt;diff --git a/hips/tiles/tile.py b/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gh"&gt;index f337e67..4a3373c 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -189,15 +189,13 @@ class HipsTile:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                    data = hdu_list[0].data
&lt;span class="w"&gt; &lt;/span&gt;                    header = hdu_list[0].header
&lt;span class="w"&gt; &lt;/span&gt;            return cls(meta, data, header)
&lt;span class="gd"&gt;-        elif meta.file_format == &amp;#39;jpg&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+        elif meta.file_format in {&amp;#39;jpg&amp;#39;, &amp;#39;png&amp;#39;}:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            with Image.open(raw_data) as image:
&lt;span class="w"&gt; &lt;/span&gt;                data = np.array(image)
&lt;span class="w"&gt; &lt;/span&gt;            return cls(meta, data)
&lt;span class="gd"&gt;-        elif meta.file_format == &amp;#39;png&amp;#39;:&lt;/span&gt;
&lt;span class="gd"&gt;-            raise NotImplementedError()&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        else:
&lt;span class="w"&gt; &lt;/span&gt;            raise ValueError(f&amp;#39;Tile file format not supported: {meta.file_format}. &amp;#39;
&lt;span class="gd"&gt;-                             &amp;#39;Supported formats: fits, jpg, png&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+                              &amp;#39;Supported formats: fits, jpg, png&amp;#39;)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def write(self, filename: str = None) -&amp;gt; None:
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Write HiPS tile by a given filename.
&lt;span class="gu"&gt;@@ -216,11 +214,9 @@ class HipsTile:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                warnings.simplefilter(&amp;#39;ignore&amp;#39;, VerifyWarning)
&lt;span class="w"&gt; &lt;/span&gt;                hdu = fits.PrimaryHDU(self.data, header=self.header)
&lt;span class="w"&gt; &lt;/span&gt;                hdu.writeto(str(path))
&lt;span class="gd"&gt;-        elif file_format == &amp;#39;jpg&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+        elif file_format in {&amp;#39;jpg&amp;#39;, &amp;#39;png&amp;#39;}:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            image = Image.fromarray(self.data)
&lt;span class="w"&gt; &lt;/span&gt;            image.save(str(path))
&lt;span class="gd"&gt;-        elif file_format == &amp;#39;png&amp;#39;:&lt;/span&gt;
&lt;span class="gd"&gt;-            raise NotImplementedError()&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        else:
&lt;span class="w"&gt; &lt;/span&gt;            raise ValueError(f&amp;#39;Tile file format not supported: {meta.file_format}. &amp;#39;
&lt;span class="gd"&gt;-                             &amp;#39;Supported formats: fits, jpg, png&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+                              &amp;#39;Supported formats: fits, jpg, png&amp;#39;)&lt;/span&gt;

commit f7aa6c9aa4e6bee51dab7801d7910f02b69dc0eb
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Mon Jul 3 22:29:06 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Update tile_access_url method

&lt;span class="gh"&gt;diff --git a/hips/draw/simple.py b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index 6f23f60..eb28f51 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -97,7 +97,7 @@ def fetch_tiles(healpix_pixel_indices: np.ndarray, order: int, hips_survey: Hips&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            frame=hips_survey.astropy_frame,
&lt;span class="w"&gt; &lt;/span&gt;            file_format=&amp;#39;fits&amp;#39;,
&lt;span class="w"&gt; &lt;/span&gt;        )
&lt;span class="gd"&gt;-        tile = HipsTile.fetch(tile_meta, hips_survey.tile_access_url + tile_meta.filename)&lt;/span&gt;
&lt;span class="gi"&gt;+        tile = HipsTile.fetch(tile_meta, hips_survey.tile_access_url(order=order, ipix=healpix_pixel_index) + tile_meta.filename)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        yield tile


&lt;span class="gh"&gt;diff --git a/hips/tiles/surveys.py b/hips/tiles/surveys.py&lt;/span&gt;
&lt;span class="gh"&gt;index 1bfd784..8013ef5 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/surveys.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/surveys.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,5 +1,6 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="w"&gt; &lt;/span&gt;from collections import OrderedDict
&lt;span class="gi"&gt;+import numpy as np&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;import urllib.request
&lt;span class="w"&gt; &lt;/span&gt;from typing import List
&lt;span class="w"&gt; &lt;/span&gt;from astropy.table import Table
&lt;span class="gu"&gt;@@ -123,14 +124,24 @@ class HipsSurveyProperties:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        return self.data[&amp;#39;hips_tile_format&amp;#39;]

&lt;span class="w"&gt; &lt;/span&gt;    @property
&lt;span class="gd"&gt;-    def base_url(self):&lt;/span&gt;
&lt;span class="gi"&gt;+    def base_url(self) -&amp;gt; str:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;HiPS access url&amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;        return self.data[&amp;#39;moc_access_url&amp;#39;].rsplit(&amp;#39;/&amp;#39;, 1)[0]

&lt;span class="gd"&gt;-    @property&lt;/span&gt;
&lt;span class="gd"&gt;-    def tile_access_url(self):&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;Tile access URL for a HiPS surveys&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-        return self.base_url + &amp;#39;/Norder&amp;#39; + str(self.hips_order) + &amp;#39;/Dir0/&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+    def directory(self, ipix: int) -&amp;gt; int:&lt;/span&gt;
&lt;span class="gi"&gt;+        return (ipix // 10000) * 10000&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def tile_access_url(self, order: int, ipix: int) -&amp;gt; str:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Tile access URL&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        Parameters&lt;/span&gt;
&lt;span class="gi"&gt;+        ----------&lt;/span&gt;
&lt;span class="gi"&gt;+        order : int&lt;/span&gt;
&lt;span class="gi"&gt;+            HiPS order&lt;/span&gt;
&lt;span class="gi"&gt;+        ipix : int&lt;/span&gt;
&lt;span class="gi"&gt;+            Index of the HiPS tile&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        return self.base_url + &amp;#39;/Norder&amp;#39; + str(order) + &amp;#39;/Dir&amp;#39; + str(self.directory(ipix)) + &amp;#39;/&amp;#39;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    @property
&lt;span class="w"&gt; &lt;/span&gt;    def hips_service_url(self) -&amp;gt; str:
&lt;span class="gh"&gt;diff --git a/hips/tiles/tests/test_surveys.py b/hips/tiles/tests/test_surveys.py&lt;/span&gt;
&lt;span class="gh"&gt;index 1130fd9..df4e812 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tests/test_surveys.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tests/test_surveys.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -32,7 +32,7 @@ class TestHipsSurveyProperties:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        assert self.hips_survey_property.base_url == &amp;#39;http://alasky.u-strasbg.fr/DSS/DSSColor&amp;#39;

&lt;span class="w"&gt; &lt;/span&gt;    def test_tile_access_url(self):
&lt;span class="gd"&gt;-        assert self.hips_survey_property.tile_access_url == &amp;#39;http://alasky.u-strasbg.fr/DSS/DSSColor/Norder9/Dir0/&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        assert self.hips_survey_property.tile_access_url(order=9, ipix=54321) == &amp;#39;http://alasky.u-strasbg.fr/DSS/DSSColor/Norder9/Dir50000/&amp;#39;&lt;/span&gt;


&lt;span class="w"&gt; &lt;/span&gt;class TestHipsSurveyPropertiesList:

commit f746d04e6a3cbe73c4c8ce6b8a32d3020c83bce5
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Mon Jul 3 18:33:27 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Make improvements to compute_healpix_pixel_indices utility function

&lt;span class="gh"&gt;diff --git a/hips/utils/healpix.py b/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gh"&gt;index 0849789..6e8213f 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -69,11 +69,12 @@ def boundaries(nside: int, pix: int, nest: bool = True) -&amp;gt; tuple:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    return theta, phi


&lt;span class="gd"&gt;-def compute_healpix_pixel_indices(wcs_geometry: WCSGeometry, order: int, healpix_frame: str = None) -&amp;gt; np.ndarray:&lt;/span&gt;
&lt;span class="gi"&gt;+def compute_healpix_pixel_indices(wcs_geometry: WCSGeometry, order: int, healpix_frame: str) -&amp;gt; np.ndarray:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;Compute HEALPix pixels within a minimal disk covering a given WCSGeometry.

&lt;span class="gd"&gt;-    This function calls `healpy.pixelfunc.ang2vec` and `healpy.query_disc`&lt;/span&gt;
&lt;span class="gd"&gt;-    to compute the HEALPix pixel indices, which will be used in tile drawing.&lt;/span&gt;
&lt;span class="gi"&gt;+    This function computes pixel coordinates for the given WCS object and&lt;/span&gt;
&lt;span class="gi"&gt;+    then calls `healpy.pixelfunc.ang2pix` and `numpy.unique` to compute&lt;/span&gt;
&lt;span class="gi"&gt;+    HEALPix pixel indices, which will be used in tile drawing.&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    Parameters
&lt;span class="w"&gt; &lt;/span&gt;    ----------
&lt;span class="gu"&gt;@@ -82,8 +83,7 @@ def compute_healpix_pixel_indices(wcs_geometry: WCSGeometry, order: int, healpix&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    order : int
&lt;span class="w"&gt; &lt;/span&gt;        The order of the HEALPix
&lt;span class="w"&gt; &lt;/span&gt;    healpix_frame : {&amp;#39;icrs&amp;#39;, &amp;#39;galactic&amp;#39;, &amp;#39;ecliptic&amp;#39;}
&lt;span class="gd"&gt;-        Coordinate system frame in which to compute the HEALPix pixel indices.&lt;/span&gt;
&lt;span class="gd"&gt;-        The default ``None`` means: take the frame from ``geometry``.&lt;/span&gt;
&lt;span class="gi"&gt;+        Coordinate system frame in which to compute the HEALPix pixel indices&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    Returns
&lt;span class="w"&gt; &lt;/span&gt;    -------
&lt;span class="gu"&gt;@@ -101,19 +101,11 @@ def compute_healpix_pixel_indices(wcs_geometry: WCSGeometry, order: int, healpix&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    ...     coordsys=&amp;#39;CEL&amp;#39;, projection=&amp;#39;AIT&amp;#39;,
&lt;span class="w"&gt; &lt;/span&gt;    ...     cdelt=1.0, crpix=(1., 1.),
&lt;span class="w"&gt; &lt;/span&gt;    ... )
&lt;span class="gd"&gt;-    &amp;gt;&amp;gt;&amp;gt; compute_healpix_pixel_indices(wcs_geometry, order=3)&lt;/span&gt;
&lt;span class="gd"&gt;-    array([176, 207, 208, 239, 240, 271, 272])&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;gt;&amp;gt;&amp;gt; compute_healpix_pixel_indices(wcs_geometry, order=3, healpix_frame=&amp;#39;galactic&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+    array([321, 611, 614, 615, 617, 618, 619, 620, 621, 622])&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-    healpix_frame = healpix_frame or wcs_geometry.celestial_frame&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    center_coord = wcs_geometry.center_skycoord.transform_to(healpix_frame)&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    pixel_coords = wcs_geometry.pixel_skycoords&lt;/span&gt;
&lt;span class="gd"&gt;-    separation = center_coord.separation(pixel_coords)&lt;/span&gt;
&lt;span class="gd"&gt;-    radius = np.nanmax(separation.rad)&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    vec = _skycoord_to_vec(center_coord)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    nside = hp.order2nside(order)
&lt;span class="gd"&gt;-    ipix = hp.query_disc(nside, vec, radius, nest=True)&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    return ipix&lt;/span&gt;
&lt;span class="gi"&gt;+    pixel_coords = wcs_geometry.pixel_skycoords.transform_to(healpix_frame)&lt;/span&gt;
&lt;span class="gi"&gt;+    theta, phi = _skycoord_to_theta_phi(pixel_coords)&lt;/span&gt;
&lt;span class="gi"&gt;+    ipix = hp.ang2pix(nside, theta, phi, nest=True)&lt;/span&gt;
&lt;span class="gi"&gt;+    return np.unique(ipix)&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/utils/tests/test_healpix.py b/hips/utils/tests/test_healpix.py&lt;/span&gt;
&lt;span class="gh"&gt;index 65b8632..9094894 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/tests/test_healpix.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/tests/test_healpix.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -23,8 +23,8 @@ def test_boundaries():&lt;/span&gt;


&lt;span class="w"&gt; &lt;/span&gt;compute_healpix_pixel_indices_pars = [
&lt;span class="gd"&gt;-    dict(frame=None, ipix=[269, 270, 271, 282, 293, 304, 305, 306]),&lt;/span&gt;
&lt;span class="gd"&gt;-    dict(frame=&amp;#39;icrs&amp;#39;, ipix=[448, 449, 450, 451, 456, 457, 663]),&lt;/span&gt;
&lt;span class="gi"&gt;+    dict(frame=&amp;#39;galactic&amp;#39;, ipix=[269, 270, 271, 280, 282, 283, 292, 293, 295, 304, 305, 306]),&lt;/span&gt;
&lt;span class="gi"&gt;+    dict(frame=&amp;#39;icrs&amp;#39;, ipix=[448, 449, 450, 451, 454, 456, 457, 460, 661, 663, 669]),&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;]



commit 3121de53a68eabea67fdce9eae1a26412484edaf
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Mon Jul 3 14:00:17 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Fix typo

&lt;span class="gh"&gt;diff --git a/docs/getting_started.rst b/docs/getting_started.rst&lt;/span&gt;
&lt;span class="gh"&gt;index 9e2e621..3133c61 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/docs/getting_started.rst&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/docs/getting_started.rst&lt;/span&gt;
&lt;span class="gu"&gt;@@ -79,5 +79,5 @@ What next?&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;That&amp;#39;s it, now you&amp;#39;ve seen the main features of the `hips` package.
&lt;span class="w"&gt; &lt;/span&gt;Note that there is API documentation explaining all available functions, classes and parameters.

&lt;span class="gd"&gt;-If you have any questions, or find something now working or a missing feature,&lt;/span&gt;
&lt;span class="gi"&gt;+If you have any questions, or find something not working or a missing feature,&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;please get in touch by posting on our Github issue tracker.

commit 880d4a2e9e9ce31e86b508750bc65ea33f4a3b4b
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Jun 30 18:32:43 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Add test case test_wcs_healpix_pixel_indices

&lt;span class="gh"&gt;diff --git a/hips/draw/tests/test_simple.py b/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index 2d47d4c..a0931bc 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -7,7 +7,7 @@ from numpy.testing import assert_allclose&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from ..simple import make_sky_image, draw_sky_image
&lt;span class="w"&gt; &lt;/span&gt;from ...tiles import HipsSurveyProperties, HipsTileMeta, HipsTile
&lt;span class="w"&gt; &lt;/span&gt;from ...utils.testing import get_hips_extra_file, make_test_wcs_geometry, requires_hips_extra
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gi"&gt;+from ...utils import compute_healpix_pixel_indices&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;def get_test_tiles():
&lt;span class="w"&gt; &lt;/span&gt;    filename = get_pkg_data_filename(&amp;#39;../../tiles/tests/data/properties.txt&amp;#39;)
&lt;span class="gu"&gt;@@ -51,3 +51,11 @@ def test_make_sky_image():&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    assert data.dtype == np.float64
&lt;span class="w"&gt; &lt;/span&gt;    assert_allclose(data[200, 994], 3717.10091363)
&lt;span class="w"&gt; &lt;/span&gt;    assert_allclose(data[200, 995], 3402.55292158)
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+def test_wcs_healpix_pixel_indices():&lt;/span&gt;
&lt;span class="gi"&gt;+    url = &amp;#39;https://raw.githubusercontent.com/hipspy/hips-extra/master/datasets/samples/DSS2Red/properties&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+    hips_survey = HipsSurveyProperties.fetch(url)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    geometry = make_test_wcs_geometry(case=2)&lt;/span&gt;
&lt;span class="gi"&gt;+    healpix_pixel_indices = compute_healpix_pixel_indices(geometry, hips_survey.hips_order)&lt;/span&gt;
&lt;span class="gi"&gt;+    assert list(healpix_pixel_indices) == [450, 451]&lt;/span&gt;

commit 5fe3ed0aa5be6544a1b71b786f0014df626d1b21
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Jun 30 15:58:32 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Some code reformatting

&lt;span class="gh"&gt;diff --git a/docs/getting_started.rst b/docs/getting_started.rst&lt;/span&gt;
&lt;span class="gh"&gt;index e730eac..57274f7 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/docs/getting_started.rst&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/docs/getting_started.rst&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,7 +1,7 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;.. include:: references.txt

&lt;span class="gd"&gt;-.. _gs:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;.. doctest-skip-all
&lt;span class="gi"&gt;+.. _gs:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;***************
&lt;span class="w"&gt; &lt;/span&gt;Getting started
&lt;span class="gu"&gt;@@ -25,7 +25,7 @@ Then it saves it on local disk in FITS file format.&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;         projection=&amp;#39;AIT&amp;#39;, cdelt=0.01, crpix=(1000, 500),
&lt;span class="w"&gt; &lt;/span&gt;    )
&lt;span class="w"&gt; &lt;/span&gt;    url = &amp;#39;https://raw.githubusercontent.com/hipspy/hips-extra/master/datasets/samples/DSS2Red/properties&amp;#39;
&lt;span class="gd"&gt;-    hips_survey = HipsSurveyProperties.fetch(url)  # doctest: +REMOTE_DATA&lt;/span&gt;
&lt;span class="gi"&gt;+    hips_survey = HipsSurveyProperties.fetch(url)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    data = make_sky_image(geometry, hips_survey)
&lt;span class="w"&gt; &lt;/span&gt;    hdu = fits.PrimaryHDU(data=data, header=geometry.fits_header)
&lt;span class="w"&gt; &lt;/span&gt;    hdu.writeto(&amp;#39;my_image.fits&amp;#39;)
&lt;span class="gh"&gt;diff --git a/hips/draw/simple.py b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index f93d9f4..55798b4 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -67,7 +67,8 @@ class SimpleTilePainter:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def warp_image(self) -&amp;gt; np.ndarray:
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Warp a HiPS tile and a sky image&amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-        return tf.warp(self.tile.data, self.projection, output_shape=self.geometry.shape, preserve_range=True)&lt;/span&gt;
&lt;span class="gi"&gt;+        return tf.warp(self.tile.data, self.projection,&lt;/span&gt;
&lt;span class="gi"&gt;+                       output_shape=self.geometry.shape, preserve_range=True)&lt;/span&gt;


&lt;span class="w"&gt; &lt;/span&gt;def _fetch_tiles(healpix_pixel_indices: np.ndarray, order: int, hips_survey: HipsSurveyProperties) -&amp;gt; &amp;#39;HipsTile&amp;#39;:

commit 145bcafab6c47ced08191a45f1feaa69907da4a8
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Jun 30 15:54:01 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Add attribute frames in HipsSurveyProperties

&lt;span class="gh"&gt;diff --git a/hips/draw/simple.py b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index 6918052..f93d9f4 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -6,7 +6,7 @@ from skimage import transform as tf&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from typing import List, Generator, Any

&lt;span class="w"&gt; &lt;/span&gt;from ..tiles import HipsSurveyProperties, HipsTile, HipsTileMeta
&lt;span class="gd"&gt;-from ..utils import WCSGeometry, compute_healpix_pixel_indices, frames&lt;/span&gt;
&lt;span class="gi"&gt;+from ..utils import WCSGeometry, compute_healpix_pixel_indices&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;__all__ = [
&lt;span class="w"&gt; &lt;/span&gt;    &amp;#39;draw_sky_image&amp;#39;,
&lt;span class="gu"&gt;@@ -89,7 +89,7 @@ def _fetch_tiles(healpix_pixel_indices: np.ndarray, order: int, hips_survey: Hip&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;    for healpix_pixel_index in healpix_pixel_indices:
&lt;span class="w"&gt; &lt;/span&gt;        tile_meta = HipsTileMeta(order=order, ipix=healpix_pixel_index,
&lt;span class="gd"&gt;-                                 frame=frames()[hips_survey.hips_frame], file_format=&amp;#39;fits&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+                                 frame=hips_survey.frames[hips_survey.hips_frame], file_format=&amp;#39;fits&amp;#39;)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        tile = HipsTile.fetch(tile_meta, hips_survey.tile_access_url + tile_meta.filename)
&lt;span class="w"&gt; &lt;/span&gt;        yield tile

&lt;span class="gh"&gt;diff --git a/hips/draw/tests/test_simple.py b/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index e4608ca..2d47d4c 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -6,7 +6,6 @@ from numpy.testing import assert_allclose&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;from ..simple import make_sky_image, draw_sky_image
&lt;span class="w"&gt; &lt;/span&gt;from ...tiles import HipsSurveyProperties, HipsTileMeta, HipsTile
&lt;span class="gd"&gt;-from ...utils import frames&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from ...utils.testing import get_hips_extra_file, make_test_wcs_geometry, requires_hips_extra


&lt;span class="gu"&gt;@@ -15,13 +14,13 @@ def get_test_tiles():&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    hips_survey = HipsSurveyProperties.read(filename)

&lt;span class="w"&gt; &lt;/span&gt;    tile1 = HipsTile.read(
&lt;span class="gd"&gt;-        meta=HipsTileMeta(order=3, ipix=450, file_format=&amp;#39;fits&amp;#39;, frame=frames()[hips_survey.hips_frame],&lt;/span&gt;
&lt;span class="gi"&gt;+        meta=HipsTileMeta(order=3, ipix=450, file_format=&amp;#39;fits&amp;#39;, frame=hips_survey.frames[hips_survey.hips_frame],&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                          tile_width=512),
&lt;span class="w"&gt; &lt;/span&gt;        filename=get_hips_extra_file(&amp;#39;datasets/samples/DSS2Red/Norder3/Dir0/Npix450.fits&amp;#39;),
&lt;span class="w"&gt; &lt;/span&gt;    )

&lt;span class="w"&gt; &lt;/span&gt;    tile2 = HipsTile.read(
&lt;span class="gd"&gt;-        meta=HipsTileMeta(order=3, ipix=451, file_format=&amp;#39;fits&amp;#39;, frame=frames()[hips_survey.hips_frame],&lt;/span&gt;
&lt;span class="gi"&gt;+        meta=HipsTileMeta(order=3, ipix=451, file_format=&amp;#39;fits&amp;#39;, frame=hips_survey.frames[hips_survey.hips_frame],&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                          tile_width=512),
&lt;span class="w"&gt; &lt;/span&gt;        filename=get_hips_extra_file(&amp;#39;datasets/samples/DSS2Red/Norder3/Dir0/Npix451.fits&amp;#39;),
&lt;span class="w"&gt; &lt;/span&gt;    )
&lt;span class="gh"&gt;diff --git a/hips/tiles/description.py b/hips/tiles/description.py&lt;/span&gt;
&lt;span class="gh"&gt;index 90c4bca..c8792e0 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/description.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/description.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -27,6 +27,7 @@ class HipsSurveyProperties:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def __init__(self, data: OrderedDict) -&amp;gt; None:
&lt;span class="w"&gt; &lt;/span&gt;        self.data = data
&lt;span class="gi"&gt;+        self.frames = dict({&amp;#39;equatorial&amp;#39;: &amp;#39;icrs&amp;#39;, &amp;#39;galactic&amp;#39;: &amp;#39;galactic&amp;#39;, &amp;#39;ecliptic&amp;#39;: &amp;#39;ecliptic&amp;#39;})&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    @classmethod
&lt;span class="w"&gt; &lt;/span&gt;    def read(cls, filename: str) -&amp;gt; &amp;#39;HipsSurveyProperties&amp;#39;:
&lt;span class="gu"&gt;@@ -116,4 +117,5 @@ class HipsSurveyProperties:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    @property
&lt;span class="w"&gt; &lt;/span&gt;    def tile_access_url(self):
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Tile access URL for a HiPS surveys&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        return self.access_url + &amp;#39;/Norder&amp;#39; + str(self.hips_order) + &amp;#39;/Dir0/&amp;#39;
&lt;span class="gh"&gt;diff --git a/hips/utils/healpix.py b/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gh"&gt;index 9eaffad..8e84629 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -13,8 +13,7 @@ from .wcs import WCSGeometry&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;__all__ = [
&lt;span class="w"&gt; &lt;/span&gt;    &amp;#39;boundaries&amp;#39;,
&lt;span class="gd"&gt;-    &amp;#39;compute_healpix_pixel_indices&amp;#39;,&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;#39;frames&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;#39;compute_healpix_pixel_indices&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;]

&lt;span class="w"&gt; &lt;/span&gt;__doctest_skip__ = [&amp;#39;boundaries&amp;#39;, &amp;#39;compute_healpix_pixel_indices&amp;#39;]
&lt;span class="gu"&gt;@@ -110,7 +109,3 @@ def compute_healpix_pixel_indices(wcs_geometry: WCSGeometry, order: int) -&amp;gt; np.n&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    vec = _skycoord_to_vec(center_coord)
&lt;span class="w"&gt; &lt;/span&gt;    nside = hp.order2nside(order)
&lt;span class="w"&gt; &lt;/span&gt;    return hp.query_disc(nside, vec, radius)
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-def frames() -&amp;gt; dict:&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;quot;&amp;quot;&amp;quot;A dictionary mapping commonly used frames&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-    return dict({&amp;#39;equatorial&amp;#39;: &amp;#39;icrs&amp;#39;, &amp;#39;galactic&amp;#39;: &amp;#39;galactic&amp;#39;, &amp;#39;ecliptic&amp;#39;: &amp;#39;ecliptic&amp;#39;})&lt;/span&gt;

commit 5c85e128e37e4c0cc86166e08fc13b1d51e3a5c0
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Jun 30 15:42:16 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Add property tile_access_url in HipsSurveyProperties

&lt;span class="gh"&gt;diff --git a/hips/draw/simple.py b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index feae223..6918052 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -87,11 +87,10 @@ def _fetch_tiles(healpix_pixel_indices: np.ndarray, order: int, hips_survey: Hip&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;#39;HipsTile&amp;#39;
&lt;span class="w"&gt; &lt;/span&gt;        Returns an object of  HipsTile
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-    base_url = hips_survey.access_url + &amp;#39;/Norder&amp;#39; + str(hips_survey.hips_order) + &amp;#39;/Dir0/&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    for healpix_pixel_index in healpix_pixel_indices:
&lt;span class="w"&gt; &lt;/span&gt;        tile_meta = HipsTileMeta(order=order, ipix=healpix_pixel_index,
&lt;span class="w"&gt; &lt;/span&gt;                                 frame=frames()[hips_survey.hips_frame], file_format=&amp;#39;fits&amp;#39;)
&lt;span class="gd"&gt;-        tile = HipsTile.fetch(tile_meta, base_url + tile_meta.filename)&lt;/span&gt;
&lt;span class="gi"&gt;+        tile = HipsTile.fetch(tile_meta, hips_survey.tile_access_url + tile_meta.filename)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        yield tile


&lt;span class="gh"&gt;diff --git a/hips/tiles/description.py b/hips/tiles/description.py&lt;/span&gt;
&lt;span class="gh"&gt;index 15d5a9a..90c4bca 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/description.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/description.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -113,3 +113,7 @@ class HipsSurveyProperties:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    def access_url(self):
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;HiPS access url&amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;        return self.data[&amp;#39;moc_access_url&amp;#39;].rsplit(&amp;#39;/&amp;#39;, 1)[0]
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @property&lt;/span&gt;
&lt;span class="gi"&gt;+    def tile_access_url(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        return self.access_url + &amp;#39;/Norder&amp;#39; + str(self.hips_order) + &amp;#39;/Dir0/&amp;#39;&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/tiles/tests/test_description.py b/hips/tiles/tests/test_description.py&lt;/span&gt;
&lt;span class="gh"&gt;index d0e87dc..9874916 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tests/test_description.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tests/test_description.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -29,3 +29,6 @@ class TestHipsSurveyProperties:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def test_access_url(self):
&lt;span class="w"&gt; &lt;/span&gt;        assert self.hips_survey_property.access_url == &amp;#39;http://alasky.u-strasbg.fr/DSS/DSSColor&amp;#39;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_tile_access_url(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        assert self.hips_survey_property.tile_access_url == &amp;#39;http://alasky.u-strasbg.fr/DSS/DSSColor/Norder9/Dir0/&amp;#39;&lt;/span&gt;

commit a4b6846ef107572e7b542114b731089346c3b1f5
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Jun 30 15:24:59 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Remove attribute self.pt from SimpleTilePainter

&lt;span class="gh"&gt;diff --git a/hips/draw/simple.py b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index d29f619..feae223 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -54,7 +54,6 @@ class SimpleTilePainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    def __init__(self, geometry: WCSGeometry, tile: HipsTile) -&amp;gt; None:
&lt;span class="w"&gt; &lt;/span&gt;        self.geometry = geometry
&lt;span class="w"&gt; &lt;/span&gt;        self.tile = tile
&lt;span class="gd"&gt;-        self.pt = None&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    @property
&lt;span class="w"&gt; &lt;/span&gt;    def projection(self) -&amp;gt; tf.ProjectiveTransform:
&lt;span class="gu"&gt;@@ -62,9 +61,9 @@ class SimpleTilePainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        corners = self.tile.meta.skycoord_corners.to_pixel(self.geometry.wcs)
&lt;span class="w"&gt; &lt;/span&gt;        src = np.array(corners).T.reshape((4, 2))
&lt;span class="w"&gt; &lt;/span&gt;        dst = self.tile.meta.dst
&lt;span class="gd"&gt;-        self.pt = tf.ProjectiveTransform()&lt;/span&gt;
&lt;span class="gd"&gt;-        self.pt.estimate(src, dst)&lt;/span&gt;
&lt;span class="gd"&gt;-        return self.pt&lt;/span&gt;
&lt;span class="gi"&gt;+        pt = tf.ProjectiveTransform()&lt;/span&gt;
&lt;span class="gi"&gt;+        pt.estimate(src, dst)&lt;/span&gt;
&lt;span class="gi"&gt;+        return pt&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def warp_image(self) -&amp;gt; np.ndarray:
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Warp a HiPS tile and a sky image&amp;quot;&amp;quot;&amp;quot;

commit 69e09af162b071c3a1f271883ebf51fd00aceee5
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Jun 30 15:23:22 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Skip example from doctest

&lt;span class="gh"&gt;diff --git a/docs/getting_started.rst b/docs/getting_started.rst&lt;/span&gt;
&lt;span class="gh"&gt;index 81c48db..e730eac 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/docs/getting_started.rst&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/docs/getting_started.rst&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,6 +1,7 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;.. include:: references.txt

&lt;span class="w"&gt; &lt;/span&gt;.. _gs:
&lt;span class="gi"&gt;+.. doctest-skip-all&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;***************
&lt;span class="w"&gt; &lt;/span&gt;Getting started
&lt;span class="gu"&gt;@@ -10,19 +11,21 @@ The example below shows a high level use case of the ``hips`` package.&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;It fetches a HiPS tile from a remote URL and draws it on a sky image.
&lt;span class="w"&gt; &lt;/span&gt;Then it saves it on local disk in FITS file format.

&lt;span class="gd"&gt;-&amp;gt;&amp;gt;&amp;gt; from astropy.io import fits&lt;/span&gt;
&lt;span class="gd"&gt;-&amp;gt;&amp;gt;&amp;gt; from astropy.coordinates import SkyCoord&lt;/span&gt;
&lt;span class="gd"&gt;-&amp;gt;&amp;gt;&amp;gt; from astropy.tests.helper import remote_data&lt;/span&gt;
&lt;span class="gd"&gt;-&amp;gt;&amp;gt;&amp;gt; from hips.utils import WCSGeometry&lt;/span&gt;
&lt;span class="gd"&gt;-&amp;gt;&amp;gt;&amp;gt; from hips.draw import make_sky_image&lt;/span&gt;
&lt;span class="gd"&gt;-&amp;gt;&amp;gt;&amp;gt; from hips.tiles import HipsSurveyProperties&lt;/span&gt;
&lt;span class="gd"&gt;-&amp;gt;&amp;gt;&amp;gt; geometry = WCSGeometry.create(&lt;/span&gt;
&lt;span class="gd"&gt;-...     skydir=SkyCoord(0, 0, unit=&amp;#39;deg&amp;#39;, frame=&amp;#39;galactic&amp;#39;),&lt;/span&gt;
&lt;span class="gd"&gt;-...     shape=(1000, 2000), coordsys=&amp;#39;GAL&amp;#39;,&lt;/span&gt;
&lt;span class="gd"&gt;-...     projection=&amp;#39;AIT&amp;#39;, cdelt=0.01, crpix=(1000, 500),&lt;/span&gt;
&lt;span class="gd"&gt;-... )&lt;/span&gt;
&lt;span class="gd"&gt;-&amp;gt;&amp;gt;&amp;gt; url = &amp;#39;https://raw.githubusercontent.com/hipspy/hips-extra/master/datasets/samples/DSS2Red/properties&amp;#39;&lt;/span&gt;
&lt;span class="gd"&gt;-&amp;gt;&amp;gt;&amp;gt; hips_survey = HipsSurveyProperties.fetch(url)  # doctest: +REMOTE_DATA&lt;/span&gt;
&lt;span class="gd"&gt;-&amp;gt;&amp;gt;&amp;gt; data = make_sky_image(geometry, hips_survey)&lt;/span&gt;
&lt;span class="gd"&gt;-&amp;gt;&amp;gt;&amp;gt; hdu = fits.PrimaryHDU(data=data, header=geometry.fits_header)&lt;/span&gt;
&lt;span class="gd"&gt;-&amp;gt;&amp;gt;&amp;gt; hdu.writeto(&amp;#39;my_image.fits&amp;#39;)&lt;/span&gt;
\ No newline at end of file
&lt;span class="gi"&gt;+::&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    from astropy.io import fits&lt;/span&gt;
&lt;span class="gi"&gt;+    from astropy.coordinates import SkyCoord&lt;/span&gt;
&lt;span class="gi"&gt;+    from astropy.tests.helper import remote_data&lt;/span&gt;
&lt;span class="gi"&gt;+    from hips.utils import WCSGeometry&lt;/span&gt;
&lt;span class="gi"&gt;+    from hips.draw import make_sky_image&lt;/span&gt;
&lt;span class="gi"&gt;+    from hips.tiles import HipsSurveyProperties&lt;/span&gt;
&lt;span class="gi"&gt;+    geometry = WCSGeometry.create(&lt;/span&gt;
&lt;span class="gi"&gt;+         skydir=SkyCoord(0, 0, unit=&amp;#39;deg&amp;#39;, frame=&amp;#39;galactic&amp;#39;),&lt;/span&gt;
&lt;span class="gi"&gt;+         shape=(1000, 2000), coordsys=&amp;#39;GAL&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+         projection=&amp;#39;AIT&amp;#39;, cdelt=0.01, crpix=(1000, 500),&lt;/span&gt;
&lt;span class="gi"&gt;+    )&lt;/span&gt;
&lt;span class="gi"&gt;+    url = &amp;#39;https://raw.githubusercontent.com/hipspy/hips-extra/master/datasets/samples/DSS2Red/properties&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+    hips_survey = HipsSurveyProperties.fetch(url)  # doctest: +REMOTE_DATA&lt;/span&gt;
&lt;span class="gi"&gt;+    data = make_sky_image(geometry, hips_survey)&lt;/span&gt;
&lt;span class="gi"&gt;+    hdu = fits.PrimaryHDU(data=data, header=geometry.fits_header)&lt;/span&gt;
&lt;span class="gi"&gt;+    hdu.writeto(&amp;#39;my_image.fits&amp;#39;)&lt;/span&gt;

commit 112fb37cd4514e84449be3e3543164cc4f171383
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Jun 30 15:02:32 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Update example for HipsTileMeta

&lt;span class="gh"&gt;diff --git a/hips/tiles/tile.py b/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gh"&gt;index c05c52a..f47888a 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -41,9 +41,9 @@ class HipsTileMeta:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;gt;&amp;gt;&amp;gt; from hips.tiles import HipsTileMeta
&lt;span class="w"&gt; &lt;/span&gt;    &amp;gt;&amp;gt;&amp;gt; tile_meta = HipsTileMeta(order=3, ipix=450, file_format=&amp;#39;fits&amp;#39;, frame=&amp;#39;icrs&amp;#39;, tile_width=512)
&lt;span class="w"&gt; &lt;/span&gt;    &amp;gt;&amp;gt;&amp;gt; tile_meta.skycoord_corners
&lt;span class="gd"&gt;-    &amp;lt;SkyCoord (Galactic): (l, b) in deg&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;lt;SkyCoord (ICRS): (ra, dec) in deg&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    [( 264.375, -24.62431835), ( 258.75 , -30.        ),
&lt;span class="gd"&gt;-     ( 264.375, -35.68533471), ( 270.   , -30.        )]&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    ( 264.375, -35.68533471), ( 270.   , -30.        )]&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;

&lt;span class="w"&gt; &lt;/span&gt;    def __init__(self, order: int, ipix: int, file_format: str, frame: str = &amp;#39;galactic&amp;#39;, tile_width: int = 512) -&amp;gt; None:

commit c34d4061a05eae864ae8de05b7fd639da36910dc
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Jun 30 14:31:47 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Update requires_hips_extra decorator function

&lt;span class="gh"&gt;diff --git a/hips/utils/testing.py b/hips/utils/testing.py&lt;/span&gt;
&lt;span class="gh"&gt;index d9d6d69..0563f7a 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/testing.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/testing.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -22,11 +22,19 @@ def get_hips_extra_file(filename):&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    return path / filename


&lt;span class="gi"&gt;+def has_hips_extra():&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;Is hips-extra available? (bool)&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+    if &amp;#39;HIPS_EXTRA&amp;#39; in os.environ:&lt;/span&gt;
&lt;span class="gi"&gt;+        path = Path(os.environ[&amp;#39;HIPS_EXTRA&amp;#39;]) / &amp;#39;datasets/samples/DSS2Red/properties&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        if path.is_file():&lt;/span&gt;
&lt;span class="gi"&gt;+            return True&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    return False&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;def requires_hips_extra():
&lt;span class="gd"&gt;-    &amp;quot;&amp;quot;&amp;quot;Decorator to mark tests requiring ``hips-extra`` data.&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-    has_hips_extra = get_hips_extra_file(&amp;#39;datasets/samples/DSS2Red/properties&amp;#39;).is_file()&lt;/span&gt;
&lt;span class="gd"&gt;-    skip_it = not has_hips_extra&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;Decorator to mark tests requiring ``hips-extra`` data.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+    skip_it = not has_hips_extra()&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    reason = &amp;#39;No hips-extra data available.&amp;#39;
&lt;span class="w"&gt; &lt;/span&gt;    return pytest.mark.skipif(skip_it, reason=reason)


commit 451b32a2985b55fad3bb2a0de8eb25ff3c75b218
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Jun 30 14:10:51 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Update read classmethod in HipsTile class

&lt;span class="gh"&gt;diff --git a/hips/tiles/tile.py b/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gh"&gt;index b7d95fc..c05c52a 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -96,6 +96,7 @@ class HipsTileMeta:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        theta, phi = boundaries(self.nside, self.ipix)
&lt;span class="w"&gt; &lt;/span&gt;        return SkyCoord(phi, np.pi / 2 - theta, unit=&amp;#39;radian&amp;#39;, frame=self.frame)

&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;class HipsTile:
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;HiPS tile container.

&lt;span class="gu"&gt;@@ -174,9 +175,9 @@ class HipsTile:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        path = Path(filename) if filename else meta.full_path

&lt;span class="w"&gt; &lt;/span&gt;        if meta.file_format == &amp;#39;fits&amp;#39;:
&lt;span class="gd"&gt;-            hdu_list = fits.open(str(path))&lt;/span&gt;
&lt;span class="gd"&gt;-            data = hdu_list[0].data&lt;/span&gt;
&lt;span class="gd"&gt;-            header = hdu_list[0].header&lt;/span&gt;
&lt;span class="gi"&gt;+            with fits.open(str(path)) as hdu_list:&lt;/span&gt;
&lt;span class="gi"&gt;+                data = hdu_list[0].data&lt;/span&gt;
&lt;span class="gi"&gt;+                header = hdu_list[0].header&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            return cls(meta, data, header)
&lt;span class="w"&gt; &lt;/span&gt;        else:
&lt;span class="w"&gt; &lt;/span&gt;            image = Image.open(str(path))

commit 186286c3a64f92ee1f9a50b053e5bc9724dd438f
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Jun 30 14:07:33 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Update test_skycoord_corners in TestHipsTileMeta

&lt;span class="gh"&gt;diff --git a/hips/tiles/tests/test_tile.py b/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gh"&gt;index 7d85e81..c0a8ccd 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -67,9 +67,11 @@ class TestHipsTileMeta:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        assert_allclose(self.meta.dst, dst)

&lt;span class="w"&gt; &lt;/span&gt;    def test_skycoord_corners(self):
&lt;span class="gd"&gt;-        assert_allclose(self.meta.skycoord_corners.ra.deg, [264.375, 258.75, 264.375, 270.])&lt;/span&gt;
&lt;span class="gd"&gt;-        assert_allclose(self.meta.skycoord_corners.dec.deg, [-24.624318, -30., -35.685335, -30.])&lt;/span&gt;
&lt;span class="gi"&gt;+        assert_allclose(self.meta.skycoord_corners.data.lat.deg, [-24.624318, -30., -35.685335, -30.])&lt;/span&gt;
&lt;span class="gi"&gt;+        assert_allclose(self.meta.skycoord_corners.data.lon.deg, [264.375, 258.75, 264.375, 270.])&lt;/span&gt;
&lt;span class="gi"&gt;+        assert self.meta.skycoord_corners.frame.name == &amp;#39;icrs&amp;#39;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        meta = HipsTileMeta(order=3, ipix=450, file_format=&amp;#39;fits&amp;#39;, frame=&amp;#39;galactic&amp;#39;, tile_width=512)
&lt;span class="gd"&gt;-        assert_allclose(meta.skycoord_corners.l.deg, [264.375, 258.75, 264.375, 270.])&lt;/span&gt;
&lt;span class="gd"&gt;-        assert_allclose(meta.skycoord_corners.b.deg, [-24.624318, -30., -35.685335, -30.])&lt;/span&gt;
&lt;span class="gi"&gt;+        assert_allclose(meta.skycoord_corners.data.lat.deg, [-24.624318, -30., -35.685335, -30.])&lt;/span&gt;
&lt;span class="gi"&gt;+        assert_allclose(meta.skycoord_corners.data.lon.deg, [264.375, 258.75, 264.375, 270.])&lt;/span&gt;
&lt;span class="gi"&gt;+        assert meta.skycoord_corners.frame.name == &amp;#39;galactic&amp;#39;&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/tiles/tile.py b/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gh"&gt;index 3ae0f4f..b7d95fc 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -94,11 +94,7 @@ class HipsTileMeta:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    def skycoord_corners(self) -&amp;gt; SkyCoord:
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Corner values for a HiPS tile&amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;        theta, phi = boundaries(self.nside, self.ipix)
&lt;span class="gd"&gt;-        if self.frame == &amp;#39;galactic&amp;#39;:&lt;/span&gt;
&lt;span class="gd"&gt;-            return SkyCoord(l=phi, b=np.pi / 2 - theta, unit=&amp;#39;radian&amp;#39;, frame=self.frame)&lt;/span&gt;
&lt;span class="gd"&gt;-        else:&lt;/span&gt;
&lt;span class="gd"&gt;-            return SkyCoord(ra=phi, dec=np.pi / 2 - theta, unit=&amp;#39;radian&amp;#39;, frame=self.frame)&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gi"&gt;+        return SkyCoord(phi, np.pi / 2 - theta, unit=&amp;#39;radian&amp;#39;, frame=self.frame)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;class HipsTile:
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;HiPS tile container.

commit de5566394997a6a2d7a87e548f12b3b179aa1f53
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Jun 30 12:46:30 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Add function frames in utils/healpix.py

&lt;span class="gh"&gt;diff --git a/hips/draw/simple.py b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index ea61810..d29f619 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -6,7 +6,7 @@ from skimage import transform as tf&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from typing import List, Generator, Any

&lt;span class="w"&gt; &lt;/span&gt;from ..tiles import HipsSurveyProperties, HipsTile, HipsTileMeta
&lt;span class="gd"&gt;-from ..utils import WCSGeometry, compute_healpix_pixel_indices&lt;/span&gt;
&lt;span class="gi"&gt;+from ..utils import WCSGeometry, compute_healpix_pixel_indices, frames&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;__all__ = [
&lt;span class="w"&gt; &lt;/span&gt;    &amp;#39;draw_sky_image&amp;#39;,
&lt;span class="gu"&gt;@@ -89,10 +89,9 @@ def _fetch_tiles(healpix_pixel_indices: np.ndarray, order: int, hips_survey: Hip&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        Returns an object of  HipsTile
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;    base_url = hips_survey.access_url + &amp;#39;/Norder&amp;#39; + str(hips_survey.hips_order) + &amp;#39;/Dir0/&amp;#39;
&lt;span class="gd"&gt;-    frames = dict({&amp;#39;equatorial&amp;#39;: &amp;#39;icrs&amp;#39;, &amp;#39;galactic&amp;#39;: &amp;#39;galactic&amp;#39;, &amp;#39;ecliptic&amp;#39;: &amp;#39;ecliptic&amp;#39;})&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    for healpix_pixel_index in healpix_pixel_indices:
&lt;span class="w"&gt; &lt;/span&gt;        tile_meta = HipsTileMeta(order=order, ipix=healpix_pixel_index,
&lt;span class="gd"&gt;-                                 frame=frames[hips_survey.hips_frame], file_format=&amp;#39;fits&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+                                 frame=frames()[hips_survey.hips_frame], file_format=&amp;#39;fits&amp;#39;)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        tile = HipsTile.fetch(tile_meta, base_url + tile_meta.filename)
&lt;span class="w"&gt; &lt;/span&gt;        yield tile

&lt;span class="gh"&gt;diff --git a/hips/draw/tests/test_simple.py b/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index 80818f9..e4608ca 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -6,21 +6,23 @@ from numpy.testing import assert_allclose&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;from ..simple import make_sky_image, draw_sky_image
&lt;span class="w"&gt; &lt;/span&gt;from ...tiles import HipsSurveyProperties, HipsTileMeta, HipsTile
&lt;span class="gi"&gt;+from ...utils import frames&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from ...utils.testing import get_hips_extra_file, make_test_wcs_geometry, requires_hips_extra


&lt;span class="w"&gt; &lt;/span&gt;def get_test_tiles():
&lt;span class="gd"&gt;-    frames = dict({&amp;#39;equatorial&amp;#39;: &amp;#39;icrs&amp;#39;, &amp;#39;galactic&amp;#39;: &amp;#39;galactic&amp;#39;, &amp;#39;ecliptic&amp;#39;: &amp;#39;ecliptic&amp;#39;})&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    filename = get_pkg_data_filename(&amp;#39;../../tiles/tests/data/properties.txt&amp;#39;)
&lt;span class="w"&gt; &lt;/span&gt;    hips_survey = HipsSurveyProperties.read(filename)

&lt;span class="w"&gt; &lt;/span&gt;    tile1 = HipsTile.read(
&lt;span class="gd"&gt;-        meta=HipsTileMeta(order=3, ipix=450, file_format=&amp;#39;fits&amp;#39;, frame=frames[hips_survey.hips_frame], tile_width=512),&lt;/span&gt;
&lt;span class="gi"&gt;+        meta=HipsTileMeta(order=3, ipix=450, file_format=&amp;#39;fits&amp;#39;, frame=frames()[hips_survey.hips_frame],&lt;/span&gt;
&lt;span class="gi"&gt;+                          tile_width=512),&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        filename=get_hips_extra_file(&amp;#39;datasets/samples/DSS2Red/Norder3/Dir0/Npix450.fits&amp;#39;),
&lt;span class="w"&gt; &lt;/span&gt;    )

&lt;span class="w"&gt; &lt;/span&gt;    tile2 = HipsTile.read(
&lt;span class="gd"&gt;-        meta=HipsTileMeta(order=3, ipix=451, file_format=&amp;#39;fits&amp;#39;, frame=frames[hips_survey.hips_frame], tile_width=512),&lt;/span&gt;
&lt;span class="gi"&gt;+        meta=HipsTileMeta(order=3, ipix=451, file_format=&amp;#39;fits&amp;#39;, frame=frames()[hips_survey.hips_frame],&lt;/span&gt;
&lt;span class="gi"&gt;+                          tile_width=512),&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        filename=get_hips_extra_file(&amp;#39;datasets/samples/DSS2Red/Norder3/Dir0/Npix451.fits&amp;#39;),
&lt;span class="w"&gt; &lt;/span&gt;    )

&lt;span class="gh"&gt;diff --git a/hips/utils/healpix.py b/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gh"&gt;index 829248f..9eaffad 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -14,6 +14,7 @@ from .wcs import WCSGeometry&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;__all__ = [
&lt;span class="w"&gt; &lt;/span&gt;    &amp;#39;boundaries&amp;#39;,
&lt;span class="w"&gt; &lt;/span&gt;    &amp;#39;compute_healpix_pixel_indices&amp;#39;,
&lt;span class="gi"&gt;+    &amp;#39;frames&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;]

&lt;span class="w"&gt; &lt;/span&gt;__doctest_skip__ = [&amp;#39;boundaries&amp;#39;, &amp;#39;compute_healpix_pixel_indices&amp;#39;]
&lt;span class="gu"&gt;@@ -109,3 +110,7 @@ def compute_healpix_pixel_indices(wcs_geometry: WCSGeometry, order: int) -&amp;gt; np.n&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    vec = _skycoord_to_vec(center_coord)
&lt;span class="w"&gt; &lt;/span&gt;    nside = hp.order2nside(order)
&lt;span class="w"&gt; &lt;/span&gt;    return hp.query_disc(nside, vec, radius)
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+def frames() -&amp;gt; dict:&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;A dictionary mapping commonly used frames&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+    return dict({&amp;#39;equatorial&amp;#39;: &amp;#39;icrs&amp;#39;, &amp;#39;galactic&amp;#39;: &amp;#39;galactic&amp;#39;, &amp;#39;ecliptic&amp;#39;: &amp;#39;ecliptic&amp;#39;})&lt;/span&gt;

commit a9b0984a670a624b791fc99f11e93972edf97aad
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Jun 30 12:37:38 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Rename draw_tile to painter

&lt;span class="gh"&gt;diff --git a/hips/draw/simple.py b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index ef0908f..ea61810 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -33,8 +33,8 @@ def draw_sky_image(geometry: WCSGeometry, tiles: Generator[HipsTile, Any, Any])&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    # TODO: Fix type annotation issue
&lt;span class="w"&gt; &lt;/span&gt;    sky_sky = np.zeros(geometry.shape)
&lt;span class="w"&gt; &lt;/span&gt;    for tile in tiles:
&lt;span class="gd"&gt;-        draw_tile = SimpleTilePainter(geometry, tile)&lt;/span&gt;
&lt;span class="gd"&gt;-        sky_sky += draw_tile.warp_image()&lt;/span&gt;
&lt;span class="gi"&gt;+        painter = SimpleTilePainter(geometry, tile)&lt;/span&gt;
&lt;span class="gi"&gt;+        sky_sky += painter.warp_image()&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    return sky_sky



commit a0ad4cb044373e879a2d88b5559de91c91557e19
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Jun 30 12:36:50 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Rename all_sky to sky_sky

&lt;span class="gh"&gt;diff --git a/hips/draw/simple.py b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index 9a849cd..ef0908f 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -31,11 +31,11 @@ def draw_sky_image(geometry: WCSGeometry, tiles: Generator[HipsTile, Any, Any])&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        Returns a numpy array containing all HiPS tiles projected onto it
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;    # TODO: Fix type annotation issue
&lt;span class="gd"&gt;-    all_sky = np.zeros(geometry.shape)&lt;/span&gt;
&lt;span class="gi"&gt;+    sky_sky = np.zeros(geometry.shape)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    for tile in tiles:
&lt;span class="w"&gt; &lt;/span&gt;        draw_tile = SimpleTilePainter(geometry, tile)
&lt;span class="gd"&gt;-        all_sky += draw_tile.warp_image()&lt;/span&gt;
&lt;span class="gd"&gt;-    return all_sky&lt;/span&gt;
&lt;span class="gi"&gt;+        sky_sky += draw_tile.warp_image()&lt;/span&gt;
&lt;span class="gi"&gt;+    return sky_sky&lt;/span&gt;


&lt;span class="w"&gt; &lt;/span&gt;class SimpleTilePainter:

commit 9b79b9d19c6faa8f1bf5386076af04dc3340e995
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Jun 30 12:33:05 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Update test_skycoord_corners in TestHipsTileMeta

&lt;span class="gh"&gt;diff --git a/hips/tiles/__init__.py b/hips/tiles/__init__.py&lt;/span&gt;
&lt;span class="gh"&gt;index 0dfc508..3e45f09 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/__init__.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/__init__.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,6 +1,5 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="w"&gt; &lt;/span&gt;&amp;quot;&amp;quot;&amp;quot;Classes and functions to manage HiPS tiles.&amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;from .tile import *
&lt;span class="gd"&gt;-from .tile_meta import *&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from .description import *
&lt;span class="w"&gt; &lt;/span&gt;from .surveys import *
&lt;span class="gh"&gt;diff --git a/hips/tiles/tests/test_tile.py b/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gh"&gt;index 95701cd..7d85e81 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -69,3 +69,7 @@ class TestHipsTileMeta:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    def test_skycoord_corners(self):
&lt;span class="w"&gt; &lt;/span&gt;        assert_allclose(self.meta.skycoord_corners.ra.deg, [264.375, 258.75, 264.375, 270.])
&lt;span class="w"&gt; &lt;/span&gt;        assert_allclose(self.meta.skycoord_corners.dec.deg, [-24.624318, -30., -35.685335, -30.])
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        meta = HipsTileMeta(order=3, ipix=450, file_format=&amp;#39;fits&amp;#39;, frame=&amp;#39;galactic&amp;#39;, tile_width=512)&lt;/span&gt;
&lt;span class="gi"&gt;+        assert_allclose(meta.skycoord_corners.l.deg, [264.375, 258.75, 264.375, 270.])&lt;/span&gt;
&lt;span class="gi"&gt;+        assert_allclose(meta.skycoord_corners.b.deg, [-24.624318, -30., -35.685335, -30.])&lt;/span&gt;

commit 3c8204abecb09c62748100cdac40d0b33def4513
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Jun 30 12:25:52 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Add property projection in SimpleTilePainter

&lt;span class="gh"&gt;diff --git a/hips/draw/simple.py b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index 4808c6d..9a849cd 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -34,7 +34,6 @@ def draw_sky_image(geometry: WCSGeometry, tiles: Generator[HipsTile, Any, Any])&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    all_sky = np.zeros(geometry.shape)
&lt;span class="w"&gt; &lt;/span&gt;    for tile in tiles:
&lt;span class="w"&gt; &lt;/span&gt;        draw_tile = SimpleTilePainter(geometry, tile)
&lt;span class="gd"&gt;-        draw_tile.apply_projection()&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        all_sky += draw_tile.warp_image()
&lt;span class="w"&gt; &lt;/span&gt;    return all_sky

&lt;span class="gu"&gt;@@ -57,19 +56,19 @@ class SimpleTilePainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        self.tile = tile
&lt;span class="w"&gt; &lt;/span&gt;        self.pt = None

&lt;span class="gd"&gt;-    def apply_projection(self) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;Apply projective transformation on a HiPS tile&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+    @property&lt;/span&gt;
&lt;span class="gi"&gt;+    def projection(self) -&amp;gt; tf.ProjectiveTransform:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Estimate projective transformation on a HiPS tile&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        corners = self.tile.meta.skycoord_corners.to_pixel(self.geometry.wcs)
&lt;span class="w"&gt; &lt;/span&gt;        src = np.array(corners).T.reshape((4, 2))
&lt;span class="w"&gt; &lt;/span&gt;        dst = self.tile.meta.dst
&lt;span class="w"&gt; &lt;/span&gt;        self.pt = tf.ProjectiveTransform()
&lt;span class="w"&gt; &lt;/span&gt;        self.pt.estimate(src, dst)
&lt;span class="gi"&gt;+        return self.pt&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def warp_image(self) -&amp;gt; np.ndarray:
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Warp a HiPS tile and a sky image&amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-        # This converts a big-endian byte integer to a float&lt;/span&gt;
&lt;span class="gd"&gt;-        data = self.tile.data.astype(&amp;#39;float&amp;#39;)&lt;/span&gt;
&lt;span class="gd"&gt;-        return tf.warp(data, self.pt, output_shape=self.geometry.shape)&lt;/span&gt;
&lt;span class="gi"&gt;+        return tf.warp(self.tile.data, self.projection, output_shape=self.geometry.shape, preserve_range=True)&lt;/span&gt;


&lt;span class="w"&gt; &lt;/span&gt;def _fetch_tiles(healpix_pixel_indices: np.ndarray, order: int, hips_survey: HipsSurveyProperties) -&amp;gt; &amp;#39;HipsTile&amp;#39;:
&lt;span class="gu"&gt;@@ -90,8 +89,10 @@ def _fetch_tiles(healpix_pixel_indices: np.ndarray, order: int, hips_survey: Hip&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        Returns an object of  HipsTile
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;    base_url = hips_survey.access_url + &amp;#39;/Norder&amp;#39; + str(hips_survey.hips_order) + &amp;#39;/Dir0/&amp;#39;
&lt;span class="gi"&gt;+    frames = dict({&amp;#39;equatorial&amp;#39;: &amp;#39;icrs&amp;#39;, &amp;#39;galactic&amp;#39;: &amp;#39;galactic&amp;#39;, &amp;#39;ecliptic&amp;#39;: &amp;#39;ecliptic&amp;#39;})&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    for healpix_pixel_index in healpix_pixel_indices:
&lt;span class="gd"&gt;-        tile_meta = HipsTileMeta(order=order, ipix=healpix_pixel_index, file_format=&amp;#39;fits&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        tile_meta = HipsTileMeta(order=order, ipix=healpix_pixel_index,&lt;/span&gt;
&lt;span class="gi"&gt;+                                 frame=frames[hips_survey.hips_frame], file_format=&amp;#39;fits&amp;#39;)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        tile = HipsTile.fetch(tile_meta, base_url + tile_meta.filename)
&lt;span class="w"&gt; &lt;/span&gt;        yield tile


commit a7d4ba4faf0dd826e94181a5ecd092036fdb12bd
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Jun 30 12:22:16 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Update test case for HipsTileMeta class

&lt;span class="gh"&gt;diff --git a/hips/tiles/tests/test_tile.py b/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gh"&gt;index cc0e65a..95701cd 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,10 +1,10 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="gi"&gt;+import numpy as np&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from astropy.tests.helper import remote_data
&lt;span class="w"&gt; &lt;/span&gt;from numpy.testing import assert_allclose
&lt;span class="w"&gt; &lt;/span&gt;from numpy.testing import assert_equal

&lt;span class="gd"&gt;-from ..tile import HipsTile&lt;/span&gt;
&lt;span class="gd"&gt;-from ..tile_meta import HipsTileMeta&lt;/span&gt;
&lt;span class="gi"&gt;+from ..tile import HipsTile, HipsTileMeta&lt;/span&gt;


&lt;span class="w"&gt; &lt;/span&gt;class TestHipsTile:
&lt;span class="gu"&gt;@@ -46,7 +46,26 @@ class TestHipsTile:&lt;/span&gt;


&lt;span class="w"&gt; &lt;/span&gt;class TestHipsTileMeta:
&lt;span class="gi"&gt;+    @classmethod&lt;/span&gt;
&lt;span class="gi"&gt;+    def setup_class(cls):&lt;/span&gt;
&lt;span class="gi"&gt;+        cls.meta = HipsTileMeta(order=3, ipix=450, file_format=&amp;#39;fits&amp;#39;, frame=&amp;#39;icrs&amp;#39;, tile_width=512)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_path(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        assert str(self.meta.path) == &amp;#39;hips/tiles/tests/data&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_filename(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        assert self.meta.filename == &amp;#39;Npix450.fits&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_full_path(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        assert str(self.meta.full_path) == &amp;#39;hips/tiles/tests/data/Npix450.fits&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_nside(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        assert self.meta.nside == 8&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_dst(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        dst = np.array([[511, 0], [511, 511], [0, 511], [0, 0]])&lt;/span&gt;
&lt;span class="gi"&gt;+        assert_allclose(self.meta.dst, dst)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    def test_skycoord_corners(self):
&lt;span class="gd"&gt;-        meta = HipsTileMeta(order=3, ipix=450, file_format=&amp;#39;fits&amp;#39;, frame=&amp;#39;icrs&amp;#39;, tile_width=512)&lt;/span&gt;
&lt;span class="gd"&gt;-        assert_allclose(meta.skycoord_corners.ra.deg, [264.375, 258.75, 264.375, 270.])&lt;/span&gt;
&lt;span class="gd"&gt;-        assert_allclose(meta.skycoord_corners.dec.deg, [-24.624318, -30., -35.685335, -30.])&lt;/span&gt;
&lt;span class="gi"&gt;+        assert_allclose(self.meta.skycoord_corners.ra.deg, [264.375, 258.75, 264.375, 270.])&lt;/span&gt;
&lt;span class="gi"&gt;+        assert_allclose(self.meta.skycoord_corners.dec.deg, [-24.624318, -30., -35.685335, -30.])&lt;/span&gt;

commit 2846fca6dd4eb6027b428920f8a81940c1e5a881
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Jun 30 12:08:08 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Merge HipsTile and HipsTileMeta within same file

&lt;span class="gh"&gt;diff --git a/hips/tiles/tile.py b/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gh"&gt;index 76e2428..3ae0f4f 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,20 +1,105 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="gd"&gt;-from pathlib import Path&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;import urllib.request
&lt;span class="w"&gt; &lt;/span&gt;from io import BytesIO
&lt;span class="gi"&gt;+from pathlib import Path&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+import healpy as hp&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;import numpy as np
&lt;span class="w"&gt; &lt;/span&gt;from PIL import Image
&lt;span class="gi"&gt;+from astropy.coordinates import SkyCoord&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from astropy.io import fits
&lt;span class="w"&gt; &lt;/span&gt;from astropy.io.fits.header import Header
&lt;span class="gd"&gt;-from .tile_meta import HipsTileMeta&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+from ..utils import boundaries&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;__all__ = [
&lt;span class="w"&gt; &lt;/span&gt;    &amp;#39;HipsTile&amp;#39;,
&lt;span class="gi"&gt;+    &amp;#39;HipsTileMeta&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;]

&lt;span class="w"&gt; &lt;/span&gt;__doctest_skip__ = [&amp;#39;HipsTile&amp;#39;]


&lt;span class="gi"&gt;+class HipsTileMeta:&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;HiPS tile metadata.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    Parameters&lt;/span&gt;
&lt;span class="gi"&gt;+    ----------&lt;/span&gt;
&lt;span class="gi"&gt;+    order : `int`&lt;/span&gt;
&lt;span class="gi"&gt;+        HEALPix order&lt;/span&gt;
&lt;span class="gi"&gt;+    ipix : `int`&lt;/span&gt;
&lt;span class="gi"&gt;+        HEALPix pixel number&lt;/span&gt;
&lt;span class="gi"&gt;+    file_format : {&amp;#39;fits&amp;#39;, &amp;#39;jpg&amp;#39;, &amp;#39;png&amp;#39;}&lt;/span&gt;
&lt;span class="gi"&gt;+        File format&lt;/span&gt;
&lt;span class="gi"&gt;+    frame : {&amp;#39;icrs&amp;#39;, &amp;#39;galactic&amp;#39;, &amp;#39;ecliptic&amp;#39;}&lt;/span&gt;
&lt;span class="gi"&gt;+        Sky coordinate frame&lt;/span&gt;
&lt;span class="gi"&gt;+    tile_width : `int`&lt;/span&gt;
&lt;span class="gi"&gt;+        Tile width (in pixels)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    Examples&lt;/span&gt;
&lt;span class="gi"&gt;+    --------&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;gt;&amp;gt;&amp;gt; from hips.tiles import HipsTileMeta&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;gt;&amp;gt;&amp;gt; tile_meta = HipsTileMeta(order=3, ipix=450, file_format=&amp;#39;fits&amp;#39;, frame=&amp;#39;icrs&amp;#39;, tile_width=512)&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;gt;&amp;gt;&amp;gt; tile_meta.skycoord_corners&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;lt;SkyCoord (Galactic): (l, b) in deg&lt;/span&gt;
&lt;span class="gi"&gt;+    [( 264.375, -24.62431835), ( 258.75 , -30.        ),&lt;/span&gt;
&lt;span class="gi"&gt;+     ( 264.375, -35.68533471), ( 270.   , -30.        )]&amp;gt;&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def __init__(self, order: int, ipix: int, file_format: str, frame: str = &amp;#39;galactic&amp;#39;, tile_width: int = 512) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+        self.order = order&lt;/span&gt;
&lt;span class="gi"&gt;+        self.ipix = ipix&lt;/span&gt;
&lt;span class="gi"&gt;+        self.file_format = file_format&lt;/span&gt;
&lt;span class="gi"&gt;+        self.frame = frame&lt;/span&gt;
&lt;span class="gi"&gt;+        self.tile_width = tile_width&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def __eq__(self, other: &amp;#39;HipsTileMeta&amp;#39;) -&amp;gt; bool:&lt;/span&gt;
&lt;span class="gi"&gt;+        return (&lt;/span&gt;
&lt;span class="gi"&gt;+            self.order == other.order and&lt;/span&gt;
&lt;span class="gi"&gt;+            self.ipix == other.ipix and&lt;/span&gt;
&lt;span class="gi"&gt;+            self.file_format == other.file_format and&lt;/span&gt;
&lt;span class="gi"&gt;+            self.tile_width == other.tile_width&lt;/span&gt;
&lt;span class="gi"&gt;+        )&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @property&lt;/span&gt;
&lt;span class="gi"&gt;+    def path(self) -&amp;gt; Path:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Default path for tile storage (`~pathlib.Path`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        return Path(&amp;#39;hips&amp;#39;, &amp;#39;tiles&amp;#39;, &amp;#39;tests&amp;#39;, &amp;#39;data&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @property&lt;/span&gt;
&lt;span class="gi"&gt;+    def filename(self) -&amp;gt; str:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Filename for HiPS tile (`str`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        return &amp;#39;&amp;#39;.join([&amp;#39;Npix&amp;#39;, str(self.ipix), &amp;#39;.&amp;#39;, self.file_format])&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @property&lt;/span&gt;
&lt;span class="gi"&gt;+    def full_path(self) -&amp;gt; Path:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Full path (folder and filename) (`~pathlib.Path`)&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        return self.path / self.filename&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @property&lt;/span&gt;
&lt;span class="gi"&gt;+    def nside(self) -&amp;gt; int:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;nside of the HEALPix map&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        return hp.order2nside(self.order)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @property&lt;/span&gt;
&lt;span class="gi"&gt;+    def dst(self) -&amp;gt; np.ndarray:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Destination array for projective transform&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        return np.array(&lt;/span&gt;
&lt;span class="gi"&gt;+            [[self.tile_width - 1, 0],&lt;/span&gt;
&lt;span class="gi"&gt;+             [self.tile_width - 1, self.tile_width - 1],&lt;/span&gt;
&lt;span class="gi"&gt;+             [0, self.tile_width - 1],&lt;/span&gt;
&lt;span class="gi"&gt;+             [0, 0]])&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @property&lt;/span&gt;
&lt;span class="gi"&gt;+    def skycoord_corners(self) -&amp;gt; SkyCoord:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Corner values for a HiPS tile&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        theta, phi = boundaries(self.nside, self.ipix)&lt;/span&gt;
&lt;span class="gi"&gt;+        if self.frame == &amp;#39;galactic&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+            return SkyCoord(l=phi, b=np.pi / 2 - theta, unit=&amp;#39;radian&amp;#39;, frame=self.frame)&lt;/span&gt;
&lt;span class="gi"&gt;+        else:&lt;/span&gt;
&lt;span class="gi"&gt;+            return SkyCoord(ra=phi, dec=np.pi / 2 - theta, unit=&amp;#39;radian&amp;#39;, frame=self.frame)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;class HipsTile:
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;HiPS tile container.

&lt;span class="gu"&gt;@@ -31,8 +116,7 @@ class HipsTile:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    Examples
&lt;span class="w"&gt; &lt;/span&gt;    --------
&lt;span class="gd"&gt;-    &amp;gt;&amp;gt;&amp;gt; from hips.tiles import HipsTile&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;gt;&amp;gt;&amp;gt; from hips.tiles import HipsTileMeta&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;gt;&amp;gt;&amp;gt; from hips.tiles import HipsTile, HipsTileMeta&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;gt;&amp;gt;&amp;gt; meta = HipsTileMeta(order=6, ipix=30889, file_format=&amp;#39;fits&amp;#39;)
&lt;span class="w"&gt; &lt;/span&gt;    &amp;gt;&amp;gt;&amp;gt; url = &amp;#39;http://alasky.unistra.fr/2MASS/H/Norder6/Dir30000/Npix30889.fits&amp;#39;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;gt;&amp;gt;&amp;gt; tile = HipsTile.fetch(meta, url)
&lt;span class="gh"&gt;diff --git a/hips/tiles/tile_meta.py b/hips/tiles/tile_meta.py&lt;/span&gt;
deleted file mode 100644
&lt;span class="gh"&gt;index 29488bc..0000000&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tile_meta.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ /dev/null&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,92 +0,0 @@&lt;/span&gt;
&lt;span class="gd"&gt;-# Licensed under a 3-clause BSD style license - see LICENSE.rst&lt;/span&gt;
&lt;span class="gd"&gt;-from pathlib import Path&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-import healpy as hp&lt;/span&gt;
&lt;span class="gd"&gt;-import numpy as np&lt;/span&gt;
&lt;span class="gd"&gt;-from astropy.coordinates import SkyCoord&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-from ..utils import boundaries&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-__all__ = [&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;#39;HipsTileMeta&amp;#39;,&lt;/span&gt;
&lt;span class="gd"&gt;-]&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-class HipsTileMeta:&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;quot;&amp;quot;&amp;quot;HiPS tile metadata.&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    Parameters&lt;/span&gt;
&lt;span class="gd"&gt;-    ----------&lt;/span&gt;
&lt;span class="gd"&gt;-    order : `int`&lt;/span&gt;
&lt;span class="gd"&gt;-        HEALPix order&lt;/span&gt;
&lt;span class="gd"&gt;-    ipix : `int`&lt;/span&gt;
&lt;span class="gd"&gt;-        HEALPix pixel number&lt;/span&gt;
&lt;span class="gd"&gt;-    file_format : {&amp;#39;fits&amp;#39;, &amp;#39;jpg&amp;#39;, &amp;#39;png&amp;#39;}&lt;/span&gt;
&lt;span class="gd"&gt;-        File format&lt;/span&gt;
&lt;span class="gd"&gt;-    frame : {&amp;#39;icrs&amp;#39;, &amp;#39;galactic&amp;#39;, &amp;#39;ecliptic&amp;#39;}&lt;/span&gt;
&lt;span class="gd"&gt;-        Sky coordinate frame&lt;/span&gt;
&lt;span class="gd"&gt;-    tile_width : `int`&lt;/span&gt;
&lt;span class="gd"&gt;-        Tile width (in pixels)&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    Examples&lt;/span&gt;
&lt;span class="gd"&gt;-    --------&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;gt;&amp;gt;&amp;gt; from hips.tiles import HipsTileMeta&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;gt;&amp;gt;&amp;gt; tile_meta = HipsTileMeta(order=3, ipix=450, file_format=&amp;#39;fits&amp;#39;, frame=&amp;#39;galactic&amp;#39;, tile_width=512)&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;gt;&amp;gt;&amp;gt; tile_meta.skycoord_corners&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;lt;SkyCoord (Galactic): (l, b) in deg&lt;/span&gt;
&lt;span class="gd"&gt;-    [( 264.375, -24.62431835), ( 258.75 , -30.        ),&lt;/span&gt;
&lt;span class="gd"&gt;-     ( 264.375, -35.68533471), ( 270.   , -30.        )]&amp;gt;&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    def __init__(self, order: int, ipix: int, file_format: str, frame: str = &amp;#39;galactic&amp;#39;, tile_width: int = 512) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gd"&gt;-        self.order = order&lt;/span&gt;
&lt;span class="gd"&gt;-        self.ipix = ipix&lt;/span&gt;
&lt;span class="gd"&gt;-        self.file_format = file_format&lt;/span&gt;
&lt;span class="gd"&gt;-        self.frame = frame&lt;/span&gt;
&lt;span class="gd"&gt;-        self.tile_width = tile_width&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    def __eq__(self, other: &amp;#39;HipsTileMeta&amp;#39;) -&amp;gt; bool:&lt;/span&gt;
&lt;span class="gd"&gt;-        return (&lt;/span&gt;
&lt;span class="gd"&gt;-            self.order == other.order and&lt;/span&gt;
&lt;span class="gd"&gt;-            self.ipix == other.ipix and&lt;/span&gt;
&lt;span class="gd"&gt;-            self.file_format == other.file_format and&lt;/span&gt;
&lt;span class="gd"&gt;-            self.tile_width == other.tile_width&lt;/span&gt;
&lt;span class="gd"&gt;-        )&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    @property&lt;/span&gt;
&lt;span class="gd"&gt;-    def path(self) -&amp;gt; Path:&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;Default path for tile storage (`~pathlib.Path`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-        return Path(&amp;#39;hips&amp;#39;, &amp;#39;tiles&amp;#39;, &amp;#39;tests&amp;#39;, &amp;#39;data&amp;#39;)&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    @property&lt;/span&gt;
&lt;span class="gd"&gt;-    def filename(self) -&amp;gt; str:&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;Filename for HiPS tile (`str`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-        return &amp;#39;&amp;#39;.join([&amp;#39;Npix&amp;#39;, str(self.ipix), &amp;#39;.&amp;#39;, self.file_format])&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    @property&lt;/span&gt;
&lt;span class="gd"&gt;-    def full_path(self) -&amp;gt; Path:&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;Full path (folder and filename) (`~pathlib.Path`)&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-        return self.path / self.filename&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    @property&lt;/span&gt;
&lt;span class="gd"&gt;-    def nside(self) -&amp;gt; int:&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;nside of the HEALPix map&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-        return hp.order2nside(self.order)&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    @property&lt;/span&gt;
&lt;span class="gd"&gt;-    def dst(self) -&amp;gt; np.ndarray:&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;Destination array for projective transform&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-        return np.array(&lt;/span&gt;
&lt;span class="gd"&gt;-            [[self.tile_width - 1, 0],&lt;/span&gt;
&lt;span class="gd"&gt;-             [self.tile_width - 1, self.tile_width - 1],&lt;/span&gt;
&lt;span class="gd"&gt;-             [0, self.tile_width - 1],&lt;/span&gt;
&lt;span class="gd"&gt;-             [0, 0]])&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    @property&lt;/span&gt;
&lt;span class="gd"&gt;-    def skycoord_corners(self) -&amp;gt; SkyCoord:&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;Corner values for a HiPS tile&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-        theta, phi = boundaries(self.nside, self.ipix)&lt;/span&gt;
&lt;span class="gd"&gt;-        if self.frame == &amp;#39;galactic&amp;#39;:&lt;/span&gt;
&lt;span class="gd"&gt;-            return SkyCoord(l=phi, b=np.pi / 2 - theta, unit=&amp;#39;radian&amp;#39;, frame=self.frame)&lt;/span&gt;
&lt;span class="gd"&gt;-        else:&lt;/span&gt;
&lt;span class="gd"&gt;-            return SkyCoord(ra=phi, dec=np.pi / 2 - theta, unit=&amp;#39;radian&amp;#39;, frame=self.frame)&lt;/span&gt;

commit 309a75a2c1b468dcd7e5969d2bd01f60f6a9005e
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Jun 30 11:57:09 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Pass fits_header to fits.PrimaryHDU function

&lt;span class="gh"&gt;diff --git a/docs/getting_started.rst b/docs/getting_started.rst&lt;/span&gt;
&lt;span class="gh"&gt;index 7018ccf..81c48db 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/docs/getting_started.rst&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/docs/getting_started.rst&lt;/span&gt;
&lt;span class="gu"&gt;@@ -24,5 +24,5 @@ Then it saves it on local disk in FITS file format.&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&amp;gt; url = &amp;#39;https://raw.githubusercontent.com/hipspy/hips-extra/master/datasets/samples/DSS2Red/properties&amp;#39;
&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&amp;gt; hips_survey = HipsSurveyProperties.fetch(url)  # doctest: +REMOTE_DATA
&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&amp;gt; data = make_sky_image(geometry, hips_survey)
&lt;span class="gd"&gt;-&amp;gt;&amp;gt;&amp;gt; hdu = fits.PrimaryHDU(data=data)&lt;/span&gt;
&lt;span class="gi"&gt;+&amp;gt;&amp;gt;&amp;gt; hdu = fits.PrimaryHDU(data=data, header=geometry.fits_header)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&amp;gt; hdu.writeto(&amp;#39;my_image.fits&amp;#39;)
\ No newline at end of file

commit a0759cbe24b41ac50a86165db4438d474385291f
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Jun 30 11:11:23 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Remove test case for SimpleTilePainter class

&lt;span class="gh"&gt;diff --git a/hips/draw/tests/test_simple.py b/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index e99338f..80818f9 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -4,7 +4,7 @@ import pytest&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from astropy.utils.data import get_pkg_data_filename
&lt;span class="w"&gt; &lt;/span&gt;from numpy.testing import assert_allclose

&lt;span class="gd"&gt;-from ..simple import make_sky_image, draw_sky_image, SimpleTilePainter&lt;/span&gt;
&lt;span class="gi"&gt;+from ..simple import make_sky_image, draw_sky_image&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from ...tiles import HipsSurveyProperties, HipsTileMeta, HipsTile
&lt;span class="w"&gt; &lt;/span&gt;from ...utils.testing import get_hips_extra_file, make_test_wcs_geometry, requires_hips_extra

&lt;span class="gu"&gt;@@ -31,7 +31,6 @@ def get_test_tiles():&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;def test_draw_sky_image():
&lt;span class="w"&gt; &lt;/span&gt;    geometry = make_test_wcs_geometry(case=2)
&lt;span class="w"&gt; &lt;/span&gt;    tiles = get_test_tiles()
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    data = draw_sky_image(geometry, tiles)

&lt;span class="w"&gt; &lt;/span&gt;    assert data.shape == geometry.shape
&lt;span class="gu"&gt;@@ -51,21 +50,3 @@ def test_make_sky_image():&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    assert data.dtype == np.float64
&lt;span class="w"&gt; &lt;/span&gt;    assert_allclose(data[200, 994], 3717.10091363)
&lt;span class="w"&gt; &lt;/span&gt;    assert_allclose(data[200, 995], 3402.55292158)
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-class TestSimpleTilePainter:&lt;/span&gt;
&lt;span class="gd"&gt;-    @classmethod&lt;/span&gt;
&lt;span class="gd"&gt;-    def setup_class(cls):&lt;/span&gt;
&lt;span class="gd"&gt;-        geometry = make_test_wcs_geometry(case=2)&lt;/span&gt;
&lt;span class="gd"&gt;-        tile = HipsTile.read(&lt;/span&gt;
&lt;span class="gd"&gt;-            meta=HipsTileMeta(order=3, ipix=450, file_format=&amp;#39;fits&amp;#39;, frame=&amp;#39;icrs&amp;#39;, tile_width=512),&lt;/span&gt;
&lt;span class="gd"&gt;-            filename=get_hips_extra_file(&amp;#39;datasets/samples/DSS2Red/Norder3/Dir0/Npix450.fits&amp;#39;),&lt;/span&gt;
&lt;span class="gd"&gt;-        )&lt;/span&gt;
&lt;span class="gd"&gt;-        cls.simple_tile_painter = SimpleTilePainter(geometry, tile)&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    # def test_warp_image(self):&lt;/span&gt;
&lt;span class="gd"&gt;-    #     self.simple_tile_painter.warp_image()&lt;/span&gt;
&lt;span class="gd"&gt;-    #     assert_allclose(self.simple_tile_painter.tile.meta.skycoord_corners.ra.deg,&lt;/span&gt;
&lt;span class="gd"&gt;-    #                     [264.375, 258.75, 264.375, 270.])&lt;/span&gt;
&lt;span class="gd"&gt;-    #     assert_allclose(self.simple_tile_painter.tile.meta.skycoord_corners.dec.deg,&lt;/span&gt;
&lt;span class="gd"&gt;-    #                     [-24.624318, -30., -35.685335, -30.])&lt;/span&gt;

commit 4571dd88dbd34a408ed934cb47cddfa752642119
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Jun 30 11:11:00 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Add comment on data type conversion

&lt;span class="gh"&gt;diff --git a/hips/draw/simple.py b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index f188755..4808c6d 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -67,7 +67,9 @@ class SimpleTilePainter:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def warp_image(self) -&amp;gt; np.ndarray:
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Warp a HiPS tile and a sky image&amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-        return tf.warp(self.tile.data.astype(&amp;#39;float&amp;#39;), self.pt, output_shape=self.geometry.shape)&lt;/span&gt;
&lt;span class="gi"&gt;+        # This converts a big-endian byte integer to a float&lt;/span&gt;
&lt;span class="gi"&gt;+        data = self.tile.data.astype(&amp;#39;float&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        return tf.warp(data, self.pt, output_shape=self.geometry.shape)&lt;/span&gt;


&lt;span class="w"&gt; &lt;/span&gt;def _fetch_tiles(healpix_pixel_indices: np.ndarray, order: int, hips_survey: HipsSurveyProperties) -&amp;gt; &amp;#39;HipsTile&amp;#39;:

commit 9e073c9370924fb0651db0c97631e53635b17abd
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Jun 30 11:01:02 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Remove docstring

&lt;span class="gh"&gt;diff --git a/hips/utils/testing.py b/hips/utils/testing.py&lt;/span&gt;
&lt;span class="gh"&gt;index ce8556c..d9d6d69 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/testing.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/testing.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -32,7 +32,6 @@ def requires_hips_extra():&lt;/span&gt;


&lt;span class="w"&gt; &lt;/span&gt;def make_test_wcs_geometry(case=0):
&lt;span class="gd"&gt;-    &amp;quot;&amp;quot;&amp;quot;An example Galactic CAR WCS that &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    if case == 0:
&lt;span class="w"&gt; &lt;/span&gt;        return WCSGeometry.create(
&lt;span class="w"&gt; &lt;/span&gt;            skydir=SkyCoord(3, 4, unit=&amp;#39;deg&amp;#39;, frame=&amp;#39;galactic&amp;#39;),

commit 9f2e5fac6222fdaa6ddde8a1d6d973fe782968d6
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Jun 30 11:00:12 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Add test case for HipsTileMeta

&lt;span class="gh"&gt;diff --git a/hips/tiles/tests/test_tile.py b/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gh"&gt;index 1514ea6..cc0e65a 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,6 +1,8 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="gd"&gt;-from numpy.testing import assert_equal&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from astropy.tests.helper import remote_data
&lt;span class="gi"&gt;+from numpy.testing import assert_allclose&lt;/span&gt;
&lt;span class="gi"&gt;+from numpy.testing import assert_equal&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from ..tile import HipsTile
&lt;span class="w"&gt; &lt;/span&gt;from ..tile_meta import HipsTileMeta

&lt;span class="gu"&gt;@@ -41,3 +43,10 @@ class TestHipsTile:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        # print(tile2.data.sum())
&lt;span class="w"&gt; &lt;/span&gt;        # print((tile == tile2).all())
&lt;span class="w"&gt; &lt;/span&gt;        # assert tile == tile2
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+class TestHipsTileMeta:&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_skycoord_corners(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        meta = HipsTileMeta(order=3, ipix=450, file_format=&amp;#39;fits&amp;#39;, frame=&amp;#39;icrs&amp;#39;, tile_width=512)&lt;/span&gt;
&lt;span class="gi"&gt;+        assert_allclose(meta.skycoord_corners.ra.deg, [264.375, 258.75, 264.375, 270.])&lt;/span&gt;
&lt;span class="gi"&gt;+        assert_allclose(meta.skycoord_corners.dec.deg, [-24.624318, -30., -35.685335, -30.])&lt;/span&gt;

commit 69ae7642417b6b76796a448b9b9a3f4b387a9021
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Jun 30 10:54:44 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Add function apply_projection in SimpleTilePainter

&lt;span class="gh"&gt;diff --git a/hips/draw/simple.py b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index 0f8b418..f188755 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -2,7 +2,6 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&amp;quot;&amp;quot;&amp;quot;HiPS tile drawing -- simple method.&amp;quot;&amp;quot;&amp;quot;

&lt;span class="w"&gt; &lt;/span&gt;import numpy as np
&lt;span class="gd"&gt;-from astropy.coordinates import SkyCoord&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from skimage import transform as tf
&lt;span class="w"&gt; &lt;/span&gt;from typing import List, Generator, Any

&lt;span class="gu"&gt;@@ -31,9 +30,11 @@ def draw_sky_image(geometry: WCSGeometry, tiles: Generator[HipsTile, Any, Any])&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    np.ndarray
&lt;span class="w"&gt; &lt;/span&gt;        Returns a numpy array containing all HiPS tiles projected onto it
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;
&lt;span class="gi"&gt;+    # TODO: Fix type annotation issue&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    all_sky = np.zeros(geometry.shape)
&lt;span class="w"&gt; &lt;/span&gt;    for tile in tiles:
&lt;span class="w"&gt; &lt;/span&gt;        draw_tile = SimpleTilePainter(geometry, tile)
&lt;span class="gi"&gt;+        draw_tile.apply_projection()&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        all_sky += draw_tile.warp_image()
&lt;span class="w"&gt; &lt;/span&gt;    return all_sky

&lt;span class="gu"&gt;@@ -54,11 +55,19 @@ class SimpleTilePainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    def __init__(self, geometry: WCSGeometry, tile: HipsTile) -&amp;gt; None:
&lt;span class="w"&gt; &lt;/span&gt;        self.geometry = geometry
&lt;span class="w"&gt; &lt;/span&gt;        self.tile = tile
&lt;span class="gi"&gt;+        self.pt = None&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def apply_projection(self) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Apply projective transformation on a HiPS tile&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        corners = self.tile.meta.skycoord_corners.to_pixel(self.geometry.wcs)&lt;/span&gt;
&lt;span class="gi"&gt;+        src = np.array(corners).T.reshape((4, 2))&lt;/span&gt;
&lt;span class="gi"&gt;+        dst = self.tile.meta.dst&lt;/span&gt;
&lt;span class="gi"&gt;+        self.pt = tf.ProjectiveTransform()&lt;/span&gt;
&lt;span class="gi"&gt;+        self.pt.estimate(src, dst)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def warp_image(self) -&amp;gt; np.ndarray:
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Warp a HiPS tile and a sky image&amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-        pt = self.tile.meta.apply_projection(self.geometry.wcs)&lt;/span&gt;
&lt;span class="gd"&gt;-        return tf.warp(self.tile.data.astype(&amp;#39;float&amp;#39;), pt, output_shape=self.geometry.shape)&lt;/span&gt;
&lt;span class="gi"&gt;+        return tf.warp(self.tile.data.astype(&amp;#39;float&amp;#39;), self.pt, output_shape=self.geometry.shape)&lt;/span&gt;


&lt;span class="w"&gt; &lt;/span&gt;def _fetch_tiles(healpix_pixel_indices: np.ndarray, order: int, hips_survey: HipsSurveyProperties) -&amp;gt; &amp;#39;HipsTile&amp;#39;:
&lt;span class="gh"&gt;diff --git a/hips/draw/tests/test_simple.py b/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index acec27f..e99338f 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -63,9 +63,9 @@ class TestSimpleTilePainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        )
&lt;span class="w"&gt; &lt;/span&gt;        cls.simple_tile_painter = SimpleTilePainter(geometry, tile)

&lt;span class="gd"&gt;-    def test_warp_image(self):&lt;/span&gt;
&lt;span class="gd"&gt;-        self.simple_tile_painter.warp_image()&lt;/span&gt;
&lt;span class="gd"&gt;-        assert_allclose(self.simple_tile_painter.tile.meta.skycoord_corners.ra.deg,&lt;/span&gt;
&lt;span class="gd"&gt;-                        [264.375, 258.75, 264.375, 270.])&lt;/span&gt;
&lt;span class="gd"&gt;-        assert_allclose(self.simple_tile_painter.tile.meta.skycoord_corners.dec.deg,&lt;/span&gt;
&lt;span class="gd"&gt;-                        [-24.624318, -30., -35.685335, -30.])&lt;/span&gt;
&lt;span class="gi"&gt;+    # def test_warp_image(self):&lt;/span&gt;
&lt;span class="gi"&gt;+    #     self.simple_tile_painter.warp_image()&lt;/span&gt;
&lt;span class="gi"&gt;+    #     assert_allclose(self.simple_tile_painter.tile.meta.skycoord_corners.ra.deg,&lt;/span&gt;
&lt;span class="gi"&gt;+    #                     [264.375, 258.75, 264.375, 270.])&lt;/span&gt;
&lt;span class="gi"&gt;+    #     assert_allclose(self.simple_tile_painter.tile.meta.skycoord_corners.dec.deg,&lt;/span&gt;
&lt;span class="gi"&gt;+    #                     [-24.624318, -30., -35.685335, -30.])&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/tiles/tile.py b/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gh"&gt;index 234fb5d..76e2428 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -95,7 +95,6 @@ class HipsTile:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        if meta.file_format == &amp;#39;fits&amp;#39;:
&lt;span class="w"&gt; &lt;/span&gt;            hdu_list = fits.open(str(path))
&lt;span class="gd"&gt;-            # This converts a big-endian byte integer to a float&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            data = hdu_list[0].data
&lt;span class="w"&gt; &lt;/span&gt;            header = hdu_list[0].header
&lt;span class="w"&gt; &lt;/span&gt;            return cls(meta, data, header)

commit c7d5db5d8c54bb91fd3e91fd24d3991215bcdc1a
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Jun 30 10:53:45 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Remove function apply_projection from HipsTileMeta

&lt;span class="gh"&gt;diff --git a/hips/tiles/tile_meta.py b/hips/tiles/tile_meta.py&lt;/span&gt;
&lt;span class="gh"&gt;index 826e56b..29488bc 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tile_meta.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tile_meta.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -4,8 +4,6 @@ from pathlib import Path&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;import healpy as hp
&lt;span class="w"&gt; &lt;/span&gt;import numpy as np
&lt;span class="w"&gt; &lt;/span&gt;from astropy.coordinates import SkyCoord
&lt;span class="gd"&gt;-from astropy.wcs import WCS&lt;/span&gt;
&lt;span class="gd"&gt;-from skimage import transform as tf&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;from ..utils import boundaries

&lt;span class="gu"&gt;@@ -92,12 +90,3 @@ class HipsTileMeta:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            return SkyCoord(l=phi, b=np.pi / 2 - theta, unit=&amp;#39;radian&amp;#39;, frame=self.frame)
&lt;span class="w"&gt; &lt;/span&gt;        else:
&lt;span class="w"&gt; &lt;/span&gt;            return SkyCoord(ra=phi, dec=np.pi / 2 - theta, unit=&amp;#39;radian&amp;#39;, frame=self.frame)
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    def apply_projection(self, wcs: WCS) -&amp;gt; tf.ProjectiveTransform:&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;Apply projective transformation on a HiPS tile&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-        corners = self.skycoord_corners.to_pixel(wcs)&lt;/span&gt;
&lt;span class="gd"&gt;-        src = np.array(corners).T.reshape((4, 2))&lt;/span&gt;
&lt;span class="gd"&gt;-        dst = self.dst&lt;/span&gt;
&lt;span class="gd"&gt;-        pt = tf.ProjectiveTransform()&lt;/span&gt;
&lt;span class="gd"&gt;-        pt.estimate(src, dst)&lt;/span&gt;
&lt;span class="gd"&gt;-        return pt&lt;/span&gt;

commit bee4bcd0542657c74b040947ec20a6d9ab41edc3
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Jun 30 10:46:03 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Document frame parameter in HipsTileMeta

&lt;span class="gh"&gt;diff --git a/hips/tiles/tile_meta.py b/hips/tiles/tile_meta.py&lt;/span&gt;
&lt;span class="gh"&gt;index 67759fb..826e56b 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tile_meta.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tile_meta.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -25,6 +25,8 @@ class HipsTileMeta:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        HEALPix pixel number
&lt;span class="w"&gt; &lt;/span&gt;    file_format : {&amp;#39;fits&amp;#39;, &amp;#39;jpg&amp;#39;, &amp;#39;png&amp;#39;}
&lt;span class="w"&gt; &lt;/span&gt;        File format
&lt;span class="gi"&gt;+    frame : {&amp;#39;icrs&amp;#39;, &amp;#39;galactic&amp;#39;, &amp;#39;ecliptic&amp;#39;}&lt;/span&gt;
&lt;span class="gi"&gt;+        Sky coordinate frame&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    tile_width : `int`
&lt;span class="w"&gt; &lt;/span&gt;        Tile width (in pixels)

&lt;span class="gu"&gt;@@ -38,7 +40,7 @@ class HipsTileMeta:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;     ( 264.375, -35.68533471), ( 270.   , -30.        )]&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;

&lt;span class="gd"&gt;-    def __init__(self, order: int, ipix: int, file_format: str, frame=&amp;#39;galactic&amp;#39;, tile_width: int = 512) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+    def __init__(self, order: int, ipix: int, file_format: str, frame: str = &amp;#39;galactic&amp;#39;, tile_width: int = 512) -&amp;gt; None:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        self.order = order
&lt;span class="w"&gt; &lt;/span&gt;        self.ipix = ipix
&lt;span class="w"&gt; &lt;/span&gt;        self.file_format = file_format

commit 9c5e687cf2948241dd13cedfe0ec79aba0389f38
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Jun 30 10:43:07 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Add missing import

&lt;span class="gh"&gt;diff --git a/docs/getting_started.rst b/docs/getting_started.rst&lt;/span&gt;
&lt;span class="gh"&gt;index ad0bdaa..7018ccf 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/docs/getting_started.rst&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/docs/getting_started.rst&lt;/span&gt;
&lt;span class="gu"&gt;@@ -11,6 +11,7 @@ It fetches a HiPS tile from a remote URL and draws it on a sky image.&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;Then it saves it on local disk in FITS file format.

&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&amp;gt; from astropy.io import fits
&lt;span class="gi"&gt;+&amp;gt;&amp;gt;&amp;gt; from astropy.coordinates import SkyCoord&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&amp;gt; from astropy.tests.helper import remote_data
&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&amp;gt; from hips.utils import WCSGeometry
&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&amp;gt; from hips.draw import make_sky_image

commit 9c82bc4c642c9417659ade65653490a39294b797
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Jun 30 10:42:52 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Use assert_allclose instead of assert

&lt;span class="gh"&gt;diff --git a/hips/draw/tests/test_simple.py b/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index de1b950..acec27f 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -36,9 +36,9 @@ def test_draw_sky_image():&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    assert data.shape == geometry.shape
&lt;span class="w"&gt; &lt;/span&gt;    assert data.dtype == np.float64
&lt;span class="gd"&gt;-    assert np.sum(data) == 4575235421.5126467&lt;/span&gt;
&lt;span class="gd"&gt;-    assert data[400, 500] == 2866.0101409848185&lt;/span&gt;
&lt;span class="gd"&gt;-    assert data[400, 501] == 2563.6916727348043&lt;/span&gt;
&lt;span class="gi"&gt;+    assert_allclose(np.sum(data), 4575235421.5126467)&lt;/span&gt;
&lt;span class="gi"&gt;+    assert_allclose(data[400, 500], 2866.0101409848185)&lt;/span&gt;
&lt;span class="gi"&gt;+    assert_allclose(data[400, 501], 2563.6916727348043)&lt;/span&gt;


&lt;span class="w"&gt; &lt;/span&gt;@pytest.mark.xfail
&lt;span class="gu"&gt;@@ -49,8 +49,8 @@ def test_make_sky_image():&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    data = make_sky_image(geometry, hips_survey)
&lt;span class="w"&gt; &lt;/span&gt;    assert data.shape == geometry.shape
&lt;span class="w"&gt; &lt;/span&gt;    assert data.dtype == np.float64
&lt;span class="gd"&gt;-    assert data[200, 994] == 3717.10091363&lt;/span&gt;
&lt;span class="gd"&gt;-    assert data[200, 995] == 3402.55292158&lt;/span&gt;
&lt;span class="gi"&gt;+    assert_allclose(data[200, 994], 3717.10091363)&lt;/span&gt;
&lt;span class="gi"&gt;+    assert_allclose(data[200, 995], 3402.55292158)&lt;/span&gt;


&lt;span class="w"&gt; &lt;/span&gt;class TestSimpleTilePainter:

commit 8adff08b687c280f302df01bd8d1306a5e0a4008
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Thu Jun 29 23:41:41 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Update drawing code

&lt;span class="gh"&gt;diff --git a/hips/draw/simple.py b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index c6652d6..0f8b418 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -34,8 +34,6 @@ def draw_sky_image(geometry: WCSGeometry, tiles: Generator[HipsTile, Any, Any])&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    all_sky = np.zeros(geometry.shape)
&lt;span class="w"&gt; &lt;/span&gt;    for tile in tiles:
&lt;span class="w"&gt; &lt;/span&gt;        draw_tile = SimpleTilePainter(geometry, tile)
&lt;span class="gd"&gt;-        draw_tile.compute_corners()&lt;/span&gt;
&lt;span class="gd"&gt;-        draw_tile.compute_projection()&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        all_sky += draw_tile.warp_image()
&lt;span class="w"&gt; &lt;/span&gt;    return all_sky

&lt;span class="gu"&gt;@@ -56,20 +54,11 @@ class SimpleTilePainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    def __init__(self, geometry: WCSGeometry, tile: HipsTile) -&amp;gt; None:
&lt;span class="w"&gt; &lt;/span&gt;        self.geometry = geometry
&lt;span class="w"&gt; &lt;/span&gt;        self.tile = tile
&lt;span class="gd"&gt;-        self.corners = None&lt;/span&gt;
&lt;span class="gd"&gt;-        self.pt = None&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    def compute_corners(self):&lt;/span&gt;
&lt;span class="gd"&gt;-        self.corners = self.tile.meta.skycoord_corners.to_pixel(self.geometry.wcs)&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    def compute_projection(self) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gd"&gt;-        src = np.array(self.corners).T.reshape((4, 2))&lt;/span&gt;
&lt;span class="gd"&gt;-        dst = self.tile.meta.dst&lt;/span&gt;
&lt;span class="gd"&gt;-        self.pt = tf.ProjectiveTransform()&lt;/span&gt;
&lt;span class="gd"&gt;-        self.pt.estimate(src, dst)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def warp_image(self) -&amp;gt; np.ndarray:
&lt;span class="gd"&gt;-        return tf.warp(self.tile.data.astype(&amp;#39;float&amp;#39;), self.pt, output_shape=self.geometry.shape)&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Warp a HiPS tile and a sky image&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        pt = self.tile.meta.apply_projection(self.geometry.wcs)&lt;/span&gt;
&lt;span class="gi"&gt;+        return tf.warp(self.tile.data.astype(&amp;#39;float&amp;#39;), pt, output_shape=self.geometry.shape)&lt;/span&gt;


&lt;span class="w"&gt; &lt;/span&gt;def _fetch_tiles(healpix_pixel_indices: np.ndarray, order: int, hips_survey: HipsSurveyProperties) -&amp;gt; &amp;#39;HipsTile&amp;#39;:
&lt;span class="gu"&gt;@@ -89,7 +78,7 @@ def _fetch_tiles(healpix_pixel_indices: np.ndarray, order: int, hips_survey: Hip&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;#39;HipsTile&amp;#39;
&lt;span class="w"&gt; &lt;/span&gt;        Returns an object of  HipsTile
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-    base_url = hips_survey.data[&amp;#39;moc_access_url&amp;#39;].rsplit(&amp;#39;/&amp;#39;, 1)[0] + &amp;#39;/Norder&amp;#39; + str(hips_survey.hips_order) + &amp;#39;/Dir0/&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+    base_url = hips_survey.access_url + &amp;#39;/Norder&amp;#39; + str(hips_survey.hips_order) + &amp;#39;/Dir0/&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    for healpix_pixel_index in healpix_pixel_indices:
&lt;span class="w"&gt; &lt;/span&gt;        tile_meta = HipsTileMeta(order=order, ipix=healpix_pixel_index, file_format=&amp;#39;fits&amp;#39;)
&lt;span class="w"&gt; &lt;/span&gt;        tile = HipsTile.fetch(tile_meta, base_url + tile_meta.filename)
&lt;span class="gu"&gt;@@ -99,6 +88,8 @@ def _fetch_tiles(healpix_pixel_indices: np.ndarray, order: int, hips_survey: Hip&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties) -&amp;gt; np.ndarray:
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;Make sky image: fetch tiles and draw.

&lt;span class="gi"&gt;+    The example for this can be found on the :ref:`gs` page.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    Parameters
&lt;span class="w"&gt; &lt;/span&gt;    ----------
&lt;span class="w"&gt; &lt;/span&gt;    geometry : `~hips.utils.WCSGeometry`
&lt;span class="gu"&gt;@@ -110,23 +101,6 @@ def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties) -&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    -------
&lt;span class="w"&gt; &lt;/span&gt;    data : `~numpy.ndarray`
&lt;span class="w"&gt; &lt;/span&gt;        Output image pixels
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    Examples&lt;/span&gt;
&lt;span class="gd"&gt;-    --------&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;gt;&amp;gt;&amp;gt; from astropy.io import fits&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;gt;&amp;gt;&amp;gt; from hips.utils import WCSGeometry&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;gt;&amp;gt;&amp;gt; from hips.draw import make_sky_image&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;gt;&amp;gt;&amp;gt; from hips.tiles import HipsSurveyProperties&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;gt;&amp;gt;&amp;gt; geometry = WCSGeometry.create(&lt;/span&gt;
&lt;span class="gd"&gt;-    ...     skydir=SkyCoord(0, 0, unit=&amp;#39;deg&amp;#39;, frame=&amp;#39;galactic&amp;#39;),&lt;/span&gt;
&lt;span class="gd"&gt;-    ...     shape=(1000, 2000), coordsys=&amp;#39;GAL&amp;#39;,&lt;/span&gt;
&lt;span class="gd"&gt;-    ...     projection=&amp;#39;AIT&amp;#39;, cdelt=0.01, crpix=(1000, 500),&lt;/span&gt;
&lt;span class="gd"&gt;-    ... )&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;gt;&amp;gt;&amp;gt; url = &amp;#39;https://raw.githubusercontent.com/hipspy/hips-extra/master/datasets/samples/DSS2Red/properties&amp;#39;&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;gt;&amp;gt;&amp;gt; hips_survey = HipsSurveyProperties.fetch(url)&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;gt;&amp;gt;&amp;gt; data = make_sky_image(geometry, hips_survey)&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;gt;&amp;gt;&amp;gt; hdu = fits.PrimaryHDU(data=data)&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;gt;&amp;gt;&amp;gt; hdu.writeto(&amp;#39;my_image.fits&amp;#39;)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;    healpix_pixel_indices = compute_healpix_pixel_indices(geometry, hips_survey.hips_order)
&lt;span class="w"&gt; &lt;/span&gt;    # TODO: this isn&amp;#39;t a good API. Will become better when we have a cache.
&lt;span class="gh"&gt;diff --git a/hips/draw/tests/test_simple.py b/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index 827b283..de1b950 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,6 +1,6 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="gd"&gt;-import pytest&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;import numpy as np
&lt;span class="gi"&gt;+import pytest&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from astropy.utils.data import get_pkg_data_filename
&lt;span class="w"&gt; &lt;/span&gt;from numpy.testing import assert_allclose

&lt;span class="gu"&gt;@@ -63,7 +63,9 @@ class TestSimpleTilePainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        )
&lt;span class="w"&gt; &lt;/span&gt;        cls.simple_tile_painter = SimpleTilePainter(geometry, tile)

&lt;span class="gd"&gt;-    def test_compute_corners(self):&lt;/span&gt;
&lt;span class="gd"&gt;-        self.simple_tile_painter.compute_corners()&lt;/span&gt;
&lt;span class="gd"&gt;-        assert_allclose(self.simple_tile_painter.corners[0], [728.54880122, 1453.32078085, 1662.33429379, 931.69072155])&lt;/span&gt;
&lt;span class="gd"&gt;-        assert_allclose(self.simple_tile_painter.corners[1], [880.33972146, 997.1604671, 288.00518917, 175.4190427])&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_warp_image(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        self.simple_tile_painter.warp_image()&lt;/span&gt;
&lt;span class="gi"&gt;+        assert_allclose(self.simple_tile_painter.tile.meta.skycoord_corners.ra.deg,&lt;/span&gt;
&lt;span class="gi"&gt;+                        [264.375, 258.75, 264.375, 270.])&lt;/span&gt;
&lt;span class="gi"&gt;+        assert_allclose(self.simple_tile_painter.tile.meta.skycoord_corners.dec.deg,&lt;/span&gt;
&lt;span class="gi"&gt;+                        [-24.624318, -30., -35.685335, -30.])&lt;/span&gt;

commit 1f67310f98e5b873d6309928f428c66245ce3ba9
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Thu Jun 29 23:41:21 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Add function apply_projection in HipsTileMeta

&lt;span class="gh"&gt;diff --git a/hips/tiles/tile_meta.py b/hips/tiles/tile_meta.py&lt;/span&gt;
&lt;span class="gh"&gt;index 6f2862d..67759fb 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tile_meta.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tile_meta.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -4,6 +4,8 @@ from pathlib import Path&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;import healpy as hp
&lt;span class="w"&gt; &lt;/span&gt;import numpy as np
&lt;span class="w"&gt; &lt;/span&gt;from astropy.coordinates import SkyCoord
&lt;span class="gi"&gt;+from astropy.wcs import WCS&lt;/span&gt;
&lt;span class="gi"&gt;+from skimage import transform as tf&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;from ..utils import boundaries

&lt;span class="gu"&gt;@@ -53,12 +55,12 @@ class HipsTileMeta:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    @property
&lt;span class="w"&gt; &lt;/span&gt;    def path(self) -&amp;gt; Path:
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;Return the default path for tile storage (`~pathlib.Path`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Default path for tile storage (`~pathlib.Path`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        return Path(&amp;#39;hips&amp;#39;, &amp;#39;tiles&amp;#39;, &amp;#39;tests&amp;#39;, &amp;#39;data&amp;#39;)

&lt;span class="w"&gt; &lt;/span&gt;    @property
&lt;span class="w"&gt; &lt;/span&gt;    def filename(self) -&amp;gt; str:
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;Return the filename of HiPS tile (`str`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Filename for HiPS tile (`str`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        return &amp;#39;&amp;#39;.join([&amp;#39;Npix&amp;#39;, str(self.ipix), &amp;#39;.&amp;#39;, self.file_format])

&lt;span class="w"&gt; &lt;/span&gt;    @property
&lt;span class="gu"&gt;@@ -67,13 +69,13 @@ class HipsTileMeta:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        return self.path / self.filename

&lt;span class="w"&gt; &lt;/span&gt;    @property
&lt;span class="gd"&gt;-    def nside(self):&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;Return the nside of the HEALPix map&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+    def nside(self) -&amp;gt; int:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;nside of the HEALPix map&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        return hp.order2nside(self.order)

&lt;span class="w"&gt; &lt;/span&gt;    @property
&lt;span class="gd"&gt;-    def dst(self):&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;Return destination array for projective transform&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+    def dst(self) -&amp;gt; np.ndarray:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Destination array for projective transform&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        return np.array(
&lt;span class="w"&gt; &lt;/span&gt;            [[self.tile_width - 1, 0],
&lt;span class="w"&gt; &lt;/span&gt;             [self.tile_width - 1, self.tile_width - 1],
&lt;span class="gu"&gt;@@ -81,10 +83,19 @@ class HipsTileMeta:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;             [0, 0]])

&lt;span class="w"&gt; &lt;/span&gt;    @property
&lt;span class="gd"&gt;-    def skycoord_corners(self):&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;Return corner values for a HiPS tile&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+    def skycoord_corners(self) -&amp;gt; SkyCoord:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Corner values for a HiPS tile&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        theta, phi = boundaries(self.nside, self.ipix)
&lt;span class="w"&gt; &lt;/span&gt;        if self.frame == &amp;#39;galactic&amp;#39;:
&lt;span class="w"&gt; &lt;/span&gt;            return SkyCoord(l=phi, b=np.pi / 2 - theta, unit=&amp;#39;radian&amp;#39;, frame=self.frame)
&lt;span class="w"&gt; &lt;/span&gt;        else:
&lt;span class="w"&gt; &lt;/span&gt;            return SkyCoord(ra=phi, dec=np.pi / 2 - theta, unit=&amp;#39;radian&amp;#39;, frame=self.frame)
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def apply_projection(self, wcs: WCS) -&amp;gt; tf.ProjectiveTransform:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Apply projective transformation on a HiPS tile&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        corners = self.skycoord_corners.to_pixel(wcs)&lt;/span&gt;
&lt;span class="gi"&gt;+        src = np.array(corners).T.reshape((4, 2))&lt;/span&gt;
&lt;span class="gi"&gt;+        dst = self.dst&lt;/span&gt;
&lt;span class="gi"&gt;+        pt = tf.ProjectiveTransform()&lt;/span&gt;
&lt;span class="gi"&gt;+        pt.estimate(src, dst)&lt;/span&gt;
&lt;span class="gi"&gt;+        return pt&lt;/span&gt;

commit 05b8ac8e97467576883da6c2dbd2be786ead4e73
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Thu Jun 29 23:40:31 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Add test case for access_url property

&lt;span class="gh"&gt;diff --git a/hips/tiles/tests/test_description.py b/hips/tiles/tests/test_description.py&lt;/span&gt;
&lt;span class="gh"&gt;index b36aa02..d0e87dc 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tests/test_description.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tests/test_description.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -26,3 +26,6 @@ class TestHipsSurveyProperties:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def test_tile_format(self):
&lt;span class="w"&gt; &lt;/span&gt;        assert self.hips_survey_property.tile_format == &amp;#39;jpeg&amp;#39;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_access_url(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        assert self.hips_survey_property.access_url == &amp;#39;http://alasky.u-strasbg.fr/DSS/DSSColor&amp;#39;&lt;/span&gt;

commit f7625a909254992cb4bbd0fc4ba7237bead65e8a
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Thu Jun 29 23:39:59 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Add property access_url in HipsSurveyProperties

&lt;span class="gh"&gt;diff --git a/hips/tiles/description.py b/hips/tiles/description.py&lt;/span&gt;
&lt;span class="gh"&gt;index 21d2eb3..15d5a9a 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/description.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/description.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -108,3 +108,8 @@ class HipsSurveyProperties:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    def tile_format(self) -&amp;gt; str:
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;HiPS tile format (`str`).&amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;        return self.data[&amp;#39;hips_tile_format&amp;#39;]
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @property&lt;/span&gt;
&lt;span class="gi"&gt;+    def access_url(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;HiPS access url&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        return self.data[&amp;#39;moc_access_url&amp;#39;].rsplit(&amp;#39;/&amp;#39;, 1)[0]&lt;/span&gt;

commit a32ba1b946c22c81d41bc8291206d90134d123d2
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Thu Jun 29 23:39:14 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Add example in Getting Started page

&lt;span class="gh"&gt;diff --git a/docs/getting_started.rst b/docs/getting_started.rst&lt;/span&gt;
&lt;span class="gh"&gt;index e586127..ad0bdaa 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/docs/getting_started.rst&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/docs/getting_started.rst&lt;/span&gt;
&lt;span class="gu"&gt;@@ -6,4 +6,22 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;Getting started
&lt;span class="w"&gt; &lt;/span&gt;***************

&lt;span class="gd"&gt;-Coming soon ...&lt;/span&gt;
&lt;span class="gi"&gt;+The example below shows a high level use case of the ``hips`` package.&lt;/span&gt;
&lt;span class="gi"&gt;+It fetches a HiPS tile from a remote URL and draws it on a sky image.&lt;/span&gt;
&lt;span class="gi"&gt;+Then it saves it on local disk in FITS file format.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&amp;gt;&amp;gt;&amp;gt; from astropy.io import fits&lt;/span&gt;
&lt;span class="gi"&gt;+&amp;gt;&amp;gt;&amp;gt; from astropy.tests.helper import remote_data&lt;/span&gt;
&lt;span class="gi"&gt;+&amp;gt;&amp;gt;&amp;gt; from hips.utils import WCSGeometry&lt;/span&gt;
&lt;span class="gi"&gt;+&amp;gt;&amp;gt;&amp;gt; from hips.draw import make_sky_image&lt;/span&gt;
&lt;span class="gi"&gt;+&amp;gt;&amp;gt;&amp;gt; from hips.tiles import HipsSurveyProperties&lt;/span&gt;
&lt;span class="gi"&gt;+&amp;gt;&amp;gt;&amp;gt; geometry = WCSGeometry.create(&lt;/span&gt;
&lt;span class="gi"&gt;+...     skydir=SkyCoord(0, 0, unit=&amp;#39;deg&amp;#39;, frame=&amp;#39;galactic&amp;#39;),&lt;/span&gt;
&lt;span class="gi"&gt;+...     shape=(1000, 2000), coordsys=&amp;#39;GAL&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+...     projection=&amp;#39;AIT&amp;#39;, cdelt=0.01, crpix=(1000, 500),&lt;/span&gt;
&lt;span class="gi"&gt;+... )&lt;/span&gt;
&lt;span class="gi"&gt;+&amp;gt;&amp;gt;&amp;gt; url = &amp;#39;https://raw.githubusercontent.com/hipspy/hips-extra/master/datasets/samples/DSS2Red/properties&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+&amp;gt;&amp;gt;&amp;gt; hips_survey = HipsSurveyProperties.fetch(url)  # doctest: +REMOTE_DATA&lt;/span&gt;
&lt;span class="gi"&gt;+&amp;gt;&amp;gt;&amp;gt; data = make_sky_image(geometry, hips_survey)&lt;/span&gt;
&lt;span class="gi"&gt;+&amp;gt;&amp;gt;&amp;gt; hdu = fits.PrimaryHDU(data=data)&lt;/span&gt;
&lt;span class="gi"&gt;+&amp;gt;&amp;gt;&amp;gt; hdu.writeto(&amp;#39;my_image.fits&amp;#39;)&lt;/span&gt;
\ No newline at end of file

commit 42ceb7af3d271bf60cd82f6b49136d1fe4bb3920
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Thu Jun 29 20:35:59 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Get HiPS order from HipsSurveyProperties class

&lt;span class="gh"&gt;diff --git a/hips/draw/simple.py b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index 006867d..c6652d6 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -89,7 +89,7 @@ def _fetch_tiles(healpix_pixel_indices: np.ndarray, order: int, hips_survey: Hip&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;#39;HipsTile&amp;#39;
&lt;span class="w"&gt; &lt;/span&gt;        Returns an object of  HipsTile
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-    base_url = hips_survey.data[&amp;#39;moc_access_url&amp;#39;].rsplit(&amp;#39;/&amp;#39;, 1)[0] + &amp;#39;/Norder3/Dir0/&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+    base_url = hips_survey.data[&amp;#39;moc_access_url&amp;#39;].rsplit(&amp;#39;/&amp;#39;, 1)[0] + &amp;#39;/Norder&amp;#39; + str(hips_survey.hips_order) + &amp;#39;/Dir0/&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    for healpix_pixel_index in healpix_pixel_indices:
&lt;span class="w"&gt; &lt;/span&gt;        tile_meta = HipsTileMeta(order=order, ipix=healpix_pixel_index, file_format=&amp;#39;fits&amp;#39;)
&lt;span class="w"&gt; &lt;/span&gt;        tile = HipsTile.fetch(tile_meta, base_url + tile_meta.filename)

commit e487e44c68b8b4a8da44fc9fb82d932767abc471
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Thu Jun 29 20:33:32 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Update drawing code

&lt;span class="gh"&gt;diff --git a/hips/draw/simple.py b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index c106c8c..006867d 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,15 +1,13 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="w"&gt; &lt;/span&gt;&amp;quot;&amp;quot;&amp;quot;HiPS tile drawing -- simple method.&amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-import os&lt;/span&gt;
&lt;span class="gd"&gt;-from pathlib import Path&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;import numpy as np
&lt;span class="w"&gt; &lt;/span&gt;from astropy.coordinates import SkyCoord
&lt;span class="w"&gt; &lt;/span&gt;from skimage import transform as tf
&lt;span class="gd"&gt;-from typing import List&lt;/span&gt;
&lt;span class="gi"&gt;+from typing import List, Generator, Any&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;from ..tiles import HipsSurveyProperties, HipsTile, HipsTileMeta
&lt;span class="gd"&gt;-from ..utils import WCSGeometry, compute_healpix_pixel_indices, boundaries&lt;/span&gt;
&lt;span class="gi"&gt;+from ..utils import WCSGeometry, compute_healpix_pixel_indices&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;__all__ = [
&lt;span class="w"&gt; &lt;/span&gt;    &amp;#39;draw_sky_image&amp;#39;,
&lt;span class="gu"&gt;@@ -18,7 +16,7 @@ __all__ = [&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;]


&lt;span class="gd"&gt;-def draw_sky_image(geometry: WCSGeometry, tiles: List[HipsTile]) -&amp;gt; np.ndarray:&lt;/span&gt;
&lt;span class="gi"&gt;+def draw_sky_image(geometry: WCSGeometry, tiles: Generator[HipsTile, Any, Any]) -&amp;gt; np.ndarray:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;Draw sky image using the simple and quick method.

&lt;span class="w"&gt; &lt;/span&gt;    Parameters
&lt;span class="gu"&gt;@@ -35,7 +33,7 @@ def draw_sky_image(geometry: WCSGeometry, tiles: List[HipsTile]) -&amp;gt; np.ndarray:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;    all_sky = np.zeros(geometry.shape)
&lt;span class="w"&gt; &lt;/span&gt;    for tile in tiles:
&lt;span class="gd"&gt;-        draw_tile = SimpleTilePainter(geometry, tile, all_sky.shape)&lt;/span&gt;
&lt;span class="gi"&gt;+        draw_tile = SimpleTilePainter(geometry, tile)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        draw_tile.compute_corners()
&lt;span class="w"&gt; &lt;/span&gt;        draw_tile.compute_projection()
&lt;span class="w"&gt; &lt;/span&gt;        all_sky += draw_tile.warp_image()
&lt;span class="gu"&gt;@@ -43,7 +41,9 @@ def draw_sky_image(geometry: WCSGeometry, tiles: List[HipsTile]) -&amp;gt; np.ndarray:&lt;/span&gt;


&lt;span class="w"&gt; &lt;/span&gt;class SimpleTilePainter:
&lt;span class="gd"&gt;-    &amp;quot;&amp;quot;&amp;quot;A class which iteratively draws a tile following the naive algorithm steps mentioned `here &amp;lt;https://hips.readthedocs.io/en/latest/drawing_algo.html#naive-algorithm&amp;gt;`_.&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;Paint a single tile using a simple projective transformation method.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    The algorithm implemented is described here: :ref:`drawing_algo`.&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    Parameters
&lt;span class="w"&gt; &lt;/span&gt;    ----------
&lt;span class="gu"&gt;@@ -51,21 +51,16 @@ class SimpleTilePainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        An object of WCSGeometry
&lt;span class="w"&gt; &lt;/span&gt;    tile : `HipsTile`
&lt;span class="w"&gt; &lt;/span&gt;       An object of HipsTile
&lt;span class="gd"&gt;-    shape : tuple&lt;/span&gt;
&lt;span class="gd"&gt;-        Shape of the all-sky image&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;

&lt;span class="gd"&gt;-    def __init__(self, geometry: WCSGeometry, tile: HipsTile, shape: tuple) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+    def __init__(self, geometry: WCSGeometry, tile: HipsTile) -&amp;gt; None:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        self.geometry = geometry
&lt;span class="w"&gt; &lt;/span&gt;        self.tile = tile
&lt;span class="gd"&gt;-        self.shape = shape&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        self.corners = None
&lt;span class="w"&gt; &lt;/span&gt;        self.pt = None

&lt;span class="gd"&gt;-    def compute_corners(self) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gd"&gt;-        theta, phi = boundaries(self.tile.meta.nside, self.tile.meta.ipix)&lt;/span&gt;
&lt;span class="gd"&gt;-        radec = SkyCoord(ra=phi, dec=np.pi / 2 - theta, unit=&amp;#39;radian&amp;#39;, frame=&amp;#39;icrs&amp;#39;)&lt;/span&gt;
&lt;span class="gd"&gt;-        self.corners = radec.to_pixel(self.geometry.wcs)&lt;/span&gt;
&lt;span class="gi"&gt;+    def compute_corners(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        self.corners = self.tile.meta.skycoord_corners.to_pixel(self.geometry.wcs)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def compute_projection(self) -&amp;gt; None:
&lt;span class="w"&gt; &lt;/span&gt;        src = np.array(self.corners).T.reshape((4, 2))
&lt;span class="gu"&gt;@@ -74,7 +69,7 @@ class SimpleTilePainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        self.pt.estimate(src, dst)

&lt;span class="w"&gt; &lt;/span&gt;    def warp_image(self) -&amp;gt; np.ndarray:
&lt;span class="gd"&gt;-        return tf.warp(self.tile.data, self.pt, output_shape=self.shape)&lt;/span&gt;
&lt;span class="gi"&gt;+        return tf.warp(self.tile.data.astype(&amp;#39;float&amp;#39;), self.pt, output_shape=self.geometry.shape)&lt;/span&gt;


&lt;span class="w"&gt; &lt;/span&gt;def _fetch_tiles(healpix_pixel_indices: np.ndarray, order: int, hips_survey: HipsSurveyProperties) -&amp;gt; &amp;#39;HipsTile&amp;#39;:
&lt;span class="gu"&gt;@@ -94,9 +89,10 @@ def _fetch_tiles(healpix_pixel_indices: np.ndarray, order: int, hips_survey: Hip&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;#39;HipsTile&amp;#39;
&lt;span class="w"&gt; &lt;/span&gt;        Returns an object of  HipsTile
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;
&lt;span class="gi"&gt;+    base_url = hips_survey.data[&amp;#39;moc_access_url&amp;#39;].rsplit(&amp;#39;/&amp;#39;, 1)[0] + &amp;#39;/Norder3/Dir0/&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    for healpix_pixel_index in healpix_pixel_indices:
&lt;span class="w"&gt; &lt;/span&gt;        tile_meta = HipsTileMeta(order=order, ipix=healpix_pixel_index, file_format=&amp;#39;fits&amp;#39;)
&lt;span class="gd"&gt;-        tile = HipsTile.fetch(tile_meta, hips_survey.base_url)&lt;/span&gt;
&lt;span class="gi"&gt;+        tile = HipsTile.fetch(tile_meta, base_url + tile_meta.filename)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        yield tile


&lt;span class="gu"&gt;@@ -129,23 +125,12 @@ def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties) -&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;gt;&amp;gt;&amp;gt; url = &amp;#39;https://raw.githubusercontent.com/hipspy/hips-extra/master/datasets/samples/DSS2Red/properties&amp;#39;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;gt;&amp;gt;&amp;gt; hips_survey = HipsSurveyProperties.fetch(url)
&lt;span class="w"&gt; &lt;/span&gt;    &amp;gt;&amp;gt;&amp;gt; data = make_sky_image(geometry, hips_survey)
&lt;span class="gd"&gt;-    &amp;gt;&amp;gt;&amp;gt; hdu = fits.PrimaryHDU(data=data, header=geometry.header)&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;gt;&amp;gt;&amp;gt; hdu = fits.PrimaryHDU(data=data)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;gt;&amp;gt;&amp;gt; hdu.writeto(&amp;#39;my_image.fits&amp;#39;)
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;    healpix_pixel_indices = compute_healpix_pixel_indices(geometry, hips_survey.hips_order)
&lt;span class="gd"&gt;-    &amp;quot;&amp;quot;&amp;quot;TODO: Take user input for HiPS survey&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-    path = Path(os.environ[&amp;#39;HIPS_EXTRA&amp;#39;])&lt;/span&gt;
&lt;span class="gd"&gt;-    tiles_path = path / &amp;#39;datasets&amp;#39; / &amp;#39;samples&amp;#39; / &amp;#39;DSS2Red&amp;#39; / &amp;#39;Norder3&amp;#39; / &amp;#39;Dir0&amp;#39;&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    tiles = []&lt;/span&gt;
&lt;span class="gd"&gt;-    for pixel_index in healpix_pixel_indices:&lt;/span&gt;
&lt;span class="gd"&gt;-        meta = HipsTileMeta(hips_survey.hips_order, pixel_index, &amp;#39;fits&amp;#39;)&lt;/span&gt;
&lt;span class="gd"&gt;-        filepath = str(tiles_path / meta.filename)&lt;/span&gt;
&lt;span class="gd"&gt;-        tiles.append(HipsTile.read(meta, filepath))&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    # Fetch the tiles&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    # TODO: this isn&amp;#39;t a good API. Will become better when we have a cache.
&lt;span class="gd"&gt;-    # tiles = _fetch_tiles(healpix_pixel_indices, order, hips_survey)&lt;/span&gt;
&lt;span class="gi"&gt;+    tiles = _fetch_tiles(healpix_pixel_indices, hips_survey.hips_order, hips_survey)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    image_data = draw_sky_image(geometry, tiles)

&lt;span class="gh"&gt;diff --git a/hips/draw/tests/test_simple.py b/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index 81f3ce4..827b283 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,23 +1,26 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="gd"&gt;-import numpy as np&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;import pytest
&lt;span class="gd"&gt;-from astropy.tests.helper import remote_data&lt;/span&gt;
&lt;span class="gi"&gt;+import numpy as np&lt;/span&gt;
&lt;span class="gi"&gt;+from astropy.utils.data import get_pkg_data_filename&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from numpy.testing import assert_allclose

&lt;span class="gd"&gt;-from ..simple import make_sky_image, draw_sky_image&lt;/span&gt;
&lt;span class="gi"&gt;+from ..simple import make_sky_image, draw_sky_image, SimpleTilePainter&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from ...tiles import HipsSurveyProperties, HipsTileMeta, HipsTile
&lt;span class="w"&gt; &lt;/span&gt;from ...utils.testing import get_hips_extra_file, make_test_wcs_geometry, requires_hips_extra


&lt;span class="w"&gt; &lt;/span&gt;def get_test_tiles():
&lt;span class="gd"&gt;-    # TODO: check if this tile is inside our image&lt;/span&gt;
&lt;span class="gi"&gt;+    frames = dict({&amp;#39;equatorial&amp;#39;: &amp;#39;icrs&amp;#39;, &amp;#39;galactic&amp;#39;: &amp;#39;galactic&amp;#39;, &amp;#39;ecliptic&amp;#39;: &amp;#39;ecliptic&amp;#39;})&lt;/span&gt;
&lt;span class="gi"&gt;+    filename = get_pkg_data_filename(&amp;#39;../../tiles/tests/data/properties.txt&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+    hips_survey = HipsSurveyProperties.read(filename)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    tile1 = HipsTile.read(
&lt;span class="gd"&gt;-        meta=HipsTileMeta(order=3, ipix=450, file_format=&amp;#39;fits&amp;#39;, tile_width=512),&lt;/span&gt;
&lt;span class="gi"&gt;+        meta=HipsTileMeta(order=3, ipix=450, file_format=&amp;#39;fits&amp;#39;, frame=frames[hips_survey.hips_frame], tile_width=512),&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        filename=get_hips_extra_file(&amp;#39;datasets/samples/DSS2Red/Norder3/Dir0/Npix450.fits&amp;#39;),
&lt;span class="w"&gt; &lt;/span&gt;    )

&lt;span class="w"&gt; &lt;/span&gt;    tile2 = HipsTile.read(
&lt;span class="gd"&gt;-        meta=HipsTileMeta(order=3, ipix=451, file_format=&amp;#39;fits&amp;#39;, tile_width=512),&lt;/span&gt;
&lt;span class="gi"&gt;+        meta=HipsTileMeta(order=3, ipix=451, file_format=&amp;#39;fits&amp;#39;, frame=frames[hips_survey.hips_frame], tile_width=512),&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        filename=get_hips_extra_file(&amp;#39;datasets/samples/DSS2Red/Norder3/Dir0/Npix451.fits&amp;#39;),
&lt;span class="w"&gt; &lt;/span&gt;    )

&lt;span class="gu"&gt;@@ -34,19 +37,33 @@ def test_draw_sky_image():&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    assert data.shape == geometry.shape
&lt;span class="w"&gt; &lt;/span&gt;    assert data.dtype == np.float64
&lt;span class="w"&gt; &lt;/span&gt;    assert np.sum(data) == 4575235421.5126467
&lt;span class="gd"&gt;-    assert_allclose(data[400, 500:504], [2866.010141, 2563.691673, 2580.759013, 2746.608711])&lt;/span&gt;
&lt;span class="gi"&gt;+    assert data[400, 500] == 2866.0101409848185&lt;/span&gt;
&lt;span class="gi"&gt;+    assert data[400, 501] == 2563.6916727348043&lt;/span&gt;


&lt;span class="w"&gt; &lt;/span&gt;@pytest.mark.xfail
&lt;span class="gd"&gt;-@remote_data&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;def test_make_sky_image():
&lt;span class="w"&gt; &lt;/span&gt;    url = &amp;#39;https://raw.githubusercontent.com/hipspy/hips-extra/master/datasets/samples/DSS2Red/properties&amp;#39;
&lt;span class="w"&gt; &lt;/span&gt;    hips_survey = HipsSurveyProperties.fetch(url)
&lt;span class="w"&gt; &lt;/span&gt;    geometry = make_test_wcs_geometry(case=2)
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    data = make_sky_image(geometry, hips_survey)
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    assert data.shape == geometry.shape
&lt;span class="w"&gt; &lt;/span&gt;    assert data.dtype == np.float64
&lt;span class="gd"&gt;-    assert_allclose(data[200, 994:1000], [3717.10091363, 3402.55292158, 3181.16613051, 2868.45175662, 2832.23001706,&lt;/span&gt;
&lt;span class="gd"&gt;-                                          2779.23366271])&lt;/span&gt;
&lt;span class="gi"&gt;+    assert data[200, 994] == 3717.10091363&lt;/span&gt;
&lt;span class="gi"&gt;+    assert data[200, 995] == 3402.55292158&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+class TestSimpleTilePainter:&lt;/span&gt;
&lt;span class="gi"&gt;+    @classmethod&lt;/span&gt;
&lt;span class="gi"&gt;+    def setup_class(cls):&lt;/span&gt;
&lt;span class="gi"&gt;+        geometry = make_test_wcs_geometry(case=2)&lt;/span&gt;
&lt;span class="gi"&gt;+        tile = HipsTile.read(&lt;/span&gt;
&lt;span class="gi"&gt;+            meta=HipsTileMeta(order=3, ipix=450, file_format=&amp;#39;fits&amp;#39;, frame=&amp;#39;icrs&amp;#39;, tile_width=512),&lt;/span&gt;
&lt;span class="gi"&gt;+            filename=get_hips_extra_file(&amp;#39;datasets/samples/DSS2Red/Norder3/Dir0/Npix450.fits&amp;#39;),&lt;/span&gt;
&lt;span class="gi"&gt;+        )&lt;/span&gt;
&lt;span class="gi"&gt;+        cls.simple_tile_painter = SimpleTilePainter(geometry, tile)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_compute_corners(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        self.simple_tile_painter.compute_corners()&lt;/span&gt;
&lt;span class="gi"&gt;+        assert_allclose(self.simple_tile_painter.corners[0], [728.54880122, 1453.32078085, 1662.33429379, 931.69072155])&lt;/span&gt;
&lt;span class="gi"&gt;+        assert_allclose(self.simple_tile_painter.corners[1], [880.33972146, 997.1604671, 288.00518917, 175.4190427])&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/tiles/tile.py b/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gh"&gt;index 70423b2..234fb5d 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -95,7 +95,8 @@ class HipsTile:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        if meta.file_format == &amp;#39;fits&amp;#39;:
&lt;span class="w"&gt; &lt;/span&gt;            hdu_list = fits.open(str(path))
&lt;span class="gd"&gt;-            data = hdu_list[0].data.astype(&amp;#39;float&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+            # This converts a big-endian byte integer to a float&lt;/span&gt;
&lt;span class="gi"&gt;+            data = hdu_list[0].data&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            header = hdu_list[0].header
&lt;span class="w"&gt; &lt;/span&gt;            return cls(meta, data, header)
&lt;span class="w"&gt; &lt;/span&gt;        else:

commit c3d3d2729a4467667793af560df9b6c371625360
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Thu Jun 29 20:33:21 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Fix link to WCSGeometry class in docs

&lt;span class="gh"&gt;diff --git a/hips/utils/healpix.py b/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gh"&gt;index f98a2bf..829248f 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -77,7 +77,7 @@ def compute_healpix_pixel_indices(wcs_geometry: WCSGeometry, order: int) -&amp;gt; np.n&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    Parameters
&lt;span class="w"&gt; &lt;/span&gt;    ----------
&lt;span class="gd"&gt;-    wcs_geometry : WCSGeometry&lt;/span&gt;
&lt;span class="gi"&gt;+    wcs_geometry : `WCSGeometry`&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        Container for WCS object and image shape
&lt;span class="w"&gt; &lt;/span&gt;    order : int
&lt;span class="w"&gt; &lt;/span&gt;        The order of the HEALPix

commit 1e4bb3b268f7b5893c5279a472d88b26fb4ecafc
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Thu Jun 29 20:30:48 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Add property skycoord_corner to HipsTileMeta

&lt;span class="gh"&gt;diff --git a/hips/tiles/tile_meta.py b/hips/tiles/tile_meta.py&lt;/span&gt;
&lt;span class="gh"&gt;index 53a2a21..6f2862d 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tile_meta.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tile_meta.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -3,6 +3,9 @@ from pathlib import Path&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;import healpy as hp
&lt;span class="w"&gt; &lt;/span&gt;import numpy as np
&lt;span class="gi"&gt;+from astropy.coordinates import SkyCoord&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+from ..utils import boundaries&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;__all__ = [
&lt;span class="w"&gt; &lt;/span&gt;    &amp;#39;HipsTileMeta&amp;#39;,
&lt;span class="gu"&gt;@@ -22,12 +25,22 @@ class HipsTileMeta:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        File format
&lt;span class="w"&gt; &lt;/span&gt;    tile_width : `int`
&lt;span class="w"&gt; &lt;/span&gt;        Tile width (in pixels)
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    Examples&lt;/span&gt;
&lt;span class="gi"&gt;+    --------&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;gt;&amp;gt;&amp;gt; from hips.tiles import HipsTileMeta&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;gt;&amp;gt;&amp;gt; tile_meta = HipsTileMeta(order=3, ipix=450, file_format=&amp;#39;fits&amp;#39;, frame=&amp;#39;galactic&amp;#39;, tile_width=512)&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;gt;&amp;gt;&amp;gt; tile_meta.skycoord_corners&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;lt;SkyCoord (Galactic): (l, b) in deg&lt;/span&gt;
&lt;span class="gi"&gt;+    [( 264.375, -24.62431835), ( 258.75 , -30.        ),&lt;/span&gt;
&lt;span class="gi"&gt;+     ( 264.375, -35.68533471), ( 270.   , -30.        )]&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;

&lt;span class="gd"&gt;-    def __init__(self, order: int, ipix: int, file_format: str, tile_width: int = 512) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+    def __init__(self, order: int, ipix: int, file_format: str, frame=&amp;#39;galactic&amp;#39;, tile_width: int = 512) -&amp;gt; None:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        self.order = order
&lt;span class="w"&gt; &lt;/span&gt;        self.ipix = ipix
&lt;span class="w"&gt; &lt;/span&gt;        self.file_format = file_format
&lt;span class="gi"&gt;+        self.frame = frame&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        self.tile_width = tile_width

&lt;span class="w"&gt; &lt;/span&gt;    def __eq__(self, other: &amp;#39;HipsTileMeta&amp;#39;) -&amp;gt; bool:
&lt;span class="gu"&gt;@@ -60,5 +73,18 @@ class HipsTileMeta:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    @property
&lt;span class="w"&gt; &lt;/span&gt;    def dst(self):
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Return destination array for projective transform&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        return np.array(
&lt;span class="gd"&gt;-            [[self.tile_width - 1, 0], [self.tile_width - 1, self.tile_width - 1], [0, self.tile_width - 1], [0, 0]])&lt;/span&gt;
&lt;span class="gi"&gt;+            [[self.tile_width - 1, 0],&lt;/span&gt;
&lt;span class="gi"&gt;+             [self.tile_width - 1, self.tile_width - 1],&lt;/span&gt;
&lt;span class="gi"&gt;+             [0, self.tile_width - 1],&lt;/span&gt;
&lt;span class="gi"&gt;+             [0, 0]])&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @property&lt;/span&gt;
&lt;span class="gi"&gt;+    def skycoord_corners(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Return corner values for a HiPS tile&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        theta, phi = boundaries(self.nside, self.ipix)&lt;/span&gt;
&lt;span class="gi"&gt;+        if self.frame == &amp;#39;galactic&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+            return SkyCoord(l=phi, b=np.pi / 2 - theta, unit=&amp;#39;radian&amp;#39;, frame=self.frame)&lt;/span&gt;
&lt;span class="gi"&gt;+        else:&lt;/span&gt;
&lt;span class="gi"&gt;+            return SkyCoord(ra=phi, dec=np.pi / 2 - theta, unit=&amp;#39;radian&amp;#39;, frame=self.frame)&lt;/span&gt;

commit 7897504b97bc594d55aa23182af76dfd29ab152d
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Thu Jun 29 10:46:33 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Fix drawing code

&lt;span class="gh"&gt;diff --git a/hips/draw/simple.py b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index 64c5eed..c106c8c 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -49,7 +49,7 @@ class SimpleTilePainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    ----------
&lt;span class="w"&gt; &lt;/span&gt;    geometry : `~hips.utils.WCSGeometry`
&lt;span class="w"&gt; &lt;/span&gt;        An object of WCSGeometry
&lt;span class="gd"&gt;-    tile : HipsTile&lt;/span&gt;
&lt;span class="gi"&gt;+    tile : `HipsTile`&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;       An object of HipsTile
&lt;span class="w"&gt; &lt;/span&gt;    shape : tuple
&lt;span class="w"&gt; &lt;/span&gt;        Shape of the all-sky image
&lt;span class="gu"&gt;@@ -65,12 +65,10 @@ class SimpleTilePainter:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    def compute_corners(self) -&amp;gt; None:
&lt;span class="w"&gt; &lt;/span&gt;        theta, phi = boundaries(self.tile.meta.nside, self.tile.meta.ipix)
&lt;span class="w"&gt; &lt;/span&gt;        radec = SkyCoord(ra=phi, dec=np.pi / 2 - theta, unit=&amp;#39;radian&amp;#39;, frame=&amp;#39;icrs&amp;#39;)
&lt;span class="gd"&gt;-        self.corners = []&lt;/span&gt;
&lt;span class="gd"&gt;-        for i in range(len(radec.ra.deg)):&lt;/span&gt;
&lt;span class="gd"&gt;-            self.corners.append([radec.ra.deg[i], radec.dec.deg[i]])&lt;/span&gt;
&lt;span class="gi"&gt;+        self.corners = radec.to_pixel(self.geometry.wcs)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def compute_projection(self) -&amp;gt; None:
&lt;span class="gd"&gt;-        src = self.geometry.wcs.wcs_world2pix(self.corners, 0)&lt;/span&gt;
&lt;span class="gi"&gt;+        src = np.array(self.corners).T.reshape((4, 2))&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        dst = self.tile.meta.dst
&lt;span class="w"&gt; &lt;/span&gt;        self.pt = tf.ProjectiveTransform()
&lt;span class="w"&gt; &lt;/span&gt;        self.pt.estimate(src, dst)
&lt;span class="gh"&gt;diff --git a/hips/draw/tests/test_simple.py b/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index 8c7aa97..81f3ce4 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -2,6 +2,7 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;import numpy as np
&lt;span class="w"&gt; &lt;/span&gt;import pytest
&lt;span class="w"&gt; &lt;/span&gt;from astropy.tests.helper import remote_data
&lt;span class="gi"&gt;+from numpy.testing import assert_allclose&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;from ..simple import make_sky_image, draw_sky_image
&lt;span class="w"&gt; &lt;/span&gt;from ...tiles import HipsSurveyProperties, HipsTileMeta, HipsTile
&lt;span class="gu"&gt;@@ -11,19 +12,18 @@ from ...utils.testing import get_hips_extra_file, make_test_wcs_geometry, requir&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;def get_test_tiles():
&lt;span class="w"&gt; &lt;/span&gt;    # TODO: check if this tile is inside our image
&lt;span class="w"&gt; &lt;/span&gt;    tile1 = HipsTile.read(
&lt;span class="gd"&gt;-        meta=HipsTileMeta(order=3, ipix=658, file_format=&amp;#39;fits&amp;#39;, tile_width=512),&lt;/span&gt;
&lt;span class="gd"&gt;-        filename=get_hips_extra_file(&amp;#39;datasets/samples/DSS2Red/Norder3/Dir0/Npix658.fits&amp;#39;),&lt;/span&gt;
&lt;span class="gi"&gt;+        meta=HipsTileMeta(order=3, ipix=450, file_format=&amp;#39;fits&amp;#39;, tile_width=512),&lt;/span&gt;
&lt;span class="gi"&gt;+        filename=get_hips_extra_file(&amp;#39;datasets/samples/DSS2Red/Norder3/Dir0/Npix450.fits&amp;#39;),&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    )

&lt;span class="w"&gt; &lt;/span&gt;    tile2 = HipsTile.read(
&lt;span class="gd"&gt;-        meta=HipsTileMeta(order=3, ipix=659, file_format=&amp;#39;fits&amp;#39;, tile_width=512),&lt;/span&gt;
&lt;span class="gd"&gt;-        filename=get_hips_extra_file(&amp;#39;datasets/samples/DSS2Red/Norder3/Dir0/Npix659.fits&amp;#39;),&lt;/span&gt;
&lt;span class="gi"&gt;+        meta=HipsTileMeta(order=3, ipix=451, file_format=&amp;#39;fits&amp;#39;, tile_width=512),&lt;/span&gt;
&lt;span class="gi"&gt;+        filename=get_hips_extra_file(&amp;#39;datasets/samples/DSS2Red/Norder3/Dir0/Npix451.fits&amp;#39;),&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    )

&lt;span class="w"&gt; &lt;/span&gt;    return [tile1, tile2]


&lt;span class="gd"&gt;-@pytest.mark.xfail&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;@requires_hips_extra()
&lt;span class="w"&gt; &lt;/span&gt;def test_draw_sky_image():
&lt;span class="w"&gt; &lt;/span&gt;    geometry = make_test_wcs_geometry(case=2)
&lt;span class="gu"&gt;@@ -33,10 +33,11 @@ def test_draw_sky_image():&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    assert data.shape == geometry.shape
&lt;span class="w"&gt; &lt;/span&gt;    assert data.dtype == np.float64
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    assert data[0, 0] == 0.0&lt;/span&gt;
&lt;span class="gi"&gt;+    assert np.sum(data) == 4575235421.5126467&lt;/span&gt;
&lt;span class="gi"&gt;+    assert_allclose(data[400, 500:504], [2866.010141, 2563.691673, 2580.759013, 2746.608711])&lt;/span&gt;


&lt;span class="gi"&gt;+@pytest.mark.xfail&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;@remote_data
&lt;span class="w"&gt; &lt;/span&gt;def test_make_sky_image():
&lt;span class="w"&gt; &lt;/span&gt;    url = &amp;#39;https://raw.githubusercontent.com/hipspy/hips-extra/master/datasets/samples/DSS2Red/properties&amp;#39;
&lt;span class="gu"&gt;@@ -47,4 +48,5 @@ def test_make_sky_image():&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    assert data.shape == geometry.shape
&lt;span class="w"&gt; &lt;/span&gt;    assert data.dtype == np.float64
&lt;span class="gd"&gt;-    assert data[800, 1000] == 1794.7673494847763&lt;/span&gt;
&lt;span class="gi"&gt;+    assert_allclose(data[200, 994:1000], [3717.10091363, 3402.55292158, 3181.16613051, 2868.45175662, 2832.23001706,&lt;/span&gt;
&lt;span class="gi"&gt;+                                          2779.23366271])&lt;/span&gt;

commit 53a0df576c6f1be0659cd4927609ae134cdb3643
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Wed Jun 28 17:51:42 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Reformat some code

&lt;span class="gh"&gt;diff --git a/hips/draw/simple.py b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index 342a107..64c5eed 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -3,10 +3,8 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;import os
&lt;span class="w"&gt; &lt;/span&gt;from pathlib import Path

&lt;span class="gd"&gt;-import healpy as hp&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;import numpy as np
&lt;span class="w"&gt; &lt;/span&gt;from astropy.coordinates import SkyCoord
&lt;span class="gd"&gt;-from astropy.wcs import WCS&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from skimage import transform as tf
&lt;span class="w"&gt; &lt;/span&gt;from typing import List

&lt;span class="gu"&gt;@@ -66,19 +64,14 @@ class SimpleTilePainter:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def compute_corners(self) -&amp;gt; None:
&lt;span class="w"&gt; &lt;/span&gt;        theta, phi = boundaries(self.tile.meta.nside, self.tile.meta.ipix)
&lt;span class="gd"&gt;-        self.corners = SkyCoord(ra=phi, dec=np.pi / 2 - theta, unit=&amp;#39;radian&amp;#39;, frame=&amp;#39;icrs&amp;#39;)&lt;/span&gt;
&lt;span class="gd"&gt;-        self.test_corners = []&lt;/span&gt;
&lt;span class="gd"&gt;-        for i in range(len(self.corners.ra.deg)):&lt;/span&gt;
&lt;span class="gd"&gt;-            self.test_corners.append([self.corners.ra.deg[i], self.corners.dec.deg[i]])&lt;/span&gt;
&lt;span class="gi"&gt;+        radec = SkyCoord(ra=phi, dec=np.pi / 2 - theta, unit=&amp;#39;radian&amp;#39;, frame=&amp;#39;icrs&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        self.corners = []&lt;/span&gt;
&lt;span class="gi"&gt;+        for i in range(len(radec.ra.deg)):&lt;/span&gt;
&lt;span class="gi"&gt;+            self.corners.append([radec.ra.deg[i], radec.dec.deg[i]])&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def compute_projection(self) -&amp;gt; None:
&lt;span class="gd"&gt;-        # print(self.corners.to_pixel(self.geometry.wcs))&lt;/span&gt;
&lt;span class="gd"&gt;-        # src = self.geometry.wcs.wcs_world2pix(self.corners.to_pixel(self.geometry.wcs), 0)&lt;/span&gt;
&lt;span class="gd"&gt;-        src = np.array(self.corners.to_pixel(self.geometry.wcs))&lt;/span&gt;
&lt;span class="gd"&gt;-        print(&amp;#39;Corners: &amp;#39;, self.test_corners)&lt;/span&gt;
&lt;span class="gd"&gt;-        print(&amp;#39;Source: &amp;#39;, src)&lt;/span&gt;
&lt;span class="gd"&gt;-        print(self.geometry.wcs.wcs_world2pix(self.corners.to_pixel(self.geometry.wcs), 0))&lt;/span&gt;
&lt;span class="gd"&gt;-        dst = np.array([[511, 0], [511, 511], [0, 511], [0, 0]])&lt;/span&gt;
&lt;span class="gi"&gt;+        src = self.geometry.wcs.wcs_world2pix(self.corners, 0)&lt;/span&gt;
&lt;span class="gi"&gt;+        dst = self.tile.meta.dst&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        self.pt = tf.ProjectiveTransform()
&lt;span class="w"&gt; &lt;/span&gt;        self.pt.estimate(src, dst)

&lt;span class="gu"&gt;@@ -142,6 +135,7 @@ def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties) -&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;gt;&amp;gt;&amp;gt; hdu.writeto(&amp;#39;my_image.fits&amp;#39;)
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;    healpix_pixel_indices = compute_healpix_pixel_indices(geometry, hips_survey.hips_order)
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;TODO: Take user input for HiPS survey&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    path = Path(os.environ[&amp;#39;HIPS_EXTRA&amp;#39;])
&lt;span class="w"&gt; &lt;/span&gt;    tiles_path = path / &amp;#39;datasets&amp;#39; / &amp;#39;samples&amp;#39; / &amp;#39;DSS2Red&amp;#39; / &amp;#39;Norder3&amp;#39; / &amp;#39;Dir0&amp;#39;

&lt;span class="gh"&gt;diff --git a/hips/utils/healpix.py b/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gh"&gt;index 4f89897..f98a2bf 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -4,10 +4,10 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;This module contains wrapper functions around HEALPix utilizing
&lt;span class="w"&gt; &lt;/span&gt;the healpy library.
&lt;span class="w"&gt; &lt;/span&gt;&amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-from typing import Tuple&lt;/span&gt;
&lt;span class="gi"&gt;+import healpy as hp&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;import numpy as np
&lt;span class="w"&gt; &lt;/span&gt;from astropy.coordinates import SkyCoord
&lt;span class="gd"&gt;-import healpy as hp&lt;/span&gt;
&lt;span class="gi"&gt;+from typing import Tuple&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;from .wcs import WCSGeometry


commit 243ef55f752c701c94f4b3736c20524f62b560a1
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Wed Jun 28 17:50:52 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Add dst property in HipsTileMeta class

&lt;span class="gh"&gt;diff --git a/hips/tiles/tile_meta.py b/hips/tiles/tile_meta.py&lt;/span&gt;
&lt;span class="gh"&gt;index fe0bacb..53a2a21 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tile_meta.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tile_meta.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,6 +1,8 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="w"&gt; &lt;/span&gt;from pathlib import Path
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;import healpy as hp
&lt;span class="gi"&gt;+import numpy as np&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;__all__ = [
&lt;span class="w"&gt; &lt;/span&gt;    &amp;#39;HipsTileMeta&amp;#39;,
&lt;span class="gu"&gt;@@ -54,4 +56,9 @@ class HipsTileMeta:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    @property
&lt;span class="w"&gt; &lt;/span&gt;    def nside(self):
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Return the nside of the HEALPix map&amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-        return hp.order2nside(self.order)&lt;/span&gt;
\ No newline at end of file
&lt;span class="gi"&gt;+        return hp.order2nside(self.order)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @property&lt;/span&gt;
&lt;span class="gi"&gt;+    def dst(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        return np.array(&lt;/span&gt;
&lt;span class="gi"&gt;+            [[self.tile_width - 1, 0], [self.tile_width - 1, self.tile_width - 1], [0, self.tile_width - 1], [0, 0]])&lt;/span&gt;

commit 3acf68e070ceeac9cce7e86c89f3c0350ed478aa
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Wed Jun 28 10:36:37 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Update corner computation

&lt;span class="gh"&gt;diff --git a/hips/draw/simple.py b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index 9ab55e9..342a107 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -16,6 +16,7 @@ from ..utils import WCSGeometry, compute_healpix_pixel_indices, boundaries&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;__all__ = [
&lt;span class="w"&gt; &lt;/span&gt;    &amp;#39;draw_sky_image&amp;#39;,
&lt;span class="w"&gt; &lt;/span&gt;    &amp;#39;make_sky_image&amp;#39;,
&lt;span class="gi"&gt;+    &amp;#39;SimpleTilePainter&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;]


&lt;span class="gu"&gt;@@ -24,7 +25,7 @@ def draw_sky_image(geometry: WCSGeometry, tiles: List[HipsTile]) -&amp;gt; np.ndarray:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    Parameters
&lt;span class="w"&gt; &lt;/span&gt;    ----------
&lt;span class="gd"&gt;-    geometry : &amp;#39;WCSGeometry&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+    geometry : `~hips.utils.WCSGeometry`&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        An object of WCSGeometry
&lt;span class="w"&gt; &lt;/span&gt;    tiles : List[HipsTile]
&lt;span class="w"&gt; &lt;/span&gt;        A list of HipsTile
&lt;span class="gu"&gt;@@ -36,23 +37,23 @@ def draw_sky_image(geometry: WCSGeometry, tiles: List[HipsTile]) -&amp;gt; np.ndarray:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;    all_sky = np.zeros(geometry.shape)
&lt;span class="w"&gt; &lt;/span&gt;    for tile in tiles:
&lt;span class="gd"&gt;-        draw_tile = DrawSkyImageOneTile(geometry, tile, all_sky.shape)&lt;/span&gt;
&lt;span class="gi"&gt;+        draw_tile = SimpleTilePainter(geometry, tile, all_sky.shape)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        draw_tile.compute_corners()
&lt;span class="gd"&gt;-        draw_tile.apply_projection()&lt;/span&gt;
&lt;span class="gi"&gt;+        draw_tile.compute_projection()&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        all_sky += draw_tile.warp_image()
&lt;span class="w"&gt; &lt;/span&gt;    return all_sky


&lt;span class="gd"&gt;-class DrawSkyImageOneTile:&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;quot;&amp;quot;&amp;quot;A private function for drawing a tile over a sky image.&lt;/span&gt;
&lt;span class="gi"&gt;+class SimpleTilePainter:&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;A class which iteratively draws a tile following the naive algorithm steps mentioned `here &amp;lt;https://hips.readthedocs.io/en/latest/drawing_algo.html#naive-algorithm&amp;gt;`_.&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    Parameters
&lt;span class="w"&gt; &lt;/span&gt;    ----------
&lt;span class="gd"&gt;-    geometry : &amp;#39;WCSGeometry&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+    geometry : `~hips.utils.WCSGeometry`&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        An object of WCSGeometry
&lt;span class="w"&gt; &lt;/span&gt;    tile : HipsTile
&lt;span class="w"&gt; &lt;/span&gt;       An object of HipsTile
&lt;span class="gd"&gt;-    shape ; tuple&lt;/span&gt;
&lt;span class="gi"&gt;+    shape : tuple&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        Shape of the all-sky image
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;

&lt;span class="gu"&gt;@@ -60,20 +61,23 @@ class DrawSkyImageOneTile:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        self.geometry = geometry
&lt;span class="w"&gt; &lt;/span&gt;        self.tile = tile
&lt;span class="w"&gt; &lt;/span&gt;        self.shape = shape
&lt;span class="gd"&gt;-        self.wcs = WCS(tile.header)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        self.corners = None
&lt;span class="w"&gt; &lt;/span&gt;        self.pt = None

&lt;span class="w"&gt; &lt;/span&gt;    def compute_corners(self) -&amp;gt; None:
&lt;span class="gd"&gt;-        nside = hp.order2nside(self.tile.meta.order)&lt;/span&gt;
&lt;span class="gd"&gt;-        theta, phi = boundaries(nside, self.tile.meta.ipix)&lt;/span&gt;
&lt;span class="gd"&gt;-        radec = SkyCoord(ra=phi, dec=np.pi / 2 - theta, unit=&amp;#39;radian&amp;#39;, frame=&amp;#39;icrs&amp;#39;)&lt;/span&gt;
&lt;span class="gd"&gt;-        self.corners = []&lt;/span&gt;
&lt;span class="gd"&gt;-        for i in range(len(radec.ra.deg)):&lt;/span&gt;
&lt;span class="gd"&gt;-            self.corners.append([radec.ra.deg[i], radec.dec.deg[i]])&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    def apply_projection(self) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gd"&gt;-        src = self.geometry.wcs.wcs_world2pix(self.corners, 0)&lt;/span&gt;
&lt;span class="gi"&gt;+        theta, phi = boundaries(self.tile.meta.nside, self.tile.meta.ipix)&lt;/span&gt;
&lt;span class="gi"&gt;+        self.corners = SkyCoord(ra=phi, dec=np.pi / 2 - theta, unit=&amp;#39;radian&amp;#39;, frame=&amp;#39;icrs&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        self.test_corners = []&lt;/span&gt;
&lt;span class="gi"&gt;+        for i in range(len(self.corners.ra.deg)):&lt;/span&gt;
&lt;span class="gi"&gt;+            self.test_corners.append([self.corners.ra.deg[i], self.corners.dec.deg[i]])&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def compute_projection(self) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+        # print(self.corners.to_pixel(self.geometry.wcs))&lt;/span&gt;
&lt;span class="gi"&gt;+        # src = self.geometry.wcs.wcs_world2pix(self.corners.to_pixel(self.geometry.wcs), 0)&lt;/span&gt;
&lt;span class="gi"&gt;+        src = np.array(self.corners.to_pixel(self.geometry.wcs))&lt;/span&gt;
&lt;span class="gi"&gt;+        print(&amp;#39;Corners: &amp;#39;, self.test_corners)&lt;/span&gt;
&lt;span class="gi"&gt;+        print(&amp;#39;Source: &amp;#39;, src)&lt;/span&gt;
&lt;span class="gi"&gt;+        print(self.geometry.wcs.wcs_world2pix(self.corners.to_pixel(self.geometry.wcs), 0))&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        dst = np.array([[511, 0], [511, 511], [0, 511], [0, 0]])
&lt;span class="w"&gt; &lt;/span&gt;        self.pt = tf.ProjectiveTransform()
&lt;span class="w"&gt; &lt;/span&gt;        self.pt.estimate(src, dst)
&lt;span class="gh"&gt;diff --git a/hips/tiles/tile_meta.py b/hips/tiles/tile_meta.py&lt;/span&gt;
&lt;span class="gh"&gt;index e1fca4c..fe0bacb 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tile_meta.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tile_meta.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,5 +1,6 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="w"&gt; &lt;/span&gt;from pathlib import Path
&lt;span class="gi"&gt;+import healpy as hp&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;__all__ = [
&lt;span class="w"&gt; &lt;/span&gt;    &amp;#39;HipsTileMeta&amp;#39;,
&lt;span class="gu"&gt;@@ -49,3 +50,8 @@ class HipsTileMeta:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    def full_path(self) -&amp;gt; Path:
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Full path (folder and filename) (`~pathlib.Path`)&amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;        return self.path / self.filename
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @property&lt;/span&gt;
&lt;span class="gi"&gt;+    def nside(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Return the nside of the HEALPix map&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        return hp.order2nside(self.order)&lt;/span&gt;
\ No newline at end of file

commit c838f6412cfeddea00d8449ee048d0429ed6a09d
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Tue Jun 27 23:18:42 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Update drawing code

&lt;span class="gh"&gt;diff --git a/hips/draw/simple.py b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index f82df43..9ab55e9 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -34,48 +34,52 @@ def draw_sky_image(geometry: WCSGeometry, tiles: List[HipsTile]) -&amp;gt; np.ndarray:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    np.ndarray
&lt;span class="w"&gt; &lt;/span&gt;        Returns a numpy array containing all HiPS tiles projected onto it
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-    # TODO: copy over (and clean up a little) the drawing code you already have in notebooks.&lt;/span&gt;
&lt;span class="gd"&gt;-    # Suggestion: don&amp;#39;t debug the code now, just put it here.&lt;/span&gt;
&lt;span class="gd"&gt;-    # First implement simple tile caching so that you can draw without needing to make web queries.&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    # Probably this function should just loop over tiles and call a `_draw_sky_image_one_tile`&lt;/span&gt;
&lt;span class="gd"&gt;-    # helper function, and sum up the results.&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    all_sky = np.zeros(geometry.shape)
&lt;span class="w"&gt; &lt;/span&gt;    for tile in tiles:
&lt;span class="gd"&gt;-        nside = hp.order2nside(tile.meta.order)&lt;/span&gt;
&lt;span class="gd"&gt;-        theta, phi = boundaries(nside, tile.meta.ipix)&lt;/span&gt;
&lt;span class="gd"&gt;-        radec = SkyCoord(ra=phi, dec=np.pi / 2 - theta, unit=&amp;#39;radian&amp;#39;, frame=&amp;#39;icrs&amp;#39;)&lt;/span&gt;
&lt;span class="gd"&gt;-        corners = []&lt;/span&gt;
&lt;span class="gd"&gt;-        for i in range(len(radec.ra.deg)):&lt;/span&gt;
&lt;span class="gd"&gt;-            corners.append([radec.ra.deg[i], radec.dec.deg[i]])&lt;/span&gt;
&lt;span class="gd"&gt;-        all_sky += _draw_sky_image_one_tile(corners, WCS(tile.header), tile, all_sky.shape)&lt;/span&gt;
&lt;span class="gi"&gt;+        draw_tile = DrawSkyImageOneTile(geometry, tile, all_sky.shape)&lt;/span&gt;
&lt;span class="gi"&gt;+        draw_tile.compute_corners()&lt;/span&gt;
&lt;span class="gi"&gt;+        draw_tile.apply_projection()&lt;/span&gt;
&lt;span class="gi"&gt;+        all_sky += draw_tile.warp_image()&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    return all_sky


&lt;span class="gd"&gt;-def _draw_sky_image_one_tile(corners: list, wcs: WCS, tile: HipsTile, shape: tuple) -&amp;gt; np.ndarray:&lt;/span&gt;
&lt;span class="gi"&gt;+class DrawSkyImageOneTile:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;A private function for drawing a tile over a sky image.

&lt;span class="w"&gt; &lt;/span&gt;    Parameters
&lt;span class="w"&gt; &lt;/span&gt;    ----------
&lt;span class="gd"&gt;-    corners : `~astropy.coordinates.SkyCoord`&lt;/span&gt;
&lt;span class="gd"&gt;-        Four corners of a HiPS tile&lt;/span&gt;
&lt;span class="gd"&gt;-    wcs : `~astropy.wcs.WCS`&lt;/span&gt;
&lt;span class="gd"&gt;-        WCS projection of a HiPS tile&lt;/span&gt;
&lt;span class="gd"&gt;-    tile : &amp;#39;HipsTile&amp;#39;&lt;/span&gt;
&lt;span class="gd"&gt;-        An object of HipsTile&lt;/span&gt;
&lt;span class="gi"&gt;+    geometry : &amp;#39;WCSGeometry&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        An object of WCSGeometry&lt;/span&gt;
&lt;span class="gi"&gt;+    tile : HipsTile&lt;/span&gt;
&lt;span class="gi"&gt;+       An object of HipsTile&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    shape ; tuple
&lt;span class="w"&gt; &lt;/span&gt;        Shape of the all-sky image
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    Returns&lt;/span&gt;
&lt;span class="gd"&gt;-    -------&lt;/span&gt;
&lt;span class="gd"&gt;-    np.ndarray&lt;/span&gt;
&lt;span class="gd"&gt;-        Returns a numpy array containing the projected HiPS tile&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-    src = wcs.wcs_world2pix(corners, 0)&lt;/span&gt;
&lt;span class="gd"&gt;-    dst = np.array([[511, 0], [511, 511], [0, 511], [0, 0]])&lt;/span&gt;
&lt;span class="gd"&gt;-    pt = tf.ProjectiveTransform()&lt;/span&gt;
&lt;span class="gd"&gt;-    pt.estimate(src, dst)&lt;/span&gt;
&lt;span class="gd"&gt;-    return tf.warp(tile.data, pt, output_shape=shape)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def __init__(self, geometry: WCSGeometry, tile: HipsTile, shape: tuple) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+        self.geometry = geometry&lt;/span&gt;
&lt;span class="gi"&gt;+        self.tile = tile&lt;/span&gt;
&lt;span class="gi"&gt;+        self.shape = shape&lt;/span&gt;
&lt;span class="gi"&gt;+        self.wcs = WCS(tile.header)&lt;/span&gt;
&lt;span class="gi"&gt;+        self.corners = None&lt;/span&gt;
&lt;span class="gi"&gt;+        self.pt = None&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def compute_corners(self) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+        nside = hp.order2nside(self.tile.meta.order)&lt;/span&gt;
&lt;span class="gi"&gt;+        theta, phi = boundaries(nside, self.tile.meta.ipix)&lt;/span&gt;
&lt;span class="gi"&gt;+        radec = SkyCoord(ra=phi, dec=np.pi / 2 - theta, unit=&amp;#39;radian&amp;#39;, frame=&amp;#39;icrs&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        self.corners = []&lt;/span&gt;
&lt;span class="gi"&gt;+        for i in range(len(radec.ra.deg)):&lt;/span&gt;
&lt;span class="gi"&gt;+            self.corners.append([radec.ra.deg[i], radec.dec.deg[i]])&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def apply_projection(self) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+        src = self.geometry.wcs.wcs_world2pix(self.corners, 0)&lt;/span&gt;
&lt;span class="gi"&gt;+        dst = np.array([[511, 0], [511, 511], [0, 511], [0, 0]])&lt;/span&gt;
&lt;span class="gi"&gt;+        self.pt = tf.ProjectiveTransform()&lt;/span&gt;
&lt;span class="gi"&gt;+        self.pt.estimate(src, dst)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def warp_image(self) -&amp;gt; np.ndarray:&lt;/span&gt;
&lt;span class="gi"&gt;+        return tf.warp(self.tile.data, self.pt, output_shape=self.shape)&lt;/span&gt;


&lt;span class="w"&gt; &lt;/span&gt;def _fetch_tiles(healpix_pixel_indices: np.ndarray, order: int, hips_survey: HipsSurveyProperties) -&amp;gt; &amp;#39;HipsTile&amp;#39;:
&lt;span class="gu"&gt;@@ -118,26 +122,21 @@ def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties) -&amp;gt;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    Examples
&lt;span class="w"&gt; &lt;/span&gt;    --------
&lt;span class="gd"&gt;-    Define which image you want:&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;gt;&amp;gt;&amp;gt; geometry = tbd&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    Define which HiPS survey you want:&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;gt;&amp;gt;&amp;gt; hips_survey = tbd&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    Compute the image:&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;gt;&amp;gt;&amp;gt; from hips import make_sky_image&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;gt;&amp;gt;&amp;gt; data = make_sky_image(geometry, hips_survey)&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    If you want, you can save the image to file:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;gt;&amp;gt;&amp;gt; from astropy.io import fits
&lt;span class="gi"&gt;+    &amp;gt;&amp;gt;&amp;gt; from hips.utils import WCSGeometry&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;gt;&amp;gt;&amp;gt; from hips.draw import make_sky_image&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;gt;&amp;gt;&amp;gt; from hips.tiles import HipsSurveyProperties&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;gt;&amp;gt;&amp;gt; geometry = WCSGeometry.create(&lt;/span&gt;
&lt;span class="gi"&gt;+    ...     skydir=SkyCoord(0, 0, unit=&amp;#39;deg&amp;#39;, frame=&amp;#39;galactic&amp;#39;),&lt;/span&gt;
&lt;span class="gi"&gt;+    ...     shape=(1000, 2000), coordsys=&amp;#39;GAL&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+    ...     projection=&amp;#39;AIT&amp;#39;, cdelt=0.01, crpix=(1000, 500),&lt;/span&gt;
&lt;span class="gi"&gt;+    ... )&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;gt;&amp;gt;&amp;gt; url = &amp;#39;https://raw.githubusercontent.com/hipspy/hips-extra/master/datasets/samples/DSS2Red/properties&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;gt;&amp;gt;&amp;gt; hips_survey = HipsSurveyProperties.fetch(url)&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;gt;&amp;gt;&amp;gt; data = make_sky_image(geometry, hips_survey)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;gt;&amp;gt;&amp;gt; hdu = fits.PrimaryHDU(data=data, header=geometry.header)
&lt;span class="w"&gt; &lt;/span&gt;    &amp;gt;&amp;gt;&amp;gt; hdu.writeto(&amp;#39;my_image.fits&amp;#39;)
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-    # Compute list of tiles needed&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    healpix_pixel_indices = compute_healpix_pixel_indices(geometry, hips_survey.hips_order)
&lt;span class="w"&gt; &lt;/span&gt;    path = Path(os.environ[&amp;#39;HIPS_EXTRA&amp;#39;])
&lt;span class="w"&gt; &lt;/span&gt;    tiles_path = path / &amp;#39;datasets&amp;#39; / &amp;#39;samples&amp;#39; / &amp;#39;DSS2Red&amp;#39; / &amp;#39;Norder3&amp;#39; / &amp;#39;Dir0&amp;#39;
&lt;span class="gu"&gt;@@ -152,7 +151,6 @@ def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties) -&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    # TODO: this isn&amp;#39;t a good API. Will become better when we have a cache.
&lt;span class="w"&gt; &lt;/span&gt;    # tiles = _fetch_tiles(healpix_pixel_indices, order, hips_survey)

&lt;span class="gd"&gt;-    # Draw the image&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    image_data = draw_sky_image(geometry, tiles)

&lt;span class="w"&gt; &lt;/span&gt;    return image_data
&lt;span class="gh"&gt;diff --git a/hips/draw/tests/test_simple.py b/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index 8f293a7..8c7aa97 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -34,7 +34,7 @@ def test_draw_sky_image():&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    assert data.shape == geometry.shape
&lt;span class="w"&gt; &lt;/span&gt;    assert data.dtype == np.float64

&lt;span class="gd"&gt;-    assert data[0, 0] == 0.060903854985925092&lt;/span&gt;
&lt;span class="gi"&gt;+    assert data[0, 0] == 0.0&lt;/span&gt;


&lt;span class="w"&gt; &lt;/span&gt;@remote_data
&lt;span class="gu"&gt;@@ -47,5 +47,4 @@ def test_make_sky_image():&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    assert data.shape == geometry.shape
&lt;span class="w"&gt; &lt;/span&gt;    assert data.dtype == np.float64
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    assert data[0, 0] == 0.060903854985925092&lt;/span&gt;
&lt;span class="gi"&gt;+    assert data[800, 1000] == 1794.7673494847763&lt;/span&gt;

commit 91e7e394a4f7bf363122703f55048ddcfee5118b
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Tue Jun 27 23:18:27 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Store FITS data as float type

&lt;span class="gh"&gt;diff --git a/hips/tiles/tile.py b/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gh"&gt;index 76e2428..70423b2 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -95,7 +95,7 @@ class HipsTile:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        if meta.file_format == &amp;#39;fits&amp;#39;:
&lt;span class="w"&gt; &lt;/span&gt;            hdu_list = fits.open(str(path))
&lt;span class="gd"&gt;-            data = hdu_list[0].data&lt;/span&gt;
&lt;span class="gi"&gt;+            data = hdu_list[0].data.astype(&amp;#39;float&amp;#39;)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            header = hdu_list[0].header
&lt;span class="w"&gt; &lt;/span&gt;            return cls(meta, data, header)
&lt;span class="w"&gt; &lt;/span&gt;        else:

commit 818a9102b3f12ab61ed13d3987b31b227719a700
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Tue Jun 27 11:49:58 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Update drawing function

&lt;span class="gh"&gt;diff --git a/hips/draw/simple.py b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index 8d7c268..f82df43 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,9 +1,15 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="w"&gt; &lt;/span&gt;&amp;quot;&amp;quot;&amp;quot;HiPS tile drawing -- simple method.&amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-import numpy as np&lt;/span&gt;
&lt;span class="gi"&gt;+import os&lt;/span&gt;
&lt;span class="gi"&gt;+from pathlib import Path&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;import healpy as hp
&lt;span class="gd"&gt;-from typing import List&lt;/span&gt;
&lt;span class="gi"&gt;+import numpy as np&lt;/span&gt;
&lt;span class="gi"&gt;+from astropy.coordinates import SkyCoord&lt;/span&gt;
&lt;span class="gi"&gt;+from astropy.wcs import WCS&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from skimage import transform as tf
&lt;span class="gi"&gt;+from typing import List&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from ..tiles import HipsSurveyProperties, HipsTile, HipsTileMeta
&lt;span class="w"&gt; &lt;/span&gt;from ..utils import WCSGeometry, compute_healpix_pixel_indices, boundaries

&lt;span class="gu"&gt;@@ -16,7 +22,17 @@ __all__ = [&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;def draw_sky_image(geometry: WCSGeometry, tiles: List[HipsTile]) -&amp;gt; np.ndarray:
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;Draw sky image using the simple and quick method.

&lt;span class="gd"&gt;-    TODO: fill in code here&lt;/span&gt;
&lt;span class="gi"&gt;+    Parameters&lt;/span&gt;
&lt;span class="gi"&gt;+    ----------&lt;/span&gt;
&lt;span class="gi"&gt;+    geometry : &amp;#39;WCSGeometry&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        An object of WCSGeometry&lt;/span&gt;
&lt;span class="gi"&gt;+    tiles : List[HipsTile]&lt;/span&gt;
&lt;span class="gi"&gt;+        A list of HipsTile&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    Returns&lt;/span&gt;
&lt;span class="gi"&gt;+    -------&lt;/span&gt;
&lt;span class="gi"&gt;+    np.ndarray&lt;/span&gt;
&lt;span class="gi"&gt;+        Returns a numpy array containing all HiPS tiles projected onto it&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;    # TODO: copy over (and clean up a little) the drawing code you already have in notebooks.
&lt;span class="w"&gt; &lt;/span&gt;    # Suggestion: don&amp;#39;t debug the code now, just put it here.
&lt;span class="gu"&gt;@@ -24,24 +40,68 @@ def draw_sky_image(geometry: WCSGeometry, tiles: List[HipsTile]) -&amp;gt; np.ndarray:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    # Probably this function should just loop over tiles and call a `_draw_sky_image_one_tile`
&lt;span class="w"&gt; &lt;/span&gt;    # helper function, and sum up the results.
&lt;span class="gd"&gt;-    for tile in HipsTile:&lt;/span&gt;
&lt;span class="gi"&gt;+    all_sky = np.zeros(geometry.shape)&lt;/span&gt;
&lt;span class="gi"&gt;+    for tile in tiles:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        nside = hp.order2nside(tile.meta.order)
&lt;span class="gd"&gt;-        corners = boundaries(nside, tile.meta.ipix)&lt;/span&gt;
&lt;span class="gd"&gt;-        # tile.meta.&lt;/span&gt;
&lt;span class="gi"&gt;+        theta, phi = boundaries(nside, tile.meta.ipix)&lt;/span&gt;
&lt;span class="gi"&gt;+        radec = SkyCoord(ra=phi, dec=np.pi / 2 - theta, unit=&amp;#39;radian&amp;#39;, frame=&amp;#39;icrs&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        corners = []&lt;/span&gt;
&lt;span class="gi"&gt;+        for i in range(len(radec.ra.deg)):&lt;/span&gt;
&lt;span class="gi"&gt;+            corners.append([radec.ra.deg[i], radec.dec.deg[i]])&lt;/span&gt;
&lt;span class="gi"&gt;+        all_sky += _draw_sky_image_one_tile(corners, WCS(tile.header), tile, all_sky.shape)&lt;/span&gt;
&lt;span class="gi"&gt;+    return all_sky&lt;/span&gt;

&lt;span class="gd"&gt;-    return np.zeros(geometry.shape)&lt;/span&gt;

&lt;span class="gd"&gt;-def _draw_sky_image_one_tile(corners: tuple, tile: HipsTile) -&amp;gt; np.ndarray:&lt;/span&gt;
&lt;span class="gi"&gt;+def _draw_sky_image_one_tile(corners: list, wcs: WCS, tile: HipsTile, shape: tuple) -&amp;gt; np.ndarray:&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;A private function for drawing a tile over a sky image.&lt;/span&gt;

&lt;span class="gi"&gt;+    Parameters&lt;/span&gt;
&lt;span class="gi"&gt;+    ----------&lt;/span&gt;
&lt;span class="gi"&gt;+    corners : `~astropy.coordinates.SkyCoord`&lt;/span&gt;
&lt;span class="gi"&gt;+        Four corners of a HiPS tile&lt;/span&gt;
&lt;span class="gi"&gt;+    wcs : `~astropy.wcs.WCS`&lt;/span&gt;
&lt;span class="gi"&gt;+        WCS projection of a HiPS tile&lt;/span&gt;
&lt;span class="gi"&gt;+    tile : &amp;#39;HipsTile&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        An object of HipsTile&lt;/span&gt;
&lt;span class="gi"&gt;+    shape ; tuple&lt;/span&gt;
&lt;span class="gi"&gt;+        Shape of the all-sky image&lt;/span&gt;

&lt;span class="gd"&gt;-def _fetch_tiles(healpix_pixel_indices, order, hips_survey):&lt;/span&gt;
&lt;span class="gi"&gt;+    Returns&lt;/span&gt;
&lt;span class="gi"&gt;+    -------&lt;/span&gt;
&lt;span class="gi"&gt;+    np.ndarray&lt;/span&gt;
&lt;span class="gi"&gt;+        Returns a numpy array containing the projected HiPS tile&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+    src = wcs.wcs_world2pix(corners, 0)&lt;/span&gt;
&lt;span class="gi"&gt;+    dst = np.array([[511, 0], [511, 511], [0, 511], [0, 0]])&lt;/span&gt;
&lt;span class="gi"&gt;+    pt = tf.ProjectiveTransform()&lt;/span&gt;
&lt;span class="gi"&gt;+    pt.estimate(src, dst)&lt;/span&gt;
&lt;span class="gi"&gt;+    return tf.warp(tile.data, pt, output_shape=shape)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+def _fetch_tiles(healpix_pixel_indices: np.ndarray, order: int, hips_survey: HipsSurveyProperties) -&amp;gt; &amp;#39;HipsTile&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;Fetch HiPS tiles from a remote URL.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    Parameters&lt;/span&gt;
&lt;span class="gi"&gt;+    ----------&lt;/span&gt;
&lt;span class="gi"&gt;+    healpix_pixel_indices : np.ndarray&lt;/span&gt;
&lt;span class="gi"&gt;+        A list of HEALPix pixel indices&lt;/span&gt;
&lt;span class="gi"&gt;+    order : int&lt;/span&gt;
&lt;span class="gi"&gt;+        Order of the HEALPix map&lt;/span&gt;
&lt;span class="gi"&gt;+    hips_survey : HipsSurveyProperties&lt;/span&gt;
&lt;span class="gi"&gt;+        An object of HipsSurveyProperties&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    Returns&lt;/span&gt;
&lt;span class="gi"&gt;+    -------&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;#39;HipsTile&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        Returns an object of  HipsTile&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    for healpix_pixel_index in healpix_pixel_indices:
&lt;span class="gd"&gt;-        tile_meta = HipsTileMeta(order=order, ipix=healpix_pixel_index, file_format=&amp;#39;jpg&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        tile_meta = HipsTileMeta(order=order, ipix=healpix_pixel_index, file_format=&amp;#39;fits&amp;#39;)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        tile = HipsTile.fetch(tile_meta, hips_survey.base_url)
&lt;span class="w"&gt; &lt;/span&gt;        yield tile


&lt;span class="gd"&gt;-def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties):&lt;/span&gt;
&lt;span class="gi"&gt;+def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties) -&amp;gt; np.ndarray:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;Make sky image: fetch tiles and draw.

&lt;span class="w"&gt; &lt;/span&gt;    Parameters
&lt;span class="gu"&gt;@@ -77,12 +137,20 @@ def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties):&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;gt;&amp;gt;&amp;gt; hdu.writeto(&amp;#39;my_image.fits&amp;#39;)
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;    # Compute list of tiles needed
&lt;span class="gd"&gt;-    order = hips_survey.hips_order&lt;/span&gt;
&lt;span class="gd"&gt;-    healpix_pixel_indices = compute_healpix_pixel_indices(geometry, order)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    healpix_pixel_indices = compute_healpix_pixel_indices(geometry, hips_survey.hips_order)&lt;/span&gt;
&lt;span class="gi"&gt;+    path = Path(os.environ[&amp;#39;HIPS_EXTRA&amp;#39;])&lt;/span&gt;
&lt;span class="gi"&gt;+    tiles_path = path / &amp;#39;datasets&amp;#39; / &amp;#39;samples&amp;#39; / &amp;#39;DSS2Red&amp;#39; / &amp;#39;Norder3&amp;#39; / &amp;#39;Dir0&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    tiles = []&lt;/span&gt;
&lt;span class="gi"&gt;+    for pixel_index in healpix_pixel_indices:&lt;/span&gt;
&lt;span class="gi"&gt;+        meta = HipsTileMeta(hips_survey.hips_order, pixel_index, &amp;#39;fits&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        filepath = str(tiles_path / meta.filename)&lt;/span&gt;
&lt;span class="gi"&gt;+        tiles.append(HipsTile.read(meta, filepath))&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    # Fetch the tiles
&lt;span class="w"&gt; &lt;/span&gt;    # TODO: this isn&amp;#39;t a good API. Will become better when we have a cache.
&lt;span class="gd"&gt;-    tiles = _fetch_tiles(healpix_pixel_indices, order, hips_survey)&lt;/span&gt;
&lt;span class="gi"&gt;+    # tiles = _fetch_tiles(healpix_pixel_indices, order, hips_survey)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    # Draw the image
&lt;span class="w"&gt; &lt;/span&gt;    image_data = draw_sky_image(geometry, tiles)
&lt;span class="gh"&gt;diff --git a/hips/draw/tests/test_simple.py b/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index fae10bc..8f293a7 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,32 +1,31 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="gd"&gt;-import pytest&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;import numpy as np
&lt;span class="gi"&gt;+import pytest&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from astropy.tests.helper import remote_data
&lt;span class="gd"&gt;-from ...tiles import HipsSurveyProperties, HipsTileMeta, HipsTile&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from ..simple import make_sky_image, draw_sky_image
&lt;span class="gi"&gt;+from ...tiles import HipsSurveyProperties, HipsTileMeta, HipsTile&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from ...utils.testing import get_hips_extra_file, make_test_wcs_geometry, requires_hips_extra


&lt;span class="w"&gt; &lt;/span&gt;def get_test_tiles():
&lt;span class="gd"&gt;-    # Could make manually a list of two tiles to draw&lt;/span&gt;
&lt;span class="gd"&gt;-    # Pointing to FITS tiles in hips-extra like e.g.&lt;/span&gt;
&lt;span class="gd"&gt;-    # hips-extra/datasets/samples//DSS2Red/Norder3/Dir0/Npix659.fits&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    # TODO: check if this tile is inside our image
&lt;span class="w"&gt; &lt;/span&gt;    tile1 = HipsTile.read(
&lt;span class="gd"&gt;-        meta=HipsTileMeta(order=3, ipix=659, file_format=&amp;#39;fits&amp;#39;, tile_width=512),&lt;/span&gt;
&lt;span class="gd"&gt;-        filename=get_hips_extra_file(&amp;#39;datasets/samples//DSS2Red/Norder3/Dir0/Npix659.fits&amp;#39;),&lt;/span&gt;
&lt;span class="gi"&gt;+        meta=HipsTileMeta(order=3, ipix=658, file_format=&amp;#39;fits&amp;#39;, tile_width=512),&lt;/span&gt;
&lt;span class="gi"&gt;+        filename=get_hips_extra_file(&amp;#39;datasets/samples/DSS2Red/Norder3/Dir0/Npix658.fits&amp;#39;),&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    )

&lt;span class="gd"&gt;-    # TODO: also fetch a second one.&lt;/span&gt;
&lt;span class="gi"&gt;+    tile2 = HipsTile.read(&lt;/span&gt;
&lt;span class="gi"&gt;+        meta=HipsTileMeta(order=3, ipix=659, file_format=&amp;#39;fits&amp;#39;, tile_width=512),&lt;/span&gt;
&lt;span class="gi"&gt;+        filename=get_hips_extra_file(&amp;#39;datasets/samples/DSS2Red/Norder3/Dir0/Npix659.fits&amp;#39;),&lt;/span&gt;
&lt;span class="gi"&gt;+    )&lt;/span&gt;

&lt;span class="gd"&gt;-    return [tile1]&lt;/span&gt;
&lt;span class="gi"&gt;+    return [tile1, tile2]&lt;/span&gt;


&lt;span class="gi"&gt;+@pytest.mark.xfail&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;@requires_hips_extra()
&lt;span class="w"&gt; &lt;/span&gt;def test_draw_sky_image():
&lt;span class="gd"&gt;-    # filename = get_pkg_data_filename(&amp;#39;../../tiles/tests/data/properties.txt&amp;#39;)&lt;/span&gt;
&lt;span class="gd"&gt;-    # hips_survey = HipsSurveyProperties.read(filename)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    geometry = make_test_wcs_geometry(case=2)
&lt;span class="w"&gt; &lt;/span&gt;    tiles = get_test_tiles()

&lt;span class="gu"&gt;@@ -35,13 +34,12 @@ def test_draw_sky_image():&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    assert data.shape == geometry.shape
&lt;span class="w"&gt; &lt;/span&gt;    assert data.dtype == np.float64

&lt;span class="gd"&gt;-    assert data[100, 200] == 42&lt;/span&gt;
&lt;span class="gi"&gt;+    assert data[0, 0] == 0.060903854985925092&lt;/span&gt;


&lt;span class="gd"&gt;-@pytest.mark.xfail&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;@remote_data
&lt;span class="w"&gt; &lt;/span&gt;def test_make_sky_image():
&lt;span class="gd"&gt;-    url = &amp;#39;https://raw.githubusercontent.com/hipspy/hips/master/hips/tiles/tests/data/properties.txt&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+    url = &amp;#39;https://raw.githubusercontent.com/hipspy/hips-extra/master/datasets/samples/DSS2Red/properties&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    hips_survey = HipsSurveyProperties.fetch(url)
&lt;span class="w"&gt; &lt;/span&gt;    geometry = make_test_wcs_geometry(case=2)

&lt;span class="gu"&gt;@@ -50,4 +48,4 @@ def test_make_sky_image():&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    assert data.shape == geometry.shape
&lt;span class="w"&gt; &lt;/span&gt;    assert data.dtype == np.float64

&lt;span class="gd"&gt;-    assert data[100, 200] == 42&lt;/span&gt;
&lt;span class="gi"&gt;+    assert data[0, 0] == 0.060903854985925092&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/utils/testing.py b/hips/utils/testing.py&lt;/span&gt;
&lt;span class="gh"&gt;index 50c7e11..ce8556c 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/testing.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/testing.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -2,10 +2,12 @@&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;Not of use for users / outside this package.
&lt;span class="w"&gt; &lt;/span&gt;&amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-import pytest&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;import os
&lt;span class="w"&gt; &lt;/span&gt;from pathlib import Path
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+import pytest&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from astropy.coordinates import SkyCoord
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from .wcs import WCSGeometry


&lt;span class="gu"&gt;@@ -50,4 +52,4 @@ def make_test_wcs_geometry(case=0):&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            projection=&amp;#39;AIT&amp;#39;, cdelt=0.01, crpix=(1000, 500),
&lt;span class="w"&gt; &lt;/span&gt;        )
&lt;span class="w"&gt; &lt;/span&gt;    else:
&lt;span class="gd"&gt;-        raise ValueError()&lt;/span&gt;
&lt;span class="gi"&gt;+        raise ValueError()  # pragma: no cover&lt;/span&gt;

commit 120ff07dd6b4cd13bcd0131927746fab7dbb7f7d
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Mon Jun 26 13:49:52 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Add skeleton of drawing code

&lt;span class="gh"&gt;diff --git a/hips/draw/simple.py b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index 309c15d..8d7c268 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,9 +1,11 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="w"&gt; &lt;/span&gt;&amp;quot;&amp;quot;&amp;quot;HiPS tile drawing -- simple method.&amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-from typing import List&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;import numpy as np
&lt;span class="gi"&gt;+import healpy as hp&lt;/span&gt;
&lt;span class="gi"&gt;+from typing import List&lt;/span&gt;
&lt;span class="gi"&gt;+from skimage import transform as tf&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from ..tiles import HipsSurveyProperties, HipsTile, HipsTileMeta
&lt;span class="gd"&gt;-from ..utils import WCSGeometry, compute_healpix_pixel_indices&lt;/span&gt;
&lt;span class="gi"&gt;+from ..utils import WCSGeometry, compute_healpix_pixel_indices, boundaries&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;__all__ = [
&lt;span class="w"&gt; &lt;/span&gt;    &amp;#39;draw_sky_image&amp;#39;,
&lt;span class="gu"&gt;@@ -22,9 +24,15 @@ def draw_sky_image(geometry: WCSGeometry, tiles: List[HipsTile]) -&amp;gt; np.ndarray:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    # Probably this function should just loop over tiles and call a `_draw_sky_image_one_tile`
&lt;span class="w"&gt; &lt;/span&gt;    # helper function, and sum up the results.
&lt;span class="gi"&gt;+    for tile in HipsTile:&lt;/span&gt;
&lt;span class="gi"&gt;+        nside = hp.order2nside(tile.meta.order)&lt;/span&gt;
&lt;span class="gi"&gt;+        corners = boundaries(nside, tile.meta.ipix)&lt;/span&gt;
&lt;span class="gi"&gt;+        # tile.meta.&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    return np.zeros(geometry.shape)

&lt;span class="gi"&gt;+def _draw_sky_image_one_tile(corners: tuple, tile: HipsTile) -&amp;gt; np.ndarray:&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;def _fetch_tiles(healpix_pixel_indices, order, hips_survey):
&lt;span class="w"&gt; &lt;/span&gt;    for healpix_pixel_index in healpix_pixel_indices:

commit 1dcc81164f456a9bd4fa7f7905630c94c429d308
Merge: a5ac131 32db7fb
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Mon Jun 26 12:04:52 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Merge branch &amp;#39;master&amp;#39; of https://github.com/hipspy/hips into tiles.draw

commit a3f7261fe255ac8a6904741f576d99bddeba3ffd
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Sun Jun 25 17:25:00 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Add documentation on HIPS_EXTRA environment variable

&lt;span class="gh"&gt;diff --git a/docs/develop.rst b/docs/develop.rst&lt;/span&gt;
&lt;span class="gh"&gt;index c34c07f..4f2fdd5 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/docs/develop.rst&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/docs/develop.rst&lt;/span&gt;
&lt;span class="gu"&gt;@@ -6,6 +6,13 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;Develop
&lt;span class="w"&gt; &lt;/span&gt;*******

&lt;span class="gi"&gt;+To run tests accessing files from `hips-extra &amp;lt;https://github.com/hipspy/hips-extra&amp;gt;`_&lt;/span&gt;
&lt;span class="gi"&gt;+repository, users must have it cloned on their system, otherwise some test cases&lt;/span&gt;
&lt;span class="gi"&gt;+will be skipped. This contains tiles from different HiPS surveys which are used&lt;/span&gt;
&lt;span class="gi"&gt;+by the drawing module. After this, the ``HIPS_EXTRA`` environment variable must&lt;/span&gt;
&lt;span class="gi"&gt;+be set up on their system. On UNIX operating systems, this can be set using&lt;/span&gt;
&lt;span class="gi"&gt;+``export HIPS_EXTRA=path/to/hips-extra``.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;Want to contribute to the ``hips`` package?

&lt;span class="w"&gt; &lt;/span&gt;Great! Talk to us by filing a Github issue any time
&lt;span class="gh"&gt;diff --git a/docs/installation.rst b/docs/installation.rst&lt;/span&gt;
&lt;span class="gh"&gt;index 094aeeb..2e33e82 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/docs/installation.rst&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/docs/installation.rst&lt;/span&gt;
&lt;span class="gu"&gt;@@ -80,6 +80,10 @@ To check if there are any issues with your installation, you can run the tests:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    python -c &amp;#39;import hips; hips.test()&amp;#39;

&lt;span class="gi"&gt;+.. note::&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    For more information, see the :ref:`develop` page.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;Development version
&lt;span class="w"&gt; &lt;/span&gt;===================


commit 8add0250a612c22b9a5ecb5dbd4ce9e4a9369f89
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Thu Jun 22 18:01:32 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Add function make_sky_image

&lt;span class="gh"&gt;diff --git a/hips/draw/simple.py b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index 362127d..3dc8f05 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,5 +1,23 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="w"&gt; &lt;/span&gt;&amp;quot;&amp;quot;&amp;quot;HiPS tile drawing -- simple method.&amp;quot;&amp;quot;&amp;quot;

&lt;span class="gi"&gt;+import healpy as hp&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+from ..tiles import HipsSurveyProperties&lt;/span&gt;
&lt;span class="gi"&gt;+from ..tiles import HipsTile&lt;/span&gt;
&lt;span class="gi"&gt;+from ..tiles import HipsTileMeta&lt;/span&gt;
&lt;span class="gi"&gt;+from ..utils import WCSGeometry&lt;/span&gt;
&lt;span class="gi"&gt;+from ..utils import compute_healpix_pixel_indices&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;__all__ = [
&lt;span class="gi"&gt;+    &amp;#39;make_sky_image&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;]
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+def make_sky_image(geometry: WCSGeometry, hips_survey: HipsSurveyProperties):&lt;/span&gt;
&lt;span class="gi"&gt;+    order = hips_survey.hips_order&lt;/span&gt;
&lt;span class="gi"&gt;+    healpix_pixel_indices = compute_healpix_pixel_indices(geometry, order)&lt;/span&gt;
&lt;span class="gi"&gt;+    tiles = list()&lt;/span&gt;
&lt;span class="gi"&gt;+    for healpix_pixel_index in healpix_pixel_indices:&lt;/span&gt;
&lt;span class="gi"&gt;+        tile_meta = HipsTileMeta(order=order, ipix=healpix_pixel_index, file_format=&amp;#39;jpg&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        tiles.append(HipsTile(tile_meta))&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/draw/tests/test_simple.py b/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index 9dce85d..9f8d838 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/tests/test_simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1 +1,21 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+from astropy.coordinates import SkyCoord&lt;/span&gt;
&lt;span class="gi"&gt;+from astropy.tests.helper import remote_data&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+from hips.tiles import HipsSurveyProperties&lt;/span&gt;
&lt;span class="gi"&gt;+from hips.utils import WCSGeometry&lt;/span&gt;
&lt;span class="gi"&gt;+from ..simple import make_sky_image&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+@remote_data&lt;/span&gt;
&lt;span class="gi"&gt;+def test_make_sky_image():&lt;/span&gt;
&lt;span class="gi"&gt;+    url = &amp;#39;https://raw.githubusercontent.com/hipspy/hips/master/hips/tiles/tests/data/properties.txt&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+    hips_survey = HipsSurveyProperties.fetch(url)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    shape = (1000, 2000)&lt;/span&gt;
&lt;span class="gi"&gt;+    y_center, x_center = shape[0] / 2, shape[1] / 2&lt;/span&gt;
&lt;span class="gi"&gt;+    skycoord = SkyCoord(0, 0, unit=&amp;quot;deg&amp;quot;)&lt;/span&gt;
&lt;span class="gi"&gt;+    wcs_geometry = WCSGeometry.create(skydir=skycoord, shape=shape, coordsys=&amp;#39;CEL&amp;#39;, projection=&amp;#39;AIT&amp;#39;, cdelt=0.01,&lt;/span&gt;
&lt;span class="gi"&gt;+                                      crpix=(y_center, x_center))&lt;/span&gt;
&lt;span class="gi"&gt;+    make_sky_image(wcs_geometry, hips_survey)&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/utils/healpix.py b/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gh"&gt;index 63f1cd7..4f89897 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -8,6 +8,7 @@ from typing import Tuple&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;import numpy as np
&lt;span class="w"&gt; &lt;/span&gt;from astropy.coordinates import SkyCoord
&lt;span class="w"&gt; &lt;/span&gt;import healpy as hp
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from .wcs import WCSGeometry

&lt;span class="w"&gt; &lt;/span&gt;__all__ = [
&lt;span class="gu"&gt;@@ -68,7 +69,7 @@ def boundaries(nside: int, pix: int, nest: bool = True) -&amp;gt; tuple:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    return theta, phi


&lt;span class="gd"&gt;-def compute_healpix_pixel_indices(wcs_geometry: WCSGeometry, nside: int) -&amp;gt; np.ndarray:&lt;/span&gt;
&lt;span class="gi"&gt;+def compute_healpix_pixel_indices(wcs_geometry: WCSGeometry, order: int) -&amp;gt; np.ndarray:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;Compute HEALPix pixels within a minimal disk covering a given WCSGeometry.

&lt;span class="w"&gt; &lt;/span&gt;    This function calls `healpy.pixelfunc.ang2vec` and `healpy.query_disc`
&lt;span class="gu"&gt;@@ -78,8 +79,8 @@ def compute_healpix_pixel_indices(wcs_geometry: WCSGeometry, nside: int) -&amp;gt; np.n&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    ----------
&lt;span class="w"&gt; &lt;/span&gt;    wcs_geometry : WCSGeometry
&lt;span class="w"&gt; &lt;/span&gt;        Container for WCS object and image shape
&lt;span class="gd"&gt;-    nside : int&lt;/span&gt;
&lt;span class="gd"&gt;-        The nside of the HEALPix map&lt;/span&gt;
&lt;span class="gi"&gt;+    order : int&lt;/span&gt;
&lt;span class="gi"&gt;+        The order of the HEALPix&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    Returns
&lt;span class="w"&gt; &lt;/span&gt;    -------
&lt;span class="gu"&gt;@@ -89,7 +90,6 @@ def compute_healpix_pixel_indices(wcs_geometry: WCSGeometry, nside: int) -&amp;gt; np.n&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    Examples
&lt;span class="w"&gt; &lt;/span&gt;    --------
&lt;span class="w"&gt; &lt;/span&gt;    &amp;gt;&amp;gt;&amp;gt; from astropy.coordinates import SkyCoord
&lt;span class="gd"&gt;-    &amp;gt;&amp;gt;&amp;gt; import healpy as hp&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;gt;&amp;gt;&amp;gt; from hips.utils import WCSGeometry
&lt;span class="w"&gt; &lt;/span&gt;    &amp;gt;&amp;gt;&amp;gt; from hips.utils import compute_healpix_pixel_indices
&lt;span class="w"&gt; &lt;/span&gt;    &amp;gt;&amp;gt;&amp;gt; skycoord = SkyCoord(10, 20, unit=&amp;quot;deg&amp;quot;)
&lt;span class="gu"&gt;@@ -98,8 +98,7 @@ def compute_healpix_pixel_indices(wcs_geometry: WCSGeometry, nside: int) -&amp;gt; np.n&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    ...     coordsys=&amp;#39;CEL&amp;#39;, projection=&amp;#39;AIT&amp;#39;,
&lt;span class="w"&gt; &lt;/span&gt;    ...     cdelt=1.0, crpix=(1., 1.),
&lt;span class="w"&gt; &lt;/span&gt;    ... )
&lt;span class="gd"&gt;-    &amp;gt;&amp;gt;&amp;gt; nside = hp.order2nside(order=3)&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;gt;&amp;gt;&amp;gt; compute_healpix_pixel_indices(wcs_geometry, nside)&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;gt;&amp;gt;&amp;gt; compute_healpix_pixel_indices(wcs_geometry, order=3)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    array([176, 207, 208, 239, 240, 271, 272])
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;    center_coord = wcs_geometry.center_skycoord
&lt;span class="gu"&gt;@@ -108,5 +107,5 @@ def compute_healpix_pixel_indices(wcs_geometry: WCSGeometry, nside: int) -&amp;gt; np.n&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    radius = np.nanmax(separation.rad)

&lt;span class="w"&gt; &lt;/span&gt;    vec = _skycoord_to_vec(center_coord)
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gi"&gt;+    nside = hp.order2nside(order)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    return hp.query_disc(nside, vec, radius)
&lt;span class="gh"&gt;diff --git a/hips/utils/tests/test_healpix.py b/hips/utils/tests/test_healpix.py&lt;/span&gt;
&lt;span class="gh"&gt;index 0fc0267..864eadb 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/tests/test_healpix.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/tests/test_healpix.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -23,6 +23,5 @@ def test_boundaries():&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;def test_compute_healpix_pixel_indices():
&lt;span class="w"&gt; &lt;/span&gt;    wcs_geometry = make_test_wcs_geometry(case=1)
&lt;span class="gd"&gt;-    nside = hp.order2nside(order=3)&lt;/span&gt;
&lt;span class="gd"&gt;-    pixels = compute_healpix_pixel_indices(wcs_geometry, nside)&lt;/span&gt;
&lt;span class="gi"&gt;+    pixels = compute_healpix_pixel_indices(wcs_geometry, order=3)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    assert_allclose(pixels, [176, 207, 208, 239, 240, 271, 272])

commit 449e4df92ed21259d527e9b2fb325ae708e46045
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Mon Jun 19 18:05:07 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Add class HipsSurveyList for parsing list of hips surveys

&lt;span class="gh"&gt;diff --git a/hips/tiles/description.py b/hips/tiles/description.py&lt;/span&gt;
&lt;span class="gh"&gt;index 86ccfae..2062836 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/description.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/description.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,26 +1,34 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="gi"&gt;+import urllib.request&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from collections import OrderedDict

&lt;span class="w"&gt; &lt;/span&gt;__all__ = [
&lt;span class="gd"&gt;-    &amp;#39;HipsDescription&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;#39;HipsSurveyProperties&amp;#39;,&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;]


&lt;span class="gd"&gt;-class HipsDescription:&lt;/span&gt;
&lt;span class="gi"&gt;+class HipsSurveyProperties:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;HiPS properties container.

&lt;span class="w"&gt; &lt;/span&gt;    Parameters
&lt;span class="w"&gt; &lt;/span&gt;    ----------
&lt;span class="w"&gt; &lt;/span&gt;    properties : `~collections.OrderedDict`
&lt;span class="gd"&gt;-        HiPS description properties&lt;/span&gt;
&lt;span class="gi"&gt;+        HiPS survey properties&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    Examples&lt;/span&gt;
&lt;span class="gi"&gt;+    --------&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; url = &amp;#39;https://raw.githubusercontent.com/hipspy/hips/master/hips/tiles/tests/data/properties.txt&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; hips_survey_property = HipsSurveyProperties.fetch(url)&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; hips_survey_property.base_url&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;#39;http://alasky.u-strasbg.fr/DSS/DSSColor&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;

&lt;span class="w"&gt; &lt;/span&gt;    def __init__(self, properties: OrderedDict) -&amp;gt; None:
&lt;span class="w"&gt; &lt;/span&gt;        self.properties = properties

&lt;span class="w"&gt; &lt;/span&gt;    @classmethod
&lt;span class="gd"&gt;-    def read(cls, filename: str) -&amp;gt; &amp;#39;HipsDescription&amp;#39;:&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;Read from HiPS description file (`HipsDescription`).&lt;/span&gt;
&lt;span class="gi"&gt;+    def read(cls, filename: str) -&amp;gt; &amp;#39;HipsSurveyProperties&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Read from HiPS survey description file (`HipsSurveyProperties`).&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        Parameters
&lt;span class="w"&gt; &lt;/span&gt;        ----------
&lt;span class="gu"&gt;@@ -33,20 +41,33 @@ class HipsDescription:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        return cls.parse(text)

&lt;span class="w"&gt; &lt;/span&gt;    @classmethod
&lt;span class="gd"&gt;-    def parse(cls, text: str) -&amp;gt; &amp;#39;HipsDescription&amp;#39;:&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;Parse HiPS description text (`HipsDescription`).&lt;/span&gt;
&lt;span class="gi"&gt;+    def fetch(cls, url: str) -&amp;gt; &amp;#39;HipsSurveyProperties&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Read from HiPS survey description file from remote URL (`HipsSurveyProperties`).&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        Parameters&lt;/span&gt;
&lt;span class="gi"&gt;+        ----------&lt;/span&gt;
&lt;span class="gi"&gt;+        url : str&lt;/span&gt;
&lt;span class="gi"&gt;+            URL containing HiPS properties&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        response = urllib.request.urlopen(url).read()&lt;/span&gt;
&lt;span class="gi"&gt;+        text = response.decode(&amp;#39;utf-8&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        return cls.parse(text)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @classmethod&lt;/span&gt;
&lt;span class="gi"&gt;+    def parse(cls, text: str) -&amp;gt; &amp;#39;HipsSurveyProperties&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Parse HiPS survey description text (`HipsSurveyProperties`).&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        Parameters
&lt;span class="w"&gt; &lt;/span&gt;        ----------
&lt;span class="w"&gt; &lt;/span&gt;        text : str
&lt;span class="gd"&gt;-            HiPS properties text&lt;/span&gt;
&lt;span class="gi"&gt;+            Text containing HiPS survey properties&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;        properties = OrderedDict()
&lt;span class="w"&gt; &lt;/span&gt;        for line in text.split(&amp;#39;\n&amp;#39;):
&lt;span class="w"&gt; &lt;/span&gt;            # Skip empty or comment lines
&lt;span class="w"&gt; &lt;/span&gt;            if line == &amp;#39;&amp;#39; or line.startswith(&amp;#39;#&amp;#39;):
&lt;span class="w"&gt; &lt;/span&gt;                continue
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            try:
&lt;span class="w"&gt; &lt;/span&gt;                key, value = [_.strip() for _ in line.split(&amp;#39;=&amp;#39;)]
&lt;span class="w"&gt; &lt;/span&gt;                properties[key] = value
&lt;span class="gh"&gt;diff --git a/hips/tiles/surveys.py b/hips/tiles/surveys.py&lt;/span&gt;
new file mode 100644
&lt;span class="gh"&gt;index 0000000..511dca5&lt;/span&gt;
&lt;span class="gd"&gt;--- /dev/null&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/surveys.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -0,0 +1,70 @@&lt;/span&gt;
&lt;span class="gi"&gt;+# Licensed under a 3-clause BSD style license - see LICENSE.rst&lt;/span&gt;
&lt;span class="gi"&gt;+import urllib.request&lt;/span&gt;
&lt;span class="gi"&gt;+from typing import List&lt;/span&gt;
&lt;span class="gi"&gt;+from astropy.table import Table&lt;/span&gt;
&lt;span class="gi"&gt;+from collections import OrderedDict&lt;/span&gt;
&lt;span class="gi"&gt;+from .description import HipsSurveyProperties&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+__all__ = [&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;#39;HipsSurveyPropertiesList&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+]&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+class HipsSurveyPropertiesList:&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;HiPS survey container.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    Parameters&lt;/span&gt;
&lt;span class="gi"&gt;+    ----------&lt;/span&gt;
&lt;span class="gi"&gt;+    surveys : List[HipsSurveyProperties]&lt;/span&gt;
&lt;span class="gi"&gt;+        HiPS surveys&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def __init__(self, surveys: List[HipsSurveyProperties]) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+        self.surveys = surveys&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @classmethod&lt;/span&gt;
&lt;span class="gi"&gt;+    def read(cls, filename: str) -&amp;gt; &amp;#39;HipsSurveyPropertiesList&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Read HiPS list from file (`HipsSurveyPropertiesList`).&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        Parameters&lt;/span&gt;
&lt;span class="gi"&gt;+        ----------&lt;/span&gt;
&lt;span class="gi"&gt;+        filename : str&lt;/span&gt;
&lt;span class="gi"&gt;+            HiPS list filename&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        with open(filename, encoding=&amp;#39;utf-8&amp;#39;, errors=&amp;#39;ignore&amp;#39;) as fh:&lt;/span&gt;
&lt;span class="gi"&gt;+            text = fh.read()&lt;/span&gt;
&lt;span class="gi"&gt;+        return cls.parse(text)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @classmethod&lt;/span&gt;
&lt;span class="gi"&gt;+    def fetch(cls, url: str=&amp;#39;http://alasky.unistra.fr/MocServer/query?hips_service_url=*&amp;amp;dataproduct_type=!catalog&amp;amp;dataproduct_type=!cube&amp;amp;get=record&amp;#39;) -&amp;gt; &amp;#39;HipsSurveyPropertiesList&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Fetch HiPS list text from remote location (`HipsSurveyPropertiesList`).&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        Parameters&lt;/span&gt;
&lt;span class="gi"&gt;+        ----------&lt;/span&gt;
&lt;span class="gi"&gt;+        url : str&lt;/span&gt;
&lt;span class="gi"&gt;+            HiPS list URL&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        response = urllib.request.urlopen(url).read()&lt;/span&gt;
&lt;span class="gi"&gt;+        text = str(response)&lt;/span&gt;
&lt;span class="gi"&gt;+        return cls.parse(text)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @classmethod&lt;/span&gt;
&lt;span class="gi"&gt;+    def parse(cls, text: str) -&amp;gt; &amp;#39;HipsSurveyPropertiesList&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Parse HiPS list text (`HipsSurveyPropertiesList`).&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        Parameters&lt;/span&gt;
&lt;span class="gi"&gt;+        ----------&lt;/span&gt;
&lt;span class="gi"&gt;+        text : str&lt;/span&gt;
&lt;span class="gi"&gt;+            HiPS list text&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        surveys = list()&lt;/span&gt;
&lt;span class="gi"&gt;+        for raw_survey in text.split(&amp;#39;\n\n&amp;#39;):&lt;/span&gt;
&lt;span class="gi"&gt;+            surveys.append(HipsSurveyProperties.parse(raw_survey))&lt;/span&gt;
&lt;span class="gi"&gt;+        return cls(surveys)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @property&lt;/span&gt;
&lt;span class="gi"&gt;+    def table(self) -&amp;gt; Table:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Astropy Table containing HiPS surveys (`HipsSurveyPropertiesList`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        table = Table()&lt;/span&gt;
&lt;span class="gi"&gt;+        table[&amp;#39;surveys&amp;#39;] = [survey.properties for survey in self.surveys]&lt;/span&gt;
&lt;span class="gi"&gt;+        return table&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/tiles/tests/data/surveys.txt b/hips/tiles/tests/data/surveys.txt&lt;/span&gt;
new file mode 100644
&lt;span class="gh"&gt;index 0000000..876234c&lt;/span&gt;
&lt;span class="gd"&gt;--- /dev/null&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tests/data/surveys.txt&lt;/span&gt;
&lt;span class="gu"&gt;@@ -0,0 +1,202 @@&lt;/span&gt;
&lt;span class="gi"&gt;+ID                   = CDS/C/MUSE-M42&lt;/span&gt;
&lt;span class="gi"&gt;+creator_did          = ivo://CDS/C/MUSE-M42&lt;/span&gt;
&lt;span class="gi"&gt;+obs_collection       = MUSE-M42&lt;/span&gt;
&lt;span class="gi"&gt;+obs_title            = MUSE map of the central Orion Nebula (M 42)&lt;/span&gt;
&lt;span class="gi"&gt;+obs_description      = Integral-field spectroscopic dataset of the central part of the Orion Nebula (M 42), observed with the MUSE instrument at the ESO VLT (reduced the data with the public MUSE pipeline) representing a FITS cube with a spatial size of ~5.9&amp;#39;x4.9&amp;#39; (corresponding to ~0.76 pc x 0.63 pc) and a contiguous wavelength coverage of 4595...9366 Angstrom, spatially sampled at 0.2&amp;quot;, with a sampling of 1.25 Angstrom in dispersion direction.&lt;/span&gt;
&lt;span class="gi"&gt;+obs_ack              = Based on data obtained from the ESO/VLT&lt;/span&gt;
&lt;span class="gi"&gt;+prov_progenitor      = MUSE Consortium&lt;/span&gt;
&lt;span class="gi"&gt;+bib_reference        = 2015A&amp;amp;A...582A.114W&lt;/span&gt;
&lt;span class="gi"&gt;+bib_reference_url    = http://simbad.u-strasbg.fr/simbad/sim-ref?bibcode=2015A%26A...582A.114W&lt;/span&gt;
&lt;span class="gi"&gt;+obs_copyright        = Copyright mention of the original data&lt;/span&gt;
&lt;span class="gi"&gt;+obs_copyright_url    = http://muse-vlt.eu/science/m42/&lt;/span&gt;
&lt;span class="gi"&gt;+hips_release_date    = 2015-07-07T00:29Z&lt;/span&gt;
&lt;span class="gi"&gt;+hips_builder         = Aladin/HipsGen v9.505&lt;/span&gt;
&lt;span class="gi"&gt;+hips_order           = 12&lt;/span&gt;
&lt;span class="gi"&gt;+hips_pixel_cut       = 0 7760&lt;/span&gt;
&lt;span class="gi"&gt;+hips_tile_format     = png fits&lt;/span&gt;
&lt;span class="gi"&gt;+hips_cube_depth      = 3818&lt;/span&gt;
&lt;span class="gi"&gt;+hips_cube_firstframe  = 1909&lt;/span&gt;
&lt;span class="gi"&gt;+hips_frame           = equatorial&lt;/span&gt;
&lt;span class="gi"&gt;+dataproduct_type     = image&lt;/span&gt;
&lt;span class="gi"&gt;+t_min                = 56693&lt;/span&gt;
&lt;span class="gi"&gt;+t_max                = 56704&lt;/span&gt;
&lt;span class="gi"&gt;+em_min               = 4,595e-7&lt;/span&gt;
&lt;span class="gi"&gt;+em_max               = 9,366e-7&lt;/span&gt;
&lt;span class="gi"&gt;+hips_version         = 1.31&lt;/span&gt;
&lt;span class="gi"&gt;+hips_creation_date   = 03/07/15 12:00:30&lt;/span&gt;
&lt;span class="gi"&gt;+hips_creator         = CDS (P.Fernique)&lt;/span&gt;
&lt;span class="gi"&gt;+hips_tile_width      = 512&lt;/span&gt;
&lt;span class="gi"&gt;+hips_status          = public master clonableOnce&lt;/span&gt;
&lt;span class="gi"&gt;+hips_pixel_bitpix    = -32&lt;/span&gt;
&lt;span class="gi"&gt;+data_pixel_bitpix    = -32&lt;/span&gt;
&lt;span class="gi"&gt;+hips_hierarchy       = mean&lt;/span&gt;
&lt;span class="gi"&gt;+hips_initial_ra      = 83.82094&lt;/span&gt;
&lt;span class="gi"&gt;+hips_initial_dec     = -5.39542&lt;/span&gt;
&lt;span class="gi"&gt;+hips_initial_fov     = 0.09811&lt;/span&gt;
&lt;span class="gi"&gt;+hips_pixel_scale     = 2.795E-5&lt;/span&gt;
&lt;span class="gi"&gt;+s_pixel_scale        = 5.555E-5&lt;/span&gt;
&lt;span class="gi"&gt;+moc_sky_fraction     = 2.980E-7&lt;/span&gt;
&lt;span class="gi"&gt;+hips_estsize         = 87653&lt;/span&gt;
&lt;span class="gi"&gt;+data_bunit           = 10**(-20)*erg/s/cm**2/Angstrom&lt;/span&gt;
&lt;span class="gi"&gt;+data_cube_crpix3     = 1&lt;/span&gt;
&lt;span class="gi"&gt;+data_cube_crval3     = 4595&lt;/span&gt;
&lt;span class="gi"&gt;+data_cube_cdelt3     = 1.25&lt;/span&gt;
&lt;span class="gi"&gt;+data_cube_bunit3     = Angstrom&lt;/span&gt;
&lt;span class="gi"&gt;+client_application   = AladinDesktopBeta&lt;/span&gt;
&lt;span class="gi"&gt;+client_application   = AladinDesktop&lt;/span&gt;
&lt;span class="gi"&gt;+hips_copyright       = CNRS/Unistra&lt;/span&gt;
&lt;span class="gi"&gt;+obs_regime           = Optical&lt;/span&gt;
&lt;span class="gi"&gt;+hips_service_url     = http://alasky.unistra.fr/MUSE/MUSE-M42&lt;/span&gt;
&lt;span class="gi"&gt;+hips_service_url_1   = http://alaskybis.unistra.fr/MUSE/MUSE-M42&lt;/span&gt;
&lt;span class="gi"&gt;+hips_status_1        = public mirror clonable&lt;/span&gt;
&lt;span class="gi"&gt;+hips_service_url_2   = https://alaskybis.unistra.fr/MUSE/MUSE-M42&lt;/span&gt;
&lt;span class="gi"&gt;+hips_status_2        = public mirror clonable&lt;/span&gt;
&lt;span class="gi"&gt;+moc_order            = 12&lt;/span&gt;
&lt;span class="gi"&gt;+obs_initial_ra       = 83.82094&lt;/span&gt;
&lt;span class="gi"&gt;+obs_initial_dec      = -5.39542&lt;/span&gt;
&lt;span class="gi"&gt;+obs_initial_fov      = 0.014314526715905856&lt;/span&gt;
&lt;span class="gi"&gt;+TIMESTAMP            = 1490387811000&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+ID                   = CDS/P/2MASS/H&lt;/span&gt;
&lt;span class="gi"&gt;+creator_did          = ivo://CDS/P/2MASS/H&lt;/span&gt;
&lt;span class="gi"&gt;+obs_collection       = The Two Micron All Sky Survey - H band (2MASS H)&lt;/span&gt;
&lt;span class="gi"&gt;+obs_title            = 2MASS H (1.66 microns)&lt;/span&gt;
&lt;span class="gi"&gt;+obs_description      = 2MASS has uniformly scanned the entire sky in three near-infrared bands to detect and characterize point sources brighter than about 1 mJy in each band, with signal-to-noise ratio (SNR) greater than 10, using a pixel size of 2.0&amp;quot;. This has achieved an 80,000-fold improvement in sensitivity relative to earlier surveys. 2MASS used two highly-automated 1.3-m telescopes, one at Mt. Hopkins, AZ, and one at CTIO, Chile. Each telescope was equipped with a three-channel camera, each channel consisting of a 256x256 array of HgCdTe detectors, capable of observing the sky simultaneously at J (1.25 microns), H (1.65 microns), and Ks (2.17 microns). The University of Massachusetts (UMass) was responsible for the overall management of the project, and for developing the infrared cameras and on-site computing systems at both facilities. The Infrared Processing and Analysis Center (IPAC) is responsible for all data processing through the Production Pipeline, and construction and distribution of the data products. Funding is provided primarily by NASA and the NSF&lt;/span&gt;
&lt;span class="gi"&gt;+obs_copyright_url    = http://www.ipac.caltech.edu/2mass/&lt;/span&gt;
&lt;span class="gi"&gt;+prov_progenitor      = IPAC/NASA&lt;/span&gt;
&lt;span class="gi"&gt;+client_category      = Image/Infrared/2MASS&lt;/span&gt;
&lt;span class="gi"&gt;+client_sort_key      = 04-001-03&lt;/span&gt;
&lt;span class="gi"&gt;+hips_creation_date   = 2013-05-06T20:36Z&lt;/span&gt;
&lt;span class="gi"&gt;+hips_release_date    = 2016-04-22T13:48Z&lt;/span&gt;
&lt;span class="gi"&gt;+hips_builder         = Aladin/HipsGen v9.017&lt;/span&gt;
&lt;span class="gi"&gt;+hips_creator         = CDS (A.Oberto)&lt;/span&gt;
&lt;span class="gi"&gt;+hips_version         = 1.31&lt;/span&gt;
&lt;span class="gi"&gt;+hips_order           = 9&lt;/span&gt;
&lt;span class="gi"&gt;+hips_frame           = equatorial&lt;/span&gt;
&lt;span class="gi"&gt;+hips_tile_width      = 512&lt;/span&gt;
&lt;span class="gi"&gt;+hips_tile_format     = jpeg fits&lt;/span&gt;
&lt;span class="gi"&gt;+dataproduct_type     = image&lt;/span&gt;
&lt;span class="gi"&gt;+hips_pixel_cut       = 0 60&lt;/span&gt;
&lt;span class="gi"&gt;+moc_access_url       = http://alasky.u-strasbg.fr/2MASS/H/Moc.fits&lt;/span&gt;
&lt;span class="gi"&gt;+hips_service_url     = http://alasky.unistra.fr/2MASS/H&lt;/span&gt;
&lt;span class="gi"&gt;+hips_status          = public master clonableOnce&lt;/span&gt;
&lt;span class="gi"&gt;+hips_copyright       = CNRS/Unistra&lt;/span&gt;
&lt;span class="gi"&gt;+obs_ack              = University of Massachusetts &amp;amp; IPAC/Caltech&lt;/span&gt;
&lt;span class="gi"&gt;+bib_reference        = 2006AJ....131.1163S&lt;/span&gt;
&lt;span class="gi"&gt;+bib_reference_url    = http://cdsbib.u-strasbg.fr/cgi-bin/cdsbib?2006AJ....131.1163S&lt;/span&gt;
&lt;span class="gi"&gt;+obs_copyright        = University of Massachusetts &amp;amp; IPAC/Caltech&lt;/span&gt;
&lt;span class="gi"&gt;+t_min                = 50600&lt;/span&gt;
&lt;span class="gi"&gt;+t_max                = 51941&lt;/span&gt;
&lt;span class="gi"&gt;+obs_regime           = Infrared&lt;/span&gt;
&lt;span class="gi"&gt;+em_min               = 1.525E-6&lt;/span&gt;
&lt;span class="gi"&gt;+em_max               = 1.798E-6&lt;/span&gt;
&lt;span class="gi"&gt;+hips_hierarchy       = mean&lt;/span&gt;
&lt;span class="gi"&gt;+hips_pixel_scale     = 2.236E-4&lt;/span&gt;
&lt;span class="gi"&gt;+hips_service_url_1   = http://alaskybis.unistra.fr/2MASS/H&lt;/span&gt;
&lt;span class="gi"&gt;+hips_status_1        = public mirror clonableOnce&lt;/span&gt;
&lt;span class="gi"&gt;+hips_service_url_2   = https://alaskybis.unistra.fr/2MASS/H&lt;/span&gt;
&lt;span class="gi"&gt;+hips_status_2        = public mirror clonableOnce&lt;/span&gt;
&lt;span class="gi"&gt;+moc_sky_fraction     = 1&lt;/span&gt;
&lt;span class="gi"&gt;+moc_order            = 9&lt;/span&gt;
&lt;span class="gi"&gt;+obs_initial_ra       = 0&lt;/span&gt;
&lt;span class="gi"&gt;+obs_initial_dec      = +0&lt;/span&gt;
&lt;span class="gi"&gt;+obs_initial_fov      = 0.11451621372724685&lt;/span&gt;
&lt;span class="gi"&gt;+client_application   = AladinDesktop&lt;/span&gt;
&lt;span class="gi"&gt;+TIMESTAMP            = 1490028262000&lt;/span&gt;
&lt;span class="gi"&gt;+# a test comment&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+ID                   = CDS/P/2MASS/J&lt;/span&gt;
&lt;span class="gi"&gt;+creator_did          = ivo://CDS/P/2MASS/J&lt;/span&gt;
&lt;span class="gi"&gt;+obs_collection       = The Two Micron All Sky Survey - J band (2MASS J)&lt;/span&gt;
&lt;span class="gi"&gt;+obs_title            = 2MASS J (1.23 microns)&lt;/span&gt;
&lt;span class="gi"&gt;+obs_description      = 2MASS has uniformly scanned the entire sky in three near-infrared bands to detect and characterize point sources brighter than about 1 mJy in each band, with signal-to-noise ratio (SNR) greater than 10, using a pixel size of 2.0&amp;quot;. This has achieved an 80,000-fold improvement in sensitivity relative to earlier surveys. 2MASS used two highly-automated 1.3-m telescopes, one at Mt. Hopkins, AZ, and one at CTIO, Chile. Each telescope was equipped with a three-channel camera, each channel consisting of a 256x256 array of HgCdTe detectors, capable of observing the sky simultaneously at J (1.25 microns), H (1.65 microns), and Ks (2.17 microns). The University of Massachusetts (UMass) was responsible for the overall management of the project, and for developing the infrared cameras and on-site computing systems at both facilities. The Infrared Processing and Analysis Center (IPAC) is responsible for all data processing through the Production Pipeline, and construction and distribution of the data products. Funding is provided primarily by NASA and the NSF&lt;/span&gt;
&lt;span class="gi"&gt;+obs_copyright_url    = http://www.ipac.caltech.edu/2mass/&lt;/span&gt;
&lt;span class="gi"&gt;+prov_progenitor      = IPAC/NASA&lt;/span&gt;
&lt;span class="gi"&gt;+client_category      = Image/Infrared/2MASS&lt;/span&gt;
&lt;span class="gi"&gt;+client_sort_key      = 04-001-02&lt;/span&gt;
&lt;span class="gi"&gt;+hips_creation_date   = 2014-02-11T11:28Z&lt;/span&gt;
&lt;span class="gi"&gt;+hips_release_date    = 2016-04-22T14:03Z&lt;/span&gt;
&lt;span class="gi"&gt;+hips_builder         = Aladin/HipsGen v9.017&lt;/span&gt;
&lt;span class="gi"&gt;+hips_creator         = CDS (A.Oberto)&lt;/span&gt;
&lt;span class="gi"&gt;+hips_version         = 1.31&lt;/span&gt;
&lt;span class="gi"&gt;+hips_order           = 9&lt;/span&gt;
&lt;span class="gi"&gt;+hips_frame           = equatorial&lt;/span&gt;
&lt;span class="gi"&gt;+hips_tile_width      = 512&lt;/span&gt;
&lt;span class="gi"&gt;+hips_tile_format     = jpeg fits&lt;/span&gt;
&lt;span class="gi"&gt;+dataproduct_type     = image&lt;/span&gt;
&lt;span class="gi"&gt;+hips_pixel_cut       = -1 116&lt;/span&gt;
&lt;span class="gi"&gt;+moc_access_url       = http://alasky.u-strasbg.fr/2MASS/J/Moc.fits&lt;/span&gt;
&lt;span class="gi"&gt;+hips_service_url     = http://alasky.unistra.fr/2MASS/J&lt;/span&gt;
&lt;span class="gi"&gt;+hips_status          = public master clonableOnce&lt;/span&gt;
&lt;span class="gi"&gt;+hips_copyright       = CNRS/Unistra&lt;/span&gt;
&lt;span class="gi"&gt;+obs_ack              = University of Massachusetts &amp;amp; IPAC/Caltech&lt;/span&gt;
&lt;span class="gi"&gt;+bib_reference        = 2006AJ....131.1163S&lt;/span&gt;
&lt;span class="gi"&gt;+bib_reference_url    = http://cdsbib.u-strasbg.fr/cgi-bin/cdsbib?2006AJ....131.1163S&lt;/span&gt;
&lt;span class="gi"&gt;+obs_copyright        = University of Massachusetts &amp;amp; IPAC/Caltech&lt;/span&gt;
&lt;span class="gi"&gt;+t_min                = 50600&lt;/span&gt;
&lt;span class="gi"&gt;+t_max                = 51941&lt;/span&gt;
&lt;span class="gi"&gt;+obs_regime           = Infrared&lt;/span&gt;
&lt;span class="gi"&gt;+em_min               = 1.147E-6&lt;/span&gt;
&lt;span class="gi"&gt;+em_max               = 1.323E-6&lt;/span&gt;
&lt;span class="gi"&gt;+hips_hierarchy       = mean&lt;/span&gt;
&lt;span class="gi"&gt;+hips_pixel_scale     = 2.236E-4&lt;/span&gt;
&lt;span class="gi"&gt;+hips_progenitor_url  = http://alasky.unistra.fr/2MASS/J/HpxFinder&lt;/span&gt;
&lt;span class="gi"&gt;+hips_service_url_1   = http://alaskybis.unistra.fr/2MASS/J&lt;/span&gt;
&lt;span class="gi"&gt;+hips_status_1        = public mirror clonableOnce&lt;/span&gt;
&lt;span class="gi"&gt;+hips_service_url_2   = https://alaskybis.unistra.fr/2MASS/J&lt;/span&gt;
&lt;span class="gi"&gt;+hips_status_2        = public mirror clonableOnce&lt;/span&gt;
&lt;span class="gi"&gt;+moc_sky_fraction     = 1&lt;/span&gt;
&lt;span class="gi"&gt;+moc_order            = 29&lt;/span&gt;
&lt;span class="gi"&gt;+obs_initial_ra       = 0&lt;/span&gt;
&lt;span class="gi"&gt;+obs_initial_dec      = +0&lt;/span&gt;
&lt;span class="gi"&gt;+obs_initial_fov      = 1.0921117184376416E-7&lt;/span&gt;
&lt;span class="gi"&gt;+client_application   = AladinDesktop&lt;/span&gt;
&lt;span class="gi"&gt;+TIMESTAMP            = 1491570319000&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+ID                   = CDS/P/2MASS/K&lt;/span&gt;
&lt;span class="gi"&gt;+creator_did          = ivo://CDS/P/2MASS/K&lt;/span&gt;
&lt;span class="gi"&gt;+obs_collection       = The Two Micron All Sky Survey - K band (2MASS K)&lt;/span&gt;
&lt;span class="gi"&gt;+obs_title            = 2MASS K (2.16 microns)&lt;/span&gt;
&lt;span class="gi"&gt;+obs_description      = 2MASS has uniformly scanned the entire sky in three near-infrared bands to detect and characterize point sources brighter than about 1 mJy in each band, with signal-to-noise ratio (SNR) greater than 10, using a pixel size of 2.0&amp;quot;. This has achieved an 80,000-fold improvement in sensitivity relative to earlier surveys. 2MASS used two highly-automated 1.3-m telescopes, one at Mt. Hopkins, AZ, and one at CTIO, Chile. Each telescope was equipped with a three-channel camera, each channel consisting of a 256x256 array of HgCdTe detectors, capable of observing the sky simultaneously at J (1.25 microns), H (1.65 microns), and Ks (2.17 microns). The University of Massachusetts (UMass) was responsible for the overall management of the project, and for developing the infrared cameras and on-site computing systems at both facilities. The Infrared Processing and Analysis Center (IPAC) is responsible for all data processing through the Production Pipeline, and construction and distribution of the data products. Funding is provided primarily by NASA and the NSF&lt;/span&gt;
&lt;span class="gi"&gt;+obs_copyright_url    = http://www.ipac.caltech.edu/2mass/&lt;/span&gt;
&lt;span class="gi"&gt;+prov_progenitor      = IPAC/NASA&lt;/span&gt;
&lt;span class="gi"&gt;+client_category      = Image/Infrared/2MASS&lt;/span&gt;
&lt;span class="gi"&gt;+client_sort_key      = 04-001-04&lt;/span&gt;
&lt;span class="gi"&gt;+hips_creation_date   = 2013-01-14T09:45Z&lt;/span&gt;
&lt;span class="gi"&gt;+hips_release_date    = 2016-04-22T14:23Z&lt;/span&gt;
&lt;span class="gi"&gt;+hips_builder         = Aladin/HipsGen v9.017&lt;/span&gt;
&lt;span class="gi"&gt;+hips_creator         = CDS (A.Oberto)&lt;/span&gt;
&lt;span class="gi"&gt;+hips_version         = 1.31&lt;/span&gt;
&lt;span class="gi"&gt;+hips_order           = 9&lt;/span&gt;
&lt;span class="gi"&gt;+hips_frame           = equatorial&lt;/span&gt;
&lt;span class="gi"&gt;+hips_tile_width      = 512&lt;/span&gt;
&lt;span class="gi"&gt;+hips_tile_format     = jpeg fits&lt;/span&gt;
&lt;span class="gi"&gt;+dataproduct_type     = image&lt;/span&gt;
&lt;span class="gi"&gt;+hips_pixel_cut       = -2 35&lt;/span&gt;
&lt;span class="gi"&gt;+moc_access_url       = http://alasky.u-strasbg.fr/2MASS/K/Moc.fits&lt;/span&gt;
&lt;span class="gi"&gt;+hips_service_url     = http://alasky.unistra.fr/2MASS/K&lt;/span&gt;
&lt;span class="gi"&gt;+hips_status          = public master clonableOnce&lt;/span&gt;
&lt;span class="gi"&gt;+hips_copyright       = CNRS/Unistra&lt;/span&gt;
&lt;span class="gi"&gt;+obs_ack              = University of Massachusetts &amp;amp; IPAC/Caltech&lt;/span&gt;
&lt;span class="gi"&gt;+bib_reference        = 2006AJ....131.1163S&lt;/span&gt;
&lt;span class="gi"&gt;+bib_reference_url    = http://cdsbib.u-strasbg.fr/cgi-bin/cdsbib?2006AJ....131.1163S&lt;/span&gt;
&lt;span class="gi"&gt;+obs_copyright        = University of Massachusetts &amp;amp; IPAC/Caltech&lt;/span&gt;
&lt;span class="gi"&gt;+t_min                = 50600&lt;/span&gt;
&lt;span class="gi"&gt;+t_max                = 51941&lt;/span&gt;
&lt;span class="gi"&gt;+obs_regime           = Infrared&lt;/span&gt;
&lt;span class="gi"&gt;+em_min               = 2.015E-6&lt;/span&gt;
&lt;span class="gi"&gt;+em_max               = 2.303E-6&lt;/span&gt;
&lt;span class="gi"&gt;+hips_hierarchy       = mean&lt;/span&gt;
&lt;span class="gi"&gt;+hips_pixel_scale     = 2.236E-4&lt;/span&gt;
&lt;span class="gi"&gt;+hips_progenitor_url  = http://alasky.unistra.fr/2MASS/K/HpxFinder&lt;/span&gt;
&lt;span class="gi"&gt;+hips_service_url_1   = http://alaskybis.unistra.fr/2MASS/K&lt;/span&gt;
&lt;span class="gi"&gt;+hips_status_1        = public mirror clonableOnce&lt;/span&gt;
&lt;span class="gi"&gt;+hips_service_url_2   = https://alaskybis.unistra.fr/2MASS/K&lt;/span&gt;
&lt;span class="gi"&gt;+hips_status_2        = public mirror clonableOnce&lt;/span&gt;
&lt;span class="gi"&gt;+moc_sky_fraction     = 1&lt;/span&gt;
&lt;span class="gi"&gt;+moc_order            = 29&lt;/span&gt;
&lt;span class="gi"&gt;+obs_initial_ra       = 0&lt;/span&gt;
&lt;span class="gi"&gt;+obs_initial_dec      = +0&lt;/span&gt;
&lt;span class="gi"&gt;+obs_initial_fov      = 1.0921117184376416E-7&lt;/span&gt;
&lt;span class="gi"&gt;+client_application   = AladinDesktop&lt;/span&gt;
&lt;span class="gi"&gt;+TIMESTAMP            = 1491570319000&lt;/span&gt;
\ No newline at end of file
&lt;span class="gh"&gt;diff --git a/hips/tiles/tests/test_description.py b/hips/tiles/tests/test_description.py&lt;/span&gt;
&lt;span class="gh"&gt;index cd5c24b..b36aa02 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tests/test_description.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tests/test_description.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,28 +1,28 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="w"&gt; &lt;/span&gt;from astropy.utils.data import get_pkg_data_filename
&lt;span class="gd"&gt;-from ..description import HipsDescription&lt;/span&gt;
&lt;span class="gi"&gt;+from ..description import HipsSurveyProperties&lt;/span&gt;


&lt;span class="gd"&gt;-class TestHiPSDescription:&lt;/span&gt;
&lt;span class="gi"&gt;+class TestHipsSurveyProperties:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    @classmethod
&lt;span class="w"&gt; &lt;/span&gt;    def setup_class(cls):
&lt;span class="w"&gt; &lt;/span&gt;        filename = get_pkg_data_filename(&amp;#39;data/properties.txt&amp;#39;)
&lt;span class="gd"&gt;-        cls.hipsdescription = HipsDescription.read(filename)&lt;/span&gt;
&lt;span class="gi"&gt;+        cls.hips_survey_property = HipsSurveyProperties.read(filename)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def test_base_url(self):
&lt;span class="gd"&gt;-        assert self.hipsdescription.base_url == &amp;#39;http://alasky.u-strasbg.fr/DSS/DSSColor&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        assert self.hips_survey_property.base_url == &amp;#39;http://alasky.u-strasbg.fr/DSS/DSSColor&amp;#39;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def test_title(self):
&lt;span class="gd"&gt;-        assert self.hipsdescription.title == &amp;#39;DSS colored&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        assert self.hips_survey_property.title == &amp;#39;DSS colored&amp;#39;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def test_hips_version(self):
&lt;span class="gd"&gt;-        assert self.hipsdescription.hips_version == &amp;#39;1.31&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        assert self.hips_survey_property.hips_version == &amp;#39;1.31&amp;#39;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def test_hips_frame(self):
&lt;span class="gd"&gt;-        assert self.hipsdescription.hips_frame == &amp;#39;equatorial&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        assert self.hips_survey_property.hips_frame == &amp;#39;equatorial&amp;#39;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def test_hips_order(self):
&lt;span class="gd"&gt;-        assert self.hipsdescription.hips_order == 9&lt;/span&gt;
&lt;span class="gi"&gt;+        assert self.hips_survey_property.hips_order == 9&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def test_tile_format(self):
&lt;span class="gd"&gt;-        assert self.hipsdescription.tile_format == &amp;#39;jpeg&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        assert self.hips_survey_property.tile_format == &amp;#39;jpeg&amp;#39;&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/tiles/tests/test_surveys.py b/hips/tiles/tests/test_surveys.py&lt;/span&gt;
new file mode 100644
&lt;span class="gh"&gt;index 0000000..99dd3d4&lt;/span&gt;
&lt;span class="gd"&gt;--- /dev/null&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tests/test_surveys.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -0,0 +1,21 @@&lt;/span&gt;
&lt;span class="gi"&gt;+# Licensed under a 3-clause BSD style license - see LICENSE.rst&lt;/span&gt;
&lt;span class="gi"&gt;+from astropy.utils.data import get_pkg_data_filename&lt;/span&gt;
&lt;span class="gi"&gt;+from astropy.tests.helper import remote_data&lt;/span&gt;
&lt;span class="gi"&gt;+from ..surveys import HipsSurveyPropertiesList&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+class TestHipsSurveyPropertiesList:&lt;/span&gt;
&lt;span class="gi"&gt;+    @classmethod&lt;/span&gt;
&lt;span class="gi"&gt;+    def setup_class(cls):&lt;/span&gt;
&lt;span class="gi"&gt;+        filename = get_pkg_data_filename(&amp;#39;data/surveys.txt&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        cls.hips_survey_list = HipsSurveyPropertiesList.read(filename)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @remote_data&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_fetch(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        self.hips_survey_list.fetch()&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_surveys(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        assert len(self.hips_survey_list.surveys) == 4&lt;/span&gt;
&lt;span class="gi"&gt;+        assert self.hips_survey_list.surveys[0].properties[&amp;#39;creator_did&amp;#39;] == &amp;#39;ivo://CDS/C/MUSE-M42&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        assert self.hips_survey_list.surveys[0].properties[&amp;#39;obs_collection&amp;#39;] == &amp;#39;MUSE-M42&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        assert self.hips_survey_list.surveys[0].properties[&amp;#39;hips_tile_format&amp;#39;] == &amp;#39;png fits&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        assert self.hips_survey_list.table[&amp;#39;surveys&amp;#39;][0][&amp;#39;hips_tile_format&amp;#39;] == &amp;#39;png fits&amp;#39;&lt;/span&gt;

commit 0bdb052115291c0657841c6becf973cec2ff59d6
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Jun 23 09:48:15 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Fix type annotation issue

&lt;span class="gh"&gt;diff --git a/hips/tiles/tests/test_tile.py b/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gh"&gt;index 0764fe7..2fabf49 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -22,11 +22,11 @@ class TestHipsTile:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        assert jpg_tile.data.shape == (512, 512, 3)
&lt;span class="w"&gt; &lt;/span&gt;        assert list(fits_tile.data[510][:12]) == data_precomp

&lt;span class="gd"&gt;-        jpg_tile.write(&amp;#39;Npix30889.jpg&amp;#39;, tmpdir)&lt;/span&gt;
&lt;span class="gd"&gt;-        fits_tile.write(&amp;#39;Npix30889.fits&amp;#39;, tmpdir)&lt;/span&gt;
&lt;span class="gi"&gt;+        jpg_tile.write(&amp;#39;Npix30889.jpg&amp;#39;, str(tmpdir / &amp;#39;Npix30889.jpg&amp;#39;))&lt;/span&gt;
&lt;span class="gi"&gt;+        fits_tile.write(&amp;#39;Npix30889.fits&amp;#39;, str(tmpdir / &amp;#39;Npix30889.fits&amp;#39;))&lt;/span&gt;

&lt;span class="gd"&gt;-        jpg_tile = HipsTile.read(self.hips_tile_meta_jpg, tmpdir)&lt;/span&gt;
&lt;span class="gd"&gt;-        fits_tile = HipsTile.read(self.hips_tile_meta_fits, tmpdir)&lt;/span&gt;
&lt;span class="gi"&gt;+        jpg_tile = HipsTile.read(self.hips_tile_meta_jpg, str(tmpdir / &amp;#39;Npix30889.jpg&amp;#39;))&lt;/span&gt;
&lt;span class="gi"&gt;+        fits_tile = HipsTile.read(self.hips_tile_meta_fits, str(tmpdir / &amp;#39;Npix30889.fits&amp;#39;))&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        data_precomp = [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1]
&lt;span class="w"&gt; &lt;/span&gt;        assert fits_tile.data.shape == (512, 512)
&lt;span class="gh"&gt;diff --git a/hips/tiles/tile.py b/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gh"&gt;index c521df6..a6b846b 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -8,7 +8,7 @@ from astropy.io import fits&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from astropy.io.fits.header import Header

&lt;span class="w"&gt; &lt;/span&gt;from .tile_meta import HipsTileMeta
&lt;span class="gd"&gt;-import py._path.local&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;__all__ = [
&lt;span class="w"&gt; &lt;/span&gt;    &amp;#39;HipsTile&amp;#39;,
&lt;span class="w"&gt; &lt;/span&gt;]
&lt;span class="gu"&gt;@@ -73,18 +73,20 @@ class HipsTile:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            return cls(meta, data)

&lt;span class="w"&gt; &lt;/span&gt;    @classmethod
&lt;span class="gd"&gt;-    def read(cls, meta: HipsTileMeta, tmpdir: py._path.local = None) -&amp;gt; &amp;#39;HipsTile&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+    def read(cls, meta: HipsTileMeta, file_path: str = None) -&amp;gt; &amp;#39;HipsTile&amp;#39;:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Read HiPS tile data from a directory and load into memory (`HipsTile`).

&lt;span class="w"&gt; &lt;/span&gt;        Parameters
&lt;span class="w"&gt; &lt;/span&gt;        ----------
&lt;span class="w"&gt; &lt;/span&gt;        meta : `HipsTileMeta`
&lt;span class="w"&gt; &lt;/span&gt;            Metadata of HiPS tile
&lt;span class="gi"&gt;+        file_path : `str`&lt;/span&gt;
&lt;span class="gi"&gt;+            File path to store a HiPS tile&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-        if tmpdir == None:&lt;/span&gt;
&lt;span class="gd"&gt;-            path = meta.path / meta.filename&lt;/span&gt;
&lt;span class="gi"&gt;+        if file_path is None:&lt;/span&gt;
&lt;span class="gi"&gt;+            path = meta.path / meta.filename  # pragma: no cover&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        else:
&lt;span class="gd"&gt;-            path = tmpdir / meta.filename&lt;/span&gt;
&lt;span class="gi"&gt;+            path = file_path&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        if meta.file_format == &amp;#39;fits&amp;#39;:
&lt;span class="w"&gt; &lt;/span&gt;            hdulist = fits.open(str(path))
&lt;span class="gu"&gt;@@ -95,18 +97,20 @@ class HipsTile:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            data = np.array(Image.open(str(path)))
&lt;span class="w"&gt; &lt;/span&gt;            return cls(meta, data)

&lt;span class="gd"&gt;-    def write(self, filename: str, tmpdir: py._path.local = None) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+    def write(self, filename: str, file_path: str = None) -&amp;gt; None:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Write HiPS tile by a given filename.

&lt;span class="w"&gt; &lt;/span&gt;        Parameters
&lt;span class="w"&gt; &lt;/span&gt;        ----------
&lt;span class="w"&gt; &lt;/span&gt;        filename : `str`
&lt;span class="w"&gt; &lt;/span&gt;            Name of the file
&lt;span class="gi"&gt;+        file_path : `str`&lt;/span&gt;
&lt;span class="gi"&gt;+            File path to store a HiPS tile&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-        if tmpdir == None:&lt;/span&gt;
&lt;span class="gd"&gt;-            path = self.meta.path / self.meta.filename&lt;/span&gt;
&lt;span class="gi"&gt;+        if file_path is None:&lt;/span&gt;
&lt;span class="gi"&gt;+            path = self.meta.path / self.meta.filename  # pragma: no cover&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        else:
&lt;span class="gd"&gt;-            path = tmpdir / self.meta.filename&lt;/span&gt;
&lt;span class="gi"&gt;+            path = file_path&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        if self.meta.file_format == &amp;#39;fits&amp;#39;:
&lt;span class="w"&gt; &lt;/span&gt;            hdu = fits.PrimaryHDU(self.data, header=self.header).writeto(str(path))
&lt;span class="gh"&gt;diff --git a/hips/tiles/tile_meta.py b/hips/tiles/tile_meta.py&lt;/span&gt;
&lt;span class="gh"&gt;index 3789609..0461b7d 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tile_meta.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tile_meta.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -30,11 +30,11 @@ class HipsTileMeta:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        self.tile_width = tile_width

&lt;span class="w"&gt; &lt;/span&gt;    @property
&lt;span class="gd"&gt;-    def path(self) -&amp;gt; str:&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;Return the default path for tile storage (`str`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+    def path(self) -&amp;gt; Path:  # pragma: no cover&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Return the default path for tile storage (`Path`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        return Path(&amp;#39;hips&amp;#39;, &amp;#39;tiles&amp;#39;, &amp;#39;tests&amp;#39;, &amp;#39;data&amp;#39;)

&lt;span class="w"&gt; &lt;/span&gt;    @property
&lt;span class="gd"&gt;-    def filename(self) -&amp;gt; str:&lt;/span&gt;
&lt;span class="gi"&gt;+    def filename(self) -&amp;gt; str:  # pragma: no cover&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Return the filename of HiPS tile (`str`).&amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;        return &amp;#39;&amp;#39;.join([&amp;#39;Npix&amp;#39;, str(self.ipix), &amp;#39;.&amp;#39;, self.file_format])

commit 1136490773612a0601b06b5c8a644d728f0f659b
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Thu Jun 22 18:52:50 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Update tiles.hips module

&lt;span class="gh"&gt;diff --git a/hips/tiles/__init__.py b/hips/tiles/__init__.py&lt;/span&gt;
&lt;span class="gh"&gt;index f6101be..2adbd76 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/__init__.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/__init__.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,4 +1,5 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="w"&gt; &lt;/span&gt;&amp;quot;&amp;quot;&amp;quot;Classes and functions to manage HiPS tiles.&amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;from .tile import *
&lt;span class="gi"&gt;+from .tile_meta import *&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from .description import *
&lt;span class="gh"&gt;diff --git a/hips/tiles/tests/test_tile.py b/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gh"&gt;index f27f1b9..0764fe7 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -7,11 +7,11 @@ from ..tile_meta import HipsTileMeta&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;class TestHipsTile:
&lt;span class="w"&gt; &lt;/span&gt;    def setup(self):
&lt;span class="gd"&gt;-        self.hips_tile_meta_fits = HipsTileMeta(order=6, ipix=30889, format=&amp;#39;fits&amp;#39;)&lt;/span&gt;
&lt;span class="gd"&gt;-        self.hips_tile_meta_jpg = HipsTileMeta(order=6, ipix=30889, format=&amp;#39;jpg&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        self.hips_tile_meta_fits = HipsTileMeta(order=6, ipix=30889, file_format=&amp;#39;fits&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        self.hips_tile_meta_jpg = HipsTileMeta(order=6, ipix=30889, file_format=&amp;#39;jpg&amp;#39;)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    @remote_data
&lt;span class="gd"&gt;-    def test_fetch_read_write(self):&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_fetch_read_write(self, tmpdir):&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        fits_tile = HipsTile.fetch(self.hips_tile_meta_fits,
&lt;span class="w"&gt; &lt;/span&gt;                                   &amp;#39;http://alasky.unistra.fr/2MASS/H/Norder6/Dir30000/Npix30889.fits&amp;#39;)
&lt;span class="w"&gt; &lt;/span&gt;        jpg_tile = HipsTile.fetch(self.hips_tile_meta_jpg,
&lt;span class="gu"&gt;@@ -22,11 +22,11 @@ class TestHipsTile:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        assert jpg_tile.data.shape == (512, 512, 3)
&lt;span class="w"&gt; &lt;/span&gt;        assert list(fits_tile.data[510][:12]) == data_precomp

&lt;span class="gd"&gt;-        jpg_tile.write(&amp;#39;Npix30889.jpg&amp;#39;)&lt;/span&gt;
&lt;span class="gd"&gt;-        fits_tile.write(&amp;#39;Npix30889.fits&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        jpg_tile.write(&amp;#39;Npix30889.jpg&amp;#39;, tmpdir)&lt;/span&gt;
&lt;span class="gi"&gt;+        fits_tile.write(&amp;#39;Npix30889.fits&amp;#39;, tmpdir)&lt;/span&gt;

&lt;span class="gd"&gt;-        fits_tile = HipsTile.read(self.hips_tile_meta_fits)&lt;/span&gt;
&lt;span class="gd"&gt;-        jpg_tile = HipsTile.read(self.hips_tile_meta_jpg)&lt;/span&gt;
&lt;span class="gi"&gt;+        jpg_tile = HipsTile.read(self.hips_tile_meta_jpg, tmpdir)&lt;/span&gt;
&lt;span class="gi"&gt;+        fits_tile = HipsTile.read(self.hips_tile_meta_fits, tmpdir)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        data_precomp = [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1]
&lt;span class="w"&gt; &lt;/span&gt;        assert fits_tile.data.shape == (512, 512)
&lt;span class="gh"&gt;diff --git a/hips/tiles/tile.py b/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gh"&gt;index bf8ae51..c521df6 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -8,7 +8,7 @@ from astropy.io import fits&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from astropy.io.fits.header import Header

&lt;span class="w"&gt; &lt;/span&gt;from .tile_meta import HipsTileMeta
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gi"&gt;+import py._path.local&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;__all__ = [
&lt;span class="w"&gt; &lt;/span&gt;    &amp;#39;HipsTile&amp;#39;,
&lt;span class="w"&gt; &lt;/span&gt;]
&lt;span class="gu"&gt;@@ -48,7 +48,6 @@ class HipsTile:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def __init__(self, meta: HipsTileMeta, data: np.ndarray = None, header: Header = None) -&amp;gt; None:
&lt;span class="w"&gt; &lt;/span&gt;        self.meta = meta
&lt;span class="gd"&gt;-        self.format = format&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        self.data = data
&lt;span class="w"&gt; &lt;/span&gt;        self.header = header

&lt;span class="gu"&gt;@@ -64,7 +63,7 @@ class HipsTile:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            URL containing HiPS tile
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;        raw_image = BytesIO(urllib.request.urlopen(url).read())
&lt;span class="gd"&gt;-        if meta.format == &amp;#39;fits&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+        if meta.file_format == &amp;#39;fits&amp;#39;:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            hdulist = fits.open(raw_image)
&lt;span class="w"&gt; &lt;/span&gt;            data = np.array(hdulist[0].data)
&lt;span class="w"&gt; &lt;/span&gt;            header = hdulist[0].header
&lt;span class="gu"&gt;@@ -74,7 +73,7 @@ class HipsTile:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            return cls(meta, data)

&lt;span class="w"&gt; &lt;/span&gt;    @classmethod
&lt;span class="gd"&gt;-    def read(cls, meta: HipsTileMeta) -&amp;gt; &amp;#39;HipsTile&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+    def read(cls, meta: HipsTileMeta, tmpdir: py._path.local = None) -&amp;gt; &amp;#39;HipsTile&amp;#39;:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Read HiPS tile data from a directory and load into memory (`HipsTile`).

&lt;span class="w"&gt; &lt;/span&gt;        Parameters
&lt;span class="gu"&gt;@@ -82,9 +81,13 @@ class HipsTile:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        meta : `HipsTileMeta`
&lt;span class="w"&gt; &lt;/span&gt;            Metadata of HiPS tile
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-        path = meta.path / meta.filename&lt;/span&gt;
&lt;span class="gd"&gt;-        if meta.format == &amp;#39;fits&amp;#39;:&lt;/span&gt;
&lt;span class="gd"&gt;-            hdulist = fits.open(path)&lt;/span&gt;
&lt;span class="gi"&gt;+        if tmpdir == None:&lt;/span&gt;
&lt;span class="gi"&gt;+            path = meta.path / meta.filename&lt;/span&gt;
&lt;span class="gi"&gt;+        else:&lt;/span&gt;
&lt;span class="gi"&gt;+            path = tmpdir / meta.filename&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        if meta.file_format == &amp;#39;fits&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+            hdulist = fits.open(str(path))&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            data = np.array(hdulist[0].data)
&lt;span class="w"&gt; &lt;/span&gt;            header = hdulist[0].header
&lt;span class="w"&gt; &lt;/span&gt;            return cls(meta, data, header)
&lt;span class="gu"&gt;@@ -92,7 +95,7 @@ class HipsTile:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            data = np.array(Image.open(str(path)))
&lt;span class="w"&gt; &lt;/span&gt;            return cls(meta, data)

&lt;span class="gd"&gt;-    def write(self, filename: str) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+    def write(self, filename: str, tmpdir: py._path.local = None) -&amp;gt; None:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Write HiPS tile by a given filename.

&lt;span class="w"&gt; &lt;/span&gt;        Parameters
&lt;span class="gu"&gt;@@ -100,8 +103,12 @@ class HipsTile:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        filename : `str`
&lt;span class="w"&gt; &lt;/span&gt;            Name of the file
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-        path = self.meta.path / self.meta.filename&lt;/span&gt;
&lt;span class="gd"&gt;-        if self.meta.format == &amp;#39;fits&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+        if tmpdir == None:&lt;/span&gt;
&lt;span class="gi"&gt;+            path = self.meta.path / self.meta.filename&lt;/span&gt;
&lt;span class="gi"&gt;+        else:&lt;/span&gt;
&lt;span class="gi"&gt;+            path = tmpdir / self.meta.filename&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        if self.meta.file_format == &amp;#39;fits&amp;#39;:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            hdu = fits.PrimaryHDU(self.data, header=self.header).writeto(str(path))
&lt;span class="w"&gt; &lt;/span&gt;        else:
&lt;span class="w"&gt; &lt;/span&gt;            Image.fromarray(self.data).save(str(path))
&lt;span class="gh"&gt;diff --git a/hips/tiles/tile_meta.py b/hips/tiles/tile_meta.py&lt;/span&gt;
&lt;span class="gh"&gt;index 50ed011..3789609 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tile_meta.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tile_meta.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -23,10 +23,10 @@ class HipsTileMeta:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        HiPS tile width
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;

&lt;span class="gd"&gt;-    def __init__(self, order: int, ipix: int, format: str, tile_width: int = 512) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gd"&gt;-        self.order = format&lt;/span&gt;
&lt;span class="gi"&gt;+    def __init__(self, order: int, ipix: int, file_format: str, tile_width: int = 512) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+        self.order = order&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        self.ipix = ipix
&lt;span class="gd"&gt;-        self.format = format&lt;/span&gt;
&lt;span class="gi"&gt;+        self.file_format = file_format&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        self.tile_width = tile_width

&lt;span class="w"&gt; &lt;/span&gt;    @property
&lt;span class="gu"&gt;@@ -37,4 +37,4 @@ class HipsTileMeta:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    @property
&lt;span class="w"&gt; &lt;/span&gt;    def filename(self) -&amp;gt; str:
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Return the filename of HiPS tile (`str`).&amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-        return &amp;#39;&amp;#39;.join([&amp;#39;Npix&amp;#39;, str(self.ipix), &amp;#39;.&amp;#39;, self.format])&lt;/span&gt;
&lt;span class="gi"&gt;+        return &amp;#39;&amp;#39;.join([&amp;#39;Npix&amp;#39;, str(self.ipix), &amp;#39;.&amp;#39;, self.file_format])&lt;/span&gt;

commit 5db3ef2ae3c3958ae86906e840c4bc4793845adf
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Thu Jun 22 09:28:31 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Update tiles.hips module

&lt;span class="gh"&gt;diff --git a/hips/tiles/tests/test_tile.py b/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gh"&gt;index a3a3397..f27f1b9 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -25,8 +25,8 @@ class TestHipsTile:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        jpg_tile.write(&amp;#39;Npix30889.jpg&amp;#39;)
&lt;span class="w"&gt; &lt;/span&gt;        fits_tile.write(&amp;#39;Npix30889.fits&amp;#39;)

&lt;span class="gd"&gt;-        fits_tile = HipsTile.read(self.hips_tile_meta_fits, &amp;#39;Npix30889.fits&amp;#39;)&lt;/span&gt;
&lt;span class="gd"&gt;-        jpg_tile = HipsTile.read(self.hips_tile_meta_jpg, &amp;#39;Npix30889.jpg&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        fits_tile = HipsTile.read(self.hips_tile_meta_fits)&lt;/span&gt;
&lt;span class="gi"&gt;+        jpg_tile = HipsTile.read(self.hips_tile_meta_jpg)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        data_precomp = [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1]
&lt;span class="w"&gt; &lt;/span&gt;        assert fits_tile.data.shape == (512, 512)
&lt;span class="gh"&gt;diff --git a/hips/tiles/tile.py b/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gh"&gt;index b822d69..bf8ae51 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -8,7 +8,6 @@ from astropy.io import fits&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from astropy.io.fits.header import Header

&lt;span class="w"&gt; &lt;/span&gt;from .tile_meta import HipsTileMeta
&lt;span class="gd"&gt;-from ..utils.tile import tile_path&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;__all__ = [
&lt;span class="w"&gt; &lt;/span&gt;    &amp;#39;HipsTile&amp;#39;,
&lt;span class="gu"&gt;@@ -28,15 +27,15 @@ class HipsTile:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        Metadata of HiPS tile
&lt;span class="w"&gt; &lt;/span&gt;    data : `~numpy.ndarray`
&lt;span class="w"&gt; &lt;/span&gt;        Data containing HiPS tile
&lt;span class="gd"&gt;-    header : `astropy.io.fits.Header`&lt;/span&gt;
&lt;span class="gi"&gt;+    header : `~astropy.io.fits.Header`&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        Header of HiPS tile

&lt;span class="w"&gt; &lt;/span&gt;    Examples
&lt;span class="w"&gt; &lt;/span&gt;    --------
&lt;span class="w"&gt; &lt;/span&gt;        &amp;gt;&amp;gt;&amp;gt; from hips.tiles import HipsTile
&lt;span class="w"&gt; &lt;/span&gt;        &amp;gt;&amp;gt;&amp;gt; from hips.tiles import HipsTileMeta
&lt;span class="gd"&gt;-        &amp;gt;&amp;gt;&amp;gt; hips_tile_met = HipsTileMeta(order=6, ipix=30889, format=&amp;#39;fits&amp;#39;)&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;gt;&amp;gt;&amp;gt; hips_tile = HipsTile.read(meta, &amp;#39;Npix30889.fits&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; meta = HipsTileMeta(order=6, ipix=30889, format=&amp;#39;fits&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; hips_tile = HipsTile.read(meta)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;gt;&amp;gt;&amp;gt; hips_tile.data
&lt;span class="w"&gt; &lt;/span&gt;        array([[0, 0, 0, ..., 0, 0, 0],
&lt;span class="w"&gt; &lt;/span&gt;               [0, 0, 0, ..., 0, 0, 0],
&lt;span class="gu"&gt;@@ -75,17 +74,15 @@ class HipsTile:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            return cls(meta, data)

&lt;span class="w"&gt; &lt;/span&gt;    @classmethod
&lt;span class="gd"&gt;-    def read(cls, meta: HipsTileMeta, filename: str) -&amp;gt; &amp;#39;HipsTile&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+    def read(cls, meta: HipsTileMeta) -&amp;gt; &amp;#39;HipsTile&amp;#39;:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Read HiPS tile data from a directory and load into memory (`HipsTile`).

&lt;span class="w"&gt; &lt;/span&gt;        Parameters
&lt;span class="w"&gt; &lt;/span&gt;        ----------
&lt;span class="w"&gt; &lt;/span&gt;        meta : `HipsTileMeta`
&lt;span class="w"&gt; &lt;/span&gt;            Metadata of HiPS tile
&lt;span class="gd"&gt;-        filename : `str`&lt;/span&gt;
&lt;span class="gd"&gt;-            File name of HiPS tile&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-        path = tile_path().absolute() / filename&lt;/span&gt;
&lt;span class="gi"&gt;+        path = meta.path / meta.filename&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        if meta.format == &amp;#39;fits&amp;#39;:
&lt;span class="w"&gt; &lt;/span&gt;            hdulist = fits.open(path)
&lt;span class="w"&gt; &lt;/span&gt;            data = np.array(hdulist[0].data)
&lt;span class="gu"&gt;@@ -103,7 +100,7 @@ class HipsTile:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        filename : `str`
&lt;span class="w"&gt; &lt;/span&gt;            Name of the file
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-        path = tile_path().absolute() / filename&lt;/span&gt;
&lt;span class="gi"&gt;+        path = self.meta.path / self.meta.filename&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        if self.meta.format == &amp;#39;fits&amp;#39;:
&lt;span class="w"&gt; &lt;/span&gt;            hdu = fits.PrimaryHDU(self.data, header=self.header).writeto(str(path))
&lt;span class="w"&gt; &lt;/span&gt;        else:
&lt;span class="gh"&gt;diff --git a/hips/tiles/tile_meta.py b/hips/tiles/tile_meta.py&lt;/span&gt;
&lt;span class="gh"&gt;index 8ac403b..50ed011 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tile_meta.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tile_meta.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,5 +1,7 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst

&lt;span class="gi"&gt;+from pathlib import Path&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;__all__ = [
&lt;span class="w"&gt; &lt;/span&gt;    &amp;#39;HipsTileMeta&amp;#39;,
&lt;span class="w"&gt; &lt;/span&gt;]
&lt;span class="gu"&gt;@@ -26,3 +28,13 @@ class HipsTileMeta:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        self.ipix = ipix
&lt;span class="w"&gt; &lt;/span&gt;        self.format = format
&lt;span class="w"&gt; &lt;/span&gt;        self.tile_width = tile_width
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @property&lt;/span&gt;
&lt;span class="gi"&gt;+    def path(self) -&amp;gt; str:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Return the default path for tile storage (`str`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        return Path(&amp;#39;hips&amp;#39;, &amp;#39;tiles&amp;#39;, &amp;#39;tests&amp;#39;, &amp;#39;data&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @property&lt;/span&gt;
&lt;span class="gi"&gt;+    def filename(self) -&amp;gt; str:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Return the filename of HiPS tile (`str`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        return &amp;#39;&amp;#39;.join([&amp;#39;Npix&amp;#39;, str(self.ipix), &amp;#39;.&amp;#39;, self.format])&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/utils/tile.py b/hips/utils/tile.py&lt;/span&gt;
deleted file mode 100644
&lt;span class="gh"&gt;index c3937ea..0000000&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/tile.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ /dev/null&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,16 +0,0 @@&lt;/span&gt;
&lt;span class="gd"&gt;-# Licensed under a 3-clause BSD style license - see LICENSE.rst&lt;/span&gt;
&lt;span class="gd"&gt;-&amp;quot;&amp;quot;&amp;quot;HiPS tile utility functions.&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-from pathlib import Path&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-__all__ = [&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;#39;tile_path&amp;#39;,&lt;/span&gt;
&lt;span class="gd"&gt;-]&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-def tile_path() -&amp;gt; Path:&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;quot;&amp;quot;&amp;quot;Return default path of HiPS tile storage.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-    path = Path(&amp;#39;hips&amp;#39;, &amp;#39;tiles&amp;#39;, &amp;#39;tests&amp;#39;, &amp;#39;data&amp;#39;)&lt;/span&gt;
&lt;span class="gd"&gt;-    return path&lt;/span&gt;

commit 9d2ef083c0dc9b73fb00e4228daf3179ca482348
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Wed Jun 21 23:20:30 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Refactor some code

&lt;span class="gh"&gt;diff --git a/hips/tiles/tile.py b/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gh"&gt;index efd0bb9..b822d69 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -24,23 +24,19 @@ class HipsTile:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    Parameters
&lt;span class="w"&gt; &lt;/span&gt;    ----------
&lt;span class="gd"&gt;-    hips_tile_meta : `HipsTileMeta`&lt;/span&gt;
&lt;span class="gi"&gt;+    meta : `HipsTileMeta`&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        Metadata of HiPS tile
&lt;span class="gd"&gt;-    format : `str`&lt;/span&gt;
&lt;span class="gd"&gt;-        Format of HiPS tile&lt;/span&gt;
&lt;span class="gd"&gt;-    data : `np.ndarray`&lt;/span&gt;
&lt;span class="gi"&gt;+    data : `~numpy.ndarray`&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        Data containing HiPS tile
&lt;span class="w"&gt; &lt;/span&gt;    header : `astropy.io.fits.Header`
&lt;span class="w"&gt; &lt;/span&gt;        Header of HiPS tile

&lt;span class="w"&gt; &lt;/span&gt;    Examples
&lt;span class="w"&gt; &lt;/span&gt;    --------
&lt;span class="gd"&gt;-        &amp;gt;&amp;gt;&amp;gt; import urllib.request&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;gt;&amp;gt;&amp;gt; from hips.tiles import HipsTile
&lt;span class="w"&gt; &lt;/span&gt;        &amp;gt;&amp;gt;&amp;gt; from hips.tiles import HipsTileMeta
&lt;span class="gd"&gt;-        &amp;gt;&amp;gt;&amp;gt; from astropy.tests.helper import remote_data&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;gt;&amp;gt;&amp;gt; hips_tile_metadata = HipsTileMeta(order=6, ipix=30889, format=&amp;#39;fits&amp;#39;)&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;gt;&amp;gt;&amp;gt; hips_tile = HipsTile.read(hips_tile_metadata, &amp;#39;Npix30889.fits&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; hips_tile_met = HipsTileMeta(order=6, ipix=30889, format=&amp;#39;fits&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; hips_tile = HipsTile.read(meta, &amp;#39;Npix30889.fits&amp;#39;)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;gt;&amp;gt;&amp;gt; hips_tile.data
&lt;span class="w"&gt; &lt;/span&gt;        array([[0, 0, 0, ..., 0, 0, 0],
&lt;span class="w"&gt; &lt;/span&gt;               [0, 0, 0, ..., 0, 0, 0],
&lt;span class="gu"&gt;@@ -51,56 +47,56 @@ class HipsTile:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;               [0, 0, 0, ..., 1, 0, 1]], dtype=int16)
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;

&lt;span class="gd"&gt;-    def __init__(self, hips_tile_meta: HipsTileMeta, data: np.ndarray = None, header: Header = None) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gd"&gt;-        self.hips_tile_meta = hips_tile_meta&lt;/span&gt;
&lt;span class="gi"&gt;+    def __init__(self, meta: HipsTileMeta, data: np.ndarray = None, header: Header = None) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+        self.meta = meta&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        self.format = format
&lt;span class="w"&gt; &lt;/span&gt;        self.data = data
&lt;span class="w"&gt; &lt;/span&gt;        self.header = header

&lt;span class="w"&gt; &lt;/span&gt;    @classmethod
&lt;span class="gd"&gt;-    def fetch(cls, hips_tile_meta: HipsTileMeta, url: str) -&amp;gt; &amp;#39;HipsTile&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+    def fetch(cls, meta: HipsTileMeta, url: str) -&amp;gt; &amp;#39;HipsTile&amp;#39;:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Fetch HiPS tile and load into memory (`HipsTile`).

&lt;span class="w"&gt; &lt;/span&gt;        Parameters
&lt;span class="w"&gt; &lt;/span&gt;        ----------
&lt;span class="gd"&gt;-        hips_tile_meta : `HipsTileMeta`&lt;/span&gt;
&lt;span class="gi"&gt;+        meta : `HipsTileMeta`&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            Metadata of HiPS tile
&lt;span class="w"&gt; &lt;/span&gt;        url : `str`
&lt;span class="w"&gt; &lt;/span&gt;            URL containing HiPS tile
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;        raw_image = BytesIO(urllib.request.urlopen(url).read())
&lt;span class="gd"&gt;-        if hips_tile_meta.format == &amp;#39;fits&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+        if meta.format == &amp;#39;fits&amp;#39;:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            hdulist = fits.open(raw_image)
&lt;span class="w"&gt; &lt;/span&gt;            data = np.array(hdulist[0].data)
&lt;span class="w"&gt; &lt;/span&gt;            header = hdulist[0].header
&lt;span class="gd"&gt;-            return cls(hips_tile_meta, data, header)&lt;/span&gt;
&lt;span class="gi"&gt;+            return cls(meta, data, header)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        else:
&lt;span class="w"&gt; &lt;/span&gt;            data = np.array(Image.open(raw_image))
&lt;span class="gd"&gt;-            return cls(hips_tile_meta, data)&lt;/span&gt;
&lt;span class="gi"&gt;+            return cls(meta, data)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    @classmethod
&lt;span class="gd"&gt;-    def read(cls, hips_tile_meta: HipsTileMeta, filename: str) -&amp;gt; &amp;#39;HipsTile&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+    def read(cls, meta: HipsTileMeta, filename: str) -&amp;gt; &amp;#39;HipsTile&amp;#39;:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Read HiPS tile data from a directory and load into memory (`HipsTile`).

&lt;span class="w"&gt; &lt;/span&gt;        Parameters
&lt;span class="w"&gt; &lt;/span&gt;        ----------
&lt;span class="gd"&gt;-        hips_tile_meta : `HipsTileMeta`&lt;/span&gt;
&lt;span class="gi"&gt;+        meta : `HipsTileMeta`&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            Metadata of HiPS tile
&lt;span class="w"&gt; &lt;/span&gt;        filename : `str`
&lt;span class="w"&gt; &lt;/span&gt;            File name of HiPS tile
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;        path = tile_path().absolute() / filename
&lt;span class="gd"&gt;-        if hips_tile_meta.format == &amp;#39;fits&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+        if meta.format == &amp;#39;fits&amp;#39;:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            hdulist = fits.open(path)
&lt;span class="w"&gt; &lt;/span&gt;            data = np.array(hdulist[0].data)
&lt;span class="w"&gt; &lt;/span&gt;            header = hdulist[0].header
&lt;span class="gd"&gt;-            return cls(hips_tile_meta, data, header)&lt;/span&gt;
&lt;span class="gi"&gt;+            return cls(meta, data, header)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        else:
&lt;span class="w"&gt; &lt;/span&gt;            data = np.array(Image.open(str(path)))
&lt;span class="gd"&gt;-            return cls(hips_tile_meta, data)&lt;/span&gt;
&lt;span class="gi"&gt;+            return cls(meta, data)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def write(self, filename: str) -&amp;gt; None:
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;Write HiPS tile by a given filename (`None`).&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Write HiPS tile by a given filename.&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        Parameters
&lt;span class="w"&gt; &lt;/span&gt;        ----------
&lt;span class="gu"&gt;@@ -108,10 +104,7 @@ class HipsTile:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            Name of the file
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;        path = tile_path().absolute() / filename
&lt;span class="gd"&gt;-        if self.hips_tile_meta.format == &amp;#39;fits&amp;#39;:&lt;/span&gt;
&lt;span class="gd"&gt;-            hdu = fits.PrimaryHDU(self.data, header=self.header)&lt;/span&gt;
&lt;span class="gd"&gt;-            hdulist = fits.HDUList([hdu])&lt;/span&gt;
&lt;span class="gd"&gt;-            hdulist.writeto(str(path))&lt;/span&gt;
&lt;span class="gd"&gt;-            hdulist.close()&lt;/span&gt;
&lt;span class="gi"&gt;+        if self.meta.format == &amp;#39;fits&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+            hdu = fits.PrimaryHDU(self.data, header=self.header).writeto(str(path))&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        else:
&lt;span class="w"&gt; &lt;/span&gt;            Image.fromarray(self.data).save(str(path))
&lt;span class="gh"&gt;diff --git a/hips/tiles/tile_meta.py b/hips/tiles/tile_meta.py&lt;/span&gt;
&lt;span class="gh"&gt;index 9ee8445..8ac403b 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tile_meta.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tile_meta.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -7,7 +7,6 @@ __all__ = [&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;class HipsTileMeta:
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;HiPS tile metadata container.
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    This class stores HiPS tile meta attributes.

&lt;span class="w"&gt; &lt;/span&gt;    Parameters
&lt;span class="gu"&gt;@@ -16,8 +15,8 @@ class HipsTileMeta:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        Order of HiPS tile
&lt;span class="w"&gt; &lt;/span&gt;    ipix : `int`
&lt;span class="w"&gt; &lt;/span&gt;        Pixel number of HiPS tile
&lt;span class="gd"&gt;-    format : `str`&lt;/span&gt;
&lt;span class="gd"&gt;-        Format of the HiPS tile&lt;/span&gt;
&lt;span class="gi"&gt;+    format : {&amp;#39;fits&amp;#39;, &amp;#39;jpg&amp;#39;, &amp;#39;png&amp;#39;}&lt;/span&gt;
&lt;span class="gi"&gt;+        File format of the HiPS tile&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    tile_width : `int`
&lt;span class="w"&gt; &lt;/span&gt;        HiPS tile width
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;
&lt;span class="gh"&gt;diff --git a/hips/utils/tile.py b/hips/utils/tile.py&lt;/span&gt;
&lt;span class="gh"&gt;index f08d640..c3937ea 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/tile.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/tile.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,8 +1,6 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="gd"&gt;-&amp;quot;&amp;quot;&amp;quot;HEALpy wrapper functions.&lt;/span&gt;
&lt;span class="gi"&gt;+&amp;quot;&amp;quot;&amp;quot;HiPS tile utility functions.&lt;/span&gt;

&lt;span class="gd"&gt;-This module contains wrapper functions around HEALPix utilizing&lt;/span&gt;
&lt;span class="gd"&gt;-the healpy library.&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&amp;quot;&amp;quot;&amp;quot;

&lt;span class="w"&gt; &lt;/span&gt;from pathlib import Path

commit 616340bd6c87cb070d324b1631ced8cbe76654b8
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Wed Jun 21 22:33:36 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Add class HipsTileMeta and update test case

&lt;span class="gh"&gt;diff --git a/hips/tiles/tests/data/Npix30889.fits b/hips/tiles/tests/data/Npix30889.fits&lt;/span&gt;
deleted file mode 100644
&lt;span class="gh"&gt;index 522852f..0000000&lt;/span&gt;
Binary files a/hips/tiles/tests/data/Npix30889.fits and /dev/null differ
&lt;span class="gh"&gt;diff --git a/hips/tiles/tests/data/Npix30889.jpg b/hips/tiles/tests/data/Npix30889.jpg&lt;/span&gt;
deleted file mode 100644
&lt;span class="gh"&gt;index b6365db..0000000&lt;/span&gt;
Binary files a/hips/tiles/tests/data/Npix30889.jpg and /dev/null differ
&lt;span class="gh"&gt;diff --git a/hips/tiles/tests/test_tile.py b/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gh"&gt;index d82e8a0..a3a3397 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,34 +1,34 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="w"&gt; &lt;/span&gt;from astropy.tests.helper import remote_data
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from ..tile import HipsTile
&lt;span class="gi"&gt;+from ..tile_meta import HipsTileMeta&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+class TestHipsTile:&lt;/span&gt;
&lt;span class="gi"&gt;+    def setup(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        self.hips_tile_meta_fits = HipsTileMeta(order=6, ipix=30889, format=&amp;#39;fits&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        self.hips_tile_meta_jpg = HipsTileMeta(order=6, ipix=30889, format=&amp;#39;jpg&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @remote_data&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_fetch_read_write(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        fits_tile = HipsTile.fetch(self.hips_tile_meta_fits,&lt;/span&gt;
&lt;span class="gi"&gt;+                                   &amp;#39;http://alasky.unistra.fr/2MASS/H/Norder6/Dir30000/Npix30889.fits&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        jpg_tile = HipsTile.fetch(self.hips_tile_meta_jpg,&lt;/span&gt;
&lt;span class="gi"&gt;+                                  &amp;#39;http://alasky.unistra.fr/2MASS/H/Norder6/Dir30000/Npix30889.jpg&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        data_precomp = [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1]&lt;/span&gt;
&lt;span class="gi"&gt;+        assert fits_tile.data.shape == (512, 512)&lt;/span&gt;
&lt;span class="gi"&gt;+        assert jpg_tile.data.shape == (512, 512, 3)&lt;/span&gt;
&lt;span class="gi"&gt;+        assert list(fits_tile.data[510][:12]) == data_precomp&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        jpg_tile.write(&amp;#39;Npix30889.jpg&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        fits_tile.write(&amp;#39;Npix30889.fits&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        fits_tile = HipsTile.read(self.hips_tile_meta_fits, &amp;#39;Npix30889.fits&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        jpg_tile = HipsTile.read(self.hips_tile_meta_jpg, &amp;#39;Npix30889.jpg&amp;#39;)&lt;/span&gt;

&lt;span class="gd"&gt;-@remote_data&lt;/span&gt;
&lt;span class="gd"&gt;-def test_fetch():&lt;/span&gt;
&lt;span class="gd"&gt;-    fits_tile = HipsTile.fetch(&amp;#39;fits&amp;#39;, &amp;#39;http://alasky.unistra.fr/2MASS/H/Norder6/Dir30000/Npix30889.fits&amp;#39;)&lt;/span&gt;
&lt;span class="gd"&gt;-    jpg_tile = HipsTile.fetch(&amp;#39;jpg&amp;#39;, &amp;#39;http://alasky.unistra.fr/2MASS/H/Norder6/Dir30000/Npix30889.jpg&amp;#39;)&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    shape_fits_precomp = (512, 512)&lt;/span&gt;
&lt;span class="gd"&gt;-    shape_jpg_precomp = (512, 512, 3)&lt;/span&gt;
&lt;span class="gd"&gt;-    data_precomp = [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1]&lt;/span&gt;
&lt;span class="gd"&gt;-    assert fits_tile.data.shape == shape_fits_precomp&lt;/span&gt;
&lt;span class="gd"&gt;-    assert jpg_tile.data.shape == shape_jpg_precomp&lt;/span&gt;
&lt;span class="gd"&gt;-    assert list(fits_tile.data[510][:12]) == data_precomp&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-def test_read_write():&lt;/span&gt;
&lt;span class="gd"&gt;-    fits_tile = HipsTile.read(&amp;#39;fits&amp;#39;, &amp;#39;Npix30889.fits&amp;#39;)&lt;/span&gt;
&lt;span class="gd"&gt;-    jpg_tile = HipsTile.read(&amp;#39;jpg&amp;#39;, &amp;#39;Npix30889.jpg&amp;#39;)&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    shape_fits_precomp = (512, 512)&lt;/span&gt;
&lt;span class="gd"&gt;-    shape_jpg_precomp = (512, 512, 3)&lt;/span&gt;
&lt;span class="gd"&gt;-    data_precomp = [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1]&lt;/span&gt;
&lt;span class="gd"&gt;-    assert (fits_tile.data.shape) == shape_fits_precomp&lt;/span&gt;
&lt;span class="gd"&gt;-    assert (jpg_tile.data.shape) == shape_jpg_precomp&lt;/span&gt;
&lt;span class="gd"&gt;-    assert list(fits_tile.data[510][:12]) == data_precomp&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    fits_tile.write(&amp;#39;test_file.fits&amp;#39;)&lt;/span&gt;
&lt;span class="gd"&gt;-    jpg_tile.write(&amp;#39;test_file.jpg&amp;#39;)&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    path_fits = fits_tile.path / &amp;#39;test_file.fits&amp;#39;&lt;/span&gt;
&lt;span class="gd"&gt;-    path_jpg = jpg_tile.path / &amp;#39;test_file.jpg&amp;#39;&lt;/span&gt;
&lt;span class="gd"&gt;-    assert True == path_fits.is_file()&lt;/span&gt;
&lt;span class="gd"&gt;-    assert True == path_jpg.is_file()&lt;/span&gt;
&lt;span class="gi"&gt;+        data_precomp = [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1]&lt;/span&gt;
&lt;span class="gi"&gt;+        assert fits_tile.data.shape == (512, 512)&lt;/span&gt;
&lt;span class="gi"&gt;+        assert jpg_tile.data.shape == (512, 512, 3)&lt;/span&gt;
&lt;span class="gi"&gt;+        assert list(fits_tile.data[510][:12]) == data_precomp&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/tiles/tile.py b/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gh"&gt;index 509667e..efd0bb9 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,16 +1,21 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="gd"&gt;-from astropy.io.fits.header import Header&lt;/span&gt;
&lt;span class="gd"&gt;-from astropy.io import fits&lt;/span&gt;
&lt;span class="gd"&gt;-from pathlib import Path&lt;/span&gt;
&lt;span class="gd"&gt;-from io import BytesIO&lt;/span&gt;
&lt;span class="gd"&gt;-from PIL import Image&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;import urllib.request
&lt;span class="gi"&gt;+from io import BytesIO&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;import numpy as np
&lt;span class="gi"&gt;+from PIL import Image&lt;/span&gt;
&lt;span class="gi"&gt;+from astropy.io import fits&lt;/span&gt;
&lt;span class="gi"&gt;+from astropy.io.fits.header import Header&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+from .tile_meta import HipsTileMeta&lt;/span&gt;
&lt;span class="gi"&gt;+from ..utils.tile import tile_path&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;__all__ = [
&lt;span class="w"&gt; &lt;/span&gt;    &amp;#39;HipsTile&amp;#39;,
&lt;span class="w"&gt; &lt;/span&gt;]

&lt;span class="gi"&gt;+__doctest_skip__ = [&amp;#39;HipsTile&amp;#39;]&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;class HipsTile:
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;HiPS tile container.
&lt;span class="gu"&gt;@@ -19,21 +24,23 @@ class HipsTile:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    Parameters
&lt;span class="w"&gt; &lt;/span&gt;    ----------
&lt;span class="gi"&gt;+    hips_tile_meta : `HipsTileMeta`&lt;/span&gt;
&lt;span class="gi"&gt;+        Metadata of HiPS tile&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    format : `str`
&lt;span class="w"&gt; &lt;/span&gt;        Format of HiPS tile
&lt;span class="gd"&gt;-    data : `int`&lt;/span&gt;
&lt;span class="gi"&gt;+    data : `np.ndarray`&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        Data containing HiPS tile
&lt;span class="gd"&gt;-    header : `format`&lt;/span&gt;
&lt;span class="gi"&gt;+    header : `astropy.io.fits.Header`&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        Header of HiPS tile

&lt;span class="w"&gt; &lt;/span&gt;    Examples
&lt;span class="w"&gt; &lt;/span&gt;    --------
&lt;span class="gd"&gt;-    ::&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;gt;&amp;gt;&amp;gt; import urllib.request
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; from hips.tiles import HipsTile&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; from hips.tiles import HipsTileMeta&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;gt;&amp;gt;&amp;gt; from astropy.tests.helper import remote_data
&lt;span class="gd"&gt;-        &amp;gt;&amp;gt;&amp;gt; text = urllib.request.urlopen(&amp;#39;https://raw.githubusercontent.com/hipspy/hips/master/hips/tiles/tests/data/properties.txt&amp;#39;).read() # doctest: +REMOTE_DATA&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;gt;&amp;gt;&amp;gt; hips_tile = HipsTile.read(&amp;#39;fits&amp;#39;, &amp;#39;Npix30889.fits&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; hips_tile_metadata = HipsTileMeta(order=6, ipix=30889, format=&amp;#39;fits&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; hips_tile = HipsTile.read(hips_tile_metadata, &amp;#39;Npix30889.fits&amp;#39;)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;gt;&amp;gt;&amp;gt; hips_tile.data
&lt;span class="w"&gt; &lt;/span&gt;        array([[0, 0, 0, ..., 0, 0, 0],
&lt;span class="w"&gt; &lt;/span&gt;               [0, 0, 0, ..., 0, 0, 0],
&lt;span class="gu"&gt;@@ -44,57 +51,53 @@ class HipsTile:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;               [0, 0, 0, ..., 1, 0, 1]], dtype=int16)
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;

&lt;span class="gd"&gt;-    def __init__(self, format: str, data: np.ndarray=None, header: Header=None) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+    def __init__(self, hips_tile_meta: HipsTileMeta, data: np.ndarray = None, header: Header = None) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+        self.hips_tile_meta = hips_tile_meta&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        self.format = format
&lt;span class="w"&gt; &lt;/span&gt;        self.data = data
&lt;span class="w"&gt; &lt;/span&gt;        self.header = header

&lt;span class="gd"&gt;-    @property&lt;/span&gt;
&lt;span class="gd"&gt;-    def path(self) -&amp;gt; Path:&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;Default path for tile storage (`Path`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-        return Path(&amp;#39;hips&amp;#39;, &amp;#39;tiles&amp;#39;, &amp;#39;tests&amp;#39;, &amp;#39;data&amp;#39;)&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    @classmethod
&lt;span class="gd"&gt;-    def fetch(cls, format: str, url: str) -&amp;gt; HipsTile:&lt;/span&gt;
&lt;span class="gi"&gt;+    def fetch(cls, hips_tile_meta: HipsTileMeta, url: str) -&amp;gt; &amp;#39;HipsTile&amp;#39;:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Fetch HiPS tile and load into memory (`HipsTile`).

&lt;span class="w"&gt; &lt;/span&gt;        Parameters
&lt;span class="w"&gt; &lt;/span&gt;        ----------
&lt;span class="gd"&gt;-        format : `str`&lt;/span&gt;
&lt;span class="gd"&gt;-            Format of HiPS tile&lt;/span&gt;
&lt;span class="gi"&gt;+        hips_tile_meta : `HipsTileMeta`&lt;/span&gt;
&lt;span class="gi"&gt;+            Metadata of HiPS tile&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        url : `str`
&lt;span class="w"&gt; &lt;/span&gt;            URL containing HiPS tile
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;        raw_image = BytesIO(urllib.request.urlopen(url).read())
&lt;span class="gd"&gt;-        if format == &amp;#39;fits&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+        if hips_tile_meta.format == &amp;#39;fits&amp;#39;:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            hdulist = fits.open(raw_image)
&lt;span class="w"&gt; &lt;/span&gt;            data = np.array(hdulist[0].data)
&lt;span class="w"&gt; &lt;/span&gt;            header = hdulist[0].header
&lt;span class="gd"&gt;-            return cls(format, data, header)&lt;/span&gt;
&lt;span class="gi"&gt;+            return cls(hips_tile_meta, data, header)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        else:
&lt;span class="w"&gt; &lt;/span&gt;            data = np.array(Image.open(raw_image))
&lt;span class="gd"&gt;-            return cls(format, data)&lt;/span&gt;
&lt;span class="gi"&gt;+            return cls(hips_tile_meta, data)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    @classmethod
&lt;span class="gd"&gt;-    def read(cls, format: str, filename: str) -&amp;gt; &amp;#39;HipsTile&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+    def read(cls, hips_tile_meta: HipsTileMeta, filename: str) -&amp;gt; &amp;#39;HipsTile&amp;#39;:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Read HiPS tile data from a directory and load into memory (`HipsTile`).

&lt;span class="w"&gt; &lt;/span&gt;        Parameters
&lt;span class="w"&gt; &lt;/span&gt;        ----------
&lt;span class="gd"&gt;-        format : `str`&lt;/span&gt;
&lt;span class="gd"&gt;-            Format of HiPS tile&lt;/span&gt;
&lt;span class="gi"&gt;+        hips_tile_meta : `HipsTileMeta`&lt;/span&gt;
&lt;span class="gi"&gt;+            Metadata of HiPS tile&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        filename : `str`
&lt;span class="w"&gt; &lt;/span&gt;            File name of HiPS tile
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-        path = Path(&amp;#39;hips&amp;#39;, &amp;#39;tiles&amp;#39;, &amp;#39;tests&amp;#39;, &amp;#39;data&amp;#39;) / filename&lt;/span&gt;
&lt;span class="gd"&gt;-        if format == &amp;#39;fits&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+        path = tile_path().absolute() / filename&lt;/span&gt;
&lt;span class="gi"&gt;+        if hips_tile_meta.format == &amp;#39;fits&amp;#39;:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            hdulist = fits.open(path)
&lt;span class="w"&gt; &lt;/span&gt;            data = np.array(hdulist[0].data)
&lt;span class="w"&gt; &lt;/span&gt;            header = hdulist[0].header
&lt;span class="gd"&gt;-            return cls(format, data, header)&lt;/span&gt;
&lt;span class="gi"&gt;+            return cls(hips_tile_meta, data, header)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        else:
&lt;span class="w"&gt; &lt;/span&gt;            data = np.array(Image.open(str(path)))
&lt;span class="gd"&gt;-            return cls(format, data)&lt;/span&gt;
&lt;span class="gi"&gt;+            return cls(hips_tile_meta, data)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def write(self, filename: str) -&amp;gt; None:
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Write HiPS tile by a given filename (`None`).
&lt;span class="gu"&gt;@@ -104,11 +107,11 @@ class HipsTile:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        filename : `str`
&lt;span class="w"&gt; &lt;/span&gt;            Name of the file
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-        path = self.path / filename&lt;/span&gt;
&lt;span class="gd"&gt;-        if self.format == &amp;#39;fits&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+        path = tile_path().absolute() / filename&lt;/span&gt;
&lt;span class="gi"&gt;+        if self.hips_tile_meta.format == &amp;#39;fits&amp;#39;:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            hdu = fits.PrimaryHDU(self.data, header=self.header)
&lt;span class="w"&gt; &lt;/span&gt;            hdulist = fits.HDUList([hdu])
&lt;span class="gd"&gt;-            hdulist.writeto(path)&lt;/span&gt;
&lt;span class="gi"&gt;+            hdulist.writeto(str(path))&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            hdulist.close()
&lt;span class="w"&gt; &lt;/span&gt;        else:
&lt;span class="w"&gt; &lt;/span&gt;            Image.fromarray(self.data).save(str(path))
&lt;span class="gh"&gt;diff --git a/hips/tiles/tile_meta.py b/hips/tiles/tile_meta.py&lt;/span&gt;
new file mode 100644
&lt;span class="gh"&gt;index 0000000..9ee8445&lt;/span&gt;
&lt;span class="gd"&gt;--- /dev/null&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tile_meta.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -0,0 +1,29 @@&lt;/span&gt;
&lt;span class="gi"&gt;+# Licensed under a 3-clause BSD style license - see LICENSE.rst&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+__all__ = [&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;#39;HipsTileMeta&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+]&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+class HipsTileMeta:&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;HiPS tile metadata container.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    This class stores HiPS tile meta attributes.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    Parameters&lt;/span&gt;
&lt;span class="gi"&gt;+    ----------&lt;/span&gt;
&lt;span class="gi"&gt;+    order : `int`&lt;/span&gt;
&lt;span class="gi"&gt;+        Order of HiPS tile&lt;/span&gt;
&lt;span class="gi"&gt;+    ipix : `int`&lt;/span&gt;
&lt;span class="gi"&gt;+        Pixel number of HiPS tile&lt;/span&gt;
&lt;span class="gi"&gt;+    format : `str`&lt;/span&gt;
&lt;span class="gi"&gt;+        Format of the HiPS tile&lt;/span&gt;
&lt;span class="gi"&gt;+    tile_width : `int`&lt;/span&gt;
&lt;span class="gi"&gt;+        HiPS tile width&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def __init__(self, order: int, ipix: int, format: str, tile_width: int = 512) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+        self.order = format&lt;/span&gt;
&lt;span class="gi"&gt;+        self.ipix = ipix&lt;/span&gt;
&lt;span class="gi"&gt;+        self.format = format&lt;/span&gt;
&lt;span class="gi"&gt;+        self.tile_width = tile_width&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/utils/tile.py b/hips/utils/tile.py&lt;/span&gt;
new file mode 100644
&lt;span class="gh"&gt;index 0000000..f08d640&lt;/span&gt;
&lt;span class="gd"&gt;--- /dev/null&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/tile.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -0,0 +1,18 @@&lt;/span&gt;
&lt;span class="gi"&gt;+# Licensed under a 3-clause BSD style license - see LICENSE.rst&lt;/span&gt;
&lt;span class="gi"&gt;+&amp;quot;&amp;quot;&amp;quot;HEALpy wrapper functions.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+This module contains wrapper functions around HEALPix utilizing&lt;/span&gt;
&lt;span class="gi"&gt;+the healpy library.&lt;/span&gt;
&lt;span class="gi"&gt;+&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+from pathlib import Path&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+__all__ = [&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;#39;tile_path&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+]&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+def tile_path() -&amp;gt; Path:&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;Return default path of HiPS tile storage.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+    path = Path(&amp;#39;hips&amp;#39;, &amp;#39;tiles&amp;#39;, &amp;#39;tests&amp;#39;, &amp;#39;data&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+    return path&lt;/span&gt;

commit 6250e9ec0c665151a84403d986a4b71a804ea632
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Wed Jun 21 16:15:41 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Minimize HipsTile classs functionality

&lt;span class="gh"&gt;diff --git a/hips/tiles/tests/test_tile.py b/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gh"&gt;index 1316b5d..d82e8a0 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,61 +1,34 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="gd"&gt;-from astropy.utils.data import get_pkg_data_filename&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from astropy.tests.helper import remote_data
&lt;span class="gd"&gt;-from ..description import HipsDescription&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from ..tile import HipsTile
&lt;span class="gd"&gt;-from pathlib import Path&lt;/span&gt;
&lt;span class="gd"&gt;-import numpy as np&lt;/span&gt;

&lt;span class="gd"&gt;-class TestHipsTile:&lt;/span&gt;
&lt;span class="gd"&gt;-    @classmethod&lt;/span&gt;
&lt;span class="gd"&gt;-    def setup_class(cls):&lt;/span&gt;
&lt;span class="gd"&gt;-        filename = get_pkg_data_filename(&amp;#39;data/properties.txt&amp;#39;)&lt;/span&gt;
&lt;span class="gd"&gt;-        hips_description = HipsDescription.read(filename)&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        cls.fits_tile = HipsTile(hips_description=hips_description, order=6,&lt;/span&gt;
&lt;span class="gd"&gt;-                            ipix=30889, format=&amp;#39;fits&amp;#39;, tile_width=512)&lt;/span&gt;
&lt;span class="gd"&gt;-        cls.jpg_tile = HipsTile(hips_description=hips_description, order=6,&lt;/span&gt;
&lt;span class="gd"&gt;-                            ipix=30889, format=&amp;#39;jpg&amp;#39;, tile_width=512)&lt;/span&gt;
&lt;span class="gd"&gt;-        cls.fits_tile.base_url = &amp;#39;http://alasky.unistra.fr/2MASS/H&amp;#39;&lt;/span&gt;
&lt;span class="gd"&gt;-        cls.jpg_tile.base_url = &amp;#39;http://alasky.unistra.fr/2MASS/H&amp;#39;&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    def test_base_url(self):&lt;/span&gt;
&lt;span class="gd"&gt;-        assert self.fits_tile.base_url == &amp;#39;http://alasky.unistra.fr/2MASS/H&amp;#39;&lt;/span&gt;
&lt;span class="gd"&gt;-        assert self.jpg_tile.base_url == &amp;#39;http://alasky.unistra.fr/2MASS/H&amp;#39;&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    def test_tile_url(self):&lt;/span&gt;
&lt;span class="gd"&gt;-        assert self.fits_tile.tile_url == &amp;#39;http://alasky.unistra.fr/2MASS/H/Norder6/Dir30000/Npix30889.fits&amp;#39;&lt;/span&gt;
&lt;span class="gd"&gt;-        assert self.jpg_tile.tile_url == &amp;#39;http://alasky.unistra.fr/2MASS/H/Norder6/Dir30000/Npix30889.jpg&amp;#39;&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    @remote_data&lt;/span&gt;
&lt;span class="gd"&gt;-    def test_fetch(self):&lt;/span&gt;
&lt;span class="gd"&gt;-        self.fits_tile.fetch()&lt;/span&gt;
&lt;span class="gd"&gt;-        self.jpg_tile.fetch()&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        shape_fits_precomp = (512, 512)&lt;/span&gt;
&lt;span class="gd"&gt;-        shape_jpg_precomp = (512, 512, 3)&lt;/span&gt;
&lt;span class="gd"&gt;-        data_precomp = [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1]&lt;/span&gt;
&lt;span class="gd"&gt;-        assert self.fits_tile.data.shape == shape_fits_precomp&lt;/span&gt;
&lt;span class="gd"&gt;-        assert self.jpg_tile.data.shape == shape_jpg_precomp&lt;/span&gt;
&lt;span class="gd"&gt;-        assert list(self.fits_tile.data[510][:12]) == data_precomp&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    def test_read(self):&lt;/span&gt;
&lt;span class="gd"&gt;-        self.fits_tile.read()&lt;/span&gt;
&lt;span class="gd"&gt;-        self.jpg_tile.read()&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        shape_fits_precomp = (512, 512)&lt;/span&gt;
&lt;span class="gd"&gt;-        shape_jpg_precomp = (512, 512, 3)&lt;/span&gt;
&lt;span class="gd"&gt;-        data_precomp = [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1]&lt;/span&gt;
&lt;span class="gd"&gt;-        assert (self.fits_tile.data.shape) == shape_fits_precomp&lt;/span&gt;
&lt;span class="gd"&gt;-        assert (self.jpg_tile.data.shape) == shape_jpg_precomp&lt;/span&gt;
&lt;span class="gd"&gt;-        assert list(self.fits_tile.data[510][:12]) == data_precomp&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    def test_write(self):&lt;/span&gt;
&lt;span class="gd"&gt;-        filename = &amp;#39;test_file&amp;#39;&lt;/span&gt;
&lt;span class="gd"&gt;-        self.fits_tile.write(filename)&lt;/span&gt;
&lt;span class="gd"&gt;-        self.jpg_tile.write(filename)&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        path_fits = self.fits_tile.path.joinpath(&amp;#39;&amp;#39;.join([filename, &amp;#39;.&amp;#39;, self.fits_tile.format]))&lt;/span&gt;
&lt;span class="gd"&gt;-        path_jpg = self.jpg_tile.path.joinpath(&amp;#39;&amp;#39;.join([filename, &amp;#39;.&amp;#39;, self.jpg_tile.format]))&lt;/span&gt;
&lt;span class="gd"&gt;-        assert True == path_fits.is_file()&lt;/span&gt;
&lt;span class="gd"&gt;-        assert True == path_jpg.is_file()&lt;/span&gt;
&lt;span class="gi"&gt;+@remote_data&lt;/span&gt;
&lt;span class="gi"&gt;+def test_fetch():&lt;/span&gt;
&lt;span class="gi"&gt;+    fits_tile = HipsTile.fetch(&amp;#39;fits&amp;#39;, &amp;#39;http://alasky.unistra.fr/2MASS/H/Norder6/Dir30000/Npix30889.fits&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+    jpg_tile = HipsTile.fetch(&amp;#39;jpg&amp;#39;, &amp;#39;http://alasky.unistra.fr/2MASS/H/Norder6/Dir30000/Npix30889.jpg&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    shape_fits_precomp = (512, 512)&lt;/span&gt;
&lt;span class="gi"&gt;+    shape_jpg_precomp = (512, 512, 3)&lt;/span&gt;
&lt;span class="gi"&gt;+    data_precomp = [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1]&lt;/span&gt;
&lt;span class="gi"&gt;+    assert fits_tile.data.shape == shape_fits_precomp&lt;/span&gt;
&lt;span class="gi"&gt;+    assert jpg_tile.data.shape == shape_jpg_precomp&lt;/span&gt;
&lt;span class="gi"&gt;+    assert list(fits_tile.data[510][:12]) == data_precomp&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+def test_read_write():&lt;/span&gt;
&lt;span class="gi"&gt;+    fits_tile = HipsTile.read(&amp;#39;fits&amp;#39;, &amp;#39;Npix30889.fits&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+    jpg_tile = HipsTile.read(&amp;#39;jpg&amp;#39;, &amp;#39;Npix30889.jpg&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    shape_fits_precomp = (512, 512)&lt;/span&gt;
&lt;span class="gi"&gt;+    shape_jpg_precomp = (512, 512, 3)&lt;/span&gt;
&lt;span class="gi"&gt;+    data_precomp = [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1]&lt;/span&gt;
&lt;span class="gi"&gt;+    assert (fits_tile.data.shape) == shape_fits_precomp&lt;/span&gt;
&lt;span class="gi"&gt;+    assert (jpg_tile.data.shape) == shape_jpg_precomp&lt;/span&gt;
&lt;span class="gi"&gt;+    assert list(fits_tile.data[510][:12]) == data_precomp&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    fits_tile.write(&amp;#39;test_file.fits&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+    jpg_tile.write(&amp;#39;test_file.jpg&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    path_fits = fits_tile.path / &amp;#39;test_file.fits&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+    path_jpg = jpg_tile.path / &amp;#39;test_file.jpg&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+    assert True == path_fits.is_file()&lt;/span&gt;
&lt;span class="gi"&gt;+    assert True == path_jpg.is_file()&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/tiles/tile.py b/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gh"&gt;index 8b033e1..509667e 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,5 +1,5 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="gd"&gt;-from .description import HipsDescription&lt;/span&gt;
&lt;span class="gi"&gt;+from astropy.io.fits.header import Header&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from astropy.io import fits
&lt;span class="w"&gt; &lt;/span&gt;from pathlib import Path
&lt;span class="w"&gt; &lt;/span&gt;from io import BytesIO
&lt;span class="gu"&gt;@@ -15,39 +15,25 @@ __all__ = [&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;class HipsTile:
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;HiPS tile container.

&lt;span class="gd"&gt;-    This class provides methods for fetching, reading,&lt;/span&gt;
&lt;span class="gd"&gt;-    and writing a HiPS tile. It also contains a few&lt;/span&gt;
&lt;span class="gd"&gt;-    getters and setters around frequently used&lt;/span&gt;
&lt;span class="gd"&gt;-    HiPS tile attributes.&lt;/span&gt;
&lt;span class="gi"&gt;+    This class provides methods for fetching, reading, and writing a HiPS tile.&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    Parameters
&lt;span class="w"&gt; &lt;/span&gt;    ----------
&lt;span class="gd"&gt;-    hips_description : `HipsDescription`&lt;/span&gt;
&lt;span class="gd"&gt;-        Class HipsDescription contains HiPS properties&lt;/span&gt;
&lt;span class="gd"&gt;-    order : `int`&lt;/span&gt;
&lt;span class="gd"&gt;-        Order of the HiPS tile&lt;/span&gt;
&lt;span class="gd"&gt;-    ipix : `int`&lt;/span&gt;
&lt;span class="gd"&gt;-        HEALPix pixel number&lt;/span&gt;
&lt;span class="gd"&gt;-    format : `format`&lt;/span&gt;
&lt;span class="gi"&gt;+    format : `str`&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        Format of HiPS tile
&lt;span class="gd"&gt;-    data : `numpy.ndarray`&lt;/span&gt;
&lt;span class="gd"&gt;-        Pixel values of HiPS tile&lt;/span&gt;
&lt;span class="gd"&gt;-    tile_width : `int`&lt;/span&gt;
&lt;span class="gd"&gt;-        Width of HiPS tile&lt;/span&gt;
&lt;span class="gd"&gt;-    base_url : `str`&lt;/span&gt;
&lt;span class="gd"&gt;-        Base URL of HiPS tile&lt;/span&gt;
&lt;span class="gi"&gt;+    data : `int`&lt;/span&gt;
&lt;span class="gi"&gt;+        Data containing HiPS tile&lt;/span&gt;
&lt;span class="gi"&gt;+    header : `format`&lt;/span&gt;
&lt;span class="gi"&gt;+        Header of HiPS tile&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    Examples
&lt;span class="w"&gt; &lt;/span&gt;    --------
&lt;span class="w"&gt; &lt;/span&gt;    ::

&lt;span class="w"&gt; &lt;/span&gt;        &amp;gt;&amp;gt;&amp;gt; import urllib.request
&lt;span class="gd"&gt;-        &amp;gt;&amp;gt;&amp;gt; from hips.tiles import HipsDescription&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;gt;&amp;gt;&amp;gt; from astropy.tests.helper import remote_data
&lt;span class="w"&gt; &lt;/span&gt;        &amp;gt;&amp;gt;&amp;gt; text = urllib.request.urlopen(&amp;#39;https://raw.githubusercontent.com/hipspy/hips/master/hips/tiles/tests/data/properties.txt&amp;#39;).read() # doctest: +REMOTE_DATA
&lt;span class="gd"&gt;-        &amp;gt;&amp;gt;&amp;gt; hips_description = HipsDescription.parse(str(text))&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;gt;&amp;gt;&amp;gt; hips_tile = HipsTile(hips_description=hips_description, order=6, ipix=30889, format=&amp;#39;fits&amp;#39;, tile_width=512)&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;gt;&amp;gt;&amp;gt; hips_tile.read()&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; hips_tile = HipsTile.read(&amp;#39;fits&amp;#39;, &amp;#39;Npix30889.fits&amp;#39;)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;gt;&amp;gt;&amp;gt; hips_tile.data
&lt;span class="w"&gt; &lt;/span&gt;        array([[0, 0, 0, ..., 0, 0, 0],
&lt;span class="w"&gt; &lt;/span&gt;               [0, 0, 0, ..., 0, 0, 0],
&lt;span class="gu"&gt;@@ -58,51 +44,57 @@ class HipsTile:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;               [0, 0, 0, ..., 1, 0, 1]], dtype=int16)
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;

&lt;span class="gd"&gt;-    def __init__(self, hips_description: HipsDescription, order: int, ipix: int, format: str,&lt;/span&gt;
&lt;span class="gd"&gt;-                 data: np.ndarray=None, tile_width: int=512, base_url: str=None) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gd"&gt;-        self.hips_description = hips_description&lt;/span&gt;
&lt;span class="gd"&gt;-        self.order = order&lt;/span&gt;
&lt;span class="gd"&gt;-        self.ipix = ipix&lt;/span&gt;
&lt;span class="gd"&gt;-        self.tile_width = tile_width&lt;/span&gt;
&lt;span class="gi"&gt;+    def __init__(self, format: str, data: np.ndarray=None, header: Header=None) -&amp;gt; None:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        self.format = format
&lt;span class="w"&gt; &lt;/span&gt;        self.data = data
&lt;span class="gd"&gt;-        self.base_url = base_url&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    @staticmethod&lt;/span&gt;
&lt;span class="gd"&gt;-    def _directory(ipix: int) -&amp;gt; int:&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;Directory of the HiPS tile (`int`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-        return np.around(ipix, decimals=-(len(str(ipix)) - 1))&lt;/span&gt;
&lt;span class="gi"&gt;+        self.header = header&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    @property
&lt;span class="w"&gt; &lt;/span&gt;    def path(self) -&amp;gt; Path:
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Default path for tile storage (`Path`).&amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;        return Path(&amp;#39;hips&amp;#39;, &amp;#39;tiles&amp;#39;, &amp;#39;tests&amp;#39;, &amp;#39;data&amp;#39;)

&lt;span class="gd"&gt;-    @property&lt;/span&gt;
&lt;span class="gd"&gt;-    def tile_url(self) -&amp;gt; str:&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;HiPS tile url (`str`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-        return &amp;#39;&amp;#39;.join([self.base_url, &amp;#39;/Norder&amp;#39;, str(self.order), &amp;#39;/Dir&amp;#39;,&lt;/span&gt;
&lt;span class="gd"&gt;-               str(self._directory(self.ipix)), &amp;#39;/Npix&amp;#39;, str(self.ipix), &amp;#39;.&amp;#39;, self.format])&lt;/span&gt;
&lt;span class="gi"&gt;+    @classmethod&lt;/span&gt;
&lt;span class="gi"&gt;+    def fetch(cls, format: str, url: str) -&amp;gt; HipsTile:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Fetch HiPS tile and load into memory (`HipsTile`).&lt;/span&gt;

&lt;span class="gd"&gt;-    def fetch(self) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;Fetch HiPS tile and load into memory (`None`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-        raw_image = BytesIO(urllib.request.urlopen(self.tile_url).read())&lt;/span&gt;
&lt;span class="gd"&gt;-        if self.format == &amp;#39;fits&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+        Parameters&lt;/span&gt;
&lt;span class="gi"&gt;+        ----------&lt;/span&gt;
&lt;span class="gi"&gt;+        format : `str`&lt;/span&gt;
&lt;span class="gi"&gt;+            Format of HiPS tile&lt;/span&gt;
&lt;span class="gi"&gt;+        url : `str`&lt;/span&gt;
&lt;span class="gi"&gt;+            URL containing HiPS tile&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        raw_image = BytesIO(urllib.request.urlopen(url).read())&lt;/span&gt;
&lt;span class="gi"&gt;+        if format == &amp;#39;fits&amp;#39;:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            hdulist = fits.open(raw_image)
&lt;span class="gd"&gt;-            self.data = np.array(hdulist[0].data)&lt;/span&gt;
&lt;span class="gd"&gt;-            self.header = hdulist[0].header&lt;/span&gt;
&lt;span class="gi"&gt;+            data = np.array(hdulist[0].data)&lt;/span&gt;
&lt;span class="gi"&gt;+            header = hdulist[0].header&lt;/span&gt;
&lt;span class="gi"&gt;+            return cls(format, data, header)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        else:
&lt;span class="gd"&gt;-            self.data = np.array(Image.open(raw_image))&lt;/span&gt;
&lt;span class="gi"&gt;+            data = np.array(Image.open(raw_image))&lt;/span&gt;
&lt;span class="gi"&gt;+            return cls(format, data)&lt;/span&gt;

&lt;span class="gd"&gt;-    def read(self) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;Read HiPS tile data from a directory and load into memory (`None`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-        path = self.path / (&amp;#39;&amp;#39;.join([&amp;#39;Npix&amp;#39;, str(self.ipix), &amp;#39;.&amp;#39;, self.format]))&lt;/span&gt;
&lt;span class="gd"&gt;-        if self.format == &amp;#39;fits&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+    @classmethod&lt;/span&gt;
&lt;span class="gi"&gt;+    def read(cls, format: str, filename: str) -&amp;gt; &amp;#39;HipsTile&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Read HiPS tile data from a directory and load into memory (`HipsTile`).&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        Parameters&lt;/span&gt;
&lt;span class="gi"&gt;+        ----------&lt;/span&gt;
&lt;span class="gi"&gt;+        format : `str`&lt;/span&gt;
&lt;span class="gi"&gt;+            Format of HiPS tile&lt;/span&gt;
&lt;span class="gi"&gt;+        filename : `str`&lt;/span&gt;
&lt;span class="gi"&gt;+            File name of HiPS tile&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        path = Path(&amp;#39;hips&amp;#39;, &amp;#39;tiles&amp;#39;, &amp;#39;tests&amp;#39;, &amp;#39;data&amp;#39;) / filename&lt;/span&gt;
&lt;span class="gi"&gt;+        if format == &amp;#39;fits&amp;#39;:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            hdulist = fits.open(path)
&lt;span class="gd"&gt;-            self.data = np.array(hdulist[0].data)&lt;/span&gt;
&lt;span class="gd"&gt;-            self.header = hdulist[0].header&lt;/span&gt;
&lt;span class="gi"&gt;+            data = np.array(hdulist[0].data)&lt;/span&gt;
&lt;span class="gi"&gt;+            header = hdulist[0].header&lt;/span&gt;
&lt;span class="gi"&gt;+            return cls(format, data, header)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        else:
&lt;span class="gd"&gt;-            self.data = np.array(Image.open(str(path)))&lt;/span&gt;
&lt;span class="gi"&gt;+            data = np.array(Image.open(str(path)))&lt;/span&gt;
&lt;span class="gi"&gt;+            return cls(format, data)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def write(self, filename: str) -&amp;gt; None:
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Write HiPS tile by a given filename (`None`).
&lt;span class="gu"&gt;@@ -112,7 +104,7 @@ class HipsTile:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        filename : `str`
&lt;span class="w"&gt; &lt;/span&gt;            Name of the file
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-        path = self.path / (&amp;#39;&amp;#39;.join([filename, &amp;#39;.&amp;#39;, self.format]))&lt;/span&gt;
&lt;span class="gi"&gt;+        path = self.path / filename&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        if self.format == &amp;#39;fits&amp;#39;:
&lt;span class="w"&gt; &lt;/span&gt;            hdu = fits.PrimaryHDU(self.data, header=self.header)
&lt;span class="w"&gt; &lt;/span&gt;            hdulist = fits.HDUList([hdu])

commit 12f0b4fe059cdd29ecb1f37956320570c87791b8
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Tue Jun 20 17:58:47 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Use / instead of joinpath

&lt;span class="gh"&gt;diff --git a/hips/tiles/tile.py b/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gh"&gt;index a0eca23..8b033e1 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -96,7 +96,7 @@ class HipsTile:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def read(self) -&amp;gt; None:
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Read HiPS tile data from a directory and load into memory (`None`).&amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-        path = self.path.joinpath(&amp;#39;&amp;#39;.join([&amp;#39;Npix&amp;#39;, str(self.ipix), &amp;#39;.&amp;#39;, self.format]))&lt;/span&gt;
&lt;span class="gi"&gt;+        path = self.path / (&amp;#39;&amp;#39;.join([&amp;#39;Npix&amp;#39;, str(self.ipix), &amp;#39;.&amp;#39;, self.format]))&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        if self.format == &amp;#39;fits&amp;#39;:
&lt;span class="w"&gt; &lt;/span&gt;            hdulist = fits.open(path)
&lt;span class="w"&gt; &lt;/span&gt;            self.data = np.array(hdulist[0].data)
&lt;span class="gu"&gt;@@ -112,7 +112,7 @@ class HipsTile:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        filename : `str`
&lt;span class="w"&gt; &lt;/span&gt;            Name of the file
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-        path = self.path.joinpath(&amp;#39;&amp;#39;.join([filename, &amp;#39;.&amp;#39;, self.format]))&lt;/span&gt;
&lt;span class="gi"&gt;+        path = self.path / (&amp;#39;&amp;#39;.join([filename, &amp;#39;.&amp;#39;, self.format]))&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        if self.format == &amp;#39;fits&amp;#39;:
&lt;span class="w"&gt; &lt;/span&gt;            hdu = fits.PrimaryHDU(self.data, header=self.header)
&lt;span class="w"&gt; &lt;/span&gt;            hdulist = fits.HDUList([hdu])

commit 2876957883d4eacc7984fa554dc176b75f35c1ad
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Tue Jun 20 17:53:59 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Change path opening method

&lt;span class="gh"&gt;diff --git a/hips/tiles/tile.py b/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gh"&gt;index 5f979d2..a0eca23 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -97,14 +97,12 @@ class HipsTile:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    def read(self) -&amp;gt; None:
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Read HiPS tile data from a directory and load into memory (`None`).&amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;        path = self.path.joinpath(&amp;#39;&amp;#39;.join([&amp;#39;Npix&amp;#39;, str(self.ipix), &amp;#39;.&amp;#39;, self.format]))
&lt;span class="gd"&gt;-        with path.open(&amp;#39;rb&amp;#39;) as rf:&lt;/span&gt;
&lt;span class="gd"&gt;-            raw_img = BytesIO(rf.read())&lt;/span&gt;
&lt;span class="gd"&gt;-            if self.format == &amp;#39;fits&amp;#39;:&lt;/span&gt;
&lt;span class="gd"&gt;-                hdulist = fits.open(raw_img)&lt;/span&gt;
&lt;span class="gd"&gt;-                self.data = np.array(hdulist[0].data)&lt;/span&gt;
&lt;span class="gd"&gt;-                self.header = hdulist[0].header&lt;/span&gt;
&lt;span class="gd"&gt;-            else:&lt;/span&gt;
&lt;span class="gd"&gt;-                self.data = np.array(Image.open(raw_img))&lt;/span&gt;
&lt;span class="gi"&gt;+        if self.format == &amp;#39;fits&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+            hdulist = fits.open(path)&lt;/span&gt;
&lt;span class="gi"&gt;+            self.data = np.array(hdulist[0].data)&lt;/span&gt;
&lt;span class="gi"&gt;+            self.header = hdulist[0].header&lt;/span&gt;
&lt;span class="gi"&gt;+        else:&lt;/span&gt;
&lt;span class="gi"&gt;+            self.data = np.array(Image.open(str(path)))&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def write(self, filename: str) -&amp;gt; None:
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Write HiPS tile by a given filename (`None`).

commit b937d1486e2c67c2519c8b572b43e899536ca24c
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Tue Jun 20 14:30:02 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Update tiles.hips module

&lt;span class="gh"&gt;diff --git a/hips/tiles/tests/data/Npix24185.fits b/hips/tiles/tests/data/Npix24185.fits&lt;/span&gt;
deleted file mode 100644
&lt;span class="gh"&gt;index d9786f5..0000000&lt;/span&gt;
Binary files a/hips/tiles/tests/data/Npix24185.fits and /dev/null differ
&lt;span class="gh"&gt;diff --git a/hips/tiles/tests/data/Npix24185.jpg b/hips/tiles/tests/data/Npix24185.jpg&lt;/span&gt;
deleted file mode 100644
&lt;span class="gh"&gt;index 63254fa..0000000&lt;/span&gt;
Binary files a/hips/tiles/tests/data/Npix24185.jpg and /dev/null differ
&lt;span class="gh"&gt;diff --git a/hips/tiles/tests/data/Npix30889.fits b/hips/tiles/tests/data/Npix30889.fits&lt;/span&gt;
new file mode 100644
&lt;span class="gh"&gt;index 0000000..522852f&lt;/span&gt;
Binary files /dev/null and b/hips/tiles/tests/data/Npix30889.fits differ
&lt;span class="gh"&gt;diff --git a/hips/tiles/tests/data/Npix30889.jpg b/hips/tiles/tests/data/Npix30889.jpg&lt;/span&gt;
new file mode 100644
&lt;span class="gh"&gt;index 0000000..b6365db&lt;/span&gt;
Binary files /dev/null and b/hips/tiles/tests/data/Npix30889.jpg differ
&lt;span class="gh"&gt;diff --git a/hips/tiles/tests/test_tile.py b/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gh"&gt;index a6c2107..1316b5d 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,5 +1,6 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="w"&gt; &lt;/span&gt;from astropy.utils.data import get_pkg_data_filename
&lt;span class="gi"&gt;+from astropy.tests.helper import remote_data&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from ..description import HipsDescription
&lt;span class="w"&gt; &lt;/span&gt;from ..tile import HipsTile
&lt;span class="w"&gt; &lt;/span&gt;from pathlib import Path
&lt;span class="gu"&gt;@@ -11,38 +12,50 @@ class TestHipsTile:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        filename = get_pkg_data_filename(&amp;#39;data/properties.txt&amp;#39;)
&lt;span class="w"&gt; &lt;/span&gt;        hips_description = HipsDescription.read(filename)

&lt;span class="gd"&gt;-        cls.tile = HipsTile(hips_description, 6, 24185, &amp;#39;jpg&amp;#39;, 512)&lt;/span&gt;
&lt;span class="gd"&gt;-        cls.tile.base_url = &amp;#39;http://alasky.unistra.fr/DSS/DSSColor&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        cls.fits_tile = HipsTile(hips_description=hips_description, order=6,&lt;/span&gt;
&lt;span class="gi"&gt;+                            ipix=30889, format=&amp;#39;fits&amp;#39;, tile_width=512)&lt;/span&gt;
&lt;span class="gi"&gt;+        cls.jpg_tile = HipsTile(hips_description=hips_description, order=6,&lt;/span&gt;
&lt;span class="gi"&gt;+                            ipix=30889, format=&amp;#39;jpg&amp;#39;, tile_width=512)&lt;/span&gt;
&lt;span class="gi"&gt;+        cls.fits_tile.base_url = &amp;#39;http://alasky.unistra.fr/2MASS/H&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        cls.jpg_tile.base_url = &amp;#39;http://alasky.unistra.fr/2MASS/H&amp;#39;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def test_base_url(self):
&lt;span class="gd"&gt;-        assert self.tile.base_url == &amp;#39;http://alasky.unistra.fr/DSS/DSSColor&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        assert self.fits_tile.base_url == &amp;#39;http://alasky.unistra.fr/2MASS/H&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        assert self.jpg_tile.base_url == &amp;#39;http://alasky.unistra.fr/2MASS/H&amp;#39;&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def test_tile_url(self):
&lt;span class="gd"&gt;-        assert self.tile.tile_url == &amp;#39;http://alasky.unistra.fr/DSS/DSSColor/Norder6/Dir20000/Npix24185.jpg&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        assert self.fits_tile.tile_url == &amp;#39;http://alasky.unistra.fr/2MASS/H/Norder6/Dir30000/Npix30889.fits&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        assert self.jpg_tile.tile_url == &amp;#39;http://alasky.unistra.fr/2MASS/H/Norder6/Dir30000/Npix30889.jpg&amp;#39;&lt;/span&gt;

&lt;span class="gi"&gt;+    @remote_data&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    def test_fetch(self):
&lt;span class="gd"&gt;-        self.tile.fetch()&lt;/span&gt;
&lt;span class="gi"&gt;+        self.fits_tile.fetch()&lt;/span&gt;
&lt;span class="gi"&gt;+        self.jpg_tile.fetch()&lt;/span&gt;

&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-        This data was obtain from &amp;#39;http://alasky.unistra.fr/DSS/DSSColor/Norder6/Dir20000/Npix24185.jpg&amp;#39;&lt;/span&gt;
&lt;span class="gd"&gt;-        at the index [10, 20]&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-        data_precomp = [0, 0, 0]&lt;/span&gt;
&lt;span class="gd"&gt;-        assert self.data[10, 20] == data_precomp&lt;/span&gt;
&lt;span class="gi"&gt;+        shape_fits_precomp = (512, 512)&lt;/span&gt;
&lt;span class="gi"&gt;+        shape_jpg_precomp = (512, 512, 3)&lt;/span&gt;
&lt;span class="gi"&gt;+        data_precomp = [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1]&lt;/span&gt;
&lt;span class="gi"&gt;+        assert self.fits_tile.data.shape == shape_fits_precomp&lt;/span&gt;
&lt;span class="gi"&gt;+        assert self.jpg_tile.data.shape == shape_jpg_precomp&lt;/span&gt;
&lt;span class="gi"&gt;+        assert list(self.fits_tile.data[510][:12]) == data_precomp&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def test_read(self):
&lt;span class="gd"&gt;-        self.tile.read()&lt;/span&gt;
&lt;span class="gi"&gt;+        self.fits_tile.read()&lt;/span&gt;
&lt;span class="gi"&gt;+        self.jpg_tile.read()&lt;/span&gt;

&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-        This data was obtain from &amp;#39;http://alasky.unistra.fr/DSS/DSSColor/Norder6/Dir20000/Npix24185.jpg&amp;#39;&lt;/span&gt;
&lt;span class="gd"&gt;-        at the index [10, 20]&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-        data_precomp = [0, 0, 0]&lt;/span&gt;
&lt;span class="gd"&gt;-        assert list(self.tile.data[10, 20]) == data_precomp&lt;/span&gt;
&lt;span class="gi"&gt;+        shape_fits_precomp = (512, 512)&lt;/span&gt;
&lt;span class="gi"&gt;+        shape_jpg_precomp = (512, 512, 3)&lt;/span&gt;
&lt;span class="gi"&gt;+        data_precomp = [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1]&lt;/span&gt;
&lt;span class="gi"&gt;+        assert (self.fits_tile.data.shape) == shape_fits_precomp&lt;/span&gt;
&lt;span class="gi"&gt;+        assert (self.jpg_tile.data.shape) == shape_jpg_precomp&lt;/span&gt;
&lt;span class="gi"&gt;+        assert list(self.fits_tile.data[510][:12]) == data_precomp&lt;/span&gt;

&lt;span class="gd"&gt;-    def test_store(self):&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_write(self):&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        filename = &amp;#39;test_file&amp;#39;
&lt;span class="gd"&gt;-        self.tile.store(filename)&lt;/span&gt;
&lt;span class="gi"&gt;+        self.fits_tile.write(filename)&lt;/span&gt;
&lt;span class="gi"&gt;+        self.jpg_tile.write(filename)&lt;/span&gt;

&lt;span class="gd"&gt;-        path = self.tile.path.joinpath(&amp;#39;&amp;#39;.join([filename, &amp;#39;.&amp;#39;, self.tile.format]))&lt;/span&gt;
&lt;span class="gd"&gt;-        assert True == path.is_file()&lt;/span&gt;
&lt;span class="gi"&gt;+        path_fits = self.fits_tile.path.joinpath(&amp;#39;&amp;#39;.join([filename, &amp;#39;.&amp;#39;, self.fits_tile.format]))&lt;/span&gt;
&lt;span class="gi"&gt;+        path_jpg = self.jpg_tile.path.joinpath(&amp;#39;&amp;#39;.join([filename, &amp;#39;.&amp;#39;, self.jpg_tile.format]))&lt;/span&gt;
&lt;span class="gi"&gt;+        assert True == path_fits.is_file()&lt;/span&gt;
&lt;span class="gi"&gt;+        assert True == path_jpg.is_file()&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/tiles/tile.py b/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gh"&gt;index a55aa20..5f979d2 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,12 +1,11 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="gd"&gt;-from astropy.utils.data import get_pkg_data_filename&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from .description import HipsDescription
&lt;span class="w"&gt; &lt;/span&gt;from astropy.io import fits
&lt;span class="w"&gt; &lt;/span&gt;from pathlib import Path
&lt;span class="w"&gt; &lt;/span&gt;from io import BytesIO
&lt;span class="w"&gt; &lt;/span&gt;from PIL import Image
&lt;span class="gd"&gt;-import numpy as np&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;import urllib.request
&lt;span class="gi"&gt;+import numpy as np&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;__all__ = [
&lt;span class="w"&gt; &lt;/span&gt;    &amp;#39;HipsTile&amp;#39;,
&lt;span class="gu"&gt;@@ -17,8 +16,8 @@ class HipsTile:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;HiPS tile container.

&lt;span class="w"&gt; &lt;/span&gt;    This class provides methods for fetching, reading,
&lt;span class="gd"&gt;-    and stroing a HiPS tile. It also contains a few&lt;/span&gt;
&lt;span class="gd"&gt;-    getters and setters around most commonly used&lt;/span&gt;
&lt;span class="gi"&gt;+    and writing a HiPS tile. It also contains a few&lt;/span&gt;
&lt;span class="gi"&gt;+    getters and setters around frequently used&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    HiPS tile attributes.

&lt;span class="w"&gt; &lt;/span&gt;    Parameters
&lt;span class="gu"&gt;@@ -26,43 +25,48 @@ class HipsTile:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    hips_description : `HipsDescription`
&lt;span class="w"&gt; &lt;/span&gt;        Class HipsDescription contains HiPS properties
&lt;span class="w"&gt; &lt;/span&gt;    order : `int`
&lt;span class="gd"&gt;-        Order of the HiPS&lt;/span&gt;
&lt;span class="gi"&gt;+        Order of the HiPS tile&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    ipix : `int`
&lt;span class="w"&gt; &lt;/span&gt;        HEALPix pixel number
&lt;span class="gd"&gt;-    tile_width : `int`&lt;/span&gt;
&lt;span class="gd"&gt;-        Width of HiPS tile&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    format : `format`
&lt;span class="w"&gt; &lt;/span&gt;        Format of HiPS tile
&lt;span class="gd"&gt;-    data : `list`&lt;/span&gt;
&lt;span class="gi"&gt;+    data : `numpy.ndarray`&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        Pixel values of HiPS tile
&lt;span class="gi"&gt;+    tile_width : `int`&lt;/span&gt;
&lt;span class="gi"&gt;+        Width of HiPS tile&lt;/span&gt;
&lt;span class="gi"&gt;+    base_url : `str`&lt;/span&gt;
&lt;span class="gi"&gt;+        Base URL of HiPS tile&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    Examples
&lt;span class="w"&gt; &lt;/span&gt;    --------
&lt;span class="w"&gt; &lt;/span&gt;    ::

&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; import urllib.request&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;gt;&amp;gt;&amp;gt; from hips.tiles import HipsDescription
&lt;span class="gd"&gt;-        &amp;gt;&amp;gt;&amp;gt; from astropy.utils.data import get_pkg_data_filename&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;gt;&amp;gt;&amp;gt; filename = get_pkg_data_filename(&amp;#39;tests/data/properties.txt&amp;#39;)&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;gt;&amp;gt;&amp;gt; hips_description = HipsDescription.read(filename)&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;gt;&amp;gt;&amp;gt; hips_tile = HipsTile(hips_description, 6, 24185, &amp;#39;fits&amp;#39;)&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;gt;&amp;gt;&amp;gt; tile.read()&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;gt;&amp;gt;&amp;gt; tile.data&lt;/span&gt;
&lt;span class="gd"&gt;-        [[3617 3304 4196 ..., 4545 4800 5701]&lt;/span&gt;
&lt;span class="gd"&gt;-         [3638 3288 3934 ..., 4448 5029 4951]&lt;/span&gt;
&lt;span class="gd"&gt;-         [3116 3653 3412 ..., 5282 4406 4967]&lt;/span&gt;
&lt;span class="gd"&gt;-         ...,&lt;/span&gt;
&lt;span class="gd"&gt;-         [3890 3463 3448 ..., 5980 6902 6543]&lt;/span&gt;
&lt;span class="gd"&gt;-         [4845 4065 3821 ..., 6928 6557 7197]&lt;/span&gt;
&lt;span class="gd"&gt;-         [4261 3581 3842 ..., 7095 6390 6191]]&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; from astropy.tests.helper import remote_data&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; text = urllib.request.urlopen(&amp;#39;https://raw.githubusercontent.com/hipspy/hips/master/hips/tiles/tests/data/properties.txt&amp;#39;).read() # doctest: +REMOTE_DATA&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; hips_description = HipsDescription.parse(str(text))&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; hips_tile = HipsTile(hips_description=hips_description, order=6, ipix=30889, format=&amp;#39;fits&amp;#39;, tile_width=512)&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; hips_tile.read()&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; hips_tile.data&lt;/span&gt;
&lt;span class="gi"&gt;+        array([[0, 0, 0, ..., 0, 0, 0],&lt;/span&gt;
&lt;span class="gi"&gt;+               [0, 0, 0, ..., 0, 0, 0],&lt;/span&gt;
&lt;span class="gi"&gt;+               [0, 0, 0, ..., 0, 0, 0],&lt;/span&gt;
&lt;span class="gi"&gt;+               ...,&lt;/span&gt;
&lt;span class="gi"&gt;+               [0, 0, 0, ..., 1, 0, 0],&lt;/span&gt;
&lt;span class="gi"&gt;+               [0, 0, 0, ..., 1, 0, 1],&lt;/span&gt;
&lt;span class="gi"&gt;+               [0, 0, 0, ..., 1, 0, 1]], dtype=int16)&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

&lt;span class="gd"&gt;-    def __init__(self, hips_description: HipsDescription, order: int, ipix: int, format: str, data: list=None, tile_width: int=512) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+    def __init__(self, hips_description: HipsDescription, order: int, ipix: int, format: str,&lt;/span&gt;
&lt;span class="gi"&gt;+                 data: np.ndarray=None, tile_width: int=512, base_url: str=None) -&amp;gt; None:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        self.hips_description = hips_description
&lt;span class="w"&gt; &lt;/span&gt;        self.order = order
&lt;span class="w"&gt; &lt;/span&gt;        self.ipix = ipix
&lt;span class="w"&gt; &lt;/span&gt;        self.tile_width = tile_width
&lt;span class="w"&gt; &lt;/span&gt;        self.format = format
&lt;span class="w"&gt; &lt;/span&gt;        self.data = data
&lt;span class="gi"&gt;+        self.base_url = base_url&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    @staticmethod
&lt;span class="w"&gt; &lt;/span&gt;    def _directory(ipix: int) -&amp;gt; int:
&lt;span class="gu"&gt;@@ -70,11 +74,6 @@ class HipsTile:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        return np.around(ipix, decimals=-(len(str(ipix)) - 1))

&lt;span class="w"&gt; &lt;/span&gt;    @property
&lt;span class="gd"&gt;-    def base_url(self) -&amp;gt; str:&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;HiPS tile base url (`str`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-        return self._base_url&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    @property&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    def path(self) -&amp;gt; Path:
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Default path for tile storage (`Path`).&amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;        return Path(&amp;#39;hips&amp;#39;, &amp;#39;tiles&amp;#39;, &amp;#39;tests&amp;#39;, &amp;#39;data&amp;#39;)
&lt;span class="gu"&gt;@@ -82,17 +81,13 @@ class HipsTile:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    @property
&lt;span class="w"&gt; &lt;/span&gt;    def tile_url(self) -&amp;gt; str:
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;HiPS tile url (`str`).&amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-        return &amp;#39;&amp;#39;.join([self._base_url, &amp;#39;/Norder&amp;#39;, str(self.order), &amp;#39;/Dir&amp;#39;, str(self._directory(self.ipix)), &amp;#39;/Npix&amp;#39;, str(self.ipix), &amp;#39;.&amp;#39;, self.format])&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    @base_url.setter&lt;/span&gt;
&lt;span class="gd"&gt;-    def base_url(self, base_url: str) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;Set the HiPS tile base url (`None`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-        self._base_url = base_url&lt;/span&gt;
&lt;span class="gi"&gt;+        return &amp;#39;&amp;#39;.join([self.base_url, &amp;#39;/Norder&amp;#39;, str(self.order), &amp;#39;/Dir&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+               str(self._directory(self.ipix)), &amp;#39;/Npix&amp;#39;, str(self.ipix), &amp;#39;.&amp;#39;, self.format])&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def fetch(self) -&amp;gt; None:
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Fetch HiPS tile and load into memory (`None`).&amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;        raw_image = BytesIO(urllib.request.urlopen(self.tile_url).read())
&lt;span class="gd"&gt;-        if self.format is &amp;#39;fits&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+        if self.format == &amp;#39;fits&amp;#39;:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            hdulist = fits.open(raw_image)
&lt;span class="w"&gt; &lt;/span&gt;            self.data = np.array(hdulist[0].data)
&lt;span class="w"&gt; &lt;/span&gt;            self.header = hdulist[0].header
&lt;span class="gu"&gt;@@ -104,15 +99,15 @@ class HipsTile:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        path = self.path.joinpath(&amp;#39;&amp;#39;.join([&amp;#39;Npix&amp;#39;, str(self.ipix), &amp;#39;.&amp;#39;, self.format]))
&lt;span class="w"&gt; &lt;/span&gt;        with path.open(&amp;#39;rb&amp;#39;) as rf:
&lt;span class="w"&gt; &lt;/span&gt;            raw_img = BytesIO(rf.read())
&lt;span class="gd"&gt;-            if self.format is &amp;#39;fits&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+            if self.format == &amp;#39;fits&amp;#39;:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                hdulist = fits.open(raw_img)
&lt;span class="w"&gt; &lt;/span&gt;                self.data = np.array(hdulist[0].data)
&lt;span class="w"&gt; &lt;/span&gt;                self.header = hdulist[0].header
&lt;span class="w"&gt; &lt;/span&gt;            else:
&lt;span class="w"&gt; &lt;/span&gt;                self.data = np.array(Image.open(raw_img))

&lt;span class="gd"&gt;-    def store(self, filename: str) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;Store HiPS tile by a given filename (`None`).&lt;/span&gt;
&lt;span class="gi"&gt;+    def write(self, filename: str) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Write HiPS tile by a given filename (`None`).&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        Parameters
&lt;span class="w"&gt; &lt;/span&gt;        ----------
&lt;span class="gu"&gt;@@ -120,12 +115,10 @@ class HipsTile:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            Name of the file
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;        path = self.path.joinpath(&amp;#39;&amp;#39;.join([filename, &amp;#39;.&amp;#39;, self.format]))
&lt;span class="gd"&gt;-        print(path)&lt;/span&gt;
&lt;span class="gd"&gt;-        with path.open(&amp;#39;w&amp;#39;) as wf:&lt;/span&gt;
&lt;span class="gd"&gt;-            if self.format is &amp;#39;fits&amp;#39;:&lt;/span&gt;
&lt;span class="gd"&gt;-                hdu = fits.PrimaryHDU(self.data, header=self.header)&lt;/span&gt;
&lt;span class="gd"&gt;-                hdulist = fits.HDUList([hdu])&lt;/span&gt;
&lt;span class="gd"&gt;-                hdulist.writeto(wf)&lt;/span&gt;
&lt;span class="gd"&gt;-                hdulist.close()&lt;/span&gt;
&lt;span class="gd"&gt;-            else:&lt;/span&gt;
&lt;span class="gd"&gt;-                Image.fromarray(self.data).save(wf)&lt;/span&gt;
&lt;span class="gi"&gt;+        if self.format == &amp;#39;fits&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+            hdu = fits.PrimaryHDU(self.data, header=self.header)&lt;/span&gt;
&lt;span class="gi"&gt;+            hdulist = fits.HDUList([hdu])&lt;/span&gt;
&lt;span class="gi"&gt;+            hdulist.writeto(path)&lt;/span&gt;
&lt;span class="gi"&gt;+            hdulist.close()&lt;/span&gt;
&lt;span class="gi"&gt;+        else:&lt;/span&gt;
&lt;span class="gi"&gt;+            Image.fromarray(self.data).save(str(path))&lt;/span&gt;

commit 7869a5d5e36a1bd2219b5b9212b2dcd68ffc77f9
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Mon Jun 19 10:34:01 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Add tile_width property to HipsTile

&lt;span class="gh"&gt;diff --git a/hips/tiles/tests/test_tile.py b/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gh"&gt;index 535b005..a6c2107 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -11,7 +11,7 @@ class TestHipsTile:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        filename = get_pkg_data_filename(&amp;#39;data/properties.txt&amp;#39;)
&lt;span class="w"&gt; &lt;/span&gt;        hips_description = HipsDescription.read(filename)

&lt;span class="gd"&gt;-        cls.tile = HipsTile(hips_description, 6, 24185, &amp;#39;jpg&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        cls.tile = HipsTile(hips_description, 6, 24185, &amp;#39;jpg&amp;#39;, 512)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        cls.tile.base_url = &amp;#39;http://alasky.unistra.fr/DSS/DSSColor&amp;#39;

&lt;span class="w"&gt; &lt;/span&gt;    def test_base_url(self):
&lt;span class="gh"&gt;diff --git a/hips/tiles/tile.py b/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gh"&gt;index 4f0fd3e..a55aa20 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -29,6 +29,8 @@ class HipsTile:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        Order of the HiPS
&lt;span class="w"&gt; &lt;/span&gt;    ipix : `int`
&lt;span class="w"&gt; &lt;/span&gt;        HEALPix pixel number
&lt;span class="gi"&gt;+    tile_width : `int`&lt;/span&gt;
&lt;span class="gi"&gt;+        Width of HiPS tile&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    format : `format`
&lt;span class="w"&gt; &lt;/span&gt;        Format of HiPS tile
&lt;span class="w"&gt; &lt;/span&gt;    data : `list`
&lt;span class="gu"&gt;@@ -54,10 +56,11 @@ class HipsTile:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;         [4261 3581 3842 ..., 7095 6390 6191]]
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;

&lt;span class="gd"&gt;-    def __init__(self, hips_description: HipsDescription, order: int, ipix: int, format: str, data: list=None) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+    def __init__(self, hips_description: HipsDescription, order: int, ipix: int, format: str, data: list=None, tile_width: int=512) -&amp;gt; None:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        self.hips_description = hips_description
&lt;span class="w"&gt; &lt;/span&gt;        self.order = order
&lt;span class="w"&gt; &lt;/span&gt;        self.ipix = ipix
&lt;span class="gi"&gt;+        self.tile_width = tile_width&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        self.format = format
&lt;span class="w"&gt; &lt;/span&gt;        self.data = data


commit 27cca875b322df5a4b0a9caa01d081f9fb3054b0
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Jun 16 20:16:00 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Update tiles.hips module

&lt;span class="w"&gt; &lt;/span&gt;   This commit adds some extra methods for reading tiles (from disk and URL). It also provides a method
&lt;span class="w"&gt; &lt;/span&gt;   for storing a HiPS tile on disk. The docstring is updated to contain an example showing fetching of a HiPS tile.

&lt;span class="gh"&gt;diff --git a/hips/tiles/tests/data/Npix24185.fits b/hips/tiles/tests/data/Npix24185.fits&lt;/span&gt;
new file mode 100644
&lt;span class="gh"&gt;index 0000000..d9786f5&lt;/span&gt;
Binary files /dev/null and b/hips/tiles/tests/data/Npix24185.fits differ
&lt;span class="gh"&gt;diff --git a/hips/tiles/tests/data/Npix24185.jpg b/hips/tiles/tests/data/Npix24185.jpg&lt;/span&gt;
new file mode 100644
&lt;span class="gh"&gt;index 0000000..63254fa&lt;/span&gt;
Binary files /dev/null and b/hips/tiles/tests/data/Npix24185.jpg differ
&lt;span class="gh"&gt;diff --git a/hips/tiles/tests/test_tile.py b/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gh"&gt;index f1f050b..535b005 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,8 +1,9 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="w"&gt; &lt;/span&gt;from astropy.utils.data import get_pkg_data_filename
&lt;span class="gd"&gt;-from ..tile import HipsTile&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from ..description import HipsDescription
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gi"&gt;+from ..tile import HipsTile&lt;/span&gt;
&lt;span class="gi"&gt;+from pathlib import Path&lt;/span&gt;
&lt;span class="gi"&gt;+import numpy as np&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;class TestHipsTile:
&lt;span class="w"&gt; &lt;/span&gt;    @classmethod
&lt;span class="gu"&gt;@@ -11,14 +12,37 @@ class TestHipsTile:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        hips_description = HipsDescription.read(filename)

&lt;span class="w"&gt; &lt;/span&gt;        cls.tile = HipsTile(hips_description, 6, 24185, &amp;#39;jpg&amp;#39;)
&lt;span class="gd"&gt;-        cls.tile.data = [0, 0, 0, 0]&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        cls.tile.base_url = &amp;#39;http://alasky.unistra.fr/DSS/DSSColor&amp;#39;

&lt;span class="w"&gt; &lt;/span&gt;    def test_base_url(self):
&lt;span class="w"&gt; &lt;/span&gt;        assert self.tile.base_url == &amp;#39;http://alasky.unistra.fr/DSS/DSSColor&amp;#39;

&lt;span class="gd"&gt;-    def test_data(self):&lt;/span&gt;
&lt;span class="gd"&gt;-        assert self.tile.data == [0, 0, 0, 0]&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    def test_tile_url(self):
&lt;span class="gd"&gt;-        assert self.tile.get_tile_url(6, 24185, &amp;#39;jpg&amp;#39;) == &amp;#39;http://alasky.unistra.fr/DSS/DSSColor/Norder6/Dir20000/Npix24185.jpg&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        assert self.tile.tile_url == &amp;#39;http://alasky.unistra.fr/DSS/DSSColor/Norder6/Dir20000/Npix24185.jpg&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_fetch(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        self.tile.fetch()&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        This data was obtain from &amp;#39;http://alasky.unistra.fr/DSS/DSSColor/Norder6/Dir20000/Npix24185.jpg&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        at the index [10, 20]&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        data_precomp = [0, 0, 0]&lt;/span&gt;
&lt;span class="gi"&gt;+        assert self.data[10, 20] == data_precomp&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_read(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        self.tile.read()&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        This data was obtain from &amp;#39;http://alasky.unistra.fr/DSS/DSSColor/Norder6/Dir20000/Npix24185.jpg&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        at the index [10, 20]&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        data_precomp = [0, 0, 0]&lt;/span&gt;
&lt;span class="gi"&gt;+        assert list(self.tile.data[10, 20]) == data_precomp&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_store(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        filename = &amp;#39;test_file&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        self.tile.store(filename)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        path = self.tile.path.joinpath(&amp;#39;&amp;#39;.join([filename, &amp;#39;.&amp;#39;, self.tile.format]))&lt;/span&gt;
&lt;span class="gi"&gt;+        assert True == path.is_file()&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/tiles/tile.py b/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gh"&gt;index 70cad39..4f0fd3e 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,6 +1,12 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="gi"&gt;+from astropy.utils.data import get_pkg_data_filename&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from .description import HipsDescription
&lt;span class="gi"&gt;+from astropy.io import fits&lt;/span&gt;
&lt;span class="gi"&gt;+from pathlib import Path&lt;/span&gt;
&lt;span class="gi"&gt;+from io import BytesIO&lt;/span&gt;
&lt;span class="gi"&gt;+from PIL import Image&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;import numpy as np
&lt;span class="gi"&gt;+import urllib.request&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;__all__ = [
&lt;span class="w"&gt; &lt;/span&gt;    &amp;#39;HipsTile&amp;#39;,
&lt;span class="gu"&gt;@@ -10,36 +16,113 @@ __all__ = [&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;class HipsTile:
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;HiPS tile container.

&lt;span class="gi"&gt;+    This class provides methods for fetching, reading,&lt;/span&gt;
&lt;span class="gi"&gt;+    and stroing a HiPS tile. It also contains a few&lt;/span&gt;
&lt;span class="gi"&gt;+    getters and setters around most commonly used&lt;/span&gt;
&lt;span class="gi"&gt;+    HiPS tile attributes.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    Parameters&lt;/span&gt;
&lt;span class="gi"&gt;+    ----------&lt;/span&gt;
&lt;span class="gi"&gt;+    hips_description : `HipsDescription`&lt;/span&gt;
&lt;span class="gi"&gt;+        Class HipsDescription contains HiPS properties&lt;/span&gt;
&lt;span class="gi"&gt;+    order : `int`&lt;/span&gt;
&lt;span class="gi"&gt;+        Order of the HiPS&lt;/span&gt;
&lt;span class="gi"&gt;+    ipix : `int`&lt;/span&gt;
&lt;span class="gi"&gt;+        HEALPix pixel number&lt;/span&gt;
&lt;span class="gi"&gt;+    format : `format`&lt;/span&gt;
&lt;span class="gi"&gt;+        Format of HiPS tile&lt;/span&gt;
&lt;span class="gi"&gt;+    data : `list`&lt;/span&gt;
&lt;span class="gi"&gt;+        Pixel values of HiPS tile&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    Examples&lt;/span&gt;
&lt;span class="gi"&gt;+    --------&lt;/span&gt;
&lt;span class="gi"&gt;+    ::&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; from hips.tiles import HipsDescription&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; from astropy.utils.data import get_pkg_data_filename&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; filename = get_pkg_data_filename(&amp;#39;tests/data/properties.txt&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; hips_description = HipsDescription.read(filename)&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; hips_tile = HipsTile(hips_description, 6, 24185, &amp;#39;fits&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; tile.read()&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; tile.data&lt;/span&gt;
&lt;span class="gi"&gt;+        [[3617 3304 4196 ..., 4545 4800 5701]&lt;/span&gt;
&lt;span class="gi"&gt;+         [3638 3288 3934 ..., 4448 5029 4951]&lt;/span&gt;
&lt;span class="gi"&gt;+         [3116 3653 3412 ..., 5282 4406 4967]&lt;/span&gt;
&lt;span class="gi"&gt;+         ...,&lt;/span&gt;
&lt;span class="gi"&gt;+         [3890 3463 3448 ..., 5980 6902 6543]&lt;/span&gt;
&lt;span class="gi"&gt;+         [4845 4065 3821 ..., 6928 6557 7197]&lt;/span&gt;
&lt;span class="gi"&gt;+         [4261 3581 3842 ..., 7095 6390 6191]]&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;

&lt;span class="gd"&gt;-    def __init__(self, hips_description: HipsDescription, order: int, ipix: int, format: str) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+    def __init__(self, hips_description: HipsDescription, order: int, ipix: int, format: str, data: list=None) -&amp;gt; None:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        self.hips_description = hips_description
&lt;span class="w"&gt; &lt;/span&gt;        self.order = order
&lt;span class="w"&gt; &lt;/span&gt;        self.ipix = ipix
&lt;span class="w"&gt; &lt;/span&gt;        self.format = format
&lt;span class="gi"&gt;+        self.data = data&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @staticmethod&lt;/span&gt;
&lt;span class="gi"&gt;+    def _directory(ipix: int) -&amp;gt; int:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Directory of the HiPS tile (`int`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        return np.around(ipix, decimals=-(len(str(ipix)) - 1))&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    @property
&lt;span class="w"&gt; &lt;/span&gt;    def base_url(self) -&amp;gt; str:
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;HiPS tile base url (`str`).&amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-        return self.base_url&lt;/span&gt;
&lt;span class="gi"&gt;+        return self._base_url&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    @property
&lt;span class="gd"&gt;-    def data(self) -&amp;gt; list:&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;HiPS tile data (`list`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-        return self.data&lt;/span&gt;
&lt;span class="gi"&gt;+    def path(self) -&amp;gt; Path:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Default path for tile storage (`Path`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        return Path(&amp;#39;hips&amp;#39;, &amp;#39;tiles&amp;#39;, &amp;#39;tests&amp;#39;, &amp;#39;data&amp;#39;)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    @property
&lt;span class="w"&gt; &lt;/span&gt;    def tile_url(self) -&amp;gt; str:
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;HiPS tile url (`str`).&amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-        directory = np.around(self.ipix, decimals=-(len(str(self.ipix)) - 1))&lt;/span&gt;
&lt;span class="gd"&gt;-        return &amp;#39;&amp;#39;.join([self.base_url, &amp;#39;/Norder&amp;#39;, str(self.order), &amp;#39;/Dir&amp;#39;, str(directory), &amp;#39;/Npix&amp;#39;, str(self.ipix), &amp;#39;.&amp;#39;, self.format])&lt;/span&gt;
&lt;span class="gi"&gt;+        return &amp;#39;&amp;#39;.join([self._base_url, &amp;#39;/Norder&amp;#39;, str(self.order), &amp;#39;/Dir&amp;#39;, str(self._directory(self.ipix)), &amp;#39;/Npix&amp;#39;, str(self.ipix), &amp;#39;.&amp;#39;, self.format])&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    @base_url.setter
&lt;span class="w"&gt; &lt;/span&gt;    def base_url(self, base_url: str) -&amp;gt; None:
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Set the HiPS tile base url (`None`).&amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;        self._base_url = base_url

&lt;span class="gd"&gt;-    @data.setter&lt;/span&gt;
&lt;span class="gd"&gt;-    def data(self, data: list) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;Set the HiPS tile data (`None`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-        self.data = data&lt;/span&gt;
&lt;span class="gi"&gt;+    def fetch(self) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Fetch HiPS tile and load into memory (`None`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        raw_image = BytesIO(urllib.request.urlopen(self.tile_url).read())&lt;/span&gt;
&lt;span class="gi"&gt;+        if self.format is &amp;#39;fits&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+            hdulist = fits.open(raw_image)&lt;/span&gt;
&lt;span class="gi"&gt;+            self.data = np.array(hdulist[0].data)&lt;/span&gt;
&lt;span class="gi"&gt;+            self.header = hdulist[0].header&lt;/span&gt;
&lt;span class="gi"&gt;+        else:&lt;/span&gt;
&lt;span class="gi"&gt;+            self.data = np.array(Image.open(raw_image))&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def read(self) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Read HiPS tile data from a directory and load into memory (`None`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        path = self.path.joinpath(&amp;#39;&amp;#39;.join([&amp;#39;Npix&amp;#39;, str(self.ipix), &amp;#39;.&amp;#39;, self.format]))&lt;/span&gt;
&lt;span class="gi"&gt;+        with path.open(&amp;#39;rb&amp;#39;) as rf:&lt;/span&gt;
&lt;span class="gi"&gt;+            raw_img = BytesIO(rf.read())&lt;/span&gt;
&lt;span class="gi"&gt;+            if self.format is &amp;#39;fits&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+                hdulist = fits.open(raw_img)&lt;/span&gt;
&lt;span class="gi"&gt;+                self.data = np.array(hdulist[0].data)&lt;/span&gt;
&lt;span class="gi"&gt;+                self.header = hdulist[0].header&lt;/span&gt;
&lt;span class="gi"&gt;+            else:&lt;/span&gt;
&lt;span class="gi"&gt;+                self.data = np.array(Image.open(raw_img))&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def store(self, filename: str) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Store HiPS tile by a given filename (`None`).&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        Parameters&lt;/span&gt;
&lt;span class="gi"&gt;+        ----------&lt;/span&gt;
&lt;span class="gi"&gt;+        filename : `str`&lt;/span&gt;
&lt;span class="gi"&gt;+            Name of the file&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        path = self.path.joinpath(&amp;#39;&amp;#39;.join([filename, &amp;#39;.&amp;#39;, self.format]))&lt;/span&gt;
&lt;span class="gi"&gt;+        print(path)&lt;/span&gt;
&lt;span class="gi"&gt;+        with path.open(&amp;#39;w&amp;#39;) as wf:&lt;/span&gt;
&lt;span class="gi"&gt;+            if self.format is &amp;#39;fits&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+                hdu = fits.PrimaryHDU(self.data, header=self.header)&lt;/span&gt;
&lt;span class="gi"&gt;+                hdulist = fits.HDUList([hdu])&lt;/span&gt;
&lt;span class="gi"&gt;+                hdulist.writeto(wf)&lt;/span&gt;
&lt;span class="gi"&gt;+                hdulist.close()&lt;/span&gt;
&lt;span class="gi"&gt;+            else:&lt;/span&gt;
&lt;span class="gi"&gt;+                Image.fromarray(self.data).save(wf)&lt;/span&gt;

commit b5a268688e48d775a6aceb82898af8fc745c3b9f
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Thu Jun 15 15:32:30 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Update tiles.hips module

&lt;span class="w"&gt; &lt;/span&gt;   Renamed the main Hips class to HipsTile. Added additional methods and test cases

&lt;span class="gh"&gt;diff --git a/hips/tiles/__init__.py b/hips/tiles/__init__.py&lt;/span&gt;
&lt;span class="gh"&gt;index 12ac39b..f6101be 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/__init__.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/__init__.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,4 +1,4 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="w"&gt; &lt;/span&gt;&amp;quot;&amp;quot;&amp;quot;Classes and functions to manage HiPS tiles.&amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-from .hips import *&lt;/span&gt;
&lt;span class="gi"&gt;+from .tile import *&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from .description import *
&lt;span class="gh"&gt;diff --git a/hips/tiles/hips.py b/hips/tiles/hips.py&lt;/span&gt;
deleted file mode 100644
&lt;span class="gh"&gt;index c01c0b8..0000000&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/hips.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ /dev/null&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,31 +0,0 @@&lt;/span&gt;
&lt;span class="gd"&gt;-# Licensed under a 3-clause BSD style license - see LICENSE.rst&lt;/span&gt;
&lt;span class="gd"&gt;-from .description import HipsDescription&lt;/span&gt;
&lt;span class="gd"&gt;-import numpy as np&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-__all__ = [&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;#39;Hips&amp;#39;,&lt;/span&gt;
&lt;span class="gd"&gt;-]&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-class Hips:&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;quot;&amp;quot;&amp;quot;HiPS base url container.&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    def __init__(self) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gd"&gt;-        pass&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    @property&lt;/span&gt;
&lt;span class="gd"&gt;-    def base_url(self) -&amp;gt; str:&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;HiPS base url (`str`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-        return self.base_url&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    @base_url.setter&lt;/span&gt;
&lt;span class="gd"&gt;-    def base_url(self, base_url: str) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;Set the HiPS base url (`None`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-        self.base_url = base_url&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    def get_tile_url(self, order: int, ipix: int, format: str) -&amp;gt; str:&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;HiPS tile url (`str`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-        directory = np.around(ipix, decimals=-(len(str(ipix)) - 1))&lt;/span&gt;
&lt;span class="gd"&gt;-        return &amp;#39;&amp;#39;.join([self.base_url, &amp;#39;/Norder&amp;#39;, str(order), &amp;#39;/Dir&amp;#39;, str(directory), &amp;#39;/Npix&amp;#39;, str(ipix), &amp;#39;.&amp;#39;, format])&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/tiles/tests/test_hips.py b/hips/tiles/tests/test_hips.py&lt;/span&gt;
deleted file mode 100644
&lt;span class="gh"&gt;index 831ccb3..0000000&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tests/test_hips.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ /dev/null&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,15 +0,0 @@&lt;/span&gt;
&lt;span class="gd"&gt;-# Licensed under a 3-clause BSD style license - see LICENSE.rst&lt;/span&gt;
&lt;span class="gd"&gt;-from ..hips import Hips&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-class TestHiPS:&lt;/span&gt;
&lt;span class="gd"&gt;-    @classmethod&lt;/span&gt;
&lt;span class="gd"&gt;-    def setup_class(cls):&lt;/span&gt;
&lt;span class="gd"&gt;-        cls.hips = Hips&lt;/span&gt;
&lt;span class="gd"&gt;-        cls.hips.base_url = &amp;#39;http://alasky.unistra.fr/DSS/DSSColor&amp;#39;&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    def test_base_url(self):&lt;/span&gt;
&lt;span class="gd"&gt;-        assert self.hips.base_url == &amp;#39;http://alasky.unistra.fr/DSS/DSSColor&amp;#39;&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    def test_tile_url(self):&lt;/span&gt;
&lt;span class="gd"&gt;-        assert self.hips.get_tile_url(self.hips, 6, 24185, &amp;#39;jpg&amp;#39;) == &amp;#39;http://alasky.unistra.fr/DSS/DSSColor/Norder6/Dir20000/Npix24185.jpg&amp;#39;&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/tiles/tests/test_tile.py b/hips/tiles/tests/test_tile.py&lt;/span&gt;
new file mode 100644
&lt;span class="gh"&gt;index 0000000..f1f050b&lt;/span&gt;
&lt;span class="gd"&gt;--- /dev/null&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tests/test_tile.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -0,0 +1,24 @@&lt;/span&gt;
&lt;span class="gi"&gt;+# Licensed under a 3-clause BSD style license - see LICENSE.rst&lt;/span&gt;
&lt;span class="gi"&gt;+from astropy.utils.data import get_pkg_data_filename&lt;/span&gt;
&lt;span class="gi"&gt;+from ..tile import HipsTile&lt;/span&gt;
&lt;span class="gi"&gt;+from ..description import HipsDescription&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+class TestHipsTile:&lt;/span&gt;
&lt;span class="gi"&gt;+    @classmethod&lt;/span&gt;
&lt;span class="gi"&gt;+    def setup_class(cls):&lt;/span&gt;
&lt;span class="gi"&gt;+        filename = get_pkg_data_filename(&amp;#39;data/properties.txt&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        hips_description = HipsDescription.read(filename)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        cls.tile = HipsTile(hips_description, 6, 24185, &amp;#39;jpg&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        cls.tile.data = [0, 0, 0, 0]&lt;/span&gt;
&lt;span class="gi"&gt;+        cls.tile.base_url = &amp;#39;http://alasky.unistra.fr/DSS/DSSColor&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_base_url(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        assert self.tile.base_url == &amp;#39;http://alasky.unistra.fr/DSS/DSSColor&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_data(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        assert self.tile.data == [0, 0, 0, 0]&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_tile_url(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        assert self.tile.get_tile_url(6, 24185, &amp;#39;jpg&amp;#39;) == &amp;#39;http://alasky.unistra.fr/DSS/DSSColor/Norder6/Dir20000/Npix24185.jpg&amp;#39;&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/tiles/tile.py b/hips/tiles/tile.py&lt;/span&gt;
new file mode 100644
&lt;span class="gh"&gt;index 0000000..70cad39&lt;/span&gt;
&lt;span class="gd"&gt;--- /dev/null&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tile.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -0,0 +1,45 @@&lt;/span&gt;
&lt;span class="gi"&gt;+# Licensed under a 3-clause BSD style license - see LICENSE.rst&lt;/span&gt;
&lt;span class="gi"&gt;+from .description import HipsDescription&lt;/span&gt;
&lt;span class="gi"&gt;+import numpy as np&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+__all__ = [&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;#39;HipsTile&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+]&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+class HipsTile:&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;HiPS tile container.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def __init__(self, hips_description: HipsDescription, order: int, ipix: int, format: str) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+        self.hips_description = hips_description&lt;/span&gt;
&lt;span class="gi"&gt;+        self.order = order&lt;/span&gt;
&lt;span class="gi"&gt;+        self.ipix = ipix&lt;/span&gt;
&lt;span class="gi"&gt;+        self.format = format&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @property&lt;/span&gt;
&lt;span class="gi"&gt;+    def base_url(self) -&amp;gt; str:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;HiPS tile base url (`str`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        return self.base_url&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @property&lt;/span&gt;
&lt;span class="gi"&gt;+    def data(self) -&amp;gt; list:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;HiPS tile data (`list`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        return self.data&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @property&lt;/span&gt;
&lt;span class="gi"&gt;+    def tile_url(self) -&amp;gt; str:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;HiPS tile url (`str`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        directory = np.around(self.ipix, decimals=-(len(str(self.ipix)) - 1))&lt;/span&gt;
&lt;span class="gi"&gt;+        return &amp;#39;&amp;#39;.join([self.base_url, &amp;#39;/Norder&amp;#39;, str(self.order), &amp;#39;/Dir&amp;#39;, str(directory), &amp;#39;/Npix&amp;#39;, str(self.ipix), &amp;#39;.&amp;#39;, self.format])&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @base_url.setter&lt;/span&gt;
&lt;span class="gi"&gt;+    def base_url(self, base_url: str) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Set the HiPS tile base url (`None`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        self._base_url = base_url&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @data.setter&lt;/span&gt;
&lt;span class="gi"&gt;+    def data(self, data: list) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Set the HiPS tile data (`None`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        self.data = data&lt;/span&gt;

commit 1f0b0dfb873e83e3af0d432e49a245ec650636da
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Wed Jun 14 09:27:43 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Remove blank lines and docstring

&lt;span class="gh"&gt;diff --git a/hips/tiles/hips.py b/hips/tiles/hips.py&lt;/span&gt;
&lt;span class="gh"&gt;index 1c7fafd..c01c0b8 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/hips.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/hips.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,7 +1,4 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="gd"&gt;-&amp;quot;&amp;quot;&amp;quot;Classes and functions to manage HiPS tiles.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from .description import HipsDescription
&lt;span class="w"&gt; &lt;/span&gt;import numpy as np


commit 6826c7b9f97f45b2ce62b6f700a7a8758c9661a3
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Wed Jun 14 09:24:54 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Update .gitignore to ignore Mypy cache directory

&lt;span class="gh"&gt;diff --git a/.gitignore b/.gitignore&lt;/span&gt;
&lt;span class="gh"&gt;index 21dfb4f..ad63e7f 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/.gitignore&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/.gitignore&lt;/span&gt;
&lt;span class="gu"&gt;@@ -29,6 +29,9 @@ docs/_build&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Pycharm editor project files
&lt;span class="w"&gt; &lt;/span&gt;.idea

&lt;span class="gi"&gt;+# Mypy cache&lt;/span&gt;
&lt;span class="gi"&gt;+.mypy_cache&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Floobits project files
&lt;span class="w"&gt; &lt;/span&gt;.floo
&lt;span class="w"&gt; &lt;/span&gt;.flooignore

commit a16a78f789b5f96f814a8856f503f979693202cb
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Wed Jun 14 09:24:00 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Add tiles.hips package

&lt;span class="gh"&gt;diff --git a/hips/tiles/__init__.py b/hips/tiles/__init__.py&lt;/span&gt;
&lt;span class="gh"&gt;index d19ff3e..12ac39b 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/__init__.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/__init__.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,3 +1,4 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="w"&gt; &lt;/span&gt;&amp;quot;&amp;quot;&amp;quot;Classes and functions to manage HiPS tiles.&amp;quot;&amp;quot;&amp;quot;
&lt;span class="gi"&gt;+from .hips import *&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from .description import *
&lt;span class="gh"&gt;diff --git a/hips/tiles/hips.py b/hips/tiles/hips.py&lt;/span&gt;
new file mode 100644
&lt;span class="gh"&gt;index 0000000..1c7fafd&lt;/span&gt;
&lt;span class="gd"&gt;--- /dev/null&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/hips.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -0,0 +1,34 @@&lt;/span&gt;
&lt;span class="gi"&gt;+# Licensed under a 3-clause BSD style license - see LICENSE.rst&lt;/span&gt;
&lt;span class="gi"&gt;+&amp;quot;&amp;quot;&amp;quot;Classes and functions to manage HiPS tiles.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+from .description import HipsDescription&lt;/span&gt;
&lt;span class="gi"&gt;+import numpy as np&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+__all__ = [&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;#39;Hips&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+]&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+class Hips:&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;HiPS base url container.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def __init__(self) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+        pass&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @property&lt;/span&gt;
&lt;span class="gi"&gt;+    def base_url(self) -&amp;gt; str:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;HiPS base url (`str`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        return self.base_url&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @base_url.setter&lt;/span&gt;
&lt;span class="gi"&gt;+    def base_url(self, base_url: str) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Set the HiPS base url (`None`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        self.base_url = base_url&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def get_tile_url(self, order: int, ipix: int, format: str) -&amp;gt; str:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;HiPS tile url (`str`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        directory = np.around(ipix, decimals=-(len(str(ipix)) - 1))&lt;/span&gt;
&lt;span class="gi"&gt;+        return &amp;#39;&amp;#39;.join([self.base_url, &amp;#39;/Norder&amp;#39;, str(order), &amp;#39;/Dir&amp;#39;, str(directory), &amp;#39;/Npix&amp;#39;, str(ipix), &amp;#39;.&amp;#39;, format])&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/tiles/tests/test_hips.py b/hips/tiles/tests/test_hips.py&lt;/span&gt;
new file mode 100644
&lt;span class="gh"&gt;index 0000000..831ccb3&lt;/span&gt;
&lt;span class="gd"&gt;--- /dev/null&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tests/test_hips.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -0,0 +1,15 @@&lt;/span&gt;
&lt;span class="gi"&gt;+# Licensed under a 3-clause BSD style license - see LICENSE.rst&lt;/span&gt;
&lt;span class="gi"&gt;+from ..hips import Hips&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+class TestHiPS:&lt;/span&gt;
&lt;span class="gi"&gt;+    @classmethod&lt;/span&gt;
&lt;span class="gi"&gt;+    def setup_class(cls):&lt;/span&gt;
&lt;span class="gi"&gt;+        cls.hips = Hips&lt;/span&gt;
&lt;span class="gi"&gt;+        cls.hips.base_url = &amp;#39;http://alasky.unistra.fr/DSS/DSSColor&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_base_url(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        assert self.hips.base_url == &amp;#39;http://alasky.unistra.fr/DSS/DSSColor&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_tile_url(self):&lt;/span&gt;
&lt;span class="gi"&gt;+        assert self.hips.get_tile_url(self.hips, 6, 24185, &amp;#39;jpg&amp;#39;) == &amp;#39;http://alasky.unistra.fr/DSS/DSSColor/Norder6/Dir20000/Npix24185.jpg&amp;#39;&lt;/span&gt;

commit 0ffe20e0ea787f11df65cd384c63e03c522aa514
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Wed Jun 21 13:31:42 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Update hips.utils package

&lt;span class="gh"&gt;diff --git a/hips/utils/healpix.py b/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gh"&gt;index 584be5f..c485bc0 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -5,6 +5,8 @@ This module contains wrapper functions around HEALPix utilizing&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;the healpy library
&lt;span class="w"&gt; &lt;/span&gt;&amp;quot;&amp;quot;&amp;quot;

&lt;span class="gi"&gt;+__doctest_skip__ = [&amp;#39;boundaries&amp;#39;, &amp;#39;compute_healpix_pixel_indices&amp;#39;]&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;__all__ = [
&lt;span class="w"&gt; &lt;/span&gt;    &amp;#39;boundaries&amp;#39;, &amp;#39;compute_healpix_pixel_indices&amp;#39;
&lt;span class="w"&gt; &lt;/span&gt;]
&lt;span class="gu"&gt;@@ -87,15 +89,17 @@ def compute_healpix_pixel_indices(wcs_geometry: WCSGeometry, nside: int) -&amp;gt; np.n&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;gt;&amp;gt;&amp;gt; order = 3
&lt;span class="w"&gt; &lt;/span&gt;        &amp;gt;&amp;gt;&amp;gt; nside = hp.order2nside(order)
&lt;span class="w"&gt; &lt;/span&gt;        &amp;gt;&amp;gt;&amp;gt; skycoord = SkyCoord(10, 20, unit=&amp;quot;deg&amp;quot;)
&lt;span class="gd"&gt;-        &amp;gt;&amp;gt;&amp;gt; wcs_geometry = WCSGeometry.create(skydir=skycoord, shape=(10, 20), coordsys=&amp;#39;CEL&amp;#39;, projection=&amp;#39;AIT&amp;#39;, cdelt=1.0, crpix=(1., 1.))&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; wcs_geometry = WCSGeometry.create(skydir=skycoord, shape=(10, 20), \&lt;/span&gt;
&lt;span class="gi"&gt;+coordsys=&amp;#39;CEL&amp;#39;, projection=&amp;#39;AIT&amp;#39;, \&lt;/span&gt;
&lt;span class="gi"&gt;+cdelt=1.0, crpix=(1., 1.))&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;gt;&amp;gt;&amp;gt; compute_healpix_pixel_indices(wcs_geometry, nside)
&lt;span class="w"&gt; &lt;/span&gt;        [ 84 111 112 113 142 143 144 145 146 174 175 176 177 178 206 207 208 209
&lt;span class="w"&gt; &lt;/span&gt;        210 238 239 240 241 270 271 272 273 274 302 303 304 305 334 335 336 337
&lt;span class="w"&gt; &lt;/span&gt;        367 368 399]
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-    y_center, x_center = wcs_geometry.shape[0] // 2, wcs_geometry.shape[1] // 2&lt;/span&gt;
&lt;span class="gi"&gt;+    y_center, x_center = wcs_geometry.Shape.ny / 2, wcs_geometry.Shape.nx / 2&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    lon_center, lat_center = wcs_geometry.wcs.all_pix2world(x_center, y_center, 1)
&lt;span class="gd"&gt;-    vec = hp.ang2vec(lon_center, lat_center, lonlat=True)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    separations = angular_separation(x_center, y_center, lon_center, lat_center)
&lt;span class="w"&gt; &lt;/span&gt;    max_separation = np.nanmax(separations)
&lt;span class="gi"&gt;+    vec = hp.ang2vec(lon_center, lat_center, lonlat=True)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    return hp.query_disc(nside, vec, max_separation)
&lt;span class="gh"&gt;diff --git a/hips/utils/tests/test_healpix.py b/hips/utils/tests/test_healpix.py&lt;/span&gt;
&lt;span class="gh"&gt;index 3d8952e..ec2dc3d 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/tests/test_healpix.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/tests/test_healpix.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -26,15 +26,14 @@ def test_boundaries():&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    assert_allclose([radec.ra, radec.dec], radec_precomp)

&lt;span class="w"&gt; &lt;/span&gt;def test_compute_healpix_pixel_indices():
&lt;span class="gd"&gt;-    order = 3&lt;/span&gt;
&lt;span class="gd"&gt;-    nside = hp.order2nside(order)&lt;/span&gt;
&lt;span class="gi"&gt;+    nside = hp.order2nside(order=3)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    skycoord = SkyCoord(10, 20, unit=&amp;quot;deg&amp;quot;)
&lt;span class="gd"&gt;-    wcs_geometry = WCSGeometry.create(skydir=skycoord, shape=(10, 20), coordsys=&amp;#39;CEL&amp;#39;, projection=&amp;#39;AIT&amp;#39;, cdelt=1.0, crpix=(1., 1.))&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gi"&gt;+    wcs_geometry_CEL = WCSGeometry.create(skydir=skycoord, shape=(10, 20), coordsys=&amp;#39;CEL&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+                                      projection=&amp;#39;AIT&amp;#39;, cdelt=1.0, crpix=(1., 1.))&lt;/span&gt;
&lt;span class="gi"&gt;+    wcs_geometry_GAL = WCSGeometry.create(skydir=skycoord, shape=(10, 20), coordsys=&amp;#39;GAL&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+                                      projection=&amp;#39;AIT&amp;#39;, cdelt=1.0, crpix=(1., 1.))&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    pixels_precomp = [84, 111, 112, 113]
&lt;span class="gd"&gt;-    len_precomp = 39&lt;/span&gt;
&lt;span class="gd"&gt;-    pixels = compute_healpix_pixel_indices(wcs_geometry, nside)&lt;/span&gt;
&lt;span class="gd"&gt;-    print(pixels)&lt;/span&gt;
&lt;span class="gd"&gt;-    assert_allclose(len(pixels), len_precomp)&lt;/span&gt;
&lt;span class="gi"&gt;+    pixels = compute_healpix_pixel_indices(wcs_geometry_CEL, nside)&lt;/span&gt;
&lt;span class="gi"&gt;+    assert len(pixels) == 39&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    assert_allclose(pixels[0:4], pixels_precomp)
&lt;span class="gh"&gt;diff --git a/hips/utils/wcs.py b/hips/utils/wcs.py&lt;/span&gt;
&lt;span class="gh"&gt;index d1d365e..9a58482 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/wcs.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/wcs.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,8 +1,11 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="w"&gt; &lt;/span&gt;import numpy as np
&lt;span class="w"&gt; &lt;/span&gt;from astropy.wcs import WCS
&lt;span class="gi"&gt;+from collections import namedtuple&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from astropy.coordinates import SkyCoord

&lt;span class="gi"&gt;+__doctest_skip__ = [&amp;#39;WCSGeometry&amp;#39;]&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;__all__ = [
&lt;span class="w"&gt; &lt;/span&gt;    &amp;#39;WCSGeometry&amp;#39;,
&lt;span class="w"&gt; &lt;/span&gt;]
&lt;span class="gu"&gt;@@ -25,7 +28,9 @@ class WCSGeometry:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;gt;&amp;gt;&amp;gt; from hips.utils import WCSGeometry
&lt;span class="w"&gt; &lt;/span&gt;        &amp;gt;&amp;gt;&amp;gt; from astropy.coordinates import SkyCoord
&lt;span class="w"&gt; &lt;/span&gt;        &amp;gt;&amp;gt;&amp;gt; skycoord = SkyCoord(10, 20, unit=&amp;quot;deg&amp;quot;)
&lt;span class="gd"&gt;-        &amp;gt;&amp;gt;&amp;gt; wcs_geometry = WCSGeometry.create(skydir=skycoord, shape=(10, 20), coordsys=&amp;#39;CEL&amp;#39;, projection=&amp;#39;AIT&amp;#39;, cdelt=1.0, crpix=1.)&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; wcs_geometry = WCSGeometry.create(skydir=skycoord, shape=(10, 20), \&lt;/span&gt;
&lt;span class="gi"&gt;+coordsys=&amp;#39;CEL&amp;#39;, projection=&amp;#39;AIT&amp;#39;, \&lt;/span&gt;
&lt;span class="gi"&gt;+cdelt=1.0, crpix=1.)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;gt;&amp;gt;&amp;gt; wcs_geometry.wcs
&lt;span class="w"&gt; &lt;/span&gt;        Number of WCS axes: 2
&lt;span class="w"&gt; &lt;/span&gt;        CTYPE : &amp;#39;RA---AIT&amp;#39;  &amp;#39;DEC--AIT&amp;#39;
&lt;span class="gu"&gt;@@ -41,7 +46,7 @@ class WCSGeometry:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def __init__(self, wcs: WCS, shape: tuple) -&amp;gt; None:
&lt;span class="w"&gt; &lt;/span&gt;        self.wcs = wcs
&lt;span class="gd"&gt;-        self.shape = shape&lt;/span&gt;
&lt;span class="gi"&gt;+        self.Shape = namedtuple(&amp;#39;Shape&amp;#39;, [&amp;#39;ny&amp;#39;, &amp;#39;nx&amp;#39;])(*shape)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    @classmethod
&lt;span class="w"&gt; &lt;/span&gt;    def create(cls, skydir: SkyCoord, shape: tuple, coordsys: str=&amp;#39;CEL&amp;#39;,
&lt;span class="gu"&gt;@@ -77,7 +82,7 @@ class WCSGeometry:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            w.wcs.crval[0] = skydir.galactic.l.deg
&lt;span class="w"&gt; &lt;/span&gt;            w.wcs.crval[1] = skydir.galactic.b.deg
&lt;span class="w"&gt; &lt;/span&gt;        else:
&lt;span class="gd"&gt;-            raise ValueError(&amp;#39;Unrecognized coordinate system.&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+            raise ValueError(&amp;#39;Unrecognized coordinate system.&amp;#39;) # pragma: no cover&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        w.wcs.crpix[0] = crpix[0]
&lt;span class="w"&gt; &lt;/span&gt;        w.wcs.crpix[1] = crpix[1]

commit 8f3139dfd23e9624e4cd9dafd412889cc49a340e
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Mon Jun 19 13:41:23 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Update hips.utils package

&lt;span class="gh"&gt;diff --git a/hips/utils/healpix.py b/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gh"&gt;index 4dfb22a..584be5f 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -58,11 +58,11 @@ def boundaries(nside: int, pix: int, nest: bool=True) -&amp;gt; tuple:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    return theta, phi

&lt;span class="w"&gt; &lt;/span&gt;def compute_healpix_pixel_indices(wcs_geometry: WCSGeometry, nside: int) -&amp;gt; np.ndarray:
&lt;span class="gd"&gt;-    &amp;quot;&amp;quot;&amp;quot;Returns an array containing pixels corresponding to an image.&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;Returns an array containing HEALPix pixels corresponding to disk regions.&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    This function calls `healpy.pixelfunc.ang2vec`, `healpy.query_disc`, and
&lt;span class="w"&gt; &lt;/span&gt;    `astropy.coordinates.angle_utilities.angular_separation` to compute
&lt;span class="gd"&gt;-    the pixel values, which will be use in tile drawing.&lt;/span&gt;
&lt;span class="gi"&gt;+    the HEALPix pixel indices, which will be used in tile drawing.&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    Parameters
&lt;span class="w"&gt; &lt;/span&gt;    ----------
&lt;span class="gu"&gt;@@ -75,6 +75,23 @@ def compute_healpix_pixel_indices(wcs_geometry: WCSGeometry, nside: int) -&amp;gt; np.n&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    -------
&lt;span class="w"&gt; &lt;/span&gt;    pixels : `numpy.ndarray`
&lt;span class="w"&gt; &lt;/span&gt;        HEALPix pixel numbers
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    Examples&lt;/span&gt;
&lt;span class="gi"&gt;+    --------&lt;/span&gt;
&lt;span class="gi"&gt;+    ::&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; import healpy as hp&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; from hips.utils import WCSGeometry&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; from astropy.coordinates import SkyCoord&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; from hips.utils import compute_healpix_pixel_indices&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; order = 3&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; nside = hp.order2nside(order)&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; skycoord = SkyCoord(10, 20, unit=&amp;quot;deg&amp;quot;)&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; wcs_geometry = WCSGeometry.create(skydir=skycoord, shape=(10, 20), coordsys=&amp;#39;CEL&amp;#39;, projection=&amp;#39;AIT&amp;#39;, cdelt=1.0, crpix=(1., 1.))&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; compute_healpix_pixel_indices(wcs_geometry, nside)&lt;/span&gt;
&lt;span class="gi"&gt;+        [ 84 111 112 113 142 143 144 145 146 174 175 176 177 178 206 207 208 209&lt;/span&gt;
&lt;span class="gi"&gt;+        210 238 239 240 241 270 271 272 273 274 302 303 304 305 334 335 336 337&lt;/span&gt;
&lt;span class="gi"&gt;+        367 368 399]&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;    y_center, x_center = wcs_geometry.shape[0] // 2, wcs_geometry.shape[1] // 2
&lt;span class="w"&gt; &lt;/span&gt;    lon_center, lat_center = wcs_geometry.wcs.all_pix2world(x_center, y_center, 1)
&lt;span class="gh"&gt;diff --git a/hips/utils/tests/test_healpix.py b/hips/utils/tests/test_healpix.py&lt;/span&gt;
&lt;span class="gh"&gt;index 8945ee4..3d8952e 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/tests/test_healpix.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/tests/test_healpix.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -30,13 +30,11 @@ def test_compute_healpix_pixel_indices():&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    nside = hp.order2nside(order)

&lt;span class="w"&gt; &lt;/span&gt;    skycoord = SkyCoord(10, 20, unit=&amp;quot;deg&amp;quot;)
&lt;span class="gd"&gt;-    wcs_geometry = WCSGeometry.create(skycoord, (10, 20), &amp;#39;CEL&amp;#39;, &amp;#39;AIT&amp;#39;, 1.0, 1)&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-    These pixel values were obtained for the all-sky image located at:&lt;/span&gt;
&lt;span class="gd"&gt;-    https://github.com/gammapy/gammapy-extra/blob/master/datasets/catalogs/fermi/gll_psch_v08.fit.gz?raw=true&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-    pixels_precomp = [0, 767]&lt;/span&gt;
&lt;span class="gi"&gt;+    wcs_geometry = WCSGeometry.create(skydir=skycoord, shape=(10, 20), coordsys=&amp;#39;CEL&amp;#39;, projection=&amp;#39;AIT&amp;#39;, cdelt=1.0, crpix=(1., 1.))&lt;/span&gt;

&lt;span class="gi"&gt;+    pixels_precomp = [84, 111, 112, 113]&lt;/span&gt;
&lt;span class="gi"&gt;+    len_precomp = 39&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    pixels = compute_healpix_pixel_indices(wcs_geometry, nside)
&lt;span class="gd"&gt;-    assert_allclose([pixels[0], pixels[-1]], pixels_precomp)&lt;/span&gt;
&lt;span class="gi"&gt;+    print(pixels)&lt;/span&gt;
&lt;span class="gi"&gt;+    assert_allclose(len(pixels), len_precomp)&lt;/span&gt;
&lt;span class="gi"&gt;+    assert_allclose(pixels[0:4], pixels_precomp)&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/utils/wcs.py b/hips/utils/wcs.py&lt;/span&gt;
&lt;span class="gh"&gt;index 472d009..d1d365e 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/wcs.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/wcs.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -17,25 +17,36 @@ class WCSGeometry:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        WCS projection object
&lt;span class="w"&gt; &lt;/span&gt;    shape : tuple
&lt;span class="w"&gt; &lt;/span&gt;        Shape of the image
&lt;span class="gd"&gt;-    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

&lt;span class="gd"&gt;-    def __init__(self, wcs: WCS, shape: tuple) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gd"&gt;-        self._wcs = wcs&lt;/span&gt;
&lt;span class="gd"&gt;-        self._shape = shape&lt;/span&gt;
&lt;span class="gi"&gt;+    Examples&lt;/span&gt;
&lt;span class="gi"&gt;+    --------&lt;/span&gt;
&lt;span class="gi"&gt;+    ::&lt;/span&gt;

&lt;span class="gd"&gt;-    @property&lt;/span&gt;
&lt;span class="gd"&gt;-    def shape(self) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;Shape of the image (`None`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-        return self._shape&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; from hips.utils import WCSGeometry&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; from astropy.coordinates import SkyCoord&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; skycoord = SkyCoord(10, 20, unit=&amp;quot;deg&amp;quot;)&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; wcs_geometry = WCSGeometry.create(skydir=skycoord, shape=(10, 20), coordsys=&amp;#39;CEL&amp;#39;, projection=&amp;#39;AIT&amp;#39;, cdelt=1.0, crpix=1.)&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; wcs_geometry.wcs&lt;/span&gt;
&lt;span class="gi"&gt;+        Number of WCS axes: 2&lt;/span&gt;
&lt;span class="gi"&gt;+        CTYPE : &amp;#39;RA---AIT&amp;#39;  &amp;#39;DEC--AIT&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+        CRVAL : 10.0  20.0&lt;/span&gt;
&lt;span class="gi"&gt;+        CRPIX : 1.0  1.0&lt;/span&gt;
&lt;span class="gi"&gt;+        PC1_1 PC1_2  : 1.0  0.0&lt;/span&gt;
&lt;span class="gi"&gt;+        PC2_1 PC2_2  : 0.0  1.0&lt;/span&gt;
&lt;span class="gi"&gt;+        CDELT : -1.0  1.0&lt;/span&gt;
&lt;span class="gi"&gt;+        NAXIS : 0  0&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; wcs_geometry.shape&lt;/span&gt;
&lt;span class="gi"&gt;+        (10, 20)&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

&lt;span class="gd"&gt;-    @property&lt;/span&gt;
&lt;span class="gd"&gt;-    def wcs(self) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;WCS object containing FITS image header (`None`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-        return self._wcs&lt;/span&gt;
&lt;span class="gi"&gt;+    def __init__(self, wcs: WCS, shape: tuple) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+        self.wcs = wcs&lt;/span&gt;
&lt;span class="gi"&gt;+        self.shape = shape&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    @classmethod
&lt;span class="gd"&gt;-    def create(cls, skydir: SkyCoord, shape: tuple, coordsys: str=&amp;#39;CEL&amp;#39;, projection: str=&amp;#39;AIT&amp;#39;, cdelt: float=1.0, crpix: float=1.) -&amp;gt; &amp;#39;WCSGeometry&amp;#39;:&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;Read from HiPS description file (`WCSGeometry`).&lt;/span&gt;
&lt;span class="gi"&gt;+    def create(cls, skydir: SkyCoord, shape: tuple, coordsys: str=&amp;#39;CEL&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+        projection: str=&amp;#39;AIT&amp;#39;, cdelt: float=1.0, crpix: tuple=(1., 1.)) -&amp;gt; &amp;#39;WCSGeometry&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Create WCS object programmatically (`WCSGeometry`).&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        Parameters
&lt;span class="w"&gt; &lt;/span&gt;        ----------
&lt;span class="gu"&gt;@@ -49,15 +60,11 @@ class WCSGeometry:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            Projection of the WCS object
&lt;span class="w"&gt; &lt;/span&gt;        cdelt : `float`
&lt;span class="w"&gt; &lt;/span&gt;            Coordinate increment at reference point
&lt;span class="gd"&gt;-        crpix : `float`&lt;/span&gt;
&lt;span class="gd"&gt;-            Pixel coordinate of reference point&lt;/span&gt;
&lt;span class="gi"&gt;+        crpix : `tuple`&lt;/span&gt;
&lt;span class="gi"&gt;+            Pixel coordinates of reference point&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;

&lt;span class="gd"&gt;-        naxis = 2&lt;/span&gt;
&lt;span class="gd"&gt;-        if shape is not None:&lt;/span&gt;
&lt;span class="gd"&gt;-            naxis += len(shape)&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        w = WCS(naxis=naxis)&lt;/span&gt;
&lt;span class="gi"&gt;+        w = WCS(naxis=2)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;        if coordsys == &amp;#39;CEL&amp;#39;:
&lt;span class="w"&gt; &lt;/span&gt;            w.wcs.ctype[0] = &amp;#39;RA---{}&amp;#39;.format(projection)
&lt;span class="gu"&gt;@@ -72,12 +79,9 @@ class WCSGeometry:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        else:
&lt;span class="w"&gt; &lt;/span&gt;            raise ValueError(&amp;#39;Unrecognized coordinate system.&amp;#39;)

&lt;span class="gd"&gt;-        try:&lt;/span&gt;
&lt;span class="gd"&gt;-            w.wcs.crpix[0] = crpix[0]&lt;/span&gt;
&lt;span class="gd"&gt;-            w.wcs.crpix[1] = crpix[1]&lt;/span&gt;
&lt;span class="gd"&gt;-        except:&lt;/span&gt;
&lt;span class="gd"&gt;-            w.wcs.crpix[0] = crpix&lt;/span&gt;
&lt;span class="gd"&gt;-            w.wcs.crpix[1] = crpix&lt;/span&gt;
&lt;span class="gi"&gt;+        w.wcs.crpix[0] = crpix[0]&lt;/span&gt;
&lt;span class="gi"&gt;+        w.wcs.crpix[1] = crpix[1]&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        w.wcs.cdelt[0] = -cdelt
&lt;span class="w"&gt; &lt;/span&gt;        w.wcs.cdelt[1] = cdelt


commit 81d74de79ec5971d0e528a3f5c2d8e0e21fc7e0a
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Sun Jun 18 09:17:17 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Add classmethod for creating a WCS object

&lt;span class="gh"&gt;diff --git a/hips/utils/healpix.py b/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gh"&gt;index 4c74dd7..4dfb22a 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -6,7 +6,7 @@ the healpy library&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&amp;quot;&amp;quot;&amp;quot;

&lt;span class="w"&gt; &lt;/span&gt;__all__ = [
&lt;span class="gd"&gt;-    &amp;#39;boundaries&amp;#39;, &amp;#39;compute_image_pixels&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;#39;boundaries&amp;#39;, &amp;#39;compute_healpix_pixel_indices&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;]

&lt;span class="w"&gt; &lt;/span&gt;import healpy as hp
&lt;span class="gh"&gt;diff --git a/hips/utils/tests/test_healpix.py b/hips/utils/tests/test_healpix.py&lt;/span&gt;
&lt;span class="gh"&gt;index afdd80d..8945ee4 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/tests/test_healpix.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/tests/test_healpix.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -3,11 +3,9 @@ import numpy as np&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;import healpy as hp
&lt;span class="w"&gt; &lt;/span&gt;from ..wcs import WCSGeometry
&lt;span class="w"&gt; &lt;/span&gt;from ..healpix import boundaries
&lt;span class="gd"&gt;-from ..healpix import compute_image_pixels&lt;/span&gt;
&lt;span class="gd"&gt;-from astropy.io import fits&lt;/span&gt;
&lt;span class="gd"&gt;-from astropy.wcs import WCS&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from astropy.coordinates import SkyCoord
&lt;span class="w"&gt; &lt;/span&gt;from numpy.testing import assert_allclose
&lt;span class="gi"&gt;+from ..healpix import compute_healpix_pixel_indices&lt;/span&gt;


&lt;span class="w"&gt; &lt;/span&gt;def test_boundaries():
&lt;span class="gu"&gt;@@ -31,9 +29,8 @@ def test_compute_healpix_pixel_indices():&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    order = 3
&lt;span class="w"&gt; &lt;/span&gt;    nside = hp.order2nside(order)

&lt;span class="gd"&gt;-    hdu = fits.open(&amp;#39;https://github.com/gammapy/gammapy-extra/blob/master/datasets/catalogs/fermi/gll_psch_v08.fit.gz?raw=true&amp;#39;)&lt;/span&gt;
&lt;span class="gd"&gt;-    wcs = WCS(hdu[0].header)&lt;/span&gt;
&lt;span class="gd"&gt;-    wcs_geometry = WCSGeometry(wcs, hdu[0].data.shape)&lt;/span&gt;
&lt;span class="gi"&gt;+    skycoord = SkyCoord(10, 20, unit=&amp;quot;deg&amp;quot;)&lt;/span&gt;
&lt;span class="gi"&gt;+    wcs_geometry = WCSGeometry.create(skycoord, (10, 20), &amp;#39;CEL&amp;#39;, &amp;#39;AIT&amp;#39;, 1.0, 1)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;    These pixel values were obtained for the all-sky image located at:
&lt;span class="gu"&gt;@@ -41,5 +38,5 @@ def test_compute_healpix_pixel_indices():&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;    pixels_precomp = [0, 767]

&lt;span class="gd"&gt;-    pixels = compute_image_pixels(wcs_geometry, nside)&lt;/span&gt;
&lt;span class="gi"&gt;+    pixels = compute_healpix_pixel_indices(wcs_geometry, nside)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    assert_allclose([pixels[0], pixels[-1]], pixels_precomp)
&lt;span class="gh"&gt;diff --git a/hips/utils/wcs.py b/hips/utils/wcs.py&lt;/span&gt;
&lt;span class="gh"&gt;index 45b5541..472d009 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/wcs.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/wcs.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,6 +1,7 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="w"&gt; &lt;/span&gt;import numpy as np
&lt;span class="w"&gt; &lt;/span&gt;from astropy.wcs import WCS
&lt;span class="gi"&gt;+from astropy.coordinates import SkyCoord&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;__all__ = [
&lt;span class="w"&gt; &lt;/span&gt;    &amp;#39;WCSGeometry&amp;#39;,
&lt;span class="gu"&gt;@@ -33,17 +34,53 @@ class WCSGeometry:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        return self._wcs

&lt;span class="w"&gt; &lt;/span&gt;    @classmethod
&lt;span class="gd"&gt;-    def create(cls, projection: str=&amp;#39;AIT&amp;#39;, cdelt: float=1.0, crpix: float=1., shape: tuple) -&amp;gt; &amp;#39;WCSGeometry&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+    def create(cls, skydir: SkyCoord, shape: tuple, coordsys: str=&amp;#39;CEL&amp;#39;, projection: str=&amp;#39;AIT&amp;#39;, cdelt: float=1.0, crpix: float=1.) -&amp;gt; &amp;#39;WCSGeometry&amp;#39;:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;Read from HiPS description file (`WCSGeometry`).

&lt;span class="w"&gt; &lt;/span&gt;        Parameters
&lt;span class="w"&gt; &lt;/span&gt;        ----------
&lt;span class="gd"&gt;-        projection : `str`&lt;/span&gt;
&lt;span class="gd"&gt;-            Project of the WCS object&lt;/span&gt;
&lt;span class="gd"&gt;-        cdelt : `str`&lt;/span&gt;
&lt;span class="gd"&gt;-        crpix : `str`&lt;/span&gt;
&lt;span class="gi"&gt;+        skydir : `~astropy.coordinates.SkyCoord`&lt;/span&gt;
&lt;span class="gi"&gt;+            Sky coordinate of the WCS reference point&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        shape : `tuple`
&lt;span class="gd"&gt;-            Image shape&lt;/span&gt;
&lt;span class="gi"&gt;+            Shape of the image&lt;/span&gt;
&lt;span class="gi"&gt;+        coordsys : `str`&lt;/span&gt;
&lt;span class="gi"&gt;+            Coordinate system&lt;/span&gt;
&lt;span class="gi"&gt;+        projection : `str`&lt;/span&gt;
&lt;span class="gi"&gt;+            Projection of the WCS object&lt;/span&gt;
&lt;span class="gi"&gt;+        cdelt : `float`&lt;/span&gt;
&lt;span class="gi"&gt;+            Coordinate increment at reference point&lt;/span&gt;
&lt;span class="gi"&gt;+        crpix : `float`&lt;/span&gt;
&lt;span class="gi"&gt;+            Pixel coordinate of reference point&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-        # TODO&lt;/span&gt;
&lt;span class="gd"&gt;-        pass&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        naxis = 2&lt;/span&gt;
&lt;span class="gi"&gt;+        if shape is not None:&lt;/span&gt;
&lt;span class="gi"&gt;+            naxis += len(shape)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        w = WCS(naxis=naxis)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        if coordsys == &amp;#39;CEL&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+            w.wcs.ctype[0] = &amp;#39;RA---{}&amp;#39;.format(projection)&lt;/span&gt;
&lt;span class="gi"&gt;+            w.wcs.ctype[1] = &amp;#39;DEC--{}&amp;#39;.format(projection)&lt;/span&gt;
&lt;span class="gi"&gt;+            w.wcs.crval[0] = skydir.icrs.ra.deg&lt;/span&gt;
&lt;span class="gi"&gt;+            w.wcs.crval[1] = skydir.icrs.dec.deg&lt;/span&gt;
&lt;span class="gi"&gt;+        elif coordsys == &amp;#39;GAL&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+            w.wcs.ctype[0] = &amp;#39;GLON-{}&amp;#39;.format(projection)&lt;/span&gt;
&lt;span class="gi"&gt;+            w.wcs.ctype[1] = &amp;#39;GLAT-{}&amp;#39;.format(projection)&lt;/span&gt;
&lt;span class="gi"&gt;+            w.wcs.crval[0] = skydir.galactic.l.deg&lt;/span&gt;
&lt;span class="gi"&gt;+            w.wcs.crval[1] = skydir.galactic.b.deg&lt;/span&gt;
&lt;span class="gi"&gt;+        else:&lt;/span&gt;
&lt;span class="gi"&gt;+            raise ValueError(&amp;#39;Unrecognized coordinate system.&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        try:&lt;/span&gt;
&lt;span class="gi"&gt;+            w.wcs.crpix[0] = crpix[0]&lt;/span&gt;
&lt;span class="gi"&gt;+            w.wcs.crpix[1] = crpix[1]&lt;/span&gt;
&lt;span class="gi"&gt;+        except:&lt;/span&gt;
&lt;span class="gi"&gt;+            w.wcs.crpix[0] = crpix&lt;/span&gt;
&lt;span class="gi"&gt;+            w.wcs.crpix[1] = crpix&lt;/span&gt;
&lt;span class="gi"&gt;+        w.wcs.cdelt[0] = -cdelt&lt;/span&gt;
&lt;span class="gi"&gt;+        w.wcs.cdelt[1] = cdelt&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        w = WCS(w.to_header())&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        return cls(w, shape)&lt;/span&gt;

commit 5f52d62d5402080dcd271b106670d86878856f3b
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Jun 16 22:35:54 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Add class WCSGeometry

&lt;span class="w"&gt; &lt;/span&gt;   This commit renames the compute_image_pixels function to compute_healpix_pixel_indices and wrap the WCS object and image shape in a separate class. Also, some refactoring is done in the test case.

&lt;span class="gh"&gt;diff --git a/hips/utils/healpix.py b/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gh"&gt;index 0ab2549..4c74dd7 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -11,6 +11,7 @@ __all__ = [&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;import healpy as hp
&lt;span class="w"&gt; &lt;/span&gt;import numpy as np
&lt;span class="gi"&gt;+from .wcs import WCSGeometry&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from astropy.coordinates.angle_utilities import angular_separation
&lt;span class="w"&gt; &lt;/span&gt;from astropy.wcs import WCS

&lt;span class="gu"&gt;@@ -52,13 +53,12 @@ def boundaries(nside: int, pix: int, nest: bool=True) -&amp;gt; tuple:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        [( 264.375, -24.62431835), ( 258.75 , -30.        ),
&lt;span class="w"&gt; &lt;/span&gt;         ( 264.375, -35.68533471), ( 270.   , -30.        )]&amp;gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    boundary_coords = hp.boundaries(nside, pix, nest=nest)
&lt;span class="w"&gt; &lt;/span&gt;    theta, phi = hp.vec2ang(np.transpose(boundary_coords))
&lt;span class="w"&gt; &lt;/span&gt;    return theta, phi

&lt;span class="gd"&gt;-def compute_image_pixels(nside: int, shape: tuple, wcs: WCS) -&amp;gt; np.ndarray:&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;quot;&amp;quot;&amp;quot;Returns an array containing the pixels corresponding to an image.&lt;/span&gt;
&lt;span class="gi"&gt;+def compute_healpix_pixel_indices(wcs_geometry: WCSGeometry, nside: int) -&amp;gt; np.ndarray:&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;Returns an array containing pixels corresponding to an image.&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    This function calls `healpy.pixelfunc.ang2vec`, `healpy.query_disc`, and
&lt;span class="w"&gt; &lt;/span&gt;    `astropy.coordinates.angle_utilities.angular_separation` to compute
&lt;span class="gu"&gt;@@ -66,22 +66,18 @@ def compute_image_pixels(nside: int, shape: tuple, wcs: WCS) -&amp;gt; np.ndarray:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    Parameters
&lt;span class="w"&gt; &lt;/span&gt;    ----------
&lt;span class="gi"&gt;+    wcs_geometry : WCSGeometry&lt;/span&gt;
&lt;span class="gi"&gt;+        Container for WCS object and image shape&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    nside : int
&lt;span class="w"&gt; &lt;/span&gt;        The nside of the HEALPix map
&lt;span class="gd"&gt;-    shape : tuple&lt;/span&gt;
&lt;span class="gd"&gt;-        Shape of the image&lt;/span&gt;
&lt;span class="gd"&gt;-    wcs : astropy.wcs.wcs.WCS&lt;/span&gt;
&lt;span class="gd"&gt;-        A WCS object containing the image header&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    Returns
&lt;span class="w"&gt; &lt;/span&gt;    -------
&lt;span class="w"&gt; &lt;/span&gt;    pixels : `numpy.ndarray`
&lt;span class="gd"&gt;-        Returns a list of pixel values&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gi"&gt;+        HEALPix pixel numbers&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    y_center, x_center = shape[0] // 2, shape[1] // 2&lt;/span&gt;
&lt;span class="gd"&gt;-    lon_center, lat_center = wcs.all_pix2world(x_center, y_center, 1)&lt;/span&gt;
&lt;span class="gi"&gt;+    y_center, x_center = wcs_geometry.shape[0] // 2, wcs_geometry.shape[1] // 2&lt;/span&gt;
&lt;span class="gi"&gt;+    lon_center, lat_center = wcs_geometry.wcs.all_pix2world(x_center, y_center, 1)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    vec = hp.ang2vec(lon_center, lat_center, lonlat=True)
&lt;span class="w"&gt; &lt;/span&gt;    separations = angular_separation(x_center, y_center, lon_center, lat_center)
&lt;span class="w"&gt; &lt;/span&gt;    max_separation = np.nanmax(separations)
&lt;span class="gh"&gt;diff --git a/hips/utils/tests/test_healpix.py b/hips/utils/tests/test_healpix.py&lt;/span&gt;
&lt;span class="gh"&gt;index da358dd..afdd80d 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/tests/test_healpix.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/tests/test_healpix.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,6 +1,7 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="w"&gt; &lt;/span&gt;import numpy as np
&lt;span class="w"&gt; &lt;/span&gt;import healpy as hp
&lt;span class="gi"&gt;+from ..wcs import WCSGeometry&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from ..healpix import boundaries
&lt;span class="w"&gt; &lt;/span&gt;from ..healpix import compute_image_pixels
&lt;span class="w"&gt; &lt;/span&gt;from astropy.io import fits
&lt;span class="gu"&gt;@@ -26,43 +27,19 @@ def test_boundaries():&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                     [-24.624318,  -30.,  -35.685335,  -30.]]
&lt;span class="w"&gt; &lt;/span&gt;    assert_allclose([radec.ra, radec.dec], radec_precomp)

&lt;span class="gd"&gt;-def test_compute_image_pixels():&lt;/span&gt;
&lt;span class="gi"&gt;+def test_compute_healpix_pixel_indices():&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    order = 3
&lt;span class="w"&gt; &lt;/span&gt;    nside = hp.order2nside(order)

&lt;span class="w"&gt; &lt;/span&gt;    hdu = fits.open(&amp;#39;https://github.com/gammapy/gammapy-extra/blob/master/datasets/catalogs/fermi/gll_psch_v08.fit.gz?raw=true&amp;#39;)
&lt;span class="w"&gt; &lt;/span&gt;    wcs = WCS(hdu[0].header)
&lt;span class="gi"&gt;+    wcs_geometry = WCSGeometry(wcs, hdu[0].data.shape)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;    These pixel values were obtained for the all-sky image located at:
&lt;span class="w"&gt; &lt;/span&gt;    https://github.com/gammapy/gammapy-extra/blob/master/datasets/catalogs/fermi/gll_psch_v08.fit.gz?raw=true
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-    pixels_precomp = \&lt;/span&gt;
&lt;span class="gd"&gt;-    [  0, 1, 2, 3, 4, 5, 6, 9, 10, 11, 12, 13, 14, 15, 20, 21, 22, 23,&lt;/span&gt;
&lt;span class="gd"&gt;-      24, 25, 26, 27, 28, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 54, 55,&lt;/span&gt;
&lt;span class="gd"&gt;-      56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 77, 78, 79, 80, 81, 82, 83,&lt;/span&gt;
&lt;span class="gd"&gt;-      84, 85, 86, 87, 88, 89, 90, 91, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113,&lt;/span&gt;
&lt;span class="gd"&gt;-     114, 115, 116, 117, 118, 119, 120, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145,&lt;/span&gt;
&lt;span class="gd"&gt;-     146, 147, 148, 149, 150, 151, 152, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178,&lt;/span&gt;
&lt;span class="gd"&gt;-     179, 180, 181, 182, 183, 184, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210,&lt;/span&gt;
&lt;span class="gd"&gt;-     211, 212, 213, 214, 215, 216, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243,&lt;/span&gt;
&lt;span class="gd"&gt;-     244, 245, 246, 247, 248, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275,&lt;/span&gt;
&lt;span class="gd"&gt;-     276, 277, 278, 279, 280, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308,&lt;/span&gt;
&lt;span class="gd"&gt;-     309, 310, 311, 312, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340,&lt;/span&gt;
&lt;span class="gd"&gt;-     341, 342, 343, 344, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373,&lt;/span&gt;
&lt;span class="gd"&gt;-     374, 375, 376, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405,&lt;/span&gt;
&lt;span class="gd"&gt;-     406, 407, 408, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438,&lt;/span&gt;
&lt;span class="gd"&gt;-     439, 440, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470,&lt;/span&gt;
&lt;span class="gd"&gt;-     471, 472, 488, 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, 499, 500, 501, 502, 503,&lt;/span&gt;
&lt;span class="gd"&gt;-     504, 519, 520, 521, 522, 523, 524, 525, 526, 527, 528, 529, 530, 531, 532, 533, 534, 535,&lt;/span&gt;
&lt;span class="gd"&gt;-     536, 552, 553, 554, 555, 556, 557, 558, 559, 560, 561, 562, 563, 564, 565, 566, 567, 568,&lt;/span&gt;
&lt;span class="gd"&gt;-     583, 584, 585, 586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 596, 597, 598, 599, 600,&lt;/span&gt;
&lt;span class="gd"&gt;-     616, 617, 618, 619, 620, 621, 622, 623, 624, 625, 626, 627, 628, 629, 630, 631, 632, 647,&lt;/span&gt;
&lt;span class="gd"&gt;-     648, 649, 650, 651, 652, 653, 654, 655, 656, 657, 658, 659, 660, 661, 662, 663, 676, 677,&lt;/span&gt;
&lt;span class="gd"&gt;-     678, 679, 680, 681, 682, 683, 684, 685, 686, 687, 688, 689, 690, 701, 702, 703, 704, 705,&lt;/span&gt;
&lt;span class="gd"&gt;-     706, 707, 708, 709, 710, 711, 712, 713, 722, 723, 724, 725, 726, 727, 728, 729, 730, 731,&lt;/span&gt;
&lt;span class="gd"&gt;-     732, 739, 740, 741, 742, 743, 744, 745, 746, 747, 752, 753, 754, 755, 756, 757, 758, 761,&lt;/span&gt;
&lt;span class="gd"&gt;-     762, 763, 764, 765, 766, 767]&lt;/span&gt;
&lt;span class="gi"&gt;+    pixels_precomp = [0, 767]&lt;/span&gt;

&lt;span class="gd"&gt;-    pixels = compute_image_pixels(nside, hdu[0].data.shape, wcs)&lt;/span&gt;
&lt;span class="gd"&gt;-    assert_allclose(pixels, pixels_precomp)&lt;/span&gt;
&lt;span class="gi"&gt;+    pixels = compute_image_pixels(wcs_geometry, nside)&lt;/span&gt;
&lt;span class="gi"&gt;+    assert_allclose([pixels[0], pixels[-1]], pixels_precomp)&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/utils/wcs.py b/hips/utils/wcs.py&lt;/span&gt;
new file mode 100644
&lt;span class="gh"&gt;index 0000000..45b5541&lt;/span&gt;
&lt;span class="gd"&gt;--- /dev/null&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/wcs.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -0,0 +1,49 @@&lt;/span&gt;
&lt;span class="gi"&gt;+# Licensed under a 3-clause BSD style license - see LICENSE.rst&lt;/span&gt;
&lt;span class="gi"&gt;+import numpy as np&lt;/span&gt;
&lt;span class="gi"&gt;+from astropy.wcs import WCS&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+__all__ = [&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;#39;WCSGeometry&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+]&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+class WCSGeometry:&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;Container for WCS object and image shape.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    Parameters&lt;/span&gt;
&lt;span class="gi"&gt;+    ----------&lt;/span&gt;
&lt;span class="gi"&gt;+    wcs : `~astropy.wcs.WCS`&lt;/span&gt;
&lt;span class="gi"&gt;+        WCS projection object&lt;/span&gt;
&lt;span class="gi"&gt;+    shape : tuple&lt;/span&gt;
&lt;span class="gi"&gt;+        Shape of the image&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def __init__(self, wcs: WCS, shape: tuple) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+        self._wcs = wcs&lt;/span&gt;
&lt;span class="gi"&gt;+        self._shape = shape&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @property&lt;/span&gt;
&lt;span class="gi"&gt;+    def shape(self) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Shape of the image (`None`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        return self._shape&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @property&lt;/span&gt;
&lt;span class="gi"&gt;+    def wcs(self) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;WCS object containing FITS image header (`None`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        return self._wcs&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @classmethod&lt;/span&gt;
&lt;span class="gi"&gt;+    def create(cls, projection: str=&amp;#39;AIT&amp;#39;, cdelt: float=1.0, crpix: float=1., shape: tuple) -&amp;gt; &amp;#39;WCSGeometry&amp;#39;:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Read from HiPS description file (`WCSGeometry`).&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        Parameters&lt;/span&gt;
&lt;span class="gi"&gt;+        ----------&lt;/span&gt;
&lt;span class="gi"&gt;+        projection : `str`&lt;/span&gt;
&lt;span class="gi"&gt;+            Project of the WCS object&lt;/span&gt;
&lt;span class="gi"&gt;+        cdelt : `str`&lt;/span&gt;
&lt;span class="gi"&gt;+        crpix : `str`&lt;/span&gt;
&lt;span class="gi"&gt;+        shape : `tuple`&lt;/span&gt;
&lt;span class="gi"&gt;+            Image shape&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        # TODO&lt;/span&gt;
&lt;span class="gi"&gt;+        pass&lt;/span&gt;

commit 6e2dd380d3d397e08a2e0f79489d10756a876787
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Thu Jun 15 11:02:58 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Add compute_image_pixels function in hips.utils

&lt;span class="gh"&gt;diff --git a/hips/utils/healpix.py b/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gh"&gt;index 36bff9d..0ab2549 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -6,11 +6,13 @@ the healpy library&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&amp;quot;&amp;quot;&amp;quot;

&lt;span class="w"&gt; &lt;/span&gt;__all__ = [
&lt;span class="gd"&gt;-    &amp;#39;boundaries&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;#39;boundaries&amp;#39;, &amp;#39;compute_image_pixels&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;]

&lt;span class="w"&gt; &lt;/span&gt;import healpy as hp
&lt;span class="w"&gt; &lt;/span&gt;import numpy as np
&lt;span class="gi"&gt;+from astropy.coordinates.angle_utilities import angular_separation&lt;/span&gt;
&lt;span class="gi"&gt;+from astropy.wcs import WCS&lt;/span&gt;


&lt;span class="w"&gt; &lt;/span&gt;def boundaries(nside: int, pix: int, nest: bool=True) -&amp;gt; tuple:
&lt;span class="gu"&gt;@@ -54,3 +56,33 @@ def boundaries(nside: int, pix: int, nest: bool=True) -&amp;gt; tuple:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    boundary_coords = hp.boundaries(nside, pix, nest=nest)
&lt;span class="w"&gt; &lt;/span&gt;    theta, phi = hp.vec2ang(np.transpose(boundary_coords))
&lt;span class="w"&gt; &lt;/span&gt;    return theta, phi
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+def compute_image_pixels(nside: int, shape: tuple, wcs: WCS) -&amp;gt; np.ndarray:&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;Returns an array containing the pixels corresponding to an image.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    This function calls `healpy.pixelfunc.ang2vec`, `healpy.query_disc`, and&lt;/span&gt;
&lt;span class="gi"&gt;+    `astropy.coordinates.angle_utilities.angular_separation` to compute&lt;/span&gt;
&lt;span class="gi"&gt;+    the pixel values, which will be use in tile drawing.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    Parameters&lt;/span&gt;
&lt;span class="gi"&gt;+    ----------&lt;/span&gt;
&lt;span class="gi"&gt;+    nside : int&lt;/span&gt;
&lt;span class="gi"&gt;+        The nside of the HEALPix map&lt;/span&gt;
&lt;span class="gi"&gt;+    shape : tuple&lt;/span&gt;
&lt;span class="gi"&gt;+        Shape of the image&lt;/span&gt;
&lt;span class="gi"&gt;+    wcs : astropy.wcs.wcs.WCS&lt;/span&gt;
&lt;span class="gi"&gt;+        A WCS object containing the image header&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    Returns&lt;/span&gt;
&lt;span class="gi"&gt;+    -------&lt;/span&gt;
&lt;span class="gi"&gt;+    pixels : `numpy.ndarray`&lt;/span&gt;
&lt;span class="gi"&gt;+        Returns a list of pixel values&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    y_center, x_center = shape[0] // 2, shape[1] // 2&lt;/span&gt;
&lt;span class="gi"&gt;+    lon_center, lat_center = wcs.all_pix2world(x_center, y_center, 1)&lt;/span&gt;
&lt;span class="gi"&gt;+    vec = hp.ang2vec(lon_center, lat_center, lonlat=True)&lt;/span&gt;
&lt;span class="gi"&gt;+    separations = angular_separation(x_center, y_center, lon_center, lat_center)&lt;/span&gt;
&lt;span class="gi"&gt;+    max_separation = np.nanmax(separations)&lt;/span&gt;
&lt;span class="gi"&gt;+    return hp.query_disc(nside, vec, max_separation)&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/utils/tests/test_healpix.py b/hips/utils/tests/test_healpix.py&lt;/span&gt;
&lt;span class="gh"&gt;index 34373ad..da358dd 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/tests/test_healpix.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/tests/test_healpix.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,7 +1,10 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="w"&gt; &lt;/span&gt;import numpy as np
&lt;span class="w"&gt; &lt;/span&gt;import healpy as hp
&lt;span class="gd"&gt;-from .. healpix import boundaries&lt;/span&gt;
&lt;span class="gi"&gt;+from ..healpix import boundaries&lt;/span&gt;
&lt;span class="gi"&gt;+from ..healpix import compute_image_pixels&lt;/span&gt;
&lt;span class="gi"&gt;+from astropy.io import fits&lt;/span&gt;
&lt;span class="gi"&gt;+from astropy.wcs import WCS&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from astropy.coordinates import SkyCoord
&lt;span class="w"&gt; &lt;/span&gt;from numpy.testing import assert_allclose

&lt;span class="gu"&gt;@@ -22,3 +25,44 @@ def test_boundaries():&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    radec_precomp = [[264.375,  258.75,  264.375,  270.],
&lt;span class="w"&gt; &lt;/span&gt;                     [-24.624318,  -30.,  -35.685335,  -30.]]
&lt;span class="w"&gt; &lt;/span&gt;    assert_allclose([radec.ra, radec.dec], radec_precomp)
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+def test_compute_image_pixels():&lt;/span&gt;
&lt;span class="gi"&gt;+    order = 3&lt;/span&gt;
&lt;span class="gi"&gt;+    nside = hp.order2nside(order)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    hdu = fits.open(&amp;#39;https://github.com/gammapy/gammapy-extra/blob/master/datasets/catalogs/fermi/gll_psch_v08.fit.gz?raw=true&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+    wcs = WCS(hdu[0].header)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+    These pixel values were obtained for the all-sky image located at:&lt;/span&gt;
&lt;span class="gi"&gt;+    https://github.com/gammapy/gammapy-extra/blob/master/datasets/catalogs/fermi/gll_psch_v08.fit.gz?raw=true&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+    pixels_precomp = \&lt;/span&gt;
&lt;span class="gi"&gt;+    [  0, 1, 2, 3, 4, 5, 6, 9, 10, 11, 12, 13, 14, 15, 20, 21, 22, 23,&lt;/span&gt;
&lt;span class="gi"&gt;+      24, 25, 26, 27, 28, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 54, 55,&lt;/span&gt;
&lt;span class="gi"&gt;+      56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 77, 78, 79, 80, 81, 82, 83,&lt;/span&gt;
&lt;span class="gi"&gt;+      84, 85, 86, 87, 88, 89, 90, 91, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113,&lt;/span&gt;
&lt;span class="gi"&gt;+     114, 115, 116, 117, 118, 119, 120, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145,&lt;/span&gt;
&lt;span class="gi"&gt;+     146, 147, 148, 149, 150, 151, 152, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178,&lt;/span&gt;
&lt;span class="gi"&gt;+     179, 180, 181, 182, 183, 184, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210,&lt;/span&gt;
&lt;span class="gi"&gt;+     211, 212, 213, 214, 215, 216, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243,&lt;/span&gt;
&lt;span class="gi"&gt;+     244, 245, 246, 247, 248, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275,&lt;/span&gt;
&lt;span class="gi"&gt;+     276, 277, 278, 279, 280, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308,&lt;/span&gt;
&lt;span class="gi"&gt;+     309, 310, 311, 312, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340,&lt;/span&gt;
&lt;span class="gi"&gt;+     341, 342, 343, 344, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373,&lt;/span&gt;
&lt;span class="gi"&gt;+     374, 375, 376, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405,&lt;/span&gt;
&lt;span class="gi"&gt;+     406, 407, 408, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438,&lt;/span&gt;
&lt;span class="gi"&gt;+     439, 440, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470,&lt;/span&gt;
&lt;span class="gi"&gt;+     471, 472, 488, 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, 499, 500, 501, 502, 503,&lt;/span&gt;
&lt;span class="gi"&gt;+     504, 519, 520, 521, 522, 523, 524, 525, 526, 527, 528, 529, 530, 531, 532, 533, 534, 535,&lt;/span&gt;
&lt;span class="gi"&gt;+     536, 552, 553, 554, 555, 556, 557, 558, 559, 560, 561, 562, 563, 564, 565, 566, 567, 568,&lt;/span&gt;
&lt;span class="gi"&gt;+     583, 584, 585, 586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 596, 597, 598, 599, 600,&lt;/span&gt;
&lt;span class="gi"&gt;+     616, 617, 618, 619, 620, 621, 622, 623, 624, 625, 626, 627, 628, 629, 630, 631, 632, 647,&lt;/span&gt;
&lt;span class="gi"&gt;+     648, 649, 650, 651, 652, 653, 654, 655, 656, 657, 658, 659, 660, 661, 662, 663, 676, 677,&lt;/span&gt;
&lt;span class="gi"&gt;+     678, 679, 680, 681, 682, 683, 684, 685, 686, 687, 688, 689, 690, 701, 702, 703, 704, 705,&lt;/span&gt;
&lt;span class="gi"&gt;+     706, 707, 708, 709, 710, 711, 712, 713, 722, 723, 724, 725, 726, 727, 728, 729, 730, 731,&lt;/span&gt;
&lt;span class="gi"&gt;+     732, 739, 740, 741, 742, 743, 744, 745, 746, 747, 752, 753, 754, 755, 756, 757, 758, 761,&lt;/span&gt;
&lt;span class="gi"&gt;+     762, 763, 764, 765, 766, 767]&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    pixels = compute_image_pixels(nside, hdu[0].data.shape, wcs)&lt;/span&gt;
&lt;span class="gi"&gt;+    assert_allclose(pixels, pixels_precomp)&lt;/span&gt;

commit d369652b5edc9cda4705d0199a862bd8d226f526
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Mon Jun 12 18:55:36 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Update class docstring

&lt;span class="gh"&gt;diff --git a/hips/tiles/description.py b/hips/tiles/description.py&lt;/span&gt;
&lt;span class="gh"&gt;index e270d1d..d58c7ee 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/description.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/description.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -15,8 +15,8 @@ class HipsDescription:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    Parameters
&lt;span class="w"&gt; &lt;/span&gt;    ----------
&lt;span class="gd"&gt;-    properties : str&lt;/span&gt;
&lt;span class="gd"&gt;-        A string containing HiPS tile properties&lt;/span&gt;
&lt;span class="gi"&gt;+    properties : OrderedDict&lt;/span&gt;
&lt;span class="gi"&gt;+        An ordered dictionary containing the HiPS file properties&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;

&lt;span class="w"&gt; &lt;/span&gt;    def __init__(self, properties: OrderedDict) -&amp;gt; None:

commit a245521987a4971aaa70f842bb045f72512c760e
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Mon Jun 12 11:55:31 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Update tiles.hipsdescription module

&lt;span class="w"&gt; &lt;/span&gt;   This commits modifies the classmethod to return the appropriate type, and changes the file reading technique.

&lt;span class="gh"&gt;diff --git a/hips/tiles/description.py b/hips/tiles/description.py&lt;/span&gt;
&lt;span class="gh"&gt;index a0d7bdb..e270d1d 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/description.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/description.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -19,9 +19,8 @@ class HipsDescription:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        A string containing HiPS tile properties
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;

&lt;span class="gd"&gt;-    def __init__(self, properties: str) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gd"&gt;-        super(HipsDescription, self).__init__()&lt;/span&gt;
&lt;span class="gd"&gt;-        self.properties = self.parse_file_properties(properties)&lt;/span&gt;
&lt;span class="gi"&gt;+    def __init__(self, properties: OrderedDict) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+        self.properties = properties&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    @classmethod
&lt;span class="w"&gt; &lt;/span&gt;    def read_file(cls, filename: str) -&amp;gt; OrderedDict:
&lt;span class="gu"&gt;@@ -39,8 +38,9 @@ class HipsDescription:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        -------
&lt;span class="w"&gt; &lt;/span&gt;        dict_properties : OrderedDict
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-        file = open(filename, &amp;#39;r&amp;#39;)&lt;/span&gt;
&lt;span class="gd"&gt;-        return cls.parse_file_properties(file.read())&lt;/span&gt;
&lt;span class="gi"&gt;+        with open(filename) as file:&lt;/span&gt;
&lt;span class="gi"&gt;+            text = file.read()&lt;/span&gt;
&lt;span class="gi"&gt;+        return cls.parse_file_properties(text)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    @classmethod
&lt;span class="w"&gt; &lt;/span&gt;    def parse_file_properties(cls, properties: str) -&amp;gt; OrderedDict:
&lt;span class="gu"&gt;@@ -58,15 +58,15 @@ class HipsDescription:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        -------
&lt;span class="w"&gt; &lt;/span&gt;        list_properties : OrderedDict
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-        properties = properties.split(&amp;#39;\n&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        properties_lines = properties.split(&amp;#39;\n&amp;#39;)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        list_properties = []
&lt;span class="gd"&gt;-        for property in properties:&lt;/span&gt;
&lt;span class="gi"&gt;+        for property in properties_lines:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;            key_value = property.split(&amp;#39;=&amp;#39;)
&lt;span class="w"&gt; &lt;/span&gt;            try:
&lt;span class="w"&gt; &lt;/span&gt;                list_properties.append((key_value[0].strip(), key_value[1].strip()))
&lt;span class="gd"&gt;-            except IndexError: # The case where a property contains comment or a blank line&lt;/span&gt;
&lt;span class="gi"&gt;+            except IndexError: # the case where a property contains a comment or a blank line&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;                pass
&lt;span class="gd"&gt;-        return OrderedDict(list_properties)&lt;/span&gt;
&lt;span class="gi"&gt;+        return cls(OrderedDict(list_properties))&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    @property
&lt;span class="w"&gt; &lt;/span&gt;    def base_url(self) -&amp;gt; str:
&lt;span class="gh"&gt;diff --git a/hips/tiles/tests/test_description.py b/hips/tiles/tests/test_description.py&lt;/span&gt;
&lt;span class="gh"&gt;index 75d42d2..5e4a2b2 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tests/test_description.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tests/test_description.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -58,7 +58,7 @@ class TestHiPSDescription:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        isColor            = true
&lt;span class="w"&gt; &lt;/span&gt;        ~
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-        cls.hipsdescription = HipsDescription(hips_properties)&lt;/span&gt;
&lt;span class="gi"&gt;+        cls.hipsdescription = HipsDescription.parse_file_properties(hips_properties)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    def test_base_url(cls):
&lt;span class="w"&gt; &lt;/span&gt;        assert cls.hipsdescription.base_url == &amp;#39;http://alasky.u-strasbg.fr/DSS/DSSColor&amp;#39;

commit dae30ac9cff524a0dc64bb638c5e6b62bc4011d4
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Jun 9 22:46:14 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Update class docstring

&lt;span class="gh"&gt;diff --git a/hips/tiles/__init__.py b/hips/tiles/__init__.py&lt;/span&gt;
&lt;span class="gh"&gt;index 1c77ba4..d19ff3e 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/__init__.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/__init__.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,3 +1,3 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="gd"&gt;-&amp;quot;&amp;quot;&amp;quot;HiPS description class -- provides methods for parsing the HiPS file properties.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+&amp;quot;&amp;quot;&amp;quot;Classes and functions to manage HiPS tiles.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from .description import *

commit fbf89687c9e5f6dab8b0de8a638abaf433ec6a87
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Jun 9 22:43:58 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Update tiles.hipsdescription module

&lt;span class="w"&gt; &lt;/span&gt;   This commits renames the hipsdescription file to description and converts the getters methods to properties. It also fixes some formatting issue.

&lt;span class="gh"&gt;diff --git a/docs/api.rst b/docs/api.rst&lt;/span&gt;
&lt;span class="gh"&gt;index 46c5c1f..eb152d9 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/docs/api.rst&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/docs/api.rst&lt;/span&gt;
&lt;span class="gu"&gt;@@ -2,4 +2,3 @@ Reference/API&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;=============

&lt;span class="w"&gt; &lt;/span&gt;.. automodapi:: hips
&lt;span class="gd"&gt;-.. automodapi:: hips.tiles&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/__init__.py b/hips/__init__.py&lt;/span&gt;
&lt;span class="gh"&gt;index 8d3819e..ce0fdf7 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/__init__.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/__init__.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -19,3 +19,4 @@ from ._astropy_init import *&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;if not _ASTROPY_SETUP_:
&lt;span class="w"&gt; &lt;/span&gt;    # For egg_info test builds to pass, put package imports here.
&lt;span class="w"&gt; &lt;/span&gt;    from .draw import *
&lt;span class="gi"&gt;+    from .tiles import *&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/tiles/__init__.py b/hips/tiles/__init__.py&lt;/span&gt;
&lt;span class="gh"&gt;index bb73205..1c77ba4 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/__init__.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/__init__.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,3 +1,3 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="w"&gt; &lt;/span&gt;&amp;quot;&amp;quot;&amp;quot;HiPS description class -- provides methods for parsing the HiPS file properties.&amp;quot;&amp;quot;&amp;quot;
&lt;span class="gd"&gt;-from .hipsdescription import *&lt;/span&gt;
&lt;span class="gi"&gt;+from .description import *&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/tiles/description.py b/hips/tiles/description.py&lt;/span&gt;
new file mode 100644
&lt;span class="gh"&gt;index 0000000..a0d7bdb&lt;/span&gt;
&lt;span class="gd"&gt;--- /dev/null&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/description.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -0,0 +1,99 @@&lt;/span&gt;
&lt;span class="gi"&gt;+# Licensed under a 3-clause BSD style license - see LICENSE.rst&lt;/span&gt;
&lt;span class="gi"&gt;+&amp;quot;&amp;quot;&amp;quot;Classes and functions to manage HiPS tiles.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+from collections import OrderedDict&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+__all__ = [&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;#39;HipsDescription&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+]&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+class HipsDescription:&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;This class provides methods for parsing the HiPS properties file.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    It also provides multiple getters around the commonly used HiPS properties.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    Parameters&lt;/span&gt;
&lt;span class="gi"&gt;+    ----------&lt;/span&gt;
&lt;span class="gi"&gt;+    properties : str&lt;/span&gt;
&lt;span class="gi"&gt;+        A string containing HiPS tile properties&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def __init__(self, properties: str) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+        super(HipsDescription, self).__init__()&lt;/span&gt;
&lt;span class="gi"&gt;+        self.properties = self.parse_file_properties(properties)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @classmethod&lt;/span&gt;
&lt;span class="gi"&gt;+    def read_file(cls, filename: str) -&amp;gt; OrderedDict:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Reads text from a file and returns an ordered dictionary containing the HiPS file properties.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        This method reads a file given by the parameter and returns an ordered dictionary&lt;/span&gt;
&lt;span class="gi"&gt;+        containing the HiPS file properties by calling the `parse_file_properties` method.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        Parameters&lt;/span&gt;
&lt;span class="gi"&gt;+        ----------&lt;/span&gt;
&lt;span class="gi"&gt;+        filename : str&lt;/span&gt;
&lt;span class="gi"&gt;+            Name of the file containing HiPS file properties&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        Returns&lt;/span&gt;
&lt;span class="gi"&gt;+        -------&lt;/span&gt;
&lt;span class="gi"&gt;+        dict_properties : OrderedDict&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        file = open(filename, &amp;#39;r&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        return cls.parse_file_properties(file.read())&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @classmethod&lt;/span&gt;
&lt;span class="gi"&gt;+    def parse_file_properties(cls, properties: str) -&amp;gt; OrderedDict:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Returns an ordered dictionary containing the HiPS file properties.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        This method parses the HiPS file properties and loads it in a dictionary,&lt;/span&gt;
&lt;span class="gi"&gt;+        then is casts it to `collections.OrderedDict` type.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        Parameters&lt;/span&gt;
&lt;span class="gi"&gt;+        ----------&lt;/span&gt;
&lt;span class="gi"&gt;+        properties : str&lt;/span&gt;
&lt;span class="gi"&gt;+            HiPS file properties&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        Returns&lt;/span&gt;
&lt;span class="gi"&gt;+        -------&lt;/span&gt;
&lt;span class="gi"&gt;+        list_properties : OrderedDict&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        properties = properties.split(&amp;#39;\n&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        list_properties = []&lt;/span&gt;
&lt;span class="gi"&gt;+        for property in properties:&lt;/span&gt;
&lt;span class="gi"&gt;+            key_value = property.split(&amp;#39;=&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+            try:&lt;/span&gt;
&lt;span class="gi"&gt;+                list_properties.append((key_value[0].strip(), key_value[1].strip()))&lt;/span&gt;
&lt;span class="gi"&gt;+            except IndexError: # The case where a property contains comment or a blank line&lt;/span&gt;
&lt;span class="gi"&gt;+                pass&lt;/span&gt;
&lt;span class="gi"&gt;+        return OrderedDict(list_properties)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @property&lt;/span&gt;
&lt;span class="gi"&gt;+    def base_url(self) -&amp;gt; str:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Returns the base url from the HiPS file properties (`str`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        return self.properties[&amp;#39;hips_service_url&amp;#39;]&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @property&lt;/span&gt;
&lt;span class="gi"&gt;+    def title(self) -&amp;gt; str:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Returns the title from the HiPS file properties (`str`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        return self.properties[&amp;#39;obs_title&amp;#39;]&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @property&lt;/span&gt;
&lt;span class="gi"&gt;+    def hips_version(self) -&amp;gt; float:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Returns the HiPS version from the HiPS file properties (`float`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        return float(self.properties[&amp;#39;hips_version&amp;#39;])&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @property&lt;/span&gt;
&lt;span class="gi"&gt;+    def hips_frame(self) -&amp;gt; str:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Returns the HiPS frame from the HiPS file properties (`str`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        return self.properties[&amp;#39;hips_frame&amp;#39;]&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @property&lt;/span&gt;
&lt;span class="gi"&gt;+    def hips_order(self) -&amp;gt; int:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Returns the HiPS order from the HiPS file properties (`int`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        return int(self.properties[&amp;#39;hips_order&amp;#39;])&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @property&lt;/span&gt;
&lt;span class="gi"&gt;+    def tile_format(self) -&amp;gt; str:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Returns the HiPS tile format from the HiPS file properties (`str`).&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        return self.properties[&amp;#39;hips_tile_format&amp;#39;]&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/tiles/hipsdescription.py b/hips/tiles/hipsdescription.py&lt;/span&gt;
deleted file mode 100644
&lt;span class="gh"&gt;index f13624a..0000000&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/hipsdescription.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ /dev/null&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,75 +0,0 @@&lt;/span&gt;
&lt;span class="gd"&gt;-# Licensed under a 3-clause BSD style license - see LICENSE.rst&lt;/span&gt;
&lt;span class="gd"&gt;-&amp;quot;&amp;quot;&amp;quot;HiPS description class -- provides methods for parsing the HiPS file properties.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-__all__ = [&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;#39;HipsDescription&amp;#39;,&lt;/span&gt;
&lt;span class="gd"&gt;-]&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-from collections import OrderedDict&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-class HipsDescription:&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;quot;&amp;quot;&amp;quot;This class provides methods for parsing the HiPS properties file.&lt;/span&gt;
&lt;span class="gd"&gt;-       It also provides multiple getters around the commonly used properties.&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    def __init__(self, properties: str) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gd"&gt;-        super(HipsDescription, self).__init__()&lt;/span&gt;
&lt;span class="gd"&gt;-        self.properties = self.parse_file_properties(properties)&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    @classmethod&lt;/span&gt;
&lt;span class="gd"&gt;-    def parse_file_properties(cls, properties: str) -&amp;gt; OrderedDict:&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;Returns an ordered dictionary containing the HiPS file properties.&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        This method parses the HiPS file properties and loads it in a dictionary,&lt;/span&gt;
&lt;span class="gd"&gt;-        then is casts it to `collections.OrderedDict` type.&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        Parameters&lt;/span&gt;
&lt;span class="gd"&gt;-        ----------&lt;/span&gt;
&lt;span class="gd"&gt;-        properties : str&lt;/span&gt;
&lt;span class="gd"&gt;-            HiPS file properties&lt;/span&gt;
&lt;span class="gd"&gt;-        &lt;/span&gt;
&lt;span class="gd"&gt;-        Returns&lt;/span&gt;
&lt;span class="gd"&gt;-        -------&lt;/span&gt;
&lt;span class="gd"&gt;-        dict_properties : OrderedDict&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-        properties = properties.split(&amp;#39;\n&amp;#39;)&lt;/span&gt;
&lt;span class="gd"&gt;-        dict_properties = {}&lt;/span&gt;
&lt;span class="gd"&gt;-        for property in properties:&lt;/span&gt;
&lt;span class="gd"&gt;-            key_value = property.split(&amp;#39;=&amp;#39;)&lt;/span&gt;
&lt;span class="gd"&gt;-            try:&lt;/span&gt;
&lt;span class="gd"&gt;-                dict_properties[key_value[0].strip()] = key_value[1].strip()&lt;/span&gt;
&lt;span class="gd"&gt;-            except:&lt;/span&gt;
&lt;span class="gd"&gt;-                pass&lt;/span&gt;
&lt;span class="gd"&gt;-        return OrderedDict(dict_properties)&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    def get_properties(self) -&amp;gt; OrderedDict:&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;OrderedDict: Returns the ordered dictionary containing the HiPS properties.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-        return self.properties&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    def get_base_url(self) -&amp;gt; str:&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;str: Returns the base url from the HiPS file properties.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-        try:&lt;/span&gt;
&lt;span class="gd"&gt;-            return self.properties[&amp;#39;hips_service_url&amp;#39;]&lt;/span&gt;
&lt;span class="gd"&gt;-        except:&lt;/span&gt;
&lt;span class="gd"&gt;-            return None&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    def get_title(self) -&amp;gt; str:&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;str: Returns the title from the HiPS file properties.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-        return self.properties[&amp;#39;obs_title&amp;#39;]&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    def get_hips_version(self) -&amp;gt; float:&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;float: Returns the HiPS version from the HiPS file properties.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-        return float(self.properties[&amp;#39;hips_version&amp;#39;])&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    def get_hips_frame(self) -&amp;gt; str:&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;str: Returns the HiPS frame from the HiPS file properties.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-        return self.properties[&amp;#39;hips_frame&amp;#39;]&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    def get_hips_order(self) -&amp;gt; int:&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;int: Returns the HiPS order from the HiPS file properties.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-        return int(self.properties[&amp;#39;hips_order&amp;#39;])&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    def get_tile_format(self) -&amp;gt; str:&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;str: Returns the HiPS tile format from the HiPS file properties.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-        return self.properties[&amp;#39;hips_tile_format&amp;#39;]&lt;/span&gt;
\ No newline at end of file
&lt;span class="gh"&gt;diff --git a/hips/tiles/tests/test_description.py b/hips/tiles/tests/test_description.py&lt;/span&gt;
new file mode 100644
&lt;span class="gh"&gt;index 0000000..75d42d2&lt;/span&gt;
&lt;span class="gd"&gt;--- /dev/null&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tests/test_description.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -0,0 +1,79 @@&lt;/span&gt;
&lt;span class="gi"&gt;+# Licensed under a 3-clause BSD style license - see LICENSE.rst&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+from ..description import HipsDescription&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+class TestHiPSDescription:&lt;/span&gt;
&lt;span class="gi"&gt;+    @classmethod&lt;/span&gt;
&lt;span class="gi"&gt;+    def setup_class(cls):&lt;/span&gt;
&lt;span class="gi"&gt;+        # These HiPS properties were obtained from: http://alasky.u-strasbg.fr/DSS/DSSColor/properties&lt;/span&gt;
&lt;span class="gi"&gt;+        hips_properties = &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        creator_did          = ivo://CDS/P/DSS2/color&lt;/span&gt;
&lt;span class="gi"&gt;+        obs_collection       = DSS colored&lt;/span&gt;
&lt;span class="gi"&gt;+        obs_title            = DSS colored&lt;/span&gt;
&lt;span class="gi"&gt;+        obs_description      = Color composition generated by CDS. This HiPS survey is based on 2 others HiPS surveys, respectively DSS2-red and DSS2-blue HiPS, both of them directly generated from original scanned plates downloaded from STScI site. The red component has been built from POSS-II F, AAO-SES,SR and SERC-ER plates. The blue component has been build from POSS-II J and SERC-J,EJ. The green component is based on the mean of other components. Three missing plates from red survey (253, 260, 359) has been replaced by pixels from the DSSColor STScI jpeg survey. The 11 missing blue plates (mainly in galactic plane) have not been replaced (only red component).&lt;/span&gt;
&lt;span class="gi"&gt;+        obs_copyright        = Digitized Sky Survey - STScI/NASA, Colored &amp;amp; Healpixed by CDS&lt;/span&gt;
&lt;span class="gi"&gt;+        obs_copyright_url    = http://archive.stsci.edu/dss/acknowledging.html&lt;/span&gt;
&lt;span class="gi"&gt;+        client_category      = Image/Optical/DSS&lt;/span&gt;
&lt;span class="gi"&gt;+        client_sort_key      = 03-00&lt;/span&gt;
&lt;span class="gi"&gt;+        hips_builder         = Aladin/HipsGen v9.039&lt;/span&gt;
&lt;span class="gi"&gt;+        hips_creation_date   = 2010-05-01T19:05Z&lt;/span&gt;
&lt;span class="gi"&gt;+        hips_release_date    = 2015-05-11T08:45Z&lt;/span&gt;
&lt;span class="gi"&gt;+        # hips_release_date    = 2016-12-13T14:51Z&lt;/span&gt;
&lt;span class="gi"&gt;+        hips_creator         = CDS (A.Oberto, P.Fernique)&lt;/span&gt;
&lt;span class="gi"&gt;+        hips_version         = 1.31&lt;/span&gt;
&lt;span class="gi"&gt;+        hips_order           = 9&lt;/span&gt;
&lt;span class="gi"&gt;+        hips_frame           = equatorial&lt;/span&gt;
&lt;span class="gi"&gt;+        hips_tile_width      = 512&lt;/span&gt;
&lt;span class="gi"&gt;+        hips_tile_format     = jpeg&lt;/span&gt;
&lt;span class="gi"&gt;+        dataproduct_type     = image&lt;/span&gt;
&lt;span class="gi"&gt;+        client_application   = AladinLite&lt;/span&gt;
&lt;span class="gi"&gt;+        moc_access_url       = http://alasky.u-strasbg.fr/DSS/DSSColor/Moc.fits&lt;/span&gt;
&lt;span class="gi"&gt;+        hips_service_url     = http://alasky.u-strasbg.fr/DSS/DSSColor&lt;/span&gt;
&lt;span class="gi"&gt;+        hips_status          = public master clonableOnce&lt;/span&gt;
&lt;span class="gi"&gt;+        hips_rgb_red         = DSS2Merged [1488.0 8488.8125 14666.0 Linear]&lt;/span&gt;
&lt;span class="gi"&gt;+        hips_rgb_blue        = DSS2-blue-XJ-S [4286.0 12122.5 19959.0 Linear]&lt;/span&gt;
&lt;span class="gi"&gt;+        hips_hierarchy       = median&lt;/span&gt;
&lt;span class="gi"&gt;+        hips_pixel_scale     = 2.236E-4&lt;/span&gt;
&lt;span class="gi"&gt;+        hips_initial_ra      = 085.30251&lt;/span&gt;
&lt;span class="gi"&gt;+        hips_initial_dec     = -02.25468&lt;/span&gt;
&lt;span class="gi"&gt;+        hips_initial_fov     = 2&lt;/span&gt;
&lt;span class="gi"&gt;+        moc_sky_fraction     = 1&lt;/span&gt;
&lt;span class="gi"&gt;+        dataproduct_subtype  = color&lt;/span&gt;
&lt;span class="gi"&gt;+        hips_copyright       = CNRS/Unistra&lt;/span&gt;
&lt;span class="gi"&gt;+        obs_ack              = The Digitized Sky Surveys were produced at the Space Telescope Science Institute under U.S. Government grant NAG W-2166. The images of these surveys are based on photographic data obtained using the Oschin Schmidt Telescope on Palomar Mountain and the UK Schmidt Telescope. The plates were processed into the present compressed digital form with the permission of these institutions&lt;/span&gt;
&lt;span class="gi"&gt;+        prov_progenitor      = STScI&lt;/span&gt;
&lt;span class="gi"&gt;+        bib_reference        = 2008AJ....136..735L&lt;/span&gt;
&lt;span class="gi"&gt;+        bib_reference_url    = http://simbad.u-strasbg.fr/simbad/sim-ref?bibcode=2008AJ....136..735L&lt;/span&gt;
&lt;span class="gi"&gt;+        # 1975-1999&lt;/span&gt;
&lt;span class="gi"&gt;+        t_min                = 42413&lt;/span&gt;
&lt;span class="gi"&gt;+        t_max                = 51179&lt;/span&gt;
&lt;span class="gi"&gt;+        obs_regime           = Optical&lt;/span&gt;
&lt;span class="gi"&gt;+        # Bandpass  422-967 THz&lt;/span&gt;
&lt;span class="gi"&gt;+        em_min               = 7.104086682464e-7&lt;/span&gt;
&lt;span class="gi"&gt;+        em_max               = 3.100232244054e-7&lt;/span&gt;
&lt;span class="gi"&gt;+        # hips_master_url     = ex: http://yourHipsServer/null&lt;/span&gt;
&lt;span class="gi"&gt;+        # For compatibility&lt;/span&gt;
&lt;span class="gi"&gt;+        label              = DSS colored&lt;/span&gt;
&lt;span class="gi"&gt;+        coordsys           = C&lt;/span&gt;
&lt;span class="gi"&gt;+        isColor            = true&lt;/span&gt;
&lt;span class="gi"&gt;+        ~&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        cls.hipsdescription = HipsDescription(hips_properties)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_base_url(cls):&lt;/span&gt;
&lt;span class="gi"&gt;+        assert cls.hipsdescription.base_url == &amp;#39;http://alasky.u-strasbg.fr/DSS/DSSColor&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_title(cls):&lt;/span&gt;
&lt;span class="gi"&gt;+        assert cls.hipsdescription.title == &amp;#39;DSS colored&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_hips_version(cls):&lt;/span&gt;
&lt;span class="gi"&gt;+        assert cls.hipsdescription.hips_version == 1.31&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_hips_frame(cls):&lt;/span&gt;
&lt;span class="gi"&gt;+        assert cls.hipsdescription.hips_frame == &amp;#39;equatorial&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_hips_order(cls):&lt;/span&gt;
&lt;span class="gi"&gt;+        assert cls.hipsdescription.hips_order == 9&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_tile_format(cls):&lt;/span&gt;
&lt;span class="gi"&gt;+        assert cls.hipsdescription.tile_format == &amp;#39;jpeg&amp;#39;&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/tiles/tests/test_hipsdescription.py b/hips/tiles/tests/test_hipsdescription.py&lt;/span&gt;
deleted file mode 100644
&lt;span class="gh"&gt;index 458e976..0000000&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/tests/test_hipsdescription.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ /dev/null&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,79 +0,0 @@&lt;/span&gt;
&lt;span class="gd"&gt;-# Licensed under a 3-clause BSD style license - see LICENSE.rst&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-from .. hipsdescription import HipsDescription&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-class TestHiPSDescription:&lt;/span&gt;
&lt;span class="gd"&gt;-    @classmethod&lt;/span&gt;
&lt;span class="gd"&gt;-    def setup_class(cls):&lt;/span&gt;
&lt;span class="gd"&gt;-        # These HiPS properties were obtained from: http://alasky.u-strasbg.fr/DSS/DSSColor/properties&lt;/span&gt;
&lt;span class="gd"&gt;-        hips_properties = &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-        creator_did          = ivo://CDS/P/DSS2/color&lt;/span&gt;
&lt;span class="gd"&gt;-        obs_collection       = DSS colored&lt;/span&gt;
&lt;span class="gd"&gt;-        obs_title            = DSS colored&lt;/span&gt;
&lt;span class="gd"&gt;-        obs_description      = Color composition generated by CDS. This HiPS survey is based on 2 others HiPS surveys, respectively DSS2-red and DSS2-blue HiPS, both of them directly generated from original scanned plates downloaded from STScI site. The red component has been built from POSS-II F, AAO-SES,SR and SERC-ER plates. The blue component has been build from POSS-II J and SERC-J,EJ. The green component is based on the mean of other components. Three missing plates from red survey (253, 260, 359) has been replaced by pixels from the DSSColor STScI jpeg survey. The 11 missing blue plates (mainly in galactic plane) have not been replaced (only red component).&lt;/span&gt;
&lt;span class="gd"&gt;-        obs_copyright        = Digitized Sky Survey - STScI/NASA, Colored &amp;amp; Healpixed by CDS&lt;/span&gt;
&lt;span class="gd"&gt;-        obs_copyright_url    = http://archive.stsci.edu/dss/acknowledging.html&lt;/span&gt;
&lt;span class="gd"&gt;-        client_category      = Image/Optical/DSS&lt;/span&gt;
&lt;span class="gd"&gt;-        client_sort_key      = 03-00&lt;/span&gt;
&lt;span class="gd"&gt;-        hips_builder         = Aladin/HipsGen v9.039&lt;/span&gt;
&lt;span class="gd"&gt;-        hips_creation_date   = 2010-05-01T19:05Z&lt;/span&gt;
&lt;span class="gd"&gt;-        hips_release_date    = 2015-05-11T08:45Z&lt;/span&gt;
&lt;span class="gd"&gt;-        # hips_release_date    = 2016-12-13T14:51Z&lt;/span&gt;
&lt;span class="gd"&gt;-        hips_creator         = CDS (A.Oberto, P.Fernique)&lt;/span&gt;
&lt;span class="gd"&gt;-        hips_version         = 1.31&lt;/span&gt;
&lt;span class="gd"&gt;-        hips_order           = 9&lt;/span&gt;
&lt;span class="gd"&gt;-        hips_frame           = equatorial&lt;/span&gt;
&lt;span class="gd"&gt;-        hips_tile_width      = 512&lt;/span&gt;
&lt;span class="gd"&gt;-        hips_tile_format     = jpeg&lt;/span&gt;
&lt;span class="gd"&gt;-        dataproduct_type     = image&lt;/span&gt;
&lt;span class="gd"&gt;-        client_application   = AladinLite&lt;/span&gt;
&lt;span class="gd"&gt;-        moc_access_url       = http://alasky.u-strasbg.fr/DSS/DSSColor/Moc.fits&lt;/span&gt;
&lt;span class="gd"&gt;-        hips_service_url     = http://alasky.u-strasbg.fr/DSS/DSSColor&lt;/span&gt;
&lt;span class="gd"&gt;-        hips_status          = public master clonableOnce&lt;/span&gt;
&lt;span class="gd"&gt;-        hips_rgb_red         = DSS2Merged [1488.0 8488.8125 14666.0 Linear]&lt;/span&gt;
&lt;span class="gd"&gt;-        hips_rgb_blue        = DSS2-blue-XJ-S [4286.0 12122.5 19959.0 Linear]&lt;/span&gt;
&lt;span class="gd"&gt;-        hips_hierarchy       = median&lt;/span&gt;
&lt;span class="gd"&gt;-        hips_pixel_scale     = 2.236E-4&lt;/span&gt;
&lt;span class="gd"&gt;-        hips_initial_ra      = 085.30251&lt;/span&gt;
&lt;span class="gd"&gt;-        hips_initial_dec     = -02.25468&lt;/span&gt;
&lt;span class="gd"&gt;-        hips_initial_fov     = 2&lt;/span&gt;
&lt;span class="gd"&gt;-        moc_sky_fraction     = 1&lt;/span&gt;
&lt;span class="gd"&gt;-        dataproduct_subtype  = color&lt;/span&gt;
&lt;span class="gd"&gt;-        hips_copyright       = CNRS/Unistra&lt;/span&gt;
&lt;span class="gd"&gt;-        obs_ack              = The Digitized Sky Surveys were produced at the Space Telescope Science Institute under U.S. Government grant NAG W-2166. The images of these surveys are based on photographic data obtained using the Oschin Schmidt Telescope on Palomar Mountain and the UK Schmidt Telescope. The plates were processed into the present compressed digital form with the permission of these institutions&lt;/span&gt;
&lt;span class="gd"&gt;-        prov_progenitor      = STScI&lt;/span&gt;
&lt;span class="gd"&gt;-        bib_reference        = 2008AJ....136..735L&lt;/span&gt;
&lt;span class="gd"&gt;-        bib_reference_url    = http://simbad.u-strasbg.fr/simbad/sim-ref?bibcode=2008AJ....136..735L&lt;/span&gt;
&lt;span class="gd"&gt;-        # 1975-1999&lt;/span&gt;
&lt;span class="gd"&gt;-        t_min                = 42413&lt;/span&gt;
&lt;span class="gd"&gt;-        t_max                = 51179&lt;/span&gt;
&lt;span class="gd"&gt;-        obs_regime           = Optical&lt;/span&gt;
&lt;span class="gd"&gt;-        # Bandpass  422-967 THz&lt;/span&gt;
&lt;span class="gd"&gt;-        em_min               = 7.104086682464e-7&lt;/span&gt;
&lt;span class="gd"&gt;-        em_max               = 3.100232244054e-7&lt;/span&gt;
&lt;span class="gd"&gt;-        # hips_master_url     = ex: http://yourHipsServer/null&lt;/span&gt;
&lt;span class="gd"&gt;-        # For compatibility&lt;/span&gt;
&lt;span class="gd"&gt;-        label              = DSS colored&lt;/span&gt;
&lt;span class="gd"&gt;-        coordsys           = C&lt;/span&gt;
&lt;span class="gd"&gt;-        isColor            = true&lt;/span&gt;
&lt;span class="gd"&gt;-        ~&lt;/span&gt;
&lt;span class="gd"&gt;-        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gd"&gt;-        cls.hipsdescription = HipsDescription(hips_properties)&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    def test_get_base_url(cls):&lt;/span&gt;
&lt;span class="gd"&gt;-        assert cls.hipsdescription.get_base_url() == &amp;#39;http://alasky.u-strasbg.fr/DSS/DSSColor&amp;#39;&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    def test_get_title(cls):&lt;/span&gt;
&lt;span class="gd"&gt;-        assert cls.hipsdescription.get_title() == &amp;#39;DSS colored&amp;#39;&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    def test_get_hips_version(cls):&lt;/span&gt;
&lt;span class="gd"&gt;-        assert cls.hipsdescription.get_hips_version() == 1.31&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    def test_get_hips_frame(cls):&lt;/span&gt;
&lt;span class="gd"&gt;-        assert cls.hipsdescription.get_hips_frame() == &amp;#39;equatorial&amp;#39;&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    def test_get_hips_order(cls):&lt;/span&gt;
&lt;span class="gd"&gt;-        assert cls.hipsdescription.get_hips_order() == 9&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    def test_get_tile_format(cls):&lt;/span&gt;
&lt;span class="gd"&gt;-        assert cls.hipsdescription.get_tile_format() == &amp;#39;jpeg&amp;#39;&lt;/span&gt;
\ No newline at end of file

commit 69a91f0a35369ccd018990746b2f830d0ecb74d8
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Jun 9 15:06:30 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Remove whitespace

&lt;span class="gh"&gt;diff --git a/hips/utils/healpix.py b/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gh"&gt;index a104bdc..36bff9d 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -18,7 +18,7 @@ def boundaries(nside: int, pix: int, nest: bool=True) -&amp;gt; tuple:&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    This function calls `healpy.boundaries` and `healpy.pixelfunc.vec2ang`
&lt;span class="w"&gt; &lt;/span&gt;    and computes the four corners of a HiPS tile. The order of the returned
&lt;span class="gd"&gt;-    corners is: N, W, S, E where N (resp. W, S, E) is the corner roughly &lt;/span&gt;
&lt;span class="gi"&gt;+    corners is: N, W, S, E where N (resp. W, S, E) is the corner roughly&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    pointing towards the North (resp. West, South and East).

&lt;span class="w"&gt; &lt;/span&gt;    Parameters

commit 8c235c2ebe5861aa2ea213ff4fe0f5d7f7c89d99
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Jun 9 14:05:56 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Update docstring

&lt;span class="w"&gt; &lt;/span&gt;   This commit updates the docstring to show the order of the returned corners.

&lt;span class="gh"&gt;diff --git a/hips/utils/healpix.py b/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gh"&gt;index 6ea5681..a104bdc 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -13,10 +13,13 @@ import healpy as hp&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;import numpy as np


&lt;span class="gd"&gt;-def boundaries(nside: int, pix: int, nest: bool=False) -&amp;gt; tuple:&lt;/span&gt;
&lt;span class="gi"&gt;+def boundaries(nside: int, pix: int, nest: bool=True) -&amp;gt; tuple:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;Returns an array containing the angle (theta and phi) in radians.

&lt;span class="gd"&gt;-    This function calls `healpy.boundaries` and `healpy.vec2ang`.&lt;/span&gt;
&lt;span class="gi"&gt;+    This function calls `healpy.boundaries` and `healpy.pixelfunc.vec2ang`&lt;/span&gt;
&lt;span class="gi"&gt;+    and computes the four corners of a HiPS tile. The order of the returned&lt;/span&gt;
&lt;span class="gi"&gt;+    corners is: N, W, S, E where N (resp. W, S, E) is the corner roughly &lt;/span&gt;
&lt;span class="gi"&gt;+    pointing towards the North (resp. West, South and East).&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    Parameters
&lt;span class="w"&gt; &lt;/span&gt;    ----------
&lt;span class="gu"&gt;@@ -41,7 +44,7 @@ def boundaries(nside: int, pix: int, nest: bool=False) -&amp;gt; tuple:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;gt;&amp;gt;&amp;gt; from astropy.coordinates import SkyCoord
&lt;span class="w"&gt; &lt;/span&gt;        &amp;gt;&amp;gt;&amp;gt; nside = 8
&lt;span class="w"&gt; &lt;/span&gt;        &amp;gt;&amp;gt;&amp;gt; pix = 450
&lt;span class="gd"&gt;-        &amp;gt;&amp;gt;&amp;gt; theta, phi = boundaries(nside, pix, nest=True)&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; theta, phi = boundaries(nside, pix)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;gt;&amp;gt;&amp;gt; SkyCoord(ra=phi, dec=np.pi/2 - theta, unit=&amp;#39;radian&amp;#39;, frame=&amp;#39;icrs&amp;#39;)
&lt;span class="w"&gt; &lt;/span&gt;        &amp;lt;SkyCoord (ICRS): (ra, dec) in deg
&lt;span class="w"&gt; &lt;/span&gt;        [( 264.375, -24.62431835), ( 258.75 , -30.        ),
&lt;span class="gh"&gt;diff --git a/hips/utils/tests/test_healpix.py b/hips/utils/tests/test_healpix.py&lt;/span&gt;
&lt;span class="gh"&gt;index 7606819..34373ad 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/tests/test_healpix.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/tests/test_healpix.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -10,7 +10,7 @@ def test_boundaries():&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    order = 3
&lt;span class="w"&gt; &lt;/span&gt;    nside = hp.order2nside(order)
&lt;span class="w"&gt; &lt;/span&gt;    pix = 450
&lt;span class="gd"&gt;-    theta, phi = boundaries(nside, pix, nest=True)&lt;/span&gt;
&lt;span class="gi"&gt;+    theta, phi = boundaries(nside, pix)&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    radec = SkyCoord(ra=phi, dec=np.pi/2 - theta, unit=&amp;#39;radian&amp;#39;, frame=&amp;#39;icrs&amp;#39;)


commit 45b4117c3e85bb6fac72aeb790dfa483c03f0b12
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Jun 9 13:14:04 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Add entry in api.rst

&lt;span class="w"&gt; &lt;/span&gt;   This commit fixes some formatting issues and adds an entry for the
&lt;span class="w"&gt; &lt;/span&gt;   tiles.hipsdescription module in api.rst file.

&lt;span class="gh"&gt;diff --git a/docs/api.rst b/docs/api.rst&lt;/span&gt;
&lt;span class="gh"&gt;index eb152d9..46c5c1f 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/docs/api.rst&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/docs/api.rst&lt;/span&gt;
&lt;span class="gu"&gt;@@ -2,3 +2,4 @@ Reference/API&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;=============

&lt;span class="w"&gt; &lt;/span&gt;.. automodapi:: hips
&lt;span class="gi"&gt;+.. automodapi:: hips.tiles&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/tiles/hipsdescription.py b/hips/tiles/hipsdescription.py&lt;/span&gt;
&lt;span class="gh"&gt;index 8c81957..f13624a 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/tiles/hipsdescription.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/hipsdescription.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -9,8 +9,8 @@ from collections import OrderedDict&lt;/span&gt;


&lt;span class="w"&gt; &lt;/span&gt;class HipsDescription:
&lt;span class="gd"&gt;-    &amp;quot;&amp;quot;&amp;quot;This class provides method for parsing the HiPS properties file.&lt;/span&gt;
&lt;span class="gd"&gt;-       It also provides various getters around the commonly used properties.&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;This class provides methods for parsing the HiPS properties file.&lt;/span&gt;
&lt;span class="gi"&gt;+       It also provides multiple getters around the commonly used properties.&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;

&lt;span class="w"&gt; &lt;/span&gt;    def __init__(self, properties: str) -&amp;gt; None:
&lt;span class="gu"&gt;@@ -28,6 +28,7 @@ class HipsDescription:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        ----------
&lt;span class="w"&gt; &lt;/span&gt;        properties : str
&lt;span class="w"&gt; &lt;/span&gt;            HiPS file properties
&lt;span class="gi"&gt;+        &lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        Returns
&lt;span class="w"&gt; &lt;/span&gt;        -------
&lt;span class="w"&gt; &lt;/span&gt;        dict_properties : OrderedDict
&lt;span class="gu"&gt;@@ -46,7 +47,7 @@ class HipsDescription:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;OrderedDict: Returns the ordered dictionary containing the HiPS properties.&amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;        return self.properties

&lt;span class="gd"&gt;-    def get_base_url(self) -&amp;gt; [str, None]:&lt;/span&gt;
&lt;span class="gi"&gt;+    def get_base_url(self) -&amp;gt; str:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        &amp;quot;&amp;quot;&amp;quot;str: Returns the base url from the HiPS file properties.&amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;        try:
&lt;span class="w"&gt; &lt;/span&gt;            return self.properties[&amp;#39;hips_service_url&amp;#39;]

commit ff3299abeb476ffb1937b6647f6bdae26d659e34
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Jun 9 12:16:52 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Add tiles.hipsdescription module

&lt;span class="w"&gt; &lt;/span&gt;   This commits adds a class named HipsDescription which provides methods for parsing the HiPS properties.
&lt;span class="w"&gt; &lt;/span&gt;   It parses the given string and returns an object of OrderedDict. Additional getter methods are also included.
&lt;span class="w"&gt; &lt;/span&gt;   For the test cases, a sample properties file was obtained from http://alasky.unistra.fr/DSS/DSSColor/properties

&lt;span class="gh"&gt;diff --git a/hips/tiles/__init__.py b/hips/tiles/__init__.py&lt;/span&gt;
new file mode 100644
&lt;span class="gh"&gt;index 0000000..bb73205&lt;/span&gt;
&lt;span class="gd"&gt;--- /dev/null&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/__init__.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -0,0 +1,3 @@&lt;/span&gt;
&lt;span class="gi"&gt;+# Licensed under a 3-clause BSD style license - see LICENSE.rst&lt;/span&gt;
&lt;span class="gi"&gt;+&amp;quot;&amp;quot;&amp;quot;HiPS description class -- provides methods for parsing the HiPS file properties.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+from .hipsdescription import *&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/tiles/hipsdescription.py b/hips/tiles/hipsdescription.py&lt;/span&gt;
new file mode 100644
&lt;span class="gh"&gt;index 0000000..8c81957&lt;/span&gt;
&lt;span class="gd"&gt;--- /dev/null&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/hipsdescription.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -0,0 +1,74 @@&lt;/span&gt;
&lt;span class="gi"&gt;+# Licensed under a 3-clause BSD style license - see LICENSE.rst&lt;/span&gt;
&lt;span class="gi"&gt;+&amp;quot;&amp;quot;&amp;quot;HiPS description class -- provides methods for parsing the HiPS file properties.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+__all__ = [&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;#39;HipsDescription&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+]&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+from collections import OrderedDict&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+class HipsDescription:&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;This class provides method for parsing the HiPS properties file.&lt;/span&gt;
&lt;span class="gi"&gt;+       It also provides various getters around the commonly used properties.&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def __init__(self, properties: str) -&amp;gt; None:&lt;/span&gt;
&lt;span class="gi"&gt;+        super(HipsDescription, self).__init__()&lt;/span&gt;
&lt;span class="gi"&gt;+        self.properties = self.parse_file_properties(properties)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    @classmethod&lt;/span&gt;
&lt;span class="gi"&gt;+    def parse_file_properties(cls, properties: str) -&amp;gt; OrderedDict:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;Returns an ordered dictionary containing the HiPS file properties.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        This method parses the HiPS file properties and loads it in a dictionary,&lt;/span&gt;
&lt;span class="gi"&gt;+        then is casts it to `collections.OrderedDict` type.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        Parameters&lt;/span&gt;
&lt;span class="gi"&gt;+        ----------&lt;/span&gt;
&lt;span class="gi"&gt;+        properties : str&lt;/span&gt;
&lt;span class="gi"&gt;+            HiPS file properties&lt;/span&gt;
&lt;span class="gi"&gt;+        Returns&lt;/span&gt;
&lt;span class="gi"&gt;+        -------&lt;/span&gt;
&lt;span class="gi"&gt;+        dict_properties : OrderedDict&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        properties = properties.split(&amp;#39;\n&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        dict_properties = {}&lt;/span&gt;
&lt;span class="gi"&gt;+        for property in properties:&lt;/span&gt;
&lt;span class="gi"&gt;+            key_value = property.split(&amp;#39;=&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+            try:&lt;/span&gt;
&lt;span class="gi"&gt;+                dict_properties[key_value[0].strip()] = key_value[1].strip()&lt;/span&gt;
&lt;span class="gi"&gt;+            except:&lt;/span&gt;
&lt;span class="gi"&gt;+                pass&lt;/span&gt;
&lt;span class="gi"&gt;+        return OrderedDict(dict_properties)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def get_properties(self) -&amp;gt; OrderedDict:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;OrderedDict: Returns the ordered dictionary containing the HiPS properties.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        return self.properties&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def get_base_url(self) -&amp;gt; [str, None]:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;str: Returns the base url from the HiPS file properties.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        try:&lt;/span&gt;
&lt;span class="gi"&gt;+            return self.properties[&amp;#39;hips_service_url&amp;#39;]&lt;/span&gt;
&lt;span class="gi"&gt;+        except:&lt;/span&gt;
&lt;span class="gi"&gt;+            return None&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def get_title(self) -&amp;gt; str:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;str: Returns the title from the HiPS file properties.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        return self.properties[&amp;#39;obs_title&amp;#39;]&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def get_hips_version(self) -&amp;gt; float:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;float: Returns the HiPS version from the HiPS file properties.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        return float(self.properties[&amp;#39;hips_version&amp;#39;])&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def get_hips_frame(self) -&amp;gt; str:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;str: Returns the HiPS frame from the HiPS file properties.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        return self.properties[&amp;#39;hips_frame&amp;#39;]&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def get_hips_order(self) -&amp;gt; int:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;int: Returns the HiPS order from the HiPS file properties.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        return int(self.properties[&amp;#39;hips_order&amp;#39;])&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def get_tile_format(self) -&amp;gt; str:&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;str: Returns the HiPS tile format from the HiPS file properties.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        return self.properties[&amp;#39;hips_tile_format&amp;#39;]&lt;/span&gt;
\ No newline at end of file
&lt;span class="gh"&gt;diff --git a/hips/tiles/tests/__init__.py b/hips/tiles/tests/__init__.py&lt;/span&gt;
new file mode 100644
&lt;span class="gh"&gt;index 0000000..e69de29&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/tiles/tests/test_hipsdescription.py b/hips/tiles/tests/test_hipsdescription.py&lt;/span&gt;
new file mode 100644
&lt;span class="gh"&gt;index 0000000..458e976&lt;/span&gt;
&lt;span class="gd"&gt;--- /dev/null&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/tiles/tests/test_hipsdescription.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -0,0 +1,79 @@&lt;/span&gt;
&lt;span class="gi"&gt;+# Licensed under a 3-clause BSD style license - see LICENSE.rst&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+from .. hipsdescription import HipsDescription&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+class TestHiPSDescription:&lt;/span&gt;
&lt;span class="gi"&gt;+    @classmethod&lt;/span&gt;
&lt;span class="gi"&gt;+    def setup_class(cls):&lt;/span&gt;
&lt;span class="gi"&gt;+        # These HiPS properties were obtained from: http://alasky.u-strasbg.fr/DSS/DSSColor/properties&lt;/span&gt;
&lt;span class="gi"&gt;+        hips_properties = &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        creator_did          = ivo://CDS/P/DSS2/color&lt;/span&gt;
&lt;span class="gi"&gt;+        obs_collection       = DSS colored&lt;/span&gt;
&lt;span class="gi"&gt;+        obs_title            = DSS colored&lt;/span&gt;
&lt;span class="gi"&gt;+        obs_description      = Color composition generated by CDS. This HiPS survey is based on 2 others HiPS surveys, respectively DSS2-red and DSS2-blue HiPS, both of them directly generated from original scanned plates downloaded from STScI site. The red component has been built from POSS-II F, AAO-SES,SR and SERC-ER plates. The blue component has been build from POSS-II J and SERC-J,EJ. The green component is based on the mean of other components. Three missing plates from red survey (253, 260, 359) has been replaced by pixels from the DSSColor STScI jpeg survey. The 11 missing blue plates (mainly in galactic plane) have not been replaced (only red component).&lt;/span&gt;
&lt;span class="gi"&gt;+        obs_copyright        = Digitized Sky Survey - STScI/NASA, Colored &amp;amp; Healpixed by CDS&lt;/span&gt;
&lt;span class="gi"&gt;+        obs_copyright_url    = http://archive.stsci.edu/dss/acknowledging.html&lt;/span&gt;
&lt;span class="gi"&gt;+        client_category      = Image/Optical/DSS&lt;/span&gt;
&lt;span class="gi"&gt;+        client_sort_key      = 03-00&lt;/span&gt;
&lt;span class="gi"&gt;+        hips_builder         = Aladin/HipsGen v9.039&lt;/span&gt;
&lt;span class="gi"&gt;+        hips_creation_date   = 2010-05-01T19:05Z&lt;/span&gt;
&lt;span class="gi"&gt;+        hips_release_date    = 2015-05-11T08:45Z&lt;/span&gt;
&lt;span class="gi"&gt;+        # hips_release_date    = 2016-12-13T14:51Z&lt;/span&gt;
&lt;span class="gi"&gt;+        hips_creator         = CDS (A.Oberto, P.Fernique)&lt;/span&gt;
&lt;span class="gi"&gt;+        hips_version         = 1.31&lt;/span&gt;
&lt;span class="gi"&gt;+        hips_order           = 9&lt;/span&gt;
&lt;span class="gi"&gt;+        hips_frame           = equatorial&lt;/span&gt;
&lt;span class="gi"&gt;+        hips_tile_width      = 512&lt;/span&gt;
&lt;span class="gi"&gt;+        hips_tile_format     = jpeg&lt;/span&gt;
&lt;span class="gi"&gt;+        dataproduct_type     = image&lt;/span&gt;
&lt;span class="gi"&gt;+        client_application   = AladinLite&lt;/span&gt;
&lt;span class="gi"&gt;+        moc_access_url       = http://alasky.u-strasbg.fr/DSS/DSSColor/Moc.fits&lt;/span&gt;
&lt;span class="gi"&gt;+        hips_service_url     = http://alasky.u-strasbg.fr/DSS/DSSColor&lt;/span&gt;
&lt;span class="gi"&gt;+        hips_status          = public master clonableOnce&lt;/span&gt;
&lt;span class="gi"&gt;+        hips_rgb_red         = DSS2Merged [1488.0 8488.8125 14666.0 Linear]&lt;/span&gt;
&lt;span class="gi"&gt;+        hips_rgb_blue        = DSS2-blue-XJ-S [4286.0 12122.5 19959.0 Linear]&lt;/span&gt;
&lt;span class="gi"&gt;+        hips_hierarchy       = median&lt;/span&gt;
&lt;span class="gi"&gt;+        hips_pixel_scale     = 2.236E-4&lt;/span&gt;
&lt;span class="gi"&gt;+        hips_initial_ra      = 085.30251&lt;/span&gt;
&lt;span class="gi"&gt;+        hips_initial_dec     = -02.25468&lt;/span&gt;
&lt;span class="gi"&gt;+        hips_initial_fov     = 2&lt;/span&gt;
&lt;span class="gi"&gt;+        moc_sky_fraction     = 1&lt;/span&gt;
&lt;span class="gi"&gt;+        dataproduct_subtype  = color&lt;/span&gt;
&lt;span class="gi"&gt;+        hips_copyright       = CNRS/Unistra&lt;/span&gt;
&lt;span class="gi"&gt;+        obs_ack              = The Digitized Sky Surveys were produced at the Space Telescope Science Institute under U.S. Government grant NAG W-2166. The images of these surveys are based on photographic data obtained using the Oschin Schmidt Telescope on Palomar Mountain and the UK Schmidt Telescope. The plates were processed into the present compressed digital form with the permission of these institutions&lt;/span&gt;
&lt;span class="gi"&gt;+        prov_progenitor      = STScI&lt;/span&gt;
&lt;span class="gi"&gt;+        bib_reference        = 2008AJ....136..735L&lt;/span&gt;
&lt;span class="gi"&gt;+        bib_reference_url    = http://simbad.u-strasbg.fr/simbad/sim-ref?bibcode=2008AJ....136..735L&lt;/span&gt;
&lt;span class="gi"&gt;+        # 1975-1999&lt;/span&gt;
&lt;span class="gi"&gt;+        t_min                = 42413&lt;/span&gt;
&lt;span class="gi"&gt;+        t_max                = 51179&lt;/span&gt;
&lt;span class="gi"&gt;+        obs_regime           = Optical&lt;/span&gt;
&lt;span class="gi"&gt;+        # Bandpass  422-967 THz&lt;/span&gt;
&lt;span class="gi"&gt;+        em_min               = 7.104086682464e-7&lt;/span&gt;
&lt;span class="gi"&gt;+        em_max               = 3.100232244054e-7&lt;/span&gt;
&lt;span class="gi"&gt;+        # hips_master_url     = ex: http://yourHipsServer/null&lt;/span&gt;
&lt;span class="gi"&gt;+        # For compatibility&lt;/span&gt;
&lt;span class="gi"&gt;+        label              = DSS colored&lt;/span&gt;
&lt;span class="gi"&gt;+        coordsys           = C&lt;/span&gt;
&lt;span class="gi"&gt;+        isColor            = true&lt;/span&gt;
&lt;span class="gi"&gt;+        ~&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+        cls.hipsdescription = HipsDescription(hips_properties)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_get_base_url(cls):&lt;/span&gt;
&lt;span class="gi"&gt;+        assert cls.hipsdescription.get_base_url() == &amp;#39;http://alasky.u-strasbg.fr/DSS/DSSColor&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_get_title(cls):&lt;/span&gt;
&lt;span class="gi"&gt;+        assert cls.hipsdescription.get_title() == &amp;#39;DSS colored&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_get_hips_version(cls):&lt;/span&gt;
&lt;span class="gi"&gt;+        assert cls.hipsdescription.get_hips_version() == 1.31&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_get_hips_frame(cls):&lt;/span&gt;
&lt;span class="gi"&gt;+        assert cls.hipsdescription.get_hips_frame() == &amp;#39;equatorial&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_get_hips_order(cls):&lt;/span&gt;
&lt;span class="gi"&gt;+        assert cls.hipsdescription.get_hips_order() == 9&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    def test_get_tile_format(cls):&lt;/span&gt;
&lt;span class="gi"&gt;+        assert cls.hipsdescription.get_tile_format() == &amp;#39;jpeg&amp;#39;&lt;/span&gt;
\ No newline at end of file

commit 0b1359036a1436551234dd21ce47149c533c1bea
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Wed Jun 7 14:12:46 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Update utils.healpix module

&lt;span class="w"&gt; &lt;/span&gt;   This commit addresses import conventions and also some formatting issues. An example was also added and the test case was updated to follow this issue: https://github.com/healpy/healpy/issues/393#issuecomment-305994042.

&lt;span class="gh"&gt;diff --git a/hips/utils/healpix.py b/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gh"&gt;index f9a86e6..6ea5681 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -9,14 +9,14 @@ __all__ = [&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;#39;boundaries&amp;#39;,
&lt;span class="w"&gt; &lt;/span&gt;]

&lt;span class="gd"&gt;-import healpy&lt;/span&gt;
&lt;span class="gi"&gt;+import healpy as hp&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;import numpy as np


&lt;span class="w"&gt; &lt;/span&gt;def boundaries(nside: int, pix: int, nest: bool=False) -&amp;gt; tuple:
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;Returns an array containing the angle (theta and phi) in radians.

&lt;span class="gd"&gt;-    This method calls `healpy.boundaries` and `healpy.vec2ang`.&lt;/span&gt;
&lt;span class="gi"&gt;+    This function calls `healpy.boundaries` and `healpy.vec2ang`.&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    Parameters
&lt;span class="w"&gt; &lt;/span&gt;    ----------
&lt;span class="gu"&gt;@@ -33,14 +33,21 @@ def boundaries(nside: int, pix: int, nest: bool=False) -&amp;gt; tuple:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        Returns the angle (theta and phi) in radians

&lt;span class="w"&gt; &lt;/span&gt;    Examples
&lt;span class="gd"&gt;-    -------&lt;/span&gt;
&lt;span class="gd"&gt;-    nside = 8&lt;/span&gt;
&lt;span class="gd"&gt;-    pix = 450&lt;/span&gt;
&lt;span class="gd"&gt;-    theta, phi = boundaries(nside, pix, nest=True)&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    radec = SkyCoord(ra=phi, dec=np.pi/2 - theta, unit=&amp;#39;radian&amp;#39;, frame=&amp;#39;icrs&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+    --------&lt;/span&gt;
&lt;span class="gi"&gt;+    ::&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; import numpy as np&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; from hips.utils import boundaries&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; from astropy.coordinates import SkyCoord&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; nside = 8&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; pix = 450&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; theta, phi = boundaries(nside, pix, nest=True)&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;gt;&amp;gt;&amp;gt; SkyCoord(ra=phi, dec=np.pi/2 - theta, unit=&amp;#39;radian&amp;#39;, frame=&amp;#39;icrs&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+        &amp;lt;SkyCoord (ICRS): (ra, dec) in deg&lt;/span&gt;
&lt;span class="gi"&gt;+        [( 264.375, -24.62431835), ( 258.75 , -30.        ),&lt;/span&gt;
&lt;span class="gi"&gt;+         ( 264.375, -35.68533471), ( 270.   , -30.        )]&amp;gt;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;

&lt;span class="gd"&gt;-    boundary_coords = healpy.boundaries(nside, pix, nest=nest)&lt;/span&gt;
&lt;span class="gd"&gt;-    theta, phi = healpy.vec2ang(np.transpose(boundary_coords))&lt;/span&gt;
&lt;span class="gi"&gt;+    boundary_coords = hp.boundaries(nside, pix, nest=nest)&lt;/span&gt;
&lt;span class="gi"&gt;+    theta, phi = hp.vec2ang(np.transpose(boundary_coords))&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    return theta, phi
&lt;span class="gh"&gt;diff --git a/hips/utils/tests/test_healpix.py b/hips/utils/tests/test_healpix.py&lt;/span&gt;
&lt;span class="gh"&gt;index 93056e4..7606819 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/tests/test_healpix.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/tests/test_healpix.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,15 +1,24 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="gi"&gt;+import numpy as np&lt;/span&gt;
&lt;span class="gi"&gt;+import healpy as hp&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from .. healpix import boundaries
&lt;span class="w"&gt; &lt;/span&gt;from astropy.coordinates import SkyCoord
&lt;span class="gd"&gt;-import numpy as np&lt;/span&gt;
&lt;span class="gi"&gt;+from numpy.testing import assert_allclose&lt;/span&gt;


&lt;span class="w"&gt; &lt;/span&gt;def test_boundaries():
&lt;span class="gd"&gt;-    nside = 8&lt;/span&gt;
&lt;span class="gi"&gt;+    order = 3&lt;/span&gt;
&lt;span class="gi"&gt;+    nside = hp.order2nside(order)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    pix = 450
&lt;span class="w"&gt; &lt;/span&gt;    theta, phi = boundaries(nside, pix, nest=True)

&lt;span class="w"&gt; &lt;/span&gt;    radec = SkyCoord(ra=phi, dec=np.pi/2 - theta, unit=&amp;#39;radian&amp;#39;, frame=&amp;#39;icrs&amp;#39;)
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+    These HEALPix corner values were verified through Aladin Lite with the &amp;quot;Show&lt;/span&gt;
&lt;span class="gi"&gt;+    healpix grid&amp;quot; option turned on. More information can be found on this GitHub&lt;/span&gt;
&lt;span class="gi"&gt;+    issue: https://github.com/healpy/healpy/issues/393#issuecomment-305994042&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    radec_precomp = [[264.375,  258.75,  264.375,  270.],
&lt;span class="w"&gt; &lt;/span&gt;                     [-24.624318,  -30.,  -35.685335,  -30.]]
&lt;span class="gd"&gt;-    np.testing.assert_allclose([radec.ra, radec.dec], radec_precomp)&lt;/span&gt;
&lt;span class="gi"&gt;+    assert_allclose([radec.ra, radec.dec], radec_precomp)&lt;/span&gt;

commit fa832132c016e7285d24abe58d1438fff27e649b
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Tue Jun 6 22:34:14 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Add example

&lt;span class="gh"&gt;diff --git a/hips/utils/healpix.py b/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gh"&gt;index 5ebe49a..f9a86e6 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -31,6 +31,14 @@ def boundaries(nside: int, pix: int, nest: bool=False) -&amp;gt; tuple:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    -------
&lt;span class="w"&gt; &lt;/span&gt;    theta, phi : float, array
&lt;span class="w"&gt; &lt;/span&gt;        Returns the angle (theta and phi) in radians
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    Examples&lt;/span&gt;
&lt;span class="gi"&gt;+    -------&lt;/span&gt;
&lt;span class="gi"&gt;+    nside = 8&lt;/span&gt;
&lt;span class="gi"&gt;+    pix = 450&lt;/span&gt;
&lt;span class="gi"&gt;+    theta, phi = boundaries(nside, pix, nest=True)&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    radec = SkyCoord(ra=phi, dec=np.pi/2 - theta, unit=&amp;#39;radian&amp;#39;, frame=&amp;#39;icrs&amp;#39;)&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;

&lt;span class="w"&gt; &lt;/span&gt;    boundary_coords = healpy.boundaries(nside, pix, nest=nest)

commit 5de3e05971f7570eeb4402a266e8918b14d9e9ca
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Tue Jun 6 22:30:41 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Update test case

&lt;span class="gh"&gt;diff --git a/hips/utils/tests/test_healpix.py b/hips/utils/tests/test_healpix.py&lt;/span&gt;
&lt;span class="gh"&gt;index 7f63ebc..93056e4 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/tests/test_healpix.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/tests/test_healpix.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -1,5 +1,6 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;# Licensed under a 3-clause BSD style license - see LICENSE.rst
&lt;span class="w"&gt; &lt;/span&gt;from .. healpix import boundaries
&lt;span class="gi"&gt;+from astropy.coordinates import SkyCoord&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;import numpy as np


&lt;span class="gu"&gt;@@ -8,7 +9,7 @@ def test_boundaries():&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    pix = 450
&lt;span class="w"&gt; &lt;/span&gt;    theta, phi = boundaries(nside, pix, nest=True)

&lt;span class="gd"&gt;-    thetaphi_precomp = ([[2.00057176,  2.0943951,  2.19362291,  2.0943951],&lt;/span&gt;
&lt;span class="gd"&gt;-                         [4.61421421,  4.51603944,  4.61421421,  4.71238898]])&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-    np.testing.assert_allclose([theta, phi], thetaphi_precomp)&lt;/span&gt;
&lt;span class="gi"&gt;+    radec = SkyCoord(ra=phi, dec=np.pi/2 - theta, unit=&amp;#39;radian&amp;#39;, frame=&amp;#39;icrs&amp;#39;)&lt;/span&gt;
&lt;span class="gi"&gt;+    radec_precomp = [[264.375,  258.75,  264.375,  270.],&lt;/span&gt;
&lt;span class="gi"&gt;+                     [-24.624318,  -30.,  -35.685335,  -30.]]&lt;/span&gt;
&lt;span class="gi"&gt;+    np.testing.assert_allclose([radec.ra, radec.dec], radec_precomp)&lt;/span&gt;

commit aca32b631e949222e88bfc48ff25b1ecaa28f3cd
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Tue Jun 6 22:17:09 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Update formatting

&lt;span class="gh"&gt;diff --git a/hips/utils/healpix.py b/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gh"&gt;index 30996fa..5ebe49a 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -15,21 +15,17 @@ import numpy as np&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;def boundaries(nside: int, pix: int, nest: bool=False) -&amp;gt; tuple:
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;Returns an array containing the angle (theta and phi) in radians.
&lt;span class="gd"&gt;-    This method calls :py:meth:`.healpy.boundaries` and :py:meth:`.healpy.vec2ang`&lt;/span&gt;
&lt;span class="gd"&gt;-    &lt;/span&gt;
&lt;span class="gd"&gt;-    Parameters&lt;/span&gt;
&lt;span class="gd"&gt;-    ----------&lt;/span&gt;
&lt;span class="gd"&gt;-        nside : int&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-            The nside of the HEALPix map&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-        pix : int&lt;/span&gt;

&lt;span class="gd"&gt;-            Pixel identifier&lt;/span&gt;
&lt;span class="gi"&gt;+    This method calls `healpy.boundaries` and `healpy.vec2ang`.&lt;/span&gt;

&lt;span class="gd"&gt;-        nest : bool, optional&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-            If True, assume NESTED pixel ordering, otherwise, RING pixel ordering&lt;/span&gt;
&lt;span class="gi"&gt;+    Parameters&lt;/span&gt;
&lt;span class="gi"&gt;+    ----------&lt;/span&gt;
&lt;span class="gi"&gt;+    nside : int&lt;/span&gt;
&lt;span class="gi"&gt;+        The nside of the HEALPix map&lt;/span&gt;
&lt;span class="gi"&gt;+    pix : int&lt;/span&gt;
&lt;span class="gi"&gt;+        Pixel identifier&lt;/span&gt;
&lt;span class="gi"&gt;+    nest : bool, optional&lt;/span&gt;
&lt;span class="gi"&gt;+        If True, assume NESTED pixel ordering, otherwise, RING pixel ordering&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;    Returns
&lt;span class="w"&gt; &lt;/span&gt;    -------

commit 9da1f1fd3e79a552839bff00fc814396dcba7920
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Tue Jun 6 14:48:28 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Add newline

&lt;span class="gh"&gt;diff --git a/hips/utils/healpix.py b/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gh"&gt;index 2cd3c73..30996fa 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -16,6 +16,7 @@ import numpy as np&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;def boundaries(nside: int, pix: int, nest: bool=False) -&amp;gt; tuple:
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;Returns an array containing the angle (theta and phi) in radians.
&lt;span class="w"&gt; &lt;/span&gt;    This method calls :py:meth:`.healpy.boundaries` and :py:meth:`.healpy.vec2ang`
&lt;span class="gi"&gt;+    &lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    Parameters
&lt;span class="w"&gt; &lt;/span&gt;    ----------
&lt;span class="w"&gt; &lt;/span&gt;        nside : int

commit bbb48be4b0c77180fa34de83a2b6a3614639e3c1
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Tue Jun 6 14:43:01 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Update utils.healpix module

&lt;span class="gh"&gt;diff --git a/docs/api.rst b/docs/api.rst&lt;/span&gt;
&lt;span class="gh"&gt;index eb152d9..0b198d9 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/docs/api.rst&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/docs/api.rst&lt;/span&gt;
&lt;span class="gu"&gt;@@ -2,3 +2,4 @@ Reference/API&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;=============

&lt;span class="w"&gt; &lt;/span&gt;.. automodapi:: hips
&lt;span class="gi"&gt;+.. automodapi:: hips.utils&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/utils/healpix.py b/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gh"&gt;index 8e85569..2cd3c73 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -10,11 +10,12 @@ __all__ = [&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;]

&lt;span class="w"&gt; &lt;/span&gt;import healpy
&lt;span class="gi"&gt;+import numpy as np&lt;/span&gt;


&lt;span class="gd"&gt;-def boundaries(nside, pix, step=1, nest=False):&lt;/span&gt;
&lt;span class="gd"&gt;-    &amp;quot;&amp;quot;&amp;quot;Returns an array containing the longitude and latitude.&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gi"&gt;+def boundaries(nside: int, pix: int, nest: bool=False) -&amp;gt; tuple:&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;Returns an array containing the angle (theta and phi) in radians.&lt;/span&gt;
&lt;span class="gi"&gt;+    This method calls :py:meth:`.healpy.boundaries` and :py:meth:`.healpy.vec2ang`&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    Parameters
&lt;span class="w"&gt; &lt;/span&gt;    ----------
&lt;span class="w"&gt; &lt;/span&gt;        nside : int
&lt;span class="gu"&gt;@@ -25,20 +26,16 @@ def boundaries(nside, pix, step=1, nest=False):&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;            Pixel identifier

&lt;span class="gd"&gt;-        step : int, optional&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gd"&gt;-            Number of elements for each side of the pixel&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        nest : bool, optional

&lt;span class="w"&gt; &lt;/span&gt;            If True, assume NESTED pixel ordering, otherwise, RING pixel ordering

&lt;span class="w"&gt; &lt;/span&gt;    Returns
&lt;span class="w"&gt; &lt;/span&gt;    -------
&lt;span class="gd"&gt;-    longitude, latitude : float, array&lt;/span&gt;
&lt;span class="gd"&gt;-        Longitude and latitude positions&lt;/span&gt;
&lt;span class="gi"&gt;+    theta, phi : float, array&lt;/span&gt;
&lt;span class="gi"&gt;+        Returns the angle (theta and phi) in radians&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;

&lt;span class="w"&gt; &lt;/span&gt;    boundary_coords = healpy.boundaries(nside, pix, nest=nest)
&lt;span class="gd"&gt;-    lon, lat = healpy.vec2ang(boundary_coords, lonlat=True)&lt;/span&gt;
&lt;span class="gd"&gt;-    return [lon, lat]&lt;/span&gt;
&lt;span class="gi"&gt;+    theta, phi = healpy.vec2ang(np.transpose(boundary_coords))&lt;/span&gt;
&lt;span class="gi"&gt;+    return theta, phi&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/utils/tests/test_healpix.py b/hips/utils/tests/test_healpix.py&lt;/span&gt;
&lt;span class="gh"&gt;index 82b2223..7f63ebc 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/tests/test_healpix.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/tests/test_healpix.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -6,8 +6,9 @@ import numpy as np&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;def test_boundaries():
&lt;span class="w"&gt; &lt;/span&gt;    nside = 8
&lt;span class="w"&gt; &lt;/span&gt;    pix = 450
&lt;span class="gd"&gt;-    lon = boundaries(nside, pix, nest=True)&lt;/span&gt;
&lt;span class="gd"&gt;-    lonlat_precomp = [[242.19350089, 270., 226.97382512, 229.39870535],&lt;/span&gt;
&lt;span class="gd"&gt;-                      [-22.6263803, -43.1943471, -19.37793463, -33.05573115]]&lt;/span&gt;
&lt;span class="gi"&gt;+    theta, phi = boundaries(nside, pix, nest=True)&lt;/span&gt;

&lt;span class="gd"&gt;-    np.testing.assert_array_almost_equal(lon, lonlat_precomp, decimal=8)&lt;/span&gt;
&lt;span class="gi"&gt;+    thetaphi_precomp = ([[2.00057176,  2.0943951,  2.19362291,  2.0943951],&lt;/span&gt;
&lt;span class="gi"&gt;+                         [4.61421421,  4.51603944,  4.61421421,  4.71238898]])&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    np.testing.assert_allclose([theta, phi], thetaphi_precomp)&lt;/span&gt;

commit e4c00b949e0ca03f06c115927256f0539cdde9ad
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Sun Jun 4 19:47:11 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Add utils.healpix module

&lt;span class="gh"&gt;diff --git a/hips/utils/__init__.py b/hips/utils/__init__.py&lt;/span&gt;
&lt;span class="gh"&gt;index d3f4517..ed56631 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/utils/__init__.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/__init__.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -2,3 +2,4 @@&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;# This sub-module is destined for common non-package specific utility
&lt;span class="w"&gt; &lt;/span&gt;# functions.
&lt;span class="gi"&gt;+from .healpix import *&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/utils/healpix.py b/hips/utils/healpix.py&lt;/span&gt;
new file mode 100644
&lt;span class="gh"&gt;index 0000000..8e85569&lt;/span&gt;
&lt;span class="gd"&gt;--- /dev/null&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/healpix.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -0,0 +1,44 @@&lt;/span&gt;
&lt;span class="gi"&gt;+# Licensed under a 3-clause BSD style license - see LICENSE.rst&lt;/span&gt;
&lt;span class="gi"&gt;+&amp;quot;&amp;quot;&amp;quot;HEALpy wrapper functions.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+This module contains wrapper functions around HEALPix utilizing&lt;/span&gt;
&lt;span class="gi"&gt;+the healpy library&lt;/span&gt;
&lt;span class="gi"&gt;+&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+__all__ = [&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;#39;boundaries&amp;#39;,&lt;/span&gt;
&lt;span class="gi"&gt;+]&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+import healpy&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+def boundaries(nside, pix, step=1, nest=False):&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;Returns an array containing the longitude and latitude.&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    Parameters&lt;/span&gt;
&lt;span class="gi"&gt;+    ----------&lt;/span&gt;
&lt;span class="gi"&gt;+        nside : int&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            The nside of the HEALPix map&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        pix : int&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            Pixel identifier&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        step : int, optional&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            Number of elements for each side of the pixel&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+        nest : bool, optional&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+            If True, assume NESTED pixel ordering, otherwise, RING pixel ordering&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    Returns&lt;/span&gt;
&lt;span class="gi"&gt;+    -------&lt;/span&gt;
&lt;span class="gi"&gt;+    longitude, latitude : float, array&lt;/span&gt;
&lt;span class="gi"&gt;+        Longitude and latitude positions&lt;/span&gt;
&lt;span class="gi"&gt;+    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    boundary_coords = healpy.boundaries(nside, pix, nest=nest)&lt;/span&gt;
&lt;span class="gi"&gt;+    lon, lat = healpy.vec2ang(boundary_coords, lonlat=True)&lt;/span&gt;
&lt;span class="gi"&gt;+    return [lon, lat]&lt;/span&gt;
&lt;span class="gh"&gt;diff --git a/hips/utils/tests/test_healpix.py b/hips/utils/tests/test_healpix.py&lt;/span&gt;
new file mode 100644
&lt;span class="gh"&gt;index 0000000..82b2223&lt;/span&gt;
&lt;span class="gd"&gt;--- /dev/null&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/utils/tests/test_healpix.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -0,0 +1,13 @@&lt;/span&gt;
&lt;span class="gi"&gt;+# Licensed under a 3-clause BSD style license - see LICENSE.rst&lt;/span&gt;
&lt;span class="gi"&gt;+from .. healpix import boundaries&lt;/span&gt;
&lt;span class="gi"&gt;+import numpy as np&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+def test_boundaries():&lt;/span&gt;
&lt;span class="gi"&gt;+    nside = 8&lt;/span&gt;
&lt;span class="gi"&gt;+    pix = 450&lt;/span&gt;
&lt;span class="gi"&gt;+    lon = boundaries(nside, pix, nest=True)&lt;/span&gt;
&lt;span class="gi"&gt;+    lonlat_precomp = [[242.19350089, 270., 226.97382512, 229.39870535],&lt;/span&gt;
&lt;span class="gi"&gt;+                      [-22.6263803, -43.1943471, -19.37793463, -33.05573115]]&lt;/span&gt;
&lt;span class="gi"&gt;+&lt;/span&gt;
&lt;span class="gi"&gt;+    np.testing.assert_array_almost_equal(lon, lonlat_precomp, decimal=8)&lt;/span&gt;

commit 53a607b4fa0031fe3fedd0b6bf89ba03436d715a
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Fri Jun 2 11:38:40 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Update Sphinx docs

&lt;span class="gh"&gt;diff --git a/docs/index.rst b/docs/index.rst&lt;/span&gt;
&lt;span class="gh"&gt;index 3e75ee2..3ba6afe 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/docs/index.rst&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/docs/index.rst&lt;/span&gt;
&lt;span class="gu"&gt;@@ -4,8 +4,11 @@&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;Python HiPS package documentation
&lt;span class="w"&gt; &lt;/span&gt;#################################

&lt;span class="gd"&gt;-.. warning::&lt;/span&gt;
&lt;span class="gd"&gt;-&lt;/span&gt;
&lt;span class="gi"&gt;+.. note::&lt;/span&gt;
&lt;span class="gi"&gt;+ &lt;/span&gt;
&lt;span class="gi"&gt;+    This package is being developed as part of Google Summer of Code 2017 program. The progress &lt;/span&gt;
&lt;span class="gi"&gt;+    of this package will be updated on this `blog &amp;lt;https://adl1995.github.io&amp;gt;`_ and the student&amp;#39;s&lt;/span&gt;
&lt;span class="gi"&gt;+    application can be found `here &amp;lt;https://github.com/adl1995/HIPS-to-Py/blob/master/documents/application.md&amp;gt;`_.&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;    This ``hips`` package is in a very early stage of development.
&lt;span class="w"&gt; &lt;/span&gt;    We started to work on it in June 2017 and expect to have a first v0.1 release in summer 2017.
&lt;span class="w"&gt; &lt;/span&gt;    That said, please have a look and try to use it for your applications.

commit c7224fe181a18d041513973f043337e3d5af33fc
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Wed May 31 11:24:44 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Update Makefile to use python3 command

&lt;span class="gh"&gt;diff --git a/Makefile b/Makefile&lt;/span&gt;
&lt;span class="gh"&gt;index 508b55f..c883419 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/Makefile&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/Makefile&lt;/span&gt;
&lt;span class="gu"&gt;@@ -22,12 +22,12 @@ help:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;   @echo &amp;#39;&amp;#39;
&lt;span class="w"&gt; &lt;/span&gt;   @echo &amp;#39; Common `setup.py` commands:&amp;#39;
&lt;span class="w"&gt; &lt;/span&gt;   @echo &amp;#39;&amp;#39;
&lt;span class="gd"&gt;-   @echo &amp;#39;     python setup.py --help-commands&amp;#39;&lt;/span&gt;
&lt;span class="gd"&gt;-   @echo &amp;#39;     python setup.py install&amp;#39;&lt;/span&gt;
&lt;span class="gd"&gt;-   @echo &amp;#39;     python setup.py develop&amp;#39;&lt;/span&gt;
&lt;span class="gd"&gt;-   @echo &amp;#39;     python setup.py test -V&amp;#39;&lt;/span&gt;
&lt;span class="gd"&gt;-   @echo &amp;#39;     python setup.py test --help # to see available options&amp;#39;&lt;/span&gt;
&lt;span class="gd"&gt;-   @echo &amp;#39;     python setup.py build_docs # use `-l` for clean build&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+   @echo &amp;#39;     python3 setup.py --help-commands&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+   @echo &amp;#39;     python3 setup.py install&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+   @echo &amp;#39;     python3 setup.py develop&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+   @echo &amp;#39;     python3 setup.py test -V&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+   @echo &amp;#39;     python3 setup.py test --help # to see available options&amp;#39;&lt;/span&gt;
&lt;span class="gi"&gt;+   @echo &amp;#39;     python3 setup.py build_docs # use `-l` for clean build&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;   @echo &amp;#39;&amp;#39;
&lt;span class="w"&gt; &lt;/span&gt;   @echo &amp;#39; More info:&amp;#39;
&lt;span class="w"&gt; &lt;/span&gt;   @echo &amp;#39;&amp;#39;
&lt;span class="gu"&gt;@@ -36,14 +36,14 @@ help:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;   @echo &amp;#39;&amp;#39;

&lt;span class="w"&gt; &lt;/span&gt;test:
&lt;span class="gd"&gt;-   python -m pytest hips&lt;/span&gt;
&lt;span class="gi"&gt;+   python3 -m pytest hips&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;# TODO: add flake8 and pylint
&lt;span class="w"&gt; &lt;/span&gt;# TODO: activate mypy testing. Fix or configure to ignore these issues:
&lt;span class="w"&gt; &lt;/span&gt;# https://travis-ci.org/hipspy/hips/jobs/236913996#L694
&lt;span class="w"&gt; &lt;/span&gt;#  python -m mypy hips
&lt;span class="w"&gt; &lt;/span&gt;code-analysis:
&lt;span class="gd"&gt;-   python -m pycodestyle hips --count&lt;/span&gt;
&lt;span class="gi"&gt;+   python3 -m pycodestyle hips --count&lt;/span&gt;

&lt;span class="w"&gt; &lt;/span&gt;doc-show:
&lt;span class="w"&gt; &lt;/span&gt;   open docs/_build/html/index.html
&lt;span class="gu"&gt;@@ -63,4 +63,4 @@ trailing-spaces:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;   find $(PROJECT) docs -name &amp;quot;*.rst&amp;quot; -exec perl -pi -e &amp;#39;s/[ \t]*$$//&amp;#39; {} \;

&lt;span class="w"&gt; &lt;/span&gt;conda:
&lt;span class="gd"&gt;-   python setup.py bdist_conda&lt;/span&gt;
&lt;span class="gi"&gt;+   python3 setup.py bdist_conda&lt;/span&gt;

commit c5942b52a60a45dbe05e154dc381dd8ef3d2b46c
Author: Adeel Ahmad &amp;lt;adeelahmad14@hotmail.com&amp;gt;
Date:   Mon May 29 10:34:09 2017 +0500

&lt;span class="w"&gt; &lt;/span&gt;   Update simple.py

&lt;span class="w"&gt; &lt;/span&gt;   Return correct value.

&lt;span class="gh"&gt;diff --git a/hips/draw/simple.py b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gh"&gt;index 21c131a..f1a1675 100644&lt;/span&gt;
&lt;span class="gd"&gt;--- a/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gi"&gt;+++ b/hips/draw/simple.py&lt;/span&gt;
&lt;span class="gu"&gt;@@ -29,4 +29,4 @@ def hello(name: str) -&amp;gt; int:&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;        Meaning of life
&lt;span class="w"&gt; &lt;/span&gt;    &amp;quot;&amp;quot;&amp;quot;
&lt;span class="w"&gt; &lt;/span&gt;    print(&amp;#39;Hello {}&amp;#39;.format(name))
&lt;span class="gd"&gt;-    return 42&lt;/span&gt;
&lt;span class="gi"&gt;+    return 43&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content><category term="GSoC"/></entry><entry><title>Fixing tile distortion issue in hips package</title><link href="https://adl1995.github.io/fixing-tile-distortion-issue-in-hips-package.html" rel="alternate"/><published>2017-08-10T11:28:00+02:00</published><updated>2017-08-10T11:28:00+02:00</updated><author><name>Adeel Ahmad</name></author><id>tag:adl1995.github.io,2017-08-10:/fixing-tile-distortion-issue-in-hips-package.html</id><summary type="html">&lt;p&gt;As documented in the &lt;a href="http://hips.readthedocs.io/en/latest/drawing_algo.html#tile-distortion-issue"&gt;tile distortion issue&lt;/a&gt; section, the previous technique for drawing HiPS tiles brings some astrometry offsets for distorted&amp;nbsp;tiles.&lt;/p&gt;
&lt;p&gt;An example of such distortions can be viewed at this link (uncheck &amp;#8220;Activate deformations reduction algorithm&amp;#8221; to view the astrometry offsets): &lt;a href="http://cds.unistra.fr/~boch/AL/test-reduce-deformations2.html"&gt;http://cds.unistra.fr/~boch/&lt;span class="caps"&gt;AL&lt;/span&gt;/test-reduce-deformations2.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;To overcome this issue, the parent tile is divided into four children tiles if it meets the following two&amp;nbsp;criteria:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;One edge is greater than 300 pixels when&amp;nbsp;projected&lt;/li&gt;
&lt;li&gt;Or, the ratio of smaller diagonal on larger diagonal is smaller than 0.7 and one of the diagonal is greater than 150 pixels when&amp;nbsp;projected&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For handling these checks, a&amp;nbsp;function &lt;code&gt;is_tile_distorted&lt;/code&gt; is&amp;nbsp;introduced:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;is_tile_distorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;corners&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool …&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</summary><content type="html">&lt;p&gt;As documented in the &lt;a href="http://hips.readthedocs.io/en/latest/drawing_algo.html#tile-distortion-issue"&gt;tile distortion issue&lt;/a&gt; section, the previous technique for drawing HiPS tiles brings some astrometry offsets for distorted&amp;nbsp;tiles.&lt;/p&gt;
&lt;p&gt;An example of such distortions can be viewed at this link (uncheck &amp;#8220;Activate deformations reduction algorithm&amp;#8221; to view the astrometry offsets): &lt;a href="http://cds.unistra.fr/~boch/AL/test-reduce-deformations2.html"&gt;http://cds.unistra.fr/~boch/&lt;span class="caps"&gt;AL&lt;/span&gt;/test-reduce-deformations2.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;To overcome this issue, the parent tile is divided into four children tiles if it meets the following two&amp;nbsp;criteria:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;One edge is greater than 300 pixels when&amp;nbsp;projected&lt;/li&gt;
&lt;li&gt;Or, the ratio of smaller diagonal on larger diagonal is smaller than 0.7 and one of the diagonal is greater than 150 pixels when&amp;nbsp;projected&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For handling these checks, a&amp;nbsp;function &lt;code&gt;is_tile_distorted&lt;/code&gt; is&amp;nbsp;introduced:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;is_tile_distorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;corners&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Is the tile with the given corners distorted?&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;edges&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;diagonals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;measure_tile_lengths&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;corners&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;diagonal_ratio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diagonals&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nb"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diagonals&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nb"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;edges&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt;
        &lt;span class="nb"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diagonals&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;150&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt;
        &lt;span class="n"&gt;diagonal_ratio&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mf"&gt;0.7&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;measure_tile_lengths&lt;/code&gt; function computes the length of tile edges and diagonals by computing the euclidean distance between two points, its source code can be found &lt;a href="https://github.com/hipspy/hips/blob/master/hips/draw/paint.py#L225"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If a tile passes the conditions specified above, it is then split into four children tiles, which is achieved by the following Python&amp;nbsp;property:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nd"&gt;@property&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;children&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;HipsTile&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Create four children tiles from parent tile.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
   &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
   &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
   &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
   &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;tiles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;idx&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;span class="n"&gt;meta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HipsTileMeta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ipix&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;file_format&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;tile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;from_numpy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;tiles&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;tiles&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The criteria for choosing the&amp;nbsp;HiPS &lt;code&gt;order&lt;/code&gt; and &lt;code&gt;ipix&lt;/code&gt; for children tiles is mentioned in the &lt;a href="http://www.ivoa.net/documents/HiPS/20160623/WD-HiPS-1.0-20160623.pdf"&gt;HiPS &lt;span class="caps"&gt;IVOA&lt;/span&gt; paper&lt;/a&gt;, Appendix, page&amp;nbsp;31.&lt;/p&gt;
&lt;p&gt;A Jupyter notebook illustrating this functionality can be found &lt;a href="https://github.com/adl1995/HIPS-to-Py/blob/master/examples/Precise%20drawing-ICRS.ipynb"&gt;here&lt;/a&gt;. Currently, there is no recursion involved in tile splitting, so the parent tile is split only once. Therefore, the results can be further improved, a side-by-side comparison is shown in this &lt;span class="caps"&gt;GIF&lt;/span&gt;&amp;nbsp;image:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Precise and simple drawing" src="https://adl1995.github.io/images/precise-simple-gamma.gif"&gt;&lt;/p&gt;
&lt;p&gt;The red line connects five stars, of which the middle one is the &lt;a href="https://en.wikipedia.org/wiki/Gamma_Cassiopeiae"&gt;Gamma Cassiopeiae&lt;/a&gt;. This can be used to verfiy if the image is drawn correctly, by looking at the corner positions and confirming if they align correctly with the&amp;nbsp;stars.&lt;/p&gt;</content><category term="GSoC"/></entry><entry><title>RGB tile drawing in hips package</title><link href="https://adl1995.github.io/rgb-tile-drawing-in-hips-package.html" rel="alternate"/><published>2017-07-23T12:46:00+02:00</published><updated>2017-07-23T12:46:00+02:00</updated><author><name>Adeel Ahmad</name></author><id>tag:adl1995.github.io,2017-07-23:/rgb-tile-drawing-in-hips-package.html</id><summary type="html">&lt;p&gt;The &lt;code&gt;hips&lt;/code&gt; package now supports &lt;span class="caps"&gt;RGB&lt;/span&gt; tile drawing. To make this possible, the output image dimensions had to be altered according to the following&amp;nbsp;configuration:&lt;/p&gt;
&lt;p&gt;The output image shape is two dimensional for grayscale, and three dimensional for color&amp;nbsp;images:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;shape = (height, width)&lt;/code&gt; for &lt;span class="caps"&gt;FITS&lt;/span&gt; images with one grayscale&amp;nbsp;channel&lt;/li&gt;
&lt;li&gt;&lt;code&gt;shape = (height, width, 3)&lt;/code&gt; for &lt;span class="caps"&gt;JPG&lt;/span&gt; images with three &lt;span class="caps"&gt;RGB&lt;/span&gt;&amp;nbsp;channels&lt;/li&gt;
&lt;li&gt;&lt;code&gt;shape = (height, width, 4)&lt;/code&gt; for &lt;span class="caps"&gt;PNG&lt;/span&gt; images with four &lt;span class="caps"&gt;RGBA&lt;/span&gt;&amp;nbsp;channels&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In addition to this, in-case of &lt;span class="caps"&gt;JPG&lt;/span&gt; and &lt;span class="caps"&gt;PNG&lt;/span&gt; format, the tiles are flipped in the vertical direction, which leads to incorrect tile drawing using the previous technique. The figure below is taken from the &lt;a href="http://www.ivoa.net/documents/HiPS/20160623/WD-HiPS-1.0-20160623.pdf"&gt;hips paper&lt;/a&gt;, figure 6, which shows the inverted&amp;nbsp;tiles.&lt;/p&gt;
&lt;p&gt;&lt;img alt="HiPS inverted tiles figure" src="https://adl1995.github.io/images/flipped-hips-tile.png"&gt;&lt;/p&gt;
&lt;p&gt;To overcome this, the …&lt;/p&gt;</summary><content type="html">&lt;p&gt;The &lt;code&gt;hips&lt;/code&gt; package now supports &lt;span class="caps"&gt;RGB&lt;/span&gt; tile drawing. To make this possible, the output image dimensions had to be altered according to the following&amp;nbsp;configuration:&lt;/p&gt;
&lt;p&gt;The output image shape is two dimensional for grayscale, and three dimensional for color&amp;nbsp;images:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;shape = (height, width)&lt;/code&gt; for &lt;span class="caps"&gt;FITS&lt;/span&gt; images with one grayscale&amp;nbsp;channel&lt;/li&gt;
&lt;li&gt;&lt;code&gt;shape = (height, width, 3)&lt;/code&gt; for &lt;span class="caps"&gt;JPG&lt;/span&gt; images with three &lt;span class="caps"&gt;RGB&lt;/span&gt;&amp;nbsp;channels&lt;/li&gt;
&lt;li&gt;&lt;code&gt;shape = (height, width, 4)&lt;/code&gt; for &lt;span class="caps"&gt;PNG&lt;/span&gt; images with four &lt;span class="caps"&gt;RGBA&lt;/span&gt;&amp;nbsp;channels&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In addition to this, in-case of &lt;span class="caps"&gt;JPG&lt;/span&gt; and &lt;span class="caps"&gt;PNG&lt;/span&gt; format, the tiles are flipped in the vertical direction, which leads to incorrect tile drawing using the previous technique. The figure below is taken from the &lt;a href="http://www.ivoa.net/documents/HiPS/20160623/WD-HiPS-1.0-20160623.pdf"&gt;hips paper&lt;/a&gt;, figure 6, which shows the inverted&amp;nbsp;tiles.&lt;/p&gt;
&lt;p&gt;&lt;img alt="HiPS inverted tiles figure" src="https://adl1995.github.io/images/flipped-hips-tile.png"&gt;&lt;/p&gt;
&lt;p&gt;To overcome this, the following function was introduced which handles the creation of tile corner pixel coordinates, which are used during projective&amp;nbsp;transformation:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;tile_corner_pixel_coordinates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;file_format&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ndarray&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Tile corner pixel coordinates for projective transform.&lt;/span&gt;

&lt;span class="sd"&gt;    The HiPS tile pixels (given by the NumPy array ``tile.data`` in our code)&lt;/span&gt;
&lt;span class="sd"&gt;    have a different orientation for FITS than for JPEG or PNG.&lt;/span&gt;
&lt;span class="sd"&gt;    This is noted in the HiPS spec (http://www.ivoa.net/documents/HiPS/20160623/index.html):&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;gt; Contrary to FITS convention, in JPG and PNG the lines of the pixel&lt;/span&gt;
&lt;span class="sd"&gt;    array are stored in top-&amp;gt;down direction.&lt;/span&gt;

&lt;span class="sd"&gt;    The order of corners below is chosen such that it matches the order&lt;/span&gt;
&lt;span class="sd"&gt;    of the pixel corner sky coordinates from ``healpix_pixel_corners``:&lt;/span&gt;

&lt;span class="sd"&gt;    - north&lt;/span&gt;
&lt;span class="sd"&gt;    - west&lt;/span&gt;
&lt;span class="sd"&gt;    - south&lt;/span&gt;
&lt;span class="sd"&gt;    - east&lt;/span&gt;

&lt;span class="sd"&gt;    which then gives correct results when used to compute the projective transform.&lt;/span&gt;
&lt;span class="sd"&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;file_format&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;fits&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;corners&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;  &lt;span class="c1"&gt;# north&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;  &lt;span class="c1"&gt;# west&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;  &lt;span class="c1"&gt;# south&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;  &lt;span class="c1"&gt;# east&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;file_format&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;jpg&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;png&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;}:&lt;/span&gt;
        &lt;span class="n"&gt;corners&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;  &lt;span class="c1"&gt;# north&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;  &lt;span class="c1"&gt;# west&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;  &lt;span class="c1"&gt;# south&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;  &lt;span class="c1"&gt;# east&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Invalid file_format: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;file_format&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;corners&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;As a result of this new update, the user can now draw &lt;span class="caps"&gt;RGB&lt;/span&gt; tiles. The following image was created from&amp;nbsp;the &lt;code&gt;DSSColor&lt;/code&gt; survey using &lt;span class="caps"&gt;JPG&lt;/span&gt; file format. For this image, the FoV (field of view) was set to 50 degrees, so we get a nice large&amp;nbsp;image:&lt;/p&gt;
&lt;p&gt;&lt;img alt="All sky image" src="https://adl1995.github.io/images/DSSColor-allsky-scaled.jpg"&gt;&lt;/p&gt;
&lt;p&gt;However, there is still an issue which is yet to be fixed. Currently, we simply make full sky images for each projected tile and then sum them up, this leads to bright lines at tiles edges as evident from the image above. This is caused due to pixels being drawn and summed twice at the same&amp;nbsp;location.&lt;/p&gt;</content><category term="GSoC"/></entry><entry><title>Parameterized testing using Pytest</title><link href="https://adl1995.github.io/parameterized-testing-using-pytest.html" rel="alternate"/><published>2017-07-13T09:40:00+02:00</published><updated>2017-07-13T09:40:00+02:00</updated><author><name>Adeel Ahmad</name></author><id>tag:adl1995.github.io,2017-07-13:/parameterized-testing-using-pytest.html</id><summary type="html">&lt;p&gt;Pytest provides a feature for parameterized testing in Python. The&amp;nbsp;built-in &lt;code&gt;pytest.mark.parametrize&lt;/code&gt; decorator enables parametrization of arguments for a test function. This allows the user to compare the values for input and&amp;nbsp;output.&lt;/p&gt;
&lt;p&gt;Here is a typical example which shows its&amp;nbsp;usage:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;get_hips_order_for_resolution_pars&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tile_width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;512&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resolution&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.01232&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resolution_res&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.06395791924665553&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tile_width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resolution&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.0016022&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resolution_res&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.003997369952915971&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tile_width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resolution&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.00009032&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resolution_res&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.00012491781102862408&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nd"&gt;@pytest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mark&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parametrize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;pars&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;get_hips_order_for_resolution_pars&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;test_get_hips_order_for_resolution&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pars&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;hips_order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_get_hips_order_for_resolution&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pars&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;tile_width&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;pars&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;resolution&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;hips_order&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;pars&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;order&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;hips_resolution&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nside2resol&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order2nside&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hips_order&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;assert_allclose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hips_resolution&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pars&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;resolution_res&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Without the support of parameterized testing, the code had to be duplicated three times …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Pytest provides a feature for parameterized testing in Python. The&amp;nbsp;built-in &lt;code&gt;pytest.mark.parametrize&lt;/code&gt; decorator enables parametrization of arguments for a test function. This allows the user to compare the values for input and&amp;nbsp;output.&lt;/p&gt;
&lt;p&gt;Here is a typical example which shows its&amp;nbsp;usage:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;get_hips_order_for_resolution_pars&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tile_width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;512&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resolution&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.01232&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resolution_res&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.06395791924665553&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tile_width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resolution&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.0016022&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resolution_res&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.003997369952915971&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tile_width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resolution&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.00009032&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resolution_res&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.00012491781102862408&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nd"&gt;@pytest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mark&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parametrize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;pars&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;get_hips_order_for_resolution_pars&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;test_get_hips_order_for_resolution&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pars&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;hips_order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_get_hips_order_for_resolution&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pars&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;tile_width&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;pars&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;resolution&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;hips_order&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;pars&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;order&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;hips_resolution&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nside2resol&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order2nside&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hips_order&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;assert_allclose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hips_resolution&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pars&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;resolution_res&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Without the support of parameterized testing, the code had to be duplicated three times in this particular case. Additionally, parameterized testing allows users to see the expected values elegantly without running the&amp;nbsp;tests. &lt;/p&gt;</content><category term="GSoC"/></entry><entry><title>Creating custom decorators in Python 3.6</title><link href="https://adl1995.github.io/creating-custom-decorators-in-python-36.html" rel="alternate"/><published>2017-07-02T12:41:00+02:00</published><updated>2017-07-02T12:41:00+02:00</updated><author><name>Adeel Ahmad</name></author><id>tag:adl1995.github.io,2017-07-02:/creating-custom-decorators-in-python-36.html</id><summary type="html">&lt;p&gt;In the &lt;a href="http://hips.readthedocs.io"&gt;hips&lt;/a&gt; package, often data has to be fetched from remote servers, especially HiPS tiles. One way to cut back on the queries was by introducing the &lt;a href="github.com/hipspy/hips-extra"&gt;hips-extra&lt;/a&gt; repository. This contains HiPS tiles from various HiPS surveys. This allows us to quickly fetch tiles from local storage, which makes the testing process less&amp;nbsp;time-consuming.&lt;/p&gt;
&lt;p&gt;As &lt;a href="github.com/hipspy/hips-extra"&gt;hips-extra&lt;/a&gt; repository does not come with the standard &lt;a href="http://hips.readthedocs.io"&gt;hips&lt;/a&gt; package, user has to manually clone it. The availability of the package is checked using an environment variable. This can be set&amp;nbsp;using:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nv"&gt;$export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;HIPS_EXTRA&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="se"&gt;\p&lt;/span&gt;ath&lt;span class="se"&gt;\t&lt;/span&gt;o&lt;span class="se"&gt;\h&lt;/span&gt;ips-extra
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In Python, the path can be retrieved using&amp;nbsp;the &lt;code&gt;os&lt;/code&gt; module: &lt;code&gt;os.environ['HIPS_EXTRA']&lt;/code&gt;. Now, what if the user does not have &lt;a href="github.com/hipspy/hips-extra"&gt;hips-extra&lt;/a&gt; repository …&lt;/p&gt;</summary><content type="html">&lt;p&gt;In the &lt;a href="http://hips.readthedocs.io"&gt;hips&lt;/a&gt; package, often data has to be fetched from remote servers, especially HiPS tiles. One way to cut back on the queries was by introducing the &lt;a href="github.com/hipspy/hips-extra"&gt;hips-extra&lt;/a&gt; repository. This contains HiPS tiles from various HiPS surveys. This allows us to quickly fetch tiles from local storage, which makes the testing process less&amp;nbsp;time-consuming.&lt;/p&gt;
&lt;p&gt;As &lt;a href="github.com/hipspy/hips-extra"&gt;hips-extra&lt;/a&gt; repository does not come with the standard &lt;a href="http://hips.readthedocs.io"&gt;hips&lt;/a&gt; package, user has to manually clone it. The availability of the package is checked using an environment variable. This can be set&amp;nbsp;using:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nv"&gt;$export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;HIPS_EXTRA&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="se"&gt;\p&lt;/span&gt;ath&lt;span class="se"&gt;\t&lt;/span&gt;o&lt;span class="se"&gt;\h&lt;/span&gt;ips-extra
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;In Python, the path can be retrieved using&amp;nbsp;the &lt;code&gt;os&lt;/code&gt; module: &lt;code&gt;os.environ['HIPS_EXTRA']&lt;/code&gt;. Now, what if the user does not have &lt;a href="github.com/hipspy/hips-extra"&gt;hips-extra&lt;/a&gt; repository on their system. In this case, we don&amp;#8217;t want to raise any errors, but to simply skip the test case. In order to do this, Python decorators become&amp;nbsp;useful.&lt;/p&gt;
&lt;h3&gt;A brief overview on Python&amp;nbsp;Decorators&lt;/h3&gt;
&lt;p&gt;Decorators provide a simple syntax for calling higher-order functions. By definition, a decorator is a function that takes another function and extends the behavior of the latter function without explicitly modifying it. Some of the most commonly used decorators&amp;nbsp;are &lt;code&gt;@classmethod&lt;/code&gt;, &lt;code&gt;@staticmethod&lt;/code&gt;,&amp;nbsp;and &lt;code&gt;@property&lt;/code&gt;. The decorators are written above a function&amp;nbsp;header:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nd"&gt;@property&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Writing your own&amp;nbsp;decorators&lt;/h3&gt;
&lt;p&gt;To write our decorators, we create a wrapper function&amp;nbsp;around &lt;code&gt;pytest.mark.skipif&lt;/code&gt;. The &lt;a href="https://docs.pytest.org/en/latest/skipping.html#id1"&gt;documentation&lt;/a&gt; of this function can be viewed to get detailed information on how it works. The function below checks if&amp;nbsp;the &lt;code&gt;HIPS_EXTRA&lt;/code&gt; environment variable is set on the user&amp;#8217;s system, and if it is, it returns a True&amp;nbsp;boolean.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;pathlib&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;has_hips_extra&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Is hips-extra available? (`bool`)&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;HIPS_EXTRA&amp;#39;&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;HIPS_EXTRA&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;datasets/samples/DSS2Red/properties&amp;#39;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_file&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;A separate function below makes use of the above function to check whether the package is available or&amp;nbsp;not:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;pytest&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;requires_hips_extra&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Decorator to mark tests requiring ``hips-extra`` data.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;skip_it&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;has_hips_extra&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;reason&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;No hips-extra data available.&amp;#39;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;pytest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mark&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;skipif&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;skip_it&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This decorator can now be used. An example illustrating its use is shown below. First, we import the function from its location and then we apply it to another function that uses data from &lt;a href="github.com/hipspy/hips-extra"&gt;hips-extra&lt;/a&gt;&amp;nbsp;repository:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;hips.utils.testing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requires_hips_extra&lt;/span&gt;

&lt;span class="nd"&gt;@requires_hips_extra&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;test_draw_sky_image&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If the&amp;nbsp;user&amp;#8217;s &lt;code&gt;HIPS_EXTRA&lt;/code&gt; environment variable does not contain a valid path, the above test will be&amp;nbsp;skipped.&lt;/p&gt;</content><category term="GSoC"/></entry><entry><title>HiPS tile drawing</title><link href="https://adl1995.github.io/hips-tile-drawing.html" rel="alternate"/><published>2017-06-24T11:50:00+02:00</published><updated>2017-06-24T11:50:00+02:00</updated><author><name>Adeel Ahmad</name></author><id>tag:adl1995.github.io,2017-06-24:/hips-tile-drawing.html</id><summary type="html">&lt;p&gt;One of the major part of the &lt;a href="http://hips.readthedocs.io"&gt;hips package&lt;/a&gt; is being able to draw HiPS tiles onto a larger sky image. This involves using projective transformation for computing and drawing a HiPS tile at the correct location. The discussion below is for the tile containing the galactic center pixel values. To achieve this, several steps are&amp;nbsp;involved.&lt;/p&gt;
&lt;h3&gt;Computing boundaries of a HiPS&amp;nbsp;tile&lt;/h3&gt;
&lt;p&gt;A tile is defined by four corners, &lt;a href="http://hips.readthedocs.io"&gt;hips&lt;/a&gt; uses the &lt;a href="https://github.com/astropy/astropy-healpix/blob/3fcb80921f362e3620f9892e934d4f0f69b7bf21/astropy_healpix/high_level.py"&gt;astropy_healpix.HEALPix.boundaries_skycoord&lt;/a&gt; function which returns the angle&amp;nbsp;(&lt;span class="math"&gt;\(\theta\)&lt;/span&gt; and &lt;span class="math"&gt;\(\phi\)&lt;/span&gt;) in radians wrapped inside &lt;a href="http://docs.astropy.org/en/stable/api/astropy.coordinates.SkyCoord.html"&gt;astropy.coordinates.SkyCoord&lt;/a&gt; class. This contains the four corners of a HiPS tile in the order (North, West, South, East). A snippet which computes the corners of a HiPS tile is provided …&lt;/p&gt;</summary><content type="html">&lt;p&gt;One of the major part of the &lt;a href="http://hips.readthedocs.io"&gt;hips package&lt;/a&gt; is being able to draw HiPS tiles onto a larger sky image. This involves using projective transformation for computing and drawing a HiPS tile at the correct location. The discussion below is for the tile containing the galactic center pixel values. To achieve this, several steps are&amp;nbsp;involved.&lt;/p&gt;
&lt;h3&gt;Computing boundaries of a HiPS&amp;nbsp;tile&lt;/h3&gt;
&lt;p&gt;A tile is defined by four corners, &lt;a href="http://hips.readthedocs.io"&gt;hips&lt;/a&gt; uses the &lt;a href="https://github.com/astropy/astropy-healpix/blob/3fcb80921f362e3620f9892e934d4f0f69b7bf21/astropy_healpix/high_level.py"&gt;astropy_healpix.HEALPix.boundaries_skycoord&lt;/a&gt; function which returns the angle&amp;nbsp;(&lt;span class="math"&gt;\(\theta\)&lt;/span&gt; and &lt;span class="math"&gt;\(\phi\)&lt;/span&gt;) in radians wrapped inside &lt;a href="http://docs.astropy.org/en/stable/api/astropy.coordinates.SkyCoord.html"&gt;astropy.coordinates.SkyCoord&lt;/a&gt; class. This contains the four corners of a HiPS tile in the order (North, West, South, East). A snippet which computes the corners of a HiPS tile is provided&amp;nbsp;below:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;numpy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;np&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;astropy.coordinates&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SkyCoord&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;hips.utils&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boundaries&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;theta&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;phi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boundaries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nside&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;450&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;SkyCoord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ra&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;phi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pi&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;theta&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;radian&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;icrs&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SkyCoord&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ICRS&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ra&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dec&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;deg&lt;/span&gt;
&lt;span class="p"&gt;[(&lt;/span&gt; &lt;span class="mf"&gt;264.375&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;24.62431835&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mf"&gt;258.75&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;30.&lt;/span&gt;        &lt;span class="p"&gt;),&lt;/span&gt;
 &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mf"&gt;264.375&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;35.68533471&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mf"&gt;270.&lt;/span&gt;   &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;30.&lt;/span&gt;        &lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;These are also referred to as HEALPix pixel corners. The returned latitude and longitude values (in degrees) are wrapped inside a list of tuples. The HiPS tile under discussion is shown&amp;nbsp;below:&lt;/p&gt;
&lt;p&gt;&lt;img alt="HiPS tile" src="https://adl1995.github.io/images/hips_tile.png"&gt;&lt;/p&gt;
&lt;h3&gt;Computing HEALPix pixel&amp;nbsp;indices&lt;/h3&gt;
&lt;p&gt;We also require HEALPix pixel indices which helps us in computing the boundaries for all the tiles. For this, &lt;a href="http://hips.readthedocs.io"&gt;hips&lt;/a&gt; provides a &lt;a href="https://github.com/hipspy/hips/blob/master/hips/utils/healpix.py#L61"&gt;hips.utils.healpix_pixels_in_sky_image&lt;/a&gt; function which takes in a &lt;a href="http://hips.readthedocs.io/en/latest/api/hips.utils.WCSGeometry.html#hips.utils.WCSGeometry"&gt;WCSGeometry&lt;/a&gt;&amp;nbsp;object, &lt;code&gt;order&lt;/code&gt;,&amp;nbsp;and &lt;code&gt;healpix_frame&lt;/code&gt; of the HEALPix map as parameter and returns a NumPy array containing the&amp;nbsp;pixels.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;astropy.coordinates&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SkyCoord&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;healpy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;hp&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;hips.utils&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;WCSGeometry&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;hips.utils&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;compute_healpix_pixel_indices&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;skycoord&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SkyCoord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;deg&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;wcs_geometry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WCSGeometry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;skydir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;skycoord&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;coordsys&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;CEL&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;projection&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;AIT&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;     &lt;span class="n"&gt;cdelt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;crpix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;nside&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order2nside&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;compute_healpix_pixel_indices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wcs_geometry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nside&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;176&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;207&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;208&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;239&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;240&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;271&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;272&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Applying projective&amp;nbsp;transformation&lt;/h3&gt;
&lt;p&gt;Once the tile corners are computed, &lt;a href="http://www.fen.bilkent.edu.tr/~franz/ag05/ag-07.pdf"&gt;projective transformation&lt;/a&gt; is applied to the image. For this, the &lt;a href="http://scikit-image.org/docs/dev/api/skimage.transform.html#skimage.transform.ProjectiveTransform"&gt;skimage.transform.ProjectiveTransform&lt;/a&gt; function is used. This function takes in two parameters,&amp;nbsp;namely &lt;code&gt;source&lt;/code&gt; and &lt;code&gt;destination&lt;/code&gt;. The destination array values are constant,&amp;nbsp;since &lt;code&gt;hips_tile_width&lt;/code&gt; is always equal&amp;nbsp;to &lt;code&gt;512&lt;/code&gt;.  In general,  the projective transform is given by a 3x3 matrix with 8 free parameters, however, in this case&amp;nbsp;the &lt;code&gt;estimate&lt;/code&gt; method is called which takes in two 4x2 arrays, i.e. 4 corners with each given by a longitude and a&amp;nbsp;latitude.&lt;/p&gt;
&lt;p&gt;The target position is calculated by multiplying this with the given matrix, i.e. the tile pixels. Thus, a HiPS tile is given as the destination parameter, which is done because the transformation works in an inverse manner i.e. it starts with the pixel coordinates in the output image, computes the corresponding coordinates in the input image, and then applies interpolation in the input image to compute the output pixel&amp;nbsp;values. &lt;/p&gt;
&lt;p&gt;Briefly, a HiPS tile is given as an input, and the output is the &amp;#8220;normal&amp;#8221; sky&amp;nbsp;image.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;skimage&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;transform&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt;
&lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;zeros&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;all_sky&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;wcs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wcs_world2pix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;corners&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;dst&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;([[&lt;/span&gt;&lt;span class="mi"&gt;511&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;511&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;511&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;511&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt;
&lt;span class="n"&gt;tform3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ProjectiveTransform&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;tform3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;estimate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dst&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;warp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tform3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;output_shape&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The resulting image is shown&amp;nbsp;below:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Projective transformation" src="https://adl1995.github.io/images/transform.png"&gt;&lt;/p&gt;
&lt;p&gt;The above image only shows a single tile being drawn onto an empty all sky image. As it can be seen, there are still some issues with tile drawing, for example, in this case, the same tile is always being drawn (that&amp;#8217;s why one can&amp;#8217;t see a sky image, just the pattern) and the handling of tiles at the &amp;#8220;edge&amp;#8221; of the sky image in this Aitoff projection. These issues will be resolved in the upcoming&amp;nbsp;milestone.&lt;/p&gt;
&lt;p&gt;&lt;img alt="All sky" src="https://adl1995.github.io/images/transfomed.png"&gt;&lt;/p&gt;
&lt;p&gt;The code for fetching a HiPS tile and drawing it using projective transformation can be found on this &lt;a href="https://github.com/adl1995/HIPS-to-Py/blob/master/examples/Affine%20and%20Projective%20transformation.ipynb"&gt;Jupyter notebook&lt;/a&gt;.&lt;/p&gt;
&lt;script type="text/javascript"&gt;if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
    var align = "center",
        indent = "0em",
        linebreak = "false";

    if (false) {
        align = (screen.width &lt; 768) ? "left" : align;
        indent = (screen.width &lt; 768) ? "0em" : indent;
        linebreak = (screen.width &lt; 768) ? 'true' : linebreak;
    }

    var mathjaxscript = document.createElement('script');
    mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
    mathjaxscript.type = 'text/javascript';
    mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML';

    var configscript = document.createElement('script');
    configscript.type = 'text/x-mathjax-config';
    configscript[(window.opera ? "innerHTML" : "text")] =
        "MathJax.Hub.Config({" +
        "    config: ['MMLorHTML.js']," +
        "    TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'none' } }," +
        "    jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
        "    extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
        "    displayAlign: '"+ align +"'," +
        "    displayIndent: '"+ indent +"'," +
        "    showMathMenu: true," +
        "    messageStyle: 'normal'," +
        "    tex2jax: { " +
        "        inlineMath: [ ['\\\\(','\\\\)'] ], " +
        "        displayMath: [ ['$$','$$'] ]," +
        "        processEscapes: true," +
        "        preview: 'TeX'," +
        "    }, " +
        "    'HTML-CSS': { " +
        "        availableFonts: ['STIX', 'TeX']," +
        "        preferredFont: 'STIX'," +
        "        styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
        "        linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
        "    }, " +
        "}); " +
        "if ('default' !== 'default') {" +
            "MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
            "MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
                "var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
                "VARIANT['normal'].fonts.unshift('MathJax_default');" +
                "VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
                "VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
                "VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
            "});" +
        "}";

    (document.body || document.getElementsByTagName('head')[0]).appendChild(configscript);
    (document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
&lt;/script&gt;</content><category term="GSoC"/></entry><entry><title>Type annotations in Python 3.6 and using Mypy as a static type checker</title><link href="https://adl1995.github.io/type-annotations-in-python-36-and-using-mypy-as-a-static-type-checker.html" rel="alternate"/><published>2017-06-11T12:58:00+02:00</published><updated>2017-06-11T12:58:00+02:00</updated><author><name>Adeel Ahmad</name></author><id>tag:adl1995.github.io,2017-06-11:/type-annotations-in-python-36-and-using-mypy-as-a-static-type-checker.html</id><summary type="html">&lt;p&gt;The main goal of type annotations is to open up Python code for static analysis. It makes it easier to debug and maintain code because each type is explicitly stated. It also makes the code review process simpler as the parameters and return types can be inferred from the function header. These changes were introduced in &lt;a href="https://www.python.org/dev/peps/pep-0484"&gt;&lt;span class="caps"&gt;PEP&lt;/span&gt; 484&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In this regards, static type checking is the most important. It allows support for off-line third-party type checkers, such as Mypy, which will be introduced in a &lt;a href="#static-type-checking-using-mypy"&gt;later section&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Purpose of&amp;nbsp;annotations&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;typing&lt;/code&gt; module in Python 3.6 contains many definitions that are useful in statically typed code. For instance,&amp;nbsp;the &lt;code&gt;Any&lt;/code&gt; type is used by default for every argument and …&lt;/p&gt;</summary><content type="html">&lt;p&gt;The main goal of type annotations is to open up Python code for static analysis. It makes it easier to debug and maintain code because each type is explicitly stated. It also makes the code review process simpler as the parameters and return types can be inferred from the function header. These changes were introduced in &lt;a href="https://www.python.org/dev/peps/pep-0484"&gt;&lt;span class="caps"&gt;PEP&lt;/span&gt; 484&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In this regards, static type checking is the most important. It allows support for off-line third-party type checkers, such as Mypy, which will be introduced in a &lt;a href="#static-type-checking-using-mypy"&gt;later section&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Purpose of&amp;nbsp;annotations&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;typing&lt;/code&gt; module in Python 3.6 contains many definitions that are useful in statically typed code. For instance,&amp;nbsp;the &lt;code&gt;Any&lt;/code&gt; type is used by default for every argument and return type of a function. This is all in regards to a checked function. If a function is to be ignored by a static type checker, the&amp;nbsp;decorator &lt;code&gt;@no_type_check&lt;/code&gt; should be provided before the function header, and the function will not be treated as having type&amp;nbsp;annotations.&lt;/p&gt;
&lt;p&gt;An example following the type annotations is shown&amp;nbsp;below:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scalar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;scalar&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This states that the expected type of&amp;nbsp;the &lt;code&gt;scalar&lt;/code&gt; and &lt;code&gt;number&lt;/code&gt; argument&amp;nbsp;is &lt;code&gt;int&lt;/code&gt; and &lt;code&gt;int&lt;/code&gt;, respectively. Analogically, the expected return type is&amp;nbsp;also &lt;code&gt;int&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The acceptable type hints that may be used&amp;nbsp;are: &lt;code&gt;None&lt;/code&gt;, &lt;code&gt;Any&lt;/code&gt;, &lt;code&gt;Union&lt;/code&gt;, &lt;code&gt;Tuple&lt;/code&gt;, &lt;code&gt;Callable&lt;/code&gt;, all of which are exported from&amp;nbsp;the &lt;code&gt;typing&lt;/code&gt; module&amp;nbsp;(e.g. &lt;code&gt;Sequence&lt;/code&gt; and &lt;code&gt;Dict&lt;/code&gt;), type variables, and type&amp;nbsp;aliases. &lt;/p&gt;
&lt;h4&gt;Using type&amp;nbsp;aliases&lt;/h4&gt;
&lt;p&gt;Type aliases can be defined by using simple variable&amp;nbsp;assignments:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;Temperature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;forecast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Temperature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;day&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;Creating your own&amp;nbsp;types&lt;/h4&gt;
&lt;p&gt;In Python 3.5 a new function is introduced&amp;nbsp;called &lt;code&gt;NewType&lt;/code&gt;. This allows the programmer to create distinct&amp;nbsp;types:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;typing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;NewType&lt;/span&gt;

&lt;span class="n"&gt;UserId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;NewType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;UserId&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;some_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;UserId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;356&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Static type checking using&amp;nbsp;Mypy&lt;/h3&gt;
&lt;p&gt;When a script is run with a standard Python interpreter, the type annotations are treated primarily as comments.&amp;nbsp;Using &lt;code&gt;mypy&lt;/code&gt;, common code bugs can be found and it checks the code for proper return types etc.&amp;nbsp;As &lt;code&gt;mypy&lt;/code&gt; is a static analyzer, it does not cause any overhead when running the&amp;nbsp;program.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: A function without a type annotation is considered dynamically&amp;nbsp;typed.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Let&amp;#8217;s&amp;nbsp;run &lt;code&gt;mypy&lt;/code&gt; for checking a class method I&amp;nbsp;wrote:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nd"&gt;@classmethod&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;parse_file_properties&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;OrderedDict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;properties&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;properties&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;list_properties&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nb"&gt;property&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;key_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;property&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;=&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;list_properties&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;key_value&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;key_value&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;IndexError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# The case where a property contains a comment or a blank line&lt;/span&gt;
            &lt;span class="k"&gt;pass&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;OrderedDict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;list_properties&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This function takes in&amp;nbsp;a &lt;code&gt;=&lt;/code&gt; separated string, parses it&amp;nbsp;using &lt;code&gt;split()&lt;/code&gt;, and returns it as an ordered dictionary. When I&amp;nbsp;run &lt;code&gt;mypy&lt;/code&gt; from a terminal, issuing the&amp;nbsp;command &lt;code&gt;python -m mypy hips/tiles&lt;/code&gt;, I get no error or warning, which indicates that the provided type annotations were&amp;nbsp;correct.&lt;/p&gt;
&lt;p&gt;However, if I change the function header&amp;nbsp;to:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;parse_file_properties&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;OrderedDict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I get this error&amp;nbsp;message:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;hips/tiles/description.py:61: error: &amp;#8220;int&amp;#8221; has no attribute&amp;nbsp;&amp;#8220;split&amp;#8221;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The illustration below shows the basic workflow of static type&amp;nbsp;checking:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Static type checking" src="https://adl1995.github.io/images/static_typing.png"&gt;&lt;/p&gt;</content><category term="GSoC"/></entry><entry><title>An overview of Hierarchical Progressive Surveys (HiPS) and the HEALPix framework</title><link href="https://adl1995.github.io/an-overview-of-hierarchical-progressive-surveys-hips-and-the-healpix-framework.html" rel="alternate"/><published>2017-05-31T19:39:00+02:00</published><updated>2017-05-31T19:39:00+02:00</updated><author><name>Adeel Ahmad</name></author><id>tag:adl1995.github.io,2017-05-31:/an-overview-of-hierarchical-progressive-surveys-hips-and-the-healpix-framework.html</id><summary type="html">&lt;p&gt;The &lt;a href="https://www.aanda.org/articles/aa/pdf/2015/06/aa26075-15.pdf"&gt;Hierarchical Progressive Surveys (HiPS)&lt;/a&gt; is a scheme for describing astronomical images and provides a solution for managing large amounts of data. Underneath, HiPS utilizes the HEALPix framework for mapping a sphere (in this case, part of a sky) and transforms it into &lt;a href="#hips-tiles"&gt;HiPS tiles&lt;/a&gt; and &lt;a href="#hips-pixels"&gt;HiPS pixels&lt;/a&gt; which contain the astronomical data. The HiPS scheme emphasizes on usability, and abstracts the scientific details to reach a wider audience. This can be further built upon for statistical analysis of large datasets. A brief overview of HEALPix is given below before moving onto the working of&amp;nbsp;HiPS.&lt;/p&gt;
&lt;h3&gt;Introduction to&amp;nbsp;HEALPix&lt;/h3&gt;
&lt;p&gt;HEALPix, an acronym of &amp;#8216;Hierarchical Equal Area isoLatitude Pixelization of a sphere&amp;#8217;, is a framework for discretizing high resolution data. It …&lt;/p&gt;</summary><content type="html">&lt;p&gt;The &lt;a href="https://www.aanda.org/articles/aa/pdf/2015/06/aa26075-15.pdf"&gt;Hierarchical Progressive Surveys (HiPS)&lt;/a&gt; is a scheme for describing astronomical images and provides a solution for managing large amounts of data. Underneath, HiPS utilizes the HEALPix framework for mapping a sphere (in this case, part of a sky) and transforms it into &lt;a href="#hips-tiles"&gt;HiPS tiles&lt;/a&gt; and &lt;a href="#hips-pixels"&gt;HiPS pixels&lt;/a&gt; which contain the astronomical data. The HiPS scheme emphasizes on usability, and abstracts the scientific details to reach a wider audience. This can be further built upon for statistical analysis of large datasets. A brief overview of HEALPix is given below before moving onto the working of&amp;nbsp;HiPS.&lt;/p&gt;
&lt;h3&gt;Introduction to&amp;nbsp;HEALPix&lt;/h3&gt;
&lt;p&gt;HEALPix, an acronym of &amp;#8216;Hierarchical Equal Area isoLatitude Pixelization of a sphere&amp;#8217;, is a framework for discretizing high resolution data. It was devised at &lt;span class="caps"&gt;NASA&lt;/span&gt;&amp;#8217;s &lt;a href="https://www.jpl.nasa.gov"&gt;Jet Propulsion Laboratory&lt;/a&gt;. The software is available in C, C++, Fortran90, &lt;span class="caps"&gt;IDL&lt;/span&gt;, Java, and Python. The main features provided by the software library&amp;nbsp;include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Pixel&amp;nbsp;manipulation&lt;/li&gt;
&lt;li&gt;Spherical Harmonics&amp;nbsp;Transforms&lt;/li&gt;
&lt;li&gt;Visualization&lt;/li&gt;
&lt;li&gt;Input / Output (with support for &lt;span class="caps"&gt;FITS&lt;/span&gt;&amp;nbsp;files)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In a nutshell, the pixelization procedure divides a spherical sphere in which each pixel is equidistant from the origin, i.e. all pixels cover the same surface area. This produces a HEALPix grid, which has the interesting property that all pixels are distributed on lines of constant latitude. Due to this iso-latitude distribution of pixels the complexity for computing integrals over each harmonics is N&lt;sup&gt;1/2&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;HEALPix provides a standard format for storing data in &lt;a href="https://en.wikipedia.org/wiki/FITS"&gt;&lt;span class="caps"&gt;FITS&lt;/span&gt;&lt;/a&gt; files. There are numerous software packages that can work with HEALPix data. For this GSoC&amp;nbsp;project, &lt;code&gt;healpy&lt;/code&gt; will be used which is an abstraction of the HEALPix C++ package. There also exist other packages, such as &lt;a href="http://aladin.u-strasbg.fr/AladinLite"&gt;Aladin Lite&lt;/a&gt;. The main functionality required for this project is HEALPix pixel index to sky coordinate transformation (and vice-versa), and a method to list HEALPix pixels in a given region of the sky&amp;nbsp;(e.g. &lt;code&gt;query_disc&lt;/code&gt; from &lt;code&gt;healpy&lt;/code&gt;).&lt;/p&gt;
&lt;h4&gt;Pixel numbering&amp;nbsp;schemes&lt;/h4&gt;
&lt;p&gt;HEALPix provides two numbering schemes for pixels, namely the &lt;strong&gt;&lt;span class="caps"&gt;RING&lt;/span&gt; scheme&lt;/strong&gt; and &lt;strong&gt;&lt;span class="caps"&gt;NESTED&lt;/span&gt; scheme&lt;/strong&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;span class="caps"&gt;RING&lt;/span&gt; scheme&lt;/strong&gt;
 In this scheme the pixels are counted down from the north to south along each iso-latitude&amp;nbsp;ring.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;span class="caps"&gt;NESTED&lt;/span&gt; scheme&lt;/strong&gt;
 This scheme arranges the pixels into 12 tree structures with respect to their base-resolution&amp;nbsp;pixels.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="HEALPix projection" src="https://adl1995.github.io/images/HEALPix_projection_SW.png"&gt;&lt;/p&gt;
&lt;h3&gt;Introduction to&amp;nbsp;HiPS&lt;/h3&gt;
&lt;p&gt;HiPS is the hierarchical tiling mechanism which allows one to access, visualize and browse image&amp;nbsp;data.&lt;/p&gt;
&lt;h4&gt;HiPS&amp;nbsp;working&lt;/h4&gt;
&lt;p&gt;The multi-resolution representation of original images provides the basis for visualizing data in a progressive way, as the pixels that are required for a given view can be accessed through pre-computed HEALPix maps, and the nested pixel numbering scheme provides a simple hierarchical indexing system that encodes pixel inheritance across different&amp;nbsp;orders.&lt;/p&gt;
&lt;p&gt;HiPS scheme groups pixels into different tiles. The general relationship between tiles and pixels is that a tile&amp;nbsp;with &lt;code&gt;n-tile&lt;/code&gt; pixels along each side forms a HEALPix mesh of order k&lt;sup&gt;tile&lt;/sup&gt;. Tiles store map information from HEALPix and are presented as square arrays. It is possible to store them in different file formats. The files are organized into different directories. Here, tiles are used as files and tile orders are used for grouping data in directories which follow a naming convention. For more information on the method used for file storage, &lt;a href="http://aladin.unistra.fr/hips/hipsdoc.pdf"&gt;this&lt;/a&gt; provides a brief&amp;nbsp;description.&lt;/p&gt;
&lt;h4&gt;HiPS&amp;nbsp;pixels&lt;/h4&gt;
&lt;p&gt;HiPS pixels are defined in&amp;nbsp;the &lt;code&gt;hips_pixel_bitpix&lt;/code&gt; header and are stored in &lt;span class="caps"&gt;BITPIX&lt;/span&gt;&amp;nbsp;code. &lt;code&gt;hips_pixel_bitpix&lt;/code&gt; refers to the data type used for storing a &lt;span class="caps"&gt;FITS&lt;/span&gt; tile (a value of 8 represents 8-bit integers, -32 represents simple floating points, and -64 represents double precision floating&amp;nbsp;points).&lt;/p&gt;
&lt;p&gt;This is usually the same value as the &lt;span class="caps"&gt;BITPIX&lt;/span&gt; value of the original images (described in&amp;nbsp;keyword &lt;code&gt;data_pixel_bitpix&lt;/code&gt;), but might be different, notably for HiPS built from heterogeneous origins. The &lt;span class="caps"&gt;BITPIX&lt;/span&gt; value is always present in the HiPS &lt;span class="caps"&gt;FITS&lt;/span&gt;&amp;nbsp;tiles.&lt;/p&gt;
&lt;h4&gt;HiPS&amp;nbsp;tiles&lt;/h4&gt;
&lt;p&gt;As it is cumbersome to transfer each pixel (which will essentially be a single file), HiPS scheme groups pixels into what they call a tile. The general relationship between the tiles and pixels is that a tile with &lt;em&gt;n&lt;/em&gt;-tile pixels along each side forms a HEALPix mesh of order &lt;em&gt;k&lt;/em&gt;-tile. A HiPS tile is show&amp;nbsp;below.&lt;/p&gt;
&lt;p&gt;&lt;img alt="A HiPS tile" src="https://adl1995.github.io/images/hips-tile.jpg"&gt;&lt;/p&gt;
&lt;h4&gt;HiPS&amp;nbsp;images&lt;/h4&gt;
&lt;p&gt;The way HiPS represents images is by re-sampling them onto a HEALPix grid at the maximum desired order, denoted by k&lt;sup&gt;max&lt;/sup&gt;. It then generates tiles based on tile orders. When mosaicking / stitching images, the angular resolution is taken into account. There are various methods for filling the data region when stitching images and dealing with background differences. The k&lt;sup&gt;max&lt;/sup&gt; determines minimum pixel size which is near to the angular pixel size or the resolution of original&amp;nbsp;data.&lt;/p&gt;
&lt;p&gt;Next important thing is whether to emphasize on &lt;em&gt;display quality&lt;/em&gt; or &lt;em&gt;photometric accuracy&lt;/em&gt;. This varies for each specific use case. Image encoding can be done either in &lt;strong&gt;&lt;span class="caps"&gt;FITS&lt;/span&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;span class="caps"&gt;PNG&lt;/span&gt;&lt;/strong&gt;, or the &lt;strong&gt;&lt;span class="caps"&gt;JPG&lt;/span&gt;&lt;/strong&gt; file format. For most cases it is enough to only generate &lt;span class="caps"&gt;FITS&lt;/span&gt; and &lt;span class="caps"&gt;PNG&lt;/span&gt; files. The lowest order pixel values correspond to a large area of the sky. The HiPS indexing structure takes care of mapping correct tiles onto a&amp;nbsp;display.&lt;/p&gt;
&lt;p&gt;HiPS generation for huge amounts of data such as the Hubble Space Telescope requires planning of system&amp;nbsp;growth.&lt;/p&gt;
&lt;h4&gt;HiPS&amp;nbsp;catalogues&lt;/h4&gt;
&lt;p&gt;A HiPS catalogue contains the &lt;span class="caps"&gt;RA&lt;/span&gt; (right ascension) / &lt;span class="caps"&gt;DEC&lt;/span&gt; (declination) coordinates stored in a &lt;span class="caps"&gt;TSV&lt;/span&gt; file. The data is &lt;span class="caps"&gt;ASCII&lt;/span&gt; tab separated and is organized into various directories in the same way as HiPS&amp;nbsp;images.&lt;/p&gt;
&lt;h3&gt;Google Summer of Code 2017&amp;nbsp;project&lt;/h3&gt;
&lt;p&gt;I have been selected for creating this package as part of Google Summer of Code program, along with my mentors, &lt;a href="https://github.com/cdeil"&gt;Christoph Deil&lt;/a&gt; and  &lt;a href="https://github.com/tboch"&gt;Thomas Boch&lt;/a&gt;. My application proposal can be found &lt;a href="https://github.com/adl1995/HIPS-to-Py/blob/master/documents/application.md"&gt;on GitHub&lt;/a&gt;. The goal of this project is to design and create a Python client for Hierarchical Progressive Surveys (HiPS). The library will extend a low and high level &lt;span class="caps"&gt;API&lt;/span&gt; for exploring and creating &lt;span class="caps"&gt;WCS&lt;/span&gt; / HEALPix&amp;nbsp;images.&lt;/p&gt;
&lt;p&gt;Currently, there are other clients built using HiPS, such as Aladin and Aladin Lite, but they are written in Java and JavaScript, respectively. Once the proposed functionality is complete, additional features such as measuring fluxes and overplotting multi-wavelength data might be added. The code repository for this project can be found &lt;a href="https://github.com/hipspy/hips"&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If anyone has any questions or suggestions regarding the package, they are encouraged to open issues on GitHub. If you want to help us build the package or add extra features onto it, please open up a pull&amp;nbsp;request.&lt;/p&gt;
&lt;p&gt;The coding period is about to begin and my future blog posts will be based around this&amp;nbsp;topic.&lt;/p&gt;</content><category term="GSoC"/></entry><entry><title>An introduction to coordinate systems used in Astronomy</title><link href="https://adl1995.github.io/an-introduction-to-coordinate-systems-used-in-astronomy.html" rel="alternate"/><published>2017-05-23T12:37:00+02:00</published><updated>2017-05-23T12:37:00+02:00</updated><author><name>Adeel Ahmad</name></author><id>tag:adl1995.github.io,2017-05-23:/an-introduction-to-coordinate-systems-used-in-astronomy.html</id><summary type="html">&lt;p&gt;From &lt;a href="https://en.wikipedia.org/wiki/Coordinate_system"&gt;Wikipedia&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In &lt;a href="https://en.wikipedia.org/wiki/Geometry" title="Geometry"&gt;geometry&lt;/a&gt;, a &lt;strong&gt;coordinate system&lt;/strong&gt; is a system which uses one or more &lt;a href="https://en.wikipedia.org/wiki/Number" title="Number"&gt;numbers&lt;/a&gt;, or &lt;strong&gt;coordinates&lt;/strong&gt;, to uniquely determine the position of the &lt;a href="https://en.wikipedia.org/wiki/Point_(geometry)" title="Point (geometry)"&gt;points&lt;/a&gt; or other geometric elements on a &lt;a href="https://en.wikipedia.org/wiki/Manifold" title="Manifold"&gt;manifold&lt;/a&gt; such as &lt;a href="https://en.wikipedia.org/wiki/Euclidean_space" title="Euclidean space"&gt;Euclidean space&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The following text briefly explains the coordinate systems being used in astronomy, some of which are listed&amp;nbsp;below:&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;&lt;span class="caps"&gt;RA&lt;/span&gt; / &lt;span class="caps"&gt;DEC&lt;/span&gt;&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span class="caps"&gt;RA&lt;/span&gt; (right ascension) and &lt;span class="caps"&gt;DEC&lt;/span&gt; (declination) are the longitudes and latitudes of the sky. &lt;span class="caps"&gt;RA&lt;/span&gt; corresponds to east / west direction, similar to longitude, while &lt;span class="caps"&gt;DEC&lt;/span&gt; measures north / south directions, like&amp;nbsp;latitude.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;&lt;span class="caps"&gt;WCS&lt;/span&gt;&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;World Coordinate System (&lt;span class="caps"&gt;WCS&lt;/span&gt;) is a set of transformations that map pixel locations in an image to their real-world units, such as their position on the sky sphere. These transformations can …&lt;/p&gt;</summary><content type="html">&lt;p&gt;From &lt;a href="https://en.wikipedia.org/wiki/Coordinate_system"&gt;Wikipedia&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In &lt;a href="https://en.wikipedia.org/wiki/Geometry" title="Geometry"&gt;geometry&lt;/a&gt;, a &lt;strong&gt;coordinate system&lt;/strong&gt; is a system which uses one or more &lt;a href="https://en.wikipedia.org/wiki/Number" title="Number"&gt;numbers&lt;/a&gt;, or &lt;strong&gt;coordinates&lt;/strong&gt;, to uniquely determine the position of the &lt;a href="https://en.wikipedia.org/wiki/Point_(geometry)" title="Point (geometry)"&gt;points&lt;/a&gt; or other geometric elements on a &lt;a href="https://en.wikipedia.org/wiki/Manifold" title="Manifold"&gt;manifold&lt;/a&gt; such as &lt;a href="https://en.wikipedia.org/wiki/Euclidean_space" title="Euclidean space"&gt;Euclidean space&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The following text briefly explains the coordinate systems being used in astronomy, some of which are listed&amp;nbsp;below:&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;&lt;span class="caps"&gt;RA&lt;/span&gt; / &lt;span class="caps"&gt;DEC&lt;/span&gt;&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span class="caps"&gt;RA&lt;/span&gt; (right ascension) and &lt;span class="caps"&gt;DEC&lt;/span&gt; (declination) are the longitudes and latitudes of the sky. &lt;span class="caps"&gt;RA&lt;/span&gt; corresponds to east / west direction, similar to longitude, while &lt;span class="caps"&gt;DEC&lt;/span&gt; measures north / south directions, like&amp;nbsp;latitude.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;&lt;span class="caps"&gt;WCS&lt;/span&gt;&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;World Coordinate System (&lt;span class="caps"&gt;WCS&lt;/span&gt;) is a set of transformations that map pixel locations in an image to their real-world units, such as their position on the sky sphere. These transformations can work both forward (from pixel to sky) and backward (from sky to&amp;nbsp;pixel).&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;&lt;span class="caps"&gt;FITS&lt;/span&gt; &lt;span class="caps"&gt;WCS&lt;/span&gt;&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;span class="caps"&gt;FITS&lt;/span&gt; &amp;#8220;World Coordinate System&amp;#8221; (&lt;span class="caps"&gt;WCS&lt;/span&gt;) standard defines some conventions and keywords to associate coordinates with each pixel of an&amp;nbsp;image.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;&lt;span class="caps"&gt;FITS&lt;/span&gt;&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Flexible Image Transport System (&lt;span class="caps"&gt;FITS&lt;/span&gt;) is a digital file format useful for storage, transmission and processing of scientific and other images. It is the defacto standard used by many sky tessellation softwares &amp;#8212; in this case,&amp;nbsp;HEALPix.&lt;/p&gt;
&lt;p&gt;HEALPix header files can contain the following three letters, each depicting the coordinate system being&amp;nbsp;used:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;C&lt;/strong&gt;:Celestial = &lt;span class="caps"&gt;ICRS&lt;/span&gt; = &lt;span class="caps"&gt;RA&lt;/span&gt; / &lt;span class="caps"&gt;DEC&lt;/span&gt; (equatorial) = &lt;span class="caps"&gt;FK5&lt;/span&gt; J2000&amp;nbsp;(default)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;G&lt;/strong&gt;:Galactic&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;E&lt;/strong&gt;:Ecliptic&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;strong&gt;International Celestial Reference System (&lt;span class="caps"&gt;ICRS&lt;/span&gt;)&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span class="caps"&gt;ICRS&lt;/span&gt; is the current standard celestial reference system adopted by the International Astronomical Union (&lt;span class="caps"&gt;IAU&lt;/span&gt;). Its &lt;strong&gt;origin is at the barycenter of the Solar System&lt;/strong&gt;, with axes that are intended to be &amp;#8220;fixed&amp;#8221; with respect to space - this is referred to as International Celestial Reference Frame (&lt;span class="caps"&gt;ICRF&lt;/span&gt;). &lt;span class="caps"&gt;ICRS&lt;/span&gt; coordinates are approximately the same as equatorial&amp;nbsp;coordinates.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Fifth Fundamental Catalogue (&lt;span class="caps"&gt;FK5&lt;/span&gt;)&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span class="caps"&gt;FK5&lt;/span&gt; is part of the &amp;#8220;Catalogue of Fundamental Stars&amp;#8221; which provides a series of six astrometric catalogues of high precision positional data for a small selection of stars to define a celestial reference frame. J2000 refers to the instant of 12 &lt;span class="caps"&gt;PM&lt;/span&gt; (midday) on 1st January, 2000. &lt;span class="caps"&gt;FK5&lt;/span&gt; was published in 1991 and added 3,117 new&amp;nbsp;stars.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Galactic coordinate&amp;nbsp;system&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;The galactic coordinate system is a celestial coordinate system in spherical coordinates, with its &lt;strong&gt;origin at the Sun&lt;/strong&gt;, the primary direction aligned with the approximate center of the Milky Way galaxy, and the fundamental plane parallel to an approximation of the galactic plane but offset to its north. &lt;span class="caps"&gt;GCS&lt;/span&gt; has its own Galactic longitude and Galactic&amp;nbsp;latitude.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;Ecliptic coordinate&amp;nbsp;system&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;A celestial coordinate system commonly used for representing the positions and orbits of Solar System objects. The system&amp;#8217;s &lt;strong&gt;origin can either be the center of the Sun or the center of the Earth&lt;/strong&gt;, its primary direction is towards the vernal (northbound) equinox, and it follows a right-handed&amp;nbsp;convention.&lt;/p&gt;</content><category term="Astronomy"/></entry><entry><title>A comparison of response times using URLLib, GRequests, and asyncio</title><link href="https://adl1995.github.io/a-comparison-of-response-times-using-urllib-grequests-and-asyncio.html" rel="alternate"/><published>2017-05-09T15:54:00+02:00</published><updated>2017-05-09T15:54:00+02:00</updated><author><name>Adeel Ahmad</name></author><id>tag:adl1995.github.io,2017-05-09:/a-comparison-of-response-times-using-urllib-grequests-and-asyncio.html</id><summary type="html">&lt;p&gt;For the &lt;a href="https://github.com/hipspy/hips"&gt;HiPS client&lt;/a&gt; multiple tiles have to be fetched for time efficiency. To achieve this, we create a separate thread for each outgoing request. Thus, requests are sent concurrently. A comparison is done utilizing&amp;nbsp;Python&amp;#8217;s &lt;code&gt;threading&lt;/code&gt; library. The elapsed time is calculated using&amp;nbsp;the &lt;code&gt;time&lt;/code&gt; module. For fetching the&amp;nbsp;tiles &lt;code&gt;urllib&lt;/code&gt;, &lt;code&gt;grequests&lt;/code&gt;, &lt;code&gt;aiohttp&lt;/code&gt;,&amp;nbsp;and &lt;code&gt;asyncio&lt;/code&gt; packages are used. The HiPS survey chosen for this comparison&amp;nbsp;is &lt;code&gt;alasky.u-strasbg.fr&lt;/code&gt;. &lt;/p&gt;
&lt;p&gt;For fetching 10 tiles, it takes the following time (in&amp;nbsp;seconds):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Elapsed Time URLLib (without concurrency): 3.5430831909179688
Elapsed Time URLLib (with concurrency): 0.388397216796875
Elapsed Time URLLib (with aiohttp): 0.3900480270385742
Elapsed Time GRequests: 1.6238431930541992
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Similarly, for fetching 100 tiles, it&amp;nbsp;takes:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Elapsed Time URLLib (without concurrency …&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</summary><content type="html">&lt;p&gt;For the &lt;a href="https://github.com/hipspy/hips"&gt;HiPS client&lt;/a&gt; multiple tiles have to be fetched for time efficiency. To achieve this, we create a separate thread for each outgoing request. Thus, requests are sent concurrently. A comparison is done utilizing&amp;nbsp;Python&amp;#8217;s &lt;code&gt;threading&lt;/code&gt; library. The elapsed time is calculated using&amp;nbsp;the &lt;code&gt;time&lt;/code&gt; module. For fetching the&amp;nbsp;tiles &lt;code&gt;urllib&lt;/code&gt;, &lt;code&gt;grequests&lt;/code&gt;, &lt;code&gt;aiohttp&lt;/code&gt;,&amp;nbsp;and &lt;code&gt;asyncio&lt;/code&gt; packages are used. The HiPS survey chosen for this comparison&amp;nbsp;is &lt;code&gt;alasky.u-strasbg.fr&lt;/code&gt;. &lt;/p&gt;
&lt;p&gt;For fetching 10 tiles, it takes the following time (in&amp;nbsp;seconds):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Elapsed Time URLLib (without concurrency): 3.5430831909179688
Elapsed Time URLLib (with concurrency): 0.388397216796875
Elapsed Time URLLib (with aiohttp): 0.3900480270385742
Elapsed Time GRequests: 1.6238431930541992
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Similarly, for fetching 100 tiles, it&amp;nbsp;takes:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Elapsed Time URLLib (without concurrency): 37.7027428150177
Elapsed Time URLLib (with concurrency): 5.575664043426514
Elapsed Time URLLib (with aiohttp): 2.4697625637054443
Elapsed Time GRequests: 4.273705244064331
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The pros&amp;nbsp;of &lt;code&gt;grequests&lt;/code&gt; is that it takes less time when large number of requests have to be sent.&amp;nbsp;But &lt;code&gt;urllib&lt;/code&gt; (with threading) gives a better response time when requests are&amp;nbsp;few.&lt;/p&gt;
&lt;p&gt;Using &lt;code&gt;aiohttp&lt;/code&gt; with &lt;code&gt;asyncio&lt;/code&gt; seems to be the best option. Its response time is almost 50% less&amp;nbsp;than &lt;code&gt;grequests&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The source code for this comparison can be found &lt;a href="https://github.com/adl1995/HIPS-to-Py/blob/master/examples/fetch-tiles-threaded.py"&gt;here&lt;/a&gt;.&lt;/p&gt;</content><category term="GSoC"/></entry><entry><title>My First Article</title><link href="https://adl1995.github.io/my-first-article.html" rel="alternate"/><published>2017-03-19T22:45:00+01:00</published><updated>2017-03-19T22:45:00+01:00</updated><author><name>Adeel Ahmad</name></author><id>tag:adl1995.github.io,2017-03-19:/my-first-article.html</id><content type="html">&lt;p&gt;Hello&amp;nbsp;World!&lt;/p&gt;
&lt;p&gt;This blog will be extensively used for posting GSoC updates, apart from other technical&amp;nbsp;ramblings.&lt;/p&gt;</content><category term="GSoC"/></entry></feed>